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

1317 lines
35 KiB
Plaintext

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