From 6be79ae7565778bd45615ce7c001fa6d1135a420 Mon Sep 17 00:00:00 2001 From: Jeff Parsons Date: Fri, 3 Feb 2017 09:40:50 -0800 Subject: [PATCH] Allow disk devices to specify their own boot requirements --- apps/pdp11/tapes/basic/README.md | 53 ++++++++++++++++---------------- modules/pdp11/lib/defines.js | 23 ++++++++++++-- modules/pdp11/lib/drive.js | 18 ++++++----- modules/pdp11/lib/rx11.js | 18 +++++------ 4 files changed, 67 insertions(+), 45 deletions(-) diff --git a/apps/pdp11/tapes/basic/README.md b/apps/pdp11/tapes/basic/README.md index 1bc8da470..01a959ff9 100644 --- a/apps/pdp11/tapes/basic/README.md +++ b/apps/pdp11/tapes/basic/README.md @@ -15,8 +15,8 @@ DEC PDP-11 BASIC [![DEC-11-AJPB-PB](DEC-11-AJPB-PB.jpg)](DEC-11-AJPB-PB.json) -According to the [PDP-11/20 Handbook (1971)](http://archive.pcjs.org/pubs/dec/pdp11/1120/PDP1120_Handbook_1971.pdf), p. 160, -notable features of PDP-11 BASIC included: +According to the [PDP-11/20 Handbook (1971)](http://archive.pcjs.org/pubs/dec/pdp11/1120/PDP1120_Handbook_1971.pdf), +p. 160, notable features of PDP-11 BASIC included: - Use of BASIC statements in immediate mode (no line number) - Ability to use any BASIC command (RUN, LIST, etc.) in deferred mode (with a line number) @@ -47,19 +47,19 @@ Debugging Notes ### PDPjs Debugger vs. SIMH -When I first tried to run BASIC in a PDPjs machine, it crashed almost immediately. It was attempting to use memory beyond -the 16Kb of installed RAM. After a bit of poking around, I found BASIC's memory sizing code here: +When I first tried to run BASIC in a PDPjs machine, it crashed almost immediately. It was attempting to use memory +beyond the 16Kb of installed RAM. After a bit of poking around, I found BASIC's memory sizing code here: 016142: 012701 160000 MOV #160000,R1 016146: 022626 CMP (SP)+,(SP)+ 016150: 014111 MOV -(R1),@R1 -The code sets R1 to highest possible RAM address and starts scanning backwards for the first valid memory location. However, -the scanning process wasn't clear to me at first glance, and the `CMP (SP)+,(SP)+` was a bit of a head-scratcher, so I decided -to do an instruction-by-instruction comparison with SIMH. +The code sets R1 to highest possible RAM address and starts scanning backwards for the first valid memory location. +However, the scanning process wasn't clear to me at first glance, and the `CMP (SP)+,(SP)+` was a bit of a +head-scratcher, so I decided to do an instruction-by-instruction comparison with SIMH. -After cloning the [SIMH project](https://github.com/simh/simh) and building the *pdp11* binary, I created a *pdp11.ini* text file -that contained: +After cloning the [SIMH project](https://github.com/simh/simh) and building the *pdp11* binary, I created a *pdp11.ini* +text file that contained: ECHO Configuring PDP-11/20 with 16Kb of RAM... SET CPU 11/20 @@ -154,15 +154,15 @@ After typing several "do tr" commands, I was surprised to see SIMH execution con Step expired, PC: 016150 (MOV -(R1),(R1)) -until I remembered that when the PDP-11 accesses an invalid address, it's supposed to trap to vector 000004, and that BASIC -must have modified vector 000004 to jump into the middle of this code. +until I remembered that when the PDP-11 accesses an invalid address, it's supposed to trap to vector 000004, and that +BASIC must have modified vector 000004 to jump into the middle of this code. -This code fragment was simply marching down the address space until it reached an address that didn't trap. The odd-looking -`CMP (SP)+,(SP)+` instruction was throwing away the PC and PSW that each trap pushed onto the stack, by effectively adding -4 to SP. +This code fragment was simply marching down the address space until it reached an address that didn't trap. The +odd-looking `CMP (SP)+,(SP)+` instruction was throwing away the PC and PSW that each trap pushed onto the stack, by +effectively adding 4 to SP. -The problem with PDPjs was that it wasn't generating a trap to vector 000004 when an invalid address was accessed. After fixing -that, I verified with the PDPjs Debugger that the memory sizing code was working properly: +The problem with PDPjs was that it wasn't generating a trap to vector 000004 when an invalid address was accessed. +After fixing that, I verified with the PDPjs Debugger that the memory sizing code was working properly: PDPjs v1.30.1 Copyright © 2012-2016 Jeff Parsons @@ -263,18 +263,19 @@ When the code starts, the TRAP instruction has already pushed two words onto the 0(SP): previous PC 2(SP): previous PSW -The first instruction, `MOV @SP,2(SP)`, copies the *previous PC* onto the *previous PSW*, which is where we'll eventually want -*previous PC*, so that the handler can eventually return with a simple `RTS PC`. +The first instruction, `MOV @SP,2(SP)`, copies the *previous PC* onto the *previous PSW*, which is where we'll +eventually want *previous PC*, so that the handler can eventually return with a simple `RTS PC`. -The next instruction, `SUB #2,@SP`, subtracts 2 from the original *previous PC*, so that it now points to the TRAP instruction. +The next instruction, `SUB #2,@SP`, subtracts 2 from the original *previous PC*, so that it now points to the TRAP +instruction. -Then `@(SP)+,-(SP)` fetches the TRAP instruction while also "popping" the original *previous PC* and then "pushing" TRAP -instruction onto the stack, overwriting the original *previous PC*. +Then `@(SP)+,-(SP)` fetches the TRAP instruction while also "popping" the original *previous PC* and then "pushing" +TRAP instruction onto the stack, overwriting the original *previous PC*. -The next few instructions shift the TRAP right to see if bit 0 is set, and if it is not, then the TRAP instruction is restored -by shifting it left again, and then a large offset is added to it, transforming the TRAP instruction (which is now known to be -an *even* value) into a jump table index. +The next few instructions shift the TRAP right to see if bit 0 is set, and if it is not, then the TRAP instruction +is restored by shifting it left again, and then a large offset is added to it, transforming the TRAP instruction (which +is now known to be an *even* value) into a jump table index. The final instruction, `MOV @(SP)+,PC`, moves the address at the jump table index into PC, while also removing the TRAP -instruction from the stack, leaving only the *previous PC* on the stack, so that when the TRAP handler is done, it can execute -`RTS PC` to return to the caller. +instruction from the stack, leaving only the *previous PC* on the stack, so that when the TRAP handler is done, it can +execute `RTS PC` to return to the caller. diff --git a/modules/pdp11/lib/defines.js b/modules/pdp11/lib/defines.js index 676906eaf..20a80be43 100644 --- a/modules/pdp11/lib/defines.js +++ b/modules/pdp11/lib/defines.js @@ -950,9 +950,26 @@ var PDP11 = { } }; -PDP11.RX11.RX01 = ["DX", 77, 1, 26, 128, 0]; -PDP11.RK11.RK05 = ["RK", 203, 2, 12, 512, PDP11.RK11.RKDS.RK05 | PDP11.RK11.RKDS.SOK | PDP11.RK11.RKDS.RRDY]; -PDP11.RL11.RL02K = ["RL", 512, 2, 40, 256, PDP11.RL11.RLMP.GS_ST.LOCKON | PDP11.RL11.RLMP.GS_BH | PDP11.RL11.RLMP.GS_HO]; +PDP11.RX11.RX01 = [ + "DX", + 77, 1, 26, 128, // disk geometry (CHSN: cylinders, heads, sectors/track, and bytes/sector) + 1, 0, 0, 128, // boot code location (cylinder, head, sector index (NOT sector number), and number of bytes) + 0 // default drive status +]; + +PDP11.RK11.RK05 = [ + "RK", + 203, 2, 12, 512, // disk geometry (CHSN: cylinders, heads, sectors/track, and bytes/sector) + 0, 0, 0, 512, // boot code location (cylinder, head, sector index (NOT sector number), and number of bytes) + PDP11.RK11.RKDS.RK05 | PDP11.RK11.RKDS.SOK | PDP11.RK11.RKDS.RRDY +]; + +PDP11.RL11.RL02K = [ + "RL", + 512, 2, 40, 256, // disk geometry (CHSN: cylinders, heads, sectors/track, and bytes/sector) + 0, 0, 0, 256, // boot code location (cylinder, head, sector index (NOT sector number), and number of bytes) + PDP11.RL11.RLMP.GS_ST.LOCKON | PDP11.RL11.RLMP.GS_BH | PDP11.RL11.RLMP.GS_HO +]; PDP11.ACCESS.READ_WORD = PDP11.ACCESS.WORD | PDP11.ACCESS.READ; // formerly READ_MODE (2) PDP11.ACCESS.READ_BYTE = PDP11.ACCESS.BYTE | PDP11.ACCESS.READ; // formerly READ_MODE (2) | BYTE_MODE (1) diff --git a/modules/pdp11/lib/drive.js b/modules/pdp11/lib/drive.js index 883732fbc..d6a006649 100644 --- a/modules/pdp11/lib/drive.js +++ b/modules/pdp11/lib/drive.js @@ -555,12 +555,16 @@ class DriveController extends Component { * NOTE: We initialize the following drive properties to their MAXIMUMs; disks may have * these or SMALLER values (subject to the limits of what the controller supports, of course). */ - drive.sName = configDrive[0] + iDrive; - drive.nCylinders = configDrive[1]; - drive.nHeads = configDrive[2]; - drive.nSectors = configDrive[3]; - drive.cbSector = configDrive[4]; - drive.status = configDrive[5]; + drive.sName = configDrive[i++] + iDrive; + drive.nCylinders = configDrive[i++]; + drive.nHeads = configDrive[i++]; + drive.nSectors = configDrive[i++]; + drive.cbSector = configDrive[i++]; + drive.iCylinderBoot = configDrive[i++]; + drive.iHeadBoot = configDrive[i++]; + drive.iSectorBoot = configDrive[i++]; + drive.cbSectorBoot = configDrive[i++]; + drive.status = configDrive[i++]; /* * The next group of properties are set by various controller command sequences. @@ -817,7 +821,7 @@ class DriveController extends Component { * a READY state is assured, and the readData() call shouldn't do anything to change that. */ this.cpu.setReset(0, true); - var err = this.readData(drive, 0, 0, 0, 512, 0x0000, 2); + var err = this.readData(drive, drive.iCylinderBoot, drive.iHeadBoot, drive.iSectorBoot, drive.cbSectorBoot, 0x0000, 2); if (err) { this.notice("Unable to read the boot sector (" + err + ")"); } diff --git a/modules/pdp11/lib/rx11.js b/modules/pdp11/lib/rx11.js index bec551b49..bea362096 100644 --- a/modules/pdp11/lib/rx11.js +++ b/modules/pdp11/lib/rx11.js @@ -217,7 +217,7 @@ class RX11 extends DriveController { * @param {Object} drive * @param {number} iCylinder * @param {number} iHead - * @param {number} iSector (0-based) + * @param {number} iSector * @param {number} nWords * @param {number} addr * @param {number} inc (normally 2, unless inhibited, in which case it's 0) @@ -293,15 +293,15 @@ class RX11 extends DriveController { var iDrive = (this.regRXCS & RX11.RXCS.UNIT)? 1 : 0; var drive = this.aDrives[iDrive]; var disk = drive && drive.disk; - var iCylinder = this.regRXTA & RX11.RXTA.MASK, iHead = 0, iSector = this.regRXSA & RX11.RXSA.MASK; + var iCylinder = this.regRXTA & RX11.RXTA.MASK, iHead = 0, nSector = this.regRXSA & RX11.RXSA.MASK; this.regRXES &= ~(RX11.RXES.CRC | RX11.RXES.PARITY | RX11.RXES.DEL | RX11.RXES.DRDY); if (disk) { this.regRXES |= RX11.RXES.DRDY; - if (this.messageEnabled()) this.printMessage(this.type + ".readSector(" + iCylinder + ":" + iHead + ":" + iSector + ")", true, true); - this.assert(iSector); // RX sector numbers (unlike RK and RL) are supposed to be 1-based - var sector = disk.seek(iCylinder, iHead, iSector, true); + if (this.messageEnabled()) this.printMessage(this.type + ".readSector(" + iCylinder + ":" + iHead + ":" + nSector + ")", true, true); + this.assert(nSector); // RX sector numbers (unlike RK and RL) are supposed to be 1-based + var sector = disk.seek(iCylinder, iHead, nSector, true); if (sector) { var i = 0, nBytes = this.abBuffer.length; while (i < nBytes) { @@ -334,15 +334,15 @@ class RX11 extends DriveController { var iDrive = (this.regRXCS & RX11.RXCS.UNIT)? 1 : 0; var drive = this.aDrives[iDrive]; var disk = drive && drive.disk; - var iCylinder = this.regRXTA & RX11.RXTA.MASK, iHead = 0, iSector = this.regRXSA & RX11.RXSA.MASK; + var iCylinder = this.regRXTA & RX11.RXTA.MASK, iHead = 0, nSector = this.regRXSA & RX11.RXSA.MASK; this.regRXES &= ~(RX11.RXES.CRC | RX11.RXES.PARITY | RX11.RXES.DEL | RX11.RXES.DRDY); if (disk) { this.regRXES |= RX11.RXES.DRDY; - if (this.messageEnabled()) this.printMessage(this.type + ".writeSector(" + iCylinder + ":" + iHead + ":" + iSector + ")", true, true); - this.assert(iSector); // RX sector numbers (unlike RK and RL) are supposed to be 1-based - var sector = disk.seek(iCylinder, iHead, iSector, true); + if (this.messageEnabled()) this.printMessage(this.type + ".writeSector(" + iCylinder + ":" + iHead + ":" + nSector + ")", true, true); + this.assert(nSector); // RX sector numbers (unlike RK and RL) are supposed to be 1-based + var sector = disk.seek(iCylinder, iHead, nSector, true); if (sector) { if (fDeleted) sector.deleted = true; var i = 0, nBytes = this.abBuffer.length;