RESTRICTION: The FP15 and XVM features of the PDP-15 are only partially debugged. Do NOT enable these features for normal operations. 1. New Features in 3.0-1 1.1 PDP-1 - Added block loader format support to LOAD. - Changed BOOT PTR to allow loading of all of the first bank of memory. 1.2 PDP-18b Family - Added PDP-4 EAE support. - Added PDP-15 FP15 support. - Added PDP-15 XVM support. - Added PDP-15 "re-entrancy ECO". - Added PDP-7, PDP-9, PDP-15 hardware RIM loader support in BOOT PTR. 2. Bugs Fixed in 3.0-1 2.1 PDP-11/VAX - Fixed bug in user disk size (found by Chaskiel M Grundman). 2.2 PDP-1 - Updated CPU, line printer, standard devices to detect indefinite I/O wait. - Fixed incorrect logical, missing activate, break in drum simulator. - Fixed bugs in instruction decoding, overprinting for line printer. 2.3 PDP-10 - Fixed bug in RP read header. 2.4 PDP-18b Family - Fixed bug in PDP-4 line printer overprinting. - Fixed bug in PDP-15 memory protect/skip interaction. - Fixed bug in RF set size routine. - Increased PTP TIME for PDP-15 operating systems. 2.5 PDP-8 - Fixed bug in DF, RF set size routine. 2.6 Nova - Fixed bug in DSK set size routine. 2.7 1401 - Revised fetch to model hardware more closely. 2.8 Ibm1130 - Fixed bugs found by APL 1130. 2.9 Altairz80 - Fixed bug in real-time clock on Windows host. 2.10 HP2100 -- Fixed DR drum sizes. -- Fixed DR variable capacity interaction with SAVE/RESTORE. 3. New Features in 3.0 vs prior releases 3.1 SCP and Libraries - Added ASSIGN/DEASSIGN (logical name) commands. - Changed RESTORE to unconditionally detach files. - Added E11 and TPC format support to magtape library. - Fixed bug in SHOW CONNECTIONS. - Added USE_ADDR64 support 3.2 All magtapes - Magtapes support SIMH format, E11 format, and TPC format (read only). - SET <tape_unit> FORMAT=format sets the specified tape unit's format. - SHOW <tape_unit> FORMAT displays the specified tape unit's format. - Tape format can also be set as part of the ATTACH command, using the -F switch. 3.3 VAX - VAX can be compiled without USE_INT64. - If compiled with USE_INT64 and USE_ADDR64, RQ and TQ controllers support files > 2GB. - VAX ROM has speed control (SET ROM DELAY/NODELAY). 4. Bugs Fixed in 3.0 vs prior releases 4.1 VAX - Fixed CVTfi bug: integer overflow not set if exponent out of range - Fixed EMODx bugs: o First and second operands reversed o Separated fraction received wrong exponent o Overflow calculation on separated integer incorrect o Fraction not set to zero if exponent out of range - Fixed interval timer and ROM access to pass power-up self-test even on very fast host processors (fixes from Mark Pizzolato). 4.2 1401 - Fixed mnemonic, instruction lengths, and reverse scan length check bug for MCS. - Fixed MCE bug, BS off by 1 if zero suppress. - Fixed chaining bug, D lost if return to SCP. - Fixed H branch, branch occurs after continue. - Added check for invalid 8 character MCW, LCA. - Fixed magtape load-mode end of record response. 4.3 Nova - Fixed DSK variable size interaction with restore. 4.4 PDP-1 - Fixed DT variable size interaction with restore. 4.5 PDP-11 - Fixed DT variable size interaction with restore. - Fixed bug in MMR1 update (found by Tim Stark). - Added XQ features and fixed bugs: o Corrected XQ interrupts on IE state transition (code by Tom Evans). o Added XQ interrupt clear on soft reset. o Removed XQ interrupt when setting XL or RL (multiple people). o Added SET/SHOW XQ STATS. o Added SHOW XQ FILTERS. o Added ability to split received packet into multiple buffers. o Added explicit runt and giant packet processing. 4.6 PDP-18B - Fixed DT, RF variable size interaction with restore. - Fixed MT bug in MTTR. 4.7 PDP-8 - Fixed DT, DF, RF, RX variable size interaction with restore. - Fixed MT bug in SKTR. 4.8 HP2100 - Fixed bug in DP (13210A controller only), DQ read status. - Fixed bug in DP, DQ seek complete. 4.9 GRI - Fixed bug in SC queue pointer management.
495 lines
13 KiB
C
495 lines
13 KiB
C
/* ibm1130_sys.c: IBM 1130 simulator interface
|
||
|
||
Based on PDP-11 simulator written by Robert M Supnik
|
||
|
||
Revision History
|
||
0.26 2002Apr24 - Added !BREAK in card deck file to stop simulator
|
||
0.25 2002Apr18 - Fixed some card reader problems. It starts the reader
|
||
properly if you attach a deck while it's waiting to a read.
|
||
0.24 2002Mar27 - Fixed BOSC bug; BOSC works in short instructions too
|
||
0.23 2002Feb26 - Added @decklist feature for ATTACH CR.
|
||
0.22 2002Feb26 - Replaced "strupr" with "upcase" for compatibility.
|
||
0.21 2002Feb25 - Some compiler compatibiity changes, couple of compiler-detected
|
||
bugs
|
||
0.01 2001Jul31 - Derived from pdp11_sys.c, which carries this disclaimer:
|
||
|
||
* (C) Copyright 2002, Brian Knittel.
|
||
* You may freely use this program, but: it offered strictly on an AS-IS, AT YOUR OWN
|
||
* RISK basis, there is no warranty of fitness for any purpose, and the rest of the
|
||
* usual yada-yada. Please keep this notice and the copyright in any distributions
|
||
* or modifications.
|
||
*
|
||
* This is not a supported product, but I welcome bug reports and fixes.
|
||
* Mail to simh@ibm1130.org
|
||
*/
|
||
|
||
#include "ibm1130_defs.h"
|
||
#include <ctype.h>
|
||
#include <stdarg.h>
|
||
|
||
extern DEVICE cpu_dev, console_dev, dsk_dev, cr_dev, cp_dev;
|
||
extern DEVICE tti_dev, tto_dev, prt_dev, log_dev;
|
||
extern DEVICE gdu_dev, console_dev;
|
||
|
||
extern UNIT cpu_unit;
|
||
extern REG cpu_reg[];
|
||
extern int32 saved_PC;
|
||
|
||
/* SCP data structures and interface routines
|
||
|
||
sim_name simulator name string
|
||
sim_PC pointer to saved PC register descriptor
|
||
sim_emax number of words for examine
|
||
sim_devices array of pointers to simulated devices
|
||
sim_stop_messages array of pointers to stop messages
|
||
sim_load binary loader
|
||
*/
|
||
|
||
char sim_name[] = "IBM 1130";
|
||
char sim_version[] = "V0.30";
|
||
|
||
REG *sim_PC = &cpu_reg[0];
|
||
|
||
int32 sim_emax = 4;
|
||
|
||
DEVICE *sim_devices[] = {
|
||
&cpu_dev, /* the cpu */
|
||
&dsk_dev, /* disk drive(s) */
|
||
&cr_dev, /* card reader/punch */
|
||
&cp_dev,
|
||
&tti_dev, /* console keyboard, selectric printer */
|
||
&tto_dev,
|
||
&prt_dev, /* 1132 printer */
|
||
&console_dev, /* console display (windows GUI) */
|
||
&gdu_dev, /* 2250 display */
|
||
NULL
|
||
};
|
||
|
||
const char *sim_stop_messages[] = {
|
||
"Unknown error",
|
||
"Wait",
|
||
"Invalid command",
|
||
"Simulator breakpoint",
|
||
"Use of incomplete simulator function",
|
||
"Power off",
|
||
"!BREAK in card deck file",
|
||
"Phase load break",
|
||
"Program has run amok",
|
||
"Run time limit exceeded"
|
||
};
|
||
|
||
/* Loader. IPL is normally performed by card reader (boot command). This function
|
||
* loads hex data from a file for testing purposes. The format is:
|
||
*
|
||
* blank lines or lines starting with ; / or # are ignored as comments
|
||
*
|
||
* @XXXX set load addresss to hex value XXXX
|
||
* XXXX store hex word value XXXX at current load address and increment address
|
||
* ...
|
||
* =XXXX set IAR to hex value XXXX
|
||
* ZXXXX zero XXXX words and increment load address
|
||
* SXXXX set console entry switches to XXXX. This lets a program specify the
|
||
* default value for the toggle switches.
|
||
*
|
||
* Multiple @ and data sections may be entered. If more than one = or S value is specified
|
||
* the last one wins.
|
||
*
|
||
* Note: the load address @XXXX and data values XXXX can be followed by the letter
|
||
* R to indicate that the values are relocatable addresses. This is ignored in this loader,
|
||
* but the asm1130 cross assembler may put them there.
|
||
*/
|
||
|
||
t_stat my_load (FILE *fileref, char *cptr, char *fnam)
|
||
{
|
||
char line[150], *c;
|
||
int iaddr = -1, runaddr = -1, val, nwords;
|
||
|
||
while (fgets(line, sizeof(line), fileref) != NULL) {
|
||
for (c = line; *c && *c <= ' '; c++) // find first nonblank
|
||
;
|
||
|
||
if (*c == '\0' || *c == '#' || *c == '/' || *c == ';')
|
||
continue; // empty line or comment
|
||
|
||
if (*c == '@') { // set load address
|
||
if (sscanf(c+1, "%x", &iaddr) != 1)
|
||
return SCPE_FMT;
|
||
}
|
||
else if (*c == '=') {
|
||
if (sscanf(c+1, "%x", &runaddr) != 1)
|
||
return SCPE_FMT;
|
||
}
|
||
else if (*c == 's' || *c == 'S') {
|
||
if (sscanf(c+1, "%x", &val) != 1)
|
||
return SCPE_FMT;
|
||
|
||
CES = val & 0xFFFF; // preload console entry switches
|
||
}
|
||
else if (*c == 'z' || *c == 'Z') {
|
||
if (sscanf(c+1, "%x", &nwords) != 1)
|
||
return SCPE_FMT;
|
||
|
||
if (iaddr == -1)
|
||
return SCPE_FMT;
|
||
|
||
while (--nwords >= 0) {
|
||
WriteW(iaddr, 0);
|
||
iaddr++;
|
||
}
|
||
}
|
||
else if (strchr("0123456789abcdefABCDEF", *c) != NULL) {
|
||
if (sscanf(c, "%x", &val) != 1)
|
||
return SCPE_FMT;
|
||
|
||
if (iaddr == -1)
|
||
return SCPE_FMT;
|
||
|
||
WriteW(iaddr, val); // store data
|
||
iaddr++;
|
||
}
|
||
else
|
||
return SCPE_FMT; // unexpected data
|
||
}
|
||
|
||
if (runaddr != -1)
|
||
IAR = runaddr;
|
||
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat my_save (FILE *fileref, char *cptr, char *fnam)
|
||
{
|
||
int iaddr, nzeroes = 0, nwords = (int) (MEMSIZE/2), val;
|
||
|
||
fprintf(fileref, "=%04x\r\n", IAR);
|
||
fprintf(fileref, "@0000\r\n");
|
||
for (iaddr = 0; iaddr < nwords; iaddr++) {
|
||
val = ReadW(iaddr);
|
||
if (val == 0) // queue up zeroes
|
||
nzeroes++;
|
||
else {
|
||
if (nzeroes >= 4) { // spit out a Z directive
|
||
fprintf(fileref, "Z%04x\r\n", nzeroes);
|
||
nzeroes = 0;
|
||
}
|
||
else { // write queued zeroes literally
|
||
while (nzeroes > 0) {
|
||
fprintf(fileref, " 0000\r\n");
|
||
nzeroes--;
|
||
}
|
||
}
|
||
fprintf(fileref, " %04x\r\n", val);
|
||
}
|
||
}
|
||
if (nzeroes >= 4) { // emit any queued zeroes
|
||
fprintf(fileref, "Z%04x\r\n", nzeroes);
|
||
nzeroes = 0;
|
||
}
|
||
else {
|
||
while (nzeroes > 0) {
|
||
fprintf(fileref, " 0000\r\n");
|
||
nzeroes--;
|
||
}
|
||
}
|
||
|
||
return SCPE_OK;
|
||
}
|
||
|
||
t_stat sim_load (FILE *fileref, char *cptr, char *fnam, int flag)
|
||
{
|
||
if (flag)
|
||
return my_save(fileref, cptr, fnam);
|
||
else
|
||
return my_load(fileref, cptr, fnam);
|
||
}
|
||
|
||
/* Specifier decode
|
||
|
||
Inputs:
|
||
*of = output stream
|
||
addr = current PC
|
||
spec = specifier
|
||
nval = next word
|
||
flag = TRUE if decoding for CPU
|
||
iflag = TRUE if decoding integer instruction
|
||
Outputs:
|
||
count = -number of extra words retired
|
||
*/
|
||
|
||
/* Symbolic decode
|
||
|
||
Inputs:
|
||
*of = output stream
|
||
addr = current PC
|
||
*val = values to decode
|
||
*uptr = pointer to unit
|
||
sw = switches
|
||
Outputs:
|
||
return = if >= 0, error code
|
||
if < 0, number of extra words retired
|
||
*/
|
||
|
||
static char *opcode[] = {
|
||
"?00 ", "XIO ", "SLA ", "SRA ",
|
||
"LDS ", "STS ", "WAIT", "?07 ",
|
||
"BSI ", "BSC ", "?0A ", "?0B ",
|
||
"LDX ", "STX ", "MDX ", "?0F ",
|
||
"A ", "AD ", "S ", "SD ",
|
||
"M ", "D ", "?16 ", "?17 ",
|
||
"LD ", "LDD ", "STO ", "STD ",
|
||
"AND ", "OR ", "EOR ", "?1F ",
|
||
};
|
||
|
||
static char relative[] = { // true if short mode displacements are IAR relative
|
||
FALSE, TRUE, FALSE, FALSE,
|
||
FALSE, TRUE, FALSE, FALSE,
|
||
TRUE, FALSE, FALSE, FALSE,
|
||
TRUE, TRUE, TRUE, FALSE,
|
||
TRUE, TRUE, TRUE, TRUE,
|
||
TRUE, TRUE, FALSE, FALSE,
|
||
TRUE, TRUE, TRUE, TRUE,
|
||
TRUE, TRUE, TRUE, FALSE
|
||
};
|
||
|
||
static char *lsopcode[] = {"SLA ", "SLCA ", "SLT ", "SLC "};
|
||
static char *rsopcode[] = {"SRA ", "?188 ", "SRT ", "RTE "};
|
||
static char tagc[] = " 123";
|
||
|
||
static int ascii_to_ebcdic_table[128] =
|
||
{
|
||
0x00,0x01,0x02,0x03,0x37,0x2d,0x2e,0x2f, 0x16,0x05,0x25,0x0b,0x0c,0x0d,0x0e,0x0f,
|
||
0x10,0x11,0x12,0x13,0x3c,0x3d,0x32,0x26, 0x18,0x19,0x3f,0x27,0x1c,0x1d,0x1e,0x1f,
|
||
0x40,0x5a,0x7f,0x7b,0x5b,0x6c,0x50,0x7d, 0x4d,0x5d,0x5c,0x4e,0x6b,0x60,0x4b,0x61,
|
||
0xf0,0xf1,0xf2,0xf3,0xf4,0xf5,0xf6,0xf7, 0xf8,0xf9,0x7a,0x5e,0x4c,0x7e,0x6e,0x6f,
|
||
|
||
0x7c,0xc1,0xc2,0xc3,0xc4,0xc5,0xc6,0xc7, 0xc8,0xc9,0xd1,0xd2,0xd3,0xd4,0xd5,0xd6,
|
||
0xd7,0xd8,0xd9,0xe2,0xe3,0xe4,0xe5,0xe6, 0xe7,0xe8,0xe9,0xba,0xe0,0xbb,0xb0,0x6d,
|
||
0x79,0x81,0x82,0x83,0x84,0x85,0x86,0x87, 0x88,0x89,0x91,0x92,0x93,0x94,0x95,0x96,
|
||
0x97,0x98,0x99,0xa2,0xa3,0xa4,0xa5,0xa6, 0xa7,0xa8,0xa9,0xc0,0x4f,0xd0,0xa1,0x07,
|
||
};
|
||
|
||
static int ebcdic_to_ascii (int ch)
|
||
{
|
||
int j;
|
||
|
||
for (j = 32; j < 128; j++)
|
||
if (ascii_to_ebcdic_table[j] == ch)
|
||
return j;
|
||
|
||
return '?';
|
||
}
|
||
|
||
t_stat fprint_sym (FILE *of, t_addr addr, t_value *val, UNIT *uptr, int32 sw)
|
||
{
|
||
int32 cflag, ch, OP, F, TAG, INDIR, DSPLC, IR, eaddr;
|
||
char *mnem, tst[12];
|
||
|
||
cflag = (uptr == NULL) || (uptr == &cpu_unit);
|
||
|
||
// if (sw & SWMASK ('A')) { /* ASCII? not useful */
|
||
// fprintf (of, (c1 < 040)? "<%03o>": "%c", c1);
|
||
// return SCPE_OK;
|
||
// }
|
||
|
||
if (sw & SWMASK ('C')) /* character? not useful -- make it EBCDIC */
|
||
sw |= SWMASK('E');
|
||
|
||
if (sw & SWMASK ('E')) { /* EBCDIC! */
|
||
ch = ebcdic_to_ascii((val[0] >> 8) & 0xFF); /* take high byte first */
|
||
fprintf (of, (ch < ' ')? "<%03o>": "%c", ch);
|
||
ch = ebcdic_to_ascii(val[0] & 0xFF);
|
||
fprintf (of, (ch < ' ')? "<%03o>": "%c", ch);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
if (sw & SWMASK ('H')) { /* HOLLERITH! now THIS is useful! */
|
||
ch = hollerith_to_ascii((int16) val[0]);
|
||
fprintf (of, (ch < ' ')? "<%03o>": "%c", ch);
|
||
return SCPE_OK;
|
||
}
|
||
|
||
if (! (sw & SWMASK ('M')))
|
||
return SCPE_ARG;
|
||
|
||
IR = val[0];
|
||
OP = (IR >> 11) & 0x1F; /* opcode */
|
||
F = IR & 0x0400; /* format bit: 1 = long instr */
|
||
TAG = IR & 0x0300; /* tag bits: index reg select */
|
||
if (TAG)
|
||
TAG >>= 8;
|
||
|
||
if (F) { /* long instruction, ASSUME it's valid (have to decrement IAR if not) */
|
||
INDIR = IR & 0x0080; /* indirect bit */
|
||
DSPLC = IR & 0x007F; /* displacement or modifier */
|
||
if (DSPLC & 0x0040)
|
||
DSPLC |= ~ 0x7F; /* sign extend */
|
||
|
||
eaddr = val[1]; /* get reference address */
|
||
}
|
||
else { /* short instruction, use displacement */
|
||
INDIR = 0; /* never indirect */
|
||
DSPLC = IR & 0x00FF; /* get displacement */
|
||
if (DSPLC & 0x0080)
|
||
DSPLC |= ~ 0xFF;
|
||
|
||
eaddr = DSPLC;
|
||
if (relative[OP] && ! TAG)
|
||
eaddr += addr+1; /* turn displacement into address */
|
||
}
|
||
|
||
mnem = opcode[OP]; /* get mnemonic */
|
||
if (OP == 0x02) { /* left shifts are special */
|
||
mnem = lsopcode[(DSPLC >> 6) & 0x0003];
|
||
DSPLC &= 0x003F;
|
||
eaddr = DSPLC;
|
||
}
|
||
else if (OP == 0x03) { /* right shifts too */
|
||
mnem = rsopcode[(DSPLC >> 6) & 0x0003];
|
||
DSPLC &= 0x003F;
|
||
eaddr = DSPLC;
|
||
}
|
||
else if ((OP == 0x08 && F)|| OP == 0x09) { // BSI L and BSC any
|
||
if (OP == 0x09 && (IR & 0x40))
|
||
mnem = "BOSC";
|
||
|
||
tst[0] = '\0';
|
||
if (DSPLC & 0x20) strcat(tst, "Z");
|
||
if (DSPLC & 0x10) strcat(tst, "-");
|
||
if (DSPLC & 0x08) strcat(tst, "+");
|
||
if (DSPLC & 0x04) strcat(tst, "E");
|
||
if (DSPLC & 0x02) strcat(tst, "C");
|
||
if (DSPLC & 0x01) strcat(tst, "O");
|
||
|
||
if (F) {
|
||
fprintf(of, "%04x %s %c%c %s,%04x ", IR & 0xFFFF, mnem, F ? (INDIR ? 'I' : 'L') : ' ', tagc[TAG], tst, eaddr & 0xFFFF);
|
||
return -1;
|
||
}
|
||
fprintf(of, "%04x %s %c%c %s ", IR & 0xFFFF, mnem, F ? (INDIR ? 'I' : 'L') : ' ', tagc[TAG], tst);
|
||
return SCPE_OK;
|
||
}
|
||
else if (OP == 0x0e && TAG == 0) { // MDX with no tag => MDM or jump
|
||
if (F) {
|
||
fprintf(of, "%04x %s %c%c %04x,%x (%d) ", IR & 0xFFFF, "MDM ", (INDIR ? 'I' : 'L'), tagc[TAG], eaddr & 0xFFFF, DSPLC & 0xFFFF, DSPLC);
|
||
return -1;
|
||
}
|
||
mnem = "JMP ";
|
||
}
|
||
|
||
fprintf(of, "%04x %s %c%c %04x ", IR & 0xFFFF, mnem, F ? (INDIR ? 'I' : 'L') : ' ', tagc[TAG], eaddr & 0xFFFF);
|
||
return F ? -1 : SCPE_OK; /* inform how many words we read */
|
||
}
|
||
|
||
int32 get_reg (char *cptr, const char *strings[], char mchar)
|
||
{
|
||
return -1;
|
||
}
|
||
|
||
/* Number or memory address
|
||
|
||
Inputs:
|
||
*cptr = pointer to input string
|
||
*dptr = pointer to output displacement
|
||
*pflag = pointer to accumulating flags
|
||
Outputs:
|
||
cptr = pointer to next character in input string
|
||
NULL if parsing error
|
||
|
||
Flags: 0 (no result), A_NUM (number), A_REL (relative)
|
||
*/
|
||
|
||
char *get_addr (char *cptr, int32 *dptr, int32 *pflag)
|
||
{
|
||
return 0;
|
||
}
|
||
|
||
/* Specifier decode
|
||
|
||
Inputs:
|
||
*cptr = pointer to input string
|
||
addr = current PC
|
||
n1 = 0 if no extra word used
|
||
-1 if extra word used in prior decode
|
||
*sptr = pointer to output specifier
|
||
*dptr = pointer to output displacement
|
||
cflag = true if parsing for the CPU
|
||
iflag = true if integer specifier
|
||
Outputs:
|
||
status = = -1 extra word decoded
|
||
= 0 ok
|
||
= +1 error
|
||
*/
|
||
|
||
t_stat get_spec (char *cptr, t_addr addr, int32 n1, int32 *sptr, t_value *dptr,
|
||
int32 cflag, int32 iflag)
|
||
{
|
||
return -1;
|
||
}
|
||
|
||
/* Symbolic input
|
||
|
||
Inputs:
|
||
*cptr = pointer to input string
|
||
addr = current PC
|
||
*uptr = pointer to unit
|
||
*val = pointer to output values
|
||
sw = switches
|
||
Outputs:
|
||
status = > 0 error code
|
||
<= 0 -number of extra words
|
||
*/
|
||
|
||
t_stat parse_sym (char *cptr, t_addr addr, UNIT *uptr, t_value *val, int32 sw)
|
||
{
|
||
return SCPE_ARG;
|
||
}
|
||
|
||
#ifndef WIN32
|
||
|
||
int strnicmp (char *a, char *b, int n)
|
||
{
|
||
int ca, cb;
|
||
|
||
for (;;) {
|
||
if (--n < 0) // still equal after n characters? quit now
|
||
return 0;
|
||
|
||
if ((ca = *a) == 0) // get character, stop on null terminator
|
||
return *b ? -1 : 0;
|
||
|
||
if (ca >= 'a' && ca <= 'z') // fold lowercase to uppercase
|
||
ca -= 32;
|
||
|
||
cb = *b;
|
||
if (cb >= 'a' && cb <= 'z')
|
||
cb -= 32;
|
||
|
||
if ((ca -= cb) != 0) // if different, return comparison
|
||
return ca;
|
||
|
||
a++, b++;
|
||
}
|
||
}
|
||
|
||
int strcmpi (char *a, char *b)
|
||
{
|
||
int ca, cb;
|
||
|
||
for (;;) {
|
||
if ((ca = *a) == 0) // get character, stop on null terminator
|
||
return *b ? -1 : 0;
|
||
|
||
if (ca >= 'a' && ca <= 'z') // fold lowercase to uppercase
|
||
ca -= 32;
|
||
|
||
cb = *b;
|
||
if (cb >= 'a' && cb <= 'z')
|
||
cb -= 32;
|
||
|
||
if ((ca -= cb) != 0) // if different, return comparison
|
||
return ca;
|
||
|
||
a++, b++;
|
||
}
|
||
}
|
||
|
||
#endif
|