{File name: DiskDlion.dfn
 Description: Definitions file for Dandelion Disk Controller microcode, Version 3.0
 Author: D. Davies
 Created: October 30, 1979, v1.0
 Last Edited: November 19, 1979  4:19 PM
 Last Edited: November 19, 1979  9:09 PM
 Last Edited: November 21, 1979  2:55 PM, "minimum code" version completed, end v2.0.
 Last Edited: January 4, 1980  5:15 PM, create "transfer run of pages command"
 Last Edited: January 24, 1980  9:54 PM, complies with standard Dandelion regsiter assignment 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: April 7, 1980  12:14 PM, modified to work with combination SA1000/SA4000 controller.  This version
	renamed back to DiskDlion.  Code also improved to reduce overhead during disk transfers.
 Last Edited: April 20, 1980  2:17 PM, address mark changed to eliminate possible illegal pattern when tag bit is a 1.
 Last Edited: June 18, 1980  9:13 PM, Change for Pilot Head (SA4000HeadDLion).
 Last Edited: June 27, 1980  11:05 PM, Change CHeadMsk to mask off FirmwareBusy so it may be set properly.
 Last Edited: June 30, 1980  10:01 PM, replace const used to mask hi label word with one used to detect read operations.
 Last Edited: July 28, 1980  12:30 PM, try 10  word labels.
 Last Edited: July 30, 1980  9:49 AM, do 12 word labels.
 Last Edited: July 31, 1980  4:13 PM, delete "Set Word with Constant" and "Mem to Mem Move" commands, update U reg defs.
 Last Edited: September 19, 1980  12:05 PM, delete Test Status command to save space.
 Last Edited: Amy Fasnacht: August 3, 1982  1:50 PM, Move IOPage constants to Dandelion.df and rename.
 Last edited by Dennis Grundler: 20-Sep-83 20:45:13. Renamed to .dfn.

-----------------------------------------------------------------------------------------
Overview:  This is the definitions module for the microcode for the Dandelion rigid disk controller.
    After Version 1.0 was completed, its size (>170 words) dictated a new microcode philosophv.  Much of the microcode was dedicated to encoding and decoding IOCB information, detecting error conditions and taking appropriate action.  Since so much effort is contained here and diagnosic routines will require operations distinct from the norm, the specification of control flow is properly put in the IOCB so it may be set by a Mesa program.  This takes the burden off the microcode while adding flexibility.  The new microcode gives the disk controller the appearance of a simple processor, with abilities to incroment, test and set words, execute simple conditional and unconditional branches (within the "program" contained in an IOCB), read, write and verify fields of a sector, send control words directly to the hardware and finish an IOCB.  IN IBM parlance, the disk controller looks like a simple channel and the IOCB its program.  While at first glance this seems much more complex than a brute force implementation of the disk functions, it is in fact more compact.  Instead of independent testing code written for each situation, one routine is called when specified by the IOCB.  This holds true for counting and other operations as well.  As an example, no separate microcode is needed for the Seek operation.  A Seek IOCB is composed of two commands to send control words, an increment and skip if zero command, an unconditional jump to loop and a FinishIOCB command.  All of these operations are also used in a Transfer IOCB.  With the additional flexibility now present, new diagnosic code may use the existing microcode for future tests.  The lack of independent diagnostic and initialization code is in itself a great boon.
    As of November 28, the concept has been modified slightly.  the overhead of processing commands for each field that process the field, test the status, update the field number proved to be to great.  That is, when the time taken to do the overhead was added to the time needed to process the data in a sector, the time needed to process 28 such sectors was greater than the rotation time of the disk.  In fact, there are only 33 spare words per sector in which to perform overhead tasks.  As a result, the "process a field" command was turned into a subroutine of the "process a run of pages command" and a command was added to load the parameters needed by the run or pages command.
    The code was again modified in March, 1980.  This was done to make the code work for the new controller.  This new board may be plugged into either an SA1000 drive or an SA4000 drive.  One signal on the connector is grounded when connected to the SA4000 drive and pulled up when connected to the SA1000 drive.  With this information, the controller may configure itself.  The major differences between the drives are:
  1. The SA4000 drive has 28 sectors per track, the SA1000 has 16.
  2. The SA4000 drive has hard sector marks available on a status bit, the address marks at the beginning of each SA1000 field each contain a flag.  The flag is available on the SectorFound status bit and is reset when a Header field has been found.
    With this release, the code overhead has been reduced.  The Header, Label and Data command words are ORed with the Freeze command before being sent.  This eliminates the need for maintaining the HeadSelected field in each one.  The file page number in the Label field and the Head/Sector word in the Header field are duplicated in U registers.  This eliminates the need to read them each time they are updated.  This last trick saves two word times, giving more margin between the last word of one sector and the sector mark of the next.
    On September 18, the initialization code and the Test Status instruction were deleted from the code.  This was done in an effort to reduce the code size.
    The microcode process exists in one of two states, dormant or executing.  It is initialized to the dormant state.  To request service, a Mesa program writes a control word with the FirmwareEnable bit set to the controller hardware.  The hardware will generate a service request (assuming the wakeup condition was correctly set to "FirmwareEnable"), arousing the microcode.  The CSB address is generated and used to fetch the address of the first IOCB in a chain of IOCBs.  When the last IOCB has been processed or an error has been encountered, the microcode re-enters the dormant state.  The disk microcode may await events signalled by service requests while in the executing state, but upon awakening, it continues with a given IOCB.  The microcode enters the dormant state between chains of IOCBs.
    While in the executing state, the microcode maintains a "program counter" in RAdr.  It steps though the IOCB, decoding and executing commands.  The decoding process is trivial, when fetched from memory, the lower four bits of the command word are used to perform an XDisp  and the command word is discarded.  Commands may be followed by operand words.  The commands available are:

1. Increment main memory loc and skip if zero.
    Address of memory loc follows.  This is used for maintain counts in the seek operation, while searching for a sector on a track and possibly while processing runs of pages in a single IOCB.  The IOCB pointer is incremented by two so a jump or other two word command must follow this one.

2. Jump
    This unconditional branch is followed by a word containing the address of the new execution location in the IOCB.  RAdr is loaded with this word and processing continues at the new location.

3. Send Control word
    The word to be sent to the hardware follows.  This word can control not only the step and direction bits used to move the read head, but also the wakeup conditions.  This command is not generally used to start a data transfer operation, that is done using the Read, Write or verify commands.  This command has the side effect that it clears the UField register so that its bits do not confuse the final status of a Seek command.  The assumption is that one will never send a control word between the time the final run of pages is finished and the FinishIOCB command is executed.  Without this feature, spurious bits in the UField register are inserted into arbitrary spots in the final status.

4. Load Transfer Parameters
   One method of reducing the overhead needed in transferring a run of pages is to ensure all the necessary parameters are in the U registers where they may be easily reached.  This command is followed by the address of these parameters which are stored at the beginning of the IOCB.  The parameters include the sector count, the commands, field lengths buffers addresses and status masks for each field of the sectors being transferred and a couple of miscellaneous counts and control words.  Note the Header, Label and Data command words contain only the bits that are particular tho their fields.  Each of these comands is ORed with the Freeze command before being sent to the controller.  This reduces the amount of overhead needed to set up and maintain common parameters (HeadSelected, for example).  Also set now is the number of sectors/track in UMaxSectTst.  This is 256-16 for the SA1000 drive and 256-28 for the SA4000 drive.  The address of the file page number in the Label template field is put in ULabPgAddr so the page number is easily updated.  The page number itself is duplicated in ULabPgLo and ULabPgMsk and ULabPgIncr.  ULabPgLo contains the least significant 16 bits of the page number plus one.  Doing the increment before the first update turns out to be convenient.  These two last registers contain two versions of the high page byte and the flags.  ULabPgMsk contains the original most significant byte with all but the boot flag masked.  ULabPgIncr contains the same flags but the hi byte in incremented.  The microcode need only choose the proper one when it is necessary to update the hi word of the file page number.  UIncrDataPtr contains a 1 if the data page is to be incremented at all, a zero if the same page is to be used for all sectors in a run.

5. Transfer Run of Pages
     The command is used to read, write or verify a the Header, Label and Data fields in a run of pages on the disk.  The same operation is done on each page in the run.  The arguments for this command are loaded using the Load Transfer Parameters command.  The run of pages may cross a track boundry (change read heads), but may not cross a cylinder boundry.  No check is made for this condition, it is assumed the count of sectors to transfer reaches zero before the condition is encountered.
     Each field in a sector has four parts: a PLL synchronization pattern (all zeros) for the disk's data separator, a sync word (all ones) or an address mark (01010000101000T1) used to find the word boundry, the actual data in the field and a CRC residue word.  For read and write operations, the word count given equals the number of words in the data section of the field.  For a verify operation, the word count equals the number of words in the data section minus one.
     The field control word is the word which when ORed with the Freeze command and sent to the hardware causes the CRC residue word to be processed.  From it is derived the control word used to process the data section of the field.  The least significant two bits of the word are WakeupControl.1 and WriteEnable.  They indicate the operation to be performed on the field.  00 => Read, 10 => verify and 11 => write.  If the operation is a write, the synchronization pattern is written, followed by the sync word or address mark, the data and the CRC residue word.  All words including the synchronization pattern are supplied by the microcode.  If the controller is connected to an SA1000 drive, the tag bit  (bit 14) of the address mark is set if a Label or Data field is being written.  If the operation is a read, the hardware sends a service request when the first data word has been found.  It is read into the first location of the data buffer.  The remaining words fill the buffer and the CRC word is checked before completion.  It is necessary to prime the controller with the first word to be verified when a verify operation is to be performed.  After this is done, the hardware is told to find the sync word.  On the first wakeup after this, the the microcode supplies the second word in the field.  In response the succeeding service requests, the microcode sends as many data words as are specified in the word count, waits two clicks then sends the freeze command.

6. Finish IOCB
     The address of the Next IOCB link, the stop control word, the Mesa Interrupt mask and address of the IOCB status word follow this command.  Bits 0 and 1 of the IOCB status word give the number of the present field (Header=1, Label=2, Data=3), zeros follow in bits 2 and 3, KStatus in bits 4 through 7 and UStatus in bits 9 through 15.  The setting of bit 8 (FirmwareEnable) is described below.  In this way, the final IOCB status contains the number of the last field processed, the final drive status is in bits 4 through 7, future FirmwareEnable in bit 8 and the appropriately masked status bits are in bits 9 through 15.  The masked status bits are used so spurious errors, such as a verify error during a write operation, will not be reported.  If the bits 9 through 15 are all zero, the operation was successful, if not, not.  If there were any errors in the final status, there was a memory error during the IOCB or the Next IOCB link was null (indicating this is the last IOCB in the chain), FirmwareBusy is reset, otherwise it is set.  This is used by the Device Head to decide whether the microcode will stop.  If a new IOCB has just been added to the chain, the Device Head will know whether or not it was seen by the microcode, hence whether it should be restarted.  After posting the final IOCB status, an interrupt is sent to a Mesa process using the Mesa interrupt mask.  If FirmwareBusy was set, the NextIOCB pointer is used to start executing commands from the new IOCB.  If FirmwareBusy was not set, The microcode turns off the FirmwareEnable bit in the hardware as it jumps to the dormant state code to await a new task.

7. Initialize Registers
      If a Transfer command was not executed in the IOCB, the UField and UStatus registers must be initialized.  They are used to create the final status and if not properly initialized may cause spurious error indicators.  This command initializes UStatus to zero and UField to CIOCBDone.  Providing this value ensures a valid, but non-zero final status.

Command Summary

   Command		opcode	arguments
Increment, skip if zero	0000	<phys addr of loc to be incremented>
Jump		0002	<phys addr of desitination in IOCB>
Send Control word	8000	<control word to be sent>
Load Transfer Parameters	0005	0 (address of base of 16 word aligned parameter block in this IOCB.  Block contains:
			0  Sector Count
			1  FailCount (>= # sectors per track)
			2  Control word for Header field
			3  Word count for header field
			4  Address of Header field in IOCB
			5  Status mask for abort-after-header conditions
			6  Status mask for try-another-header conditions
			7  Control word for Label field
			8  Word count for Label field
			9  Address of Label field in IOCB
			10 Status mask for abort-after-Label conditions
			11 Control word for Data field
			12 Word count for Data field
			13 Virtual page number of buffer for Data field, bit 0 set => use different page for each
			    sector on the disk.  bit 0 reset => use same memory page for each sector.
			14 Status mask for abort-after-Data conditions
			15 Control word used to halt hardware after each field is processed
			16 Control word used by have hardware find the next sector mark
Transfer Run of Pages	0800
Finish up IOCB	0006	<lo 16 bits of next IOCB's virt addr (hi bits are all 0)> <interrupt bits used to awaken a
			mesa process> <control word used to stop the hardware at the end of the IOCB>
			<address of IOCB status word in IOCB>
Initialize Registers	0007	<dummy parameter>

Control word layout
    Bit	function

    0	HeadSelect.16
    1	HeadSelect.8
    2	HeadSelect.4
    3	HeadSelect.2

    4	HeadSelect.1
    5	DriveSelect
    6	FaultClear
    7	ReduceIW

    8	Step
    9	DirectionIn
   10	FirmwareEnable
   11	TransferEnable

   12	WriteCRC
   13	WakeupControl.0
   14	WakeupControl.1
   15	WriteEnable

Wakeup conditions
Condtion		KCtl.[12..15]
FirmwareEnable	0
SeekComplete	2
IndexFound		4
SectorFound		6
Read Operation	0  (TransferEnable=1)
Verify Operation	2  (TransferEnable=1)
Write Operation	B  (TransferEnable=1)

Raw Status word layout after being read.   Note the HeadSelect bits are inverted
   Bit	Meaning

    0	HeadSelect.16'
    1	HeadSelect.8'
    2	HeadSelect.4'
    3	HeadSelect.2'

    4	HeadSelect.1'
    5	SeekComplete
    6	Track00
    7	FirmwareBusy

    8	IndexFound
    9	If SA4000 => SectorFound, If SA1000 => HeaderTag
   10	SA1000/SA4000'
   11	DriveNotReady

   12	WriteFault
   13	Overrun
   14	CRCError
   15	VerifyError

IOCB Status word layout.  This is a masked version of what's above.
  Bit	Meaning

    0	LastField.0
    1	LastField.1
    2	<not used>
    3	<not used>

    4	<not used>
    5	SeekComplete
    6	Track00
    7	FirmwareBusy

    8	IndexFound
    9	If SA4000 => SectorFound, If SA1000 => HeaderTag
   10	MemoryFault  (memory error occured sometime during last IOCB)
   11	DriveNotReady

   12	WriteFault
   13	Overrun
   14	CRCError
   15	VerifyError



Register Usage:

R registers:
RAdr - generally holds lo 16 bits of an address.  Used as IOCB program counter when fetching commands and as a Counter register when transferring data.
RCnt - Used to hold word addresses during data transfers and as a scratch register during execution of other commands.

U Registers:

URAdrBlk -                   AltUAddr pseudo-reg specifying all regs under RAdr.
URCntBlk -                   AltUAddr pseudo-reg specifying all regs under RCnt.

U0C00 -                         holds the constant 0C00 used to clear the Disk Task's memory error flag
USaveRAdr -                 used to hold IOCB program counter when RAdr is needed during execution of a command.
UInterrupt -                   holds bits specifying which mesa process should be interrupt or awakened
UStatus-                        holds results of test status command and staus tests performed during the Transfer command
The following U registers hold parameters for the Transfer Run of Pages command:
USectorCount -            number of sectors to transfer.
UFailCount -	               number of sectors to be examined on one track before deciding the desired sector isn't there.
UHeaderCmd-              control word used to process the next header field
UHeaderLen-               word count for header field (a function of the operation performed)
UHeaderAddr-             physical address of header buffer in memory.  must be in the IOCB
UHeaderQuitMsk   -    status mask used to decide whether a fatal error has occurred while processing the Header field of a sector
UHeaderLoopMsk -    status mask used to decide whether the processor should abort oprations on the present sector and examine the next sector instead.  Used to find the proper sector.
ULabelCmd -                control word used to process the next label field
ULabelLen -                  word count for label field (function of operation performed)
ULabelAddr -                physical addr of label buffer in memory.  Must be in IOCB.
ULabelQuitMsk -          status mask used to decide whether a fatal error has occurred while processing the Label field of a sector
UDataCmd -                  control word used to proces the next data field
UDataLen -                    word count for the data field (function of the operation performed)
UDataPgNum -              virtual page number of the next data buffer.  If bit 0=1, increment page num for each sector in run.
UDataQuitMsk -           status mask used to decide whether a fatal error has occurred while processing the Data field of a sector
UFreezeCmd -              control word used to stop hardware at the end of each field.
UFindSectMkCmd -     control word used to find the sector mark at the beginning of each sector.
The following U registers hold parameters used to speed up the Transfer Run of Pages command.  They are loaded by the Load parameters command
UHeaderNotOkMsk -    Mask used to test status after Header operation.  If passes this, continue to label, if not, either quit or loop.  This holds the OR of UHeaderQuitMsk and UHeaderLoopMsk.
UHeadSectorAddr -    Address of word containing numbers of current read head and Sector in the Header Template
ULabPgAddr -              Address of word containing low 16 bits of file page number in Label field template.  IF 0, don't incr file
	              page number in label for each sector.
ULabPgLo -	              dupicate of low 16 bits of file page number.  This version is pre-incremented for easy storage.
ULabPgMsk -              duplicate of hi byte of file page number and file flags.  The flags masked as is appropriate for the 2nd and later pages of a file.
UHeadSector -          holds a duplicate of the Head/Sector word of the Header template.
The following U register are used to pass arguments to the Transfer (a single field) subroutine:
UWdCount -               word count for the field being transferred.
UXferCmd -                control word for the field being transferred.
UWaitCmd -               control word used to processing the CRC word of the current field.
The following U registers are used by the FinishIOCB command.
UNextIOCBLnk -          holds lo 16 bits of address of next IOCB int he chain (high 6 bits guaranteed to be 0s)
UInterruptMsk -            holds the interrupt flags sued to wake upt he Mesa Device Head after finishing an IOCB
UField -	               holds # or field in sector being processed.  40 => Header, 80=> Label, C0=> Data.
UStatusMsk -                holds constant used to mask raw status before inserting Last Field, masked status (from UStatus) and
	               FirmwareBusy.

Assumptions:

1. Exactly one word may be transferred between disk and main memeory per click.  The disk controller has one 16 bit buffer for data read and one for data to be written or verified.
2. The Device Head keeps track of the read head's position.
3. The disk format and CSB, IOCB and status and control word layouts are given in the Face and Head programs, SA4000Face.mesa nd SA4000HeadDLion.mesa.
4. Pages needed for a transfer are present and locked in physical memory.
5. All IOCBs reside in the first 64k of virtual memory, no assumptions are made about their locations in physical memory.
6. IOCBs and data buffers do not cross page boundries (the microcode never checks for page boundries).
}

{---------------------  Register and Constant Initialization  -----------}
{  Constants }
{constants which define the address mark for the SA1000}
Set[CAddrMkHi, 0A1];
Set[CAddrMkLo, 041];
{offset of Head/Sector word in Header Template}
Set[CHeadSectOffset, 1];
{tag bit inserted into the address marks of Label and Data fields}
Set[CLabDatTag, 2];
{offset of file page number in Label template}
Set[CLabPgOffset, 5];
{Mask used to turn off HeadSelected field}
Set[CHeadMsk, 0F9];
{Mask used to detect Read operations.}
Set[CReadMask, 0F];
{number used to increment the read head field of a command.  The constant is byte-swapped after being loaded}
Set[CHeadIncr, 08];
{Mask used to turn off WriteCRC bit in a control word}
Set[CWrCRC, 8];
{constant used to clear memory errors caused by the disk process}
Set[CClearMemError, 0C];
{constant used to signal that a memory error has occured- put into the final device status}
Set[CMemError, 20];
{constant used to indicate IOCB has been completed.  It is in the final status posted}
Set[CIOCBDone, 8];
{constants used to set the field number in UField}
Set[CHeaderField, 40];
Set[CLabelField, 80];
Set[CDataField, 0C0];
{mask used to turn off irrelevant error flags in raw status for the SA1000 drive (includes Header Tag)}
Set[CSA1ErrMsk, 7F];
{mask used to turn off irrelevant error flags in raw status for the SA4000 drive (does not include Header Tag)}
Set[CSA4ErrMsk, 3F];
{256 - number of sectors in a track = 256-28-1 = 227 for the SA4000.  Used to cause PgCarryBr  <=> sector number+UMaxSectTst+1 >= 256.  The "+1" comes from the fact that a U register is being stored in the same instruction as the test is being done.}
Set[CSA4MaxSectTst, 227'D];
{256 - number of sectors in a track = 256-16-1 = 239 for the SA1000.  Used to cause PgCarryBr  <=> sector number+UMaxSectTst+1 >= 256.  The "+1" comes from the fact that a U register is being stored in the same instruction as the test is being done.}
Set[CSA1MaxSectTst, 239'D];

{R, RH and U register assignments given in Dandelion.df.  Link regsiter 4 is assigned to the disk process}