1
0
mirror of https://github.com/livingcomputermuseum/Darkstar.git synced 2026-03-02 10:06:45 +00:00
Files
livingcomputermuseum.Darkstar/D/IOP/Source/BootSubs.asm,v
2023-09-27 16:17:41 -07:00

39 lines
53 KiB
Plaintext
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
head 1.1;
branch 1.1.1;
access ;
symbols start:1.1.1.1 Xerox:1.1.1;
locks ; strict;
comment @;; @;
1.1
date 2001.08.12.22.22.19; author freier; state Exp;
branches 1.1.1.1;
next ;
1.1.1.1
date 2001.08.12.22.22.19; author freier; state Exp;
branches ;
next ;
desc
@@
1.1
log
@Initial revision
@
text
@{
----------- Dandelion Processor Program - I/O Processor -----------
DESCRIPTION: Boot Program: IOP subroutines.
Last modification by Roy RXO : January 28, 1982 4:28 PM
File: BootSubs.asm
Stored: [Iris]<Workstation>Boot30>BootEPromRAM.dm
Written by Roy RXO .
Dennis DEG : 2-Sep-84 15:18:30, Add copyright notice.
}
{ Copyright (C) 1980, 1981, 1982 by Xerox Corporation. All rights reserved.}
; Modification History:
; - Created (November 13, 1980 9:37 AM)
; - Added TPC0 fix (November 20, 1980 4:37 PM)
; - Changed PhaseToMP, ErrorReport (December 22, 1980)
; - General CSImage size (December 22, 1980 5:14 PM)
; - Sector runs to end of track only (January 5, 1981 10:23 AM)
; - BootInit blanks MPanel (June 17, 1981 5:52 PM)
; - Changes for 2D, 2S boot floppies (June 17, 1981 6:12 PM)
; - Changes to CheckAltBootDevice (November 24, 1981 2:05 PM)
; - Changes to CheckAltBootDevice (December 10, 1981 6:12 PM)
; - UReg block uses GenericBootDevice (December 11, 1981 3:19 PM)
; - Ethernet host address reads 3 words, preset RS366 control (December 14, 1981 5:32 PM)
; - Added version number OR in BootDevice (January 13, 1982 4:22 PM)
; - Set interrupt state moved to BootInit (January 13, 1982 4:22 PM)
; - Add FloppyInitE entrypoint, check for no floppy (January 14, 1982 2:00 PM)
; - Took out di/ei in ReadSector, added blink-only to ErrorReport (January 22, 1982 4:48 PM)
; - Simplified double count check, other simplification (January 28, 1982 4:03 PM)
; DEFNITIONS:
get "SysDefs"
get "BootDefs"
; EXPORTS:
EXP BootInit,CheckAltBootDevice
EXP StartNextRead,GetNextWord
EXP InitCSTPCImage,TransferCSImage,TransferTPCImage
EXP WriteCS
EXP SetupUregisters,InterpretUBlock,InformCPBootDevice,ReadMainMem
EXP CheckCPStopped,StartCPKernel,StartCP
EXP InitCPCmd,ReadCPWord,WriteCPword,ByteToWord
EXP FloppyInit,FloppyInitE,DoSeekCmd
EXP PhaseToMP,PutMP,ClearMPanel,IncrMP,DeltaMP,Delay,ErrorReport
EXP SectorRunSize,MaxBufCnt,FloppyBuffer
EXP IntMask,ORVersionNo ; For Burdock command file debugging
; IMPORTS:
IMP StartIOPBoot ; From StartIOPBootYYY - Start of IOP boot file (Phase 0)
IMP StartNextPhase ; From BootMain
{ This code contains the subroutines which are used by the Boot code in BootMain.asm.
Notes:
Boot file in Main memory should not cross 64K boundary
Maximum of 16 loadU blocks in Phase 0 boot file.
}
;
; SUBROUTINES.
; Subroutine: BootInit.
; Initialize the IOP, and the various data structures.
; Note: BootType is initialized at the start of execution.
BootInit:
; Ensure that IOPWait = SwTAddr = 1 (should be after hard boot).
mvi a,CPWaitSwT
out CPControl
mvi a,DisableFDC ; Disable floppy controller, Enable Waits
out FDCState
; Set hardware interrupt mask. Interrupts are disabled at this point.
SetIntMask:
mvi a,BootIntrState ; Enable interrupts
sim
IntMask equ SetIntMask+1 ; Mask byte for modification by Burdock command file.
; Initialize and disable the Dma controller.
in DmaStatus ; Clear any flags
xra a
out DmaMode
; Set miscellaneous registers.
cma ; Set all Misc clock bits high
out MiscClocks1
in CPIn ; Clear CPIn flags
mvi a,BlankMPanel ; Clear MiscControl1, blank MPanel
out MiscControl1
mvi a,CallReq+DigPr ; Set up RS366 control register
out RS366Reg
; Initialize Boot flags.
mvi a,BootMode+CPStopped ; BootMode, CPStopped True
sta BootFlags
; Initialize data structures.
lxi h,CSImage ; Initialize pointer to start of CSImage
shld CSImageStart
lxi h,CSImageSizeVal ; Initialize size of CSImage (in CSImageSize)
shld CSImageSize
xra a
sta Phase ; Phase _ 0
sta uBlockCnt ; No. of uBlocks _ 0
sta BootSource ; BootSource _ 0 (IOP memory)
sta DiagBoot ; DiagBoot _ 0
sta LastBlockFlags ; LastBlockFlags _ 0
cma
sta BootDevice ; Boot device _ undefined (-1)
lxi h,uBlockPtrArray ; Initialize uBlockPtr
shld uBlockPtr ; uBlockPtr _ uBlockPtrArray
lxi h,0
shld MPOffset ; MPOffset _ 0
; Initialize default pointer for IOP execution after end of a Phase other than Phase 0.
lxi h,StartNextPhase ; Pointer to IOP start address
shld StartIOPAddress
; Initialize IOP boot file pointer for Phase 0.
; This value is imported from the file: StartIOPBootRAM or StartIOPBootProm,
; depending on whether it is the RAM or Prom configuration.
lhld StartIOPBoot ; Start of Boot file in IOP memory
shld BootAddrIOP
; Initialize PCB's.
lxi h,StartCPBootFile ; Start of Boot file in CP memory
shld BootAddrCP
shld BootPCB+CPAddr1 ; High part of address = 0
lxi h,0
shld MemPCB+CPAddr1 ; High part of address = 0
lxi h,1
shld MemPCB+CPCnt ; Count = 1
ret
; Subroutine: ReadAltBoot (in PreBoot*.asm).
; Subroutine: CheckAltBootDevice.
; Check the BootType. If non-rigid disk booting is specified, set BootDevice appropriately.
; If rigid disk booting, set BootDevice according to what the value of CPDevice is.
; Note that the range of BootType was checked in ReadAltBoot.
; AltBoot codes:
; 0 - diagnostic rigid disk booting (default if no AltBoot)
; 1 - rigid disk booting
; 2 - floppy disk booting
; 3 - ethernet booting
; 4 - diagnostic ethernet booting
; 5 - diagnostic floppy disk booting
; 6 - alternante ether booting
; 7 - diagnostic Trident1 booting
; 8 - diagnostic Trident2 booting
; 9 - diagnostic Trident3 booting
; Table of BootDevice values. Indexed by BootType.
; An entry is the BootDevice value or -1 if rigid disk booting.
BootDeviceTable:
db -1 ; 0: diagnostic rigid
db -1 ; 1: rigid
db BootFloppy ; 2: floppy
db BootEthernet ; 3: ethernet
db BootEthernet ; 4: diagnostic ethernet
db BootFloppy ; 5: diagnostic floppy
db BootAltEthernet ; 6: alternate ethernet
db -1 ; 7: diagnostic Trident1
db -1 ; 8: diagnostic Trident2
db -1 ; 9: diagnostic Trident3
; ENTRY point:
CheckAltBootDevice:
lda BootType
mov c,a ; Form table index in B,C
xra a
mov b,a
lxi h,BootDeviceTable ; Point to start of table
dad b ; H,L _ Start of Table + BootType
mov a,m ; Read value for BootDevice
ora a ; Check for -1
jm CheckRigidBoot ; m => -1, thus rigid booting
; Booting not from rigid.
NonRigidBoot:
SetGenericBootDevice:
sta GenericBootDevice
SetBootDevice:
sta BootDevice
ret
; Booting is to be from the rigid disk. In order to determine what the BootDevice value is,
; we have to look at both BootType and CPDevices.
; Check also if no disk or more than one disk is specified in CPDevice.
; The following table is used to determine BootDevice:
; CPDevice
; AltBoot 4000 1000 Trident
; -------------- ----------------------------------------------------------------------------
; 0 BootSA4000 BootSA1000 BootTrident0
; 1 BootSA4000 BootSA1000 BootTrident0
; 7 error error BootTrident1
; 8 error error BootTrident2
; 9 error error BootTrident3
; C has BootType.
CheckRigidBoot:
lda CPDevices ; Check for no rigid disk
ani DiskBootMask ; Mask disk bits
jz NoDiskFound ; z => No disk indicated
mov b,a ; B _ CPDevice (modified)
mov a,c ; A _ BootType
cpi AltFloppyBoot ; Check if less than AltFloppyBoot
jc AltBootRigid ; c => BootType < AltFloppyBoot
; The AltBoot specified Trident 1, 2, or 3. Check value in CPDevices.
AltBootTrident:
mov a,b ; Get CPDevices
cpi BootSA1000Mask ; Is it the SA1000?
jz InvalidBootType ; z => No SA1000 on system
cpi BootSA4000Mask ; Is it the SA4000?
jz InvalidBootType ; z => No SA4000 on system
cpi BootTridentMask ; Is it the Trident?
jnz MultiDisksFound ; nz => more than one disk bit set
; Set the Trident as BootDevice.
; ***Note: assumes that AltTrident1Boot (BootType)=BootTrident1 (BootDevice), etc.
SetTrident:
mvi a,BootTrident0 ; Set GenericBootDevice to Trident0
sta GenericBootDevice
mov a,c ; BootDevice _ BootType
jmp SetBootDevice
; Set the implemented rigid disk drive as the BootDevice.
AltBootRigid:
mov a,b ; Get CPDevices
cpi BootSA1000Mask ; Is it the SA1000?
jz SetSA1000 ; z => SA1000
cpi BootSA4000Mask ; Is it the SA4000?
jz SetSA4000 ; z => SA4000
cpi BootTridentMask ; Is it the Trident?
jz SetTrident0 ; z => Trident0
; Supposed to be a disk boot, but more than one disk bits were set.
MultiDisksFound:
mvi c,ErrorMultiDisksFound ; ERROR: Multi disk bits specified in Mem 0
jmp ErrorReport
; Supposed to be a disk boot, but no disk bits were set.
NoDiskFound:
mvi c,ErrorNoDiskFound ; ERROR: No disk bits specified in Mem 0
jmp ErrorReport
; AltBoot specifies Trident disk, but no Trident on system.
InvalidBootType:
mvi c,ErrorInvalidBootType ; ERROR: No Trident disk on system
jmp ErrorReport
; Set the SA1000 as the boot device.
SetSA1000:
mvi a,BootSA1000 ; Set the SA1000
jmp SetGenericBootDevice
; Set the SA1000 as the boot device.
SetSA4000:
mvi a,BootSA4000 ; Set the SA4000
jmp SetGenericBootDevice
; Set the Trident0 as the boot device.
SetTrident0:
mvi a,BootTrident0 ; Set the Trident0
jmp SetGenericBootDevice
; Subroutine: InitCSTPCImage.
; Initialize the CS image with the instruction:
; GOTO [K1Entry];
; where K1Entry = 0F8F.
; Initialize the TPC array with all slots empty, i.e. high bit of word = 1.
InitCSTPCImage:
; First do the TPC image.
mvi c,8 ; Counter for 8 words
mvi e,0 ; Initialize TPC slot to 8000H (empty)
mvi d,80H
lxi h,TPCBuffer ; Start of Buffer
InitTPCImageLoop:
mov m,e ; Store low byte
inx h
mov m,d ; Store high byte
inx h
dcr c ; More TPC's?
jnz InitTPCImageLoop ; nz => More to do
; Initialize the CSImage.
; Set up the instruction counter.
lhld CSImageSize ; Initialize count to value in CSImageSize
xchg ; D,E has count
lhld CSImageStart ; Initialize pointer to image
InitCSImageLoop:
mvi a,DefaultCS0 ; Byte 0
mov m,a
inx h
mvi a,DefaultCS1 ; Byte 1
mov m,a
inx h
mvi a,DefaultCS2 ; Byte 2
mov m,a
inx h
mvi a,DefaultCS3 ; Byte 3
mov m,a
inx h
mvi a,DefaultCS4 ; Byte 4
mov m,a
inx h
mvi a,DefaultCS5 ; Byte 5
mov m,a
inx h
dcx d ; End of loop?
mov a,e ; Check for count = 0
ora d ; Low OR high
jnz InitCSImageLoop ; nz => nonzero
ret
; Subroutines to read the next word from the boot file.
; The boot file can be in EProm or in main memory or on the floppy.
; If the boot file is in main memory then the CP transfer needs to be initialized.
; StartNextRead is used to initialize the transfer in this case.
; The location of the boot file is determined by the value of BootSource:
; 0: IOP memory
; 1: CP memory
; 2: Floppy streaming
; Subroutine: StartNextRead [H,L: word count].
; Start the next transfer from the boot file if in Main memory.
; BootSource=0 or 2 - do nothing
; BootSource=1 - initialize transfer through the CP port.
; On entry: H,L - number of words to be read in the next transaction.
StartNextRead:
lda BootSource ; Check boot source for main memory
cpi BootSourceCP
jz StartNextCP ; z => Boot file in main memory
cpi BootSourceFloppy
rz ; z => Boot file on Floppy, do nothing
cpi BootSourceIOP
rz ; z => IOP memory, do nothing
jmp UnimplBootSource ; ERROR: undefined boot source
; Boot file is in main memory. Initialize the CP transfer.
; Format of initialize:
; Command, low address, middle address, high address, low [count], high [count].
; Command is ReadCP memory.
; CP address in BootAddrCP (in BootPCB)
; CP count in CPCount.
StartNextCP:
shld BootPCB+CPCnt ; Save the count.
mvi a,CPReadCmd ; Read command
lxi h,BootPCB ; Point to the Boot PCB
jmp InitCPCmd ; First byte is command
; Jump to InitCPCmd subroutine and Return.
; Subroutine: GetNextWord [H,L: Pointer to IOP buffer].
; Get the next word from the boot file.
; The location of the boot file is determined by the value of BootSource:
; 0: IOP memory
; 1: CP memory
; 2: Floppy streaming
; IOP memory - Next boot file word pointer in BootAddrIOP.
; BootAddrIOP is incremented.
; CP memory - Next boot file word pointer in BootAddrCP.
; Read the word from the port, and increment the CP address.
; Note: assume no 64K crossing.
; Floppy Streaming - Next word in the floppy buffer, or take a disk fault.
; Check buffer count; if zero, start the next disk transfer, else return the next word in buffer.
; On entry: H,L - Address of the IOP buffer in which the word is to be placed.
; On exit: H,L - Address of the next word after the IOP buffer in which the word was placed.
GetNextWord:
lda BootSource ; Check boot source for main memory
cpi BootSourceIOP
jz GetNextIOP ; z => IOP memory
cpi BootSourceCP
jz GetNextCP ; z => CP memory
cpi BootSourceFloppy
jz GetNextFloppy ; z => Floppy
; Undefined boot source.
UnimplBootSource:
mvi c,ErrorUnimplBootSource ; ERROR: Unimplemented BootSource
jmp ErrorReport
; Next word from the CP port.
; It is assumed that the transaction has been initiated. Read the data from the port
; and stores the data into the IOP buffer.
; H,L Points to the IOP buffer to which the word should be transferred.
GetNextCP:
call ReadCPbyte ; Get byte from port (returned in A)
mov m,a ; Store in low byte
inx h ; Point to the high byte
call ReadCPbyte ; Get byte from port (returned in A)
mov m,a ; Store in high byte
inx h ; Point to the next byte
push h ; Save H,L temporarily
lhld BootAddrCP ; Increment the CP address
inx h
shld BootAddrCP ; Restore pointer in memory
pop h ; Restore H,L
ret
; Next word from the IOP memory.
; H,L Points to the IOP buffer to which the word should be transferred.
; BootAddrIOP points to the next location in the boot file.
GetNextIOP:
xchg ; D,E _ Pointer to IOP buffer
lhld BootAddrIOP ; H,L _ Pointer to boot file
mov a,m ; Low byte
stax d ; Store in destination
inx h ; Increment pointers
inx d
mov a,m ; High byte
stax d ; Store in destination
inx h ; Increment pointers
inx d
shld BootAddrIOP ; Update pointer in memory
xchg ; H,L _ Pointer to next word after IOP buffer
ret
; Next word from the Floppy buffer.
; H,L Points to the IOP buffer to which the word should be transferred.
; FloppyBufPtr points to the next location in the boot file (in floppy buffer).
GetNextFloppy:
xchg ; D,E _ pointer to buffer where word is to be returned
lhld FloppyBufCnt ; Check number of words left in floppy buffer
xra a
cmp l ; Check low part
jnz GetNextBuffer ; nz => buffer count is not zero, return word from buffer
; Low part is zero, check the high part.
cmp h ; Check high part
jnz GetNextBuffer ; nz => buffer count is not zero, return word from buffer
; The word count is zero. Fetch the next run of sectors from the disk.
GetNextDisk:
push d ; Save D,E (pointer to word buffer)
call ReadSectorRun
pop d ; Restore D,E (pointer to word buffer)
; The word is in the Floppy Buffer. D,E still points to the word buffer.
GetNextBuffer:
lhld FloppyBufPtr ; Store word in specified word buffer
D1:
inx d ; Point to high byte in buffer (Byte swap)
; nop ; (Non byte swap)
mov a,m ; Low byte
stax d ; Store in destination
inx h ; Increment pointers
D2:
dcx d ; Point to low byte in buffer (Byte swap)
; inx d ; Point to high byte in buffer (Non Byte swap)
mov a,m ; High byte
stax d ; Store in destination
inx h ; Increment pointers
inx d
D3:
inx d ; Point to next byte in buffer (Byte swap)
; nop ; (Non byte swap)
shld FloppyBufPtr ; Update pointer in memory
lhld FloppyBufCnt ; Decrement the Floppy word count
dcx h
shld FloppyBufCnt
xchg ; H,L _ Pointer to next word after word buffer
ret
; Subroutine: WriteCS.
; Write the microinstruction in CSBuffer in either control store or a control store image
; in IOP memory. If the CS address is greater than or equal to the value in CSIMageSize,
; then write the microinstruction in the control store. Otherwise write the
; microinstruction in the image.
; Thus, CSAddress - CSImageSize < 0 => In overlay area
; and, CSAddress - CSImageSize >= 0 => Out of overlay area
; The current CS address is in CSAddress.
; If the write is directly into control store, and BootMode=0,
; then first stop the CP, and then restart it.
WriteCS:
lxi d,CSAddress ; D,E points to CSAddress
lxi h,CSImageSize ; H,L points to CS address
ldax d ; Subtract low bytes
sub m ; We don't care about result, only the sign
inx d ; Point to high bytes
inx h
ldax d ; Subtract high bytes
sbb m
; Check sign of the difference:
jm DoCSImage ; m => CSAddress - CSImageSize < 0 => In overlay area
; Write the microinstruction directly into control store.
; Use TPC [CSTask] for CS addressing.
lda BootFlags ; Check whether BootMode=1
ani BootMode
cz StopCP ; z => Not BootMode, Stop the CP
lhld CSAddress ; H,L _ CS address
xchg ; move to D,E
mvi c,CSTask ; Use special TPC for control store addressing (to C)
call DoWriteTPC ; Write the TPC
; Now write the location.
lxi d,CSBuffer ; Point to the CS buffer
call DoWriteCS ; Write the CS location
lda BootFlags ; Check whether BootMode=1
ani BootMode
cz StartCP ; Restart the CP
ret
; Microinstruction is to be written into the image area in IOP memory.
; Write the instruction starting at IOP address: CSImage + 6*CSAddress.
DoCSImage:
lhld CSAddress ; H,L _ CSAddress
dad h ; H,L _ 2*CSAddress
mov e,l
mov d,h ; D,E _ 2*CSAddress
dad h ; H,L _ 4*CSAddress
dad d ; H,L _ 6*CSAddress
xchg ; D,E _ 6*CSAddress
lhld CSImageStart ; H,L _ Pointer to start of CSImage
dad d ; H,L _ CSImage + 6*CSAddress
; Now transfer the instruction into the image.
; H,L - pointer into the image.
; D,E - pointer to CSBuffer.
; C - byte counter
lxi d,CSBuffer ; D,E _ Ptr to CSBuffer
mvi c,6 ; 6 bytes
WriteCSImageLoop:
ldax d ; Get the byte from CSBuffer
mov m,a ; Store in the image
inx h ; Increment the pointers
inx d
dcr c ; Are we done?
jnz WriteCSImageLoop ; nz => more bytes
ret
; Subroutine: TransferCSImage.
; Transfer the control store image in IOP memory to the control store,
; starting at control store location 0.
; It is assumed that the CP is in the kernel.
; The control store image starts at location CSImage.
; Number of microinstructions in the image is given by CSImageSize.
; Current CS address in CSAddress.
; Counter for microinstructions in CSImageCnt.
; Pointer to next micronstructionin the image in CSImagePtr.
TransferCSImage:
lhld CSImageStart ; Initialize pointer to start of CSImage
shld CSImagePtr
lxi h,0 ; Initialize CS address to 0
shld CSAddress
lhld CSImageSize ; Initialize count to value in CSImageSize
shld CSImageCnt
TransferCSLoop:
; Use TPC [CSTask] for CS addressing.
lhld CSAddress ; H,L _ CS address
xchg ; move to D,E
mvi c,CSTask ; Use special TPC for control store addressing (to C)
call DoWriteTPC ; Write the TPC
; Now write the location.
lhld CSImagePtr ; Point to the next instruction in the image
xchg ; D,E _ H,L (Pointer to next instruction)
call DoWriteCS ; Write the CS location
xchg ; H,L _ D,E (Pointer to next instruction)
shld CSImagePtr ; Store back in memory
lhld CSAddress ; Increment CS address
inx h
shld CSAddress
lhld CSImageCnt ; Get count and check if zero
dcx h
shld CSImageCnt
mov a,l ; Check for count = 0
ora h ; Low OR high
jnz TransferCSLoop ; nz => nonzero
ret
; Subroutine: TransferTPCImage.
; Transfer the TPC entries that were filled during this boot phase into the TPC's.
; There is a table of 8 TPC entries, starting at TPCBuffer.
; The table is initialized with the high bit = 1, indicating empty.
; If an entry is inserted, then the high bit will be 0.
; Pointer to TPC buffer area is in TPCBufPtr.
; TPC address is TPCAddress.
; It is assumed that the CP is in the kernel.
; NOTE: If the BootType is floppy booting AND Phase =2 then add 1 to TPC 0,
; top start the emulator at Go instead of Germ.
; THis will be removed when the germ is changed to know about Floppy booting.
TransferTPCImage:
;*** Kludge : Fix TPC 0 if Phase=2 AND (BootType = AltFloppyBoot or AltDiagFloppyBoot).
lda Phase
cpi 2 ; Phase 2?
jnz ContTPCTransfer ; nz => not Phase 2
; Phase 2: Check BootType.
lda BootType
cpi AltFloppyBoot
jz FixTPC0 ; z => It is a floppy boot
cpi AltDiagFloppyBoot
jz FixTPC0 ; z => It is a floppy boot
;*** End Kludge.
ContTPCTransfer:
lxi h,TPCBuffer ; Initialize TPCBufPtr to start of TPCBuffer
shld TPCBufPtr
xra a ; Initialize TPCAddress to 0
sta TPCAddress
TransferTPCLoop:
lhld TPCBufPtr ; H,L _ Ptr to next TPC slot
mov e,m
inx h
mov d,m ; D,E _ TPC value
inx h ; H,L _ Ptr to next TPC slot
shld TPCBufPtr ; Store back
mov a,d ; Check high nibble of slow for empty or full
ora a
jm NextTPCSlot ; m => Slot is still empty
lda TPCAddress ; C _ TPC address
mov c,a
call DoWriteTPC ; Write the TPC
NextTPCSlot:
lda TPCAddress ; Increment TPC address and check for done
inr a
sta TPCAddress
cpi 8 ; Is TPC address = 8 (done)?
jnz TransferTPCLoop ; nz => Still more to do
ret
; Increment TPC0.
FixTPC0:
lhld TPCBuffer+0
inx h
shld TPCBuffer+0
jmp ContTPCTransfer
; Subroutine: SetupUregisters.
; Interpret the U register blocks that were found in the Boot file.
; Pointers to the 2nd word in each block were saved during phase 0 in uBlockPtrArray.
; uBlockCnt contains the number of blocks.
SetupUregisters:
lxi h,uBlockPtrArray ; Point to start of array
shld uBlockPtr ; Store in pointer
lda uBlockCnt
ora a ; Set flags
jmp CheckUCnt
SetupULoop:
call InterpretUBlock
lda uBlockCnt
dcr a
sta uBlockCnt
CheckUCnt:
jnz SetupULoop
ret
; Subroutine: InterpretUBlock.
; Interpret a particular U block.
; Note: This subroutine assumes Phase 0 activity, i.e. Boot file in IOP memory.
InterpretUBlock:
lhld uBlockPtr ; Get pointer to uBlock Ptr
mov e,m ; Get the pointer
inx h
mov d,m ; D,E _ Pointer to uBlock
inx h
shld uBlockPtr ; Store back pointer to next pointer in array
xchg ; H,L _ pointer to uBlock in IOP memory
shld BootAddrIOP ; Fix boot file pointer
; Note: No StartNextRead called, since it is assumed to be Phase 0.
lxi h,Header ; Get next word into Header
call GetNextWord
lda Header
ani uBootDeviceMask ; Mask Boot device
jz DoSetUBlock ; z => an unconditional u block
; The U block was not unconditional. Check if it matches the GenericBootDevice
rrc ; Right align the BootDevice
rrc
rrc
rrc
lxi h,GenericBootDevice ; Compare with GenericBootDevice
cmp m
jz DoSetUBlock ; z => BootDevice matched
; Not appropriate boot device.
ret
; Interpret the U block.
; First get the 16 words of U regsiter values.
; Format to CP is: command, uBlock number, 16 words of U register value.
DoSetUBlock:
mvi c,16 ; Set up a counter in C
lxi h,uBlockBuffer ; Point to buffer area
GetUValLoop:
call GetNextWord ; Store in buffer (H,L updated)
dcr c ; Done?
jnz GetUValLoop ; nz => Not done yet
; We are now ready for the CP command.
mvi a,CPLoadUCmd ; First byte is command
call WriteCPbyte
lda Header ; Send the block number
ani uBlockMask
call WriteCPbyte
; Now send the 16 words of data.
mvi c,16 ; Set up a counter in C
lxi h,uBlockBuffer ; Point to buffer area
SetUValLoop:
mov e,m ; Low part
inx h
mov d,m ; High part
inx h
call WriteCPword ; Send to port
dcr c ; Done?
jnz SetUValLoop ; nz => Not done yet
ret
; Subroutine: InformCPBootDevice.
; Inform the CP that the U registers were loaded.
; Also provide the host address, value of DiagBoot, and BootDevice.
; Format of command: command, host address (6 bytes), DiagBoot, EPromVersion/BootDevice.
InformCPBootDevice:
mvi a,CPSetBootCmd ; Command
call WriteCPbyte ; First byte is command
; Send the host address:
lxi d,8000H+HostAddr ; Point to the HAddr prom (high word)
call SendHostWord ; Send high host address word
call SendHostWord ; Middle word
call SendHostWord ; Low word
; Inform CP of DiagBoot.
lda DiagBoot ; Send DiagBoot to CP as index
call WriteCPbyte
; Inform CP of EPromVersion/BootDevice.
lda BootDevice
lxi h,EPromVersionLoc ; Point to shifted Version number
ORVersionNo: {Change to "ana m" for debugging with version 2.4 EProms}
ora m ; Merge with BootDevice
jmp WriteCPbyte
; Jump to WriteCPbyte subroutine and Return.
; Subroutine: SendHostWord.
; Read a host address word from the host address prom and transmit to CP port.
; The prom has 12 nibbles containing the 48-bit host address, plus an 8-bit checksum.
; (Checksum is not checked.)
;
; Register usage:
; H,L - Word buffer
; D,E - Host address prom pointer
; C - byte counter
;
; On entry: D,E points to the first nibble of the Host word to be read.
; On exit: D,E points to the first nibble of the next Host word to be read.
SendHostWord:
lxi h,HeaderHi ; Use Header as a buffer, point to high byte
mvi c,2 ; Count of 2 bytes
ReadHAddrLoop:
ldax d ; Get low nibble of byte
inx d ; Point to next nibble
ani 0FH ; Mask out high part
mov b,a ; Store in B
ldax d ; Get high nibble of byte
inx d ; Point to next nibble
ani 0FH ; Mask out high part
rlc ; Move to high part of byte
rlc
rlc
rlc
ora b ; Form byte in A
mov m,a ; Store in buffer
dcx h ; Point to next byte up
dcr c ; End of loop?
jnz ReadHAddrLoop ; nz => still more to do
; Header has the word. Write to port.
lhld Header
xchg ; D,E _ address word, H,L _ Prom address
call WriteCPword ; To CP
xchg ; D,E _ Prom address
ret
; Subroutine: ReadMainMem.
; Read the contents of a main memory location in the first 64K space.
; On entry: H,L = low 16 bits of address.
; On exit: low byte in A, high byte in B.
ReadMainMem:
shld MemPCB+CPAddr3 ; Store address in FlagPCB
mvi a,CPReadCmd ; Read command
lxi h,MemPCB ; Point to the FlagPCB
call InitCPCmd ; First byte is command
; Now get the contents.
call ReadCPbyte ; Get low byte from port (returned in A)
mov c,a ; Save temporarily in C
call ReadCPbyte ; Get high byte from port (returned in A)
mov b,a ; Store in high byte
mov a,c ; Return low byte in A
ret
; Subroutine: CheckCPStopped.
; Check if CP is stopped, i.e. in CP kernel.
; If it is not, stop the CP.
CheckCPStopped:
lda BootFlags ; Check value in BootFlags
ani CPStopped
rnz ; nz => CP already stopped
; CP is not stopped. Send it back to the kernel.
jmp StopCP
; Jump to StopCP subroutine and Return.
; Subroutine: DoWriteCS.
; Write the CS location.
; In BootMode the CS location is written.
; In non-BootMode, IOPWait is set, a CS byte is written,
; the CP re-enabled, and a Refresh is requested. Repeated for each byte.
; (Maximum time that IOPWait should be set is 80 340 ns IOP cycles, 92 333 ns cycles.)
; On entry: Assumes that the TPC[6] has been set up correctly.
; CS data is in a CSBuffer [0..5], (MSB..LSB).
; where D,E is pointing to the buffer.
; On exit:
; D,E points to the next byte after current CS buffer (useful for image transfer)
; All registers are used.
; Register usage:
; H,L - Points to CPControl (memory mapped I/O)
; B,C - Points to CSa-CSe (memory mapped I/O)
; D,E - Points to CSBuffer
DoWriteCS:
lxi h,8000H+CPControl ; Set H,L to point to CPControl (memory mapped I/O)
lxi b,8000H+CSa ; Set B,C to point to CSa-CSe (memory mapped I/O)
; Check for BootMode.
lda BootFlags ; Check whether the CP kernel is running (BootMode)
ani BootMode ;
jnz BootDoWriteCS ; BootMode (nz) => no CP control, Refresh
; Not BootMode.
mvi a,6 ; Counter for 6 bytes
DoWriteCSLoop:
sta CSCount ; Use memory for CS byte counter
mvi m,CPWait ; Set IOPWait in CPControl
; CP now in WAIT state.
mvi m,CPWaitSwT ; [10] Set IOPWait, SwTAddr in CPControl
ldax d ; [7] Get byte into A
cma ; [4] Complement for CP LS240
stax b ; [7] Output CS byte
mvi m,CPWait ; [10] Set IOPWait, clear SwTAddr in CPControl
mvi m,CPEnable ; [10] Clear IOPWait, SwTAddr in CPControl
; CP now out of WAIT state. In wait state for 48 IOP cycles.
mvi a,CPRefresh ; Request refresh
call WriteCPbyte
NextWriteCS:
inx b ; Point to next CS I/O slot
inx d ; Point to next CS buffer location
lda CSCount ; Decrement CS byte counter
dcr a
jnz DoWriteCSLoop ; nz => still more
ret
; In BootMode. IOPWait is true.
BootDoWriteCS:
mvi a,6 ; Counter for 6 bytes
BootDoWriteCSLoop:
sta CSCount ; Use memory for CS byte counter
ldax d ; Get byte into A
cma ; Complement for CP LS240
stax b ; Output CS byte
inx b ; Point to next CS I/O slot
inx d ; Point to next CS buffer location
lda CSCount ; Decrement CS byte counter
dcr a
jnz BootDoWriteCSLoop ; nz => still more
ret
; Subroutine: DoWriteTPC
; Write the TPC.
; In BootMode the TPC is written.
; In non-BootMode, IOPWait is set, TPC is written,
; the CP re-enabled, and a Refresh is requested.
; (Maximum time that IOPWait should be set is 80 340 ns IOP cycles, 92 333 ns cycles.)
; On entry: C contains the TPC address (3 bits right-justified)
; DE contains the TPC data (12 bits right-justified)
; Format of TPCHigh (write): TPCAddr[0:2],,TPCData[0:4]'
; Format of TPCLow (write): don't care,,TPCData[5:11]'
; Compute the values of TPCHigh, TPCLow beforehand so that the
; CP is kept with IOPWait high for a minimum of time.
DoWriteTPC:
call LeftAlignTPCAddr ; Left align 3 bits of address in C
mov a,e ; Move TPC[4] into B for TPCHigh format
ral ; TPC[4] into carry
mov a,d ; get high part
ral ; TPC[4] into B[7]
cma ; complement for port
ani 1FH ; Clear High 3 bits
ora c ; OR in address
; Value in C not needed again.
mov d,a ; Store back in D
mov a,e ; Get low part (E[0] is don't care)
cma ; complement for port
mov e,a ; Store back in E
lxi h,8000H+CPControl ; Set H,L to point to CPControl (memory mapped I/O)
lxi b,8000H+TPCHigh ; Set B,C to point to CPControl (memory mapped I/O)
; Check for BootMode.
lda BootFlags ; Check whether the CP kernel is running (BootMode)
ani BootMode ;
jnz BootWriteTPC ; BootMode (nz) => no CP control, Refresh
mvi m,CPWait ; Set IOPWait in CPControl
; CP now in WAIT state.
mvi m,CPWaitSwT ; [10] Set IOPWait, SwTAddr in CPControl
mov a,d ; [4] Get high part
stax b ; [7] Output TPCHigh (address, high data)
inx b ; [6] point to TPCLow
mov a,e ; [4] Get low part
stax b ; [7] Output TPCLow (low data)
mvi m,CPWait ; [10] Set IOPWait, clear SwTAddr in CPControl
mvi m,CPEnable ; [10] Clear IOPWait, SwTAddr in CPControl
; CP now out of WAIT state. In wait state for 58 IOP cycles.
mvi a,CPRefresh ; Request refresh
jmp WriteCPbyte
; Jump to WriteCPbyte subroutine and Return.
; In BootMode. IOPWait is true.
BootWriteTPC:
mov a,d ; Get high part
stax b ; Output TPCHigh (address, high data)
inx b ; point to TPCLow
mov a,e ; Get low part
stax b ; Output TPCLow (low data)
ret
; Subroutine to left align TPC address in C.
LeftAlignTPCAddr:
mov a,c
ani 7H ; Clear top bits
rrc
rrc
rrc
mov c,a
ret
; Subroutine: StartCPKernel and StopCP.
; Set IOPWait=0, SwTAddr=0, clear BootMode, set CPStopped in BootFlags.
; StartCPKernel:
; Start CP after CPKernel loading.
; On entry: IOPWait = SwTAddr = 1.
; StopCP:
; Stop CP execution.
; Toggle IOPWait in CPControl, in order to cause CP to enter the CPKernel.
; On entry: IOPWait = SwTAddr = 0.
StopCP:
StartCPKernel:
mvi a,CPWait ; Clear SwTAddr (Set IOPWait) in CPControl
out CPControl
mvi a,CPEnable ; Clear IOPWait, SwTAddr in CPControl
out CPControl
mvi a,NoBootMode+CPStopped ; BootMode=0, CPStopped=1
sta BootFlags
ret
; Subroutine: StartCP.
; Start CP execution, by issuing the ExitKernel command.
; Force the CP to exit the CP Kernel.
StartCP:
mvi a,CPExitKernel ; Command to CPKernel to exit CPKernel
call WriteCPbyte
; Clear out flags in BootFlags.
mvi a,NoBootMode ; BootMode=0, CPStopped=0 in BootFlags
sta BootFlags
ret
; Subroutine: InitCPCmd.
; Initialize CP port for reads or writes.
; On entry:
; A = command to microcode
; H,L = Pointer to CPport Control Block
; Format of initialize:
; Command, low address, middle address, high address, low [count], high [count].
InitCPCmd:
call WriteCPbyte ; First byte is command
lxi b,CPAddr3 ; Index in control block of CP buffer address
dad b ; H,L points to Low 16 bits of CP buffer address
mov e,m ; Address byte 3
inx h
mov d,m ; Address byte 2
call WriteCPword ; Send to port
inx h
mov a,m ; High part address (address byte 1)
ani CP64KMask ; Mask for address size
call WriteCPbyte ; Send to port
; Note the high byte, which is always 0, is not transmitted to CP.
inx h ; Ignore top address byte
inx h ; Point to count
mov e,m ; Low part count
inx h
mov d,m ; High part count
dcx d ; Microcode wants count-1
jmp WriteCPword ; Send to port
; Jump to WriteCPword subroutine and Return.
; CPport data transfer subroutines.
; Subroutine: ReadCPbyte.
; Read CP byte.
; Byte returned in A register.
ReadCPbyte:
in CPStatus ; Read the port interrupt bits
ani CPInIntMask ; CPIn requesting an interrupt?
jz ReadCPbyte ; Zero means no interrupt
in CPIn ; get data
ret
; Subroutine: ReadCPword.
; Read CP word.
; Word returned in D,E.
ReadCPword:
call ReadCPbyte
mov e,a
call ReadCPbyte
mov d,a
ret
; Subroutine: WriteCPbyte.
; Write CP byte.
; Byte in A register written into port.
WriteCPbyte:
out CPOut ; Output data
WaitCPOutAck:
in CPStatus ; Read the port interrupt bits
ani CPOutIntMask ; CPOut requesting an interrupt, i.e data read?
jz WaitCPOutAck ; Zero means no interrupt
ret
; Subroutine: WriteCPword.
; Write CP word.
; Word in D,E written into port.
; NOTE: Least significant byte written first through port.
WriteCPword:
mov a,e
call WriteCPbyte
mov a,d
jmp WriteCPbyte
; Jump to WriteCPbyte subroutine and Return.
; Subroutine: ByteToWord [H,L: word] RETURNS [H,L: word].
; Convert value in H,L (byte count) to word count.
; i.e. long shift right by 2.
ByteToWord:
ora a ; Clear carry
mov a,h ; High part
rar ; shift high part right, low bit into carry
mov h,a
mov a,l ; Low part
rar ; shift low part right, shift in high bit
mov l,a
ret
;
; FLOPPY DISK subroutines.
; Constant data variables:
SectorRunSize:
db SectorNo ; Number of sectors read in a run
SectorSize:
dw SectorLen ; Sector size (bytes)
MaxBufCnt:
dw (SectorNo*SectorLen)/2 ; Size of Floppy Buffer (words)
MaxSector:
db MaxSectorNo+1 ; Maximum sector number+1
Density:
db DDen ; Density: 0 = single, 1 = double
FloppyBuffer:
dw FloppyBuf ; Pointer to start of Floppy buffer
; Subroutine: ReadSectorRun.
; The floppy buffer was empty. Read the next run of sectors from the disk into the floppy buffer.
; If the end of a track is encountered before the end of the run, then stop the run.
;
; On entry:
; The disk address for the next access is in DSide, DCylinder (1 word), and Sector.
; The current disk head position is in Side, and Cylinder (1 word).
; The size of the sector run is in SectorRunSize.
; On exit:
; The disk address for the next access is in DSide, DCylinder (1 word), and Sector.
; The current disk head position is in Side, and Cylinder (1 word).
ReadSectorRun:
lda SectorRunSize ; Initialize: SectorCnt _ SectorRunSize
sta SectorCnt
lhld FloppyBuffer ; Initialize the buffer pointer of the ReadSector command
shld FloppyBufPtr
lxi h,0 ; Initialize: FloppyBufCnt _ 0
shld FloppyBufCnt
; Check if the Side should be switched.
CheckSideSwitch:
lxi h,Side ; Point to current Side
lda DSide ; Get desired Side
cmp m
cnz DoSwitchSide ; nz => different, switch the side select
; Check if a seek is needed.
CheckSeek:
lxi h,Cylinder ; Point to current cylinder
lda DCylinder ; Assumes high part of Cylinder=0
cmp m
cnz DoSeekCmd ; nz => different, do a seek, update Cylinder
; Now do the read sector.
; Buffer pointer in FloppyBufPtr, sector size in SectorSize, sector number in Sector.
ReadSectorLoop:
lda Sector
mov d,a ; Desired sector in D
lda ReadSectorCom ; ReadSector command to E
mov e,a
mvi a,RetryNo ; Initialize retry counter
sta RetryCount
ReadAgain:
call DoReadSector ; Do the read, soft error status returned in A
; (A) = 0 for no errors, (A) # 0 for soft errors (with errors = 1).
ora a
jz ReadSectorGood ; z => no errors
; We had a soft error. Try again. (Later: restore and try again)
DecrReadRetry:
lxi h,RetryCount
dcr m
jnz ReadAgain ; z => Too many retries
; Too many retries. Report error.
ReadSectorFail:
mvi c,ErrorReadSectorFail
jmp ErrorReport ; ERROR: Too many read sector failures.
; The sector was read without error.
; Increment the Buffer count, by the sector size.
; Check if more sectors are too be read.
ReadSectorGood:
lhld SectorSize ; Update FloppyBufCnt
call ByteToWord ; Convert to words
xchg ; D,E _ Sector size (words)
lhld FloppyBufCnt ; H,L _ Buffer count
dad d ; H,L _ Buffer count + Sector size (words)
shld FloppyBufCnt ; Store Buffer count
lxi h,SectorCnt ; Decrement the sector count
dcr m
jz SectorRunDone ; z => Sector run completed
; Still more sectors to be read. Fix the next disk address.
; Increment the Buffer pointer, by the sector size.
MoreSectors:
lhld SectorSize ; Update BufferPtr for next transfer
xchg ; D,E _ Sector size (bytes)
lhld FloppyBufPtr ; H,L _ Buffer pointer
dad d ; H,L _ Buffer pointer + Sector size
shld FloppyBufPtr ; Store buffer pointer
lxi h,Sector ; Increment sector number
inr m
lda MaxSector ; Get maximum sector number+1
cmp m
jz TrackCross ; z => Sector number = MaxSector+1, i.e. end of track
jmp ReadSectorLoop ; Do it again
; All the sectors in the run have been correctly read.
; Form the next disk address in DCylinder, Sector.
; Initialize the buffer pointer and count for GetNextWord.
SectorRunDone:
lhld FloppyBuffer ; Initialize the buffer pointer of the next GetNextWord
shld FloppyBufPtr
lxi h,Sector ; Increment sector number
inr m
lda MaxSector ; Get maximum sector number+1
cmp m
jz FormNextDiskAddress ; z => end of cylinder
; If zero, jump to FormNextDiskAddress subroutine and Return.
; Sector did not cross track boundary. Set DCylinder _ Cylinder.
lda Cylinder
sta DCylinder
ret
; The next sector crossed a track boundary. Terminate the run of sectors.
; Form the next disk address, and initialize the Buffer pointer for GetNextWord.
TrackCross:
lhld FloppyBuffer ; Initialize the buffer pointer for the next GetNextWord
shld FloppyBufPtr
; Jump to FormNextDiskAddress subroutine (**below) and Return.
; Subroutine: FormNextDiskAddress.
; Form the disk address of the beginning of the next track.
; Single-sided disk:
; Sector _ 1;
; DCylinder _ Cylinder + 1;
; Double-sided disk:
; Sector _ 1;
; IF Side = 0 THEN DSide _ 1
; ELSE {i.e. Side = 1} BEGIN DSide _ 0;
; DCylinder _ Cylinder + 1;
; END;
FormNextDiskAddress:
mvi a,1 ; Sector _ 1;
sta Sector
in FDCStatusReg ; Check in external status reg. whether single or double-sided
ani FDCTwoSidedMask
jz DoSingleSide ; z => single sided
; Double sided disk.
lda Side
xri Side1Mask
sta DSide ; DSide _ Side xor Side1Mask
rnz ; nz => DSide =1, i.e. was 0
; DSide = 0, i.e. was 1. Increment the cylinder number.
DoSingleSide:
lda Cylinder
inr a
sta DCylinder
ret
; Subroutine: DoSwitchSide.
; Switch the side select values in Side and FDCState.
; On entry: H,L points to Side
; A = DSide
DoSwitchSide:
mov m,a ; Side _ DSide
ora a ; Check if desired side is 0 or 1
jnz SetSide1 ; nz => DSide is 1
SetSide0:
mvi a,nFDCSide1Mask ; Set Side 0 (clear Side 1 bit)
call ClearFDCStateBit
lxi h,ReadSectorCom ; Fix up ReadSector Cmd
mvi a,nType2SMask ; Clear S bit
ana m
EndSwitchSide:
mov m,a
; Delay to wait for head settling time.
lxi h,1
call Delay
ret
SetSide1:
mvi a,FDCSide1Mask ; Set Side 1
call SetFDCStateBit
lxi h,ReadSectorCom ; Fix up ReadSector Cmd
mvi a,Type2SMask ; Set S bit
ora m
jmp EndSwitchSide
; Subroutine: DoSeekCmd.
; A seek is needed. Do the command, and retry if errors occur.
; On entry:
; Cylinder has current cylinder.
; DCylinder has desired cylinder.
; The sector number is greater than the maximum on a track. Move to the next track.
DoSeekCmd:
lda DCylinder ; Desired cylinder to D
mov d,a
lda SeekCom ; Seek command to E
mov e,a
mvi a,RetryNo ; Initialize retry counter
sta RetryCount
SeekAgain:
call DoSeek ; Do the seek, soft error status returned in A
; (A) = 0 for no errors, (A) # 0 for soft errors (with errors = 1).
; Track location was updated if command was successful.
ora a
rz ; z => Seek was successful, return
; We had a soft error. Try again. (Later: restore and try again)
DecrSeekRetry:
lxi h,RetryCount
dcr m
jnz SeekAgain ; z => Too many retries
; Too many retries. Report error.
SeekFail:
mvi c,ErrorSeekFail
jmp ErrorReport ; ERROR: Too many Seek failures.
; Subroutine: DoReadSector.
; Implement Read Sector command.
; Assumes Head at track and Track Register up to date.
; On entry:
; D = Desired sector
; E = Command
; On exit:
; A = Internal FDC completion status
; Call subroutine at DoReadAddressTrack, for Read Address or Read Track.
DoReadSector:
mov a,d ; Set up sector for ReadSector command
out FDCSector ; Set desired sector in FDCSector Register
; Program channel 0 of Dma controller for memory writes.
; All interrupts are disabled:
; di ; Disable interrupts while programming Dma
lhld FloppyBufPtr
mov a,l ; Program low buffer address
out DmaCh0Addr
mov a,h ; Program high buffer address
out DmaCh0Addr
lhld SectorSize
dcx h ; Decrement for Dma
mov a,l ; Program low Count
out DmaCh0Count
mov a,h ; Program high Count
ori DmaWriteBit ; OR in function (memory writes)
out DmaCh0Count
mvi a,EnFloppyChannel ; Enable channel 0: TCS, EW, EN0
out DmaMode
; ei ; Re-enable interrupts
; Dma channel is now programmed and enabled.
DoReadContinue:
mov a,e ; Get FDC command
call SendCommand ; Issue command
; Command has ended. Check completion status.
; Check for error completion (internal status in A, external in B).
; (Not Ready, RNF, CRC error, Lost Data, Busy)
ani ReadErrorMask
sta DiskStatus ; Store away
ani ReadHardMask ; (Not Ready, Busy)
jz DoReadGood
ReadHardError:
mvi c,ErrorReadHardError
jmp ErrorReport ; ERROR: Read Sector hard error
; Correct FDC termination.
; If we had an RNF or LostData error then we shouldn't check for DMA completion.
DoReadGood:
lda DiskStatus ; Restore status
ani FDCRNFMask
jnz DoReadDone ; Don't check Dma
lda DiskStatus ; Restore status
ani FDCLostData
jnz DoReadDone ; Don't check Dma
; Check that DMA terminated.
mov a,b ; B has external Status Reg value
ani FDCEndCountMask
jnz GoodDmaEndCount1
NoDmaEndCount1:
mvi c,ErrorNoDmaEndCount1
jmp ErrorReport ; ERROR: External Dma End Count register not set
; Check internal Dma status to ensure completion.
GoodDmaEndCount1:
in DmaStatus ; Read internal Dma Status
ani DmaCh0Mask
jnz DoReadDone ; nz => channel completed
NoDmaEndCount2:
mvi c,ErrorNoDmaEndCount2
jmp ErrorReport ; ERROR: Internal Dma End Count not set.
; Read sector done without any hard errors.
DoReadDone:
xra a
out DmaMode ; Disable Dma
lda DiskStatus ; Return status
ret
; Subroutine: DoSeek.
; Issue seek command. Assumes FDC Track register is up-to-date.
; Checks validity of track number on completion.
; Update DDen bit in FDCState for dsestination cylinder.
; Note that PreComp bit does not have to be changed since we do no writing.
; On entry:
; D = Desired Cylinder
; E = Command
; On exit:
; A = Internal FDC completion status
DoSeek:
mov a,d ; Check if desired Track OK
cpi 77
jc SeekCylinderOK
TrackToBig:
mvi c,ErrorTrackToBig
jmp ErrorReport ; ERROR: Track number is too large.
; Track number is less than 77.
; Output desired cylinder for Seek.
SeekCylinderOK:
out FDCData ; Set desired track in Data Register for Seek
; Cylinder number is OK. Check the value of the destination cylinder and update FDCState.
DoType1Cmd:
; Check if Cylinder 0 and force to single density.
mov a,d ; Get cylinder number
ora a
jz SetSD ; z => At cylinder 0, force single density
; The head is not at cylinder 0. Check the Density.
lda Density ; Check for double density
ora a
jz SetSD ; z => single density, i.e. no DDen, no Precomp.
; Double density. Set DDen bit.
mvi a,FDCDDenMask ; Set the DDen bit
call SetFDCStateBit
; Now issue the command.
IssueType1Cmd:
mov a,e ; Get command
call SendCommand ; Issue command
; Check for error completion (in A).
; (Not Ready, Seek error, CRC error, Busy)
sta DiskStatus ; Store away
ani Type1ErrorMask
mov b,a ; save away
ani Type1HardMask ; (Not Ready, Busy)
jz CheckTrack ; z => no hard error
Type1HardError:
mvi c,ErrorType1HardError
jmp ErrorReport ; ERROR: Type 1 hard error
CheckTrack:
in FDCTrack ; Check track register
xra d
jz UpdateCylinder ; z => Track register is correct
CommandTrackError:
mvi c,ErrorCommandTrackError
jmp ErrorReport ; ERROR: Track register is not correct
; Track register is correct. Update Cylinder location.
UpdateCylinder:
mov l,d
mvi h,0
shld Cylinder
ExitType1Cmd:
mov a,b ; Return status
ret
; Set single density (clear the DDen bit).
SetSD:
mvi a,nFDCDDenMask ; Clear double density bit
call ClearFDCStateBit
jmp IssueType1Cmd
; Subroutine: SetFDCStateBit.
; Set a bit in FDCState and FDCStateVal.
; On entry: A = Mask of bit to be set.
SetFDCStateBit:
lxi h,FDCStateVal ; Point to FDCStateVal
ora m ; Set the bit
StoreNewBit:
mov m,a ; Store in FDCStateVal
out FDCState ; Store in FDCState
ret
; Subroutine: ClearFDCStateBit.
; Clear a bit in FDCState and FDCStateVal.
; On entry: A = Complement of mask of bit to be cleared.
ClearFDCStateBit:
lxi h,FDCStateVal ; Point to FDCStateVal
ana m ; Clear the bit
jmp StoreNewBit
; EXTERNAL Subroutine: FloppyInit.
; Reset the floppy controller, and restore the the heads.
FloppyInit:
mvi a,RestoreCmd ; Initialize Restore command
sta RestoreCom
; This entry point is the value of the Restore command is to be changed.
; First check if there is a floppy (based on switch setting).
FloppyInitE:
in FDCStatusReg ; Read SA800 bit (now no-floppy bit)
ani FDCSA800Mask
jnz NoFloppyFound ; nz => no Floppy
mvi a,RetryNo ; Initialize retry counter
sta RetryCount
InitializeAgain:
call FDCReset
mvi c,5 ; Issue 5 Stepin commands
mvi e,StepInCmdNoV ; Send StepIn commands (no verify)
InitializeStepLoop:
mov a,e
call SendCommand ; Issue command
; Don't care what the status is. Errors will be caught by the Restore.
ani Type1HardMask ; (Not Ready, Busy)
jnz Type1HardError ; nz => hard error
dcr c
jnz InitializeStepLoop
; We should now be beyond (in) from Track 00. Issue Restore.
lda RestoreCom
mov e,a
mvi d,0 ; D _ 0
RestoreAgain:
call DoType1Cmd ; Issue Restore
; Check for error completion.
; (A) = 0 for no errors, (A) # 0 for soft errors (with errors = 1).
; Cylinder location was updated if command was successful.
ora a
jz CheckTrk0
; We had a soft error. Try again. (Later: restore and try again)
DecrRestoreRetry:
lxi h,RetryCount
dcr m
jnz InitializeAgain ; z => Too many retries
; Too many retries. Report error.
RestoreFail:
mvi c,ErrorRestoreFail
jmp ErrorReport ; ERROR: Too many Restore failures.
; Check the Track 0 bit in the Status.
CheckTrk0:
lda DiskStatus
ani FDCTk0Mask ; Track 0 bit set?
jz DecrRestoreRetry ; z => no track 0 bit
; Restore was successful.
; We are now sitting at track 0. Cylinder is initialized.
; Initialize Side and Sector, ReadSectorCom and SeekCom.
RestoreGood:
xra a
sta Side ; Initialize to side 0
inr a ; Sector starts at 1
sta Sector
mvi a,ReadSectorCmd
sta ReadSectorCom ; Initialize ReadSector command for Side 0
mvi a,SeekCmd
sta SeekCom
ret
; Switch setting indicated that no floppy was installed.
NoFloppyFound:
mvi c,ErrorNoFloppy
jmp ErrorReport ; ERROR: No floppy drive installed.
; Subroutine: FDCReset.
; Reset the FDC, abort the automatic Restore with ForceInterupt,
; and wait until Busy is 0.
; Sets the default FDCState value in FDCState.
; Returns status in A, after command completes.
FDCReset:
mvi a,DisableFDC ; clear state register, reset FDC, enable Waits
out FDCState
lxi h,4 ; Wait for more than 50 usec (MR pulse)
call Delay
mvi a,DefaultFDCStateVal ; Get control byte for FDC
sta FDCStateVal
out FDCState
mvi a,ForceInt0Cmd ; Issue Force Interrupt command
out FDCCommand
lxi h,1 ; Busy status not available for 12 usec
call Delay
; Wait for Busy to reset. Status returned in A and B.
WaitFDCBusy:
in FDCStatus ; Check for restore command completion
mov b,a
ani FDCBusyMask
jnz WaitFDCBusy
mov a,b ; Return status in A too
ret
; Subroutine: SendCommand.
; Issue the command to the FDC.
; Command is issued, and the Int Req register is polled for command completion.
; Call subroutine at GetFDCStatus to get status only, after Busy resets.
; On entry:
; A = Command
; On exit:
; A = Internal status
; B = External status
SendCommand:
out FDCCommand
lxi h,1
call Delay
; Check for FDC command in IntReq.
GetFDCStatus:
in IntReq ; Check for completion in IntReq Reg.
xri IntReqMask ; complement signals which are active low
ani FDCIntMask
jz GetFDCStatus
; Command has completed. Get status.
in FDCStatusReg ; Get external status register.
mov b,a ; Save in B
in FDCStatus ; Check for restore command completion
sta DiskStatus ; Store away
ret ; return status in A, external status in B
;
; MAINTENANCE PANEL subroutines.
; Subroutine: PhaseToMP.
; Put Phase*50 + 99 in maintenance panel
; Put Phase*50 + 100 in MPOffset
; Note: Won't work for Phase=0.
PhaseToMP:
lxi h,MPStartPhase0-1 ; MP = 99
call PutMP
lxi b,MPStartPhase0 ; B,C = 100
lxi h,Phase ; Point to Phase number
mov e,m ; E is counter for number of 50's
PhaseToMPLoop:
lxi h,50 ; Increment panel by 50
call DeltaMP
lxi h,50 ; Increment Offset in B,C by 50
dad b
mov c,l
mov b,h
dcr e ; Are we done?
jnz PhaseToMPLoop ; nz => still more
; Store B,C in MPOffset.
mov l,c
mov h,b
shld MPOffset ; MPOffset _ MP number
ret
; Subroutine: DoMiscClock.
; Clocks a bit in the MiscClocks1 register.
; Width of clock pulse is 14 cycles (~5 usec).
; On entry: D contains a mask of the bit(s) to be toggled.
DoMiscClock:
mvi a,0FFH ; Set all high
xra d ; Clear clock bit(s)
out MiscClocks1
xra d ; Toggle bit again
out MiscClocks1
ret
; Subroutine: PutMP [H,L: number].
; Put a number in the maintenance panel.
; Number is put modulo 10,000D.
; On entry: H,L contains the number to be put in the panel.
; Note: Call at DeltaMP to increment the MP by the contents of H,L.
PutMP:
call ClearMPanel ; Clear the panel
DeltaMP:
inx h ; Bias so that a value of zero can be used
mvi a,BlankMPanel ; Blank MP
out MiscControl1
jmp CheckMPCount
PutMPLoop:
mvi d,IncMPanel ; Mask for IncMPanel clock
call DoMiscClock
CheckMPCount:
dcx h ; Decrement the count
mov a,l ; Check for count = 0
ora h ; Low OR high
jnz PutMPLoop ; nz => nonzero
; Done.
xra a ; Unblank MP
out MiscControl1
ret
; Subroutine: ClearMPanel.
; Clear the maintenance panel, and disable blanking.
ClearMPanel:
mvi d,ClrMPanel ; Mask for ClrMPanel clock
call DoMiscClock
xra a ; Clear BlankMPanel
out MiscControl1
ret
; Subroutine: IncrMP.
; Increment the maintenance panel.
IncrMP:
push d ; Save D,E
mvi d,IncMPanel ; Mask for ClrMPanel clock
call DoMiscClock
pop d
ret
; Subroutine: ErrorReport.
; Blink the maintenance panel with the error number.
; No return from this subroutine.
; On entry: C >= 0 => Blink (MPOffset + C) in MP
; C < 0 => Blink current number in MP
ErrorReport:
mov a,c ; Chek sign of C
ora a
jm ErrorBlink ; A < 0 => don't put new number in MP
; Put (MPOffset+C) in MP.
lhld MPOffset ; Get offset value
mvi b,0 ; Clear high part of B,C
dad b ; Add offset: H,L _ actual number
call PutMP ; Put the number in the panel
ErrorBlink:
mvi a,BlankMPanel ; Blank bit in A
lxi h,BlinkDelay ; H,L _ Blink delay constant
ErrorTrap:
out MiscControl1 ; Blank/unblank the panel
call Delay
xri BlankMPanel ; Toggle Blank bit
jmp ErrorTrap
; Subroutine: Delay.
; Wait the period of time specified in H,L.
; H,L restored, A restored.
; Delay is 74 + 24*const cycles (const in H,L)
; = 25 + 8* const usec
; Maximum delay: H,L = 0FFFFH = ~.5 sec
Delay:
push psw ; [12] Save A
push h ; [12] Save H,L
; Inner loop approximately 24 cycles (=8 usec)
DelayLoop:
dcx h ; [6]
xra a ; [4]
cmp l ; [4]
jnz DelayLoop ; [10]
cmp h ; [4]
jnz DelayLoop ; [10]
pop h ; [10] Restore H,L
pop a ; [10] Restore A
ret ; [12]
END BootSubs
@
1.1.1.1
log
@first add
@
text
@@