| 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 |