Removed all obsolete @suppress directives
This commit is contained in:
parent
a99a91999d
commit
426fd0ff40
60 changed files with 6221 additions and 340 deletions
|
|
@ -2,7 +2,6 @@
|
|||
* @fileoverview This file implements the C1Pjs Computer component.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @version 1.0
|
||||
* @suppress {missingProperties}
|
||||
* Created 2012-Jun-15
|
||||
*
|
||||
* Copyright © 2012-2014 Jeff Parsons <Jeff@pcjs.org>
|
||||
|
|
@ -53,7 +52,7 @@
|
|||
* The C1Pjs JavaScript files do have some initialization-order dependencies.
|
||||
* If you load the files individually, it's recommended that you load them in
|
||||
* the same order that they're compiled (see above).
|
||||
*
|
||||
*
|
||||
* Generally speaking, component.js should be first, computer.js should be
|
||||
* last (of the files based on component.js), and panel.js should be listed
|
||||
* early so that the Control Panel is ready as soon as possible.
|
||||
|
|
@ -74,13 +73,13 @@
|
|||
* play nice and use only its assigned section of abMemory -- and pretend it's an array
|
||||
* of bytes, when in fact it's an array of floating-point values (the only primitive
|
||||
* numeric data type that JavaScript provides).
|
||||
*
|
||||
*
|
||||
* This component also insures that all the other components are ready; in particular,
|
||||
* this means that the ROM and Video components have finished loading their resources
|
||||
* and are ready for operation. Other components become ready as soon as we call their
|
||||
* setBuffer() method (eg, CPU, RAM, Keyboard, Debugger, SerialPort, DiskController), and
|
||||
* others, like Panel, become ready even earlier, at the end of their initialization.
|
||||
*
|
||||
*
|
||||
* Once every component has indicated it's ready, we call its setPower() notification
|
||||
* function (if it has one; it's optional). We call the CPU's setPower() function last,
|
||||
* so that the CPU is assured that all other components are ready and "powered".
|
||||
|
|
@ -91,7 +90,7 @@
|
|||
function C1PComputer(parmsComputer, modules)
|
||||
{
|
||||
Component.call(this, "C1PComputer", parmsComputer);
|
||||
|
||||
|
||||
this.modules = modules;
|
||||
}
|
||||
|
||||
|
|
@ -126,7 +125,7 @@ C1PComputer.prototype.reset = function(fPowerOn)
|
|||
|
||||
/**
|
||||
* @this {C1PComputer}
|
||||
*
|
||||
*
|
||||
* Called by the CPU to notify all component start() handlers
|
||||
*/
|
||||
C1PComputer.prototype.start = function()
|
||||
|
|
@ -146,7 +145,7 @@ C1PComputer.prototype.start = function()
|
|||
* @this {C1PComputer}
|
||||
* @param {number} msStart
|
||||
* @param {number} nCycles
|
||||
*
|
||||
*
|
||||
* Called by the CPU to notify all component stop() handlers
|
||||
*/
|
||||
C1PComputer.prototype.stop = function(msStart, nCycles)
|
||||
|
|
@ -191,7 +190,7 @@ C1PComputer.prototype.setBinding = function(c, t, s, e)
|
|||
* @this {C1PComputer}
|
||||
* @param {string} sType
|
||||
* @return {Component}
|
||||
*
|
||||
*
|
||||
* NOTE: If there are multiple components for a given type, we may need to provide a means of discriminating.
|
||||
*/
|
||||
C1PComputer.prototype.getComponentByType = function(sType)
|
||||
|
|
@ -232,11 +231,11 @@ C1PComputer.power = function(computer)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* The entire computer is finally ready; we call our own setReady() for completeness, not because any
|
||||
* other component actually cares when we're ready.
|
||||
*/
|
||||
*/
|
||||
computer.setReady();
|
||||
|
||||
computer.println(C1PComputer.sAppName + " v" + C1PComputer.sAppVer + "\n" + C1PComputer.sCopyright);
|
||||
|
|
@ -273,13 +272,13 @@ C1PComputer.init = function()
|
|||
|
||||
var abMemory;
|
||||
var addrStart = 0, addrEnd = 0;
|
||||
|
||||
|
||||
for (var iAddr=0; iAddr < parmsComputer['modules'].length; iAddr++) {
|
||||
var addrInfo = parmsComputer['modules'][iAddr];
|
||||
/*
|
||||
* The first address range (ie, the CPU range) must specify the range for the entire
|
||||
* address space (abMemory), which we allocate and zero-initialize.
|
||||
*
|
||||
*
|
||||
* NOTE: We might consider doing what the Video component does on first reset: initializing
|
||||
* the entire memory buffer to random values. However, a constant (eg, 0xA5) might be
|
||||
* more useful, acting as a crude indicator of memory the client code hasn't written yet.
|
||||
|
|
@ -308,7 +307,7 @@ C1PComputer.init = function()
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (abMemory === undefined) {
|
||||
Component.error("<module type=\"cpu\"> definition must appear first in the <computer> specification");
|
||||
return;
|
||||
|
|
@ -317,7 +316,7 @@ C1PComputer.init = function()
|
|||
/*
|
||||
* Let's see if the Debugger is installed (NOTE: its ID must be "debugger", and only one per machine is supported);
|
||||
* the Debugger needs our setBuffer(), setPower() and reset() notifications, and this relieves us from having an explicit
|
||||
* <module> entry for type="debugger".
|
||||
* <module> entry for type="debugger".
|
||||
*/
|
||||
component = Component.getComponentByID('debugger', parmsComputer['id']);
|
||||
if (component) {
|
||||
|
|
@ -326,12 +325,12 @@ C1PComputer.init = function()
|
|||
component.setBuffer(abMemory, addrStart, addrEnd, modules['cpu'][0]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var computer = new C1PComputer(parmsComputer, modules);
|
||||
|
||||
/*
|
||||
* Let's see if the Control Panel is installed (NOTE: its ID must be "panel", and only one per machine is supported);
|
||||
* the Panel needs our setPower() notifications, and this relieves us from having an explicit <module> entry for type="panel".
|
||||
* Let's see if the Control Panel is installed (NOTE: its ID must be "panel", and only one per machine is supported);
|
||||
* the Panel needs our setPower() notifications, and this relieves us from having an explicit <module> entry for type="panel".
|
||||
*/
|
||||
var panel = Component.getComponentByID('panel', parmsComputer['id']);
|
||||
if (panel) {
|
||||
|
|
@ -355,7 +354,7 @@ C1PComputer.init = function()
|
|||
* We may eventually add a "Power" button, but for now, all we have is a "Reset" button
|
||||
*/
|
||||
Component.bindComponentControls(computer, eComputer, C1PJSCLASS);
|
||||
|
||||
|
||||
/*
|
||||
* "Power" the computer automatically
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
* @fileoverview This file implements the C1Pjs 6502 CPU component.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @version 1.0
|
||||
* @suppress {missingProperties}
|
||||
* Created 2012-Jun-15
|
||||
*
|
||||
* Copyright © 2012-2014 Jeff Parsons <Jeff@pcjs.org>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
* @fileoverview This file implements the C1Pjs Debugger component.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @version 1.0
|
||||
* @suppress {missingProperties}
|
||||
* Created 2012-Jun-21
|
||||
*
|
||||
* Copyright © 2012-2014 Jeff Parsons <Jeff@pcjs.org>
|
||||
|
|
@ -35,12 +34,12 @@
|
|||
|
||||
/**
|
||||
* C1PDebugger(parmsDbg)
|
||||
*
|
||||
*
|
||||
* The C1PDebugger component has no required (parmsDbg) properties.
|
||||
*
|
||||
* The C1PDebugger component is an optional component that implements a variety of user
|
||||
* commands for controlling the CPU, dumping and editing memory, etc.
|
||||
*
|
||||
*
|
||||
* @constructor
|
||||
* @extends Component
|
||||
*/
|
||||
|
|
@ -49,7 +48,7 @@ function C1PDebugger(parmsDbg)
|
|||
if (DEBUGGER) {
|
||||
|
||||
Component.call(this, "C1PDebugger", parmsDbg);
|
||||
|
||||
|
||||
/*
|
||||
* This keeps track of instruction activity, but only when tracing or when
|
||||
* Debugger checks have been enabled (eg, one or more breakpoints have been set).
|
||||
|
|
@ -58,25 +57,25 @@ function C1PDebugger(parmsDbg)
|
|||
* We set it here to -1 to indicate that the CPU has not yet initialized us.
|
||||
*/
|
||||
this.cIns = -1;
|
||||
|
||||
|
||||
/*
|
||||
* Some commands, like the dump (d) command, start at nextAddr when no address
|
||||
* is given (and they also update nextAddr when they're done).
|
||||
*/
|
||||
this.nextAddr = 0;
|
||||
|
||||
|
||||
/*
|
||||
* When Enter is pressed on an empty input buffer, we default to the previous
|
||||
* command, which is preserved here.
|
||||
*/
|
||||
this.prevCmd = null;
|
||||
|
||||
|
||||
/*
|
||||
* fAssemble is true when "assemble mode" is active, false when not.
|
||||
*/
|
||||
this.fAssemble = false;
|
||||
this.addrAssembleNext = 0;
|
||||
|
||||
|
||||
/*
|
||||
* Initialize the lists of breakpoint addresses. aExecBreak is a list (Array) of addresses
|
||||
* to halt at whenever attempting to execute an instruction at the corresponding address,
|
||||
|
|
@ -84,7 +83,7 @@ function C1PDebugger(parmsDbg)
|
|||
* respectively, occurs at the corresponding address.
|
||||
*/
|
||||
this.clearBreakpoints();
|
||||
|
||||
|
||||
/*
|
||||
* Instead of pre-allocating these arrays, we wait until our reset() function is called.
|
||||
* These arrays are updated in checkInstruction(), but the CPU will never actually call it
|
||||
|
|
@ -94,7 +93,7 @@ function C1PDebugger(parmsDbg)
|
|||
this.iStepHistory = 0;
|
||||
this.aStepHistory = [];
|
||||
this.aaOpcodeFreqs = [];
|
||||
|
||||
|
||||
/*
|
||||
* This "info" buffer is a lightweight logging mechanism that has minimal impact on the
|
||||
* browser (unlike printing to either window.console.log or an HTML control, which can make
|
||||
|
|
@ -106,7 +105,7 @@ function C1PDebugger(parmsDbg)
|
|||
this.iInfoBuffer = 0;
|
||||
this.aInfoBuffer = new Array(10000);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Message categories supported by the message() function; they are designed to be combined
|
||||
* (ie, OR'ed) as needed. The Debugger's "option" command is used to turn message categories
|
||||
|
|
@ -131,7 +130,7 @@ function C1PDebugger(parmsDbg)
|
|||
'disk': this.MESSAGE_DISK,
|
||||
'serial': this.MESSAGE_SERIAL
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* The aaOperations array is indexed by opcode, and each element is a sub-array that
|
||||
* describes the corresponding opcode. The sub-elements are as follows:
|
||||
|
|
@ -202,7 +201,7 @@ function C1PDebugger(parmsDbg)
|
|||
this.OP_TYA = 55;
|
||||
this.OP_SIM = 56;
|
||||
this.OP_DB = 57;
|
||||
|
||||
|
||||
this.aOpCodes = [
|
||||
"ADC","AND","ASL","BCC","BCS","BEQ","BIT","BMI",
|
||||
"BNE","BPL","BRK","BVC","BVS","CLC","CLD","CLI",
|
||||
|
|
@ -213,13 +212,13 @@ function C1PDebugger(parmsDbg)
|
|||
"STX","STY","TAX","TAY","TSX","TXA","TXS","TYA",
|
||||
"SIM",".DB"
|
||||
];
|
||||
|
||||
|
||||
this.aOpSimCodes = [
|
||||
"HLT", "MSG"
|
||||
];
|
||||
|
||||
|
||||
this.setOpModes(true);
|
||||
|
||||
|
||||
this.aaOperations = [
|
||||
/* 0x00 */ [this.OP_BRK],
|
||||
/* 0x01 */ [this.OP_ORA, 1, this.MODE_INDX],
|
||||
|
|
@ -657,7 +656,7 @@ if (DEBUGGER) {
|
|||
|
||||
var sRegEx = "";
|
||||
var iMode, sMode;
|
||||
|
||||
|
||||
if (fClassic) {
|
||||
this.aOpModes = [
|
||||
"A", // MODE_ACC
|
||||
|
|
@ -986,7 +985,7 @@ if (DEBUGGER) {
|
|||
* NOTE: We keep track of zero-page writes mainly as a reminder to look into whether it makes sense
|
||||
* for the CPU to calculate zero-page EAs using a different variable (eg, regEAWriteZP instead of regEAWrite),
|
||||
* because write-notification handlers never care about page zero accesses, and while write breakpoints *may*
|
||||
* care, it may not be worth the cost of tracking writes to page zero if there's an associated perf penalty.
|
||||
* care, it may not be worth the cost of tracking writes to page zero if there's an associated perf penalty.
|
||||
*/
|
||||
if (!(addr & 0xff00))
|
||||
this.cWritesZP++;
|
||||
|
|
@ -1207,7 +1206,7 @@ if (DEBUGGER) {
|
|||
|
||||
/**
|
||||
* @this {C1PDebugger}
|
||||
* @param {number} addr to compare to addrTempBP; the latter is cleared if there's a match
|
||||
* @param {number} addr to compare to addrTempBP; the latter is cleared if there's a match
|
||||
*/
|
||||
C1PDebugger.prototype.clearTempBreakpoint = function(addr)
|
||||
{
|
||||
|
|
@ -1517,7 +1516,6 @@ if (DEBUGGER) {
|
|||
* @this {C1PDebugger}
|
||||
* @param {string|undefined} [sAddr]
|
||||
* @return {number|undefined}
|
||||
* @suppress {checkTypes}
|
||||
*/
|
||||
C1PDebugger.prototype.getUserAddr = function(sAddr)
|
||||
{
|
||||
|
|
@ -1843,7 +1841,7 @@ if (DEBUGGER) {
|
|||
};
|
||||
|
||||
/**
|
||||
* Prints the contents of the Debugger's "info" buffer (filled by calls like cpu.dbg.info())
|
||||
* Prints the contents of the Debugger's "info" buffer (filled by calls like cpu.dbg.info())
|
||||
* @this {C1PDebugger}
|
||||
* @param {string|undefined} sCount
|
||||
* @return {boolean|undefined} true only if the "info" command is supported
|
||||
|
|
@ -2127,7 +2125,7 @@ if (DEBUGGER) {
|
|||
sCmd = dbg.prevCmd;
|
||||
}
|
||||
if (dbg.isReady() && !dbg.isBusy(true) && sCmd.length > 0) {
|
||||
|
||||
|
||||
if (dbg.fAssemble) {
|
||||
sCmd = "a " + str.toHexWord(dbg.addrAssembleNext) + " " + sCmd;
|
||||
}
|
||||
|
|
@ -2139,10 +2137,10 @@ if (DEBUGGER) {
|
|||
var ch = sCmd.charAt(0).toLowerCase();
|
||||
sCmd = ch + " " + sCmd.substr(1);
|
||||
}
|
||||
|
||||
|
||||
var asArgs = sCmd.split(" ");
|
||||
dbg.prevCmd = asArgs[0];
|
||||
|
||||
|
||||
switch(asArgs[0].toLowerCase()) {
|
||||
case "a":
|
||||
dbg.doAssemble(asArgs);
|
||||
|
|
@ -2189,14 +2187,14 @@ if (DEBUGGER) {
|
|||
break;
|
||||
case "i":
|
||||
if (dbg.doInfo(asArgs[1])) break;
|
||||
/* falls through */
|
||||
/* falls through */
|
||||
default:
|
||||
dbg.println("unknown command: " + sCmd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* C1PDebugger.init()
|
||||
*
|
||||
|
|
@ -2217,7 +2215,7 @@ if (DEBUGGER) {
|
|||
Component.bindComponentControls(dbg, eDbg, C1PJSCLASS);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Initialize every Debugger module on the page (as IF there's ever going to be more than one ;-))
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
* @fileoverview This file implements the C1Pjs DiskController component.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @version 1.0
|
||||
* @suppress {missingProperties}
|
||||
* Created 2012-Aug-09
|
||||
*
|
||||
* Copyright © 2012-2014 Jeff Parsons <Jeff@pcjs.org>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
* @fileoverview This file implements the C1Pjs Keyboard component.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @version 1.0
|
||||
* @suppress {missingProperties}
|
||||
* Created 2012-Jun-20
|
||||
*
|
||||
* Copyright © 2012-2014 Jeff Parsons <Jeff@pcjs.org>
|
||||
|
|
@ -327,7 +326,7 @@ C1PKeyboard.prototype.reset = function()
|
|||
*
|
||||
* Like bitsShift, this 8x8 array (8 byte values, each with 8 bits) represents
|
||||
* the physical state of the keyboard, encoded in C1P format; the C1P won't
|
||||
* actually see data this until updateMemory() decides it's time to propagate it.
|
||||
* actually see data this until updateMemory() decides it's time to propagate it.
|
||||
*/
|
||||
this.abKbdCols = [this.BIT_SHIFTLOCK,0x00,0x00,0x00,0x00,0x00,0x00,0x00];
|
||||
|
||||
|
|
@ -539,7 +538,7 @@ C1PKeyboard.prototype.setReady = function()
|
|||
|
||||
/**
|
||||
* calcReleaseDelay(fRepeat
|
||||
*
|
||||
*
|
||||
* This attempts to scale our default "release" delay appropriately for the current CPU speed.
|
||||
*
|
||||
* Note that if the effective CPU speed exceeds 16Mhz, it becomes very difficult to rely on timer-driven key events
|
||||
|
|
@ -631,7 +630,7 @@ C1PKeyboard.prototype.injectKeysFromBuffer = function(msDelay)
|
|||
*
|
||||
* UPDATE: Even though keyPressSimulate() currently has some code to do this automatically now,
|
||||
* it's really intended as a work-around for a SHIFT-related problem on iOS devices only, so
|
||||
* we can't rely on that in the general case.
|
||||
* we can't rely on that in the general case.
|
||||
*/
|
||||
if (ch >= 0x41 && ch <= 0x5A)
|
||||
ch += 0x20;
|
||||
|
|
@ -711,7 +710,7 @@ C1PKeyboard.prototype.keyEvent = function(event, fDown)
|
|||
* browser will see it and give focus to the next control. But the "down" side is that
|
||||
* that no "press" event will be generated. This puts it in the same category as ESC,
|
||||
* which also generates "down" and "up" events (LOTS of "down" events for that matter),
|
||||
* but no "press" event. However, the C1P has no TAB key, so it's safe to completely ignore.
|
||||
* but no "press" event. However, the C1P has no TAB key, so it's safe to completely ignore.
|
||||
*/
|
||||
fPass = fAutoClear = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
* @fileoverview This file implements the C1Pjs Panel component.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @version 1.0
|
||||
* @suppress {missingProperties}
|
||||
* Created 2012-Jun-19
|
||||
*
|
||||
* Copyright © 2012-2014 Jeff Parsons <Jeff@pcjs.org>
|
||||
|
|
@ -44,7 +43,7 @@
|
|||
function C1PPanel(parmsPanel)
|
||||
{
|
||||
Component.call(this, "C1PPanel", parmsPanel);
|
||||
|
||||
|
||||
this.fPower = false;
|
||||
}
|
||||
|
||||
|
|
@ -90,19 +89,19 @@ C1PPanel.prototype.setPower = function(fOn, cmp)
|
|||
|
||||
/**
|
||||
* C1PPanel.init()
|
||||
*
|
||||
*
|
||||
* This function operates on every element (e) of class "panel", and initializes
|
||||
* all the necessary HTML to construct the Panel module(s) as spec'ed.
|
||||
*
|
||||
* Note that each element (e) of class "panel" is expected to have a "data-value"
|
||||
* attribute containing the same JSON-encoded parameters that the Panel constructor
|
||||
* expects.
|
||||
*
|
||||
*
|
||||
* NOTE: Unlike most other component init() functions, this one is designed to be
|
||||
* called multiple times: once at load time, so that we can binding our print()
|
||||
* function to the panel's output control ASAP, and again when the Computer component
|
||||
* is verifying that all components are ready and invoking their setPower() functions.
|
||||
*
|
||||
*
|
||||
* Our setPower() method gives us a second opportunity to notify any components that
|
||||
* that might care (eg, CPU, Keyboard, and Debugger) that we have some controls they
|
||||
* might want to use.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
* @fileoverview This file implements the C1Pjs RAM component.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @version 1.0
|
||||
* @suppress {missingProperties}
|
||||
* Created 2012-Jun-15
|
||||
*
|
||||
* Copyright © 2012-2014 Jeff Parsons <Jeff@pcjs.org>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
* @fileoverview This file implements the C1Pjs ROM component.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @version 1.0
|
||||
* @suppress {missingProperties}
|
||||
* Created 2012-Jun-15
|
||||
*
|
||||
* Copyright © 2012-2014 Jeff Parsons <Jeff@pcjs.org>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
* @fileoverview This file implements the C1Pjs SerialPort component.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @version 1.0
|
||||
* @suppress {missingProperties}
|
||||
* Created 2012-Jul-01
|
||||
*
|
||||
* Copyright © 2012-2014 Jeff Parsons <Jeff@pcjs.org>
|
||||
|
|
@ -47,7 +46,7 @@ function C1PSerialPort(parmsSerial)
|
|||
|
||||
this.fPower = false;
|
||||
this.fDemo = parmsSerial['demo'];
|
||||
|
||||
|
||||
this.STATUS_NONE = 0x00;
|
||||
this.STATUS_DATA = 0x01; // indicates data available
|
||||
|
||||
|
|
@ -155,7 +154,7 @@ C1PSerialPort.prototype.setBuffer = function(abMemory, start, end, cpu)
|
|||
* @this {C1PSerialPort}
|
||||
* @param {boolean} fOn
|
||||
* @param {C1PComputer} cmp
|
||||
*
|
||||
*
|
||||
* We make a note of the Computer component, so that we can invoke its reset() method whenever we need to
|
||||
* simulate a warm start, and we query the Keyboard component so that we can use its injectKeys() function.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
* @fileoverview This file implements the C1Pjs Video component
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @version 1.0
|
||||
* @suppress {missingProperties}
|
||||
* Created 2012-Jun-15
|
||||
*
|
||||
* Copyright © 2012-2014 Jeff Parsons <Jeff@pcjs.org>
|
||||
|
|
@ -35,7 +34,7 @@
|
|||
|
||||
/**
|
||||
* C1PVideo(parmsVideo, eCanvas, context, imgChars)
|
||||
*
|
||||
*
|
||||
* The Video component can be configured with the following (parmsVideo) properties:
|
||||
*
|
||||
* model: model number (one of: 540 or 600; 600 is the default)
|
||||
|
|
@ -56,7 +55,7 @@
|
|||
* must be given to it by the Computer object. We allocate a separate buffer, called
|
||||
* the screen buffer, into which we periodically copy the contents of the video buffer
|
||||
* via updateScreen(); any differences between the two buffers are then rendered in the
|
||||
* associated window, via updateWindow().
|
||||
* associated window, via updateWindow().
|
||||
*
|
||||
* When updateScreen() finds a byte in the screen buffer must be redisplayed, it converts
|
||||
* the offset of that byte into a (col,row) character position for the updateWindow() function,
|
||||
|
|
@ -71,7 +70,7 @@
|
|||
* columns, the practical maximum is 24 rows x 24 columns; the last 4 rows of the video buffer
|
||||
* are never used, and while content scrolls through the top 4 lines of the buffer, it is never
|
||||
* assumed that you can see the top 4 lines.
|
||||
*
|
||||
*
|
||||
* This is partially confirmed by the "C1P Character Graphics Reference Manual", p3, which says
|
||||
* that the "the visible character field consists of 25 lines of 25 columns" and that the "first
|
||||
* visible character in the upper left of the screen is accessed via address 53379," or 0xD083,
|
||||
|
|
@ -79,66 +78,66 @@
|
|||
* regarding "25 lines of 25 columns" seems to be off by one in both dimensions. And why would
|
||||
* they say that the first visible address is 0xD083 instead of 0xD085? An indentation of 5 bytes,
|
||||
* rather than 3, would be more consistent with how the C1P ROMs use video memory.
|
||||
*
|
||||
*
|
||||
* Model 540 Video Board vs. Model 600 "Superboard II"
|
||||
* ---------------------------------------------------
|
||||
* This emulation was originally written for the Model 600 "Superboard II" (eg, Challenger 1P).
|
||||
* Support for the Model 540 video board (as used in the Challenger II-4P and II-8P) was added
|
||||
* later.
|
||||
*
|
||||
*
|
||||
* NOTE: When Model 540 video emulation is enabled, Model 542 keyboard emulation must also be
|
||||
* enabled, because the former always came with the latter keyboard interface; this is why when
|
||||
* we call this.setModel(540), we must also notify the Keyboard via kbd.setModel(542).
|
||||
*
|
||||
*
|
||||
* Key features/differences of the Model 540 video board include:
|
||||
*
|
||||
*
|
||||
* 2K (8 pages) of video memory located at 0xD000-0xD7FF
|
||||
* Two display modes: 32 rows x 64 cols (default on power up), and 32 rows x 32 cols
|
||||
* 64 bytes per screen row, regardless which display mode is selected
|
||||
* The following options can be selected via WRITE to port address 0xDE00:
|
||||
* Bit 0: clear to enable 32/64 mode (default on power up), set to enable 32/32
|
||||
* Bit 1: 1=tone on (542 keyboard)
|
||||
* Bit 2: 1=color on (Rev. B only?)
|
||||
* Bit 2: 1=color on (Rev. B only?)
|
||||
* bit 3: 1=enable 38-40Khz AC Home control output (Rev. B only?)
|
||||
* Video timing counter status via READ from port address 0xDE00:
|
||||
* Bit 7: 0 for 1/120 second, then 1 for 1/120 second, based on video clock (60Hz)
|
||||
*
|
||||
*
|
||||
* @constructor
|
||||
* @extends Component
|
||||
*/
|
||||
function C1PVideo(parmsVideo, eCanvas, context, imgChars)
|
||||
{
|
||||
Component.call(this, "C1PVideo", parmsVideo);
|
||||
|
||||
|
||||
this.nDefaultModel = parmsVideo['model'];
|
||||
this.nDefaultCols = parmsVideo['charCols'];
|
||||
this.nDefaultRows = parmsVideo['charRows'];
|
||||
|
||||
|
||||
this.cxScreen = parmsVideo['screenWidth'];
|
||||
this.cyScreen = parmsVideo['screenHeight'];
|
||||
|
||||
|
||||
/*
|
||||
* These (source) character dimensions are tentative, and may not even be provided,
|
||||
* but they will become definitive once imgChars has finished loading and setReady() is called.
|
||||
*/
|
||||
this.cxChar = parmsVideo['charWidth'];
|
||||
this.cyChar = parmsVideo['charHeight'];
|
||||
|
||||
|
||||
/*
|
||||
* This is a preliminary call to setDimensions(), to initialize default screen buffer and
|
||||
* window dimensions. A more extensive call to setDimensions() will take place when setModel()
|
||||
* is called later, from reset() and possibly via the tripGuard() handler.
|
||||
*
|
||||
*
|
||||
* This preliminary call merely establishes a default screen buffer size, so that when
|
||||
* setBuffer() is called, it's able to verify the assigned address space is at least as big
|
||||
* setBuffer() is called, it's able to verify the assigned address space is at least as big
|
||||
* as the screen buffer.
|
||||
*/
|
||||
this.setDimensions();
|
||||
|
||||
|
||||
this.eCanvas = eCanvas;
|
||||
this.context = context;
|
||||
this.imgChars = imgChars;
|
||||
|
||||
|
||||
/*
|
||||
* QUESTION: Does this video port exist only on the Model 540?
|
||||
*/
|
||||
|
|
@ -155,7 +154,7 @@ Component.subclass(Component, C1PVideo);
|
|||
C1PVideo.prototype.reset = function(fPowerOn)
|
||||
{
|
||||
this.setModel(this.nDefaultModel);
|
||||
|
||||
|
||||
if (this.abMem) {
|
||||
/*
|
||||
* Let's treat every reset like a power-cycle, just for fun.
|
||||
|
|
@ -245,7 +244,7 @@ C1PVideo.prototype.setDimensions = function(nCols, nRows, iRowTop, nRowsVisible)
|
|||
|
||||
/**
|
||||
* @this {C1PVideo}
|
||||
*
|
||||
*
|
||||
* cxScreen and cyScreen give us the overall dimensions of the destination surface. Dividing that by the number of
|
||||
* columns and rows yields a target cell size (cxCharDst,cyCharDst), which may or may not map 1-1 to the source cell size
|
||||
* (cxChar,cyChar).
|
||||
|
|
@ -283,7 +282,7 @@ C1PVideo.prototype.setModel = function(nModel)
|
|||
* buffer range, not the FIRST byte, which has the same effect but with the
|
||||
* added benefit of deferring any screen update until after the "Model 540"
|
||||
* screen initialization code has completely blanked the entire 2K buffer,
|
||||
* avoiding a brief flicker of unsightly characters.
|
||||
* avoiding a brief flicker of unsightly characters.
|
||||
*/
|
||||
this.addrGuard = this.offVideoLimit + this.cbScreen - 1;
|
||||
this.cpu.addWriteNotify(this.addrGuard, this.addrGuard, this, this.tripGuard);
|
||||
|
|
@ -315,7 +314,7 @@ C1PVideo.prototype.setPower = function(fOn, cmp)
|
|||
/*
|
||||
* If we have an associated keyboard, then ensure that the keyboard will be notified whenever
|
||||
* the canvas gets focus and receives input.
|
||||
*
|
||||
*
|
||||
* Also, when simulating a Model 540 video board, we need to access to the Keyboard component due
|
||||
* to some shared I/O responsibilities; ie, bit 1 of the video control port at 0xDE00 enables whatever
|
||||
* tone has been selected via the keyboard frequency port at 0xDF01 (frequency == 49152/n, where n
|
||||
|
|
@ -340,7 +339,7 @@ C1PVideo.prototype.setPower = function(fOn, cmp)
|
|||
|
||||
/**
|
||||
* @this {C1PVideo}
|
||||
*
|
||||
*
|
||||
* cxChar and cyChar are the source cell size. Originally, those values came strictly from the parmsVideo
|
||||
* 'charWidth' and 'charHeight' properties. Now, if those aren't defined (which is normally the case now),
|
||||
* then we infer the source cell size from the dimensions of imgChars, which is expected to be a 16x16 array of
|
||||
|
|
@ -372,7 +371,7 @@ C1PVideo.prototype.getByte = function(addr, addrFrom)
|
|||
* The only documented READ bit in addrVideoPort is bit 7, which is supposed to alternate between
|
||||
* 0 and 1 every 1/120 of a second. There's no way we're going to add special code to the emulator to update
|
||||
* this stupid byte every 8,333 cycles (assuming 1Mhz operation), so clearly we're going to fake it.
|
||||
*
|
||||
*
|
||||
* Faking it means that any polling code will unavoidably get a stale value the FIRST time it reads bit 7.
|
||||
* However, we can still do a pretty good job of faking any EXTENSIVE polling: get the number of cycles
|
||||
* executed so far, divide that by 8333, floor the quotient, and then set/clear bit 7 according to whether the
|
||||
|
|
@ -410,7 +409,7 @@ C1PVideo.prototype.tripGuard = function(addr, addrFrom)
|
|||
/*
|
||||
* The CPU has just written to the guard address we established just beyond the video buffer's 1K boundary,
|
||||
* implying that the system thinks we have a 2K buffer instead. So we bump our model to 540, bump the
|
||||
* associated keyboard model to 542, and remove this guard handler.
|
||||
* associated keyboard model to 542, and remove this guard handler.
|
||||
*/
|
||||
this.setModel(540);
|
||||
if (this.kbd) this.kbd.setModel(542);
|
||||
|
|
@ -431,7 +430,7 @@ C1PVideo.prototype.initScreen = function()
|
|||
|
||||
/**
|
||||
* updateScreen() updates the screen buffer from the video buffer and updates the window with any changes.
|
||||
*
|
||||
*
|
||||
* @this {C1PVideo}
|
||||
* @return {boolean}
|
||||
*
|
||||
|
|
@ -474,19 +473,19 @@ C1PVideo.prototype.writeByte = function(offset, b)
|
|||
|
||||
/**
|
||||
* updateWindow() updates a particular position (row,col) in the associated window with the given byte (b)
|
||||
*
|
||||
*
|
||||
* @this {C1PVideo}
|
||||
* @param {number} col
|
||||
* @param {number} row
|
||||
* @param {number} b
|
||||
* @return {boolean} true if successful, false if not
|
||||
*
|
||||
* @return {boolean} true if successful, false if not
|
||||
*
|
||||
* I originally used (screenWidth,screenHeight) == (512,448) and (cols,rows) == (32,32) and (cxChar,cyChar) == (16,16),
|
||||
* and I simply copied the source cells 1-to-1 to the destination (16,16), knowing that we would never try to display more
|
||||
* than 28 rows (the last 4 rows of the 32 possible rows were never used to display any content). However, I should still
|
||||
* have ignored any attempt to draw past row 28 (aka screenHeight 448). I now perform row clipping and biasing, according
|
||||
* to the first visible row (iRowTop) and total visible rows (nRowsVisible).
|
||||
*
|
||||
*
|
||||
* Moreover, I no longer copy the source cell images to the destination 1-to-1. I calculate (cxCharDst,cyCharDst) separately
|
||||
* (see setDrawingDimensions). And I no longer assume that (cxChar,cyChar) are (16,16); once the source image file has finished
|
||||
* loading, I calculate (cxChar,cyChar) based on the size of image file (see setReady). I made this change when I created
|
||||
|
|
@ -527,7 +526,7 @@ C1PVideo.init = function()
|
|||
for (var iVideo=0; iVideo < aeVideo.length; iVideo++) {
|
||||
var eVideo = aeVideo[iVideo];
|
||||
var parmsVideo = Component.getComponentParms(eVideo);
|
||||
|
||||
|
||||
/*
|
||||
* As noted in keyboard.js, the keyboard on an iOS device pops up with the SHIFT key depressed,
|
||||
* which is not the initial keyboard state that the C1P expects. I originally tried to fix that by
|
||||
|
|
@ -545,7 +544,7 @@ C1PVideo.init = function()
|
|||
eCanvas.setAttribute("class", C1PJSCLASS + "-canvas");
|
||||
eCanvas.setAttribute("width", parmsVideo['screenWidth']);
|
||||
eCanvas.setAttribute("height", parmsVideo['screenHeight']);
|
||||
|
||||
|
||||
eCanvas.setAttribute("contenteditable", "true");
|
||||
eCanvas.setAttribute("autocapitalize", "off");
|
||||
eCanvas.setAttribute("autocorrect", "off");
|
||||
|
|
@ -569,7 +568,7 @@ C1PVideo.init = function()
|
|||
|
||||
/*
|
||||
* Now we can create the Video object, record it, and wire it up to the associated document elements.
|
||||
*
|
||||
*
|
||||
* Regarding "new Image()", see https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement.Image:
|
||||
*
|
||||
* This constructor exists for historical reasons only and returns an HTMLImageElement instance just as
|
||||
|
|
@ -577,7 +576,7 @@ C1PVideo.init = function()
|
|||
*/
|
||||
var imgCharSet = new Image();
|
||||
var contextVideo = eCanvas.getContext("2d");
|
||||
var video = new C1PVideo(parmsVideo, eCanvas, contextVideo, imgCharSet);
|
||||
var video = new C1PVideo(parmsVideo, eCanvas, contextVideo, imgCharSet);
|
||||
imgCharSet.onload = function(video, sCharSet) {
|
||||
return function() {
|
||||
if (DEBUG) video.log("onload(): finished loading " + sCharSet);
|
||||
|
|
@ -588,7 +587,7 @@ C1PVideo.init = function()
|
|||
|
||||
/*
|
||||
* Bind any video-specific controls (eg, the Refresh button). There are no essential controls, however;
|
||||
* even the "Refresh" button is just a diagnostic tool, to verify that the screen contents are up-to-date.
|
||||
* even the "Refresh" button is just a diagnostic tool, to verify that the screen contents are up-to-date.
|
||||
*/
|
||||
Component.bindComponentControls(video, eVideo, C1PJSCLASS);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue