Clean up the CPU/ChipSet interface, and get the PC8080 Video component ready to actually do something

This commit is contained in:
Jeff Parsons 2016-05-01 10:13:43 -07:00
commit 8c5ab8d73b
19 changed files with 1210 additions and 993 deletions

View file

@ -68,7 +68,7 @@ function ChipSet(parmsChipSet)
Component.notice("Unrecognized ChipSet model: " + model);
}
this.model = ChipSet.MODELS[model];
this.model = ChipSet.MODELS[model] || ChipSet.SI_1978.MODEL;
this.bSwitches = this.parseDIPSwitches(parmsChipSet['swDIP']);
@ -276,7 +276,7 @@ ChipSet.prototype.reset = function()
this.bStatus0 = 0;
this.bStatus1 = 0;
this.bStatus2 = 0;
this.bShiftData = 0;
this.wShiftData = 0;
this.bShiftCount = 0;
this.bSound1 = this.bSound2 = 0;
};
@ -293,7 +293,7 @@ ChipSet.prototype.save = function()
{
var state = new State(this);
if (this.model == ChipSet.SI_1978.MODEL) {
state.set(0, [this.bStatus0, this.bStatus1, this.bStatus2, this.bShiftData, this.bShiftCount, this.bSound1, this.bSound2]);
state.set(0, [this.bStatus0, this.bStatus1, this.bStatus2, this.wShiftData, this.bShiftCount, this.bSound1, this.bSound2]);
}
return state.data();
};
@ -315,7 +315,7 @@ ChipSet.prototype.restore = function(data)
this.bStatus0 = a[0];
this.bStatus1 = a[1];
this.bStatus2 = a[2];
this.bShiftData = a[3];
this.wShiftData = a[3];
this.bShiftCount = a[4];
this.bSound1 = a[5];
this.bSound2 = a[6];
@ -323,6 +323,34 @@ ChipSet.prototype.restore = function(data)
return true;
};
/**
* start()
*
* Notification from the CPU that it's starting.
*
* @this {ChipSet}
*/
ChipSet.prototype.start = function()
{
/*
* Currently, all we (may) do with this notification is allow the speaker to make noise.
*/
};
/**
* stop()
*
* Notification from the CPU that it's stopping.
*
* @this {ChipSet}
*/
ChipSet.prototype.stop = function()
{
/*
* Currently, all we (may) do with this notification is prevent the speaker from making noise.
*/
};
/**
* inSIStatus0(port, addrFrom)
*
@ -378,7 +406,7 @@ ChipSet.prototype.inSIStatus2 = function(port, addrFrom)
*/
ChipSet.prototype.inSIShiftResult = function(port, addrFrom)
{
var b = ((this.bShiftData << this.bShiftCount) >> 8) & 0xff;
var b = (this.wShiftData >> (8 - this.bShiftCount)) & 0xff;
this.printMessageIO(port, null, addrFrom, "SHIFT.RESULT", b, true);
return b;
};
@ -422,7 +450,7 @@ ChipSet.prototype.outSISound1 = function(port, b, addrFrom)
ChipSet.prototype.outSIShiftData = function(port, b, addrFrom)
{
this.printMessageIO(port, b, addrFrom, "SHIFT.DATA", null, true);
this.bShiftData = b;
this.wShiftData = (b << 8) | (this.wShiftData >> 8);
};
/**
@ -439,6 +467,19 @@ ChipSet.prototype.outSISound2 = function(port, b, addrFrom)
this.bSound2 = b;
};
/**
* outSIWatchdog(port, b, addrFrom)
*
* @this {ChipSet}
* @param {number} port (0x06)
* @param {number} b
* @param {number} [addrFrom] (not defined if the Debugger is trying to write the specified port)
*/
ChipSet.prototype.outSIWatchdog = function(port, b, addrFrom)
{
this.printMessageIO(port, b, addrFrom, "WATCHDOG", null, true);
};
/*
* Port input notification tables
*/
@ -456,7 +497,8 @@ ChipSet.aPortOutput = {
0x02: ChipSet.prototype.outSIShiftCount,
0x03: ChipSet.prototype.outSISound1,
0x04: ChipSet.prototype.outSIShiftData,
0x05: ChipSet.prototype.outSISound2
0x05: ChipSet.prototype.outSISound2,
0x06: ChipSet.prototype.outSIWatchdog
};
/**

View file

@ -1047,7 +1047,7 @@ CPU.prototype.startCPU = function(fUpdateFocus)
if (this.cmp) this.cmp.start(this.aCounts.msStartRun, this.getCycles());
this.flags.fRunning = true;
this.flags.fStarting = true;
if (this.chipset) this.chipset.setSpeaker();
if (this.chipset) this.chipset.start();
var controlRun = this.bindings["run"];
if (controlRun) controlRun.textContent = "Halt";
if (this.cmp) {
@ -1091,7 +1091,7 @@ CPU.prototype.stopCPU = function(fComplete)
this.nRunCycles = 0;
if (this.flags.fRunning) {
this.flags.fRunning = false;
if (this.chipset) this.chipset.setSpeaker();
if (this.chipset) this.chipset.stop();
var controlRun = this.bindings["run"];
if (controlRun) controlRun.textContent = "Run";
}

View file

@ -2362,7 +2362,7 @@ CPUDef.opJNC = function()
CPUDef.opOUT = function()
{
var port = this.getPCByte();
this.bus.checkPortOutputNotify(port, 1, this.regA);
this.bus.checkPortOutputNotify(port, 1, this.regA, this.offPC(-2));
this.nStepCycles -= 10;
};
@ -2450,7 +2450,7 @@ CPUDef.opJC = function()
CPUDef.opIN = function()
{
var port = this.getPCByte();
this.regA = this.bus.checkPortInputNotify(port, 1) & 0xff;
this.regA = this.bus.checkPortInputNotify(port, 1, this.offPC(-2)) & 0xff;
this.nStepCycles -= 10;
};

View file

@ -450,6 +450,18 @@ CPUSim.prototype.getPC = function()
return this.regPC;
};
/**
* offPC()
*
* @this {CPUSim}
* @param {number} off
* @return {number}
*/
CPUSim.prototype.offPC = function(off)
{
return (this.regPC + off) & 0xffff;
};
/**
* setPC(off)
*

View file

@ -1578,6 +1578,9 @@ if (DEBUGGER) {
/**
* messageIO(component, port, bOut, addrFrom, name, bIn, bitsMessage)
*
* Most (if not all) port handlers should provide a name for their respective ports, so if no name is provided,
* we assume this is an unknown port, and display a message by default.
*
* @this {Debugger}
* @param {Component} component
* @param {number} port
@ -1590,7 +1593,7 @@ if (DEBUGGER) {
Debugger.prototype.messageIO = function(component, port, bOut, addrFrom, name, bIn, bitsMessage)
{
bitsMessage |= Messages.PORT;
if (addrFrom == null || (this.bitsMessage & bitsMessage) == bitsMessage) {
if (name == null || (this.bitsMessage & bitsMessage) == bitsMessage) {
this.message(component.idComponent + '.' + (bOut != null? "outPort" : "inPort") + '(' + str.toHexWord(port) + ',' + (name? name : "unknown") + (bOut != null? ',' + str.toHexByte(bOut) : "") + ')' + (bIn != null? (": " + str.toHexByte(bIn)) : "") + (addrFrom != null? (" at " + this.toHexOffset(addrFrom)) : ""));
}
};
@ -4499,7 +4502,11 @@ if (DEBUGGER) {
{
if (fSave) {
if (!sCmd) {
sCmd = this.aPrevCmds[this.iPrevCmd+1];
if (this.fAssemble) {
sCmd = "end";
} else {
sCmd = this.aPrevCmds[this.iPrevCmd+1];
}
} else {
if (this.iPrevCmd < 0 && this.aPrevCmds.length) {
this.iPrevCmd = 0;

View file

@ -50,11 +50,19 @@ if (NODE) {
* screenWidth: width of the screen canvas, in pixels
* screenHeight: height of the screen canvas, in pixels
* screenColor: background color of the screen canvas (default is black)
* aspectRatio
* frameBuffer
* interruptRate (eg, 120)
* refreshRate (eg, 60)
* rotation (eg, 90)
* screenRotation: the amount of counter-clockwise rotation required (eg, 90)
* aspectRatio (eg, 1.33)
* bufferAddr: the starting address of the frame buffer (eg, 0x2400)
* bufferCols: the width of a single frame buffer row, in pixels (eg, 256)
* bufferRows: the number of frame buffer rows (eg, 224)
* bufferBits: the number of bits per pixel (eg, 1)
* interruptRate: normally the same as (or some multiple of) the refreshRate (eg, 120)
* refreshRate: how many times updateScreen() should be called per second (eg, 60)
*
* We record all the above values now, but we defer creation of the frame buffer until our initBus()
* handler is called. At that point, we will also compute the extent of the frame buffer, determine the
* appropriate "cell" size (ie, the number of pixels that updateScreen() will fetch and process at once),
* and allocate our cell cache.
*
* @constructor
* @extends Component

View file

@ -81,7 +81,7 @@ function ChipSet(parmsChipSet)
Component.notice("Unrecognized ChipSet model: " + model);
}
this.model = model && ChipSet.MODELS[model] || ChipSet.MODEL_5150_OTHER;
this.model = ChipSet.MODELS[model] || ChipSet.MODEL_5150_OTHER;
var bSwitches;
this.aDIPSwitches = [];
@ -2126,6 +2126,36 @@ ChipSet.prototype.restore = function(data)
return true;
};
/**
* start()
*
* Notification from the CPU that it's starting.
*
* @this {ChipSet}
*/
ChipSet.prototype.start = function()
{
/*
* Currently, all we do with this notification is allow the speaker to make noise.
*/
this.setSpeaker();
};
/**
* stop()
*
* Notification from the CPU that it's stopping.
*
* @this {ChipSet}
*/
ChipSet.prototype.stop = function()
{
/*
* Currently, all we do with this notification is prevent the speaker from making noise.
*/
this.setSpeaker();
};
ChipSet.aDMAControllerInit = [0, null, null, 0, new Array(4), 0];
/**

View file

@ -1052,7 +1052,7 @@ CPU.prototype.startCPU = function(fUpdateFocus)
if (this.cmp) this.cmp.start(this.aCounts.msStartRun, this.getCycles());
this.flags.fRunning = true;
this.flags.fStarting = true;
if (this.chipset) this.chipset.setSpeaker();
if (this.chipset) this.chipset.start();
var controlRun = this.bindings["run"];
if (controlRun) controlRun.textContent = "Halt";
if (this.cmp) {
@ -1096,7 +1096,7 @@ CPU.prototype.stopCPU = function(fComplete)
this.nRunCycles = 0;
if (this.flags.fRunning) {
this.flags.fRunning = false;
if (this.chipset) this.chipset.setSpeaker();
if (this.chipset) this.chipset.stop();
var controlRun = this.bindings["run"];
if (controlRun) controlRun.textContent = "Run";
}

View file

@ -52,18 +52,17 @@ if (NODE) {
*
* model: model (eg, "mda" for Monochrome Display Adapter)
* mode: initial video mode (default is null, which selects a mode based on model)
* memory: amount of installed memory (ignored for MDA/CGA)
* screenWidth: width of the screen canvas, in pixels
* screenHeight: height of the screen canvas, in pixels
* screenColor: background color of the screen canvas (default is black)
* scale: true for font scaling, false (default) to center the display on the screen
* charCols: number of character columns
* charRows: number of character rows
* fontROM: path to .rom file (or a JSON representation) containing the character set
* screenColor: background color of the screen canvas (default is black)
* touchScreen: string specifying desired touch-screen support (default is none)
* autoLock: true to (attempt to) auto-lock the mouse to the canvas (default is false)
*
* An EGA may specify the following additional properties:
* An EGA/VGA may specify the following additional properties:
*
* switches: string representing EGA switches (see "SW1-SW4" documentation below)
* memory: the size of the EGA's on-board memory (overrides EGA's Video.cardSpecs)

View file

@ -604,7 +604,7 @@
<xsl:call-template name="component">
<xsl:with-param name="machine" select="$machine"/>
<xsl:with-param name="class" select="'cpu'"/>
<xsl:with-param name="parms">,model:<xsl:value-of select="$model"/>,stepping:'<xsl:value-of select="$stepping"/>',fpu:<xsl:value-of select="$fpu"/>,cycles:<xsl:value-of select="$cycles"/>,multiplier:<xsl:value-of select="$multiplier"/>,autoStart:<xsl:value-of select="$autoStart"/>,csStart:<xsl:value-of select="$csStart"/>,csInterval:<xsl:value-of select="$csInterval"/>,csStop:<xsl:value-of select="$csStop"/></xsl:with-param>
<xsl:with-param name="parms">,model:'<xsl:value-of select="$model"/>',stepping:'<xsl:value-of select="$stepping"/>',fpu:<xsl:value-of select="$fpu"/>,cycles:<xsl:value-of select="$cycles"/>,multiplier:<xsl:value-of select="$multiplier"/>,autoStart:<xsl:value-of select="$autoStart"/>,csStart:<xsl:value-of select="$csStart"/>,csInterval:<xsl:value-of select="$csInterval"/>,csStop:<xsl:value-of select="$csStop"/></xsl:with-param>
</xsl:call-template>
</xsl:template>
@ -631,7 +631,7 @@
<xsl:call-template name="component">
<xsl:with-param name="machine" select="$machine"/>
<xsl:with-param name="class" select="'fpu'"/>
<xsl:with-param name="parms">,model:<xsl:value-of select="$model"/>,stepping:'<xsl:value-of select="$stepping"/>'</xsl:with-param>
<xsl:with-param name="parms">,model:'<xsl:value-of select="$model"/>',stepping:'<xsl:value-of select="$stepping"/>'</xsl:with-param>
</xsl:call-template>
</xsl:template>
@ -646,7 +646,7 @@
<xsl:variable name="model">
<xsl:choose>
<xsl:when test="@model"><xsl:value-of select="@model"/></xsl:when>
<xsl:otherwise>5150</xsl:otherwise>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="sw1">
@ -984,7 +984,49 @@
<xsl:otherwise>224</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="screenColor">
<xsl:choose>
<xsl:when test="@screencolor"><xsl:value-of select="@screencolor"/></xsl:when>
<xsl:when test="@screenColor"><xsl:value-of select="@screenColor"/></xsl:when>
<xsl:otherwise>black</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="screenRotation">
<xsl:choose>
<xsl:when test="@screenRotation"><xsl:value-of select="@screenRotation"/></xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="bufferAddr">
<xsl:choose>
<xsl:when test="@bufferAddr"><xsl:value-of select="@bufferAddr"/></xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="bufferCols">
<xsl:choose>
<xsl:when test="@bufferCols"><xsl:value-of select="@bufferCols"/></xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="bufferRows">
<xsl:choose>
<xsl:when test="@bufferRows"><xsl:value-of select="@bufferRows"/></xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="bufferBits">
<xsl:choose>
<xsl:when test="@bufferBits"><xsl:value-of select="@bufferBits"/></xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="memory">
<!-- PCjs uses this to determine the amount of Video memory installed; in retrospect, "bufferSize"
might seem like a better choice for the name, but PCjs doesn't use ANY of the "buffer" properties,
because it supports dynamic frame buffers (ie, frame buffers whose location and size change according
to the programmed mode). PC8080 machines use the "buffer" properties instead, because their frame
buffers generally have a fixed location, size, and format. -->
<xsl:choose>
<xsl:when test="@memory"><xsl:value-of select="@memory"/></xsl:when>
<xsl:otherwise>0</xsl:otherwise>
@ -1024,13 +1066,6 @@
<xsl:otherwise/>
</xsl:choose>
</xsl:variable>
<xsl:variable name="screenColor">
<xsl:choose>
<xsl:when test="@screencolor"><xsl:value-of select="@screencolor"/></xsl:when>
<xsl:when test="@screenColor"><xsl:value-of select="@screenColor"/></xsl:when>
<xsl:otherwise>black</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="touchScreen">
<xsl:choose>
<xsl:when test="@touchscreen"><xsl:value-of select="@touchscreen"/></xsl:when>
@ -1052,12 +1087,6 @@
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="frameBuffer">
<xsl:choose>
<xsl:when test="@frameBuffer"><xsl:value-of select="@frameBuffer"/></xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="interruptRate">
<xsl:choose>
<xsl:when test="@interruptRate"><xsl:value-of select="@interruptRate"/></xsl:when>
@ -1070,16 +1099,10 @@
<xsl:otherwise>60</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="rotation">
<xsl:choose>
<xsl:when test="@rotation"><xsl:value-of select="@rotation"/></xsl:when>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:call-template name="component">
<xsl:with-param name="machine" select="$machine"/>
<xsl:with-param name="class">video</xsl:with-param>
<xsl:with-param name="parms">,model:'<xsl:value-of select="$model"/>',mode:<xsl:value-of select="$mode"/>,screenWidth:<xsl:value-of select="$screenWidth"/>,screenHeight:<xsl:value-of select="$screenHeight"/>,memory:<xsl:value-of select="$memory"/>,switches:'<xsl:value-of select="$switches"/>',scale:<xsl:value-of select="$scale"/>,charCols:<xsl:value-of select="$charCols"/>,charRows:<xsl:value-of select="$charRows"/>,fontROM:'<xsl:value-of select="$fontROM"/>',screenColor:'<xsl:value-of select="$screenColor"/>',touchScreen:'<xsl:value-of select="$touchScreen"/>',autoLock:<xsl:value-of select="$autoLock"/>,aspectRatio:<xsl:value-of select="$aspectRatio"/>,frameBuffer:<xsl:value-of select="$frameBuffer"/>,interruptRate:<xsl:value-of select="$interruptRate"/>,refreshRate:<xsl:value-of select="$refreshRate"/>,rotation:<xsl:value-of select="$rotation"/></xsl:with-param>
<xsl:with-param name="parms">,model:'<xsl:value-of select="$model"/>',mode:<xsl:value-of select="$mode"/>,screenWidth:<xsl:value-of select="$screenWidth"/>,screenHeight:<xsl:value-of select="$screenHeight"/>,screenColor:'<xsl:value-of select="$screenColor"/>',screenRotation:<xsl:value-of select="$screenRotation"/>,bufferAddr:<xsl:value-of select="$bufferAddr"/>,bufferCols:<xsl:value-of select="$bufferCols"/>,bufferRows:<xsl:value-of select="$bufferRows"/>,bufferBits:<xsl:value-of select="$bufferBits"/>,memory:<xsl:value-of select="$memory"/>,switches:'<xsl:value-of select="$switches"/>',scale:<xsl:value-of select="$scale"/>,charCols:<xsl:value-of select="$charCols"/>,charRows:<xsl:value-of select="$charRows"/>,fontROM:'<xsl:value-of select="$fontROM"/>',touchScreen:'<xsl:value-of select="$touchScreen"/>',autoLock:<xsl:value-of select="$autoLock"/>,aspectRatio:<xsl:value-of select="$aspectRatio"/>,interruptRate:<xsl:value-of select="$interruptRate"/>,refreshRate:<xsl:value-of select="$refreshRate"/></xsl:with-param>
</xsl:call-template>
</xsl:template>