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