Files
Sepp J Morris 31738079c4 Upload
Digital Research
2020-11-06 18:50:37 +01:00

1308 lines
35 KiB
Plaintext
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.

title 'CCP/M-86 Loader Program and BIOS'
;*****************************************************
; The Loader consists of the three modules:
; Loader BDOS (the file LBDOS.H86), the Loader
; Program and the Loader BIOS. This
; module contains both the Loader Program
; and the Loader BIOS.
;
; The Loader resides in sectors 2-8 on the first track
; of an IBM PC floppy diskette. The Loader is
; is brought into memory by the Boot Sector which
; resides in sector 1 of track 0. The Boot Sector
; is brought into memory by the IBM PC's ROM
; monitor.
;
; The Loader Program opens the file 'CCPM.SYS' using the
; Loader BDOS and Loader BIOS,
; and then reads it into memory. The DS register is set
; to the start of the CCPM DATA area, and a JMPF to the
; first byte of the CCPM code is executed.
;
; The first 128 byte record of the CCPM.SYS file is a header
; with the following format:
;
; +----+----+----+----+----+----+----+----+----+
; |TYPE| LEN | ABS | MIN | MAX |
; +----+----+----+----+----+----+----+----+----+
;
; type rb 1 ;seg type
; len rw 1 ;length
; abs dw 1 ;absolute segment address for LOADER
; min rw 1 ;minimum mem
; max rw 1 ;max mem needed
;
; The code is expected first and then the data within CCPM.SYS
; This header record is constructed automatically by the
; GENCCPM utility. See the variables declared at 'SEC1:'
; where the first sector of CCPM.SYS will be read.
;
; Loader may be read into any segment by the Boot
; Sector that does not overlap the area of memory
; the system image in CCPM.SYS will occupy.
;
;*****************************************************
false equ 0
true equ not false
cr equ 0dh
lf equ 0ah
ldbdos_offset equ 0000H ;offset of Loader BDOS
ldr_offset equ 0900h ;offset of Loader BIOS
codetype equ 1 ; code type CMD header
datatype equ 2 ; data type CMD header
; bdos function numbers
seldskf equ 14
openf equ 15
readsf equ 20
setdmaf equ 26
setuserf equ 32
setmultcntf equ 44
setdmabf equ 51
;*****************************************************
;*
;* CCPMLDR starts here
;*
;*****************************************************
cseg
org ldr_offset
jmp init
jmp entry
jmp loadp
ldusr db 0 ;load user is 0
loadp: ; loader entry from BDOS init
;-----
; entry: CH = boot user number
; CL = boot disk number
; DS,ES,SS = CS
mov dx,offset signon
call msg
mov dl,ldusr
mov cl,setuserf ! int 224 ;set user number
mov dx,offset ccpmfcb
mov cl,openf ! int 224 ;open CCPM.SYS file
cmp al,255 ! jne perr ;insure good file
mov dx,offset nofile
jmp stop
perr:
mov dx,ds
mov cl,setdmabf ! int 224 ;set DMA segment address
mov dx,offset sec1
mov cl,setdmaf ! int 224 ;set DMA offset address
mov dl,1
mov cl,setmultcntf ! int 224 ;set Multi-sector count to 1
mov dx,offset ccpmfcb
mov cl,readsf ! int 224 ; read first record
; the following
; "commented out" code
; can be used to
; perform error checking
; cmp ctype,codetype ; code should be first
; jnz badhdr
; cmp dtype,datatype ; then data
; jnz badhdr
; mov ax,cldseg ; code abs + code length
; add ax,clen ; should be = to data abs
; cmp ax,dldseg ! jnz badhdr
; add ax,dlen ! cmp ax,cldseg ; check for wrap around
; ja hdrok
;badhdr:
; mov dx,offset rerr
; jmp stop
;hdrok:
mov dx,offset csegment
call msg ; put memory map on console
mov ax,cldseg
call phex ; print base code segment
mov dx,offset dsegment
call msg ; print base data segment
mov ax,dldseg
call phex
mov dx,128
mov cl,setmultcntf ! int 224 ; set multi-sector count to 128
mov dx,0
mov cl,setdmaf ! int 224 ; set DMA offset to 0
mov dx,cldseg ; initial DMA segment
readit1:
push dx ; save dma segment
mov cl,setdmabf ! int 224 ; set DMA segment for disk IO
mov dx,offset ccpmfcb
mov cl,readsf ! int 224 ; next 128 sector read
pop dx ; restore dma segment
add dx,8*128 ; increment dma segment
cmp al,01H ! je done ; check for EOF
cmp al,0 ! je readit1 ; check for good write
mov dx,offset rerr ; print READ ERROR message
jmp stop
done:
call pcrlf ; and a crlf
mov clen,0 ; set CCPM offset to 0
mov ds,dldseg ; CCP/M data segment
jmpf cs:dword ptr clen ; leap to CCPM initialization
;*****************************
;*
;* subroutines
;*
;*****************************
stop:
;----
call msg
cli ! hlt
phex: ;print 4 hex characters from ax
;----
mov cx,0404h ; 4 in both CH and CL
lhex:
rol ax,cl ; rotate left 4
push cx ! push ax ; save crucial registers
call pnib ; print hex nibble
pop ax ! pop cx ; restore registers
dec ch ! jnz lhex ; and loop four times
ret
pnib: ;print low nibble in AL as hex char
and al,0fh ! cmp al,9
ja p10 ;above 9 ?
add al,'0' ;digit
jmps prn
p10: add al,'A'-10 ;char a-e
prn: mov dl,al
putchar:
;-------
mov cl,dl
jmp io_conout
pcrlf:
;-----
mov dx,offset crlf ;print carriage return, line feed
msg: ;print msg starting at dx until $
;---
mov bx,dx
msg1:
mov dl,[bx]
cmp dl,'$' ! je msg2
push bx
call putchar
pop bx
inc bx
jmps msg1
msg2:
ret
eject
;************************************************************************
;*
;*
;* L O A D E R
;*
;* B I O S - 8 6
;* =============
;*
;* CP/M-86 3.0 or Concurrent CP/M-86 2.0
;*
;* Boot Loader I/O System
;* for the
;* IBM Personal Computer
;*
;* Copyright (c) 1982
;* Digital Research, Inc.
;* box 579, Pacific Grove
;* California, 93950
;*
;* (Permission is hereby granted to use or
;* abstract the following program in the
;* implementation of Concurrent CP/M-86,
;* CP/M, MP/M or CP/Net for the 8086 or 8088
;* micro-processor.)
;*
;************************************************************************
;*
;* Register usage for BIOS interface routines:
;*
;* Entry: AL = function # (in entry)
;* CX = entry parameter
;* DX = entry parameter
;* DS = LDBDOS data segment
;*
;* Exit: AX = return
;* BX = AX (in exit)
;* ALL SEGMENT REGISTERS PRESERVED:
;* CS,DS,ES,SS must be preserved though call
;*
;* Notes: flag set and the far jump to the dispatcher are
;* the only legal Operating System "entry" points
;* for an interrupt routine)
;*
;* changes have been made in the
;* register conventions from
;* the CP/M-86 BIOS and the MP/M-86 BIOS.
;*
;************************************************************************
;************************************************************************
;* *
;* IBM PC SOFTWARE INTERRUPT STRUCTURE *
;* *
;************************************************************************
tick_interrupt equ 08h
keyboard_interrupt equ 09h
disk_interrupt equ 0Eh
os_interrupt equ 224 ;Loader BDOS entry
debugger_interrupt equ 225 ;debugger entry to O.S.
;************************************************************************
;* *
;* INTERFACE TO LOADER BDOS *
;* *
;************************************************************************
;=====
;=====
entry: ;arrive here from JMP at
;===== ;03H in BIOS code segment
;=====
; entry: AL = function number
; CX, DX parameters
; exit: AX = BX = return
; ALL SEGMENT REGISTERS PRESERVED:
; CS,DS,ES,SS must be preserved though call
; Note: no alteration of stack is allowed during entry except
; for the return address caused by the "call function_table[bx]"
; instruction.
cld ;set the direction flag
xor ah,ah
shl al,1 ;multiply by 2
mov bx,ax ;put in indirect register
call function_table[bx] ;no range checking needed
mov bx,ax ;only called by loader BDOS
retf ;return to loader BDOS
io_ret:
ret
;------
i_disk:
;------
mov al,pic_nseoi ;signal end of interrupt
out pic_even_port,al ;to 8259
iret ;note we destroyed AL
;=========
io_conout:
;=========
; entry: CL = character to output
; DL = device number
; exit: None
; ALL SEGMENT REGISTERS PRESERVED:
; CS,DS,ES,SS must be preserved though call
; Put character in screen and update cursor position
; BX = screen structure
mov bx,offset ss0
mov di,ss_cursor[bx] ;cursor offset in bytes
cmp cl,cr
je carriage_return
cmp cl,lf
je linefeed
push es
mov ax,bw_video_seg
mov es,ax ;segment of foreground screen
mov al,cl
mov ah,07h ;default attribute
stosw ;update and don't touch the attribute
pop es ;DI incremented by 2
cmp ss_column[bx],columns_per_screen - 1
jne inc_col
call carriage_return
jmp line_feed
inc_col:
inc ss_column[bx] ;DI = next data, attribute
mov ss_cursor[bx],di ;save new cursor position
ret
;---------------
carriage_return:
;---------------
; entry: BX = screen structure
; exit: BX preserved
xor dx,dx ;AX = 0
xchg dl,ss_column[bx] ;set cursor position to begining of
shl dx,1 ;times 2 for data and attribute
sub ss_cursor[bx],dx ;set new cursor position
ret
;---------
line_feed:
;---------
; entry: BX = screen structure
; exit: BX preserved
; This routine assumes the loader will never
; need to scroll the screen
inc ss_row[bx]
add ss_cursor[bx],columns_per_screen*2
ret
;************************************************************************
;* *
;* 6845 CRT CONTROLLER PORT AND COMMAND EQUATES *
;* *
;************************************************************************
; The IBM PC's monochrome memory mapped video display begins
; at paragraph 0B000H. It represents a screen 80 X 25.
; Each video character requires a word value, the low byte
; is the ASCII code (characters codes > 128 are also displayed)
; and the high byte is an attribute byte. The 25th line
; is reserved by this BIOS as a status line.
bw_card equ 003b4h
video_on equ 00029h
video_off equ 00021h
cursor_start equ 10
cursor_end equ 11
display_start_hi equ 12
display_start_low equ 13
cursor_hi equ 14
cursor_low equ 15
light_pen_hi equ 16
light_pen_low equ 17
;************************************************************************
;* *
;* SCREEN PARAMETERS *
;* *
;************************************************************************
rows_per_screen equ 24
columns_per_screen equ 80
screen_siz equ rows_per_screen * columns_per_screen
;in words
bw_video_seg equ 0b000h ;segment address of
;start of video ram
bw_video_status_line equ screen_siz * 2
;byte offset of status line
;************************************************************************
;* *
;* SCREEN STRUCTURES *
;* *
;************************************************************************
; Each virtual console has a structure of the following
; format associated with it. (SS = Screen Structure)
; The data in this structure is dependent on the type of screen
; supported and any escape sequence handling in io_conout.
; Note: ss_cursor, ss_row, ss_column are relative to 0 and are
; word pointers, i.e., if ss_cursor is 1 then it refers to
; bytes 2 and 3 in the video RAM or a background screen's
; data area.
ss_cursor equ word ptr 0 ;points at data/attrib
ss_escape equ word ptr ss_cursor + word ;escape routine to return to
ss_screen_seg equ word ptr ss_escape + word ;data for screen image
ss_row equ byte ptr ss_screen_seg + word ;current row
ss_column equ byte ptr ss_row + byte ;current col
; DISK I/O
; --------
;************************************************************************
;* *
;* 8237 DIRECT MEMORY ACCESS CONTROLLER PORT AND COMMANDS *
;* *
;************************************************************************
dma_c0_address equ 000h ;8237 channel 0 address rw
dma_c0_count equ 001h ;8237 channel 0 transfer count rw
dma_c1_address equ 002h ;8237 channel 1 address rw
dma_c1_count equ 003h ;8237 channel 1 transfer count rw
dma_c2_address equ 004h ;8237 channel 2 address rw
dma_c2_count equ 005h ;8237 channel 2 transfer count rw
dma_c3_address equ 006h ;8237 channel 3 address rw
dma_c3_count equ 007h ;8237 channel 3 transfer count rw
dma_stat_reg equ 008h ;8237 status register ro
dma_cmd_reg equ dma_stat_reg ;8237 command register wo
dma_requ_reg equ dma_stat_reg+1 ;8237 software dma request wo
dma_bmsk_reg equ dma_stat_reg+2 ;8237 binary channel mask wo
dma_mode_reg equ dma_stat_reg+3 ;8237 mode register wo
dma_cbpf equ dma_stat_reg+4 ;8237 clear byte pointer f/f wo
dma_temp_reg equ dma_stat_reg+5 ;8237 temporary register ro
dma_clear equ dma_stat_reg+5 ;8237 master clear wo
dma_mask_reg equ dma_stat_reg+7 ;8237 linear channel mask wo
dma_page_c1 equ 080h ;a16 to a20 for channel 1
dma_page_fdc equ 081h ;a16 to a20 for channel 2
dma_page_c3 equ 082h ;a16 to a20 for channel 3
; The following labels define single mode, address increment
; auto-initialization disable, read or write using channel 2
dma_mode_write_fdc equ 01001010b
dma_mode_read_fdc equ 01000110b
dma_bmsk_fdc equ 00000010b ;binary channel mask for disk
; DMA channel assignments
;channel 0 dynamic memory refresh
;channel 1
;channel 2 floppy disk controller
;channel 3
;************************************************************************
;* *
;* FLOPPY DISK DRIVER EQUATES *
;* *
;************************************************************************
; The following equates are set to the size of a double density,
; single sided 5 & 1/4" floppy.
bytes_per_sector equ 512
sectors_per_track equ 8 ;1 to 8
bytes_per_track equ sectors_per_track * bytes_per_sector
tracks_per_disk equ 40 ;0 to 39
bytes_per_disk equ tracks_per_disk * bytes_per_track
; The following equates are for the INTEL 8272 Floppy Disk
; Controller.
fdc_stat equ 03f4h ;status port for the disk controller
fdc_data equ fdc_stat+1 ;data port for the disk controller
fdc_port equ 03f2h ;all bits clear on channel reset
;7 6 5 4 3 2 1 0
;| | | | | | \_/
;| | | | | | |
;| | | | | | drive select 00=a,01=b,10=c,11=d
;| | | | | fdc reset*
;| | | | int & dmarq enable
;d c b a motor on
fdc_on equ 00001100b ;mask to keep the 8272 unreset
fdc_no_motor equ 11111100b ;mask for no motors
fdc_read_cmd equ 01100110b ;mfm, skip deleted data, read
fdc_write_cmd equ 01000101b ;mfm, write
fdc_format_cmd equ 01001101b ;mfm, format
fdc_seek_cmd equ 00001111b ;seek
fdc_recal_cmd equ 00000111b ;home to track 0
fdc_si_cmd equ 00001000b ;sense interupt status
fdc_spec_cmd equ 00000011b ;specify
fdc_ready equ 10000000b ;mask for transfer ready
fdc_spec_1 equ 11001111b ;srt=0c, hd unload=0f first specify byte
fdc_spec_2 equ 00000011b ;hd load=1, mode=DMA second specify byte
f_bytes equ 2 ;magic number for 512 bytes per sector
f_sectors equ 8 ;sectors per track
f_gap equ 03ah ;magic number for format gap
f_filler equ 0e5h ;fill character
r_bytes equ 2 ;magic number for 512 bytes
r_sectors equ 8
r_gap equ 02ah
r_dtl equ 0ffh
; Equates for paramter passing for read and write from the
; BDOS.
; At the disk read and write function entries,
; all disk I/O parameters are on the stack.
; The stack at these entries appears as
; follows:
;
; +-------+-------+
; +14 | DRV | MCNT | Drive and Multi sector count
; +-------+-------+
; +12 | TRACK | Track number
; +-------+-------+
; +10 | SECTOR | Physical sector number
; +-------+-------+
; +8 | DMA_SEG | DMA segment
; +-------+-------+
; +6 | DMA_OFF | DMA offset
; +-------+-------+
; +4 | RET_SEG | BDOS return segment
; +-------+-------+
; +2 | RET_OFF | BDOS return offset
; +-------+-------+
; SP+0 | RET_ADR | Return address to BIOS ENTRY routine
; +-------+-------+
;
; These parameters may be indexed and modifided
; directly on the stack by the BIOS read and write rotines
; They will be removed by the BDOS when the BIOS completes
; the read/write function and returns to the BDOS.
drive equ byte ptr 14[bp]
mcnt equ byte ptr 15[bp]
track equ word ptr 12[bp]
sector equ word ptr 10[bp]
dma_seg equ word ptr 8[bp]
dma_off equ word ptr 6[bp]
; Some equtes in the Disk Parameter Header (DPH)
; and the Disk Parameter Block.
xlt equ 0 ;translation table offset in DPH
dpb equ 8 ;disk parameter block offset in DPH
spt equ 0 ;sectors per track offset in DPB
psh equ 15 ;physical shift factor offset in DPB
;************************************************************************
;* *
;* DISK DRIVER ROUTINES *
;* *
;************************************************************************
;=========
io_seldsk: ; Function 7: Select Disk
;=========
; entry: CL = disk to be selected
; DL = 00h if disk has not been previously selected
; = 01h if disk has been previously selected
; exit: AX = 0 if illegal disk
; = offset of DPH relative from
; BIOS Data Segment
; ALL SEGMENT REGISTERS PRESERVED:
; CS,DS,ES,SS must be preserved though call
xor ax,ax ;get ready for error(s)
cmp cl,0 ;is it a A: ?
jne sel_ret ;if not just exit
mov ax,offset dph0
sel_ret:
ret
;=======
io_read: ; Function 11: Read sector
;=======
; Reads the sector on the current disk, track and
; sector into the current DMA buffer.
; entry: parameters on stack
; exit: AL = 00 if no error occured
; AL = 01 if an error occured
; AL = 0ffh if density change detected
; ALL SEGMENT REGISTERS PRESERVED:
; CS,DS,ES,SS must be preserved though call
mov bp,sp ;set BP for indexing into IOPB
; mov dma_mode_storage,dma_mode_read_fdc
; mov fdc_rw_cmd,fdc_read_cmd ;loader always reads
call flp_io
mov al,1
jnz i_read_ret ;zero flag is set with successful
dec al ;I/O
i_read_ret:
ret
flp_io:
;------
; entry: parameters on stack
; exit: zero flag set if no error
mov ax,sectors_per_track;max sectors - starting sector =
sub ax,sector ;sectors left on track
xor bh,bh
mov bl,mcnt ;multi sector count is byte variable
cmp ax,bx ;sectors left on track, sectors requested
jbe flp_track ;transfer to end of track
mov al,mcnt ;transfer to before end of track
flp_track:
sub mcnt,al ;AL = # sectors to R/W on this iteration
;through FLP_IO,
mov track_mcnt,al ;sectors to R/W on current track
;check for 64K page overlap
mov ah,al ;shl al,8 (* 256)
xor al,al
shl ah,1 ;* 512
push ax ;how many bytes to R/W on cur trk
mov ax,dma_seg ;compute new 20 bit DMA addr
mov bx,dma_off
call comp_dma ;returns AX = low 16 bits of 20 bit adr
not ax ;how many bytes left in this 64K page
pop bx ;BX=bytes to R/W on current track
cmp ax,bx ;does this transfer fit in 64K page
jb flp_end_64K
jmp flp_pg_ok
flp_end_64K:
;read to end of 64K page and then
;read spanning sector locally
mov al,ah ;how many sectors fit in this page ?
xor ah,ah ;bytes left in 64K page
shr ax,1 ;divided by 512
test ax,ax ;if 0, no sectors fit
jz flp_rwlocal ;in the rest of this 64K page
mov num_sec,al ;sectors that fit in end 64K page
sub track_mcnt,al ;track_mcnt always > AL
call flp_phys_io ;read/write them, DMA already computed
jz flp_end64k_ok
jmp flp_ret ;return if zero reset on error
flp_end64K_ok:
xor ax,ax
mov al,num_sec ;compute new DMA offset
add sector,ax ;still on the same track
xchg ah,al ;shl ax,8 (* 256)
shl ah,1 ;* bytes_per_sector (512)
add dma_off,ax ;64K wrap around is legal
flp_rwlocal: ;read into BIOS data segment the spanning
mov bx,local_buf ;sector
mov ax,ds ;compute 20 bit local DMA addr
call comp_dma
; cmp fdc_rw_cmd,fdc_read_cmd ;loader always reads
; ;only need this for writes
; ;reading or writing ?
; je flp_local
; mov si,dma_off ;get the sector to write from local
; mov di,local_buf ;buffer
; push es ! push ds ! push ds ! pop es
; mov ax,dma_seg ! mov ds,ax
; mov cx,bytes_per_sector/2
; rep movsw
; pop ds ! pop es
; mov dma_off,si ;update DMA offset
;
;flp_local:
mov num_sec,1 ;read/write one sector
call flp_phys_io
jnz flp_ret ;return zero flag reset
inc sector ! dec track_mcnt
; cmp fdc_rw_cmd,fdc_read_cmd ;loader always reads
; jne flp_local_done
mov di,dma_off ;move the sector to user's area
mov si,local_buf ;if reading
push es ! mov es,dma_seg
mov cx,bytes_per_sector/2
rep movsw
mov dma_off,di ;update DMA offset
pop es
flp_local_done:
mov ax,dma_seg ;compute new 20 bit DMA addr
mov bx,dma_off ;for next FDC read/write
call comp_dma
flp_pg_ok: ;read will not cross 64K boundary
mov al,track_mcnt ;could be 0 if we just read locally
test al,al ! jz nxt_track
mov num_sec,al ;read the rest from this track
call flp_phys_io ;DMA is already computed
jnz flp_ret ;return zero flag reset on error
xor ax,ax
mov ah,num_sec ;shl num_sec,8 (* 256)
shl ah,1 ;* 512
add dma_off,ax
nxt_track:
xor al,al
cmp mcnt,al
jz flp_ret ;return successful with zero flag set
inc track
mov sector,0
jmp flp_io
flp_ret:
ret
comp_dma: ;Compute 20 bit address from offset, segment
;--------
; entry: AX = segment
; BX = offset
; exit: AX = low 16 bits
; CH = highest 4 bits of address, always less then 16 -
; no megabyte wrap around
;
; The BIOS variables DMA_LOW16 and DMA_HIGH4 are
; set by this routine. These variables are transferred
; to the floppy disk controller by the routine DMA_SET_UP.
mov cl,4 ! rol ax,cl ;make paragraphs into bytes
mov ch,al ! and al,0f0h ;save high 4 bits, 0 low 4 bits
add ax,bx ;add byte offset
adc ch,0 ! and ch,0fh ;add in the carry, page is less than
mov dma_low16,ax ;16
mov dma_high4,ch
ret
flp_phys_io:
;-----------
; entry: num_sec = number of sectors to read in this
; operation
; disk parameters on the stack
; exit: zero flag set if ok
; Perform physical actions to read/write floppy diskette
mov d_a_number,r_bytes
mov d_a_eot,r_sectors
mov d_a_gpl,r_gap
mov d_a_dtl,r_dtl
call motor_and_recal ;make sure motor is on
mov recals,max_recals
recal_loop:
mov retries,max_retries
retry_loop:
call seek
call dma_setup
call fdc_read_write
jz phys_ret ;if errors
dec retries ;attempt retries
jnz retry_loop
call recal
dec recals
jnz recal_loop
or al,1 ;reset zero flag
phys_ret: ;and return error
ret
motor_and_recal:
;---------------
; entry: none
; exit: none
; Turn on the motor if off. Also do RECAL operation if
; motor is off. Note: loader does not turn off the motor.
mov al,010h ;pick up a bit for motor a
mov cl,drive ;fetch the binary drive code
shl al,cl ;make it a bit for motor x
mov ah,motor_flags ;fetch the motor bits
test ah,al ;check to see if its on
jnz motor_on_done ;yes then leap
or al,fdc_on ;mask in the no reset,enable interupt
or al,drive ;mask in the drive
mov motor_flags,al ;save for later
mov dx,fdc_port ;point to the port number
out dx,al ;select & motor on
call recal ;back to track zero
motor_on_done: ;when we first turn on the motor
ret
recal:
;-----
; entry: none
; exit: none
; Move the head to home on the selected disk drive.
mov disk_arguments,2 ;specify number of arguments
mov d_a_command,fdc_recal_cmd
mov al,drive ;get current disk
mov d_a_drive,al ;set up the command block
call fdc_command_put ;go send the command block
hlt ;wait for FDC interrupt
jmp sense_interrupt ;required after RECAL command
;ret
seek:
;----
; entry: TRACK to seek to on the stack relative to BP
; exit: none
mov disk_arguments,3 ;request 3 byte command transfer
mov d_a_command,fdc_seek_cmd
xor bl,bl ;select head 0
mov ax,track ;get track off stack
cmp al,40 ;test for off side 1
jb side_ok ;if single sided then leap
mov bl,1 ;else get a head 1
mov ah,79 ;else get back track bias
sub ah,al ;compute new track number
xchg ah,al ;and get it into al
side_ok:
mov d_a_head,bl ;set up the head
add bl,bl ;multiply by 2
add bl,bl ;multiply by 4
mov d_a_cylinder,al ;set up the track number
or bl,drive ;bits 0,1 are drive number
mov d_a_drive,bl ;set up disk number
mov ax,sector ;get sector off stack
inc ax ;sectors are relative to 1
mov d_a_record,al ;and set it up
add al,num_sec ;compute new end of track
dec al
mov d_a_eot,al ;save it
call fdc_command_put ;send the command block
hlt ;wait for FDC interrupt
jmp sense_interrupt ;required after SEEK command
;ret
dma_setup:
;---------
; entry: DMA_MODE_STORAGE, DMA_LOW16, DMA_HIGH4 set up
; exit: none
; Set the DMA device up for a read/write operation.
; The current DMA command word must in DMA_MODE_STORAGE.
; DMA_LOW16 and DMA_HIGH4 are the twenty bit starting address.
; The read/write operation cannot cross a physical 64K boundary.
out dma_cbpf,al ;reset the byte pointer
mov al,dma_mode_storage ;get the mode byte
out dma_mode_reg,al ;set the mode
mov ax,dma_low16 ;low 16 bits of 20 bit DMA address
out dma_c2_address,al ;send low 8 bits
mov al,ah
out dma_c2_address,al ;send next 8 bits
mov al,dma_high4 ;high 4 bits of 20 bit DMA address
out dma_page_fdc,al
xor ax,ax
mov ah,num_sec ;shl num_sec,8 (* 256)
shl ah,1 ;*512
dec ax ;0 relative
out dma_c2_count,al ;set up the low byte
mov al,ah ;get the low byte
out dma_c2_count,al ;and the high byte
mov al,dma_bmsk_fdc ;get the binary channel mask
out dma_bmsk_reg,al ;enable the disk channel
ret
fdc_read_write:
;--------------
; entry: DMA device set up, head positioned on correct
; track
; exit: zero flag set if successful
; Send read or write command to 8272 FDC
mov disk_arguments,9 ;9 byte command for read or write
mov al,fdc_rw_cmd ;get read or write command
mov d_a_command,al ;put it in the command string
call fdc_command_put ;send the command to the FDC
hlt ;wait for FDC interrupt
mov disk_results,7 ;7 byte result transfer
call fdc_status_get ;get the result bytes
test d_r_st0,0C0H ;test status register 0
ret ;return zero flag set on success
sense_interrupt:
;---------------
; Sense interrupt command on the FDC. It is called
; after a recal or a seek to test for seek complete.
mov disk_arguments,1 ;only one byte of command
mov d_a_command,fdc_si_cmd ;sense interrupt commmand
call fdc_command_put ;send the command to the FDC
mov disk_results,2 ;2 bytes are returned
call fdc_status_get ;get the 2 result bytes
ret
fdc_command_put:
;---------------
; entry: DISK_ARGUMENT array set up
; exit: none
; Send the command block in the DISK_ARGUMENTS table to
; the 8272 FDC.
; The number of commands to write to the FDC is the
; first item in the table.
mov dx,fdc_stat ;point to the i/o port
mov si,offset disk_arguments ;point to the table of arguments
cld ;make sure we go forward
lodsb ;get the length of the arguments table
mov cl,al ;get it into the count register
sub ch,ch ;zero the high byte
fdc_command_loop:
in al,dx ;get the current control byte
test al,fdc_ready ;if not ok to send next byte
jz fdc_command_loop ;then loop waiting
inc dx ;point at the data port
lodsb ;else get the byte to send
out dx,al ;send it
dec dx ;point back at the status port
loop fdc_command_loop ;if not last byte then loop
ret ;else were all done
fdc_status_get:
;--------------
; entry: number of results in 1st byte of DISK_RESULTS array
; exit: none
; Get the status information from the 8272
; FDC and place them in the table at DISK_RESULTS.
; The first byte in the table is the number of results
; to read from the FDC
push es ;save UDA
mov dx,fdc_stat ;point at the status port
mov ax,ds ;get our data segment
mov es,ax ;into the extra segment
mov di,offset disk_results + 1 ;point to where to put the data
cld ;make sure we go forward
mov cl,disk_results ;fetch the number of expected results
sub ch,ch ;zero the high byte
fdc_status_loop:
in al,dx ;get the current control byte
test al,fdc_ready ;if not ok to read next byte
jz fdc_status_loop ;then loop waiting
inc dx ;point at the data port
in al,dx ;get the byte
stosb ;put it in the structure
dec dx ;point back at the status port
loop fdc_status_loop ;if not last then loop
pop es ;restore UDA
ret ;else return
; INIT ROUTINE
;************************************************************************
;* *
;* 8259 PROGRAMMABLE INTERRUPT CONTROLLER COMMANDS *
;* AND PORTS *
;* *
;************************************************************************
pic_even_port equ 020h ;port 0
pic_odd_port equ 021h ;bit 0 is A0 of 8259 instructions
pic_nseoi equ 020h ;non specific end of interupt
;************************************************************************
;* *
;* BIOS INITIALIZATION ROUTINE *
;* *
;************************************************************************
;track buffer
memory_size equ 12h ;ROM interrupt to get memory size
equipment equ 11h ;ROM interrupt to get equipment word
; The following routine is used to initialize any required
; data areas and alter any peripheral chip programming when
; starting up CCP/M-86. This code is called once from the
; SUP(ERVISOR) after calling the SUP has called the RTM,
; RTM, CIO, MEM, BDOS initialization routines and before the
; SUP has created the RSP processes.
; This code can be placed in an BIOS data area if the BIOS is
; 8080 model (mixed code and data). Usually, overlaying the
; initialization code with a data area is done after the BIOS
; has been debugged.
;====
;====
init: ;arrive here from the JMP
;==== ;at 0 in BIOS code segment
;====
cli ! cld ;Supervisor restores DS,ES and int
;224 after on INIT call
; Disk I/O initialization
set_up_interrupts:
mov al,10111101b ;enable diskette and keyboard
out pic_odd_port,al ;interrupts only
;and mask off the rest
; mov disk_arguments,3 ;send specify command to the
; mov d_a_command,fdc_spec_cmd ;FDC (Intel 8272 or NEC 765)
; mov d_a_drive,0d0h ;step rate=3ms, head unload=0ms
; mov d_a_cylinder,20h ;head load=2ms, DMA mode true
; call fdc_command_put ;head unload and head load times
;are meaningless on mini floppies
;where the head is loaded
;when the motor is turned on
mov ax,ds
add data_buf_seg,ax ;fix up data buffer segment
push es ! xor ax,ax
mov es,ax
mov di,disk_interrupt*4 ;point at vector location
mov ax,offset i_disk
stosw ;save and inc DI
mov ax,cs
stosw
pop es
set_up_video:
mov dx,bw_card ;get the video chip port
mov si,offset bw_table ;initialization commands
mov cx,length bw_table ;how many commands
call video_init ;send commands to port
;color board is similar ...
push es
mov di,bw_video_seg ;clear the video RAM
mov es,di
xor di,di
mov cx,screen_siz + columns_per_screen
;screen length in words plus
;status line
mov ax,0720h ;7 = default attribute
;20 = ASCII space
rep stosw
pop es
sti
retf ;initializaiton done
;----------
video_init:
;----------
xor bl,bl
video_init_l:
mov al,bl ! inc bl
out dx,al ! inc dx
lodsb ! out dx,al
dec dx !
loop video_init_l
add dx,4
mov al,video_on
out dx,al
ret
last_code_offset equ offset $
DSEG
ORG last_code_offset
; Loader BIOS data
bw_table db 61h,50h,52h,0fh,19h,06h,19h,19h,02h,0dh,0bh,0ch,0,0,0,0
function_table:
dw io_ret ; 0 console status
dw io_ret ; 1 console input
dw io_conout ; 2 console output
dw io_ret ; 3 list status
dw io_ret ; 4 list output
dw io_ret ; 5 auxillary input
dw io_ret ; 6 auxillary out
dw io_ret ; 7 switch screen
dw io_ret ; 8 update or print new status
dw io_seldsk ; 9 select disk
dw io_read ;10 read logical sector
dw io_ret ;11 write logical sector
dw io_ret ;12 flush buffers
dw io_ret ;13 poll device
;short screen structure
;for IO_CONOUT
ss0 dw 0,0,0 ;cursor, escape, screen_seg
db 0,0 ;row,column
; Floppy Disk Driver data
max_retries equ 3
retries db 0 ;retry counter
max_recals equ 5
recals db 0
; The following 2 tables are used to issue commands to,
; and read results from the 8272 FDC. The first entry
; in each table is the number of bytes to send or receive
; from the device
disk_arguments db 0 ;number of arguments to send
d_a_command db 0 ;command read/write
d_a_drive db 0 ;drive select & head select
d_a_cylinder db 0 ;cylinder to read/write
d_a_head db 0 ;head
d_a_record db 0 ;sector
d_a_number db 2 ;magic number for 512 bytes/sector
d_a_eot db sectors_per_track ;end of track sector number
d_a_gpl db 02ah ;inter sector gap length
d_a_dtl db 0ffh ;data length
disk_results db 0 ;number of bytes to read
d_r_st0 db 0 ;status byte 0
d_r_st1 db 0 ;status byte 1
d_r_st2 db 0 ;status byte 2
d_r_cylinder db 0 ;cylinder we are on now
d_r_head db 0
d_r_record db 0
d_r_number db 0 ;number of sectors read
f_a_bytes db 0
f_a_sectors db 0
f_a_gap db 0
f_a_filler db 0
fdc_rw_cmd db fdc_read_cmd ;read or write command
dma_mode_storage db dma_mode_read_fdc
num_sec db 1 ;num sectors to read/write
;in one call to controller
track_mcnt rb 1 ;multi sector count on
;current track
dma_low16 rw 1 ;20 bit address storage
dma_high4 rb 1
motor_flags db 0 ;last state of the motor bits
; Disk Parameter Header
dph0 dw 0 ;translate table
dw 0,0,0 ;scratch area
dw dpb0 ;dsk parm block
dw 0 ;check
dw 0 ;alloc vectors
dw dir_bcb_hdr ;dir buff cntrl blk
dw data_bcb_hdr ;data buff cntrl blk
dw 0 ;hash table segment
; Disk Parameter Block
dpb0 dw 8 ;sectors per track
db 3 ;block shift
db 7 ;block mask
db 0 ;extnt mask
dw 155 ;disk size in 1k blocks
;less offset track(s)
dw 63 ;directory max
db 11000000b ;alloc0
db 0 ;alloc1
dw 16 ;check size
dw 1 ;offset
db 2 ;phys sec shift
db 3 ;phys sec mask
dir_bcb_hdr dw dir_bcb
db 1
dir_bcb db 0ffh ;drive
db 0,0,0 ;record
db 0,0 ;wflg,seg
dw 0,0 ;track,sector
dir_buf_off dw dir_buf ;buffer offset
dw 0,0 ;link,pdadr
data_bcb_hdr dw data_bcb
db 1
data_bcb db 0ffh ;drive
db 0,0,0 ;record
db 0,0 ;wflg,seg
dw 0,0 ;track,sector
data_buf_seg dw data_buf;buffer segment, fixed up by LBIOS init:
dw 0,0 ;link,pdadr
; Loader Program data
signon db 'CCP/M-86 Loader 2.0$'
nofile db cr,lf,'No CCPM.SYS File On Disk$'
rerr db cr,lf,'Error Reading CCPM.SYS$'
csegment db cr,lf,'Code Paragraph = $'
dsegment db cr,lf,'Data Paragraph = $'
crlf db cr,lf,'$'
ccpmfcb db 1,'CCPM ','SYS',0,0,0,0
db 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
db 0
; Note the Loader Program and Loader BIOS uninitialized
; data areas are combined to save space.
; Unitialized data for Loader Program
;read first CMD header of
;CCPM.SYS file here
ctype rb 1 ;type
clen rw 1 ;length
cldseg rw 1 ;abs
cmin rw 1 ;minimum
cmax rw 1 ;maximum
dtype rb 1
dlen rw 1
dldseg rw 1
dmin rw 1
dmax rw 1
org offset ctype
sec1 rb 128 ;reserve space for the sector
; Unitialized data for Loader BIOS
dir_buf equ ((offset $) + 15) and 0fff0h
; Sector for reads that span 64K boundaries;
; this sector can not cross a 64K boundary
local_buf equ dir_buf + 512
data_buf equ (local_buf + 512)/16