More 8080 improvements, including some initial "Fake CP/M" support

This commit is contained in:
Jeff Parsons 2016-04-27 08:28:52 -07:00
commit 5b6f6756a2
25 changed files with 1051 additions and 842 deletions

View file

@ -12,7 +12,7 @@ indent_style = space
indent_size = 8
trim_trailing_whitespace = true
[*.{ASM,INC,asm,inc,nasm,lst}]
[*.{ASM,INC,MAC,NASM,LST,asm,inc,mac,nasm,lst}]
indent_style = tab
indent_size = 8
trim_trailing_whitespace = true

View file

@ -2,7 +2,7 @@
<?xml-stylesheet type="text/xsl" href="/versions/pc8080/1.21.7/machine.xsl"?>
<machine id="invaders" class="pc8080" border="1" pos="center" background="#FAEBD7">
<name pos="center">Space Invaders</name>
<computer id="computer"/>
<computer id="computer" busWidth="16"/>
<cpu id="cpu8080" model="8080" cycles="2000000"/>
<rom id="romH" addr="0x0000" size="0x0800" file="/devices/pc8080/rom/invaders/INVADERS-H.json"/>
<rom id="romG" addr="0x0800" size="0x0800" file="/devices/pc8080/rom/invaders/INVADERS-G.json"/>
@ -14,6 +14,6 @@
<title>224x256 Screen (Rotated)</title>
</menu>
</video>
<panel ref="/devices/pc8080/panel/default.xml"/>
<debugger id="debugger"/>
<panel ref="/devices/pc8080/panel/left.xml"/>
<debugger id="debugger" commands="s 8086"/>
</machine>

View file

@ -0,0 +1,14 @@
---
layout: page
title: 8080 Test Machine
permalink: /devices/pc8080/machine/test/
machines:
- type: pc8080
id: test8080
debugger: true
---
8080 Test Machine
---
{% include machine.html id="test8080" %}

View file

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="/versions/pc8080/1.21.7/machine.xsl"?>
<machine id="test8080" class="pc8080" border="1" pos="center" background="#FAEBD7">
<name pos="center">8080 Test Machine</name>
<computer id="computer" busWidth="16"/>
<cpu id="cpu8080" model="8080" cycles="2000000"/>
<rom id="romTest" addr="0x0100" size="0x0400" file="/devices/pc8080/rom/test/8080PRE.json" writable="true"/>
<ram id="ramTest" addr="0x0500" size="0xFB00"/>
<panel ref="/devices/pc8080/panel/wide.xml"/>
<debugger id="debugger" commands="s 8086"/>
</machine>

View file

@ -6,13 +6,9 @@
</control>
<control type="container" class="pcjs-registers" width="100%" padleft="8px">
<control type="register" label="A" binding="A" width="20px" padright="8px" padbottom="8px">00</control>
<control type="register" label="F" binding="F" width="20px" padright="8px" padbottom="8px">00</control>
<control type="register" label="B" binding="B" width="20px" padright="8px" padbottom="8px">00</control>
<control type="register" label="C" binding="C" width="20px" padright="8px" padbottom="8px">00</control>
<control type="register" label="D" binding="D" width="20px" padright="8px" padbottom="8px">00</control>
<control type="register" label="E" binding="E" width="20px" padright="8px" padbottom="8px">00</control>
<control type="register" label="H" binding="H" width="20px" padright="8px" padbottom="8px">00</control>
<control type="register" label="L" binding="L" width="20px" padright="8px" padbottom="8px">00</control>
<control type="register" label="BC" binding="BC" width="40px" padright="8px" padbottom="8px">0000</control>
<control type="register" label="DE" binding="DE" width="40px" padright="8px" padbottom="8px">0000</control>
<control type="register" label="HL" binding="HL" width="40px" padright="8px" padbottom="8px">0000</control>
<control type="register" label="SP" binding="SP" width="40px" padright="8px" padbottom="8px">0000</control>
<control type="register" label="PC" binding="PC" width="40px" padright="8px" padbottom="8px">0000</control>
<control type="container" pos="left">

View file

@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<panel id="panel" width="100%" padding="8px">
<name>Control Panel</name>
<control type="container" class="pcjs-textarea" width="100%" padright="8px">
<control type="textarea" binding="print" width="100%" height="252px" pos="relative" style="resize:vertical;"/>
</control>
<control type="container" class="pcjs-registers" width="100%" padleft="8px">
<control type="register" label="A" binding="A" width="20px" padright="8px" padbottom="8px">00</control>
<control type="register" label="BC" binding="BC" width="40px" padright="8px" padbottom="8px">0000</control>
<control type="register" label="DE" binding="DE" width="40px" padright="8px" padbottom="8px">0000</control>
<control type="register" label="HL" binding="HL" width="40px" padright="8px" padbottom="8px">0000</control>
<control type="register" label="SP" binding="SP" width="40px" padright="8px" padbottom="8px">0000</control>
<control type="register" label="PC" binding="PC" width="40px" padright="8px" padbottom="8px">0000</control>
<control type="container" pos="left">
<control type="flag" label="I" binding="IF" width="8px" padright="4px" padbottom="8px">0</control>
<control type="flag" label="S" binding="SF" width="8px" padright="4px" padbottom="8px">0</control>
<control type="flag" label="Z" binding="ZF" width="8px" padright="4px" padbottom="8px">0</control>
<control type="flag" label="A" binding="AF" width="8px" padright="4px" padbottom="8px">0</control>
<control type="flag" label="P" binding="PF" width="8px" padright="4px" padbottom="8px">0</control>
<control type="flag" label="C" binding="CF" width="8px" padright="4px" padbottom="8px">0</control>
</control>
<control type="description" padleft="16px" padbottom="8px">Speed:</control>
<control type="description" binding="speed" padleft="4px" padbottom="8px">Stopped</control>
</control>
<control type="container" padleft="8px" padbottom="8px">
<control type="button" binding="run">Run</control>
<control type="button" binding="step">Step</control>
<control type="button" binding="reset">Reset</control>
<control type="button" binding="setSpeed">Fast</control>
<control pos=""/>
<control type="text" binding="debugInput" width="100px"/>
<control type="button" binding="debugEnter">Enter</control>
<control type="button" binding="clear">Clear</control>
</control>
</panel>

View file

@ -0,0 +1,66 @@
{"bytes":[
62,1,254,2,202,0,0,254,1,194,0,0,195,17,1,118,
255,205,23,1,195,0,0,225,124,254,1,202,33,1,195,0,
0,125,254,20,202,42,1,195,0,0,49,153,3,241,193,209,
225,49,169,3,229,213,197,245,58,161,3,254,2,194,0,0,
58,162,3,254,4,194,0,0,58,163,3,254,6,194,0,0,
58,164,3,254,8,194,0,0,58,165,3,254,10,194,0,0,
58,166,3,254,12,194,0,0,58,167,3,254,14,194,0,0,
58,168,3,254,16,194,0,0,33,169,3,126,254,165,194,0,
0,33,170,3,126,254,60,194,0,0,49,0,5,33,149,1,
229,201,195,0,0,62,255,230,15,254,15,194,0,0,62,90,
230,15,254,10,194,0,0,15,254,5,194,0,0,15,254,130,
194,0,0,15,254,65,194,0,0,15,254,160,194,0,0,33,
52,18,229,193,120,254,18,194,0,0,121,254,52,194,0,0,
33,1,0,229,241,220,219,1,195,82,3,225,33,214,0,229,
241,212,231,1,195,82,3,225,33,245,1,229,33,1,0,229,
241,216,205,82,3,33,2,2,229,33,214,0,229,241,208,205,
82,3,33,1,0,229,241,218,13,2,205,82,3,33,214,0,
229,241,210,24,2,205,82,3,33,4,0,229,241,236,35,2,
195,82,3,225,33,211,0,229,241,228,47,2,195,82,3,225,
33,61,2,229,33,4,0,229,241,232,205,82,3,33,74,2,
229,33,211,0,229,241,224,205,82,3,33,4,0,229,241,234,
85,2,205,82,3,33,211,0,229,241,226,96,2,205,82,3,
33,64,0,229,241,204,107,2,195,82,3,225,33,151,0,229,
241,196,119,2,195,82,3,225,33,133,2,229,33,64,0,229,
241,200,205,82,3,33,146,2,229,33,151,0,229,241,192,205,
82,3,33,64,0,229,241,202,157,2,205,82,3,33,151,0,
229,241,194,168,2,205,82,3,33,128,0,229,241,252,179,2,
195,82,3,225,33,87,0,229,241,244,191,2,195,82,3,225,
33,205,2,229,33,128,0,229,241,248,205,82,3,33,218,2,
229,33,87,0,229,241,240,205,82,3,33,128,0,229,241,250,
229,2,205,82,3,33,87,0,229,241,242,240,2,205,82,3,
33,247,2,233,205,82,3,62,165,6,4,15,5,194,251,2,
254,90,196,82,3,6,16,60,5,194,7,3,254,106,196,82,
3,6,0,33,0,0,35,5,194,22,3,124,254,1,196,82,
3,125,254,0,196,82,3,17,50,3,14,9,205,5,0,195,
0,0,56,48,56,48,32,80,114,101,108,105,109,105,110,97,
114,121,32,116,101,115,116,115,32,99,111,109,112,108,101,116,
101,36,193,38,4,120,15,15,15,15,230,15,111,126,205,138,
3,120,230,15,111,126,205,138,3,121,15,15,15,15,230,15,
111,126,205,138,3,121,230,15,111,126,205,138,3,62,13,205,
138,3,62,10,205,138,3,195,0,0,245,197,213,229,14,2,
95,205,5,0,225,209,193,241,201,2,4,6,8,10,12,14,
16,0,0,0,0,0,0,0,0,165,60,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
48,49,50,51,52,53,54,55,56,57,97,98,99,100,101,102,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]
}

View file

@ -901,10 +901,16 @@
<xsl:otherwise/>
</xsl:choose>
</xsl:variable>
<xsl:variable name="writable">
<xsl:choose>
<xsl:when test="@writable"><xsl:value-of select="@writable"/></xsl:when>
<xsl:otherwise>false</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:call-template name="component">
<xsl:with-param name="machine" select="$machine"/>
<xsl:with-param name="class">rom</xsl:with-param>
<xsl:with-param name="parms">,addr:<xsl:value-of select="$addr"/>,size:<xsl:value-of select="$size"/>,alias:<xsl:value-of select="$alias"/>,file:'<xsl:value-of select="$file"/>',notify:'<xsl:value-of select="$notify"/>'</xsl:with-param>
<xsl:with-param name="parms">,addr:<xsl:value-of select="$addr"/>,size:<xsl:value-of select="$size"/>,alias:<xsl:value-of select="$alias"/>,file:'<xsl:value-of select="$file"/>',notify:'<xsl:value-of select="$notify"/>',writable:<xsl:value-of select="$writable"/></xsl:with-param>
</xsl:call-template>
</xsl:template>
@ -1139,7 +1145,7 @@
<xsl:choose>
<xsl:when test="@buswidth"><xsl:value-of select="@buswidth"/></xsl:when>
<xsl:when test="@busWidth"><xsl:value-of select="@busWidth"/></xsl:when>
<xsl:otherwise>20</xsl:otherwise>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="resume">

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -78,7 +78,7 @@ function Bus(parmsBus, cpu, dbg)
this.cpu = cpu;
this.dbg = dbg;
this.nBusWidth = parmsBus['buswidth'] || 16;
this.nBusWidth = parmsBus['busWidth'] || 16;
/*
* Compute all Bus memory block parameters, based on the width of the bus.
@ -108,6 +108,7 @@ function Bus(parmsBus, cpu, dbg)
*
* Bus Width Block Shift Block Size
* --------- ----------- ----------
* 16 bits (64Kb address space): 10 1Kb (64 maximum blocks)
* 20 bits (1Mb address space): 12 4Kb (256 maximum blocks)
* 24 bits (16Mb address space): 14 16Kb (1K maximum blocks)
* 32 bits (4Gb address space); 15 32Kb (128K maximum blocks)
@ -119,7 +120,7 @@ function Bus(parmsBus, cpu, dbg)
*/
this.addrTotal = Math.pow(2, this.nBusWidth);
this.nBusLimit = this.nBusMask = (this.addrTotal - 1) | 0;
this.nBlockShift = (this.nBusWidth <= 20)? 12 : (this.nBusWidth <= 24? 14 : 15);
this.nBlockShift = (this.nBusWidth <= 16)? 10 : ((this.nBusWidth <= 20)? 12 : (this.nBusWidth <= 24? 14 : 15));
this.nBlockSize = 1 << this.nBlockShift;
this.nBlockLen = this.nBlockSize >> 2;
this.nBlockLimit = this.nBlockSize - 1;
@ -268,16 +269,15 @@ Bus.prototype.powerUp = function(data, fRepower)
* allocations, etc.
*
* We've relaxed some of the original requirements (ie, that addresses must start at a
* block-granular address, or that sizes must be equal to exactly one or more blocks), because
* machines with large block sizes can make it impossible to load certain ROMs at at their
* required addresses.
* block-granular address, or that sizes must be equal to exactly one or more blocks),
* because machines with large block sizes can make it impossible to load certain ROMs at
* their required addresses. Every allocation still allocates a whole number of blocks.
*
* Even so, Bus memory management does NOT provide a general-purpose heap. Most memory
* allocations occur during machine initialization and never change. The only notable
* exception is the Video frame buffer, which ranges from 4Kb (MDA) to 16Kb (CGA) to
* 32Kb/64Kb/128Kb (EGA), and only the EGA changes its buffer address post-initialization.
* allocations occur during machine initialization and never change. In particular, there
* is NO support for removing partial-block allocations.
*
* Each Memory block keeps track of a single address (addr) and length (used), indicating
* Each Memory block keeps track of a start address (addr) and length (used), indicating
* the used space within the block; any free space that precedes or follows that used space
* can be allocated later, by simply extending the beginning or ending of the previously used
* space. However, any holes that might have existed between the original allocation and an
@ -293,17 +293,18 @@ Bus.prototype.addMemory = function(addr, size, type)
{
var iBlock = addr >>> this.nBlockShift;
while (size > 0 && iBlock < this.aMemBlocks.length) {
var block = this.aMemBlocks[iBlock];
var addrBlock = iBlock * this.nBlockSize;
var sizeBlock = size > this.nBlockSize? this.nBlockSize : size;
var sizeBlock = this.nBlockSize - (addr - addrBlock);
if (sizeBlock > size) sizeBlock = size;
if (block && block.size) {
if (block.type == type) {
/*
* Where there is already a block with a non-zero size, we can allow the allocation only if:
* Where there is already a similar block with a non-zero size, we allow the allocation only if:
*
* 1) addr + size <= block.addr (the request precedes the used portion of the current block)
* or:
* 1) addr + size <= block.addr (the request precedes the used portion of the current block), or
* 2) addr >= block.addr + block.used (the request follows the used portion of the current block)
*/
if (addr + size <= block.addr) {
@ -315,23 +316,27 @@ Bus.prototype.addMemory = function(addr, size, type)
var sizeAvail = block.size - (addr - addrBlock);
if (sizeAvail > size) sizeAvail = size;
block.used = addr - block.addr + sizeAvail;
size -= sizeAvail;
addr = addrBlock + this.nBlockSize;
size -= sizeAvail;
iBlock++;
continue;
}
}
return this.reportError(Bus.ERROR.ADD_MEM_INUSE, addr, size);
}
var blockOld = this.aMemBlocks[iBlock];
var blockNew = new Memory(addr, sizeBlock, this.nBlockSize, type);
blockNew.copyBreakpoints(this.dbg, blockOld);
blockNew.copyBreakpoints(this.dbg, block);
this.aMemBlocks[iBlock++] = blockNew;
addr = addrBlock + this.nBlockSize;
size -= sizeBlock;
}
if (size <= 0) {
return true;
}
return this.reportError(Bus.ERROR.ADD_MEM_BADRANGE, addr, size);
};

View file

@ -166,7 +166,7 @@ function Computer(parmsComputer, parmsMachine, fSuspended) {
/*
* Initialize the Bus component
*/
this.bus = new Bus({'id': this.idMachine + '.bus', 'buswidth': this.nBusWidth}, this.cpu, this.dbg);
this.bus = new Bus({'id': this.idMachine + '.bus', 'busWidth': this.nBusWidth}, this.cpu, this.dbg);
/*
* Iterate through all the components and connect them to the Control Panel, if any

View file

@ -104,6 +104,7 @@ var CPUDef = {
* Opcode definitions
*/
OPCODE: {
HLT: 0x76,
ACI: 0xCE, // PS.ALL
CALL: 0xCD,
RST0: 0xC7

View file

@ -1299,23 +1299,37 @@ CPUDef.opHLT = function()
* we are signalling to stepCPU() that it's free to end the current burst AND that it should not
* execute any more instructions until checkINTR() indicates a hardware interrupt is requested.
*/
var addr = this.getPC() - 1;
/*
* If any HLT check functions are installed, call them, and if any of them return true, then
* immediately stop HLT processing.
*/
if (this.afnHalt.length) {
for (var i = 0; i < this.afnHalt.length; i++) {
if (this.afnHalt[i](addr)) return;
}
}
this.intFlags |= CPUDef.INTFLAG.HALT;
this.nStepCycles -= 7;
/*
* If a Debugger is present and the HALT message category is enabled, then we REALLY halt the CPU,
* on the theory that whoever's using the Debugger would like to see HLTs.
*/
if (DEBUGGER && this.dbg && this.messageEnabled(Messages.HALT)) {
this.setPC(this.getPC() - 1); // this is purely for the Debugger's benefit, to show the HLT
this.setPC(addr); // this is purely for the Debugger's benefit, to show the HLT
this.dbg.stopCPU();
return;
}
/*
* We also REALLY halt the machine if interrupts have been disabled, since that means it's dead
* in the water (we have no NMI generation mechanism at the moment).
*/
if (!this.getIF()) {
if (DEBUGGER && this.dbg) this.setPC(this.getPC() - 1);
if (DEBUGGER && this.dbg) this.setPC(addr);
this.stopCPU();
}
};

View file

@ -100,6 +100,12 @@ function CPUSim(parmsCPU)
this.nBusMask = 0;
this.nBlockShift = this.nBlockSize = this.nBlockLimit = this.nBlockTotal = this.nBlockMask = 0;
/*
* Array of halt handlers, if any (see addHaltCheck)
*/
this.afnHalt = [];
this.addrReset = 0x0000;
/*
* This initial resetRegs() call is important to create all the registers, so that if/when we call restore(),
* it will have something to fill in.
@ -130,14 +136,22 @@ CPUSim.prototype.initMemory = function(aBusBlocks, nBlockShift, nBusMask)
this.nBusMask = nBusMask;
};
/**
* addHaltCheck(fn)
*
* Records a function that will be called during HLT opcode processing.
*
* @this {CPUSim}
* @param {function(number)} fn
*/
CPUSim.prototype.addHaltCheck = function(fn)
{
this.afnHalt.push(fn);
};
/**
* addMemBreak(addr, fWrite)
*
* NOTE: addMemBreak() could be merged with addMemCheck(), but the new merged interface would
* have to provide one additional parameter indicating whether the Debugger or the CPU is the client.
*
* For now, this is simply a DEBUGGER-only interface.
*
* @this {CPUSim}
* @param {number} addr
* @param {boolean} fWrite is true for a memory write breakpoint, false for a memory read breakpoint
@ -153,11 +167,6 @@ CPUSim.prototype.addMemBreak = function(addr, fWrite)
/**
* removeMemBreak(addr, fWrite)
*
* NOTE: removeMemBreak() could be merged with removeMemCheck(), but the new merged interface would
* have to provide one additional parameter indicating whether the Debugger or the CPU is the client.
*
* For now, this is simply a DEBUGGER-only interface.
*
* @this {CPUSim}
* @param {number} addr
* @param {boolean} fWrite is true for a memory write breakpoint, false for a memory read breakpoint
@ -170,41 +179,6 @@ CPUSim.prototype.removeMemBreak = function(addr, fWrite)
}
};
/**
* addMemCheck(addr, fWrite)
*
* These functions provide Debug register functionality to the CPU by leveraging the same Memory block-based
* breakpoint support originally created for our built-in Debugger. Only minimal changes were required to the
* Memory component, by adding additional checkMemoryException() call-outs from the "checked" Memory access
* functions.
*
* Note that those call-outs occur only AFTER our own Debugger (if present) has checked the address and has
* passed on it, because we want our own Debugger's breakpoints to take precedence over any breakpoints that
* the emulated machine may have enabled.
*
* @this {CPUSim}
* @param {number} addr
* @param {boolean} fWrite is true for a memory write check, false for a memory read check
*/
CPUSim.prototype.addMemCheck = function(addr, fWrite)
{
var iBlock = addr >>> this.nBlockShift;
this.aBusBlocks[iBlock].addBreakpoint(addr & this.nBlockLimit, fWrite, this);
};
/**
* removeMemCheck(addr, fWrite)
*
* @this {CPUSim}
* @param {number} addr
* @param {boolean} fWrite is true for a memory write check, false for a memory read check
*/
CPUSim.prototype.removeMemCheck = function(addr, fWrite)
{
var iBlock = addr >>> this.nBlockShift;
this.aBusBlocks[iBlock].removeBreakpoint(addr & this.nBlockLimit, fWrite);
};
/**
* initProcessor()
*
@ -244,7 +218,7 @@ CPUSim.prototype.resetRegs = function()
this.regH = 0;
this.regL = 0;
this.setSP(0);
this.setPC(0);
this.setPC(this.addrReset);
/*
* This resets the Processor Status flags (regPS), along with all the internal "result registers";
@ -260,6 +234,18 @@ CPUSim.prototype.resetRegs = function()
this.intFlags = CPUDef.INTFLAG.NONE;
};
/**
* setReset(addr)
*
* @this {CPUSim}
* @param {number} addr
*/
CPUSim.prototype.setReset = function(addr)
{
this.addrReset = addr;
this.setPC(addr);
};
/**
* getChecksum()
*
@ -336,13 +322,16 @@ CPUSim.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
case "A":
case "B":
case "C":
case "BC":
case "D":
case "E":
case "DE":
case "H":
case "L":
case "HL":
case "SP":
case "PC":
case "F":
case "PS":
case "SF":
case "ZF":
case "AF":
@ -1047,14 +1036,17 @@ CPUSim.prototype.updateStatus = function(fForce)
this.updateReg("A", this.regA);
this.updateReg("B", this.regB);
this.updateReg("C", this.regC);
this.updateReg("BC", this.getBC(), 4);
this.updateReg("D", this.regD);
this.updateReg("E", this.regE);
this.updateReg("DE", this.getDE(), 4);
this.updateReg("H", this.regH);
this.updateReg("L", this.regL);
this.updateReg("HL", this.getHL(), 4);
this.updateReg("SP", this.getSP(), 4);
this.updateReg("PC", this.getPC(), 4);
var regPS = this.getPS();
this.updateReg("F", regPS, 2);
this.updateReg("PS", regPS, 4);
this.updateReg("SF", (regPS & CPUDef.PS.SF), 1);
this.updateReg("ZF", (regPS & CPUDef.PS.ZF), 1);
this.updateReg("AF", (regPS & CPUDef.PS.AF), 1);

View file

@ -87,7 +87,7 @@ function Debugger(parmsDbg)
Component.call(this, "Debugger", parmsDbg, Debugger);
this.style = Debugger.STYLE_8086;
this.style = Debugger.STYLE_8080;
/*
* These keep track of instruction activity, but only when tracing or when Debugger checks
@ -300,7 +300,7 @@ if (DEBUGGER) {
"CALLC", "CALLS", "CALLNC", "CALLNZ", "CALLNS", "CALLP", "CALLNP", "CALLZ",
"NOT", "CMC", "CMP", "CMP", "DAA", "ADD", "DEC", "DEC",
"CLI", "STI", "HLT", "IN", "INC", "INC", "JMP", "JC",
"JS", "JNC", "JNZ", "JNS", "JP", "JNP", "JZ", "LDA",
"JS", "JNC", "JNZ", "JNS", "JP", "JNP", "JZ", "MOV",
"MOV", "MOV", "MOV", "MOV", "MOV", "NOP", "OR", "OR",
"OUT", "JMP", "POP", "PUSH", "RCL", "RCR", "RET", "RETC",
"RETS", "RETNC", "RETNZ", "RETNS", "RETP", "RETNP", "RETZ", "ROL",
@ -321,7 +321,7 @@ if (DEBUGGER) {
Debugger.REG_HL = 0x0A;
Debugger.REG_SP = 0x0B;
Debugger.REG_PC = 0x0C;
Debugger.REG_PS = 0x0D; // aka PSW (aka AF if Z80-style)
Debugger.REG_PS = 0x0D; // aka PSW (aka AF if Z80-style mnemonics)
Debugger.REGS = [
"B", "C", "D", "E", "H", "L", "M", "A", "BC", "DE", "HL", "SP", "PC", "PSW"
@ -410,7 +410,7 @@ if (DEBUGGER) {
/* 0x06 */ [Debugger.INS.MVI, Debugger.TYPE_B, Debugger.TYPE_IMM],
/* 0x07 */ [Debugger.INS.RLC],
/* 0x08 */ [Debugger.INS.NOP, Debugger.TYPE_UNDOC],
/* 0x09 */ [Debugger.INS.DAD, Debugger.TYPE_HL, Debugger.TYPE_BC], // let's be more explicit about the operands
/* 0x09 */ [Debugger.INS.DAD, Debugger.TYPE_HL | Debugger.TYPE_OPT, Debugger.TYPE_BC],
/* 0x0A */ [Debugger.INS.LDAX, Debugger.TYPE_A | Debugger.TYPE_OPT, Debugger.TYPE_BC | Debugger.TYPE_MEM],
/* 0x0B */ [Debugger.INS.DCX, Debugger.TYPE_BC],
/* 0x0C */ [Debugger.INS.INR, Debugger.TYPE_C],
@ -426,7 +426,7 @@ if (DEBUGGER) {
/* 0x16 */ [Debugger.INS.MVI, Debugger.TYPE_D, Debugger.TYPE_IMM],
/* 0x17 */ [Debugger.INS.RAL],
/* 0x18 */ [Debugger.INS.NOP, Debugger.TYPE_UNDOC],
/* 0x19 */ [Debugger.INS.DAD, Debugger.TYPE_HL, Debugger.TYPE_DE], // let's be more explicit about the operands
/* 0x19 */ [Debugger.INS.DAD, Debugger.TYPE_HL | Debugger.TYPE_OPT, Debugger.TYPE_DE],
/* 0x1A */ [Debugger.INS.LDAX, Debugger.TYPE_A | Debugger.TYPE_OPT, Debugger.TYPE_DE | Debugger.TYPE_MEM],
/* 0x1B */ [Debugger.INS.DCX, Debugger.TYPE_DE],
/* 0x1C */ [Debugger.INS.INR, Debugger.TYPE_E],
@ -442,7 +442,7 @@ if (DEBUGGER) {
/* 0x26 */ [Debugger.INS.MVI, Debugger.TYPE_H, Debugger.TYPE_IMM],
/* 0x27 */ [Debugger.INS.DAA],
/* 0x28 */ [Debugger.INS.NOP, Debugger.TYPE_UNDOC],
/* 0x29 */ [Debugger.INS.DAD, Debugger.TYPE_HL, Debugger.TYPE_HL], // let's be more explicit about the operands
/* 0x29 */ [Debugger.INS.DAD, Debugger.TYPE_HL | Debugger.TYPE_OPT, Debugger.TYPE_HL],
/* 0x2A */ [Debugger.INS.LHLD, Debugger.TYPE_HL | Debugger.TYPE_OPT, Debugger.TYPE_ADDR | Debugger.TYPE_MEM],
/* 0x2B */ [Debugger.INS.DCX, Debugger.TYPE_HL],
/* 0x2C */ [Debugger.INS.INR, Debugger.TYPE_L],
@ -458,7 +458,7 @@ if (DEBUGGER) {
/* 0x36 */ [Debugger.INS.MVI, Debugger.TYPE_M, Debugger.TYPE_IMM],
/* 0x37 */ [Debugger.INS.STC],
/* 0x38 */ [Debugger.INS.NOP, Debugger.TYPE_UNDOC],
/* 0x39 */ [Debugger.INS.DAD, Debugger.TYPE_HL, Debugger.TYPE_SP], // let's be more explicit about the operands
/* 0x39 */ [Debugger.INS.DAD, Debugger.TYPE_HL | Debugger.TYPE_OPT, Debugger.TYPE_SP],
/* 0x3A */ [Debugger.INS.LDA, Debugger.TYPE_A | Debugger.TYPE_OPT, Debugger.TYPE_ADDR | Debugger.TYPE_MEM],
/* 0x3B */ [Debugger.INS.DCX, Debugger.TYPE_SP],
/* 0x3C */ [Debugger.INS.INR, Debugger.TYPE_A],
@ -519,7 +519,7 @@ if (DEBUGGER) {
/* 0x73 */ [Debugger.INS.MOV, Debugger.TYPE_M, Debugger.TYPE_E],
/* 0x74 */ [Debugger.INS.MOV, Debugger.TYPE_M, Debugger.TYPE_H],
/* 0x75 */ [Debugger.INS.MOV, Debugger.TYPE_M, Debugger.TYPE_L],
/* 0x76 */ [Debugger.INS.MOV, Debugger.TYPE_M, Debugger.TYPE_M],
/* 0x76 */ [Debugger.INS.HLT],
/* 0x77 */ [Debugger.INS.MOV, Debugger.TYPE_M, Debugger.TYPE_A],
/* 0x78 */ [Debugger.INS.MOV, Debugger.TYPE_A, Debugger.TYPE_B],
/* 0x79 */ [Debugger.INS.MOV, Debugger.TYPE_A, Debugger.TYPE_C],
@ -628,7 +628,7 @@ if (DEBUGGER) {
/* 0xE0 */ [Debugger.INS.RPO],
/* 0xE1 */ [Debugger.INS.POP, Debugger.TYPE_HL],
/* 0xE2 */ [Debugger.INS.JPO, Debugger.TYPE_ADDR],
/* 0xE3 */ [Debugger.INS.XTHL, Debugger.TYPE_SP | Debugger.TYPE_MEM, Debugger.TYPE_HL],// let's be more explicit about the operands
/* 0xE3 */ [Debugger.INS.XTHL, Debugger.TYPE_SP | Debugger.TYPE_MEM| Debugger.TYPE_OPT, Debugger.TYPE_HL | Debugger.TYPE_OPT],
/* 0xE4 */ [Debugger.INS.CPO, Debugger.TYPE_ADDR],
/* 0xE5 */ [Debugger.INS.PUSH, Debugger.TYPE_HL],
/* 0xE6 */ [Debugger.INS.ANI, Debugger.TYPE_A | Debugger.TYPE_OPT, Debugger.TYPE_IMM | Debugger.TYPE_BYTE],
@ -636,7 +636,7 @@ if (DEBUGGER) {
/* 0xE8 */ [Debugger.INS.RPE],
/* 0xE9 */ [Debugger.INS.PCHL, Debugger.TYPE_HL],
/* 0xEA */ [Debugger.INS.JPE, Debugger.TYPE_ADDR],
/* 0xEB */ [Debugger.INS.XCHG, Debugger.TYPE_HL, Debugger.TYPE_DE], // let's be more explicit about the operands
/* 0xEB */ [Debugger.INS.XCHG, Debugger.TYPE_HL | Debugger.TYPE_OPT, Debugger.TYPE_DE | Debugger.TYPE_OPT],
/* 0xEC */ [Debugger.INS.CPE, Debugger.TYPE_ADDR],
/* 0xED */ [Debugger.INS.CALL, Debugger.TYPE_ADDR | Debugger.TYPE_UNDOC],
/* 0xEE */ [Debugger.INS.XRI, Debugger.TYPE_A | Debugger.TYPE_OPT, Debugger.TYPE_IMM | Debugger.TYPE_BYTE],
@ -650,7 +650,7 @@ if (DEBUGGER) {
/* 0xF6 */ [Debugger.INS.ORI, Debugger.TYPE_A | Debugger.TYPE_OPT, Debugger.TYPE_IMM | Debugger.TYPE_BYTE],
/* 0xF7 */ [Debugger.INS.RST, Debugger.TYPE_INT],
/* 0xF8 */ [Debugger.INS.RM],
/* 0xF9 */ [Debugger.INS.SPHL, Debugger.TYPE_SP, Debugger.TYPE_HL], // let's be more explicit about the operands
/* 0xF9 */ [Debugger.INS.SPHL, Debugger.TYPE_SP | Debugger.TYPE_OPT, Debugger.TYPE_HL | Debugger.TYPE_OPT],
/* 0xFA */ [Debugger.INS.JM, Debugger.TYPE_ADDR],
/* 0xFB */ [Debugger.INS.EI],
/* 0xFC */ [Debugger.INS.CM, Debugger.TYPE_ADDR],
@ -2651,7 +2651,6 @@ if (DEBUGGER) {
this.getRegOutput(Debugger.REG_DE) +
this.getRegOutput(Debugger.REG_HL) +
this.getRegOutput(Debugger.REG_SP) +
this.getRegOutput(Debugger.REG_PS) +
this.getFlagOutput('I') + this.getFlagOutput('S') + this.getFlagOutput('Z') +
this.getFlagOutput('A') + this.getFlagOutput('P') + this.getFlagOutput('C');
return s;

View file

@ -38,6 +38,7 @@ if (NODE) {
var DumpAPI = require("../../shared/lib/dumpapi");
var Component = require("../../shared/lib/component");
var Memory = require("./memory");
var CPUDef = require("./cpudef");
}
/**
@ -49,7 +50,7 @@ if (NODE) {
* size: amount of ROM, in bytes
* alias: physical alias address (null if none)
* file: name of ROM data file
* notify: ID of a component to notify once the ROM is in place (optional)
* writable: true to make ROM writable (default is false)
*
* NOTE: The ROM data will not be copied into place until the Bus is ready (see initBus()) AND the
* ROM data file has finished loading (see doneLoad()).
@ -57,6 +58,10 @@ if (NODE) {
* Also, while the size parameter may seem redundant, I consider it useful to confirm that the ROM you received
* is the ROM you expected.
*
* Finally, while making ROM "writable" may seem a contradiction in terms, I want to be able to load selected
* CP/M binary files into memory purely for testing purposes, and the RAM component has no "file" option, so the
* simplest solution was to add the option to load binary files into memory as "writable ROMs".
*
* @constructor
* @extends Component
* @param {Object} parmsROM
@ -68,6 +73,7 @@ function ROM(parmsROM)
this.abROM = null;
this.addrROM = parmsROM['addr'];
this.sizeROM = parmsROM['size'];
this.fWritable = parmsROM['writable'];
/*
* The new 'alias' property can now be EITHER a single physical address (like 'addr') OR an array of
@ -106,6 +112,8 @@ function ROM(parmsROM)
Component.subclass(ROM);
ROM.FAKECPM_VECTORS = [0x0000, 0x0005];
/*
* NOTE: There's currently no need for this component to have a reset() function, since
* once the ROM data is loaded, it can't be changed, so there's nothing to reinitialize.
@ -268,6 +276,12 @@ ROM.prototype.copyROM = function()
this.setReady();
}
else if (this.abROM && this.bus) {
/*
* If no explicit size was specified, then use whatever the actual size is.
*/
if (!this.sizeROM) {
this.sizeROM = this.abROM.length;
}
if (this.abROM.length != this.sizeROM) {
/*
* Note that setError() sets the component's fError flag, which in turn prevents setReady() from
@ -314,11 +328,28 @@ ROM.prototype.copyROM = function()
*/
ROM.prototype.addROM = function(addr)
{
if (this.bus.addMemory(addr, this.sizeROM, Memory.TYPE.ROM)) {
if (this.bus.addMemory(addr, this.sizeROM, this.fWritable? Memory.TYPE.RAM : Memory.TYPE.ROM)) {
if (DEBUG) this.log("addROM(): copying ROM to " + str.toHexLong(addr) + " (" + str.toHexLong(this.abROM.length) + " bytes)");
var bto = null;
for (var off = 0; off < this.abROM.length; off++) {
this.bus.setByteDirect(addr + off, this.abROM[off]);
var i;
for (i = 0; i < this.abROM.length; i++) {
this.bus.setByteDirect(addr + i, this.abROM[i]);
}
if (this.fWritable && addr == 0x100) {
/*
* Here's where we enable our "Fake CP/M" support, triggered by the user loading a "writable ROM" image
* at offset 0x100. Fake CP/M support works by installing HLT opcodes at well-known hard-coded CP/M offsets
* (namely, 0x0000, which is the CP/M reset vector, and 0x0005, which is the CP/M system call vector) and
* then telling the CPU to call us whenever a HLT occurs, so we can check PC for one of these vectors.
*/
for (i = 0; i < ROM.FAKECPM_VECTORS.length; i++) {
this.bus.setByteDirect(ROM.FAKECPM_VECTORS[i], CPUDef.OPCODE.HLT);
}
this.cpu.addHaltCheck(function(rom) {
return function(addr) {rom.checkHalt(addr)};
}(this));
this.cpu.setReset(addr);
}
return true;
}
@ -328,6 +359,24 @@ ROM.prototype.addROM = function(addr)
return false;
};
/**
* checkHalt(addr)
*
* @this {ROM}
* @param {number} addr (of the HLT opcode)
* @return {boolean} true if special processing performed, false if not
*/
ROM.prototype.checkHalt = function(addr)
{
if (this.dbg) {
this.println("CP/M vector " + str.toHexWord(addr));
this.cpu.setPC(addr); // this is purely for the Debugger's benefit, to show the HLT
this.dbg.stopCPU();
return true;
}
return false;
};
/**
* cloneROM(addr)
*

View file

@ -78,7 +78,7 @@ function Bus(parmsBus, cpu, dbg)
this.cpu = cpu;
this.dbg = dbg;
this.nBusWidth = parmsBus['buswidth'] || 20;
this.nBusWidth = parmsBus['busWidth'] || 20;
/*
* Compute all Bus memory block parameters, based on the width of the bus.
@ -93,8 +93,8 @@ function Bus(parmsBus, cpu, dbg)
*
* iBlock & this.nBlockMask
*
* Similarly, we mask addresses with busMask to enforce "A20 wrap" on 20-bit busses.
* For larger busses, A20 wrap can be simulated by either clearing bit 20 of busMask or by
* Similarly, we mask addresses with busMask to enforce "A20 wrap" on 20-bit buses.
* For larger buses, A20 wrap can be simulated by either clearing bit 20 of busMask or by
* changing all the block entries for the 2nd megabyte to match those in the 1st megabyte.
*
* Bus Property Old hard-coded values (when nBusWidth was always 20)
@ -354,16 +354,16 @@ Bus.prototype.powerUp = function(data, fRepower)
* allocations, etc.
*
* We've relaxed some of the original requirements (ie, that addresses must start at a
* block-granular address, or that sizes must be equal to exactly one or more blocks), because
* machines with large block sizes can make it impossible to load certain ROMs at at their
* required addresses.
* block-granular address, or that sizes must be equal to exactly one or more blocks),
* because machines with large block sizes can make it impossible to load certain ROMs at
* their required addresses. Every allocation still allocates a whole number of blocks.
*
* Even so, Bus memory management does NOT provide a general-purpose heap. Most memory
* allocations occur during machine initialization and never change. The only notable
* exception is the Video frame buffer, which ranges from 4Kb (MDA) to 16Kb (CGA) to
* 32Kb/64Kb/128Kb (EGA), and only the EGA changes its buffer address post-initialization.
* allocations occur during machine initialization and never change. In particular, there
* is NO support for removing partial-block allocations. Typically, the only region that
* changes post-initialization is the Video buffer, and only in the EGA/VGA implementation.
*
* Each Memory block keeps track of a single address (addr) and length (used), indicating
* Each Memory block keeps track of a start address (addr) and length (used), indicating
* the used space within the block; any free space that precedes or follows that used space
* can be allocated later, by simply extending the beginning or ending of the previously used
* space. However, any holes that might have existed between the original allocation and an
@ -380,17 +380,18 @@ Bus.prototype.addMemory = function(addr, size, type, controller)
{
var iBlock = addr >>> this.nBlockShift;
while (size > 0 && iBlock < this.aMemBlocks.length) {
var block = this.aMemBlocks[iBlock];
var addrBlock = iBlock * this.nBlockSize;
var sizeBlock = size > this.nBlockSize? this.nBlockSize : size;
var sizeBlock = this.nBlockSize - (addr - addrBlock);
if (sizeBlock > size) sizeBlock = size;
if (block && block.size) {
if (block.type == type && block.controller == controller) {
/*
* Where there is already a block with a non-zero size, we can allow the allocation only if:
* Where there is already a similar block with a non-zero size, we allow the allocation only if:
*
* 1) addr + size <= block.addr (the request precedes the used portion of the current block)
* or:
* 1) addr + size <= block.addr (the request precedes the used portion of the current block), or
* 2) addr >= block.addr + block.used (the request follows the used portion of the current block)
*/
if (addr + size <= block.addr) {
@ -402,17 +403,19 @@ Bus.prototype.addMemory = function(addr, size, type, controller)
var sizeAvail = block.size - (addr - addrBlock);
if (sizeAvail > size) sizeAvail = size;
block.used = addr - block.addr + sizeAvail;
size -= sizeAvail;
addr = addrBlock + this.nBlockSize;
size -= sizeAvail;
iBlock++;
continue;
}
}
return this.reportError(Bus.ERROR.ADD_MEM_INUSE, addr, size);
}
var blockOld = this.aMemBlocks[iBlock];
var blockNew = new Memory(addr, sizeBlock, this.nBlockSize, type, controller);
blockNew.copyBreakpoints(this.dbg, blockOld);
blockNew.copyBreakpoints(this.dbg, block);
this.aMemBlocks[iBlock++] = blockNew;
addr = addrBlock + this.nBlockSize;
size -= sizeBlock;
}

View file

@ -195,7 +195,7 @@ function Computer(parmsComputer, parmsMachine, fSuspended) {
/*
* Initialize the Bus component
*/
this.bus = new Bus({'id': this.idMachine + '.bus', 'buswidth': this.nBusWidth}, this.cpu, this.dbg);
this.bus = new Bus({'id': this.idMachine + '.bus', 'busWidth': this.nBusWidth}, this.cpu, this.dbg);
/*
* Iterate through all the components and connect them to the Control Panel, if any

View file

@ -305,6 +305,12 @@ ROM.prototype.copyROM = function()
this.setReady();
}
else if (this.abROM && this.bus) {
/*
* If no explicit size was specified, then use whatever the actual size is.
*/
if (!this.sizeROM) {
this.sizeROM = this.abROM.length;
}
if (this.abROM.length != this.sizeROM) {
/*
* Note that setError() sets the component's fError flag, which in turn prevents setReady() from

View file

@ -903,10 +903,16 @@
<xsl:otherwise/>
</xsl:choose>
</xsl:variable>
<xsl:variable name="writable">
<xsl:choose>
<xsl:when test="@writable"><xsl:value-of select="@writable"/></xsl:when>
<xsl:otherwise>false</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:call-template name="component">
<xsl:with-param name="machine" select="$machine"/>
<xsl:with-param name="class">rom</xsl:with-param>
<xsl:with-param name="parms">,addr:<xsl:value-of select="$addr"/>,size:<xsl:value-of select="$size"/>,alias:<xsl:value-of select="$alias"/>,file:'<xsl:value-of select="$file"/>',notify:'<xsl:value-of select="$notify"/>'</xsl:with-param>
<xsl:with-param name="parms">,addr:<xsl:value-of select="$addr"/>,size:<xsl:value-of select="$size"/>,alias:<xsl:value-of select="$alias"/>,file:'<xsl:value-of select="$file"/>',notify:'<xsl:value-of select="$notify"/>',writable:<xsl:value-of select="$writable"/></xsl:with-param>
</xsl:call-template>
</xsl:template>
@ -1141,7 +1147,7 @@
<xsl:choose>
<xsl:when test="@buswidth"><xsl:value-of select="@buswidth"/></xsl:when>
<xsl:when test="@busWidth"><xsl:value-of select="@busWidth"/></xsl:when>
<xsl:otherwise>20</xsl:otherwise>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="resume">

View file

@ -901,10 +901,16 @@
<xsl:otherwise/>
</xsl:choose>
</xsl:variable>
<xsl:variable name="writable">
<xsl:choose>
<xsl:when test="@writable"><xsl:value-of select="@writable"/></xsl:when>
<xsl:otherwise>false</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:call-template name="component">
<xsl:with-param name="machine" select="$machine"/>
<xsl:with-param name="class">rom</xsl:with-param>
<xsl:with-param name="parms">,addr:<xsl:value-of select="$addr"/>,size:<xsl:value-of select="$size"/>,alias:<xsl:value-of select="$alias"/>,file:'<xsl:value-of select="$file"/>',notify:'<xsl:value-of select="$notify"/>'</xsl:with-param>
<xsl:with-param name="parms">,addr:<xsl:value-of select="$addr"/>,size:<xsl:value-of select="$size"/>,alias:<xsl:value-of select="$alias"/>,file:'<xsl:value-of select="$file"/>',notify:'<xsl:value-of select="$notify"/>',writable:<xsl:value-of select="$writable"/></xsl:with-param>
</xsl:call-template>
</xsl:template>
@ -1139,7 +1145,7 @@
<xsl:choose>
<xsl:when test="@buswidth"><xsl:value-of select="@buswidth"/></xsl:when>
<xsl:when test="@busWidth"><xsl:value-of select="@busWidth"/></xsl:when>
<xsl:otherwise>20</xsl:otherwise>
<xsl:otherwise>0</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<xsl:variable name="resume">

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long