Equates for Network 4 (Omninet) Ports
OMSB
EQU 0D3H
; MSB of Omninet Address Pointer
OLSB
EQU 0D1H
; LSB of Omninet Adddress Pointer
OSTROB
EQU 0D2H
; Omninet Strobe Port
OMOUT
EQU 0D0H
; Output with Auto Increment
OMIN
EQU 0D2H
; Input without Auto Increment
OMINPL
EQU 0D0H
; Input With Auto Increment
OMSTAT
EQU 0D3H
; Transporter Status
Equates for Hard Disk Ports
HSTAT
EQU 0CFH
; Status Register
HCOMND
EQU 0CFH
; Command Register
HDRIVE
EQU 0CEH
; Size/Drive/Head Register
HCYLHI
EQU 0CDH
; Cylinder High Byte
HCYLLO
EQU 0CCH
; Cylinder Low Byte
HSECT
EQU 0CBH
; Sector Number
HDATA
EQU 0C8H
; Data Register
HCNTL
EQU 0C1H
; Control Register
Equates for Misc Other Items
DISPIT
EQU 021BH
; ROM Display line routine
OMBUFF
EQU 7A00H
; Read Buffer
BUFMSB
EQU 7AH
; MSB of the OMBUFF Address
Begin …
LD HL,(6FF0H)
;Get Special Flag
XOR A
; Start by Resetting Transporter
OUT (OLSB),A
; Point at Zero – LSB First
OUT (OMSB),A
; Point at Zero – then MSB
OUT (OMOUT),A
; Response Code
LD A,20H
; Reset Command (i.e., a space)
OUT (OMOUT),A
; Put into Transporter
OUT (OMOUT),A
; Reponse Address = Zero
; Register A Now contains our Transporter Address
CP 63H
; Are we the server? (Check for “?”)
; If so, then the hard disk is directly attached to the computer making this system the server. With this, jump to the hard drive to load the server code.
If we are here, then the station address is not 63 and this is not the server. With this, we must read (across the network) the bootstrap pointer sector (sector 0 of drive 1 at the server) to get the boot code volume for THIS station, then get the location of the boot volume from the volume table, and then load the object code found at that location and jump to it. So let’s start …
EX AF,AF’
; Save address until later
LD DE,20H
; Logical Record Number of Boot Pointer
LD BC,OMBUFF
; Point at our budffer
EX AF,AF’
; Get our address back
LD H,BUFMSB
; MSB of our Buffer Address
ADD A,A
; Double our Address
LD L,A
; HL is our Boot Volume Number
INC HL
; Bump HL to point to the MSB
Now have Volume Number of our Boot Code. Next we must get the Volume Entry for Start Address …
LD BC,10H
; Size of volume entry (Binary = 16)
LD HL,0
; Start at Zero offset
GETV1
LD A,E
; Check remaining count
DEC DE
; Count down volume numbers
ADD HL,BC
; Bump sector and offswt
“GETV2” – At this point H = the Sector Offset and L = Buffer offset.
GETV2
LD E,H
; Sector offset into E
LD H,BUFMSB
; MSB of Buffer
PUSH HL
; Save Buffer Offset for a Moment
LD HL,15H
; Sart of Volume Table (15)
EX DE,HL
; Put adjusted sector into DE
LD BC,OMBUFF
; Point at Buffer
POP HL
; HL now points at the type code
INC HL
; Point at the server number
LD A,(HL)
; Get server number
LD (SERV1),A
; Put into the command area
INC HL
; Point at the drive number
LD (RDDRV),A
; Put into read command
INC HL
; Point at the LSB of the Starting Sector
INC HL
; Point at the MSB of the Starting Sector
LD BC,OMBUFF+255
; Point at End of Buffer
“BOOT0” – We know where the Boot Code is so BOOT!
CP 02H
; Is it a start record?
; Yes, go get start addres
CP 01H
; Is it object code?
; No, comment, go ignore it
Load file format error!
LD HL,FMTERR
; Point at error message
FATAL
EQU $
; Any error comes here
STOP
; Hang the system in an infinite loop
BOOT1
; Get count to ignore
DJNZ BOOT2
; Spin our wheels
; Loop back to BOOT0 to get next byte
BOOT3
; Get the size of the record
DEC B
; Remove the address part of the size
; Get the LSB of the Load Address
; Get the MSB of the Load Address
BOOT4
; Get Next Byte of Object Code
LD (HL),A
; Put it into memory
INC HL
; Bump the memory pointer
DJNZ BOOT4
; Go get the next byte
; Get the LSB of the Start Address
; Get the MSB of the Start Address
LD (6FF0H),HL
; Wipe out the flag
GETBYT” – Returns the next byte from the disk drive reading the buffer when necessary
; Read the sector, if necessary
LD A,(BC)
; Fetch the byte.
GREAD
PUSH BC
; Save buffer
SWITCH
; Self modifying code switch
INC DE
; Point to the next one
“OREAD” – Omninet Read Routine: BC = Buffer Address; DE = Record Number Wanted.
OREAD
LD A,08H
; Retry count
LD (RETRYS),A
; Put into field
LD (RDSECT),DE
; Put sector into read buffer
PUSH BC
; Save buffer address for a moment
OUT (OLSB),A
; Point Pointer At 0
LD C,OMOUT
; Point C at the Data Port
LD B,CMDEND-CMDS
; Get length of area
LD HL,CMDS
; Point to command area
OTIR
; Move it to the transporter
Transporter is now formatted the way we want it, so issue the read. Remember that A is still zero.
LD A,RES1-CMDS
; Point at the result
CP 0FEH
; = Successful set up?
INC A
; Move A back to 255
OUT (OMOUT),A
; Put back into the transporter
LD A,SEND1-CMDS
; Point at send command
LD A,RES2-CMDS
; Point at result to send
CP 80H
; Check for 80H as a fatal error
LD A,RES1-CMDS
; Point at RES1 again
; Now wait for the server
CP 80H
; Check for 80H as a fatal error
LD A,STAT-CMDS
; Point at disk status
OUT (OLSB),A
; Do the point
CP 80H
; Check for 80H as a fatal error
Now retrieve the read record.
OUT (OMSB),A
; Point at the data area
OUT (OLSB),A
; Start of it
LD BC,OMINPL
; B = 0, C = Input data port
POP HL
; Retrieve the buffer
RET
; Return to the routine calling this one
Let’s just stick a random variable right in the middle of the code for some reason.
Reset the transporter socket
OUT (OLSB),A
; Point at Address 0 – LSB First
OUT (OMSB),A
; Point at Address 0 – then MSB
OUT (OMOUT),A
; Response Code
LD A,10H
; End receive command
OUT (OMOUT),A
; Send the end receive command
OUT (OMOUT),A
; Response address = 0
LD A,0B0H
; Load the socket address into A
XOR A
; Point at response.
Transporter now reset, but before continuing, Let’s wait for a random amount of time.
“STROBE” – Issue an omninet command in zero page with Register A containing the address within the page.
STROBE
PUSH AF
; Save the LSB of strobe
POP AF
; Restore the LSB of Strobe
OSTRB
PUSH AF
; Save it again
OSTRB1
IN A,(OMSTAT)
; Ready?
RLA
; Move the bit to carry
OUT (OSTROB),A
; Stroke it.
“OMWAIT” – Look at an address in zero page (specified in “A”) and waits for it to become something other than 255.
OMWAIT
OUT (OLSB),A
; Point at it
OMW1
IN A,(OMIN)
; Look at it
CP 0FFH
; Check for 255 to see if its done
EX (SP),HL
; Delay in case it is in the
EX (SP),HL
; process of changing
IN A,(OMIN)
; It is ready!
“WAITR” – This routine delays APX 4 seconds to protect against rapid resets. This routine counts from 1 to 65536 32 times.
WAITR
LD B,32H
; Outer Loop – Set to 32 count (DJNZ Command)
LD HL,0
; Inner Loop – Set HL to 0
WAITR1
INC HL
; Inner Loop – Bump HL
; Inner Loop – If HL is NOT Zero, bump again
DJNZ WAITR1
; Outer Loop – Decrement B and run the loop again until B is 0.
“SERVR” – This is the Hard Drive Server Boot Routine, to run on systems which ARE the server.
SERVR
LD A,10H
; First, reset the hard disk
OUT (HCNTL),A
; Wake up the hard disk
LD B,40H
; Give it some time
LD A,0CH
; Set the software reset bit
RLA
; Move busy into carry
; Awake yet? No, go redo it.
OUT (HDRIVE),A
; Point to drive 0
LD A,16H
; Restore command slow mode
SRV1
IN A,(HSTAT)
; Get status
LD A,10H
; Restore command fast mode
SRV2
IN A,(HSTAT)
; Get status
The ROM author comments the new 6 instructions as “Now do some nice self-modifying code”.
LD HL,HDREAD
; Load HL with the Hard Disk Read Routine
LD (SWITCH+1),HL
; Put into the read vector
LD DE,82H
; Start of server code volume
LD BC,OMBUFF+255
; Point at the end of the buffer
EXX
; Flip to the other side
“HDREAD” – Now let’s read drive 0, head 0, of the hard drive. BC is the BUFFER and DE is the Logical Sector
HDREAD
LD HL,05H
;True sector offset
OUT (HSECT),A
; Put into cylinder register
This code divides HL by 32 which will then be the cylinder
SRL H
; Shift the contents of Register H right one bit. Bit 0 is copied to the CARRY FLAG, and a zero is put into BIT 7.
RR L
; Shift the contents of Register L right one bit. Bit 0 is copied to the CARRY FLAG, and the old CARRY FLAG are copied to BIT 7.
Now that HL has the cylinder, we need the track.
LD A,L
; Get low cylinder byte
OUT (HCYLLO),A
; Put into the CYLINDER LOW register
LD A,H
; Get high cylinder byte
OUT (HCYLHI),A
; Put into the CYLINDER HIGH register
Logical seek now done! Do the read.
LD H,B
; Put the buffer address from BC into HL. First H ..
LD BC,HDATA
; B = 0, C = Data Port
LD A,20H
; Read command itself
OUT (HCOMND),A
; Issue the read command
HDR1
IN A,(HSTAT)
; Get the status
RLA
; Put the busy into carry
IN A,(HSTAT)
; Get the status again
RRA
; Put the error into carry
RET NC
; Return if there is no error
HERROR
LD HL,HDERR
; Point at the error message
Data and constants.
WELCOM
DEFB 1CH
; Home Cursor
DEFM ‘Network 4 Model III Transporter ‘
DEFM ‘ROM Version 01.01.00’
DEFM ‘Initializing, please wait.’
DEFB 0DH
; Carriage return
DEFM ‘LOAD FILE FORMAT ERROR’
DEFB 0DH
; Carriage return
DEFB 0DH
; Carriage return
DEFB 0DH
; Carriage return
Data and constants – Transporter control block.
CMDS
DEFB 0F0H
; Receive Command
DEFB 4
; Response record is at 0400H
DEFB 4
; Record is up to 400H (1K)
DEFB 3
; Control length is 3
STAT
DEFB 0
; Disk status byte
DEFB 4
; 4 bytes of control data
SERV1
DEFB 3FH
; Send to server
RDCMD
DEFB 22H
; Read Command