Commit | Line | Data |
---|---|---|
196ba1fc PH |
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 |