:IF[WithCDC9730]; **************************************

TITLE[CDC9730];
*VIDEO DISK CONTROLLER TASK FIRMWARE
SetTask[vdTASK];

% CDC9730.mc
Tom Rich October 12, 1982 move vdCSBpointer, vdCSBptrHi. delete vdCSBptrHi initialization kluge.
Tom Rich May 18, 1982 initialize vdCSBptrHi to 120C(!). move CSB to task 7’s slot if in Pilot.
Tom Rich April 23, 1982 merge VdDefs and xVdcs together, rename CDC9730.
Tom Rich April 22, 1982 make conditional on WithCDC9730.
Tom Rich March 25, 1982 move vdPage1..vdPage4 dispatches.
Tom Rich July 20, 1981 retain CSW’s encoded field status after good completion of run-of-pages.
Tom Rich June 15, 1981 cosmetics.
Tom Rich June 11-12, 1981 add runs-of-pages implementation.
Tom Rich June 10, 1981 spill over control commands onto eim page to make room for runs-of-pages.
Tom Rich June 9, 1981 strip out bravo looks. add CSB, IOCB formats. initialize vdCSBpointer from R0.
Tom Rich May 14-15, 1981 pilot IOCB format changes anticipating runs-of-pages.
Tom Rich May 12-14, 1981 cleanup. juggle RM registers.
Tom Rich May 5, 1981 remove time-of-day clock.
rej November 20, 1980 vdSEEK, big bad terrible output gotcha
rej June 30, 1980 mods for amargosa
rej November 26, 1979 move vdPage3 dispatches
rej October 16, 1979 SRDs idle loop
rej August 28, 1979 3:49 PM
rej August 20, 1979 moved vdPage3 dispatches
rej August 20, 1979 init changed for 8G
last modified by J.A. Steffen July 12, 1979 9:40 AM
%

*
VIDEO DISK CONTROLLER TASK DEFINITIONS

MC[vdTaskComplement,AND[17,NOT[vdTask]]];
SET[vdNotifyTask,LSHIFT[vdTask,14]];

*
Controller hardware I/O register definitions

SET[vdRDATA,7]; * data buffer (input and output)
SET[vdDATA,ADD[LSHIFT[vdTASK,4],vdRDATA]]; * data buffer (iostore and iofetch)

SET[vdRESET,0]; * hardware reset register (output)

SET[vdRHDCYL,1]; * head/cylinder register (output)
* bit 0-3=head in [0..4] for single track mode
* =16 for verdi format five track mode
* bit 4-5=unused
* bit 6-17=cylinder in [0..819d]

SET[vdRORD,2]; * order register (output)
* bit 0-7=unused
* if control order
* bit 10-17=4 for seek
*=5 for return to zero
*=6 for clear disk fault
* else if diagnostic read/write order
* bit 10-15=34 for naked write
*=54 for naked read
*=15 for diagnostic write
*=16 for diagnostic read
* bit 16-17=format
* else if buffer diagnostic order
* bit 10-17=45 for buffer load
*=51 for buffer unload
*=55 for buffer switch
* else if format write order
* bit 10-15=25
* bit 16-17=format
* else
* bit 10-11=header order=0 for header nop
*=1 for header write
*=2 for header read
*=3 for header search
* bit 12-13=label order=0 for label nop
*=1 for label write
*=2 for label read
*=3 for label verify
* bit 14-15=data order=0 for data nop
*=1 for data write
*=2 for data read
*=3 for data verify
* bit 16-17=format

* format=1 for Pilot format (256d data words, 32d trailer words)
* =2 for Xenia format (1600d data words, 32d trailer words)
* =3 for Verdi format (7680d data words, 96d trailer words)

SET[vdRSELECT,3]; * format/unit select register (output)
* bit 0-11=unused
* bit 12=allowwake
* bit 13=select
* bit 14-15=format
* bit 16-17=unit in [0..3]

SET[vdRID,0]; * id register (input)
MC[vdIDhi,4400]; * upper half of VDC ID

SET[vdRSTATUS,2]; * status register (input)
* bit 0=on cylinder
* bit 1=seek end
* bit 2=sector mark
* bit 3=index mark
* bit 4=data overrun error
* bit 5=five track disk
* bit 6=flawed sector
* bit 7=parity or memory fault error
* bit 10=no compare error
* bit 11=sync error
* bit 12=CRC error
* bit 13=nonzero data
* bit 14=disk fault
* bit 15=seek error
* bit 16=unit selected
* bit 17=unit ready

*
the CSB

* The following are offsets within the CSB.
MC[vdCSBnext,0]; * next IOCB to process
MC[vdCSBwakeupMask,1]; * wakeup mask to schedule mesa processes at end of operation
MC[vdCSBstatus,2]; * most recent status
MC[vdCSBformatDrive,3]; * current format and drive
MC[vdCSBdiskAddress,4]; * ARRAY [0..3] contains current head/cylinder
* address for each drive (-1 means recalibrate)
MC[vdCSBlast,10]; * last IOCB processed

*
the IOCB

* The following are offsets within the IOCB.
MC[vdIOCBdataPtr,0]; * hardware LONG POINTER
MC[vdIOCBdataPtr1,1]; * hardware LONG POINTER, high word
MC[vdIOCBnext,2]; * link to next IOCB
MC[vdIOCBtrailerPtr,4]; * hardware LONG POINTER
MC[vdIOCBtrailerPtr1,5]; * hardware LONG POINTER, high word
MC[vdIOCBstatus,6]; * completion status
MC[vdIOCBcommand,14]; * command for controller and for microcode
MC[vdIOCBhdCyl,15]; * head and cylinder
MC[vdIOCBsector,16]; * sector (sign bit signifies flawed sector)
MC[vdIOCBpageCount,17]; * number of pages for this IOCB (zero suppresses all runs-of-pages features)
MC[vdIOCBdiskLabel,20]; * packed label read from the disk
MC[vdIOCBdiskLabel+4,24]; * packed label read from the disk
MC[vdIOCBclientLabel,30]; * packed label from client, sent to the disk for write or verify
MC[vdIOCBclientLabel+4,34]; * packed label from client, sent to the disk for write or verify
MC[vdIOCBFileFlags,34]; * contains flags which must be zeroed on runs-of-pages
MC[vdIOCBFilePageLo,35]; * must be incremented on runs-of-pages

*
some constants

MC[vdEFS,10000]; * encoded field status lsb
MC[vdDFS,30000]; * encoded field status for data field
MC[vdCylSeekInhibit,2000]; * bit to inhibit cylinder seeks in hd/cyl address

MC[vdMaskFileFlags,7]; * masks the three file flags which must be cleared in the label
* of subsequent pages in a run-of-pages

*
D0 task register definitions

SET[vdRbase,AND[60,LSHIFT[vdTASK,4]]];

RV2[vdPTR,vdPTRhi,ADD[vdRbase,0]]; * general purpose base register (must be even-aligned)
* n.b. vdPTR and vdPTRhi are clobbered by NotifyInterrupt
**RV2[vdCSBpointer,vdCSBptrHi,ADD[vdRbase,2]]; * CSB base register (must be even-aligned)
RV2[vdCSBpointer,vdCSBptrHi,ADD[vdRbase,22]]; * CSB base register (must be even-aligned)
* n.b. vdCSBpointer and vdCSBptrHi are now registers 342-343b, in task 16b’s block.
* this means they cannot be the data source/destination of a memory operation.
* in memory operations, they are used only as the base address.
* the change was made to avoid changing the Trinity ProcessorHeadD0, which "knows"
* the location of vCrystal at register 323b.
RV4[vdIOCB,vdSYNCH,vdCSW,vdSLT,ADD[vdRbase,4]]; * CSB 0-3 (must be quad-aligned)
* vdIOCB - pointer to current IOCB
* vdSYNCH - mesa interrupt mask (also used as vdIOCBhi)
* vdCSW - current status word
* vdSLT - current format and drive
RV4[vdCMD,vdHDCYL,vdSECTOR,vdPGCNT,ADD[vdRbase,10]]; * IOCB 14-17 (must be quad-aligned)
* vdCMD - command
* vdHDCYL - current head/cylinder address
* vdSECTOR - sector address
* vdPGCNT - page count
RV[vdBUFINDX,ADD[vdRbase,14]]; * data transfer buffer index
RV[vdBCNT,ADD[vdRbase,15]]; * data transfer block count
RV[vdTMP0,ADD[vdRbase,16]];
RV[vdTMP1,ADD[vdRbase,17]];

*
INITIALIZATION CODE.

OnPage[vdInitPage];

* Redefine temporary register for initialization.
RV[vdR0, ADD[vdRbase,0]]; * Initialize.mc (or CDC9730Loader.mc) points R0 at the CSB.

vdInit:
OUTPUT[vdTMP1,vdRESET], AT[vdInitLoc];*Reset controller.
vdSLT←4C; *Specify drive 0 and Pilot format.
OUTPUT[vdSLT,vdRSELECT];*Send code to controller.

**
T←vdR0;
vdTMP0←177400C;*Address of Pilot’s CSB page.
LU←(vdR0)-(177400C); *Compare CSB address parameter to address of Pilot’s CSB page.
Skip[ALU>=0],*Determine whether we are in Pilot or Initial microcode.
T←(vdTMP0)+(160C);*Address of task 7 CSB, which is used in Pilot microcode.
T←vdR0;*If we are part of Initial microcode, R0 is to be believed.

vdCSBpointer←T;*Set up base pointer to CSB.

vdCSBptrHi←1C; *The CSB is in MDS 0.
**
vdCSBptrHi←120C; *This is a BIZARRE KLUGE.
* Because the CSB is always entirely in MDS 0, the second byte of vdCSBptrHi
* is not used by the memory access mechanism. However, SDD and 5700 boot files
* expect to find vCrystal in vdCSBptrHi; 120C is the correct value for a 40 mHz
* processor crystal (c.f. Initialize.mc). This makes ProcessorHeadD0’s notion of
* vCrystal correct, enabling use with SDD/5700 boot files.

vdSLT←(vdSLT) OR (60C);*Allow wakeup, select drive. Gotcha # 15.
OUTPUT[vdSLT,vdRSELECT];*Select drive and enable wakeups.
vdCMD←ZERO;
vdHDCYL←ZERO;
vdSECTOR←ZERO;
vdPGCNT←ZERO;
NOP; *Gotcha # 5.
PSTORE4[vdCSBpointer,vdCMD,0];*Initialize CSB, first 4 words.
vdCMD←(ZERO)-1;
vdHDCYL←(ZERO)-1;
vdSECTOR←(ZERO)-1;
vdPGCNT←(ZERO)-1;
NOP; *Gotcha # 5.
PSTORE4[vdCSBpointer,vdCMD,4];*Set all drives to recalibrate.
INPUT[vdTMP1,vdRSTATUS];*Get status.
LU←(vdTMP1) AND (2C); *Test if drive is selected.
Goto[vdDSKNOTSELT, ALU=0];*Jump if not.
Loadpage[vdPage];
GotoP[vdIDLE];*Initialize TPC.
vdDSKNOTSELT:
vdTMP1←(1000C);
Loadpage[0];
T←(vdTMP1) OR (236C), CallP[PNIP]; *Post error code = 670.
Goto[vdDSKNOTSELT];



Onpage[vdPage];

vdSLEEP:
IOSTROBE; *Reset wakeups.

vdTASKOUT: RETURN;
*Task switch.



%
IDLE LOOP.

Awake each sector time and update the CSW in the CSB. Test the CSB for an IOCB chain and go process it if present, else go back to sleep. %

vdIDLE:
NOP;*Call placement (vdFETCH is at .+1).
Call[vdSLEEP];*Sleep until sector wakeup.

vdSTAT:
PFETCH4[vdCSBpointer, vdIOCB, 0]; *Get CSB0 - CSB3.
INPUT[vdCSW, vdRSTATUS];*Get disk status.
vdCSW ← LDF[vdCSW, 4, 14];*Clear upper 4 status bits.
LU ← vdIOCB;*Test if any IOCB to be processed.
PSTORE4[vdCSBpointer, vdIOCB, 0], *Write updated status to CSB.
DblGoto[vdIDLE, vdFETCH, ALU=0]; *Idle if no IOCB.



%
COMMAND DECODE

Fetch an IOCB and decode the command. Start the controller in the proper routine and dispatch on the command bits. %


* Fetch the IOCB pointed to by the CSB.

vdFETCH:
vdSYNCH←1C,*Make vdIOCB a base reg pointer to IOCB.
TASK;
PSTORE1[vdCSBpointer,vdIOCB,10]; *Task switch. Save current IOCB pointer.
PFETCH4[vdIOCB,vdCMD,14];*Fetch IOCB command.
***
vdCSW ← LDF[vdCSW, 4, 14];*Initialize CSW. ***Now done elsewhere.

* Test if new format same as current format.

T←LDF[vdCMD,14,2];*New format to T.
LU←(LDF[vdSLT,14,2]) - (T);*Compare.
Goto[vdNEWFMT,ALU#0],
T←(vdCMD) AND (14C); *Parameter for vdNEWFMT.

* Test for control command or seek required.

vdSLTD:
LU←LDF[vdCMD,4,1];*Test for control command.
Goto[vdCNTRLCMD,ALU#0],*Jump if yes.
PFETCH1[vdCSBpointer,vdTMP1,4]; *Get old seek address.
T←vdHDCYL;*New head/cyl to T.
LU←(vdTMP1) - (T);*Is seek required?
Goto[vdSKDN,ALU=0],*Jump if not,
vdTMP0←(4C);*Set up for seek.
LoadPage[vdPage4];
GotoP[vdSEEK];*else go seek.

* Dispatch on command bits 5 - 7 (diagnostic mode bit,,header order).

vdSKDN:
vdCSW←(vdCSW) OR (vdEFS);*Set header field op in CSW.
T←LDF[vdCMD,6,10],*Generate useq start address.
TASK;
vdTMP1←T;*Put it in TMP1.
OUTPUT[vdTMP1,vdRORD];*Start useq.
DISPATCH[vdCMD,5,3];*Set up dispatch.
DISP[vdH0], *Dispatch on header code
IOSTROBE;*Reset wakeup.

SET[vdHloc,ADD[LSHIFT[vdPage,10],20]];*Set dispatch table boundary.

* Control commands come here to decode by dispatch on command bits 9-11.

vdCNTRLCMD:
LoadPage[vdPage4];
GotoP[.+1],
DISPATCH[vdCMD,11,3]; *Set up dispatch.

OnPage[vdPage4];

DISP[vdNOP],*Dispatch on control command.
vdTMP0←(4C);*Set up for seek.

Loca[vdCloc,vdPage4,260]; *Dispatch table.



%
SELECT A NEW FORMAT.

Select a new format as specified in the IOCB command and update the CSW. Wait for index to synchronize the sector counter hardware. %

OnPage[vdPage];

vdNEWFMT: vdSLT←T,
Call[vdSLEEP];*Wait for next sector (assures seek end).
vdSLT←(vdSLT) OR (60C);*Allow wake & unit select.

* Wait for index.

NOP; *gotcha # 6.
vdWTSLT:
OUTPUT[vdSLT,vdRSELECT];*Select new format.
PSTORE2[vdCSBpointer,vdCSW,2],*Update status & select code.
Call[vdSLEEP];*Wait for index.
INPUT[vdCSW,vdRSTATUS];*Get disk status.
LU←LDF[vdCSW,3,1];*Test if index yet.
Goto[vdWTSLT,ALU=0], *Jump if not.
vdCSW←LDF[vdCSW,4,14];*Zero upper 4 status bits.
NOP;
PSTORE2[vdCSBpointer,vdCSW,2],*Update status.
Goto[vdSLTD];*Re-enter command decode.



%
CONTROL COMMANDS.
%

OnPage[vdPage4];

* NOP. Delays one sector.

vdNOP:
LoadPage[vdPage], AT[vdCloc,0];*End of command. Go wait for next sector.
GotoP[vdCMDEND2];

* CLEAR. Clear disk fault.

vdTMP0←6C, AT[vdCloc,2];*useq start address to TMP0.
vdCLR:
NOP;
OUTPUT[vdTMP0,vdRORD], *Start useq.
Goto[vdNOP];

* RTZ. Perform RTZ & check if also CLR.

vdRTZ:
vdTMP0←5C, AT[vdCloc,1];*useq start address for RTZ.
vdRTZA:
vdHDCYL←ZERO;*Zero out head/cyl address.
OUTPUT[vdTMP0,vdRORD];*Start useq.
LoadPage[vdPage];
PSTORE1[vdCSBpointer,vdHDCYL,4], *0 to CSW seek adddress.
CallP[vdSLEEP];*Wait for RTZ seek to complete.
LU←LDF[vdCMD,12,1];*Test if clear command also.
Goto[vdCLR,ALU#0],*Jump if yes.
vdTMP0←6C; *useq start address to TMP0.
NOP;*gotcha # 2.
LoadPage[vdPage];
GotoP[vdCMDEND];

* RESET. Reset the controller and reselect the device. The command is changed to a NOP and executed.

vdRST:
OUTPUT[vdTMP0,vdRESET], AT[vdCloc,5];*Reset the controller.
LoadPage[vdPage];
vdCMD←(vdCMD) AND NOT (360C),*Turn command into a NOP.
GotoP[vdWTSLT];

* RTZCLR. Perform RTZ then CLR.

vdRTZCLR:
vdTMP0←5C, *Start address for useq RTZ.
Goto[vdRTZA], AT[vdCloc,3];



%
SEEK ROUTINE.

The cylinder and head address of the current IOCB is loaded to the controller and a seek order started. The controller will wake us up when the seek is complete and we have a sector pulse high. %

vdSEEK:
OUTPUT[vdHDCYL,vdRHDCYL], at[vdCloc,4];*send head/cyl to controller.
OUTPUT[vdTMP0,vdRORD];*start useq.
LoadPage[vdPage];
vdTMP0←vdTMP0,
CallP[vdSLEEP];*go sleep until seek complete.

* Seek has completed.

NOP;
LU←LDF[vdCMD,4,1],*Test for control command.
Goto[vdSKEND,IOATTEN];*Abort command if IOATTEN.
PSTORE1[vdCSBpointer,vdHDCYL,4], *Re-enter command decode if not control
Goto[vdSEKDN,ALU=0]; *command & update CSB seek address.
LoadPage[vdPage];
Goto[vdCMDEND];*End of command.
vdSKEND:
NOP;*gotcha # 2.
LoadPage[vdPage];
Goto[vdCMDEND];*End of command.
vdSEKDN:
LoadPage[vdPage];
Goto[vdSKDN];*Re-enter command decode.



%
HEADER READ OPERATION.

Read one header and load it into the data buffer area (16 words). %

OnPage[vdPage];

vdHDRRD:
PFETCH2[vdIOCB,vdPTR,0], AT[vdHloc,2];*Fetch data buffer pointer.
Call[vdTASKOUT];*Wait for data
NOP;*Wait for IOATTEN.
Goto[vdCMDEND,IOATTEN];*Abort if an error occurred.
IOSTORE16[vdPTR,vdDATA,0],*Fetch the header data.
Goto[vdCMDEND2];



%
HEADER WRITE & FORMAT WRITE.

Dispatch on command bits 11 - 13. Determine format & single header write or format write and set block count and transfer count to the proper values, then jump to the data transfer routine to output the data to the controller data buffers. %

SET[vdHWloc,ADD[LSHIFT[vdPage,10],60]];*Assign dispatch table.

vdHDRWR:
DISPATCH[vdCMD,13,3], AT[vdHloc,1];*Set up dispatch on format bits.
vdHWR:
DISP[vdHWR],AT[vdHWloc,0],*Dispatch on format.
vdBUFINDX←0C;*Init register.

* Set transfer count and block count for various formats.

vdBUFINDX←(vdBUFINDX) - (1C),*Set xfer count to 16 words.
Goto[vdHWR1], AT[vdHWloc,1];*Pilot format; header write.
vdBUFINDX←(vdBUFINDX) - (1C),*Set xfer count to 16 words.
Goto[vdHWR1], AT[vdHWloc,2];*Xenia format; header write.
vdBUFINDX←(vdBUFINDX) - (1C),*Set xfer count to 16 words.
Goto[vdHWR1], AT[vdHWloc,3];*Verdi format; header write.
vdBUFINDX←(vdBUFINDX) - (4C),*set xfer count to 64 words.
Goto[vdHWR1], AT[vdHWloc,5];*Pilot format; Format write.
vdBUFINDX←(vdBUFINDX) - (1C),*Set xfer count to 16 words.
Goto[vdHWR1], AT[vdHWloc,6];*Xenia format; format write.
vdBUFINDX←(vdBUFINDX) - (5C),*Set xfer count to 80 words.
AT[vdHWloc,7];*Verdi format; format write.
vdBCNT← 5C, *Set block transfer size to 5 blocks.
Goto[vdWRITE];
vdHWR1:
vdBCNT← 1C;*Set block transfer size to 1 block.
vdWRITE:
PFETCH2[vdIOCB,vdPTR,0],*Fetch data buffer pointer.
Call[vdTASKOUT];*Wait for data request.

* Continue here when VDC requests header data.

vdBUFINDX←LSH[vdBUFINDX,4];*Convert block count to word count.
Goto[vdHWR2,IOATTEN]; *Abort op if IOATTEN.
Goto[vdDTWR];*Go to data write routine.
vdHWR2:
Goto[vdCMDEND];



%
HEADER VERIFY ROUTINE.

Dispatch on the format and set up the data field count size. Then, supply the header from the IOCB for the verify operation. Then, test if chaining & fetch the next seek address if chaining on the same device and send it to the VDC; otherwise leave the old address in the VDC. %

SET[vdHVloc,ADD[LSHIFT[vdPage,10],100]];*Set up dispatch table.

vdH0:
NOP, AT[vdHloc,0];*Clock over header (fake a verify).
vdHDRVFY:
DISPATCH[vdCMD,14,2], AT[vdHloc,3];*Set up dispatch on format.
vdHVOP:
DISP[vdHVOP], AT[vdHVloc,0],*Dispatch on format.
vdBUFINDX←0C;*Init register.

*Set up data counts (BCNT & BUFINDX).

vdBUFINDX←(vdBUFINDX) - (20C),*transfer count ← 256 words (16 blocks).
Goto[vdHV01], AT[vdHVloc,1];*PILOT format.
vdBUFINDX←(vdBUFINDX) - (144C),*transfer count ← 1600 words (100 blocks).
Goto[vdHV01], AT[vdHVloc,2];*XENIA format.
vdBUFINDX←(vdBUFINDX) - (400C),*transfer count ← 7680 words (480 blocks).
AT[vdHVloc,3];*VERDI format.
vdBUFINDX←(vdBUFINDX) - (340C);
vdHV05:
vdBCNT←5C, *Set block transfer size to 5 blocks.
Goto[vdHV02];
vdHV01:
vdBCNT←1C;*Set block transfer size to 1 block.

* Test for diagnostic command & fetch next IOCB for seek ahead.

vdHV02:
LU←LDF[vdCMD,5,1];*Test if diagnostic command.
Goto[vdHV03,ALU=0]; *Jump if not,
vdCSW←(vdCSW) OR (vdDFS),
Goto[vdWRITE];*Diagnostic mode.
vdHV03:
vdBUFINDX←LSH[vdBUFINDX,4];*Convert block count to word count.
PFETCH1[vdIOCB,vdPTR,2],*Fetch next IOCB pointer.
Call[vdTASKOUT];*Sleep (IOSTROBE is under vdSKDN).

* Header data requested. Transfer two bytes for PILOT & XENIA, 6 for VERDI.

T←3C;*Delay for IOATTEN. Setup verdi test.
vdPTRhi←(1C), *Set up for seek ahead and/or label op.
Goto[vdHV04,NOATTEN];*Jump if no error,
Goto[vdCMDEND];*else abort.
vdHV04:
LU←(LDF[vdCMD,14,2]) XOR (T);*Test if VERDI.
Goto[vdHVXENIA,ALU#0],*Jump if not.
OUTPUT[vdHDCYL,vdRDATA];*Send hd/cyl to VDC.
IOFETCH4[vdCSBpointer,vdDATA,0]; *VERDI, dump 4 filler words to VDC buffer.
vdHVXENIA: OUTPUT[vdSECTOR,vdRDATA];
*Send sector to VDC.

****** BEGIN runs-of-pages ******

LU←(LDF[vdCMD,14,2])-1; *test for Pilot format.
Goto[vdTESTLINK,ALU#0], *runs-of-pages only in Pilot format.
LU←RSH[vdPGCNT,1];*test page count for 0 or 1.
Skip[ALU#0], *if incomplete run-of-pages.
LU←(vdSECTOR) XOR (33c);*test for last sector of track.
Goto[vdTESTLINK];*placement.
Goto[vdLBL,ALU#0],*if not last sector of track. no seek.
T←(4c);*set up test for last head of cylinder.
LU←(LDF[vdHDCYL,0,4]) XOR (T);*test for last head of cylinder.
Skip[ALU=0], *if last head of cylinder.
T←(LDF[vdHDCYL,6,12])+1;*increment cylinder address, zero head.
T←(vdHDCYL)+(10000c); *increment head address.
vdTMP0←T, *vdHVNXT expects seek address in vdTMP0.
Goto[vdHVNXT1];*skip past the fetch.
vdTESTLINK:

****** END runs-of-pages ******

* Test if chaining on the same drive to determine seek ahead address.

LU←vdPTR; *Test if chaining.
Goto[vdHVNXT,ALU#0]; *Jump if yes
Goto[vdLBL];*else not; go to label field op routine.

* Fetch next seek address.

vdHVNXT:
PFETCH1[vdPTR,vdTMP0,15];*Fetch next cylinder/hd address.

****** BEGIN runs-of-pages ******

vdHVNXT1:

****** END runs-of-pages ******

T←LDF[vdHDCYL,6,12];*Get current cylinder address.
LU←(LDF[vdTMP0,6,12]) XOR (T);*Test if seek ahead is to same cylinder.
Goto[vdOUTCYL,ALU#0];*Jump if different cylinder.
vdTMP0←(vdTMP0) OR (vdCYLSEEKinhibit);*Same cyl, hd select only, inhibit cyl seek.

vdOUTCYL:
Call[vdTASKOUT];
OUTPUT[vdTMP0,vdRHDCYL];*Output next cylinder/hd address to VDC.
vdTMP0←(vdTMP0) AND NOT (vdCYLSEEKinhibit); *Remove cyl seek inhibit bit.
PSTORE1[vdCSBPOINTER,vdTMP0,4]; *Update seek addr in CSB.

* End of header field. Wait for wake up.

vdLBL:
T←(vdIOCB)+(30c),*Address of client label (lbl write/verify).
Call[vdSLEEP];*Wait for end of header field.
NOP;*Gotcha # 12.
vdPTR←T,*PTR to client label, for write or verify op.
Goto[vdLBLOP,NOATTEN];*Continue (jump) if no error,
Goto[vdCMDEND];*or abort the operation.



%
LABEL FIELD OPERATIONS (PILOT ONLY).

Dispatch on the label field operation. %

vdLBLOP:
DISPATCH[vdCMD,10,2]; *Set up dispatch on label op.
DISP[vdENDLBL1];*Dispatch on label op.

SET[vdLBloc,ADD[LSHIFT[vdPage,10],120]];*Set up dispatch table.

* Label read operation.

NOP, AT[vdLBloc, 2];*Allow call to align.
vdPTR←(vdPTR)-(10c),*Change PTR to disk label (label read op).
Call[vdSLEEP];*Wait for data.
IOSTORE4[vdPTR, vdDATA, 0];*Fetch label from VDC.
IOSTORE4[vdPTR, vdDATA, 4],
Goto[vdENDLBL];

* Label write and verify operation.

NOP, AT[vdLBloc, 3];*Label verify.
IOFETCH4[vdPTR, vdDATA, 0], AT[vdLBloc, 1];*Send label to VDC.
IOFETCH4[vdPTR, vdDATA, 4],
Call[vdTASKOUT];
IOFETCH4[vdPTR, vdDATA, 0];*Send label filler to VDC.
IOFETCH4[vdPTR, vdDATA, 4],
Call[vdSLEEP];
Call[vdSLEEP];*Wait for end of label field.

* End of label field.

vdENDLBL:
vdCSW←(vdCSW)+(vdEFS);*Wait for IOATTEN. Indicate label field.
vdENDLBL1: Goto[vdDTFLD,NOATTEN], AT[vdLBloc,0];
*Jump if no errors.
Goto[vdCMDEND];*else abort op.



%
DATA FIELD OPERATIONS.

Dispatch on the operation. %

vdDTFLD:
DISPATCH[vdCMD,12,2]; *Set up for dispatch on data field op.
PFETCH2[vdIOCB,vdPTR,0],*Fetch the data buffer pointer.
DISP[vdDNOP];*Dispatch on data field op.

SET[vdDFloc,ADD[LSHIFT[vdPage,10],140]];*Set up dispatch table..

* Data field commands.

vdDNOP:
Goto[vdCMDEND], AT[vdDFloc,0];*NOP on data field. End of command.
vdDATAWR: vdCSW←(vdCSW) OR (vdDFS),
*Update status to indicate data field.
Goto[vdDTWR], AT[vdDFloc,1];*Data write.
vdCSW←(vdCSW) OR (vdDFS),*Update status to indicate data field.
Goto[vdDTRD], AT[vdDFloc,2];*Data read
vdCSW←(vdCSW) OR (vdDFS),*Update status to indicate data field.
Goto[vdDTWR], AT[vdDFloc,3];*Data verify.



%
DATA READ TRANSFER ROUTINE.

Transfer the data from the VDC to the D0 memory buffer area. %

* Wait for the data.

vdDTRD:
Call[vdSLEEP];
NOP;*Wait for IOATTEN.
Goto[vdDTRD1,NOATTEN];*Jump if no error,
Goto[vdCMDEND];*else abort.

* Set up for transfer loop.

vdDTRD1:
Call[vdCALCADR], T←vdBUFINDX;*Calculate base reg address.
T←vdBUFINDX, *Set up T as index for first transfer.
Call[vdDRLOP];*Set up and enter loop.

* Read data transfer loop.

vdDRLOP:
LU←(vdBUFINDX)+(20C); *Test if this is last xfer.
IOSTORE16[vdPTR,vdDATA], Freezeresult;*Transfer 16 words to D0 memory,
vdBUFINDX←T←(vdBUFINDX)+(20C), *Increment buffer index by 16 words
Goto[vdNRLOP,ALU>=0];*Jump out of loop when done.
IOSTROBE, *Reset wakeup,
Goto[vdTASKOUT];*task, and loop.
vdNRLOP:
PFETCH2[vdIOCB,vdPTR,4],*Fetch trailer buffer pointer.
Goto[vdCRC,IOATTEN]; *Jump if CRC error.
Goto[vdCMDEND];*Else end of command.

*CRC error occurred. Set up for transfer to trailer buffer & wait for hamming codes.

vdCRC:
vdBUFINDX←T←3C;*Init registers.
LU←(LDF[vdCMD,14,2]) XOR (T);*Test if VERDI.
vdBUFINDX←(vdBUFINDX)-(11C),*Set block count to 6 for VERDI.
Goto[vdCRC1,ALU=0]; *Jump if VERDI,
vdBUFINDX←(vdBUFINDX)+(4C);*else PILOT,XENIA. Set block count to 2.
vdCRC1:
vdBUFINDX←LSH[vdBUFINDX,4];* Convert block count to word count.
Call[vdCALCADR], T←vdBUFINDX,*Calculate base reg.
IOSTROBE; *Task & wait for data.

* Transfer in HAMMING codes.

T←vdBUFINDX, *Set up index for first transfer.
Call[vdTRLOP];*Set up and enter loop.
vdTRLOP:
LU←(vdBUFINDX)+(20C); *Test if this is last xfer.
IOSTORE16[vdPTR,vdDATA], Freezeresult;*Transfer 16 words to D0 memory,
vdBUFINDX←T←(vdBUFINDX)+(20C), *Increment buffer index by 16 words
Goto[vdNTRLOP,ALU>=0];*Jump out of loop when done.
IOSTROBE, *Reset wakeup,
Goto[vdTASKOUT];*task, and loop.
vdNTRLOP:
Goto[vdCMDEND];*End of command.



%
DATA WRITE TRANSFER ROUTINE.

Transfer the data from the D0 data buffer to the VDC data buffer for a data write operation.
%

vdDTWR:
Call[vdCALCADR],T←vdBUFINDX;*Go calculate the base address.
T←vdBCNT; *Initialize blockcount.
vdTMP1←T; *Set up TMP1 as block counter.
T←vdBUFINDX, *Set up T for index for first xfer.
Call[vdDWLOP];*Set up and enter loop.

* Data transfer loop for write op.

vdDWLOP:
vdBUFINDX←(vdBUFINDX)+(20C);*Increment buffer index by 16 words.
IOFETCH16[vdPTR,vdDATA], Freezeresult;*Xfer 16 words to VDC buffer.
vdTMP1←(vdTMP1)-1,*Decrement block counter.
Goto[vdNDLOP,ALU>=0];*Jump out of loop when done.
T←vdBCNT,
Goto[vdDWLOP1,ALU#0];*Jump if a block is not done.
vdTMP1←T, *Block done. Re-init block cntr,
IOSTROBE; *reset wakeup.
T←vdBUFINDX,
RETURN; *Task and loop.
vdDWLOP1:
T←vdBUFINDX,
Goto[vdDWLOP];*Loop.

* Transfer Time-Of-Day clock.
* Fetch TOD clock MSW into TMP0, and output it and R25 (TOD LSW in task 15).

* vdNDLOP: vdPTRhi←(1C);*Form base pointer.
* vdPTR←(30C);*Form base pointer to TOD clock.
* vdPTR←(vdPTR) OR (400C);*Form base pointer.
* T←vdTOD,IOSTROBE;*Get TOD LSW
* vdTMP1←T,TASK;*Save TOD LSW.
* PFETCH1[vdPTR,vdTMP0,0];*Fetch TOD MSW & sleep.
* vdTMP0←vdTMP0;*Interlock.
* OUTPUT[vdTMP0,vdRDATA];*Output TOD MSW.
* OUTPUT[vdTMP1,vdRDATA];*Output TOD LSW.

* No Longer Transfer Time-Of-Day clock... it’s not maintained by Rubicon microcode.
* (commented code above assumes Alto compatibility).
* Instead send two words of zero.

vdNDLOP:
IOSTROBE;
vdTMP1←(0C),
Call[vdTASKOUT];
OUTPUT[vdTMP1,vdRDATA];*Output zero.
OUTPUT[vdTMP1,vdRDATA];*Output zero.

vdFILL:
IOFETCH16[vdCSBpointer,vdDATA,0]; *Output filler to buffer.
vdBCNT←(vdBCNT)-1;*Count out filler words.
Goto[vdFILL,ALU#0];*Loop until done.

vdENDWRITE:
T←(25C);*Test for format write.
LU←(LDF[vdCMD,6,6]) XOR (T);
T←LDF[vdCMD,4,10],
Goto[vdNOTFMTWR,ALU#0];
Goto[vdCMDEND2];*This was fmt write. End command.

vdNOTFMTWR:
vdTMP1←115C, *Look at command bits for diagnostic.
Call[vdSLEEP];
LU←(vdTMP1) XOR (T);*Test if diag write/read.
Goto[vdDWR,ALU=0];*Jump if yes,
Goto[vdCMDEND2];*else, end of command.
vdDWR:
PFETCH2[vdIOCB,vdPTR,4],*Diag write/read. Fetch data buf ptr.
Goto[vdCRC];



%
CALCULATE BASE ADDRESS

Calculate the new base register value by subtracting the displacement value (BUFINDX) from the buffer pointer. PTR,PTRhi is the buffer pointer upon entry & the base register upon exit.
Call this routine with:
Call[vdCALCADR],T←vdBUFINDX
%

vdCALCADR: vdPTR←(vdPTR)-(T);
*Generate LSB of pointer.
Goto[vdCALCRTN,CARRY];*Return if done,
vdPTRhi←(vdPTRhi)-(400C)-1;*else decrement MSB pointer.
vdCALCRTN: RETURN;



%
NAKED WRITES

Write the entire sector starting with the first word after the header sync byte. %

LOADPAGE[vdPage1], AT[vdHloc,5]; *Naked write entry.
GotoP[vdNWP],
DISPATCH[vdCMD,14,2]; *Set up dispatch on format.

Onpage[vdPage1];

Loca[vdNW,vdPage1,200]; *Dispatch table.

vdNWP:
DISP[vdNWP],
vdBUFINDX←ZERO, At[vdNW,0];

* Set up data counts (BCNT & BUFINDX)

vdBUFINDX←(vdBUFINDX)-(24C),*PILOT format. Set count to
Goto[vdNW01], at[vdNW,1];* 320 words (20 blocks).
vdBUFINDX←(vdBUFINDX)-(147C),*XENIA format. Set count to
Goto[vdNW01], at[vdNW,2];* 1648 words (103 blocks).
vdBUFINDX←(vdBUFINDX)-(400C),*VERDI format. Set count to
at[vdNW,3]; * 8000 words (500 blocks).
vdBUFINDX←(vdBUFINDX)-(364C);
LoadPage[vdPage];
GotoP[vdHV05];
vdNW01:
LoadPage[vdPage];
GotoP[vdHV01];



%
NAKED READS

Read the data field, CRC, TOD clock, and HAMMING codes as one data field. No CRC is checked on this read. %

Onpage[vdPage];

LOADPAGE[vdPage2], at[vdHloc,6]; *Naked read entry.
vdCMD←(vdCMD) XOR (340C),*Change command to nop label, read data.
GotoP[vdNRP];

Onpage[vdPage2];

Loca[vdNR,vdPage2,220]; *Dispatch table.

vdNRP:
DISPATCH[vdCMD,14,2], at[vdNR,0];*Set up dispatch.
DISP[vdNRP], *Dispatch on format.
vdBUFINDX←ZERO;
vdBUFINDX←(vdBUFINDX)-(21C),*PILOT format. Set transfer count to
Goto[vdNR01], at[vdNR,1];* 272 words (17 blocks).
vdBUFINDX←(vdBUFINDX)-(145C),*XENIA format. Set transfer count to
Goto[vdNR01], at[vdNR,2];* 1616 words (101 blocks).
vdBUFINDX←(vdBUFINDX)-(400C),*VERDI format. Set xfer count to
at[vdNR,3]; * 7760 words (485 blocks).
vdBUFINDX←(vdBUFINDX)-(345C);
Loadpage[vdPage];
GotoP[vdHV03],
vdBCNT←5C;*Set block size to 5.
vdNR01:
Loadpage[vdPage];
GotoP[vdHV03],
vdBCNT←1C;*Set block size to 1.



%
DIAGNOSTIC COMMANDS.
%

Onpage[vdPage];

vdDIAGNOSTIC:
Loadpage[vdPage3], at[vdHloc,4];
GotoP[vdDIAG],
DISPATCH[vdCMD,11,3]; *Setup dispatch on diagnostic command.

Onpage[vdPage3];

Loca[vddiags,vdPage3,240]; *Dispatch table.

vdWAIT:
RETURN;
vdDIAG:
DISP[vdDIAG],
vdBUFINDX←ZERO, at[vddiags,0];

* Buffer Load, unload, & switch.

vdBUFLOAD:
PFETCH2[vdIOCB,vdPTR,0], at[vddiags,1];*Fetch data buffer pointer.
Call[vdWAIT];*Wait for data request.
IOFETCH16[vdPTR,vdDATA,0];*Load buffer with 16 words.
vdBUFSWITCH:
Loadpage[vdPage], at[vddiags,3];
vdCSW←(vdCSW) AND NOT (vdDFS), *Clear EFS.
GotoP[vdCMDEND2];*End of command. Goto end routine.
vdBUFUNLOAD:
PFETCH2[vdIOCB,vdPTR,0], at[vddiags,2];*Fetch data buffer pointer.
Call[vdWAIT];
IOSTORE16[vdPTR,vdDATA,0],*Fetch data from buffer.
Goto[vdBUFSWITCH];

* Diagnostic write/read.

vdWRITEREAD:
Loadpage[vdPage], at[vddiags,5];
GotoP[vdHDRVFY];*go write then read.



%
ENDING ROUTINE

Check status for any errors. Test command flags for notifies and halts and perform accordingly. Then update status in the CSW and IOCB and test for chaining. If not chaining, go back to the IDLE loop, else continue with the next IOCB. %

Onpage[vdPage];

* Wait for a wakeup.

vdCMDEND2: Call[vdSLEEP];
*Task & wait for wakeup.
NOP;

* Get status and test for a status error.

vdCMDEND: INPUT[vdTMP1,vdRSTATUS];
*Get status from controller.
vdCSW←(vdCSW) AND (172000C);*Mask off lower 12 bts of CSW.
vdTMP1←T←(vdTMP1) AND NOT (172000C);*Mask off upper 4 bits of controller status.
vdCSW←(vdCSW) OR (T);*Form status word.
vdTMP1←(vdTMP1) AND NOT (21C);*Mask off unit ready & write protect.
LU←(vdTMP1) XOR (2C); *Test if status O.K.
Goto[vdUNE,ALU#0];*Jump if any error.

* Update IOCB & CSB.

****** BEGIN runs-of-pages ******

***vdEND:
NOP;
vdEND:
LU←(LDF[vdCMD,14,2])-1; *test for Pilot format.
Goto[vdIOCBDONE,ALU#0],*runs-of-pages only in Pilot format.
LU←vdPGCNT;*test page count.
Skip[ALU#0], *runs-of-pages only if page count > 0.
LU←(LDF[vdCMD,3,1]); *test incrementDataPtr.
Goto[vdIOCBDONE];*placement.
Skip[ALU#0], *if incrementDataPtr true.
T←(34c); *set up for fetching from client label.
Goto[vdUPDATELABEL]; *placement. incrementDataPtr false.
PFETCH4[vdIOCB,vdBUFINDX,0],*fetch quadword containing data ptr.
Call[vdTASKOUT];
vdBUFINDX←(vdBUFINDX)+(400c);*update low part of data ptr.
Skip[NoCarry];
vdBCNT←(vdBCNT)+(400c)+1;*if carry, increment high & high+1 parts.
NOP;*gotcha # 5.
PSTORE4[vdIOCB,vdBUFINDX,0];*store quadword with updated data ptr.
vdUPDATELABEL:
*n.b. T contains 34c, the offset to the second half of the client label.
PFETCH4[vdIOCB,vdBUFINDX],*fetch second half of client label.
Call[vdTASKOUT];
vdBUFINDX←(vdBUFINDX) AND NOT (7c);*clear flags.
vdBCNT←(vdBCNT)+1;*increment filePageLo.
LU←(vdSECTOR) XOR (33c); *gotcha # 5. test for last sector of track.
PSTORE4[vdIOCB,vdBUFINDX],*store updated second half of client label.
FreezeResult; *preserve sector test.
vdSECTOR←(vdSECTOR)+1, *tentatively increment sector address.
Goto[vdUPDATEPGCNT,ALU#0];*if not last sector of track.
*continue if sector address overflowed. must test head address. sector zero’ed below.
T←(4c);*set up test for last head of cylinder.
LU←(LDF[vdHDCYL,0,4]) XOR (T);*test for last head of cylinder.
vdHDCYL←(vdHDCYL)+(10000c),*tentatively increment head address.
Skip[ALU#0]; *if not last head of cylinder.
vdHDCYL←(LDF[vdHDCYL,6,12])+1;*increment cylinder address, zero head.
vdSECTOR←(ZERO);*placement.
vdUPDATEPGCNT:
vdPGCNT←(vdPGCNT)-1;
NOP; *gotcha # 5.
PSTORE4[vdIOCB,vdCMD,14],*store updated disk address and page count.
Call[vdTASKOUT];
LU←vdPGCNT; *test for completion of run.
Skip[ALU=0]; *if run completed.
Goto[vdSLTD], *if run not completed.
vdCSW←LDF[vdCSW,4,14]; *re-initialize CSW for vdFETCH.
NOP;*placement.
vdIOCBDONE:

****** END runs-of-pages ******

PSTORE1[vdIOCB,vdCSW,6],*Store CSW in IOCB ending status.
TASK;
PFETCH1[vdIOCB,vdIOCB,2];*Fetch pointer to next IOCB.
vdEND1:
PFETCH1[vdCSBpointer,vdSYNCH,1]; *Fetch SYNCH.
PSTORE4[vdCSBpointer,vdIOCB,0];*Update CSB.

* Notifies.

SET[vdNloc,ADD[LSHIFT[vdPage,10],160]];*Setup dispatch table.

DISPATCH[vdCMD,0,2]; *Setup dispatch on notify flags.
DISP[vdNOTIFY];
vdNOTIFY:
Goto[vdEND2], AT[vdNloc,0];*No notify.
LU←LDF[vdCSW,0,1], AT[vdNloc,1];*Notify on error. Test for UNE.
Goto[vdEND2,ALU=0]; *Jump if no UNE.
Goto[vdNOTE];
Goto[vdNOTE], AT[vdNloc,2];*Notify always.

* Notify here. SYNCH from CSB is ORed into notify register (NWW) and IntPending is set.

vdNOTE:
LoadPage[NotifyInterruptPage], AT[vdNloc,3];*Notify always.
T←vdSYNCH, *Bits to OR into NWW.
CallP[NotifyInterrupt]; *Clobbers registers 0,1 (vdPTR,vdPTRhi).

* Test for chaining.

vdEND2:
LU ← vdIOCB;*Test if pointer to next IOCB is zero.
vdCSW←LDF[vdCSW,4,14], *Re-initialize CSW for vdFETCH.
DblGoto[vdIDLE,vdFETCH,ALU=0]; *Idle if no IOCB.

* Unusual end routine.

vdUNE:
vdCSW←(vdCSW) OR (100000C);*Set UNE.
LU←LDF[vdCMD,2,1];*Test if SOE flag is set.
Skip[ALU#0]; *Skip if set.
Goto[vdEND];*Not set.
PSTORE1[vdIOCB,vdCSW,6],*Store CSW in IOCB.
TASK;
vdIOCB←ZERO;*Zero pointer to next IOCB.
Goto[vdEND1];

END[CDC9730];

:ELSE; *********************************************

TITLE[No.CDC.9730.disk.microcode];

:ENDIF; ********************************************