{File name: DiskDlionA.mc
Description: Dandelion Disk Controller microcode, Version 2.0
Purcell: 2-Jul-84 3:16:42 MCtl ← 8002h or XX for large real memory
Mesa 11: 31-Aug-83 9:32:46
Fasnacht 31-Aug-83 9:32:44, Move IOPage for larger VM
================
Author: D. Davies
Created: October 30, 1979
Last Edited: November 19, 1979 4:19 PM
Last Edited: November 19, 1979 4:19 PM
Last Edited: November 21, 1979 2:55 PM, "minimum code" version completed
Last Edited: January 9, 1980 8:09 PM, create "transfer run of pages command"
Last Edited: February 1, 1980 10:48 AM, complies with standard Dandelion register assignments in Dandelion.df
Last Edited: March 14, 1980 11:14 AM, renamed to SA4DiskDlion from DiskDlion so a new version of the
microcode may be produced for the SA1000/SA4000 controller.
Last Edited: March 18, 1980 10:07 AM, update to Rev D CP board which cancels MDR← following an automatic
PgCrossBr. Extra code now inserted to make sure last word in page is stored anyway.
Last Edited: April 11, 1980 1:50 PM, to comply with the new standards for the composite SA1000/SA4000 controller which
has longer label fields (code overhead reduced).
Last Edited: April 18, 1980 3:07 PM, take out features that depend on IgnorePgCr.
Last Edited: April 30, 1980 11:06 AM, perform combined test on Header Field.
Last Edited: May 16, 1980 4:19 PM, add extra delay before Read or Verify on SA1000 drive to account for extra delay
after writing a field on that drive.
Last Edited: May 22, 1980 3:27 PM, add extra two pattern words for SA1000 drive to account for extra delay added above.
Last Edited: June 11, 1980 7:13 PM add extra word to sync pattern for SA4000 drive to try to decrease intermitent errors.
Decrease length of sync pattern on SA1000 by one word to make room for 10 word labels eventually.
Last Edited: June 18, 1980 8:19 PM, modify to work with SA4000HeadDLion.
Last Edited: June 25, 1980 12:48 PM, fix ancient Inr bug and delete absolute starting address.
Last Edited: June 27, 1980 2:29 PM, fix ancient bug in FinishIOCB.
Last Edited: June 30, 1980 4:40 PM, reduce code overhead in an attempt to allow 10 word labels.
Last Edited: June 30, 1980 10:29 PM, reduce code overhead further and ensure that read headers and labels are not incremented.
Last Edited: July 1, 1980 1:12 AM, show final Sector Count after a transfer command.
Last Edited: July 1, 1980 12:46 PM, detect memory error in midst of run of pages.
Last Edited: July 2, 1980 9:29 AM, complement KStatus for new HSIO board.
Last Edited: July 1, 1980 4:51 PM, split into DiskDlionA.mc and DiskDlionB.mc
Last Edited: July 1, 1980 9:47 PM, Fix bug in logic that doesn’t change value read in label read.
Last Edited: August 2, 1980 3:07 PM, Use AltUaddr and trim down code.
Last Edited: September 19, 1980 2:24 PM, re-code Inr instruction and discard initialization of constants and page cross
detection.
Last Edited: October 8, 1980 4:53 PM, add CANCELBRs found by new burdock.
Last Edited: October 16, 1980 6:03 PM, take out MDR← not preceeded by MAR←.
Last Edited: November 4, 1980 11:09 AM, trap illegal instructions.
Last Edited: March 20, 1981 3:02 PM, Move "incrementDataPage" flag to UDataLen.
Last Edited: September 9, 1981 1:25 PM, Insert Kludge to work with old Rubicon Head (code looks for "IncrementDataPage"
flag in UDataPgNum. If it’s there it is moved to UDataLen and UDataPgNum is pre-decremented).
Last Edited: Jim Frandeen November 9, 1981 10:17 AM, Delete kludge for old head. delete bad inst addresses that could result from DISP4[Inr,8]
Last Edited: Amy Fasnacht August 3, 1982 1:02 PM, Modify references to IOPage
Last Edited: Amy Fasnacht 31-Aug-83 9:32:44, Move IOPage for larger VM
}
{----------------------------------- Start of Code ----------------------------------------}
{ THIS CODE WILL NOT RUN PROPERLY UNLESS InitDLion HAS INITIALIZED REGISTERS U0C00, UMaxSectTst, UStatusMsk and USyncAddrMk!!}
{Initially, the microcode is in the dormant state. Upon awakening, it constructs the CSB address and looks there for a pointer to an IOCB}
GetCSB:RAdr ← uIOPage, SetTask[4], StartAddress[GetCSB], c1;
RHRAdr ← IOPageHigh,c2, at[0F,10];
Noop,c3;
{Get IOCB physical address from CSB}
MAR ← [RHRAdr, RAdr + DiskCSBOffsetIOCB],c1; {fet IOCB Addr from
CSB}
RHRAdr ← 0, CANCELBR[GetCSB2C3,2],c2; {IOCB in 0th 64k
block of virt mem}
GetCSB2C3:RAdr←MD,c3; {get Lo 16 bits of
IOCB address}
{Find the physical address of the IOCB from its virtual address}
NewIOCB: Map ← [RHRAdr, RAdr+0], ZeroBr,c1; {start map ref, is this a nil IOCB?}
RCnt ← RAdr, BRANCH[GoodIOCB, NilIOCB],c2; {save lo 8 bits of IOCB address, decide about
IOCB Addr}
GoodIOCB:RAdr ← MD, RHRAdr ← MD, GOTO[StartIOCB],c3, at[0,2,NilIOCB]; {get hi 10 bits of IOCB address}
NilIOCB:KCtl ← 0, GOTO[GetCSB],c3, at[1,2,GoodIOCB]; {turn off hardware and wait
again.}
{ Re-assemble the IOCB’s address in RAdr}
StartIOCB:RAdr←MAR←[RHRAdr, RCnt+0],c1;
RCnt ← U0C00,c2; {clear the memory error flags before the IOCB
starts with constant reg.}
{MCtl ← RCnt or 2,}c3; {and do the actual clearing}
Noop,c1;
RCnt ← RRot1(RCnt + RCnt + 1),c2; {clear the memory error flags before the IOCB
starts with constant reg.}
MCtl ← RCnt or 2,c3; {and do the actual clearing}
{Now in executing state, instruction fetch is first. Since all instructions require at least one operand, It is fetched also before branching to each command routine}
GetCmd:MAR ← [RHRAdr, RAdr],c1, at[0,2,DoInc];
GetCmdC2:RAdr ← RAdr+1, CANCELBR[GetCmdC3, 2],c2; {set next IOCB addr}
GetCmdC3:RCnt ← MD,XHDisp,c3; {Decode command
from bits 0 and 4, there are only four possible
actions that may be taken in the first C2 of
a command}
FetArg:MAR ← [RHRAdr, RAdr], RAdr ← RAdr+1,DISP2[SameC2s],c1, at[0F,10]; {Start fetch
of 1st operand and
jump to cmd routine}
{to save code, the Increment memory, TestStatus, Jump, Set word, Memory to Memory move, Load Parameters, FinishIOCB and InitRegs commands are lumped together here. All do nothing in their first C2 and RCnt ← MD in the following C3.}
SameC2s:[] ← RCnt, YDisp, CANCELBR[SameC3s, 2],c2, at[0,4,SameC2s]; {decide which command it is}
SameC3s:RCnt ← MD, USaveRAdr ← RAdr, DISP4[Inr, 8],c3; {get first arg and continue with command}
{------------------- Command Routines -------------------------------------}
{Increment memory location, skip if zero
This routine is used both to count during loops and to increment label and header addresses. The word following the command contains the physical address of the location to be incremented. In the remainder of this click, the address is fetched. In the next click, the location’s contents are fetched. The value is then incremented and tested for zero. In the final click, the location is updated and the IOCB pointer incremented if the location’s new value is zero.}
{fetch the location’s contents}
Inr:RAdr ← MAR ← [RHRAdr, RCnt+0],c1, at[8,10,Inr]; {get address of loc to be
incremented}
Noop,c2;
RCnt ← MD,c3; {get value, MD too slow to Incr and test now}
{Increment, test and restore the value}
MAR ← [RHRAdr, RAdr+0],c1; {start store cycle}
MDR ← RCnt +1, ZeroBr, c2; {increment and Update location}
IncBr:RAdr ← USaveRAdr, BRANCH[GetCmd, DoInc],c3; {restore IOCB ptr and decide whether to get
the next command or the one after it.}
{Now start fetch of new command two words beyond the present one, can’t incremetn ptr on reading it from RSaveRAdr}
DoInc:MAR ← [RHRAdr, RAdr+2], RAdr ← RAdr+2, GOTO[GetCmdC2],c1, at[1,2,GetCmd];
{Jump
This instruction is used to unconditionally transfer control from one point in the IOCB to another. The low 16 bits of the physical address follows the command word. Note we can do RAdr ← RCnt while MAR← forces the upper half of the ALU to RAdr or 0 because the IOCB does not cross page boundries. Hence, only the least significant 8 bits are useful.}
Jump:RAdr ← MAR ← [RHRAdr, RCnt], GOTO[GetCmdC2],c1, at[0A,10,Inr];
{Send Control Word
This instruction is used generally to send particular commands to the drive, like HeadSelect or Step and also to control when the hardware send the service requests that enable the microcode. The control word to be sent follows the command word.}
SndCtlWd:ClrKFlags, CANCELBR[$, 2],c2, at[1,4,SameC2s]; {reset the latched
flags first so a wakeup depending
on them will be valid. }
KCtl ← MD, GOTO[GetCmd],c3; {send control wd and get next cmd}
{Load Transfer parameters
It is desirable to separate the loading of the Transfer Command’s parameters from the start of the command itself. This allows one to start the transfer immediately after sensing some condition on the disk (like index mark plus n sector pulses). This command loads the transfer command’s parameters from The location given in RCnt in the IOCB into U registers. Note that the parameter list must begin on a 16 word boundry. Only the fact that the parameters lie in U registers instead of main memory allows the transfer command to proceed quickly enough. The command word is followed by the address of the start of the parameter list.
To optimize the code for speed, dupicates of some quantities are held in U registers. These are the addresses of the file page numbers in the Label template and the address of the Head/Sector word in the Header, the file page number and the Head/Sector number.
It is also necessary to load 256 - the number of sectors/track into UMaxSectTst. This is done to speed the check for end of track.}
LoadPar:RAdr ← MAR ← [RHRAdr, RCnt], GOTO[LoadRCLpC2],c1, at[0D,10,Inr]; {start fet of sector
count, put the mem addr into RAdr.
This will load trash into U reg88 (fZ(Noop)
equals 8, U Reg 88 =ULabelLen) but
that’s ok since it will be fixed up in this loop.}
{this is the start of a loop to store the parameters. The mem addr register (RAdr) serves as the index into memory, the index into the U Registers and the loop index. This is why the parameters must begin at word 0 mod 16 in the IOCB. Note the NibCarryBr condition only detects the lower nibble of RAdr being zero, so all the 8x U regs are loaded here. URegs 81-88 are loaded correctly and 89 - 80 are loaded incorrectly. This is ok though because they will all be either loaded again below or in the transfer command.}
LoadRCLp:MAR ← [RHRAdr, RAdr+1], RAdr ← RAdr+1, NibCarryBr, AltUaddr,c1; {start fetch of next parameter, address Ureg
for prev. parm. Done with this loop?}
LoadRCLpC2: URCntBlk ← RCnt, BRANCH[LoadRCLpC3, StartRALp,2],c2; {store previous parm, decide ifdone with this
loop.}
LoadRCLpC3: RCnt ← MD, GOTO[LoadRCLp],c3, at[2,4,StartRALp]; {get next parm from list.,
loop back to store it.}
{Now that all the 8x parameters have been loaded, load the 7x parameters, or those belonging in U registers numbered 78 to 70. This is done by setting RAdr to x7 and starting to fetch again. note register 77 (UHeadSectorAddr) is loaded with trash in the first pass through the loop. This is ok, it is loaded correctly below anyway.}
StartRALp:RAdr ← RAdr-0A,c3, at[3,4,LoadRCLpC3]; {RAdr←xxx6, so first
parameter fetched is one in addr xxx7}
LoadRALp:MAR ← [RHRAdr, RAdr+1], RAdr ← RAdr+1, NibCarryBr, AltUaddr,c1; {fet next parm, see if it
is the last one and address the U Reg needed to
store the previous parm.}
LoadRALpC2: URAdrBlk ← RCnt, BRANCH[LoadRALpC3, FinLd,2],c2; {store previous parm, decide whether one
now being fetched is the last one}
LoadRALpC3: RCnt ← MD, GOTO[LoadRALp],c3, at[2,4,FinLd]; {fet parm, not the last one}
FinLd:RCnt ← MD,c3, at[3,4,LoadRALpC3]; {fetch last parameter}
UFindSectMkCmd ← RCnt,c1; {store last parameter}
RAdr ← RAdr-10,c2; {get address of SectorCount (beginning of
parm list, RAdr always =beginning of list +10
because of end conditions). USectorCntAddr
was trashed in loop that loaded RCnt U regs.
We will fix it below.}
{If the Header operation is a Read, it shouldn’t be incremented. This is signalled by having the saved HeadSector word be 8000.}
RCnt ← CReadMask,c3; {get mask used to detect read operation}
[] ← RCnt and UHeaderCmd, ZeroBr,c1; {is the header op a read?}
USectorCntAddr ← RAdr, BRANCH[SaveHeadSect, IsHeaderRead],c2; {fix up the address of the Sector Count.}
{The header operation is a Read, so don’t increment it after reading it. Flag this condition by seting UHeadSector to 8000.}
IsHeaderRead: RAdr ← RShift1 0, SE ← 1, GOTO[SetUHeadSect],c3, at[1,2,SaveHeadSect]; {load 8000 into
RCnt}
{The Header operation is not read so find the address of the HeadSector word in the Header}
SaveHeadSect: RAdr ← UHeaderAddr,c3, at[0,2,IsHeaderRead]; {get addr of beginning
of Header in IOCB}
{Load the Head/Sector word itself so it may be updated without reading the original for each update. Save its address for later update}
RAdr ← MAR ← [RHRAdr, RAdr+1],c1; {start read of the HeadSector word, note
The IOCB is on a single page so RHRAdr and
the upper byte of RAdr are still valid.}
UHeadSectorAddr ← RAdr, CANCELBR[LdHeadSectC3,2],c2; {save Head sector address}
LdHeadSectC3: RAdr ← MD,c3; {load the Head/Sector word into RAdr}
{Save the Head/Sector word, either the real one or 8000 => don’t change the one in memory since it will be read.}
SetUHeadSect: UHeadSector ← RAdr,c1; {save Head Sector word}
{Now decide if the Label operation is a read. If so, save the address of the Page number in the label as 0. this is a flag that the label is not be incremented.}
FixupDone:RAdr ← CReadMask,c2; {get mask used to detect read operations}
[] ← RAdr and ULabelCmd, ZeroBr,c3; {Is the Label operation a Read? (Zero=>Yes)}
RAdr ← USectorCntAddr , BRANCH[SavePgNum, IsLabelRead],c1; {Decide whether Label operation is a read.
Also put page address back into RAdr.}
IsLabelRead: ULabPgAddr ← 0, GOTO[FormHdNotOkMsk],c2, at[1,2,SavePgNum]; {the label operation is a
Read, so don’t increment the read value.}
SavePgNum:RCnt ← ULabelAddr,c2, at[0,2,IsLabelRead]; {RCnt ← addr of Label
Template in IOCB}
RCnt ← RCnt + CLabPgOffset,c3; {RCnt←addr of file pg num in IOCB}
{Save the address of the file page number and get the lo 16 bits of the file page number, it is also duplicated for speed}
ULabPgAddr ← MAR ← [RHRAdr, RCnt],c1; {save addr of page num and get page num}
CANCELBR[LoadPgC3,2],c2;
LoadPgC3:RCnt ← MD,c3; {get lo 16 bits of file page num}
{store a pre-incremented version of the file page number}
RCnt ← RCnt+1,c1; {pre-increment for easy storage when time
is critical later}
ULabPgLo ← RCntc2; {save label}
{form and save a single mask used to test the Header field of each sector. This mask combines the HeaderQuit and HeaderLoop masks. Doing this means that a good header need only pass one test, not two.}
FormHdNotOkMsk: RCnt ← UHeaderQuitMsk,c3; {get first Header mask}
RCnt ← RCnt or UHeaderLoopMsk,c1; {combine with second mask}
{Can also here from the Init regs command.}
FinLdReg:UHeaderNotOkMsk ← RCnt, GOTO[IncBr],c2; {If status passes this mask, Header is ok,
if not, either quit or loop}
{Transfer Run of Pages
This command is used to read, write or verify the Header, Label and Data fields of a run of sectors on the disk. A run of sectors occupies consecutive sectors on consecutive tracks. The same operation is done to each sector. With the LoadTransfer Pararmeters command, the Device Head or diagnostic client has complete control over the operation to be performed on each field and the status bits to be tested. This command has no arguments following it. If an error condition is encountered during the transfer, control is returned immediately to the command following this one. If this command completes successfully, the two words following this command are skipped. Thus by putting a JUMP to an abort instruction immediately after this command, one may catch all error conditions. Bits specifying the error will be left in UStatus. The single exception to this rule is found when a Memory error is encountered. In that case, the page number in the Label template is still incremented before control is returned and UStatus is left cleared. The Memory fault will be detected again in the FinishIOCB command and the proper status posted. The error is detected here so an IOCB may be aborted with some hope of knowing where to begin a retry.
It is assumed that no run of pages may cross a cylinder boundry. A run may cross a track boundry, causing the microcode to switch heads. Hence the maximum number of pages read by this command is (#of heads) * 28 sectors/track when connected to an SA4000 drive. It is (# of heads) * 16 sectors/track when connected to an SA1000 drive.
Processing begins by loading the parameters for transferring a header and finding the first sector mark. The Header field is then processed and the results tested using first the HeaderNotOKMsk. If this test is passed, we proceed to the Label field. If it fails, we test first against HeaderQuitMsk then the HeaderLoopMsk. If any status bits indicated by the Header Quit mask are enabled, control returns to the IOCB "program". If any status bits indicated by the HeaderLoop mask are set, the command decrements the FailCount. If non-zero, control is passed back to the start of the command to look for another header. If the FailCount is zero, a Header meeting the requirements cannot be found on this track so the command is aborted. Note one of hte bits tested when connected to an SA1000 drive should be the HeaderTag/SectorFound bit. This is set by a bit in the address mark of the Label and Data fields. It acts an error bit when looking for Header fields. If the status passed the HeaderNotOKMsk test, parameters are loaded for the label field and it is processed. If it passes the test specified in ULabelQuitMsk, the process is repeated for the Data section, otherwise the command is aborted. One difference between the Header and Label fields on the one hand and the Data field on the other is that the same two buffers are used to process the Header and Label fields of every page while each data page may require a new data buffer. Addresses of the string of data buffers are found by incrementing and mapping a virtual address in UDataPgNum. If the most significant bit of UDataLen is a zero, the same virtual data page is used for all sectors in the run. Only if it was set is UDataPgNum incremented for each page. If the status resulting from processing the Data field is meets the conditions specified in UDataQuitMsk, one sector has been processed and we usually increment the HeadSector number in the Header and the lo 16 bits of the file page number in the label. If the Header was just read, is is not changed, the same is true of the Label. First, the sector number in the low byte of the second Header template word is incremented. If less than 28 (0-27) for the SA4000 drive or 16 (0-15) for the SA1000 drive, we decide whether to increment the Label. If the sector number is greater than or equal to the number of sectors per track, the Head number is incremented in the Header Template and both command words where it appears (UFreezeCmd, UFindSectMkCmd). No test is made to see if the maximum head number has been exceeded as it is assumed this will never happen. The lower 16 bits of the page number is in the Label field template word 5. The upper 8 bits of file page number and the flags are not touched by this code. This means runs of pages may not cross page 0 to page 1 boundries of a file (the flags change) or 64k page boundries. After this, the sector count is decremented. If it becomes zero, we finish. Otherwise, control passes to NewSector.}
TransferRun:RAdr ← RAdr-1, CANCELBR[$, 2],c2, at[2,4,SameC2s]; {no args for this cmd}
USaveRAdr ← RAdr,c3; {so save RAdr pointing to next IOCB wd}
NewSector:RCnt ← CHeaderField,c1, at[0,2,DoneQuit]; {set partial status to say}
UField ← RCnt,c2; { starting the Header Field}
RAdr ← UHeaderLen,c3; {load the word count for the Header field}
UWdCount ← RAdr,c1; {pass it to TransferField}
RAdr ← RHRAdr,c2; {get hi bits of IOCB addr}
RHRCnt ← RAdr LRot0,c3; {set up for TransferField}
{return here when looking for another header}
NewHeader:RCnt ← UHeaderAddr, ClrKFlags,c1, at[0,2,NotFound]; {start hardware looking
for another header, clear SectorFounf flag}
RAdr ← UHeaderCmd, pCall4,c2; {get control wd for Header field}
KCtl ← UFindSectMkCmd, CALL[TransferField],c3, at[0,10]; {start transferring as soon as the
sector mark is found.}
HeaderRet:RCnt ← UHeaderNotOkMsk,c1, at[0,10,HeaderRet]; {get incorrect Header
status mask}
RCnt ← RCnt and ~KStatus, ZeroBr,c2; {any reason not to proceed with Label?}
UStatus ← RCnt , BRANCH[HeaderWrong, DoLabel],c3; {stop if so, else process Label Field}
HeaderWrong: RCnt ← UHeaderQuitMsk,c1, at[0,2,DoLabel]; {get abort after Header
status mask}
RCnt ← RCnt and ~KStatus, ZeroBr,c2; {should we quit now?}
BRANCH[HeaderQuit, TstSeenAll],c3; {quit if so, test FailCount if not since that was
not a fatal error.}
HeaderQuit:GOTO[FinXferRun],c1, at[0,2,TstSeenAll]; {set up error br and quit}
{When we reach here, the field just read was a fine field, but not the header we wanted. Test FailCount to see if we have exhausted the number of sectors on this track. If so, quit. If not, try the next one.}
TstSeenAll:RCnt ← UFailCount,c1, at[1,2,HeaderQuit]; {get # sectors left before}
RCnt ← RCnt-1, ZeroBr,c2; {out of sectors now?}
UFailCount ← RCnt, BRANCH[NewHeader, NotFound],c3; {quit if so, look at next if not}
{At this point, we have run out of sectors to inspect, so load the final error flags and quit}
NotFound:RCnt ← UStatus, GOTO[FinXferRun],c1, at[1,2,NewHeader];
{Here, the header was processed successfully, we proceed with the Label field. This is done by loading its parameters, setting the partial status to know the field being processed and calling TransferField}
DoLabel:RCnt ← ULabelLen,c1, at[1,2,HeaderWrong]; {get word count}
UWdCount ← RCnt,c2; {set up for TransferField}
RCnt ← CLabelField,c3; {get field number in case we die here}
UField ← RCnt,c1; {save it for FinishIOCB}
RAdr ← ULabelCmd, pCall4,c2; {get control word for label field}
RCnt ← ULabelAddr, CALL[TransferField],c3, at[1,10]; {go transfer the label field. }
{Return here after processing the label field}
LabelRet:RCnt ← ULabelQuitMsk,c1, at[1,10,HeaderRet]; {get quit status mask}
RCnt ← RCnt and ~KStatus, ZeroBr,c2; {anything wrong with this label?}
RAdr ← RRot1 ~1, BRANCH[LabelQuit, DoData],c3; {quit if so, else proceed. Load mask used to
separate "incrementDataPgNum" flag from
length of data field. The status is
not updated at this point because it must be
the same as the Header status if good and will
be set if bad. The data length is loaded now
so setup for the data field will fit into 3 clicks}
{At this point, we know there was something wrong with the processing of the label field, so we quit here}
LabelQuit:GOTO[FinXferRun],c1, at[0,2,DoData];
{The Header and label were ok, set up to do the Data field. In addition to setting the partial status and passing the word count and control word, we must map the virtual address before passing it. We also conditionally increment the virtual address. It is incremented if a new page is to be used for each sector transferred, left unchanged if the same virtual page is always to be used. The flag used to decide is in the most significant bit of the data field word count. It was placed there by the Head.}
DoData:RAdr ← RAdr and UDataLen, XHDisp,c1, at[1,2,LabelQuit]; {get the word count, incr
the virtual page number?}
RCnt ← UDataPgNum, BRANCH[NoIncrDatPtr, IncrDatPtr,2],c2; {get the virtual page number, decide whether
to increment it after use.}
NoIncrDatPtr: RHRCnt ← 1, GOTO[MapDataAddr],c3, at[2,4,IncrDatPtr]; {just point to Map in mem}
IncrDatPtr:RHRCnt ← 1, RCnt ← RCnt+1,c3, at[3,4,NoIncrDatPtr]; {also increment virt page
number before use}
MapDataAddr: MAR ← UDataPgNum ← [RHRCnt, RCnt], c1; {Start Mapping to get physical data page
address. Store incremented (or not) data page
number.}
RCnt ← CDataField, CANCELBR[GetPhysDatAddr, 2],c2; {Get constant used to say we are
now processing the data field.}
GetPhysDatAddr: UField ← RCnt, RHRCnt ← RCnt ← MD,c3; {update the state of the microcode
w.r.t. this page and get the physical page #
of this data page}
RCnt ← MAR ← [RHRCnt, 0+0],c1; {get address of the first word in the data page.}
UWdCount ← RAdr, pCall4,c2; {finally store the word count}
RAdr ← UDataCmd, CALL[TransferField],c3, at[2,10]; {go transfer the data (finally!)}
{Return here after processing the data field. Test to see if there was a fatal error in processing it}
DataRet:RCnt ← UDataQuitMsk,c1, at[2,10,HeaderRet]; {get msk specifying fatal
errors in the data field}
RCnt ← RCnt and ~KStatus, ZeroBr,c2; {any fatal errors?}
{arrive here after testing the data status above (obviously) and after setting the memory error bit after a memory error}
DataAndMemErrBr: RAdr ← ULabPgAddr, ZeroBr, BRANCH[DataQuit, NextSector], c3; {quit if so, else load addr of Page number and
see if done with Run. If addr =0, we did a label
read, so don’t increment the read value.}
{there was an fatal error in processing the Data field, so quit now}
DataQuit:CANCELBR[FinXferRun],c1, at[0,2,NextSector]; {set up error br and quit}
{At least one sector has been successfully transferred and more may follow. If necessary, the pointer to the next virtual memory page was incremented when it was used above. Increment the page number found in word 4 of the label. Only the low 16 bits of this page label will be incremented. The Head guarantees never to specify a run of pages that crosses a page-0-to-page-1 boundry or a 64K page boundry. Thus the most significant word of the label will be taken care of in the Head, not here.}
NextSector:MAR ← [RHRAdr, RAdr], RAdr ← UHeadSectorAddr+0, SuppressTimingWarning, BRANCH[StoLabel, NoStoLabel],
c1, at[1,2,DataQuit]; {start store of
l.s. 16 bits of page #, can never get
pageCarry here. Decide if L.S. 16 bits to be
stored at all. Note that a bogus mem operation
will be done here is the pg addr was set to zero.
Can this cause a Memory error?}
StoLabel:MDR ← RCnt ← ULabPgLo, GOTO[IncLabel],c2, at[0,2,NoStoLabel]; {update page num in
Label template}
NoStoLabel:RAdr ← UHeadSectorAddr,c2, at[1,2,StoLabel]; {don’t update the number in
the label that as read. Do fixup address of Head-
Sector Word in RAdr. The RAdr← in the MAR←
above onlyloads the bottom half and the top half
will havebeen zeroed bythe test value. This load
loads all 16 bits of physical address properly.
RCnt is lift with trash, but its value is never used
so that’s ok.}
IncLabel:RCnt ← RCnt+1, Xbus ← MStatus, XDisp,c3, at[2,4]; {pre-increment for next store, got a
memory error on last page transferred? Must
check now so the Head can restore the proper
file page num, sector count, etc and retry without
remembering the initial parameters for each
transfer in each IOCB. This instruction is at
[2,4] in case the Timing for the U+0 doesn’t
work.}
{save the low 16 bits of the page number and fetch the Head/Sector word so it can be updated}
SaveLoLabl:ULabPgLo ← RCnt, RCnt ← 0, DISP4[NoMemErr,7],c1; {save l.s. 16 bits of page num, decide about
memory error. }
MemErr:RCnt ← CMemError, GOTO[DataAndMemErrBr],c2, at[0F,10,NoMemErr]; {set mem error status and
kill time until we can quit}
NoMemErr:RCnt ← UHeadSector, NegBr,c2, at[7,10,MemErr]; {get the old Head/Sector
word, Is the "no increment" flag on?}
RCnt ← RCnt+1, BRANCH[IncSect, NoIncSect],c3; {increment the sector number and decide
whether to store it or not.}
{update the new Head/Sector word in the Header Template}
IncSect:MAR ← [RHRAdr, RAdr], RAdr ← UMaxSectTst+0, SuppressTimingWarning, c1; {start store of Head/Sector wordand
increment the sector number}
MDR ← UHeadSector ← RCnt, RAdr ← RAdr+RCnt+1, PgCarryBr, GOTO[UpDtHdSctC3], c2; {update new Head/
Sector word, assume its ok and check to
see if the last sector was just stored. Note
the "+1" exists so the U register may be
written. Because of it, the number in
UMaxSectTst is one less than the desired
value.}
NoIncSect:Noop,c1, at[1,2,IncSect]; {leave the Head/Sector
number just read unchanged.}
Noop,c2; {Note we may only read Headers in runs of
one page, soalways take the SectorTest
branch below.}
UpDtHdSctC3: RAdr ← UHeadSectorAddr, BRANCH[SectorTest, NewHead],c3, at[2,4]; {prepare to update Head # in Header if
necessary and and decide whether that was
the last sector in the track. This instruction is
at [2,4] in case the Timing for the U+0
doesn’t work.}
{The run of pages has just crossed a track boundry, so the Head number must be incremented everywhere it exists. The most obvious place is in the Header word just stored. We create the proper HeadSector word by setting the Sector number to zero and incrementing the Head number, then store the correct head and sector numbers. Then we go through both commands that contains a Head number. The Freeze command is done first, so the disk has the longest time to change heads. After this command is changed, is sent to the hardware.}
NewHead:RCnt ← RCnt and ~0FF,c1, at[1,2,SectorTest]; {set sector number to 0}
RCnt ← RCnt + 0FF + 1,c2; {increment the head number}
UHeadSector ← RCnt,c3; {update the U reg version of the word also}
MAR ← [RHRAdr, RAdr+0],c1; {start update of head# in Template}
MDR ← RCnt, RAdr ← CHeadIncr,c2; {store new head#, sector 0. load byte-
swapped version of field used to incr head #
in the commands}
RAdr ← RAdr LRot8,c3; {set up const to increment head # in control
words}
{increment the head number in both control words}
RCnt ← UFreezeCmd,c1; {start with the Freeze command}
RCnt ← RCnt+RAdr,c2; {incr head #, Can’t send command here
because of timing problems}
UFreezeCmd ← RCnt,c3; {save new version of Freeze control word}
RCnt ← UFindSectMkCmd,c1; {update control wd for header field}
RCnt ← RCnt+RAdr, KCtl ← UFreezeCmd,c2; {incr Head # and sent new command so
disk can switch heads}
UFindSectMkCmd ← RCnt,c3; {save new Header field control wd}
{One sector has been successfully processed. Was that the last sector? Decrement the sector count and find out.}
SectorTest:RCnt ← USectorCount,c1, at[0,2,NewHead]; {get old # remaining sects}
RCnt ← RCnt-1, ZeroBr,c2; {get new # sectors left, are we done?}
USectorCount ← RCnt, BRANCH[NewSector, DoneQuit],c3; {update count, quit if all done}
{This is the exit taken if all pages were transferred sucessfully.}
DoneQuit:Noop,c1, at[1,2,NewSector]; {set up no error br and quit}
{finish up the run of pages command. Coming into the statement, each statement did a test of the error status. The command will skip the next two bytes if it complleted error free, otherwise it continues directly on.}
FinXferRun:UStatus ← RCnt, ZeroBr, GOTO[IncBr],c2; {goto restore IOCB ptr and get next command
or the one after that}
{--------------------------- End of main body of Transfer Run of Pages Command -------------------------}
{ Rest of Code in DiskDlionB.mc }