mirror of
https://github.com/livingcomputermuseum/Darkstar.git
synced 2026-03-02 10:06:45 +00:00
39 lines
53 KiB
Plaintext
39 lines
53 KiB
Plaintext
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
|
||
@@
|