"Fake" XDF disk support works now

What this means is that XDF disk images formatted as 80-track 23-sector-per-track images will work with XDF code that honors that format; however, I've not yet been able to test with "real" XDF disk images (ie, disk images created with DiskDump using the experimental --xdf flag).  "Real" XDF disk images almost certainly need more work (eg, setting all the sector IDs properly), but the groundwork has been laid.
This commit is contained in:
Jeff Parsons 2014-10-27 22:20:41 -07:00 committed by jeffpar
commit 024f9fccf8
20 changed files with 1973 additions and 1809 deletions

View file

@ -10,7 +10,7 @@
"APPNAME": false,
"APPVERSION": false,
"SITEHOST": false,
"DEBUG": false,
"DEBUG": true,
"MAXDEBUG": false,
"PCJSCLASS": true,
"DEBUGGER": true,
@ -55,4 +55,4 @@
"webkitAudioContext": false,
"window": true
}
}
}

View file

@ -1121,7 +1121,11 @@ Computer.prototype.onReset = function()
* wants to clutter the UI with confusing options. ;-)
*/
if (this.resume && !this.sResumePath) {
var fSave = (this.resume == Computer.RESUME_AUTO || !web.confirmUser("Click OK to save the " + Computer.sAppName + " machine state.\n\nWARNING: If you CANCEL, all disk changes will be discarded."));
/*
* I used to bypass the prompt if this.resume == Computer.RESUME_AUTO, setting fSave to true automatically,
* but that gives the user no means of resetting a resumable machine that contains errors in its resume state.
*/
var fSave = (/* this.resume == Computer.RESUME_AUTO || */ !web.confirmUser("Click OK to save the " + Computer.sAppName + " machine state.\n\nWARNING: If you CANCEL, all disk changes will be discarded."));
this.powerOff(fSave, true);
/*
* Forcing the page to reload is an expedient option, but ugly. It's preferable to call powerOn()

View file

@ -1388,7 +1388,7 @@ if (DEBUGGER) {
/*
* TODO: Filtering of interrupt numbers below should be user-definable; this is very quick-and-dirty.
*/
if (nInt < 0x20 && nInt != 0x10 && nInt != 0x15 && nInt != 0x16 && nInt != 0x1A && nInt != 0x1C) {
if (nInt != 0x10 && nInt != 0x15 && nInt != 0x16 && nInt != 0x1A && nInt != 0x1C) {
var AH = this.cpu.regAX >> 8;
var aFuncs = Debugger.INT_FUNCS[nInt];
var sFunc = (aFuncs && aFuncs[AH]) || "";
@ -1556,7 +1556,7 @@ if (DEBUGGER) {
*/
Debugger.prototype.intDOSCall = function(addr)
{
if (this.messageEnabled(this.MESSAGE_DOS)) this.messageInt(Debugger.INT_DOS, addr);
if (this.messageEnabled(this.MESSAGE_DOS | this.MESSAGE_INT)) this.messageInt(Debugger.INT_DOS, addr);
return true;
};
@ -4485,19 +4485,36 @@ if (DEBUGGER) {
}
sCmd = sCmd.toLowerCase();
if (this.isReady() && !this.isBusy(true) && sCmd.length > 0) {
if (this.fAssemble) {
sCmd = "a " + this.hexAddr(this.aAddrAssemble) + " " + sCmd;
}
else {
/*
* Process any "whole word" commands here first (eg, "reset").
* Process any "whole" commands here first (eg, "debug", "nodebug", "reset", etc.)
*
* For all other commands, if they lack a space between the command and argument portions,
* insert a space before the first non-alpha character, so that split() will have the desired effect.
*/
/*
* These commands work great, except that they won't compile, and in fact, I don't WANT them in the
* compiled version, but putting them inside (!COMPILED) doesn't help, so I must disable them for now.
*
if (!COMPILED) {
if (sCmd == "debug") {
DEBUG = true;
this.println("DEBUG checks on");
return true;
}
else if (sCmd == "nodebug") {
DEBUG = false;
this.println("DEBUG checks off");
return true;
}
}
*/
var ch, ch0, i;
switch (sCmd) {
case "reset":
@ -4518,71 +4535,70 @@ if (DEBUGGER) {
}
var asArgs = sCmd.split(" ");
switch (asArgs[0].charAt(0)) {
case "a":
this.doAssemble(asArgs);
break;
case "b":
this.doBreak(asArgs[0], asArgs[1]);
break;
case "c":
this.doClear(asArgs[0]);
break;
case "d":
this.doDump(asArgs[0], asArgs[1], asArgs[2]);
break;
case "e":
this.doEdit(asArgs);
break;
case "f":
this.doFreqs(asArgs[1]);
break;
case "g":
this.doRun(asArgs[1]);
break;
case "h":
this.doHalt(asArgs[1]);
break;
case "i":
this.doInput(asArgs[1]);
break;
case "l":
this.doLoad(asArgs);
break;
case "m":
this.doMessages(asArgs);
break;
case "o":
this.doOutput(asArgs[1], asArgs[2]);
break;
case "p":
case "pr":
this.doProcStep(asArgs[0]);
break;
case "r":
this.doRegisters(asArgs);
break;
case "t":
case "tr":
this.doStep(asArgs[0], asArgs[1]);
break;
case "u":
this.doUnassemble(asArgs[1], asArgs[2], 8);
break;
case "x":
this.doExecOptions(asArgs);
break;
case "?":
this.doHelp();
break;
case "n":
if (this.doInfo(asArgs)) break;
/* falls through */
default:
if (!fQuiet) this.println("unknown command: " + sCmd);
result = false;
break;
case "a":
this.doAssemble(asArgs);
break;
case "b":
this.doBreak(asArgs[0], asArgs[1]);
break;
case "c":
this.doClear(asArgs[0]);
break;
case "d":
this.doDump(asArgs[0], asArgs[1], asArgs[2]);
break;
case "e":
this.doEdit(asArgs);
break;
case "f":
this.doFreqs(asArgs[1]);
break;
case "g":
this.doRun(asArgs[1]);
break;
case "h":
this.doHalt(asArgs[1]);
break;
case "i":
this.doInput(asArgs[1]);
break;
case "l":
this.doLoad(asArgs);
break;
case "m":
this.doMessages(asArgs);
break;
case "o":
this.doOutput(asArgs[1], asArgs[2]);
break;
case "p":
case "pr":
this.doProcStep(asArgs[0]);
break;
case "r":
this.doRegisters(asArgs);
break;
case "t":
case "tr":
this.doStep(asArgs[0], asArgs[1]);
break;
case "u":
this.doUnassemble(asArgs[1], asArgs[2], 8);
break;
case "x":
this.doExecOptions(asArgs);
break;
case "?":
this.doHelp();
break;
case "n":
if (this.doInfo(asArgs)) break;
/* falls through */
default:
if (!fQuiet) this.println("unknown command: " + sCmd);
result = false;
break;
}
}
return result;

View file

@ -256,6 +256,13 @@ FDC.REG_STATUS.RQM = 0x80; // indicates FDC Data Register is ready
FDC.REG_DATA = {};
FDC.REG_DATA.PORT = 0x3F5;
/*
* FDC "Fixed Disk" Register (0x3F6, write-only)
*
* Since this register's functions are all specific to the Hard Disk Controller, see the HDC component for details.
* The fact that this HDC register is in the middle of the FDC I/O port range is an oddity of the "HFCOMBO" controller.
*/
/*
* FDC Digital Input Register (0x3F7, read-only, MODEL_5170 only)
*
@ -850,7 +857,17 @@ FDC.prototype.initDrive = function(drive, iDrive, data)
* care to preserve any drive defaults that initController() already obtained for us, falling back to
* bare minimums only when all else fails.
*/
data[1] = [FDC.DEFAULT_DRIVE_NAME, drive.nCylinders || 40, drive.nHeads || data[3], drive.nSectors || 9, drive.cbSector || 512, data[1]];
data[1] = [
FDC.DEFAULT_DRIVE_NAME, // a[0]
drive.nCylinders || 40, // a[1]
drive.nHeads || data[3],// a[2]
drive.nSectors || 9, // a[3]
drive.cbSector || 512, // a[4]
data[1], // a[5]
drive.nDiskCylinders, // a[6]
drive.nDiskHeads, // a[7]
drive.nDiskSectors // a[8]
];
}
/*
@ -864,25 +881,24 @@ FDC.prototype.initDrive = function(drive, iDrive, data)
/*
* Some additional drive properties/defaults that are largely for the Disk component's benefit.
*/
drive.name = data[i][0];
drive.nCylinders = data[i][1]; // cylinders
drive.nHeads = data[i][2]; // heads/cylinders
drive.nSectors = data[i][3]; // sectors/track
drive.cbSector = data[i][4]; // bytes/sector
drive.fRemovable = data[i][5];
var a = data[i++];
drive.name = a[0];
drive.nCylinders = a[1]; // cylinders
drive.nHeads = a[2]; // heads/cylinders
drive.nSectors = a[3]; // sectors/track
drive.cbSector = a[4]; // bytes/sector
drive.fRemovable = a[5];
/*
* If we have current media parameters, restore them; otherwise, default to the drive's physical parameters.
*/
if (drive.nDiskCylinders = data[i][6]) {
drive.nDiskHeads = data[i][7];
drive.nDiskSectors = data[i][8];
if (drive.nDiskCylinders = a[6]) {
drive.nDiskHeads = a[7];
drive.nDiskSectors = a[8];
} else {
drive.nDiskCylinders = drive.nCylinders;
drive.nDiskHeads = drive.nHeads;
drive.nDiskSectors = drive.nSectors;
}
i++;
/*
* The next group of properties are set by various FDC command sequences.

View file

@ -218,20 +218,21 @@ HDC.ATC = {
DATA: { PORT: 0x1F0}, // no register (read-write)
DIAG: { // this.regError (read-only)
PORT: 0x1F1,
NO_ERROR: 0x01,
CTRL_ERROR: 0x02,
SEC_ERROR: 0x03,
ECC_ERROR: 0x04,
PROC_ERROR: 0x05
NO_ERROR: 0x01,
CTRL_ERROR: 0x02,
SEC_ERROR: 0x03,
ECC_ERROR: 0x04,
PROC_ERROR: 0x05
},
ERROR: { // this.regError (read-only)
PORT: 0x1F1,
NO_DAM: 0x01, // Data Address Mark (DAM) not found
NO_TRK0: 0x02, // Track 0 not detected
CMD_ABORT: 0x04, // Aborted Command
NO_CHS: 0x10, // ID field with the specified C:H:S not found
ECC_ERR: 0x40, // Data ECC Error
BAD_BLOCK: 0x80 // Bad Block Detect
NONE: 0x00,
NO_DAM: 0x01, // Data Address Mark (DAM) not found
NO_TRK0: 0x02, // Track 0 not detected
CMD_ABORT: 0x04, // Aborted Command
NO_CHS: 0x10, // ID field with the specified C:H:S not found
ECC_ERR: 0x40, // Data ECC Error
BAD_BLOCK: 0x80 // Bad Block Detect
},
WPREC: { PORT: 0x1F1}, // this.regWPreC (write-only)
SECCNT: { PORT: 0x1F2}, // this.regSecCnt (read-write; 0 implies a 256-sector request)
@ -239,39 +240,46 @@ HDC.ATC = {
CYLLO: { PORT: 0x1F4}, // this.regCylLo (read-write; all 8 bits are used)
CYLHI: { // this.regCylHi (read-write; only bits 0-1 are used, for a total of 10 bits, or 1024 max cylinders)
PORT: 0x1F5,
MASK: 0x03
MASK: 0x03
},
DRVHD: { // this.regDrvHd (read-write)
PORT: 0x1F6,
HEAD_MASK: 0x0F, // set this to the max number of heads before issuing a SET PARAMETERS command
DRIVE_MASK: 0x10,
SET_MASK: 0xE0,
SET_BITS: 0xA0 // for whatever reason, these bits must always be set
HEAD_MASK: 0x0F, // set this to the max number of heads before issuing a SET PARAMETERS command
DRIVE_MASK: 0x10,
SET_MASK: 0xE0,
SET_BITS: 0xA0 // for whatever reason, these bits must always be set
},
STATUS: { // this.regStatus (read-only; reading clears IRQ.ATC)
PORT: 0x1F7,
BUSY: 0x80, // if this is set, no other STATUS bits are valid
READY: 0x40, // if this is set (along with the SEEK_OK bit), the drive is ready to read/write/seek again
WFAULT: 0x20, // write fault
SEEK_OK: 0x10, // seek operation complete
DATA_REQ: 0x08, // indicates that "the sector buffer requires servicing during a Read or Write command. If either bit 7 (BUSY) or this bit is active, a command is being executed. Upon receipt of any command, this bit is reset."
CORRECTED: 0x04,
INDEX: 0x02, // set once for every revolution of the disk
ERROR: 0x01 // set when the previous command ended in an error; one or more bits are set in the ERROR register (the next command to the controller resets the ERROR bit)
BUSY: 0x80, // if this is set, no other STATUS bits are valid
READY: 0x40, // if this is set (along with the SEEK_OK bit), the drive is ready to read/write/seek again
WFAULT: 0x20, // write fault
SEEK_OK: 0x10, // seek operation complete
DATA_REQ: 0x08, // indicates that "the sector buffer requires servicing during a Read or Write command. If either bit 7 (BUSY) or this bit is active, a command is being executed. Upon receipt of any command, this bit is reset."
CORRECTED: 0x04,
INDEX: 0x02, // set once for every revolution of the disk
ERROR: 0x01 // set when the previous command ended in an error; one or more bits are set in the ERROR register (the next command to the controller resets the ERROR bit)
},
COMMAND:{ // this.regCommand (write-only)
COMMAND: { // this.regCommand (write-only)
PORT: 0x1F7,
RESTORE: 0x10, // low nibble x 500us equal stepping rate (except for 0, which corresponds to 35us) (aka RECALIBRATE)
READ_DATA: 0x20, // also supports NO_RETRIES and WITH_ECC
WRITE_DATA: 0x30, // also supports NO_RETRIES and WITH_ECC
READ_VERF: 0x40, // also supports NO_RETRIES
FORMAT_TRK: 0x50,
SEEK: 0x70, // low nibble x 500us equal stepping rate (except for 0, which corresponds to 35us)
DIAGNOSE: 0x90,
SETPARMS: 0x91,
NO_RETRIES: 0x01,
WITH_ECC: 0x02,
MASK: 0xF0
RESTORE: 0x10, // low nibble x 500us equal stepping rate (except for 0, which corresponds to 35us) (aka RECALIBRATE)
READ_DATA: 0x20, // also supports NO_RETRIES and WITH_ECC
WRITE_DATA: 0x30, // also supports NO_RETRIES and WITH_ECC
READ_VERF: 0x40, // also supports NO_RETRIES
FORMAT_TRK: 0x50,
SEEK: 0x70, // low nibble x 500us equal stepping rate (except for 0, which corresponds to 35us)
DIAGNOSE: 0x90,
SETPARMS: 0x91,
NO_RETRIES: 0x01,
WITH_ECC: 0x02,
MASK: 0xF0
},
FDR: { // this.regFDR
PORT: 0x3F6,
INT_DISABLE: 0x02, // a logical 0 enables fixed disk interrupts
RESET: 0x04, // a logical 1 enables reset fixed disk function
HS3: 0x08, // a logical 1 enables head select 3 (a logical 0 enables reduced write current)
RESERVED: 0xF1
}
};
@ -692,6 +700,7 @@ HDC.prototype.initController = function(data, fHard)
this.regDrvHd = data[i++];
this.regStatus = data[i++];
this.regCommand = data[i++];
this.regFDR = data[i++];
/*
* Additional state is maintained by the Drive object (eg, abSector, ibSector)
*/
@ -766,6 +775,7 @@ HDC.prototype.saveController = function()
data[i++] = this.regDrvHd;
data[i++] = this.regStatus;
data[i++] = this.regCommand;
data[i++] = this.regFDR;
} else {
data[i++] = this.regConfig;
data[i++] = this.regStatus;
@ -1410,7 +1420,7 @@ HDC.prototype.inATCData = function(port, addrFrom)
var hdc = this;
this.readByte(this.drive, function(b, fAsync) {
if (b >= 0) {
if (hdc.chipset) hdc.chipset.setIRR(ChipSet.IRQ.ATC);
hdc.setATCIRR();
/*
* I shouldn't have to set BUSY (or DATA_REQ) again, because it should still be set, no?
*/
@ -1464,7 +1474,7 @@ HDC.prototype.outATCData = function(port, bOut, addrFrom)
else if (this.drive.ibSector == this.drive.cbSector) {
this.drive.nBytes -= this.drive.cbSector;
this.regSecCnt = (this.regSecCnt - 1) & 0xff;
if (this.chipset) this.chipset.setIRR(ChipSet.IRQ.ATC);
this.setATCIRR();
if (this.drive.nBytes >= this.drive.cbSector) {
/*
* I shouldn't have to set BUSY (or DATA_REQ) again, because it should still be set, no?
@ -1715,6 +1725,28 @@ HDC.prototype.outATCCommand = function(port, bOut, addrFrom)
this.doATCommand();
};
/**
* outATCFDR(port, bOut, addrFrom)
*
* This is referred to in IBM's docs as the "Fixed Disk Register" (write-only)
*
* @this {HDC}
* @param {number} port (0x3F6)
* @param {number} bOut
* @param {number} [addrFrom] (not defined whenever the Debugger tries to write the specified port)
*/
HDC.prototype.outATCFDR = function(port, bOut, addrFrom)
{
this.messagePort(port, bOut, addrFrom, "FDR");
/*
* I'm not really sure if I should set HDC.ATC.DIAG.NO_ERROR in regError after *every* write where
* HDC.ATC.FDR.RESET is clear, or only after it has transitioned from set to clear; since the BIOS only
* requires the latter, I'm going to be conservative and restrict regError updates to the latter.
*/
if ((this.regFDR & HDC.ATC.FDR.RESET) && !(bOut & HDC.ATC.FDR.RESET)) this.regError = HDC.ATC.DIAG.NO_ERROR;
this.regFDR = bOut;
};
/**
* doATCommand()
*
@ -1734,7 +1766,7 @@ HDC.prototype.doATCommand = function()
var nSectors = this.regSecCnt || 256;
this.drive = null;
this.regError = 0;
this.regError = HDC.ATC.ERROR.NONE;
this.regStatus = HDC.ATC.STATUS.READY | HDC.ATC.STATUS.SEEK_OK;
var drive = this.aDrives[iDrive];
@ -1775,7 +1807,7 @@ HDC.prototype.doATCommand = function()
*/
this.readByte(drive, function(b, fAsync) {
if (b >= 0 && hdc.chipset) {
hdc.chipset.setIRR(ChipSet.IRQ.ATC);
hdc.setATCIRR();
/*
* As with the WRITE_DATA command, I'm not sure which of BUSY and DATA_REQ (or both)
* should be set here, so I'm setting both of them for now.
@ -1793,16 +1825,16 @@ HDC.prototype.doATCommand = function()
break;
case HDC.ATC.COMMAND.WRITE_DATA:
if (hdc.chipset) {
hdc.chipset.setIRR(ChipSet.IRQ.ATC);
if (this.chipset) {
this.setATCIRR();
/*
* I know that DATA_REQ must be set at this point, but I'm not sure about BUSY; so I'm
* setting both of them for now.
*/
hdc.regStatus = HDC.ATC.STATUS.BUSY | HDC.ATC.STATUS.DATA_REQ;
this.regStatus = HDC.ATC.STATUS.BUSY | HDC.ATC.STATUS.DATA_REQ;
} else {
hdc.regStatus = HDC.ATC.STATUS.ERROR;
hdc.regError = HDC.ATC.ERROR.CMD_ABORT;
this.regStatus = HDC.ATC.STATUS.ERROR;
this.regError = HDC.ATC.ERROR.CMD_ABORT;
}
break;
@ -1853,7 +1885,19 @@ HDC.prototype.doATCommand = function()
break;
}
if (fInterrupt && this.chipset) this.chipset.setIRR(ChipSet.IRQ.ATC);
if (fInterrupt) this.setATCIRR();
};
/**
* setATCIRR()
*
* Raise the ATC's IRQ, provided ATC interrupts are enabled.
*
* @this {HDC}
*/
HDC.prototype.setATCIRR = function()
{
if (this.chipset && !(this.regFDR & HDC.ATC.FDR.INT_DISABLE)) this.chipset.setIRR(ChipSet.IRQ.ATC);
};
/**
@ -2723,7 +2767,8 @@ HDC.aATCPortOutput = {
0x1F4: HDC.prototype.outATCCylLo,
0x1F5: HDC.prototype.outATCCylHi,
0x1F6: HDC.prototype.outATCDrvHd,
0x1F7: HDC.prototype.outATCCommand
0x1F7: HDC.prototype.outATCCommand,
0x3F6: HDC.prototype.outATCFDR
};
/**

View file

@ -1955,8 +1955,9 @@ Video.prototype.initBus = function(cmp, bus, cpu, dbg)
if (DEBUGGER && dbg) {
var video = this;
dbg.messageInit(Video);
this.cpu.addIntNotify(Video.BIOS.INT_VIDEO, this, this.intBIOSVideo);
dbg.messageDump(dbg.MESSAGE_VIDEO, function onDumpVideo(sParm) {
dbg.messageDump(Video.MESSAGE_VIDEO, function onDumpVideo(sParm) {
video.dumpVideo(sParm);
});
}
@ -1989,7 +1990,7 @@ Video.prototype.initBus = function(cmp, bus, cpu, dbg)
Video.prototype.intBIOSVideo = function(addr)
{
if (DEBUGGER) {
if (this.dbg && this.dbg.messageEnabled(this.dbg.MESSAGE_VIDEO)) {
if (this.dbg && this.dbg.messageEnabled(Video.MESSAGE_VIDEO | Video.MESSAGE_INT)) {
this.dbg.messageInt(Video.BIOS.INT_VIDEO, addr);
this.cpu.addIntReturn(addr, function (video, nCycles) {
return function onBIOSVideoReturn(nLevel) {
@ -3742,7 +3743,7 @@ Video.prototype.updateChar = function(col, row, data, context)
this.contextScreen.fillRect(xDst, yDst, this.cxScreenCell, this.cyScreenCell);
}
if (MAXDEBUG && DEBUGGER && this.dbg && this.dbg.messageEnabled(this.dbg.MESSAGE_VIDEO | this.dbg.MESSAGE_LOG)) {
if (MAXDEBUG && DEBUGGER && this.dbg && this.dbg.messageEnabled(Video.MESSAGE_VIDEO | Video.MESSAGE_LOG)) {
this.log("updateCharBgnd(" + col + "," + row + "," + bChar + "): filled " + xDst + "," + yDst);
}
@ -3753,7 +3754,7 @@ Video.prototype.updateChar = function(col, row, data, context)
var xSrcFgnd = (bChar & 0xf) * font.cxCell;
var ySrcFgnd = (bChar >> 4) * font.cyCell;
if (MAXDEBUG && DEBUGGER && this.dbg && this.dbg.messageEnabled(this.dbg.MESSAGE_VIDEO | this.dbg.MESSAGE_LOG)) {
if (MAXDEBUG && DEBUGGER && this.dbg && this.dbg.messageEnabled(Video.MESSAGE_VIDEO | Video.MESSAGE_LOG)) {
this.log("updateCharFgnd(" + col + "," + row + "," + bChar + "): draw from " + xSrcFgnd + "," + ySrcFgnd + " (" + font.cxCell + "," + font.cyCell + ") to " + xDst + "," + yDst);
}
@ -4121,9 +4122,10 @@ Video.prototype.updateScreenGraphicsEGA = function(addrScreen, addrScreenLimit)
* whereas bit-wise operations operate ONLY on the low 32 bits.
*
* This can be confirmed by looking at dwPixel.toString(16), which returns "-80000000". The solution
* is to check for a negative dwPixel and make it positive.
* is to add 4294967296 (0x100000000) to any negative 32-bit value for which you need the positive
* representation instead.
*/
if (dwPixel < 0) dwPixel = -dwPixel;
if (dwPixel < 0) dwPixel += 0x100000000;
/*
* Since assertions don't fix problems (only catch them, and only in DEBUG builds), I'm also insuring
* that bPixel will always default to 0 if an undefined value ever slips through again.
@ -4925,7 +4927,7 @@ Video.prototype.dumpVideo = function(sParm)
Video.prototype.messageDebugger = function(sMessage, fForce)
{
if (DEBUGGER && this.dbg) {
if (fForce || this.dbg.messageEnabled(this.dbg.MESSAGE_VIDEO)) {
if (fForce || this.dbg.messageEnabled(Video.MESSAGE_VIDEO)) {
this.dbg.message(sMessage);
}
}
@ -4946,7 +4948,7 @@ Video.prototype.messageDebugger = function(sMessage, fForce)
Video.prototype.messagePort = function(port, bOut, addrFrom, name, bIn)
{
if (DEBUGGER && this.dbg) {
this.dbg.messagePort(this, port, bOut, addrFrom, name, this.dbg.MESSAGE_VIDEO, bIn);
this.dbg.messagePort(this, port, bOut, addrFrom, name, Video.MESSAGE_VIDEO, bIn);
}
};