Added support for converting numbers to/from octal, along with improved support for binary numbers
This commit is contained in:
parent
375ddc12f3
commit
1265104e6d
15 changed files with 888 additions and 789 deletions
|
|
@ -1283,7 +1283,7 @@ DiskDump.prototype.dumpBuffer = function(sKey, buf, len, cbItem, offData)
|
|||
}
|
||||
else {
|
||||
sLine += str.toHexByte(v);
|
||||
if (!sASCII) sASCII = "0x" + str.toHex(offData + off) + " ";
|
||||
if (!sASCII) sASCII = str.toHex(offData + off, 0, true) + " ";
|
||||
sASCII += (v >= 0x20 && v < 0x7F && v != 0x3C && v != 0x3E? String.fromCharCode(v) : ".");
|
||||
}
|
||||
}
|
||||
|
|
@ -1377,7 +1377,7 @@ DiskDump.prototype.trimSector = function(buf, len)
|
|||
dwPattern = buf.readInt32LE(off);
|
||||
while ((off -= cbPattern) >= 0) {
|
||||
var dw = buf.readInt32LE(off);
|
||||
// if (fDebug) DiskDump.logConsole("0x" + str.toHex(off) + ": comparing 0x" + str.toHex(dw) + " to pattern 0x" + str.toHex(dwPattern));
|
||||
// if (fDebug) DiskDump.logConsole(str.toHex(off, 0, true) + ": comparing " + str.toHex(dw, 0, true) + " to pattern " + str.toHex(dwPattern, 0, true));
|
||||
if (dw != dwPattern) break;
|
||||
cbTrim += cbPattern;
|
||||
}
|
||||
|
|
@ -2081,7 +2081,7 @@ DiskDump.prototype.buildClusters = function(aFiles, offDisk, cbCluster, iParentC
|
|||
if (!err) {
|
||||
if (fDebug && cb != buf.length) DiskDump.logConsole(file.FILE_NAME + ": initial size (" + cb + ") does not match actual size (" + buf.length + ")");
|
||||
buf.copy(obj.bufDisk.buf || obj.bufDisk, off);
|
||||
if (fDebug) DiskDump.logConsole("0x" + str.toHex(off) + ": 0x" + str.toHex(buf.length) + " bytes written for " + file.FILE_PATH);
|
||||
if (fDebug) DiskDump.logConsole(str.toHex(off, 0, true) + ": " + str.toHex(buf.length, 0, true) + " bytes written for " + file.FILE_PATH);
|
||||
if (obj.sManifestFile) file.FILE_MD5 = crypto.createHash('md5').update(buf).digest('hex');
|
||||
}
|
||||
if (!--obj.cWritesPending) done(err);
|
||||
|
|
@ -2101,7 +2101,7 @@ DiskDump.prototype.buildClusters = function(aFiles, offDisk, cbCluster, iParentC
|
|||
}
|
||||
if (bufData) {
|
||||
bufData.copy(this.bufDisk, offDisk);
|
||||
if (fDebug) DiskDump.logConsole("0x" + str.toHex(offDisk) + ": 0x" + str.toHex(bufData.length) + " bytes IMMEDIATELY written for " + aFiles[iFile].FILE_PATH);
|
||||
if (fDebug) DiskDump.logConsole(str.toHex(offDisk, 0, true) + ": " + str.toHex(bufData.length, 0, true) + " bytes IMMEDIATELY written for " + aFiles[iFile].FILE_PATH);
|
||||
}
|
||||
offDisk += cbData;
|
||||
cClusters += ((cbData / cbCluster) | 0);
|
||||
|
|
@ -2117,11 +2117,11 @@ DiskDump.prototype.buildClusters = function(aFiles, offDisk, cbCluster, iParentC
|
|||
for (iFile = 0; iFile < aFiles.length; iFile++) {
|
||||
var cb = aFiles[iFile].FILE_SIZE;
|
||||
if (cb < 0) {
|
||||
if (fDebug) DiskDump.logConsole("0x" + str.toHex(offDisk) + ": buildClusters()");
|
||||
if (fDebug) DiskDump.logConsole(str.toHex(offDisk, 0, true) + ": buildClusters()");
|
||||
var cSubClusters = this.buildClusters(aFiles[iFile].FILE_DATA, offDisk, cbCluster, aFiles[iFile].FILE_CLUS, iLevel + 1, done);
|
||||
cClusters += cSubClusters;
|
||||
offDisk += cSubClusters * cbCluster;
|
||||
if (fDebug) DiskDump.logConsole("0x" + str.toHex(offDisk) + ": buildClusters() returned, writing " + cSubClusters + " clusters");
|
||||
if (fDebug) DiskDump.logConsole(str.toHex(offDisk, 0, true) + ": buildClusters() returned, writing " + cSubClusters + " clusters");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2288,7 +2288,7 @@ DiskDump.prototype.buildImageFromFiles = function(aFiles, done)
|
|||
var nTargetSectors = (this.kbTarget? this.kbTarget * 2 : 0);
|
||||
var cbTotal = this.calcFileSizes(aFiles);
|
||||
|
||||
if (fDebug) DiskDump.logConsole("total calculated size for " + aFiles.length + " files/folders: " + cbTotal + " bytes (0x" + str.toHex(cbTotal) + ")");
|
||||
if (fDebug) DiskDump.logConsole("total calculated size for " + aFiles.length + " files/folders: " + cbTotal + " bytes (" + str.toHex(cbTotal, 0, true) + ")");
|
||||
|
||||
if (cbTotal >= cbMax) {
|
||||
err = new Error("file(s) too large (" + cbTotal + " bytes total, " + cbMax + " bytes maximum)");
|
||||
|
|
@ -2914,7 +2914,7 @@ DiskDump.prototype.convertOSIDiskToJSON = function()
|
|||
json += this.dumpLine(-2, "]");
|
||||
}
|
||||
else {
|
||||
DiskDump.logError(new Error("unrecognized OSI disk track at 0x" + str.toHex(offTrack)));
|
||||
DiskDump.logError(new Error("unrecognized OSI disk track at " + str.toHex(offTrack, 0, true)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -452,7 +452,7 @@ FileDump.prototype.dumpBuffer = function(sKey, buf, len, cbItem, offDump, nWidth
|
|||
} else {
|
||||
sLine += str.toHexByte(v);
|
||||
}
|
||||
if (!sASCII) sASCII = "0x" + str.toHex(off) + " ";
|
||||
if (!sASCII) sASCII = str.toHex(off, 0, true) + " ";
|
||||
sASCII += (v >= 0x20 && v < 0x7F && v != 0x3C && v != 0x3E? String.fromCharCode(v) : ".");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3195,7 +3195,7 @@ if (DEBUGGER) {
|
|||
this.println("warning: " + str.toHex(vNew) + " exceeds " + size + "-byte value");
|
||||
}
|
||||
var vOld = fnGet.call(this, dbgAddr);
|
||||
this.println("changing " + this.toHexAddr(dbgAddr) + " from 0x" + str.toHex(vOld, cch) + " to 0x" + str.toHex(vNew, cch));
|
||||
this.println("changing " + this.toHexAddr(dbgAddr) + " from " + str.toHex(vOld, cch, true) + " to " + str.toHex(vNew, cch, true));
|
||||
fnSet.call(this, dbgAddr, vNew, size);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -6151,7 +6151,7 @@ if (DEBUGGER) {
|
|||
this.println("warning: " + str.toHex(vNew) + " exceeds " + size + "-byte value");
|
||||
}
|
||||
var vOld = fnGet.call(this, dbgAddr);
|
||||
this.println("changing " + this.toHexAddr(dbgAddr) + " from 0x" + str.toHex(vOld, cch) + " to 0x" + str.toHex(vNew, cch));
|
||||
this.println("changing " + this.toHexAddr(dbgAddr) + " from " + str.toHex(vOld, cch, true) + " to " + str.toHex(vNew, cch, true));
|
||||
fnSet.call(this, dbgAddr, vNew, size);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2697,7 +2697,7 @@ if (DEBUGGER) {
|
|||
this.println("warning: " + str.toHex(vNew) + " exceeds " + size + "-byte value");
|
||||
}
|
||||
var vOld = fnGet.call(this, dbgAddr);
|
||||
this.println("changing " + this.toHexAddr(dbgAddr) + " from 0x" + str.toHex(vOld, cch) + " to 0x" + str.toHex(vNew, cch));
|
||||
this.println("changing " + this.toHexAddr(dbgAddr) + " from " + str.toHex(vOld, cch, true) + " to " + str.toHex(vNew, cch, true));
|
||||
fnSet.call(this, dbgAddr, vNew, size);
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ function Debugger(parmsDbg)
|
|||
this.aPrevCmds = [];
|
||||
|
||||
/*
|
||||
* aVariables is an object with properties that grows as setVariable() assigns more variables;
|
||||
* aVariables is an object with properties that grow as setVariable() assigns more variables;
|
||||
* each property corresponds to one variable, where the property name is the variable name (ie,
|
||||
* a string beginning with a letter or underscore, followed by zero or more additional letters,
|
||||
* digits, or underscores) and the property value is the variable's numeric value. See doVar()
|
||||
|
|
@ -108,7 +108,7 @@ function Debugger(parmsDbg)
|
|||
* unprefixed hex value (eg, "a5" as opposed to "0xa5") will trump the numeric value. Unprefixed
|
||||
* hex values are a convenience of parseValue(), which always calls str.parseInt() with a default
|
||||
* base of 16; however, that default be overridden with a variety of explicit prefixes or suffixes
|
||||
* (eg, a trailing period to indicate a decimal number).
|
||||
* (eg, a leading zero to indicate octal, or a trailing period to indicate decimal).
|
||||
*/
|
||||
this.aVariables = {};
|
||||
|
||||
|
|
@ -141,6 +141,162 @@ if (DEBUGGER) {
|
|||
'*': 9 // multiplication
|
||||
};
|
||||
|
||||
/**
|
||||
* getRegIndex(sReg, off)
|
||||
*
|
||||
* NOTE: This must be implemented by the individual debuggers.
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {string} sReg
|
||||
* @param {number} [off] optional offset into sReg
|
||||
* @return {number} register index, or -1 if not found
|
||||
*/
|
||||
Debugger.prototype.getRegIndex = function(sReg, off)
|
||||
{
|
||||
return -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* getRegValue(iReg)
|
||||
*
|
||||
* NOTE: This must be implemented by the individual debuggers.
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {number} iReg
|
||||
* @return {number|undefined}
|
||||
*/
|
||||
Debugger.prototype.getRegValue = function(iReg)
|
||||
{
|
||||
return undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* parseAddrReference(s, sAddr)
|
||||
*
|
||||
* Returns the given string with the given address reference replaced with the contents of that address.
|
||||
*
|
||||
* NOTE: This must be implemented by the individual debuggers.
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {string} s
|
||||
* @param {string} sAddr
|
||||
* @return {string}
|
||||
*/
|
||||
Debugger.prototype.parseAddrReference = function(s, sAddr)
|
||||
{
|
||||
return s.replace('[' + sAddr + ']', "unimplemented");
|
||||
};
|
||||
|
||||
/**
|
||||
* getNextCommand()
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @return {string}
|
||||
*/
|
||||
Debugger.prototype.getNextCommand = function()
|
||||
{
|
||||
var sCmd;
|
||||
if (this.iPrevCmd > 0) {
|
||||
sCmd = this.aPrevCmds[--this.iPrevCmd];
|
||||
} else {
|
||||
sCmd = "";
|
||||
this.iPrevCmd = -1;
|
||||
}
|
||||
return sCmd;
|
||||
};
|
||||
|
||||
/**
|
||||
* getPrevCommand()
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @return {string|null}
|
||||
*/
|
||||
Debugger.prototype.getPrevCommand = function()
|
||||
{
|
||||
var sCmd = null;
|
||||
if (this.iPrevCmd < this.aPrevCmds.length - 1) {
|
||||
sCmd = this.aPrevCmds[++this.iPrevCmd];
|
||||
}
|
||||
return sCmd;
|
||||
};
|
||||
|
||||
/**
|
||||
* parseCommand(sCmd, fSave, chSep)
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {string|undefined} sCmd
|
||||
* @param {boolean} [fSave] is true to save the command, false if not
|
||||
* @param {string} [chSep] is the command separator character (default is ';')
|
||||
* @return {Array.<string>}
|
||||
*/
|
||||
Debugger.prototype.parseCommand = function(sCmd, fSave, chSep)
|
||||
{
|
||||
if (fSave) {
|
||||
if (!sCmd) {
|
||||
if (this.fAssemble) {
|
||||
sCmd = "end";
|
||||
} else {
|
||||
sCmd = this.aPrevCmds[this.iPrevCmd+1];
|
||||
}
|
||||
} else {
|
||||
if (this.iPrevCmd < 0 && this.aPrevCmds.length) {
|
||||
this.iPrevCmd = 0;
|
||||
}
|
||||
if (this.iPrevCmd < 0 || sCmd != this.aPrevCmds[this.iPrevCmd]) {
|
||||
this.aPrevCmds.splice(0, 0, sCmd);
|
||||
this.iPrevCmd = 0;
|
||||
}
|
||||
this.iPrevCmd--;
|
||||
}
|
||||
}
|
||||
var a = [];
|
||||
if (sCmd) {
|
||||
/*
|
||||
* With the introduction of breakpoint commands (ie, quoted command sequences
|
||||
* associated with a breakpoint), we can no longer perform simplistic splitting.
|
||||
*
|
||||
* a = sCmd.split(chSep || ';');
|
||||
* for (var i = 0; i < a.length; i++) a[i] = str.trim(a[i]);
|
||||
*
|
||||
* We may now split on semi-colons ONLY if they are outside a quoted sequence.
|
||||
*
|
||||
* Also, to allow quoted strings *inside* breakpoint commands, we first replace all
|
||||
* DOUBLE double-quotes with single quotes.
|
||||
*/
|
||||
sCmd = sCmd.toLowerCase().replace(/""/g, "'");
|
||||
|
||||
var iPrev = 0;
|
||||
var chQuote = null;
|
||||
chSep = chSep || ';';
|
||||
/*
|
||||
* NOTE: Processing charAt() up to and INCLUDING length is not a typo; we're taking
|
||||
* advantage of the fact that charAt() with an invalid index returns an empty string,
|
||||
* allowing us to use the same substring() call to capture the final portion of sCmd.
|
||||
*
|
||||
* In a sense, it allows us to pretend that the string ends with a zero terminator.
|
||||
*/
|
||||
for (var i = 0; i <= sCmd.length; i++) {
|
||||
var ch = sCmd.charAt(i);
|
||||
if (ch == '"' || ch == "'") {
|
||||
if (!chQuote) {
|
||||
chQuote = ch;
|
||||
} else if (ch == chQuote) {
|
||||
chQuote = null;
|
||||
}
|
||||
}
|
||||
else if (ch == chSep && !chQuote || !ch) {
|
||||
/*
|
||||
* Recall that substring() accepts starting (inclusive) and ending (exclusive)
|
||||
* indexes, whereas substr() accepts a starting index and a length. We need the former.
|
||||
*/
|
||||
a.push(str.trim(sCmd.substring(iPrev, i)));
|
||||
iPrev = i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return a;
|
||||
};
|
||||
|
||||
/**
|
||||
* evalExpression(aVals, aOps, cOps)
|
||||
*
|
||||
|
|
@ -239,145 +395,6 @@ if (DEBUGGER) {
|
|||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* getNextCommand()
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @return {string}
|
||||
*/
|
||||
Debugger.prototype.getNextCommand = function()
|
||||
{
|
||||
var sCmd;
|
||||
if (this.iPrevCmd > 0) {
|
||||
sCmd = this.aPrevCmds[--this.iPrevCmd];
|
||||
} else {
|
||||
sCmd = "";
|
||||
this.iPrevCmd = -1;
|
||||
}
|
||||
return sCmd;
|
||||
};
|
||||
|
||||
/**
|
||||
* getPrevCommand()
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @return {string|null}
|
||||
*/
|
||||
Debugger.prototype.getPrevCommand = function()
|
||||
{
|
||||
var sCmd = null;
|
||||
if (this.iPrevCmd < this.aPrevCmds.length - 1) {
|
||||
sCmd = this.aPrevCmds[++this.iPrevCmd];
|
||||
}
|
||||
return sCmd;
|
||||
};
|
||||
|
||||
/**
|
||||
* getRegIndex(sReg, off)
|
||||
*
|
||||
* NOTE: This must be implemented by the individual debuggers.
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {string} sReg
|
||||
* @param {number} [off] optional offset into sReg
|
||||
* @return {number} register index, or -1 if not found
|
||||
*/
|
||||
Debugger.prototype.getRegIndex = function(sReg, off)
|
||||
{
|
||||
return -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* getRegValue(iReg)
|
||||
*
|
||||
* NOTE: This must be implemented by the individual debuggers.
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {number} iReg
|
||||
* @return {number|undefined}
|
||||
*/
|
||||
Debugger.prototype.getRegValue = function(iReg)
|
||||
{
|
||||
return undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* parseCommand(sCmd, fSave, chSep)
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {string|undefined} sCmd
|
||||
* @param {boolean} [fSave] is true to save the command, false if not
|
||||
* @param {string} [chSep] is the command separator character (default is ';')
|
||||
* @return {Array.<string>}
|
||||
*/
|
||||
Debugger.prototype.parseCommand = function(sCmd, fSave, chSep)
|
||||
{
|
||||
if (fSave) {
|
||||
if (!sCmd) {
|
||||
if (this.fAssemble) {
|
||||
sCmd = "end";
|
||||
} else {
|
||||
sCmd = this.aPrevCmds[this.iPrevCmd+1];
|
||||
}
|
||||
} else {
|
||||
if (this.iPrevCmd < 0 && this.aPrevCmds.length) {
|
||||
this.iPrevCmd = 0;
|
||||
}
|
||||
if (this.iPrevCmd < 0 || sCmd != this.aPrevCmds[this.iPrevCmd]) {
|
||||
this.aPrevCmds.splice(0, 0, sCmd);
|
||||
this.iPrevCmd = 0;
|
||||
}
|
||||
this.iPrevCmd--;
|
||||
}
|
||||
}
|
||||
var a = [];
|
||||
if (sCmd) {
|
||||
/*
|
||||
* With the introduction of breakpoint commands (ie, quoted command sequences
|
||||
* associated with a breakpoint), we can no longer perform simplistic splitting.
|
||||
*
|
||||
* a = sCmd.split(chSep || ';');
|
||||
* for (var i = 0; i < a.length; i++) a[i] = str.trim(a[i]);
|
||||
*
|
||||
* We may now split on semi-colons ONLY if they are outside a quoted sequence.
|
||||
*
|
||||
* Also, to allow quoted strings *inside* breakpoint commands, we first replace all
|
||||
* DOUBLE double-quotes with single quotes.
|
||||
*/
|
||||
sCmd = sCmd.toLowerCase().replace(/""/g, "'");
|
||||
|
||||
var iPrev = 0;
|
||||
var chQuote = null;
|
||||
chSep = chSep || ';';
|
||||
/*
|
||||
* NOTE: Processing charAt() up to and INCLUDING length is not a typo; we're taking
|
||||
* advantage of the fact that charAt() with an invalid index returns an empty string,
|
||||
* allowing us to use the same substring() call to capture the final portion of sCmd.
|
||||
*
|
||||
* In a sense, it allows us to pretend that the string ends with a zero terminator.
|
||||
*/
|
||||
for (var i = 0; i <= sCmd.length; i++) {
|
||||
var ch = sCmd.charAt(i);
|
||||
if (ch == '"' || ch == "'") {
|
||||
if (!chQuote) {
|
||||
chQuote = ch;
|
||||
} else if (ch == chQuote) {
|
||||
chQuote = null;
|
||||
}
|
||||
}
|
||||
else if (ch == chSep && !chQuote || !ch) {
|
||||
/*
|
||||
* Recall that substring() accepts starting (inclusive) and ending (exclusive)
|
||||
* indexes, whereas substr() accepts a starting index and a length. We need the former.
|
||||
*/
|
||||
a.push(str.trim(sCmd.substring(iPrev, i)));
|
||||
iPrev = i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return a;
|
||||
};
|
||||
|
||||
/**
|
||||
* parseExpression(sExp, fPrint)
|
||||
*
|
||||
|
|
@ -473,23 +490,6 @@ if (DEBUGGER) {
|
|||
return value;
|
||||
};
|
||||
|
||||
/**
|
||||
* parseAddrReference(s, sAddr)
|
||||
*
|
||||
* Returns the given string with the given address references replaced with the contents of the address.
|
||||
*
|
||||
* NOTE: This must be implemented by the individual debuggers.
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {string} s
|
||||
* @param {string} sAddr
|
||||
* @return {string}
|
||||
*/
|
||||
Debugger.prototype.parseAddrReference = function(s, sAddr)
|
||||
{
|
||||
return s.replace('[' + sAddr + ']', "unimplemented");
|
||||
};
|
||||
|
||||
/**
|
||||
* parseReference(s)
|
||||
*
|
||||
|
|
@ -585,7 +585,7 @@ if (DEBUGGER) {
|
|||
var fDefined = false;
|
||||
if (value !== undefined) {
|
||||
fDefined = true;
|
||||
sValue = str.toHexLong(value) + " " + value + ". (" + str.toBinBytes(value) + ")";
|
||||
sValue = str.toHex(value, 0, true) + " " + value + ". " + str.toOctal(value, 0, true) + " " + str.toBinBytes(value, 0, true);
|
||||
}
|
||||
sVar = (sVar != null? (sVar + ": ") : "");
|
||||
this.println(sVar + sValue);
|
||||
|
|
|
|||
|
|
@ -38,56 +38,100 @@ var str = {};
|
|||
*
|
||||
* 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.
|
||||
*
|
||||
* So it's best to use our own str.parseInt() function, which will in turn 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 above number; 10 is the default (only 2, 8, 10 and 16 are supported)
|
||||
* @param {number} [base] is the radix to use (default is 10); only 2, 8, 10 and 16 are supported
|
||||
* @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;
|
||||
if (base == 2) return s.match(/^[01]+$/i) !== null;
|
||||
if (base == 8) return s.match(/^[0-7]+$/) !== null;
|
||||
if (base == 2) return s.match(/^[01]+$/) !== null;
|
||||
return false;
|
||||
};
|
||||
|
||||
/**
|
||||
* parseInt(s, base)
|
||||
*
|
||||
* This is a wrapper around the built-in parseInt() function, which recognizes certain prefixes (eg,
|
||||
* '$' or "0x" for hex) and suffixes (eg, 'h' for hex, or '.' for decimal), and then calls isValidInt()
|
||||
* to ensure we don't convert strings that contain partial values (see isValidInt() for details).
|
||||
* This is a wrapper around the built-in parseInt() function. Our wrapper recognizes certain prefixes
|
||||
* ('$' or "0x" for hex, '#' or "0o" for octal) and suffixes ('.' for decimal, 'h' for hex, 'y' for
|
||||
* binary), and then calls isValidInt() to ensure we don't convert strings that contain partial values;
|
||||
* see isValidInt() for details.
|
||||
*
|
||||
* We don't support multiple prefix/suffix combinations, nor do we support the "0b" prefix (or "b" suffix)
|
||||
* for binary, because 1) it's not commonly used, and 2) it conflicts with valid hex sequences.
|
||||
* The use of multiple prefix/suffix combinations is undefined (although for the record, we process
|
||||
* prefixes first). We do NOT support the "0b" prefix to indicate binary UNLESS one or more commas are
|
||||
* also present (because "0b" is also a valid hex sequence), and we do NOT support a single leading zero
|
||||
* to indicate octal (because such a number could also be decimal or hex). Any number of commas are
|
||||
* allowed; we remove them all before calling the built-in parseInt().
|
||||
*
|
||||
* To summarize our non-standard alternatives: a 'y' suffix indicates binary, a '#' prefix indicates
|
||||
* octal, a '$' prefix indicates hex, and a "0b" prefix indicates binary IF at least one comma is present.
|
||||
* Commas are useful for grouping binary digits, but if you don't want to use them, then use a 'y' suffix.
|
||||
*
|
||||
* @param {string} s is the string representation of some number
|
||||
* @param {number} [base] is the default radix to use (default is 10); can be overridden by prefixes/suffixes
|
||||
* @param {number} [base] is the radix to use (default is 10); can be overridden by prefixes/suffixes
|
||||
* @return {number|undefined} corresponding value, or undefined if invalid
|
||||
*/
|
||||
str.parseInt = function(s, base)
|
||||
{
|
||||
var value;
|
||||
|
||||
if (s) {
|
||||
if (!base) base = 10;
|
||||
if (s.charAt(0) == '$') {
|
||||
var chPrefix = s.charAt(0);
|
||||
var fCommas = (s.indexOf(',') > 0);
|
||||
if (fCommas) s = s.replace(/,/g, '');
|
||||
if (chPrefix == '#') {
|
||||
base = 8;
|
||||
chPrefix = null;
|
||||
}
|
||||
else if (chPrefix == '$') {
|
||||
base = 16;
|
||||
chPrefix = null;
|
||||
}
|
||||
if (chPrefix == null) {
|
||||
s = s.substr(1);
|
||||
} else if (s.substr(0, 2) == "0x") {
|
||||
base = 16;
|
||||
s = s.substr(2);
|
||||
} else {
|
||||
var chSuffix = s.charAt(s.length-1).toLowerCase();
|
||||
if (chSuffix == 'h') {
|
||||
base = 16;
|
||||
chSuffix = null;
|
||||
}
|
||||
else {
|
||||
if (chPrefix == '0') {
|
||||
chPrefix = s.charAt(1);
|
||||
if (chPrefix == 'b' && fCommas) {
|
||||
base = 2;
|
||||
chPrefix = null;
|
||||
}
|
||||
if (chPrefix == 'o') {
|
||||
base = 8;
|
||||
chPrefix = null;
|
||||
}
|
||||
else if (chPrefix == 'x') {
|
||||
base = 16;
|
||||
chPrefix = null;
|
||||
}
|
||||
}
|
||||
else if (chSuffix == '.') {
|
||||
base = 10;
|
||||
chSuffix = null;
|
||||
if (chPrefix == null) {
|
||||
s = s.substr(2);
|
||||
}
|
||||
else {
|
||||
var chSuffix = s.charAt(s.length-1).toLowerCase();
|
||||
if (chSuffix == 'y') {
|
||||
base = 2;
|
||||
chSuffix = null;
|
||||
}
|
||||
else if (chSuffix == '.') {
|
||||
base = 10;
|
||||
chSuffix = null;
|
||||
}
|
||||
else if (chSuffix == 'h') {
|
||||
base = 16;
|
||||
chSuffix = null;
|
||||
}
|
||||
if (chSuffix == null) s = s.substr(0, s.length-1);
|
||||
}
|
||||
if (chSuffix == null) s = s.substr(0, s.length-1);
|
||||
}
|
||||
var v;
|
||||
if (str.isValidInt(s, base) && !isNaN(v = parseInt(s, base))) {
|
||||
|
|
@ -109,7 +153,7 @@ str.parseInt = function(s, base)
|
|||
str.toBin = function(n, cch)
|
||||
{
|
||||
var s = "";
|
||||
if (cch === undefined) {
|
||||
if (!cch) {
|
||||
cch = 32;
|
||||
} else {
|
||||
if (cch > 32) cch = 32;
|
||||
|
|
@ -134,30 +178,74 @@ str.toBin = function(n, cch)
|
|||
};
|
||||
|
||||
/**
|
||||
* toBinBytes(n, cb)
|
||||
* toBinBytes(n, cb, fPrefix)
|
||||
*
|
||||
* Converts an integer to binary, with the specified number of bytes (up to the default of 4).
|
||||
*
|
||||
* @param {number|null|undefined} n is a 32-bit value
|
||||
* @param {number} [cb] is the desired number of binary bytes (4 is both the default and the maximum)
|
||||
* @param {boolean} [fPrefix]
|
||||
* @return {string} the binary representation of n
|
||||
*/
|
||||
str.toBinBytes = function(n, cb)
|
||||
str.toBinBytes = function(n, cb, fPrefix)
|
||||
{
|
||||
var s = "";
|
||||
if (!cb || cb > 4) cb = 4;
|
||||
for (var i = 0; i < cb; i++) {
|
||||
if (s) s = ',' + s;
|
||||
s = str.toBin(n & 0xff, 8) + 'b' + s;
|
||||
s = str.toBin(n & 0xff, 8) + s;
|
||||
n >>= 8;
|
||||
}
|
||||
return s;
|
||||
return (fPrefix? "0b" : "") + s;
|
||||
};
|
||||
|
||||
/**
|
||||
* toHex(n, cch)
|
||||
* toOctal(n, cch, fPrefix)
|
||||
*
|
||||
* Converts an integer to hex, with the specified number of digits (up to the default of 8).
|
||||
* Converts an integer to octal, with the specified number of digits (default of 6; max of 11)
|
||||
*
|
||||
* You might be tempted to use the built-in n.toString(8) instead, but it doesn't zero-pad and it
|
||||
* doesn't properly convert negative values. Moreover, if n is undefined, n.toString() will throw
|
||||
* an exception, whereas this function will return '?' characters.
|
||||
*
|
||||
* @param {number|null|undefined} n is a 32-bit value
|
||||
* @param {number} [cch] is the desired number of octal digits (0 or undefined for default of either 6 or 11)
|
||||
* @param {boolean} [fPrefix]
|
||||
* @return {string} the octal representation of n
|
||||
*/
|
||||
str.toOctal = function(n, cch, fPrefix)
|
||||
{
|
||||
var s = "";
|
||||
|
||||
if (cch) {
|
||||
if (cch > 11) cch = 11;
|
||||
} else {
|
||||
cch = (n & ~0xffff)? 11 : 6;
|
||||
}
|
||||
/*
|
||||
* An initial "falsey" check for null takes care of both null and undefined;
|
||||
* we can't rely entirely on isNaN(), because isNaN(null) returns false, oddly enough.
|
||||
*
|
||||
* Alternatively, we could mask and shift n regardless of whether it's null/undefined/NaN,
|
||||
* since JavaScript coerces such operands to zero, but I think there's "value" in seeing those
|
||||
* values displayed differently.
|
||||
*/
|
||||
if (n == null || isNaN(n)) {
|
||||
while (cch-- > 0) s = '?' + s;
|
||||
} else {
|
||||
while (cch-- > 0) {
|
||||
var d = (n & 7) + 0x30;
|
||||
s = String.fromCharCode(d) + s;
|
||||
n >>= 3;
|
||||
}
|
||||
}
|
||||
return (fPrefix? "0o" : "") + s;
|
||||
};
|
||||
|
||||
/**
|
||||
* toHex(n, cch, fPrefix)
|
||||
*
|
||||
* Converts an integer to hex, with the specified number of digits (default of 4 or 8, max of 8).
|
||||
*
|
||||
* You might be tempted to use the built-in n.toString(16) instead, but it doesn't zero-pad and it
|
||||
* doesn't properly convert negative values; for example, if n is -2147483647, then n.toString(16)
|
||||
|
|
@ -172,16 +260,18 @@ str.toBinBytes = function(n, cb)
|
|||
* s = s.substr(0, cch).toUpperCase();
|
||||
*
|
||||
* @param {number|null|undefined} n is a 32-bit value
|
||||
* @param {number} [cch] is the desired number of hex digits (8 is both the default and the maximum)
|
||||
* @param {number} [cch] is the desired number of hex digits (0 or undefined for default of either 4 or 8)
|
||||
* @param {boolean} [fPrefix]
|
||||
* @return {string} the hex representation of n
|
||||
*/
|
||||
str.toHex = function(n, cch)
|
||||
str.toHex = function(n, cch, fPrefix)
|
||||
{
|
||||
var s = "";
|
||||
if (cch === undefined) {
|
||||
cch = 8;
|
||||
} else {
|
||||
|
||||
if (cch) {
|
||||
if (cch > 8) cch = 8;
|
||||
} else {
|
||||
cch = (n & ~0xffff)? 8 : 4;
|
||||
}
|
||||
/*
|
||||
* An initial "falsey" check for null takes care of both null and undefined;
|
||||
|
|
@ -201,46 +291,46 @@ str.toHex = function(n, cch)
|
|||
n >>= 4;
|
||||
}
|
||||
}
|
||||
return s;
|
||||
return (fPrefix? "0x" : "") + s;
|
||||
};
|
||||
|
||||
/**
|
||||
* toHexByte(b)
|
||||
*
|
||||
* Alias for "0x" + str.toHex(b, 2)
|
||||
* Alias for str.toHex(b, 2, true)
|
||||
*
|
||||
* @param {number|null|undefined} b is a byte value
|
||||
* @return {string} the hex representation of b
|
||||
*/
|
||||
str.toHexByte = function(b)
|
||||
{
|
||||
return "0x" + str.toHex(b, 2);
|
||||
return str.toHex(b, 2, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* toHexWord(w)
|
||||
*
|
||||
* Alias for "0x" + str.toHex(w, 4)
|
||||
* Alias for str.toHex(w, 4, true)
|
||||
*
|
||||
* @param {number|null|undefined} w is a word (16-bit) value
|
||||
* @return {string} the hex representation of w
|
||||
*/
|
||||
str.toHexWord = function(w)
|
||||
{
|
||||
return "0x" + str.toHex(w, 4);
|
||||
return str.toHex(w, 4, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* toHexLong(l)
|
||||
*
|
||||
* Alias for "0x" + toHex(l)
|
||||
* Alias for str.toHex(l, 8, true)
|
||||
*
|
||||
* @param {number|null|undefined} l is a dword (32-bit) value
|
||||
* @return {string} the hex representation of w
|
||||
*/
|
||||
str.toHexLong = function(l)
|
||||
{
|
||||
return "0x" + str.toHex(l);
|
||||
return str.toHex(l, 8, true);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in a new issue