| 1 | /* lgp_sys.c: LGP-30 simulator interface\r |
| 2 | \r |
| 3 | Copyright (c) 2004-2005, Robert M Supnik\r |
| 4 | \r |
| 5 | Permission is hereby granted, free of charge, to any person obtaining a\r |
| 6 | copy of this software and associated documentation files (the "Software"),\r |
| 7 | to deal in the Software without restriction, including without limitation\r |
| 8 | the rights to use, copy, modify, merge, publish, distribute, sublicense,\r |
| 9 | and/or sell copies of the Software, and to permit persons to whom the\r |
| 10 | Software is furnished to do so, subject to the following conditions:\r |
| 11 | \r |
| 12 | The above copyright notice and this permission notice shall be included in\r |
| 13 | all copies or substantial portions of the Software.\r |
| 14 | \r |
| 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r |
| 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r |
| 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\r |
| 18 | ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER\r |
| 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN\r |
| 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r |
| 21 | \r |
| 22 | Except as contained in this notice, the name of Robert M Supnik shall not be\r |
| 23 | used in advertising or otherwise to promote the sale, use or other dealings\r |
| 24 | in this Software without prior written authorization from Robert M Supnik.\r |
| 25 | \r |
| 26 | 04-Jan-05 RMS Modified VM pointer setup\r |
| 27 | */\r |
| 28 | \r |
| 29 | #include "lgp_defs.h"\r |
| 30 | #include <ctype.h>\r |
| 31 | \r |
| 32 | t_stat parse_sym_m (char *cptr, t_value *val, int32 sw);\r |
| 33 | void lgp_init (void);\r |
| 34 | \r |
| 35 | extern DEVICE cpu_dev;\r |
| 36 | extern UNIT cpu_unit;\r |
| 37 | extern DEVICE tti_dev, tto_dev;\r |
| 38 | extern DEVICE ptr_dev, ptp_dev;\r |
| 39 | extern REG cpu_reg[];\r |
| 40 | extern uint32 M[];\r |
| 41 | extern uint32 PC;\r |
| 42 | extern uint32 ts_flag;\r |
| 43 | extern int32 sim_switches;\r |
| 44 | extern int32 flex_to_ascii[128], ascii_to_flex[128];\r |
| 45 | \r |
| 46 | extern void (*sim_vm_fprint_addr) (FILE *st, DEVICE *dptr, t_addr addr);\r |
| 47 | extern t_addr (*sim_vm_parse_addr) (DEVICE *dptr, char *cptr, char **tptr);\r |
| 48 | \r |
| 49 | /* SCP data structures and interface routines\r |
| 50 | \r |
| 51 | sim_name simulator name string\r |
| 52 | sim_PC pointer to saved PC register descriptor\r |
| 53 | sim_emax maximum number of words for examine/deposit\r |
| 54 | sim_devices array of pointers to simulated devices\r |
| 55 | sim_stop_messages array of pointers to stop messages\r |
| 56 | sim_load binary loader\r |
| 57 | */\r |
| 58 | \r |
| 59 | char sim_name[] = "LGP30";\r |
| 60 | \r |
| 61 | REG *sim_PC = &cpu_reg[0];\r |
| 62 | \r |
| 63 | int32 sim_emax = 1;\r |
| 64 | \r |
| 65 | DEVICE *sim_devices[] = {\r |
| 66 | &cpu_dev,\r |
| 67 | &tti_dev,\r |
| 68 | &tto_dev,\r |
| 69 | &ptr_dev,\r |
| 70 | &ptp_dev,\r |
| 71 | NULL\r |
| 72 | };\r |
| 73 | \r |
| 74 | const char *sim_stop_messages[] = {\r |
| 75 | "Unknown error",\r |
| 76 | "STOP",\r |
| 77 | "Breakpoint",\r |
| 78 | "Arithmetic overflow"\r |
| 79 | };\r |
| 80 | \r |
| 81 | /* Binary loader - implements a restricted form of subroutine 10.4\r |
| 82 | \r |
| 83 | Switches:\r |
| 84 | -t, input file is transposed Flex\r |
| 85 | -n, no checksums on v commands (10.0 compatible)\r |
| 86 | default is ASCII encoded Flex\r |
| 87 | Commands (in bits 0-3):\r |
| 88 | (blank) instruction\r |
| 89 | + command (not supported)\r |
| 90 | ; start fill\r |
| 91 | / set modifier\r |
| 92 | . stop and transfer\r |
| 93 | , hex words\r |
| 94 | v hex fill (checksummed unless -n)\r |
| 95 | 8 negative instruction\r |
| 96 | */\r |
| 97 | \r |
| 98 | /* Utility routine - read characters until ' (conditional stop) */\r |
| 99 | \r |
| 100 | t_stat load_getw (FILE *fi, uint32 *wd)\r |
| 101 | {\r |
| 102 | int32 flex, c;\r |
| 103 | \r |
| 104 | *wd = 0;\r |
| 105 | while ((c = fgetc (fi)) != EOF) {\r |
| 106 | if (sim_switches & SWMASK ('T'))\r |
| 107 | flex = ((c << 1) | (c >> 5)) & 0x3F;\r |
| 108 | else flex = ascii_to_flex[c & 0x7F];\r |
| 109 | if ((flex == FLEX_CR) || (flex == FLEX_DEL) ||\r |
| 110 | (flex == FLEX_UC) || (flex == FLEX_LC) ||\r |
| 111 | (flex == FLEX_BS) || (flex < 0)) continue;\r |
| 112 | if (flex == FLEX_CSTOP) return SCPE_OK;\r |
| 113 | *wd = (*wd << 4) | ((flex >> 2) & 0xF);\r |
| 114 | }\r |
| 115 | return SCPE_FMT;\r |
| 116 | }\r |
| 117 | \r |
| 118 | /* Utility routine - convert ttss decimal address to binary */\r |
| 119 | \r |
| 120 | t_stat load_geta (uint32 wd, uint32 *ad)\r |
| 121 | {\r |
| 122 | uint32 n1, n2, n3, n4, tr, sc;\r |
| 123 | \r |
| 124 | n1 = (wd >> 12) & 0xF;\r |
| 125 | n2 = (wd >> 8) & 0xF;\r |
| 126 | n3 = (wd >> 4) & 0xF;\r |
| 127 | n4 = wd & 0xF;\r |
| 128 | if ((n2 > 9) || (n4 > 9)) return SCPE_ARG;\r |
| 129 | tr = (n1 * 10) + n2;\r |
| 130 | sc = (n3 * 10) + n4;\r |
| 131 | if ((tr >= NTK_30) || (sc >= NSC_30)) return SCPE_ARG;\r |
| 132 | *ad = (tr * NSC_30) + sc;\r |
| 133 | return SCPE_OK;\r |
| 134 | }\r |
| 135 | \r |
| 136 | /* Loader proper */\r |
| 137 | \r |
| 138 | t_stat sim_load (FILE *fi, char *cptr, char *fnam, int flag)\r |
| 139 | {\r |
| 140 | uint32 wd, origin, amod, csum, cnt, tr, sc, ad, cmd;\r |
| 141 | \r |
| 142 | origin = amod = 0;\r |
| 143 | for (;;) { /* until stopped */\r |
| 144 | if (load_getw (fi, &wd)) break; /* get ctrl word */\r |
| 145 | cmd = (wd >> 28) & 0xF; /* get <0:3> */\r |
| 146 | switch (cmd) { /* decode <0:3> */\r |
| 147 | \r |
| 148 | case 0x2: /* + command */\r |
| 149 | return SCPE_FMT;\r |
| 150 | \r |
| 151 | case 0x3: /* ; start fill */\r |
| 152 | if (load_geta (wd, &origin)) return SCPE_FMT; /* origin = addr */\r |
| 153 | break;\r |
| 154 | \r |
| 155 | case 0x4: /* / set modifier */\r |
| 156 | if (load_geta (wd, &amod)) return SCPE_FMT; /* modifier = addr */\r |
| 157 | break;\r |
| 158 | \r |
| 159 | case 0x5: /* . transfer */\r |
| 160 | if (load_geta (wd, &PC)) return SCPE_FMT; /* PC = addr */\r |
| 161 | return SCPE_OK; /* done! */\r |
| 162 | \r |
| 163 | case 0x6: /* hex words */\r |
| 164 | if (load_geta (wd, &cnt)) return SCPE_FMT; /* count = addr */\r |
| 165 | if ((cnt == 0) || (cnt > 63)) return SCPE_FMT;\r |
| 166 | while (cnt--) { /* fill hex words */\r |
| 167 | if (load_getw (fi, &wd)) return SCPE_FMT;\r |
| 168 | Write (origin, wd);\r |
| 169 | origin = (origin + 1) & AMASK;\r |
| 170 | }\r |
| 171 | break;\r |
| 172 | \r |
| 173 | case 0x7: /* hex fill */\r |
| 174 | cnt = (wd >> 16) & 0xFFF; /* hex count */\r |
| 175 | tr = (wd >> 8) & 0xFF; /* hex track */\r |
| 176 | sc = wd & 0xFF; /* hex sector */\r |
| 177 | if ((cnt == 0) || (cnt > 0x7FF) || /* validate */\r |
| 178 | (tr >= NTK_30) || (sc >= NSC_30)) return SCPE_ARG;\r |
| 179 | ad = (tr * NSC_30) + sc; /* decimal addr */\r |
| 180 | for (csum = 0; cnt; cnt--) { /* fill words */\r |
| 181 | if (load_getw (fi, &wd)) return SCPE_FMT;\r |
| 182 | Write (ad, wd);\r |
| 183 | csum = (csum + wd) & MMASK;\r |
| 184 | ad = (ad + 1) & AMASK;\r |
| 185 | }\r |
| 186 | if (!(sim_switches & SWMASK ('N'))) { /* unless -n, csum */\r |
| 187 | if (load_getw (fi, &wd)) return SCPE_FMT;\r |
| 188 | /* if ((csum ^wd) & MMASK) return SCPE_CSUM; */\r |
| 189 | }\r |
| 190 | break;\r |
| 191 | \r |
| 192 | case 0x0: case 0x8: /* instructions */\r |
| 193 | if (load_geta (wd, &ad)) return SCPE_FMT; /* get address */\r |
| 194 | if ((wd & 0x00F00000) != 0x00900000) /* if not x, */\r |
| 195 | ad = (ad + amod) & AMASK; /* modify */\r |
| 196 | wd = (wd & (SIGN|I_OP)) + (ad << I_V_EA); /* instruction */\r |
| 197 | \r |
| 198 | default: /* data word */\r |
| 199 | Write (origin, wd);\r |
| 200 | origin = (origin + 1) & AMASK;\r |
| 201 | break;\r |
| 202 | } /* end case */\r |
| 203 | } /* end for */\r |
| 204 | return SCPE_OK;\r |
| 205 | }\r |
| 206 | \r |
| 207 | /* Symbol tables */\r |
| 208 | \r |
| 209 | static const char opcode[] = "ZBYRIDNMPEUTHCAS";\r |
| 210 | \r |
| 211 | static const char hex_decode[] = "0123456789FGJKQW";\r |
| 212 | \r |
| 213 | void lgp_fprint_addr (FILE *st, DEVICE *dptr, t_addr addr)\r |
| 214 | {\r |
| 215 | if ((dptr == sim_devices[0]) &&\r |
| 216 | ((sim_switches & SWMASK ('T')) ||\r |
| 217 | ((cpu_unit.flags & UNIT_TTSS_D) && !(sim_switches & SWMASK ('N')))))\r |
| 218 | fprintf (st, "%02d%02d", addr >> 6, addr & SCMASK_30);\r |
| 219 | else fprint_val (st, addr, dptr->aradix, dptr->awidth, PV_LEFT);\r |
| 220 | return;\r |
| 221 | }\r |
| 222 | \r |
| 223 | t_addr lgp_parse_addr (DEVICE *dptr, char *cptr, char **tptr)\r |
| 224 | {\r |
| 225 | t_addr ad, ea;\r |
| 226 | \r |
| 227 | if ((dptr == sim_devices[0]) &&\r |
| 228 | ((sim_switches & SWMASK ('T')) ||\r |
| 229 | ((cpu_unit.flags & UNIT_TTSS_D) && !(sim_switches & SWMASK ('N'))))) {\r |
| 230 | ad = (t_addr) strtotv (cptr, tptr, 10);\r |
| 231 | if (((ad / 100) >= NTK_30) || ((ad % 100) >= NSC_30)) {\r |
| 232 | *tptr = cptr;\r |
| 233 | return 0;\r |
| 234 | }\r |
| 235 | ea = ((ad / 100) * NSC_30) | (ad % 100);\r |
| 236 | }\r |
| 237 | else ea = (t_addr) strtotv (cptr, tptr, dptr->aradix);\r |
| 238 | return ea;\r |
| 239 | }\r |
| 240 | \r |
| 241 | void lgp_vm_init (void)\r |
| 242 | {\r |
| 243 | sim_vm_fprint_addr = &lgp_fprint_addr;\r |
| 244 | sim_vm_parse_addr = &lgp_parse_addr;\r |
| 245 | return;\r |
| 246 | }\r |
| 247 | \r |
| 248 | /* Symbolic decode\r |
| 249 | \r |
| 250 | Inputs:\r |
| 251 | *of = output stream\r |
| 252 | addr = current PC\r |
| 253 | *val = pointer to data\r |
| 254 | *uptr = pointer to unit \r |
| 255 | sw = switches\r |
| 256 | Outputs:\r |
| 257 | return = status code\r |
| 258 | */\r |
| 259 | \r |
| 260 | t_stat fprint_sym (FILE *of, t_addr addr, t_value *val,\r |
| 261 | UNIT *uptr, int32 sw)\r |
| 262 | {\r |
| 263 | int32 i, c;\r |
| 264 | uint32 inst, op, ea;\r |
| 265 | \r |
| 266 | inst = val[0];\r |
| 267 | if (sw & SWMASK ('A')) { /* alphabetic? */\r |
| 268 | if ((uptr == NULL) || !(uptr->flags & UNIT_ATT)) return SCPE_ARG;\r |
| 269 | if (uptr->flags & UNIT_FLEX) { /* Flex file? */\r |
| 270 | c = flex_to_ascii[inst]; /* get ASCII equiv */\r |
| 271 | if (c <= 0) return SCPE_ARG;\r |
| 272 | }\r |
| 273 | else c = inst & 0x7F; /* ASCII file */\r |
| 274 | fputc (c, of);\r |
| 275 | return SCPE_OK;\r |
| 276 | }\r |
| 277 | \r |
| 278 | if (uptr && (uptr != &cpu_unit)) return SCPE_ARG; /* must be CPU */\r |
| 279 | if ((sw & SWMASK ('M')) && /* symbolic decode? */\r |
| 280 | ((inst & ~(SIGN|I_OP|I_EA)) == 0)) {\r |
| 281 | op = I_GETOP (inst);\r |
| 282 | ea = I_GETEA (inst);\r |
| 283 | if (inst & SIGN) fputc ('-', of);\r |
| 284 | fprintf (of, "%c ", opcode[op]);\r |
| 285 | lgp_fprint_addr (of, sim_devices[0], ea);\r |
| 286 | return SCPE_OK;\r |
| 287 | }\r |
| 288 | \r |
| 289 | if ((sw & SWMASK ('L')) || /* LGP hex? */\r |
| 290 | ((cpu_unit.flags & UNIT_LGPH_D) && !(sw & SWMASK ('H')))) {\r |
| 291 | for (i = 0; i < 8; i++) {\r |
| 292 | c = (inst >> (4 * (7 - i))) & 0xF;\r |
| 293 | fputc (hex_decode[c], of);\r |
| 294 | }\r |
| 295 | return SCPE_OK;\r |
| 296 | }\r |
| 297 | return SCPE_ARG;\r |
| 298 | }\r |
| 299 | \r |
| 300 | /* Symbolic input\r |
| 301 | \r |
| 302 | Inputs:\r |
| 303 | *cptr = pointer to input string\r |
| 304 | addr = current PC\r |
| 305 | *uptr = pointer to unit\r |
| 306 | *val = pointer to output values\r |
| 307 | sw = switches\r |
| 308 | Outputs:\r |
| 309 | status = error status\r |
| 310 | */\r |
| 311 | \r |
| 312 | t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw)\r |
| 313 | {\r |
| 314 | int32 i, c;\r |
| 315 | char *tptr;\r |
| 316 | \r |
| 317 | while (isspace (*cptr)) cptr++; /* absorb spaces */\r |
| 318 | if ((sw & SWMASK ('A')) || ((*cptr == '\'') && cptr++)) {\r |
| 319 | if ((uptr == NULL) || !(uptr->flags & UNIT_ATT)) return SCPE_ARG;\r |
| 320 | if (uptr->flags & UNIT_FLEX) { /* Flex file? */ \r |
| 321 | c = ascii_to_flex[*cptr & 0x7F]; /* get Flex equiv */\r |
| 322 | if (c < 0) return SCPE_ARG;\r |
| 323 | val[0] = ((c >> 1) | (c << 5)) & 0x3F; /* transpose */\r |
| 324 | }\r |
| 325 | else val[0] = *cptr & 0x7F; /* ASCII file */\r |
| 326 | return SCPE_OK;\r |
| 327 | } \r |
| 328 | \r |
| 329 | if (uptr && (uptr != &cpu_unit)) return SCPE_ARG; /* must be CPU */\r |
| 330 | if (!parse_sym_m (cptr, val, sw)) return SCPE_OK; /* symbolic parse? */\r |
| 331 | if ((sw & SWMASK ('L')) || /* LGP hex? */\r |
| 332 | ((cpu_unit.flags & UNIT_LGPH_D) && !(sw & SWMASK ('H')))) {\r |
| 333 | val[0] = 0;\r |
| 334 | while (isspace (*cptr)) cptr++; /* absorb spaces */\r |
| 335 | for (i = 0; i < 8; i++) {\r |
| 336 | c = *cptr++; /* get char */\r |
| 337 | if (c == 0) return SCPE_OK;\r |
| 338 | if (islower (c)) c = toupper (c);\r |
| 339 | if (tptr = strchr (hex_decode, c))\r |
| 340 | val[0] = (val[0] << 4) | (tptr - hex_decode);\r |
| 341 | else return SCPE_ARG;\r |
| 342 | }\r |
| 343 | if (*cptr == 0) return SCPE_OK;\r |
| 344 | }\r |
| 345 | return SCPE_ARG;\r |
| 346 | }\r |
| 347 | \r |
| 348 | /* Instruction parse */\r |
| 349 | \r |
| 350 | t_stat parse_sym_m (char *cptr, t_value *val, int32 sw)\r |
| 351 | {\r |
| 352 | uint32 ea, sgn;\r |
| 353 | char *tptr, gbuf[CBUFSIZE];\r |
| 354 | \r |
| 355 | if (*cptr == '-') {\r |
| 356 | cptr++;\r |
| 357 | sgn = SIGN;\r |
| 358 | }\r |
| 359 | else sgn = 0;\r |
| 360 | cptr = get_glyph (cptr, gbuf, 0); /* get opcode */\r |
| 361 | if (gbuf[1] != 0) return SCPE_ARG;\r |
| 362 | if (tptr = strchr (opcode, gbuf[0]))\r |
| 363 | val[0] = ((tptr - opcode) << I_V_OP) | sgn; /* merge opcode */\r |
| 364 | else return SCPE_ARG;\r |
| 365 | cptr = get_glyph (cptr, gbuf, 0); /* get address */\r |
| 366 | ea = lgp_parse_addr (sim_devices[0], gbuf, &tptr);\r |
| 367 | if ((tptr == gbuf) || (*tptr != 0) || (ea > AMASK))\r |
| 368 | return SCPE_ARG;\r |
| 369 | val[0] = val[0] | (ea << I_V_EA); /* merge address */\r |
| 370 | if (*cptr != 0) return SCPE_2MARG;\r |
| 371 | return SCPE_OK;\r |
| 372 | }\r |