Improved DOS message output (use "m dos on" Debugger command)

This commit is contained in:
Jeff Parsons 2015-07-15 09:09:28 -07:00
commit 6295c112fe
6 changed files with 196 additions and 131 deletions

View file

@ -1159,25 +1159,25 @@ if (DEBUGGER) {
0x13: {
0x00: "disk reset",
0x01: "get status",
0x02: "read drive DL (CH:DH:CL,AL) into ES:BX",
0x03: "write drive DL (CH:DH:CL,AL) from ES:BX",
0x04: "verify drive DL (CH:DH:CL,AL)",
0x05: "format drive DL using ES:BX",
0x08: "read drive DL parameters into ES:DI",
0x15: "get drive DL DASD type",
0x16: "get drive DL change line status",
0x17: "set drive DL DASD type",
0x18: "set drive DL media type"
0x02: "read drive %DL (%CH:%DH:%CL,%AL) into %ES:%BX",
0x03: "write drive %DL (%CH:%DH:%CL,%AL) from %ES:%BX",
0x04: "verify drive %DL (%CH:%DH:%CL,%AL)",
0x05: "format drive %DL using %ES:%BX",
0x08: "read drive %DL parameters into %ES:%DI",
0x15: "get drive %DL DASD type",
0x16: "get drive %DL change line status",
0x17: "set drive %DL DASD type",
0x18: "set drive %DL media type"
},
0x15: {
0x80: "open device",
0x81: "close device",
0x82: "program termination",
0x83: "wait CX:DXus for event",
0x83: "wait %CX:%DXus for event",
0x84: "joystick support",
0x85: "SYSREQ pressed",
0x86: "wait CX:DXus",
0x87: "move block (CX words)",
0x86: "wait %CX:%DXus",
0x87: "move block (%CX words)",
0x88: "get extended memory size",
0x89: "processor to virtual mode",
0x90: "device busy loop",
@ -1185,94 +1185,94 @@ if (DEBUGGER) {
},
0x21: {
0x00: "terminate program",
0x01: "read character (al) from stdin with echo",
0x02: "write character DL to stdout",
0x03: "read character (al) from stdaux", // eg, COM1
0x04: "write character DL to stdaux", // eg, COM1
0x05: "write character DL to stdprn", // eg, LPT1
0x06: "direct console output (input if DL=FF)",
0x01: "read character (AL) from stdin with echo",
0x02: "write character #%DL to stdout",
0x03: "read character (AL) from stdaux", // eg, COM1
0x04: "write character #%DL to stdaux", // eg, COM1
0x05: "write character #%DL to stdprn", // eg, LPT1
0x06: "direct console output (input if %DL=FF)",
0x07: "direct console input without echo",
0x08: "read character (al) from stdin without echo",
0x09: "write $-terminated string DS:DX to stdout",
0x0A: "buffered input (ds:dx)", // byte 0 is maximum chars, byte 1 is number of previous characters, byte 2 is number of characters read
0x08: "read character (AL) from stdin without echo",
0x09: "write string $%DS:%DX to stdout",
0x0A: "buffered input (DS:DX)", // byte 0 is maximum chars, byte 1 is number of previous characters, byte 2 is number of characters read
0x0B: "get stdin status",
0x0C: "flush buffer and read stdin", // AL is a function # (0x01, 0x06, 0x07, 0x08, or 0x0A)
0x0C: "flush buffer and read stdin", // AL is a function # (0x01, 0x06, 0x07, 0x08, or 0x0A)
0x0D: "disk reset",
0x0E: "select default drive DL", // returns # of available drives in AL
0x0F: "open file using fcb DS:DX", // DS:DX -> unopened File Control Block
0x10: "close file using fcb DS:DX",
0x11: "find first matching file using fcb DS:DX",
0x12: "find next matching file using fcb DS:DX",
0x13: "delete file using fcb DS:DX",
0x14: "sequential read from file using fcb DS:DX",
0x15: "sequential write to file using fcb DS:DX",
0x16: "create or truncate file using fcb DS:DX",
0x17: "rename file using fcb DS:DX",
0x19: "get current default drive (al)",
0x1A: "set disk transfer area (dta) DS:DX",
0x0E: "select default drive %DL", // returns # of available drives in AL
0x0F: "open file using FCB ^%DS:%DX", // DS:DX -> unopened File Control Block
0x10: "close file using FCB ^%DS:%DX",
0x11: "find first matching file using FCB ^%DS:%DX",
0x12: "find next matching file using FCB ^%DS:%DX",
0x13: "delete file using FCB ^%DS:%DX",
0x14: "sequential read from file using FCB ^%DS:%DX",
0x15: "sequential write to file using FCB ^%DS:%DX",
0x16: "create or truncate file using FCB ^%DS:%DX",
0x17: "rename file using FCB ^%DS:%DX",
0x19: "get current default drive (AL)",
0x1A: "set disk transfer area (DTA=%DS:%DX)",
0x1B: "get allocation information for default drive",
0x1C: "get allocation information for specific drive DL",
0x1C: "get allocation information for specific drive %DL",
0x1F: "get drive parameter block for default drive",
0x21: "read random record from file using fcb DS:DX",
0x22: "write random record to file using fcb DS:DX",
0x23: "get file size using fcb DS:DX",
0x24: "set random record number for fcb DS:DX",
0x25: "set address DS:DX of interrupt vector AL",
0x26: "create new program segment prefix (psp) at segment DX",
0x27: "random block read from file using fcb DS:DX",
0x28: "random block write to file using fcb DS:DX",
0x29: "parse filename DS:SI into fcb ES:DI using AL",
0x2A: "get system date (year=cx, mon=dh, day=dl)",
0x2B: "set system date (year=CX, mon=DH, day=DL)",
0x2C: "get system time (hour=ch, min=cl, sec=dh, 100ths=dl)",
0x2D: "set system time (hour=CH, min=CL, sec=DH, 100ths=DL)",
0x2E: "set verify flag AL",
0x2F: "get disk transfer area address (es:bx)", // DOS 2.00+
0x30: "get DOS version (al=major, ah=minor)",
0x21: "read random record from file using FCB ^%DS:%DX",
0x22: "write random record to file using FCB ^%DS:%DX",
0x23: "get file size using FCB ^%DS:%DX",
0x24: "set random record number for FCB ^%DS:%DX",
0x25: "set address %DS:%DX of interrupt vector %AL",
0x26: "create new PSP at segment %DX",
0x27: "random block read from file using FCB ^%DS:%DX",
0x28: "random block write to file using FCB ^%DS:%DX",
0x29: "parse filename $%DS:%SI into FCB %ES:%DI using %AL",
0x2A: "get system date (year=CX, mon=DH, day=DL)",
0x2B: "set system date (year=%CX, mon=%DH, day=%DL)",
0x2C: "get system time (hour=CH, min=CL, sec=DH, 100ths=DL)",
0x2D: "set system time (hour=%CH, min=%CL, sec=%DH, 100ths=%DL)",
0x2E: "set verify flag %AL",
0x2F: "get disk transfer area (DTA=ES:BX)", // DOS 2.00+
0x30: "get DOS version (AL=major, AH=minor)",
0x31: "terminate and stay resident",
0x32: "get drive parameter block (dpb=ds:bx) for drive DL",
0x32: "get drive parameter block (DPB=DS:BX) for drive %DL",
0x33: "extended break check",
0x34: "get address (es:bx) of InDOS flag",
0x35: "get address (es:bx) of interrupt vector AL",
0x36: "get free disk space of drive DL",
0x37: "get(0)/set(1) switch character DL (AL)",
0x34: "get address (ES:BX) of InDOS flag",
0x35: "get address (ES:BX) of interrupt vector %AL",
0x36: "get free disk space of drive %DL",
0x37: "get(0)/set(1) switch character %DL (%AL)",
0x38: "get country-specific information",
0x39: "create subdirectory DS:DX",
0x3A: "remove subdirectory DS:DX",
0x3B: "set current directory DS:DX",
0x3C: "create or truncate file DS:DX with attributes CX",
0x3D: "open existing file DS:DX with mode AL",
0x3E: "close file BX",
0x3F: "read CX bytes from file BX into buffer DS:DX",
0x40: "write CX bytes to file BX from buffer DS:DX",
0x41: "delete file DS:DX",
0x42: "set position CX:DX of file BX relative to AL",
0x43: "get(0)/set(1) attributes CX of file DS:DX (AL)",
0x39: "create subdirectory $%DS:%DX",
0x3A: "remove subdirectory $%DS:%DX",
0x3B: "set current directory $%DS:%DX",
0x3C: "create or truncate file $%DS:%DX with attributes %CX",
0x3D: "open existing file $%DS:%DX with mode %AL",
0x3E: "close file %BX",
0x3F: "read %CX bytes from file %BX into buffer %DS:%DX",
0x40: "write %CX bytes to file %BX from buffer %DS:%DX",
0x41: "delete file $%DS:%DX",
0x42: "set position %CX:%DX of file %BX relative to %AL",
0x43: "get(0)/set(1) attributes %CX of file %DS:%DX (%AL)",
0x44: "get device information (IOCTL)",
0x45: "duplicate file handle BX",
0x46: "force file handle CX to duplicate file handle BX",
0x47: "get current directory (ds:si) for drive DL",
0x48: "allocate memory segment with BX paragraphs",
0x49: "free memory segment ES",
0x4A: "resize memory segment ES to BX paragraphs",
0x4B: "load program DS:DX using parameter block ES:BX",
0x4C: "terminate with return code AL",
0x4D: "get return code (al)",
0x4E: "find first matching file DS:DX with attributes CX",
0x45: "duplicate file handle %BX",
0x46: "force file handle %CX to duplicate file handle %BX",
0x47: "get current directory (DS:SI) for drive %DL",
0x48: "allocate memory segment with %BX paragraphs",
0x49: "free memory segment %ES",
0x4A: "resize memory segment %ES to %BX paragraphs",
0x4B: "load program $%DS:%DX using parameter block %ES:%BX",
0x4C: "terminate with return code %AL",
0x4D: "get return code (AL)",
0x4E: "find first matching file $%DS:%DX with attributes %CX",
0x4F: "find next matching file",
0x50: "set current psp BX",
0x51: "get current psp (bx)",
0x52: "get system variables (es:bx)",
0x53: "translate bpb DS:SI to dpb (es:bp)",
0x54: "get verify flag (al)",
0x55: "create child psp at segment DX",
0x56: "rename file DS:DX to name ES:DI",
0x57: "get(0)/set(1) file date DX and time CX (AL)",
0x58: "get(0)/set(1) memory allocation strategy (AL)", // DOS 2.11+
0x59: "get extended error information", // DOS 3.00+
0x5A: "create temporary file DS:DX with attributes CX", // DOS 3.00+
0x5B: "create file DS:DX with attributes CX", // DOS 3.00+ (doesn't truncate existing files like 0x3C)
0x5C: "lock(0)/unlock(1) file BX region CX:DX length SI:DI (AL)" // DOS 3.00+
0x50: "set current PSP %BX",
0x51: "get current PSP (bx)",
0x52: "get system variables (ES:BX)",
0x53: "translate BPB %DS:%SI to DPB (ES:BP)",
0x54: "get verify flag (AL)",
0x55: "create child PSP at segment %DX",
0x56: "rename file $%DS:%DX to $%ES:%DI",
0x57: "get(0)/set(1) file date %DX and time %CX (%AL)",
0x58: "get(0)/set(1) memory allocation strategy (%AL)", // DOS 2.11+
0x59: "get extended error information", // DOS 3.00+
0x5A: "create temporary file $%DS:%DX with attributes %CX", // DOS 3.00+
0x5B: "create file $%DS:%DX with attributes %CX", // DOS 3.00+ (doesn't truncate existing files like 0x3C)
0x5C: "lock(0)/unlock(1) file %BX region %CX:%DX length %SI:%DI (%AL)" // DOS 3.00+
}
};
@ -1672,7 +1672,7 @@ if (DEBUGGER) {
*
* @this {Debugger}
* @param {{DbgAddr}} dbgAddr
* @param {number|undefined} inc contains value to increment dbgAddr by (default is 1)
* @param {number} [inc] contains value to increment dbgAddr by (default is 1)
*/
Debugger.prototype.incAddr = function(dbgAddr, inc)
{
@ -1716,25 +1716,27 @@ if (DEBUGGER) {
};
/**
* dumpSZ(dbgAddr, cchMax)
* getSZ(dbgAddr, cchMax)
*
* Dump helper for zero-terminated strings.
* Get zero-terminated (aka "ASCIIZ") string from dbgAddr. It also stops at the first '$', in case this is
* a '$'-terminated string -- mainly because I'm lazy and didn't feel like writing a separate get() function.
* Yes, a zero-terminated string containing a '$' will be prematurely terminated, and no, I don't care.
*
* @this {Debugger}
* @param {{DbgAddr}} dbgAddr
* @param {number} [cchMax]
* @param {number} [cchMax] (default is 256)
* @return {string} (and dbgAddr advanced past the terminating zero)
*/
Debugger.prototype.dumpSZ = function(dbgAddr, cchMax)
Debugger.prototype.getSZ = function(dbgAddr, cchMax)
{
var sChars = "";
var s = "";
cchMax = cchMax || 256;
while (sChars.length < cchMax) {
while (s.length < cchMax) {
var b = this.getByte(dbgAddr, 1);
if (!b) break;
sChars += (b >= 32 && b < 128? String.fromCharCode(b) : ".");
if (!b || b == 0x24) break;
s += (b >= 32 && b < 128? String.fromCharCode(b) : ".");
}
return sChars;
return s;
};
/**
@ -1765,7 +1767,7 @@ if (DEBUGGER) {
var wPID = this.getShort(dbgAddr, 2);
var wParas = this.getShort(dbgAddr, 5);
if (bSig != 0x4D && bSig != 0x5A) break;
this.println(this.hexOffset(0, sel) + ": '" + String.fromCharCode(bSig) + "' PID=" + str.toHexWord(wPID) + " LEN=" + str.toHexWord(wParas) + ' "' + this.dumpSZ(dbgAddr, 8) + '"');
this.println(this.hexOffset(0, sel) + ": '" + String.fromCharCode(bSig) + "' PID=" + str.toHexWord(wPID) + " LEN=" + str.toHexWord(wParas) + ' "' + this.getSZ(dbgAddr, 8) + '"');
sel += 1 + wParas;
}
};
@ -2048,10 +2050,14 @@ if (DEBUGGER) {
*
* @this {Debugger}
* @param {string} sReg
* @return {number}
* @param {number} [off] optional offset into sReg
* @return {number} register index, or -1 if not found
*/
Debugger.prototype.getRegIndex = function(sReg) {
return usr.indexOf(Debugger.REGS, sReg.toUpperCase());
Debugger.prototype.getRegIndex = function(sReg, off) {
off = off || 0;
var i = usr.indexOf(Debugger.REGS, sReg.substr(off, 2).toUpperCase());
if (i < 0 && sReg.length > 2) i = usr.indexOf(Debugger.REGS, sReg.substr(off, 3).toUpperCase());
return i;
};
/**
@ -2200,11 +2206,55 @@ if (DEBUGGER) {
* @return {string}
*/
Debugger.prototype.replaceRegs = function(s) {
for (var iReg = 0; iReg < Debugger.REGS.length; iReg++) {
var sReg = Debugger.REGS[iReg];
if (s.indexOf(sReg) >= 0) {
s = str.replaceAll(sReg, this.getRegValue(iReg), s);
/*
* Replace every %XX (or %XXX), where XX (or XXX) is a register, with the register's value.
*/
var i = 0;
var b, sChar, sAddr, dbgAddr, sReplace;
while ((i = s.indexOf('%', i)) >= 0) {
var iReg = this.getRegIndex(s, i+1);
if (iReg >= 0) {
s = s.replace('%' + Debugger.REGS[iReg], this.getRegValue(iReg));
}
i++;
}
/*
* Replace every #XX, where XX is a hex byte value, with the corresponding ASCII character (if printable).
*/
i = 0;
while ((i = s.indexOf('#', i)) >= 0) {
sChar = s.substr(i+1, 2);
b = str.parseInt(sChar, 16);
if (b != null && b >= 32 && b < 128) {
sReplace = '#' + sChar + " '" + String.fromCharCode(b) + "'";
s = s.replace('#' + sChar, sReplace);
i += sReplace.length;
continue;
}
i++;
}
/*
* Replace every $XXXX:XXXX, where XXXX:XXXX is a segmented address, with the zero-terminated string at that address.
*/
i = 0;
while ((i = s.indexOf('$', i)) >= 0) {
sAddr = s.substr(i+1, 9);
dbgAddr = this.parseAddr(sAddr);
sReplace = sAddr + ' "' + this.getSZ(dbgAddr) + '"';
s = s.replace('$' + sAddr, sReplace);
i += sReplace.length;
}
/*
* Replace every ^XXXX:XXXX, where XXXX:XXXX is a segmented address, with the FCB filename stored at that address.
*/
i = 0;
while ((i = s.indexOf('^', i)) >= 0) {
sAddr = s.substr(i+1, 9);
dbgAddr = this.parseAddr(sAddr);
this.incAddr(dbgAddr);
sReplace = sAddr + ' "' + this.getSZ(dbgAddr, 11) + '"';
s = s.replace('^' + sAddr, sReplace);
i += sReplace.length;
}
return s;
};
@ -2277,9 +2327,9 @@ if (DEBUGGER) {
var sFunc = (aFuncs && aFuncs[AH]) || "";
if (sFunc) sFunc = ' ' + this.replaceRegs(sFunc);
/*
* For purposes of display only, rewind addr to the address of the responsible "INT n" instruction; we
* know it's the two-byte "INT n" instruction because that's the only opcode handler that calls checkIntNotify()
* at the moment.
* For display purposes only, rewind addr to the address of the responsible "INT n" instruction;
* we know it's the two-byte "INT n" instruction because that's the only opcode handler that calls
* checkIntNotify() at the moment.
*/
addr -= 2;
this.message("INT " + str.toHexByte(nInt) + ": AH=" + str.toHexByte(AH) + " @" + this.hexOffset(addr - this.cpu.segCS.base, this.cpu.getCS()) + sFunc);
@ -3737,13 +3787,13 @@ if (DEBUGGER) {
*
* @this {Debugger}
* @param {string|undefined} sAddr
* @param {number|undefined} type is the address segment type, in case sAddr doesn't specify a segment
* @param {number|undefined} [type] is the address segment type, in case sAddr doesn't specify a segment
* @return {{DbgAddr}}
*/
Debugger.prototype.parseAddr = function(sAddr, type)
{
var dbgAddr;
var dbgAddrNext = (type == Debugger.ADDR_DATA? this.dbgAddrNextData : this.dbgAddrNextCode);
var dbgAddrNext = (type === Debugger.ADDR_CODE? this.dbgAddrNextCode : this.dbgAddrNextData);
var off = dbgAddrNext.off, sel = dbgAddrNext.sel, addr = dbgAddrNext.addr;
if (sAddr !== undefined) {

View file

@ -1279,13 +1279,13 @@ Disk.prototype.updateSector = function(file, pba, off)
var cylinder, head, sector;
if ((cylinder = this.aDiskData[iCylinder]) && (head = cylinder[iHead]) && (sector = head[iSector])) {
this.assert(sector['sector'] == iSector +1);
if (sector.file) {
if (sector['file']) {
if (DEBUG && this.messageEnabled()) {
this.printMessage('"' + sector.file.sPath + '" cross-linked at offset ' + sector.file.offFile + ' with "' + file.sPath + '" at offset ' + off);
this.printMessage('"' + sector['file'].sPath + '" cross-linked at offset ' + sector['file'].offFile + ' with "' + file.sPath + '" at offset ' + off);
}
return false;
}
sector.file = file;
sector['file'] = file;
sector.offFile = off;
return true;
}
@ -2114,7 +2114,16 @@ Disk.prototype.restore = function(deltas)
*/
Disk.prototype.toJSON = function()
{
var s = JSON.stringify(this.aDiskData);
var s = JSON.stringify(this.aDiskData, function(key, value) {
/*
* If BACKTRACK support is enabled, we have to filter out any 'file' properties that may
* be attached to the sector objects, lest we risk blowing the stack due to circular references.
*/
if (key == 'file') {
return undefined;
}
return value;
});
s = s.replace(/,"length":512/gm, "").replace(/,"pattern":0/gm, "");
/*
* I don't really want to strip quotes from disk image property names, since I would have to put them

View file

@ -1172,8 +1172,9 @@ FDC.prototype.autoMount = function(fRemount)
*/
var iDrive = sDrive.charCodeAt(0) - 0x41;
if (iDrive >= 0 && iDrive < this.aDrives.length) {
if (!this.loadDiskette(iDrive, configDrive['name'], configDrive['path'], true) && fRemount)
if (!this.loadDiskette(iDrive, configDrive['name'], configDrive['path'], true) && fRemount) {
this.setReady(false);
}
continue;
}
}

View file

@ -1523,11 +1523,16 @@ X86CPU.prototype.checkIntNotify = function(nInt)
}
}
/*
* The enabling of INT messages is one of the criteria that's also included in the Debugger's
* checksEnabled() function, and therefore in fDebugCheck, so for maximum speed, we check fDebugCheck first.
* The enabling of INT messages is one of the criteria that's also included in the Debugger's checksEnabled()
* function, and therefore included in fDebugCheck, so for maximum speed, we check fDebugCheck first.
*
* NOTE: By wrapping this in MAXDEBUG, we're effectively eliminating the need for any checkIntReturn() calls;
* onIntReturn() generates a lot of noise, via dbg.messageIntReturn(), and because there's no way to be be sure
* we'll catch the return (or for some interrupts, *whether* they will return), it's safer to disable this
* feature unless you really want it.
*/
if (DEBUGGER && this.aFlags.fDebugCheck) {
if (this.messageEnabled(Messages.INT) && this.dbg.messageInt(nInt, this.regLIP)) {
if (this.messageEnabled(Messages.INT) && this.dbg.messageInt(nInt, this.regLIP) && MAXDEBUG) {
this.addIntReturn(this.regLIP, function(cpu, nCycles) {
return function onIntReturn(nLevel) {
cpu.dbg.messageIntReturn(nInt, nLevel, cpu.getCycles() - nCycles);

View file

@ -1256,7 +1256,7 @@ X86.fnIRET = function IRET()
if (DEBUG) this.printMessage(" returning to " + str.toHex(newCS, 4) + ':' + str.toHex(newIP, this.dataSize << 1), this.bitsMessage, true);
if (this.setCSIP(newIP, newCS, false) != null) {
this.setPS(newPS, cpl);
if (this.cIntReturn) this.checkIntReturn(this.regLIP);
if (MAXDEBUG && this.cIntReturn) this.checkIntReturn(this.regLIP);
}
};
@ -1290,7 +1290,7 @@ X86.fnJMPFdw = function JMPFdw(dst, src)
return X86.fnGRPUndefined.call(this, dst, src);
}
this.setCSIP(dst, this.getShort(this.regEA + this.dataSize));
if (this.cIntReturn) this.checkIntReturn(this.regLIP);
if (MAXDEBUG && this.cIntReturn) this.checkIntReturn(this.regLIP);
this.nStepCycles -= this.cycleCounts.nOpCyclesJmpDM;
this.opFlags |= X86.OPFLAG.NOWRITE;
return dst;
@ -2169,7 +2169,7 @@ X86.fnRETF = function RETF(n)
this.segES.load(0);
}
}
if (n == 2 && this.cIntReturn) this.checkIntReturn(this.regLIP);
if (MAXDEBUG && n == 2 && this.cIntReturn) this.checkIntReturn(this.regLIP);
};
/**

View file

@ -37,19 +37,19 @@ var str = {};
/**
* isValidInt(s, base)
*
* Since the built-in parseInt() function has the annoying feature of returning a partial value
* when it encounters an invalid character (eg, parseInt("foo", 16) returns 0xf), use this function
* to validate the entire string first.
* The built-in parseInt() function has the annoying feature of returning a partial value (ie,
* up to the point where it encounters an invalid character); eg, parseInt("foo", 16) returns 0xf.
* So use this function to validate the entire string.
*
* @param {string} s is the string representation of some number
* @param {number} [base] is the radix of the number represented above (only 10 and 16 are supported)
* @return {boolean} true if valid (or we're not sure because the base isn't recognized), false if invalid
* @return {boolean} true if valid, false if invalid (or the specified base isn't supported)
*/
str.isValidInt = function(s, base)
{
if (!base || base == 10) return s.match(/^[0-9]+$/) !== null;
if (base == 16) return s.match(/^[0-9a-f]+$/i) !== null;
return true;
return false;
};
/**
@ -105,7 +105,7 @@ str.parseInt = function(s, base)
* an exception, whereas toHex() will simply return '?' characters.
*
* NOTE: The following work-around (adapted from code found on StackOverflow) would be another solution,
* taking care of negative values, zero-padding, and upper-casing, but not undefined/NaN values:
* taking care of negative values, zero-padding, and upper-casing, but not null/undefined/NaN values:
*
* s = (n < 0? n + 0x100000000 : n).toString(16);
* s = "00000000".substr(0, 8 - s.length) + s;
@ -312,7 +312,7 @@ str.replaceArray = function(a, s)
/**
* pad(s, cch)
*
* Note that the maximum amount of padding currently supported is 40 spaces.
* NOTE: the maximum amount of padding currently supported is 40 spaces.
*
* @param {string} s is a string
* @param {number} cch is desired length