Commit | Line | Data |
---|---|---|
196ba1fc PH |
1 | /* ibm1130_sys.c: IBM 1130 simulator interface\r |
2 | \r | |
3 | Based on PDP-11 simulator written by Robert M Supnik\r | |
4 | \r | |
5 | Revision History\r | |
6 | 0.27 2005Mar08 - Added sca device\r | |
7 | 0.26 2002Apr24 - Added !BREAK in card deck file to stop simulator\r | |
8 | 0.25 2002Apr18 - Fixed some card reader problems. It starts the reader\r | |
9 | properly if you attach a deck while it's waiting to a read.\r | |
10 | 0.24 2002Mar27 - Fixed BOSC bug; BOSC works in short instructions too\r | |
11 | 0.23 2002Feb26 - Added @decklist feature for ATTACH CR.\r | |
12 | 0.22 2002Feb26 - Replaced "strupr" with "upcase" for compatibility.\r | |
13 | 0.21 2002Feb25 - Some compiler compatibiity changes, couple of compiler-detected\r | |
14 | bugs\r | |
15 | 0.01 2001Jul31 - Derived from pdp11_sys.c, which carries this disclaimer:\r | |
16 | \r | |
17 | * (C) Copyright 2002, Brian Knittel.\r | |
18 | * You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN\r | |
19 | * RISK basis, there is no warranty of fitness for any purpose, and the rest of the\r | |
20 | * usual yada-yada. Please keep this notice and the copyright in any distributions\r | |
21 | * or modifications.\r | |
22 | *\r | |
23 | * This is not a supported product, but I welcome bug reports and fixes.\r | |
24 | * Mail to simh@ibm1130.org\r | |
25 | */\r | |
26 | \r | |
27 | #include "ibm1130_defs.h"\r | |
28 | #include <ctype.h>\r | |
29 | #include <stdarg.h>\r | |
30 | \r | |
31 | extern DEVICE cpu_dev, console_dev, dsk_dev, cr_dev, cp_dev, ptr_dev, ptp_dev, t2741_dev;\r | |
32 | extern DEVICE tti_dev, tto_dev, prt_dev, log_dev, sca_dev;\r | |
33 | extern DEVICE gdu_dev, console_dev, plot_dev;\r | |
34 | \r | |
35 | extern UNIT cpu_unit;\r | |
36 | extern REG cpu_reg[];\r | |
37 | extern int32 saved_PC;\r | |
38 | \r | |
39 | /* SCP data structures and interface routines\r | |
40 | \r | |
41 | sim_name simulator name string\r | |
42 | sim_PC pointer to saved PC register descriptor\r | |
43 | sim_emax number of words for examine\r | |
44 | sim_devices array of pointers to simulated devices\r | |
45 | sim_stop_messages array of pointers to stop messages\r | |
46 | sim_load binary loader\r | |
47 | */\r | |
48 | \r | |
49 | char sim_name[] = "IBM 1130";\r | |
50 | \r | |
51 | REG *sim_PC = &cpu_reg[0];\r | |
52 | \r | |
53 | int32 sim_emax = 4;\r | |
54 | \r | |
55 | DEVICE *sim_devices[] = {\r | |
56 | &cpu_dev, /* the cpu */\r | |
57 | &dsk_dev, /* disk drive(s) */\r | |
58 | &cr_dev, /* card reader/punch */\r | |
59 | &cp_dev,\r | |
60 | &tti_dev, /* console keyboard, selectric printer */\r | |
61 | &tto_dev,\r | |
62 | &prt_dev, /* 1132 printer */\r | |
63 | &ptr_dev, /* 1134 paper tape reader */\r | |
64 | &ptp_dev, /* 1055 paper tape punch */\r | |
65 | &sca_dev, /* Synchronous communications adapter option */\r | |
66 | &console_dev, /* console display (windows GUI) */\r | |
67 | &gdu_dev, /* 2250 display */\r | |
68 | &t2741_dev, /* nonstandard serial interface used by APL\1130 */\r | |
69 | &plot_dev, /* plotter device, in ibm1130_plot.c */\r | |
70 | NULL\r | |
71 | };\r | |
72 | \r | |
73 | const char *sim_stop_messages[] = {\r | |
74 | "Unknown error",\r | |
75 | "Wait",\r | |
76 | "Invalid command", \r | |
77 | "Simulator breakpoint",\r | |
78 | "Use of incomplete simulator function",\r | |
79 | "Power off",\r | |
80 | "!BREAK in card deck file",\r | |
81 | "Phase load break",\r | |
82 | "Program has run amok",\r | |
83 | "Run time limit exceeded",\r | |
84 | "Immediate Stop key requested",\r | |
85 | "Simulator break key pressed",\r | |
86 | "Simulator step count expired",\r | |
87 | "Simulator IO error",\r | |
88 | };\r | |
89 | \r | |
90 | /* Loader. IPL is normally performed by card reader (boot command). This function\r | |
91 | * loads hex data from a file for testing purposes. The format is:\r | |
92 | *\r | |
93 | * blank lines or lines starting with ; / or # are ignored as comments\r | |
94 | *\r | |
95 | * @XXXX set load addresss to hex value XXXX\r | |
96 | * XXXX store hex word value XXXX at current load address and increment address\r | |
97 | * ...\r | |
98 | * =XXXX set IAR to hex value XXXX\r | |
99 | * ZXXXX zero XXXX words and increment load address\r | |
100 | * SXXXX set console entry switches to XXXX. This lets a program specify the\r | |
101 | * default value for the toggle switches.\r | |
102 | *\r | |
103 | * Multiple @ and data sections may be entered. If more than one = or S value is specified\r | |
104 | * the last one wins.\r | |
105 | *\r | |
106 | * Note: the load address @XXXX and data values XXXX can be followed by the letter\r | |
107 | * R to indicate that the values are relocatable addresses. This is ignored in this loader,\r | |
108 | * but the asm1130 cross assembler may put them there.\r | |
109 | */\r | |
110 | \r | |
111 | t_stat my_load (FILE *fileref, char *cptr, char *fnam)\r | |
112 | {\r | |
113 | char line[150], *c;\r | |
114 | int iaddr = -1, runaddr = -1, val, nwords;\r | |
115 | \r | |
116 | while (fgets(line, sizeof(line), fileref) != NULL) {\r | |
117 | for (c = line; *c && *c <= ' '; c++) /* find first nonblank */\r | |
118 | ;\r | |
119 | \r | |
120 | if (*c == '\0' || *c == '#' || *c == '/' || *c == ';')\r | |
121 | continue; /* empty line or comment */\r | |
122 | \r | |
123 | if (*c == '@') { /* set load address */\r | |
124 | if (sscanf(c+1, "%x", &iaddr) != 1)\r | |
125 | return SCPE_FMT;\r | |
126 | }\r | |
127 | else if (*c == '=') {\r | |
128 | if (sscanf(c+1, "%x", &runaddr) != 1)\r | |
129 | return SCPE_FMT;\r | |
130 | }\r | |
131 | else if (*c == 's' || *c == 'S') {\r | |
132 | if (sscanf(c+1, "%x", &val) != 1)\r | |
133 | return SCPE_FMT;\r | |
134 | \r | |
135 | CES = val & 0xFFFF; /*preload console entry switches */\r | |
136 | }\r | |
137 | else if (*c == 'z' || *c == 'Z') {\r | |
138 | if (sscanf(c+1, "%x", &nwords) != 1)\r | |
139 | return SCPE_FMT;\r | |
140 | \r | |
141 | if (iaddr == -1)\r | |
142 | return SCPE_FMT;\r | |
143 | \r | |
144 | while (--nwords >= 0) {\r | |
145 | WriteW(iaddr, 0);\r | |
146 | iaddr++;\r | |
147 | }\r | |
148 | }\r | |
149 | else if (strchr("0123456789abcdefABCDEF", *c) != NULL) {\r | |
150 | if (sscanf(c, "%x", &val) != 1)\r | |
151 | return SCPE_FMT;\r | |
152 | \r | |
153 | if (iaddr == -1)\r | |
154 | return SCPE_FMT;\r | |
155 | \r | |
156 | WriteW(iaddr, val); /*store data */\r | |
157 | iaddr++;\r | |
158 | }\r | |
159 | else\r | |
160 | return SCPE_FMT; /*unexpected data */\r | |
161 | }\r | |
162 | \r | |
163 | if (runaddr != -1)\r | |
164 | IAR = runaddr;\r | |
165 | \r | |
166 | return SCPE_OK;\r | |
167 | }\r | |
168 | \r | |
169 | t_stat my_save (FILE *fileref, char *cptr, char *fnam)\r | |
170 | {\r | |
171 | int iaddr, nzeroes = 0, nwords = (int) (MEMSIZE/2), val;\r | |
172 | \r | |
173 | fprintf(fileref, "=%04x\r\n", IAR);\r | |
174 | fprintf(fileref, "@0000\r\n");\r | |
175 | for (iaddr = 0; iaddr < nwords; iaddr++) {\r | |
176 | val = ReadW(iaddr);\r | |
177 | if (val == 0) /*queue up zeroes */\r | |
178 | nzeroes++;\r | |
179 | else {\r | |
180 | if (nzeroes >= 4) { /*spit out a Z directive */\r | |
181 | fprintf(fileref, "Z%04x\r\n", nzeroes);\r | |
182 | nzeroes = 0;\r | |
183 | }\r | |
184 | else { /*write queued zeroes literally */\r | |
185 | while (nzeroes > 0) {\r | |
186 | fprintf(fileref, " 0000\r\n");\r | |
187 | nzeroes--;\r | |
188 | }\r | |
189 | }\r | |
190 | fprintf(fileref, " %04x\r\n", val);\r | |
191 | }\r | |
192 | }\r | |
193 | if (nzeroes >= 4) { /*emit any queued zeroes */\r | |
194 | fprintf(fileref, "Z%04x\r\n", nzeroes);\r | |
195 | nzeroes = 0;\r | |
196 | }\r | |
197 | else {\r | |
198 | while (nzeroes > 0) {\r | |
199 | fprintf(fileref, " 0000\r\n");\r | |
200 | nzeroes--;\r | |
201 | }\r | |
202 | }\r | |
203 | \r | |
204 | return SCPE_OK;\r | |
205 | }\r | |
206 | \r | |
207 | t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag)\r | |
208 | {\r | |
209 | if (flag)\r | |
210 | return my_save(fileref, cptr, fnam);\r | |
211 | else\r | |
212 | return my_load(fileref, cptr, fnam);\r | |
213 | }\r | |
214 | \r | |
215 | /* Specifier decode\r | |
216 | \r | |
217 | Inputs:\r | |
218 | *of = output stream\r | |
219 | addr = current PC\r | |
220 | spec = specifier\r | |
221 | nval = next word\r | |
222 | flag = TRUE if decoding for CPU\r | |
223 | iflag = TRUE if decoding integer instruction\r | |
224 | Outputs:\r | |
225 | count = -number of extra words retired\r | |
226 | */\r | |
227 | \r | |
228 | /* Symbolic decode\r | |
229 | \r | |
230 | Inputs:\r | |
231 | *of = output stream\r | |
232 | addr = current PC\r | |
233 | *val = values to decode\r | |
234 | *uptr = pointer to unit\r | |
235 | sw = switches\r | |
236 | Outputs:\r | |
237 | return = if >= 0, error code\r | |
238 | if < 0, number of extra words retired\r | |
239 | */\r | |
240 | \r | |
241 | static char *opcode[] = {\r | |
242 | "?00 ", "XIO ", "SLA ", "SRA ",\r | |
243 | "LDS ", "STS ", "WAIT", "?07 ",\r | |
244 | "BSI ", "BSC ", "?0A ", "?0B ",\r | |
245 | "LDX ", "STX ", "MDX ", "?0F ",\r | |
246 | "A ", "AD ", "S ", "SD ",\r | |
247 | "M ", "D ", "?16 ", "?17 ",\r | |
248 | "LD ", "LDD ", "STO ", "STD ",\r | |
249 | "AND ", "OR ", "EOR ", "?1F ",\r | |
250 | };\r | |
251 | \r | |
252 | static char relative[] = { /*true if short mode displacements are IAR relative */\r | |
253 | FALSE, TRUE, FALSE, FALSE,\r | |
254 | FALSE, TRUE, FALSE, FALSE,\r | |
255 | TRUE, FALSE, FALSE, FALSE,\r | |
256 | TRUE, TRUE, TRUE, FALSE,\r | |
257 | TRUE, TRUE, TRUE, TRUE,\r | |
258 | TRUE, TRUE, FALSE, FALSE,\r | |
259 | TRUE, TRUE, TRUE, TRUE,\r | |
260 | TRUE, TRUE, TRUE, FALSE\r | |
261 | };\r | |
262 | \r | |
263 | static char *lsopcode[] = {"SLA ", "SLCA ", "SLT ", "SLC "};\r | |
264 | static char *rsopcode[] = {"SRA ", "?188 ", "SRT ", "RTE "};\r | |
265 | static char tagc[] = " 123";\r | |
266 | \r | |
267 | static int ascii_to_ebcdic_table[128] = \r | |
268 | {\r | |
269 | 0x00,0x01,0x02,0x03,0x37,0x2d,0x2e,0x2f, 0x16,0x05,0x25,0x0b,0x0c,0x0d,0x0e,0x0f,\r | |
270 | 0x10,0x11,0x12,0x13,0x3c,0x3d,0x32,0x26, 0x18,0x19,0x3f,0x27,0x1c,0x1d,0x1e,0x1f,\r | |
271 | 0x40,0x5a,0x7f,0x7b,0x5b,0x6c,0x50,0x7d, 0x4d,0x5d,0x5c,0x4e,0x6b,0x60,0x4b,0x61,\r | |
272 | 0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7, 0xf8,0xf9,0x7a,0x5e,0x4c,0x7e,0x6e,0x6f,\r | |
273 | \r | |
274 | 0x7c,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7, 0xc8,0xc9,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,\r | |
275 | 0xd7,0xd8,0xd9,0xe2,0xe3,0xe4,0xe5,0xe6, 0xe7,0xe8,0xe9,0xba,0xe0,0xbb,0xb0,0x6d,\r | |
276 | 0x79,0x81,0x82,0x83,0x84,0x85,0x86,0x87, 0x88,0x89,0x91,0x92,0x93,0x94,0x95,0x96,\r | |
277 | 0x97,0x98,0x99,0xa2,0xa3,0xa4,0xa5,0xa6, 0xa7,0xa8,0xa9,0xc0,0x4f,0xd0,0xa1,0x07,\r | |
278 | };\r | |
279 | \r | |
280 | static int ebcdic_to_ascii (int ch)\r | |
281 | {\r | |
282 | int j;\r | |
283 | \r | |
284 | for (j = 32; j < 128; j++)\r | |
285 | if (ascii_to_ebcdic_table[j] == ch)\r | |
286 | return j;\r | |
287 | \r | |
288 | return '?';\r | |
289 | }\r | |
290 | \r | |
291 | t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw)\r | |
292 | {\r | |
293 | int32 cflag, ch, OP, F, TAG, INDIR, DSPLC, IR, eaddr;\r | |
294 | char *mnem, tst[12];\r | |
295 | \r | |
296 | cflag = (uptr == NULL) || (uptr == &cpu_unit);\r | |
297 | \r | |
298 | /* if (sw & SWMASK ('A')) { // ASCII? not useful\r | |
299 | fprintf (of, (c1 < 040)? "<%03o>": "%c", c1);\r | |
300 | return SCPE_OK;\r | |
301 | }\r | |
302 | */\r | |
303 | \r | |
304 | if (sw & SWMASK ('C')) /* character? not useful -- make it EBCDIC */\r | |
305 | sw |= SWMASK('E');\r | |
306 | \r | |
307 | if (sw & SWMASK ('E')) { /* EBCDIC! */\r | |
308 | ch = ebcdic_to_ascii((val[0] >> 8) & 0xFF); /* take high byte first */\r | |
309 | fprintf (of, (ch < ' ')? "<%03o>": "%c", ch);\r | |
310 | ch = ebcdic_to_ascii(val[0] & 0xFF);\r | |
311 | fprintf (of, (ch < ' ')? "<%03o>": "%c", ch);\r | |
312 | return SCPE_OK;\r | |
313 | }\r | |
314 | \r | |
315 | if (sw & SWMASK ('H')) { /* HOLLERITH! now THIS is useful! */\r | |
316 | ch = hollerith_to_ascii((int16) val[0]);\r | |
317 | fprintf (of, (ch < ' ')? "<%03o>": "%c", ch);\r | |
318 | return SCPE_OK;\r | |
319 | }\r | |
320 | \r | |
321 | if (! (sw & SWMASK ('M')))\r | |
322 | return SCPE_ARG;\r | |
323 | \r | |
324 | IR = val[0];\r | |
325 | OP = (IR >> 11) & 0x1F; /* opcode */\r | |
326 | F = IR & 0x0400; /* format bit: 1 = long instr */\r | |
327 | TAG = IR & 0x0300; /* tag bits: index reg select */\r | |
328 | if (TAG)\r | |
329 | TAG >>= 8;\r | |
330 | \r | |
331 | if (F) { /* long instruction, ASSUME it's valid (have to decrement IAR if not) */\r | |
332 | INDIR = IR & 0x0080; /* indirect bit */\r | |
333 | DSPLC = IR & 0x007F; /* displacement or modifier */\r | |
334 | if (DSPLC & 0x0040)\r | |
335 | DSPLC |= ~ 0x7F; /* sign extend */\r | |
336 | \r | |
337 | eaddr = val[1]; /* get reference address */\r | |
338 | }\r | |
339 | else { /* short instruction, use displacement */\r | |
340 | INDIR = 0; /* never indirect */\r | |
341 | DSPLC = IR & 0x00FF; /* get displacement */\r | |
342 | if (DSPLC & 0x0080)\r | |
343 | DSPLC |= ~ 0xFF;\r | |
344 | \r | |
345 | eaddr = DSPLC;\r | |
346 | if (relative[OP] && ! TAG)\r | |
347 | eaddr += addr+1; /* turn displacement into address */\r | |
348 | }\r | |
349 | \r | |
350 | mnem = opcode[OP]; /* get mnemonic */\r | |
351 | if (OP == 0x02) { /* left shifts are special */\r | |
352 | mnem = lsopcode[(DSPLC >> 6) & 0x0003];\r | |
353 | DSPLC &= 0x003F;\r | |
354 | eaddr = DSPLC;\r | |
355 | }\r | |
356 | else if (OP == 0x03) { /* right shifts too */\r | |
357 | mnem = rsopcode[(DSPLC >> 6) & 0x0003];\r | |
358 | DSPLC &= 0x003F;\r | |
359 | eaddr = DSPLC;\r | |
360 | }\r | |
361 | else if ((OP == 0x08 && F)|| OP == 0x09) { /* BSI L and BSC any */\r | |
362 | if (OP == 0x09 && (IR & 0x40))\r | |
363 | mnem = "BOSC";\r | |
364 | \r | |
365 | tst[0] = '\0';\r | |
366 | if (DSPLC & 0x20) strcat(tst, "Z");\r | |
367 | if (DSPLC & 0x10) strcat(tst, "-");\r | |
368 | if (DSPLC & 0x08) strcat(tst, "+");\r | |
369 | if (DSPLC & 0x04) strcat(tst, "E");\r | |
370 | if (DSPLC & 0x02) strcat(tst, "C");\r | |
371 | if (DSPLC & 0x01) strcat(tst, "O");\r | |
372 | \r | |
373 | if (F) {\r | |
374 | fprintf(of, "%04x %s %c%c %s,%04x ", IR & 0xFFFF, mnem, F ? (INDIR ? 'I' : 'L') : ' ', tagc[TAG], tst, eaddr & 0xFFFF);\r | |
375 | return -1;\r | |
376 | }\r | |
377 | fprintf(of, "%04x %s %c%c %s ", IR & 0xFFFF, mnem, F ? (INDIR ? 'I' : 'L') : ' ', tagc[TAG], tst);\r | |
378 | return SCPE_OK;\r | |
379 | }\r | |
380 | else if (OP == 0x0e && TAG == 0) { /* MDX with no tag => MDM or jump */\r | |
381 | if (F) {\r | |
382 | fprintf(of, "%04x %s %c%c %04x,%x (%d) ", IR & 0xFFFF, "MDM ", (INDIR ? 'I' : 'L'), tagc[TAG], eaddr & 0xFFFF, DSPLC & 0xFFFF, DSPLC);\r | |
383 | return -1;\r | |
384 | }\r | |
385 | mnem = "JMP ";\r | |
386 | }\r | |
387 | \r | |
388 | fprintf(of, "%04x %s %c%c %04x ", IR & 0xFFFF, mnem, F ? (INDIR ? 'I' : 'L') : ' ', tagc[TAG], eaddr & 0xFFFF);\r | |
389 | return F ? -1 : SCPE_OK; /* inform how many words we read */\r | |
390 | }\r | |
391 | \r | |
392 | int32 get_reg (char *cptr, const char *strings[], char mchar)\r | |
393 | {\r | |
394 | return -1;\r | |
395 | }\r | |
396 | \r | |
397 | /* Number or memory address\r | |
398 | \r | |
399 | Inputs:\r | |
400 | *cptr = pointer to input string\r | |
401 | *dptr = pointer to output displacement\r | |
402 | *pflag = pointer to accumulating flags\r | |
403 | Outputs:\r | |
404 | cptr = pointer to next character in input string\r | |
405 | NULL if parsing error\r | |
406 | \r | |
407 | Flags: 0 (no result), A_NUM (number), A_REL (relative)\r | |
408 | */\r | |
409 | \r | |
410 | char *get_addr (char *cptr, int32 *dptr, int32 *pflag)\r | |
411 | {\r | |
412 | return 0;\r | |
413 | }\r | |
414 | \f\r | |
415 | /* Specifier decode\r | |
416 | \r | |
417 | Inputs:\r | |
418 | *cptr = pointer to input string\r | |
419 | addr = current PC\r | |
420 | n1 = 0 if no extra word used\r | |
421 | -1 if extra word used in prior decode\r | |
422 | *sptr = pointer to output specifier\r | |
423 | *dptr = pointer to output displacement\r | |
424 | cflag = true if parsing for the CPU\r | |
425 | iflag = true if integer specifier\r | |
426 | Outputs:\r | |
427 | status = = -1 extra word decoded\r | |
428 | = 0 ok\r | |
429 | = +1 error\r | |
430 | */\r | |
431 | \r | |
432 | t_stat get_spec (char *cptr, t_addr addr, int32 n1, int32 *sptr, t_value *dptr,\r | |
433 | int32 cflag, int32 iflag)\r | |
434 | {\r | |
435 | return -1;\r | |
436 | }\r | |
437 | \r | |
438 | /* Symbolic input\r | |
439 | \r | |
440 | Inputs:\r | |
441 | *cptr = pointer to input string\r | |
442 | addr = current PC\r | |
443 | *uptr = pointer to unit\r | |
444 | *val = pointer to output values\r | |
445 | sw = switches\r | |
446 | Outputs:\r | |
447 | status = > 0 error code\r | |
448 | <= 0 -number of extra words\r | |
449 | */\r | |
450 | \r | |
451 | t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw)\r | |
452 | {\r | |
453 | return SCPE_ARG;\r | |
454 | }\r | |
455 | \r | |
456 | #ifndef _WIN32\r | |
457 | \r | |
458 | int strnicmp (const char *a, const char *b, int n)\r | |
459 | {\r | |
460 | int ca, cb;\r | |
461 | \r | |
462 | for (;;) {\r | |
463 | if (--n < 0) /* still equal after n characters? quit now */\r | |
464 | return 0;\r | |
465 | \r | |
466 | if ((ca = *a) == 0) /* get character, stop on null terminator */\r | |
467 | return *b ? -1 : 0;\r | |
468 | \r | |
469 | if (ca >= 'a' && ca <= 'z') /* fold lowercase to uppercase */\r | |
470 | ca -= 32;\r | |
471 | \r | |
472 | cb = *b;\r | |
473 | if (cb >= 'a' && cb <= 'z')\r | |
474 | cb -= 32;\r | |
475 | \r | |
476 | if ((ca -= cb) != 0) /* if different, return comparison */\r | |
477 | return ca;\r | |
478 | \r | |
479 | a++, b++;\r | |
480 | }\r | |
481 | }\r | |
482 | \r | |
483 | int strcmpi (const char *a, const char *b)\r | |
484 | {\r | |
485 | int ca, cb;\r | |
486 | \r | |
487 | for (;;) {\r | |
488 | if ((ca = *a) == 0) /* get character, stop on null terminator */\r | |
489 | return *b ? -1 : 0;\r | |
490 | \r | |
491 | if (ca >= 'a' && ca <= 'z') /* fold lowercase to uppercase */\r | |
492 | ca -= 32;\r | |
493 | \r | |
494 | cb = *b;\r | |
495 | if (cb >= 'a' && cb <= 'z')\r | |
496 | cb -= 32;\r | |
497 | \r | |
498 | if ((ca -= cb) != 0) /* if different, return comparison */\r | |
499 | return ca;\r | |
500 | \r | |
501 | a++, b++;\r | |
502 | }\r | |
503 | }\r | |
504 | \r | |
505 | #endif\r |