Files
Digital-Research-Source-Code/CONTRIBUTIONS/cpm-handbook/cpmsrc/FIG7-7.ASM
Sepp J Morris 31738079c4 Upload
Digital Research
2020-11-06 18:50:37 +01:00

272 lines
7.4 KiB
NASM
Raw 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.

; Figure 7-7.
;
; Example CP/M Cold Bootstrap Loader
;
; This program is written out to Track 0, Head 0, Sector 1
; by the PUTCPMF5 program.
; It is loaded into memory at location 100H on up by the
; PROM-based bootstrap mechanism that gets control of the
; CPU on power-up or system reset.
;
Version EQU '01' ;Equates used in the sign on message
Month EQU '07'
Day EQU '24'
Year EQU '82'
;
Debug EQU 0 ;Set Non-zero to debug as normal
; transient program
;
; The actual layout of the diskette is as follows :
;
; Track 0 Sector
; 1 2 3 4 5 6 7 8 9
; Head +-----+-----+-----+-----+-----+-----+-----+-----+-----+
; 0 |Boot |<======== CCP ========>|<======= BDOS ========|
; +-----+-----+-----+-----+-----+-----+-----+-----+-----+
; 1 |====== BDOS ====>|<============= BIOS ============>|
; +-----+-----+-----+-----+-----+-----+-----+-----+-----+
; 10 11 12 13 14 15 16 17 18
; Sector
;
; Equates for defining memory size and the base address and
; length of the system components.
;
Memory$Size EQU 64 ;Number of Kbytes of RAM
;
; The BIOS Length must match that declared in the BIOS.
;
BIOS$Length EQU 0900H
;
CCP$Length EQU 0800H ;Constant
BDOS$Length EQU 0E00H ;Constant
;
Length$In$K EQU ((CCP$Length + BDOS$Length + BIOS$Length) / 1024) + 1
Length$In$Bytes EQU CCP$Length + BDOS$Length + BIOS$Length
;
IF NOT Debug
CCP$Entry EQU (Memory$Size - Length$In$K) * 1024
ENDIF
IF Debug
CCP$Entry EQU 3980H ;Read into a lower address.
;This address is chosen to be above
; the area into which DDT initially loads
; and the 980H makes the addresses similar
; to the SYSGEN values so that the memory
; image can be checked with DDT.
ENDIF
BDOS$Entry EQU CCP$Entry + CCP$Length + 6
BIOS$Entry EQU CCP$Entry + CCP$Length + BDOS$Length
;
;
; Disk Characteristics
;
; These equates describe the physical characteristics of
; the floppy diskette so that the program can move from
; one sector to the next, updating the track and resetting
; the sector when necessary.
;
First$Sector$on$Track EQU 1
Last$Sector$on$Track EQU 18
Last$Sector$on$Head$0 EQU 9
Sector$Size EQU 512
;
;
; Controller Characteristics
;
; On this computer system, the floppy disk controller can read
; multiple sectors in a single command. However, in order to
; to produce a more general example it is shown only reading one
; sector at a time.
;
Sectors$Per$Read EQU 1
;
;
; Cold Boot Characteristics
;
Start$Track EQU 0 ;Initial values for CP/M image
Start$Sector EQU 2 ;= " =
Sectors$To$Read EQU (Length$In$Bytes + Sector$Size - 1) / Sector$Size
;
;
;
ORG 100H
Cold$Boot$Loader:
JMP Main$Code ;Enter main code body
;For reasons of clarity, the main
; data structures are shown before the
; executable code.
CR EQU 0DH ;Carriage Return
LF EQU 0AH ;Line Feed
;
Signon$Message:
DB CR,LF,'CP/M Bootstrap Loader'
IF Debug
DB ' (Debug)'
ENDIF
DB CR,LF
DB 'Version '
DW Version
DB ' '
DW Month
DB '/'
DW Day
DB '/'
DW Year
DB CR,LF,0
;
; Disk Control Tables
;
Disk$Control$5 EQU 45H ;5 1/4" Control Byte
Command$Block$5 EQU 46H ;Control Table Pointer
Disk$Status EQU 43H ;Completion status
;
;
; The command table Track and DMA$Address can also be used
; as working storage and updated as the load process
; continues. The Sector in the command table cannot be
; used directly as the disk controller requires it to be
; the sector number on the specified head (1 - 9) rather
; than the sector number on track. Hence a separate variable
; must be used.
;
Sector: DB Start$Sector
;
Command$Table: DB 01H ;Command - Read
Unit: DB 0 ;Unit (Drive) number = 0 or 1
Head: DB 0 ;Head number = 0 or 1
Track: DB Start$Track ;Used as working variable
Sector$on$head: DB 0 ;Converted by low-level driver
Byte$Count: DW Sector$Size * Sectors$Per$Read
DMA$Address: DW CCP$Entry
Next$Status: DW Disk$Status ;Pointer to next Status Block
; if commands are chained.
Next$Control: DW Disk$Control$5 ;Pointer to next Control Byte
; if commands are chained.
Main$Code:
LXI SP,Cold$Boot$Loader ;Stack grows down below code
LXI H,Signon$Message ;Sign on
CALL Display$Message
LXI H,Command$Table ;Point the disk controller at
SHLD Command$Block$5 ; the command block
MVI C,Sectors$To$Read ;Set sector count
Load$Loop:
CALL Cold$Boot$Read ;Read data into memory
DCR C ;Downdate sector count
IF NOT Debug
JZ BIOS$Entry ;Enter BIOS when load done
ENDIF
IF Debug
JZ 0 ;Warm Boot
ENDIF
LXI H,Sector ;Update sector number
MVI A,Sectors$Per$Read ; by adding on number of sectors
ADD M ; by controller
MOV M,A ;Save result
MVI A,Last$Sector$On$Track + 1 ;Check if at end of track
CMP M
JNZ Not$End$Track
MVI M,First$Sector$On$Track ;Yes, reset to beginning
LHLD Track ;Update Track number
INX H
SHLD Track
Not$End$Track:
LHLD DMA$Address ;Update DMA Address
LXI D,Sector$Size * Sectors$Per$Read
DAD D
SHLD DMA$Address
JMP Load$Loop ;Read next block
;
Cold$Boot$Read: ;At this point, the description of the
; operation required is in the variables
; contained in the Command Table, along
; with the Sector variable.
PUSH B ;Save sector count in C
;------ Change this routine to match the disk controller in use ------
MVI B,0 ;Assume head 0
LDA Sector ;Get requested sector
MOV C,A ;Take a copy of it
CPI Last$Sector$on$Head$0+1 ;Check if on head 1
JC Head$0 ;No
SUI Last$Sector$on$Head$0 ;Bias down for head 1
MOV C,A ;Save copy
INR B ;Set head 1
Head$0:
MOV A,B ;Get head
STA Head
MOV A,C ;Get sector
STA Sector$On$Head
LXI H,Disk$Control$5 ;Activate controller
MVI M,80H
Wait$For$Boot$Complete:
MOV A,M ;Get status byte
ORA A ;Check if complete
JNZ Wait$For$Boot$Complete ;No
;Yes, check for errors
LDA Disk$Status
CPI 80H
JC Cold$Boot$Error ;Yes, an error occurred
;------ End of Physical Read routine ------
POP B ;Recover sector count in C
RET
;
Cold$Boot$Error:
LXI H,Cold$Boot$Error$Message
CALL Display$Message ;Output error message
JMP Main$Code ;Restart the loader
;
Cold$Boot$Error$Message:
DB CR,LF,'Bootstrap Loader Error - retrying...',CR,LF,0
;
; Equates for Terminal Output
;
Terminal$Status$Port EQU 01H
Terminal$Data$Port EQU 02H
;
Terminal$Output$Ready EQU 0000$0001B
;
;
Display$Message: ;Displays the specified message on the console.
;On entry, HL points to a stream of bytes to be
;output. A 00H-byte terminates the message.
MOV A,M ;Get next message byte
ORA A ;Check if terminator
RZ ;Yes, return to caller
MOV C,A ;Prepare for output
Output$Not$Ready:
IN Terminal$Status$Port ;Check if ready for output
ANI Terminal$Output$Ready
JZ Output$Not$Ready ;No, wait
MOV A,C ;Get Data character
OUT Terminal$Data$Port ;Output to Screen
INX H ;Move to next byte of message
JMP Display$Message ;Loop until complete message output
;The PROM-based bootstrap loader checks
; to see that the characters "CP/M"
; are on the diskette bootstrap sector
; before it transfers control to it.
ORG 2E0H
DB 'CP/M'
END Cold$Boot$Loader