Digital Research
This commit is contained in:
2020-11-06 18:50:37 +01:00
parent 621ed8ccaf
commit 31738079c4
8481 changed files with 1888323 additions and 0 deletions

View File

@@ -0,0 +1,51 @@
TITLE '** CCP/M-86 XIOS for IBM PC **'
;*****************************************************************
;*
;*
;* X I O S
;* ============
;*
;* Concurrent CP/M-86 eXtended I/O System
;* for the
;* IBM Personal Computer
;*
;* Copyright (C) 1983, Digital Research, Inc.
;* Box 579, Pacific Grove, California 93950
;*
;*
;* Dec 13, 1983 adapted by: Dean Ballard
;*
;* Ctrl/Alt/Del left alone, 0D000 MDisk works
;* XIOS does not trap in extended error modes
;* FIDDS hooks in, but are they really usable
;* Interrupt init to handle div, ovf, and unx
;* HDMAINT now modified for CCP/M version 3.1
;* Disk & Printer error handlers now included
;* Graphics support via Function 30 and ESC a
;* Setup Interface and Full Top special cases
;* Queue Driven Serial Communication included
;* Modified for version 3.1 CON and RSP files
;* Prog Func Keys and mono/color Status Lines
;* Double sided floppy disks and XT hard disk
;* Color monitor with windowing now supported
;* Backdoor window control entry points added
;*
;*************************************************
;
;
include xbegin.lib ; header, entry, patch_space, ints
include xchar.lib ; conin, status_line, list, ccb/lcb's
include xconout.lib ; console out only, including windows
include xfloppy.lib ; floppy, mdisk, disk error handling
include xhard.lib ; XT hard disk, FIDDS
include xinit.lib ; startup, hardware init, int vectors
end


View File

@@ -0,0 +1,822 @@
; *** SERIN.RSP ***
; A Concurrent CP/M-86
; Resident System Process for
; Queue Driven Serial Character Input
; Oct 19, 1983 Dean Ballard
; This program reads characters from a circular buffer and
; writes them to the serial input queue for consumption by
; an application program. The characters are put into the
; buffer by an interrupt routine which is contained within
; this module. The interrupt routine also serves to alert
; the serial output routine when it is waiting on transmit
; buffer empty.
; First the hardware independent equate values.
; These have to do with the queue management, and generally
; pertain to the application program side of things.
q_mes_size equ 17 ; byte count plus 16 characters
in_q_num equ 4 ; number of in queue messages
out_q_num equ 2 ; number of out queue messages
c_buf_size equ 300 ; receive int circular buffer
stack_size equ 64 ; for interrupt routine
bdos equ 0E0h ; bdos interrupt number
c_detach equ 93h ; detach console
dev_flag_wait equ 84h ; wait for a flag
dev_flag_set equ 85h ; release a flag
p_delay equ 8Dh ; delay dx ticks
p_priority equ 91h ; set process priority
p_term equ 8Fh ; terminate a process
q_make equ 86h ; create queue code
q_open equ 87h ; open queue code
q_read equ 89h ; read queue code
q_write equ 8Bh ; write queue code
s_sysdat equ 9Ah ; get system data seg
; RSP data origin addresses
rsp_header equ 00h ; start of data segment
rsp_pd equ 10h ; process descriptor offset
rsp_uda equ 40h ; user data area offset
rsp_stack_top equ 13Ah ; at the end of uda
rsp_data_end equ 140h ; end of rsp stuff
; protocol constants
dtr_dsr equ 01h ; bit for dsr/dtr protocol
rts_cts equ 02h ; bit for rts/cts protocol
xon_xoff equ 04h ; bit for xon/xoff protocol
xon equ 11h ; start transmission
xoff equ 13h ; stop transmission
; system data page address pointers
dseg
org 00h
sup_o rw 1 ; supervisor offset
sup_s rw 1 ; supervisor segment
org 38h
disp_o rw 1 ; dispatcher offset
disp_s rw 1 ; dispatcher segment
; Now the hardware specific equates
; These have to do with the I/O port and interrupt management,
; and are, for the most part, specific to the IBM PC
; async port bit patterns
dr equ 01h ; data ready
tbe equ 20h ; xmit buf empty
r_t_mask equ 03h ; rec/trans int en
rec_int equ 04h ; from int id reg
tran_int equ 02h ; from int id reg
no_int equ 01h ; no hardware interrupt
dsr_bit equ 20h ; from modem status
cts_bit equ 10h ; from modem status
rts_bit equ 02h ; also with int en
dtr_bit equ 01h ; to modem control
i_en equ 08h ; interrupt enable
; 8259 programmable interrupt ports and patterns
pic_ack equ 20h ; 8259 acknowledge address
pic_mask equ pic_ack + 1 ; 8259 int mask address
ns_eoi equ 20h ; non specific end of int
; **************************
; *** Code begins here ***
; **************************
cseg
org 00h ; small model
serial_in:
call which_port ; are we port 0 or port 1 ?
call is_it_there ; if the port is not present
jnz terminate_in ; then terminate the process
call get_sys_data ; read supervisor, dispatcher
call make_queues ; input, output, and mx queues
call open_queues ; open all queues
call write_mx_queue ; set up the mx queue
call write_t_block ; send address to serial out
call install_int ; prepare for interrupts
in_loop:
call read_c_buf ; get char(s) from buffer
call pause ; wait if there's no hurry
call write_q ; write message to queue
jmps in_loop ; go forever
terminate_in:
mov pd_flag,0 ; turn off the keep flag
mov cl,p_term
mov dl,0
int bdos ; back to bdos
; ***************************
; *** Setup Subroutines ***
; ***************************
; see which port we are, and adjust params if necessary
which_port:
mov al,ncp ; get copy number
cmp al,0 ; if port = 0
jz which_port_done ; then leave params alone
push ds
pop es ; local move
mov si,offset qn_list ; list of queue names
mov cx,qn_list_size ; count of names to change
add al,'0' ; change port number to ascii
qn_list_loop:
mov di,[si]
stosb ; change to '1'
inc si ! inc si
loop qn_list_loop
mov si,offset port1_params ; now adjust parameters
mov di,offset port0_params
mov cx,port_param_size
cld
rep movsb ; copy port1 over port0
which_port_done:
ret
; check to see if the port is present
; exit: zf set if port is present
is_it_there:
mov dx,port_int_id ; interrupt ident port
in al,dx
test al,0F8h ; should be zero if present
ret
; fetch pointers to supervisor and dispatcher entries
get_sys_data:
mov cl,s_sysdat
int bdos ; es:bx points to sysdat
mov sys_data_s,es ; save system data segment
mov ax,es:sup_o[bx]
mov i_sup_o,ax ; save supervisor offset
mov ax,es:sup_s[bx]
mov i_sup_s,ax ; save supervisor segment
mov ax,es:disp_o[bx]
mov i_disp_o,ax ; save dispatcher offset
mov ax,es:disp_s[bx]
mov i_disp_s,ax ; save dispatcher segment
ret
; create input, output, and mx queues
make_queues:
mov dx,offset qd_in
call make_one ; make the input queue
mov dx,offset qd_out
call make_one ; make the output queue
mov dx,offset qd_mx ; and the mx queue too
make_one:
mov cl,q_make
int bdos ; make it
ret
; open all three queues
open_queues:
mov dx,offset qpb_in
call open_one ; open the input queue
mov dx,offset qpb_out
call open_one ; open the output queue
mov dx,offset qpb_mx ; and the mx queue too
open_one:
mov cl,q_open
int bdos ; open it
ret
; perform the initial write to the mx queue
write_mx_queue:
mov cl,q_write
mov dx,offset qpb_mx
int bdos ; port is now available
ret
; write the address of the shared data block to the out queue
; to enable the Serial Out process access
write_t_block:
mov t_block_s,ds ; our data segment
mov cl,q_write ; offset is already there
mov dx,offset qpb_out
int bdos ; start up the out routine
ret
; set up interrupt vector and hardware
install_int:
mov di,ser_ds_ptr ; point to the place to
mov cs:[di],ds ; store our data segment
; store it in the code seg
sub ax,ax
mov es,ax ; base page extra segment
mov di,ser_int_vect ; vector destination
mov ax,ser_int_entry ; for port 0 or port 1
stosw ; store offset
mov ax,cs
stosw ; store segment
in al,pic_mask ; now program the 8259
and al,ser_pic_mask ; to allow this interrupt
out pic_mask,al
mov dx,port_int_en ; which comm ints to use
mov al,r_t_mask ; allow receive and transmit
out dx,al
mov dx,port_modem_ctrl
mov al,dtr_bit + rts_bit + i_en
out dx,al ; set up modem control
mov dx,port_int_id
in al,dx ; clear a pending xmit int
ret ; ready to go
; **************************
; *** Loop Subroutines ***
; **************************
; read characters from the circular buffer
read_c_buf:
cli ;; critical section
mov si,buf_out_ptr ;; for reading
mov ax,buf_in_ptr ;; used by int routine
cmp ax,si ;; if they're different
jnz read_now ;; then something's there
mov r_wait,0FFh ;; if not, then wait
sti ; interrupts are ok
call rec_prot_on ; enable handshakes
mov cl,dev_flag_wait
mov dl,r_flag
int bdos ; wait for c_buf to fill
jmps read_c_buf ; go back and set it up
read_now:
sti
push ds
pop es ; local extra seg
mov cx,q_mes_size - 1 ; max number of chars
mov di,offset msg_in + 1 ; start of char message
read_loop:
movsb ; from c_buf to message
cmp si,c_buf_end ; check for wrap around
jb read_no_wrap
mov si,offset c_buf
read_no_wrap:
cmp ax,si ; are there more chars?
loopnz read_loop ; if so, loop up to max
mov buf_out_ptr,si ; update the pointer
mov al,q_mes_size - 1
sub al,cl ; al = char count
mov msg_in,al ; store in the message
ret
; pause to fill the buffer a bit
; this maximizes the use of full queue messages
; and frees up the processor for other tasks
pause:
cmp msg_in,q_mes_size - 1 ; if last message was full
jz pause_done ; then don't wait
mov cl,p_delay
mov dx,1 ; wait at least 1 tick
int bdos
pause_done:
ret
; write one message to the queue
write_q:
mov cl,q_write
mov dx,offset qpb_in ; input queue param block
int bdos ; wait here if not ready
ret ; return when ready to send
; receive protocol handler
rec_prot_on:
mov bx,offset r_on_prot ; receive on data
mov al,0FFh ; state = true
xchg al,r_prot_state ; test and set state
test al,al ; if protocol if off now
jz receive_prot ; then turn it on
ret ; else just return
rec_prot_off:
mov bx,offset r_off_prot ; receive stop data
mov r_prot_state,0 ; protocol is off now
; jmps receive_prot
receive_prot:
mov al,rec_prot_code ; code for which protocol
test al,al ; if none being used
jz rec_prot_done ; then just skip it
and al,03h ; if not hardware handshake
jz rec_prot_x ; then do xon/xoff
xlat bx ; turn code into control bits
mov dx,port_modem_ctrl ; to control dtr, rts
out dx,al ; set modem lines
jmps rec_prot_done
rec_prot_x:
mov al,[bx]
mov send_x,al ; either xon or xoff
cmp al,xon
jnz rec_prot_done ; if we're sending an xon
call fake_int ; then fake an interrupt
; to kick it off
rec_prot_done:
ret
; Interrupt entries to kick off an xon transmit
fake_int0:
int 12 ; port 0 interrupt
ret
fake_int1:
int 11 ; port 1 interrupt
ret
serin_code_top equ offset $
; *****************************
; *** RSP header, pd, uda ***
; *****************************
dseg
org rsp_header ; header start
dw 0,0
ncp db 1,0 ; one copy
dw 0,0,0
dw 0,0
org rsp_pd ; process descriptor
dw 0,0 ; link, thread
db 0 ; ready to run
db 180 ; priority better than PIN's
pd_flag dw 2 ; process flag "keep"
db 'SerIn ' ; process name
dw rsp_uda/10h ; uda segment
dw 0,0 ; disk, user, reserved
dw 1 ; for shared code
dw 0,0,0 ; and a mess of zeros
dw 0,0,0
dw 0,0,0
dw 0,0,0
org rsp_uda ; user data area
dw 0,0,0,0,0,0 ; no dma buffer
dw 0,0,0,0,0,0
dw 0,0,0,0,0,0
dw 0,0,0,0,0,0
dw 0,0,rsp_stack_top,0,0,0
dw 0,0,0,0,0,0
dw 0,0,0,0,0,0
dw 0,0,0,0,0,0
db 1,0,0,0,0,0 ; don't switch from UDA
; stack at SUP entry
org rsp_stack_top
dw offset serial_in ; code start
dw 0 ; code seg (genccpm)
dw 0 ; flags (genccpm)
; ******************************
; *** Our data begins here ***
; ******************************
org rsp_data_end ; above the rsp stuff
; first we have parameters for port 0
port0_params rb 0
p0_base equ 03F8h ; port base address
port_data dw p0_base ; rw data
port_int_en dw p0_base + 1 ; wo int enable
port_int_id dw p0_base + 2 ; ro int ident
port_modem_ctrl dw p0_base + 4 ; wo bit 3 = en int
port_status dw p0_base + 5 ; ro tbe and da
port_modem_stat dw p0_base + 6 ; ro dsr cts
fake_int dw fake_int0 ; for software interrupt
ser_int_vect dw 12 * 4 ; interrupt vector location
ser_int_entry dw i_serial_0 ; interrupt entry point
ser_ds_ptr dw data_seg_0 ; port 0's data segment
ser_pic_mask db 0EFh ; enable pic interrupt
r_flag db 08h ; XIOS receive flag
t_flag db 09h ; XIOS transmit flag
; if using port 1, these values are copied over port 0's
port1_params rb 0
p1_base equ 02F8h ; port base address
dw p1_base ; rw data
dw p1_base + 1 ; wo int enable
dw p1_base + 2 ; ro int ident
dw p1_base + 4 ; wo bit 3 = en int
dw p1_base + 5 ; ro tbe and da
dw p1_base + 6 ; ro dsr cts
dw fake_int1 ; soft int
dw 11 * 4 ; int vector location
dw i_serial_1 ; int entry point
dw data_seg_1 ; port 1's data seg
db 0F7h ; pic interrupt mask
db 0Ah ; rec flag
db 0Bh ; xmit flag
port_param_size equ offset $ - offset port1_params
; queue descriptors, buffers, and parameter blocks
q_in_buf rb q_mes_size * in_q_num
qd_in dw 0,0,02h
db 'SerIn'
qd_in_p db '0 '
dw q_mes_size,in_q_num,0
dw 0,0,0
dw q_in_buf
q_out_buf rb q_mes_size * out_q_num
qd_out dw 0,0,02h
db 'SerOut'
qd_out_p db '0 '
dw q_mes_size,out_q_num,0
dw 0,0,0
dw q_out_buf
qd_mx dw 0,0,03h
db 'MXSer'
qd_mx_p db '0 '
dw 0,1,0
dw 0,0,0,0
qpb_in dw 0,0,0,msg_in
db 'SerIn'
qpb_in_p db '0 '
qpb_out dw 0,0,0,t_block_o
db 'SerOut'
qpb_out_p db '0 '
qpb_mx dw 0,0,0,0
db 'MXSer'
qpb_mx_p db '0 '
; queue name adjustment list for port numbers
qn_list dw qd_in_p
dw qd_out_p
dw qd_mx_p
dw qpb_in_p
dw qpb_out_p
dw qpb_mx_p
qn_list_size equ (offset $ - offset qn_list) / 2
t_block_o dw t_block
t_block_s rw 1 ; filled by write t_block
msg_in rb q_mes_size
r_prot_state db 0FFh ; 0FFh => rec protocol on
all_on equ i_en+dtr_bit+rts_bit
r_on_prot db xon, all_on, all_on, all_on
r_off_prot db xoff, rts_bit+i_en, dtr_bit+i_en, i_en
serin_data_top equ offset $
eject
; ***************************************
; *** Interrupt routine begins here ***
; ***************************************
cseg
org serin_code_top
; this collection of data must be in the code segment
; and it must not change, or reentrancy is blown
data_seg0 rw 1 ; port 0's data segment
data_seg1 rw 1 ; port 1's data segment
i_sup_o rw 1 ; supervisor offset
i_sup_s rw 1 ; supervisor segment
supervisor equ dword ptr i_sup_o
; serial char interrupt entry for port 0
i_serial_0:
push ds ; on user's stack
mov ds,data_seg0 ; get port zero's data seg
jmps i_serial_shared ; and share the rest
; serial char interrupt entry for port 1
i_serial_1:
push ds ; on user's stack
mov ds,data_seg1 ; get port 1's data seg
; jmps i_serial_shared ; and share the rest
; shared serial char interrupt code
i_serial_shared:
mov save_ax,ax ; first switch stacks
mov save_ss,ss
mov save_sp,sp
mov ax,ds ; set up local stack
mov ss,ax
mov sp,local_stack
push bx ! push cx ! push dx ! push bp
push si ! push di ! push es
mov es,ax ; for receive stosb
mov dx,port_int_id ; int identification reg
in al,dx ; this says which int it is
and al,07h ; 3 lsb's only
mov int_id,al ; save for pic reset
cmp al,rec_int ; if not from char received
jnz i_transmit ; then skip
i_receive:
mov dx,port_status ; just to double check
in al,dx ; we look at line status
test al,dr ; if not data ready
jz i_rec_wait ; then skip buffer write
i_rec_char:
mov dx,port_data
in al,dx ; fetch the data char
test tran_prot_code,xon_xoff ; check to see if this is
jz i_rec_to_buf ; an xon/xoff character
cmp al,xon ; for the transmit side
jz i_rec_x ; if it is, get out of here
cmp al,xoff
jz i_rec_x
i_rec_to_buf: ; a valid data char rec'd
mov di,buf_in_ptr
stosb ; store to circular buffer
cmp di,c_buf_end ; check for wrap around
jb i_rec_no_wrap
mov di,offset c_buf ; wrap to beginning
i_rec_no_wrap:
mov ax,di
sub ax,buf_out_ptr ; check buffer fullness
jz i_rec_too_full ; right to the brim
mov buf_in_ptr,di ; update if any room at all
jnc i_rec_valid ; subtraction worked
add ax,c_buf_size ; ax = chars in buffer
i_rec_valid:
cmp ax,c_buf_size - 10h ; if not too full
jb i_rec_wait ; then carry on
i_rec_too_full:
call rec_prot_off ; else, try to stop the stream
i_rec_wait:
mov al,0
xchg al,r_wait ; test and set wait flag
test al,al ; if Serial In isn't waiting
jz i_transmit ; then check for transmit
mov dl,r_flag ; if it is waiting
call i_set_flag ; then set the flag
jmps i_transmit ; check for xmit message
i_rec_x:
mov rec_x,al ; save xon/xoff for transmit
; jmps i_transmit ; and carry on
; in case a character was received just before transmit is to
; be checked, we always check for a pending transmit message.
i_transmit:
cmp send_x,0 ; if there is an xon/xoff
jz i_tran_from_buf ; to send, it has top priority
call trans_ready ; check tbe
jz i_exit ; if not ready, come back again
mov dx,port_data
mov al,send_x
out dx,al ; send an xon/xoff char
mov send_x,0 ; don't do it again
jmps i_exit ; and depart
i_tran_from_buf:
cmp t_count,0 ; if transmit message is empty
jz i_tran_wait ; see if serout is waiting
call trans_prot ; if protocol blocks transmission
jz i_tran_wait ; then skip the output
call trans_ready ; check tbe
jz i_tran_wait ; for a flag wait on serout
les si,t_pointer ; point to transmit message
mov al,es:[si] ; grab the character
mov dx,port_data
out dx,al ; ship the character
inc si ; bump and
mov t_ptr_off,si ; update the pointer
dec t_count ; and if characters remain
jnz i_exit ; then don't set the flag
i_tran_wait:
mov al,0
xchg al,t_wait ; test and set wait flag
test al,al ; if Serial Out not waiting
jz i_exit ; then we're done
mov dl,t_flag ; if it is waiting
call i_set_flag ; then set flag
i_exit:
cmp int_id,no_int ; if interrupt was soft
jz i_no_pic ; then don't reset pic
mov al,ns_eoi ; reset the pic
out pic_ack,al
i_no_pic:
pop es ! pop di ! pop si
pop bp ! pop dx ! pop cx ! pop bx
mov ax,save_ax
mov ss,save_ss ; restore the stack
mov sp,save_sp
pop ds ; get back user's ds
iret ; do not dispatch!
; check for transmit buffer empty. zf set if not
trans_ready:
mov dx,port_status ; if message pending, see if
in al,dx ; the port is ready
test al,tbe ; if not ready, then check
ret
; check for transmit protocol ready. zf set if not
trans_prot:
mov ah,tran_prot_code ; which protocol to use
test ah,ah ; if zero
jz trans_prot_ok ; then skip the works
mov dx,port_modem_stat
in al,dx ; get the status bits
test ah,dtr_dsr
jz trans_prot1 ; if dsr/dtr protocol
test al,dsr_bit ; see if dsr is on
jz trans_prot_done ; if not, it's no good
trans_prot1:
test ah,rts_cts
jz trans_prot2 ; if rts/cts protocol
test al,cts_bit ; see if cts is on
jz trans_prot_done ; if not, forget it
trans_prot2:
test ah,xon_xoff
jz trans_prot_ok ; if xon/xoff protocol
cmp rec_x,xoff ; see if we've got an xoff
jz trans_prot_done ; if so, go back
trans_prot_ok:
or al,0FFh ; clear the zero flag
trans_prot_done:
ret ; with zf
; set the flag passed in register dl
i_set_flag:
mov dh,0 ; ensure dx = flag num
mov cx,dev_flag_set
push ds
mov ds,sys_data_s
callf supervisor ; right to the sup
pop ds
ret
; ************************************
; *** Interrupt data begins here ***
; ************************************
dseg
org serin_data_top
save_ax rw 1 ; for stack switch
save_ss rw 1
save_sp rw 1
int_id rb 1 ; saves the int_id_reg
r_wait db 00h ; is Serial In waiting?
; this is the data block which is shared with SerOut
t_block equ offset $
t_wait db 00h ; is Serial Out waiting?
t_count db 00h ; message byte count
t_ptr_off rw 1 ; offset of trans message
t_ptr_seg rw 1 ; segment of trans message
t_pointer equ dword ptr t_ptr_off
rec_prot_code db 00h ; receive protocol code
tran_prot_code db dtr_dsr + xon_xoff ; transmit protocol code
buf_in_ptr dw c_buf ; used by int to fill
buf_out_ptr dw c_buf ; used by Serial In to empty
rec_x db xon ; our received xon/xoff
send_x db 00h ; an xon/xoff to send
sys_data_s rw 1 ; system data seg
i_disp_o rw 1 ; dispatcher offset
i_disp_s rw 1 ; dispatcher segment
dispatcher equ dword ptr i_disp_o
c_buf rb c_buf_size ; receive circular buffer
c_buf_end equ offset $
rb stack_size ; local interrupt stack
local_stack equ offset $
db 00h ; for gencmd
end


View File

@@ -0,0 +1,316 @@
; *** SEROUT.RSP ***
; A Concurrent CP/M-86
; Resident System Process for
; Queue Driven Serial Character Output
; Oct 19, 1983 Dean Ballard
; This program reads the serial out queue and sends those
; characters to the asynchronous communications port. It
; is interrupt driven in that it performs a flag-wait any
; time that the async port is not ready to transmit chars.
; Setting up the actual interrupt routine is the respons-
; ibility of the companion Serial Input routine.
; First the hardware independent equate values.
; These have to do with the queue management, and generally
; pertain to the application program side of things.
q_mes_size equ 17 ; byte count plus 16 characters
out_q_num equ 2 ; number of out queue messages
bdos equ 0E0h ; bdos interrupt number
c_detach equ 93h ; detach console
dev_flag_wait equ 84h ; wait for a flag
dev_flag_set equ 85h ; release a flag
p_delay equ 8Dh ; delay dx ticks
p_term equ 8Fh ; terminate process
q_make equ 86h ; create queue code
q_open equ 87h ; open queue code
q_read equ 89h ; read queue code
q_write equ 8Bh ; write queue code
; RSP origin equates
rsp_header equ 00h ; start of data seg
rsp_pd equ 10h ; process descriptor offset
rsp_uda equ 40h ; user data area offset
rsp_stack_top equ 13Ah ; UDA stack
rsp_data_end equ 140h ; end of rsp stuff
; Data structure for information shared with interrupt routine
t_wait equ byte ptr 0 ; flag wait semaphore
t_count equ byte ptr 1 ; chars left in message
t_ptr_off equ word ptr 2 ; offset of trans message
t_ptr_seg equ word ptr 4 ; segment of trans message
rec_prot equ byte ptr 6 ; receive protocol code
tran_prot equ byte ptr 7 ; transmit protocol code
; Now the hardware specific equates
; These have to do with the I/O port and interrupt management,
; and are, for the most part, specific to the IBM PC
; async port bit patterns
dr equ 01h ; data ready
tbe equ 20h ; xmit buf empty
r_t_mask equ 03h ; rec/trans int en
modem_mask equ 0Bh ; dtr, cts, en int
; **************************
; *** Code begins here ***
; **************************
cseg
org 00h ; small model
serial_out:
call which_port ; are we port 0 or port 1 ?
call is_it_there ; if the port is not present
jnz terminate_out ; then terminate the process
call open_out ; open the output queue
call get_t_block ; copy address to local mem
out_loop:
call read_q ; wait for queue to fill
call send_chars ; transmit queue message
jmps out_loop ; go forever
terminate_out:
mov pd_flag,0 ; turn off the keep flag
mov cl,p_term
mov dl,0
int bdos ; back to bdos
; ***************************
; *** Setup Subroutines ***
; ***************************
; see which port we are, and adjust params if necessary
which_port:
mov al,ncp ; get copy number
cmp al,0 ; if port = 0
jz which_port_done ; then leave params alone
add al,'0' ; change port number to ascii
mov qpb_out_p,al ; and update queue name
mov si,offset port1_params ; now adjust parameters
mov di,offset port0_params
mov cx,port_param_size
cld
rep movsb ; copy port1 over port0
which_port_done:
ret
; check to see if the port is present
; exit: zf set if port is present
is_it_there:
mov dx,port_int_id ; interrupt ident port
in al,dx
test al,0F8h ; should be zero if present
ret
; Open the output queue
; If the open fails, it is because the companion Serial Input
; routine has not yet made the queue. Wait here until it does.
open_out:
mov cl,q_open
mov dx,offset qpb_out ; output queue param block
int bdos ; open it
test ax,ax ; if not successful
jnz open_out ; then try again
ret
; copy t_block address to local memory
; this address is passed by the companion Serial In routine
get_t_block:
mov cl,q_read
mov dx,offset qpb_out ; output queue param block
int bdos ; read shared data pointer
les di,dword ptr msg_out ; get segment and offset
mov t_block_o,di
mov t_block_s,es ; save for wait loop
mov es:t_ptr_seg[di],ds ; tell int where to find
ret ; our trans message
; **************************
; *** Loop Subroutines ***
; **************************
; read one message from the queue
; return only valid data messages
read_q:
mov cl,q_read
mov dx,offset qpb_out ; output queue param block
int bdos ; wait here until ready
mov ax,word ptr msg_out ; al = count, ah = 1st byte
cmp al,q_mes_size ; check message count
jb read_q_done ; return if valid data msg
les di,t_block ; point to shared data
cmp al,0FEh ; recieve protocol code
jnz read_q1
mov es:rec_prot[di],ah ; set receive protocol
read_q1:
cmp al,0FFh ; trans protocol code
jnz read_q
mov es:tran_prot[di],ah ; set transmit protocol
jmps read_q ; go back for another
read_q_done:
ret ; return when ready to send
; send a new queue message to the interrupt routine
send_chars:
les di,t_block ; point to shared data
mov si,offset msg_out
lodsb ; get message count
mov es:t_ptr_off[di],si ; save the message pointer
mov es:t_count[di],al ; and the message count
send_again:
les di,t_block ; point to shared data
mov es:t_wait[di],0FFh ; tell int we're waiting
call fake_int ; and fake an interrupt
mov cl,dev_flag_wait
mov dl,t_flag ; transmit flag
int bdos ; wait here for int done
les di,t_block ; point to shared data
cmp es:t_count[di],0 ; if whole message sent
jz send_done ; then we're done
mov cl,p_delay ; if not, there must be
mov dx,2 ; some protocol hang up
int bdos ; so, wait a bit
jmps send_again ; and try again
send_done:
ret
; Interrupt entries to kick off a transmit burst
fake_int0:
int 12 ; port 0 interrupt
ret
fake_int1:
int 11 ; port 1 interrupt
ret
; *****************************
; *** RSP header, pd, uda ***
; *****************************
dseg
org rsp_header ; header start
dw 0,0
ncp db 1,0 ; one copy
dw 0,0,0
dw 0,0
org rsp_pd ; process descriptor
dw 0,0 ; link, thread
db 0 ; ready to run
db 181 ; priority better than PIN's
pd_flag dw 2 ; process flag "keep"
db 'SerOut ' ; process name
dw rsp_uda/10h ; uda segment
dw 0,0 ; disk, user, reserved
dw 1 ; for shared code
dw 0,0,0 ; and a mess of zeros
dw 0,0,0
dw 0,0,0
dw 0,0,0
org rsp_uda ; user data area
dw 0,0,0,0,0,0 ; no dma buffer
dw 0,0,0,0,0,0
dw 0,0,0,0,0,0
dw 0,0,0,0,0,0
dw 0,0,rsp_stack_top,0,0,0
dw 0,0,0,0,0,0
dw 0,0,0,0,0,0
dw 0,0,0,0,0,0
db 1,0,0,0,0,0 ; don't switch from UDA
; stack at SUP entry
org rsp_stack_top
dw offset serial_out ; code start
dw 0 ; code seg (genccpm)
dw 0 ; flags (genccpm)
; ******************************
; *** Our data begins here ***
; ******************************
org rsp_data_end ; above the rsp stuff
; first we have parameters for port 0
port0_params rb 0
p0_base equ 03F8h ; port base address
port_int_id dw p0_base + 2 ; ro int ident
fake_int dw fake_int0 ; port 0's interrupt
t_flag db 09h ; XIOS transmit flag
; if using port 1, these values are copied over port 0's
port1_params rb 0
p1_base equ 02F8h ; port base address
dw p1_base + 2 ; ro int ident
dw fake_int1 ; port 1's int
db 0Bh ; xmit flag
port_param_size equ offset $ - offset port1_params
t_block_o rw 1 ; address of interrupt
t_block_s rw 1 ; shared data block
t_block equ dword ptr t_block_o
qpb_out dw 0,0,0,msg_out
db 'SerOut'
qpb_out_p db '0 '
msg_out rb q_mes_size
db 00 ; pad
end


File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,897 @@
eject ; Dec 3, 1983
reorg14 equ offset $ ; save for code start
; this module now also includes the FIDDS hooks
;****************************************************************
;* *
;* HARD DISK DATA STRUCTURES *
;* *
;****************************************************************
; GLOBAL CONSTANTS
SLOW_SEEK equ 0 ;if -1, forces 3 ms step rate
IBM_SYS_ID equ 0AA55h ;partition label id word
HD_SYS_ID equ 0DBh ;(yours truly)
HD_VFY_ERR equ 0AAh ;artificial ROS error code
block_size equ 2048 ;remap block size
;also used for dpb phys sector
; XT HARD DISK DATA STRUCTURE DEFINITIONS
DSEG
; This IOPB is used to communicate with the ROS access routine "hd_rom"
; Note:some parameters are not EXACTLY as defined by IBM.
org 0
;IBM_iopb STRUCT
IBM_fn rb 1 ;ibm ROS function code (0 - 14d)
IBM_errcode rb 1 ;what is returned by ROS in AH
IBM_lun rb 1 ;unit number (0h or 1h)
IBM_head rb 1 ;head number only (0 - maxheads)
IBM_cylinder rw 1 ;entire cylinder number (0 - maxcyl)
IBM_sector rb 1 ;sector number only (0 - maxsector)
IBM_count rb 1 ;number of sectors to xfer (1 - 80h)
IBM_xfer_ofs rw 1 ;transfer buffer offset address
IBM_xfer_seg rw 1 ;transfer buffer segment address
IBM_XFER_PTR equ dword ptr ibm_xfer_ofs
HD_IOPB_SIZE equ offset $
;IBM_iopb ENDS
; definition of IBM XT hard disk partition table record
org 0
;XT_PT_record STRUCT
XT_PT_bootfl rb 1 ;80h if bootable
XT_PT_start_head rb 1 ;partition start info
XT_PT_start_sector rb 1 ; high 2 bits of cyl also
XT_PT_start_cylinder rb 1 ; low 8 bits only
XT_PT_system rb 1 ;system indicator (DB for cpm)
XT_PT_end_head rb 1 ;partition end info
XT_PT_end_sector rb 1 ; high 2 bits of cyl also
XT_PT_end_cylinder rb 1 ; low 8 bits only
XT_PT_rel_sect rw 2 ;start in absolute sectors
XT_PT_size rw 2 ;size in sectors
XT_PT_REC_SIZE equ offset $
;XT_PT_record ENDS
; definition of the overall IBM XT hard disk partition sector
org 0
;XTLBL_table STRUCT
XTLBL_code rb 1beh ;contains bootstrap code
XTLBL_part1 rb XT_PT_REC_SIZE ;partition record 1
XTLBL_part2 rb XT_PT_REC_SIZE ;partition record 2
XTLBL_part3 rb XT_PT_REC_SIZE ;partition record 3
XTLBL_part4 rb XT_PT_REC_SIZE ;partition record 4
XTLBL_id rw 1 ;contains signature (0aa55h)
;XTLBL_table ENDS
; This defines the CP/M label for a volume
org 0
;HDLB_label STRUCT
HDLB_chks rw 1 ;checksum
HDLB_id rb 31 ;contains dr copyright
HDLB_pstart rw 1 ;partition start cylinder
HDLB_plength rw 1 ;partition length in cylinders
HDLB_nheads rb 1 ;number of heads
HDLB_ibm_spt rb 1 ;ibm sectors per track
HDLB_cpm_options rb 1 ;lsb = 1 means write verify
HDLB_cpm_nsect rb 1 ;no longer used
HDLB_cpm_dpb: ;DPB
HDLB_cpm_spt rw 1 ;as per usual abbreviations
HDLB_cpm_bsh rb 1
HDLB_cpm_blm rb 1
HDLB_cpm_exm rb 1
HDLB_cpm_dsm rw 1
HDLB_cpm_drm rw 1
HDLB_cpm_al0 rb 1
HDLB_cpm_al1 rb 1
HDLB_cpm_cks rw 1
HDLB_cpm_off rw 1
HDLB_cpm_psh rb 1
HDLB_cpm_prm rb 1
HDLB_cpm_rsvd rw 2 ;for future use
HDLB_cpm_fudge rw 1 ;for cpm_off shift remainder
HDLB_map_ctl: ;remap table begins
HDLB_map_spb rw 1 ;# of ibm sectors per remap block
HDLB_map_max rw 1 ;# total length of table in entries
HDLB_map_cnt rw 1 ;# of currently active entries
HDLB_map_spare_end rw 1 ;last spare cpm relative block number
HDLB_map_table rw 1 ;beginning of remap table entries
HD_LABEL_SIZE equ 512
;HDLB_label ENDS
org 0
; this defines a table entry in the XIOS hard disk information table.
; it stores current status and limit information for the physical disk
;HDINF_vector STRUCT
HDINF_exists rb 1 ;means that hardware exists
HDINF_logged rb 1 ;currently logged in
HDINF_fixed rb 1 ;login indicated it is a fixed drive
HDINF_heads rb 1 ;number of heads (according to ROS)
HDINF_spt rb 1 ;sectors/track (according to ROS)
HDINF_cyl rw 1 ;max cylinders (according to ROS)
HDINF_enddir rw 1 ;last block of dir (in remap units)
HDINF_label_ofs rw 1 ;points to its label buffer
HDINF_SIZE equ offset $
;HDINF_vector ENDS
eject
;****************************************************************
;* *
;* HARD DISK DRIVER ROUTINES *
;* *
;****************************************************************
; These routines hook into M.P. Vano's drivers for CCP/M-86
; version 1.0. Every attempt has been made to mess with
; his code as little as possible. Some changes were made
; for different sector sizes and different dph's, but most
; of it is in the form envisioned by its creator.
; A new parameter HDLB_cpm_fudge has been added to take
; care of a problem arising in disk drives with other than
; a multiple of 4 heads. Grouping tracks in fours to fool
; CP/M into handling 2K physical sectors creates a problem
; in this case. The 2 bit shift of HDLB_cpm_off can relult
; in bits falling on the floor. HDLB_cpm_fudge saves those
; bits, and adds them back when converting to physical
; track number in hd_io:
cseg
org reorg14
; Select hard disk, save spt for multi sector count
select_hd:
sub cl,byte ptr num_flop
mov aux_drive,cl ; save drive code
mov aux_code,dl ; and which select
mov ax,0 ; select function code
call hd_driver ; login and select
test ax,ax ; if bad select
jz sel_hd1 ; then skip sector count
mov si,.8[bx] ; get dpb pointer
mov ax,[si] ; get sectors per track
mov max_sector,ax ; and save for mcnt
mov ax,bx ; restore dph
sel_hd1:
ret
; Hard disk read routine
read_hd:
mov aux_func,2 ; read function code
jmp read_write_hd ; jump to shared code
; Write to hard disk
write_hd:
mov aux_func,4 ; write function code
read_write_hd: ; shared code
call aux_param_set ; set up param block
mov ax,num_flop
sub aux_drive,al ; correct the drive code
rw_hd1:
mov ax,aux_func ; restore function code
call hd_driver
test al,al ; check for errors
jnz rw_hd3 ; skip if bad
dec aux_code ; check multi-count first
jz rw_hd3 ; and skip if done
mov ax,aux_sector ; increment the current
inc ax ; sector (and track if
cmp ax,max_sector ; necessary) for multi
jb rw_hd2 ; sector operations
inc aux_track
sub ax,ax ; reset sector
rw_hd2:
mov aux_sector,ax
add aux_dma_s,128 ; add to seg to avoid overflow
jmps rw_hd1 ; back for another sector
rw_hd3:
ret
; This routine sets up the "aux" parameter block
; It is shared by the FIDDS driver
aux_param_set:
mov al,drive
mov aux_drive,al
mov al,mcnt
mov aux_code,al ; use code for multi-count
mov ax,track
mov aux_track,ax
mov ax,sector
mov aux_sector,ax
mov ax,dma_seg
mov aux_dma_s,ax
mov ax,dma_off
mov aux_dma_o,ax ; save the works
ret
eject
;
; XTCODE.LIB: XT INTERIM XIOS HARD DRIVER CODE MODULE
;
; written for DRI by M.P. Vano
; Vano Associates, Inc.
; 24 May 1983
;
;
; This driver uses the XT ROM BIOS and so cannot relinquish the CPU
; during disk IO operations. This was done at the request of DRI to
; ensure compatibility with the ever-mysterious future plans of IBM.
;
; NOTE that although many data structures and routines of a highly
; general nature are used by this module, the behaviour of this driver
; is not defined except within the frame of reference of the existing
; 4 disk drives set by drive parameters in the XT ROS tables. In
; particular, the use of REMAP blocks of a size other than 2k will not
; succeed unless the XIOS is re-assembled for a different buffer size.
; In addition, binary multiples only are assumed for the ratio of
; CP/M to physical sector sizes supported.
;****************************************************************************
HD_HW_INTERRUPT equ 0dh ;IBM controller interrupt
HD_INTERRUPT equ 13h ;IBM entry point for hard disk
HD_PARMS_INTERRUPT equ 41h ;points to hard disk parms
HD_FD_INTERRUPT equ 40h ;ROS revectors floppy io here
HD_ROS_DATASEG equ 40h
HD_OPTION_ADDR equ 76h ;ibm controller option control
HD_PTR equ dword ptr HD_INTERRUPT * 4
hd_control_byte equ byte ptr HD_ROS_DATA_SEG*16 + HD_OPTION_ADDR
; Hard disk IO calls get vectored here from AUX DISK handler. The entry
; conditions are similar to an ATTACH-A-MATIC except that the aux drive
; parameter pointer is NOT setup.
;
; The GLOBAL XIOS variables AUX_CODE, AUX_TRACK, AUX_DRIVE,AUX_SECTOR
; AUX_LONGWORD must be directly accessed to specify the operation.
; AX has the function to execute as per attach-a-matic usage.
;
; CALLS: hd_seldsk,hd_read,hd_write
hd_driver:
mov hd_function,ax ;save for debug use
push es ! push ds
mov hd_uda,es
mov bx,ax ;save fnc code for jump
mov ax,HDINF_SIZE ;usually correct
mov cl,AUX_DRIVE ! and cl,1 ;keep in index range
mul cl ;si--> info vector[AUX_DRIVE]
mov si,offset hd_disk_info
add si,ax
; stack switch has been deleted here
call hd_fn_table[bx] ;do case(fn)
;
pop ds ! pop es
ret
; function # 0 2 4
hd_fn_table dw hd_seldsk, hd_read, hd_write
; if (!logged_in(info_ptr)) return 0 else return &(dph_array[vol])
; At entry, SI--> info array ptr for that drive and
; external globals AUX_DRIVE and AUX_CODE
; Since AUX_DRIVE is always valid when called, SI's HDINF pointer
; may be trusted.
;
; CALLS: hd_seldsk
hd_seldsk:
call hd_login ;if (!hd_login(info_ptr) return 0)
or al,al
mov ax,0
jz hd_seldsk_ret
mov al,AUX_DRIVE ;else return &dph_array[DPH_SIZE][,vol]
mov ah,dph_size
mul ah
add ax,offset hd_dph0
hd_seldsk_ret:
mov bx,ax
ret
; checks if current hard disk is logged in properly. If it is,
; it returns true. otherwise it tries to log it in and if it can't
; it returns false. The same happens for out of range requests.
; NOTE that this routine is called from INIT also. It takes as input
; the global AUX_DRIVE and the HDINF pointer for that drive in SI.
;
; CALLS: hd_get_partition,hd_read_label,hd_patch_dpb
hd_login:
test HDINF_exists[si],0ffh ;is the hardware there?
jz hd_login_err ;if not, don't go any farther
test HDINF_logged[si],1 ;has it ever been logged-in?
jz hd_log_it_in ; if not, go try to do it
test HDINF_fixed[si],1 ;else don't not be too hasty...
jnz hd_login_ok ; labels on fixed can't change
test AUX_CODE,1 ;even a removable only needs
jnz hd_login_ok ;re-login on first access
;
hd_log_it_in: ;really needs to be logged in
mov HDINF_logged[si],0 ;mark it unlogged
call hd_get_partition ;read IBM's master table
or al,al
jnz hd_login_err ;can't read table or no cp/m
call hd_read_label ;else go look for XIOS label
or al,al ;can't read or doesn't exist
jnz hd_login_err
call hd_patch_dpb ;fix up dpbs for ver 2
hd_login_ok:
mov al,0ffh ! ret ;else tell caller we found it
hd_login_err:
xor al,al ! ret ;return false if we fail
; read partition table into label area and find partition if one exists
; uses cp/m label buffer as temporary bufer
; returns 0 in al if succeeds, else nz
; CALLS: hd_rom
hd_get_partition:
mov bx,offset hd_rd_partition_iopb ;setup iopb
mov al,AUX_DRIVE ;setup fields that vary
mov IBM_lun[bx],al
mov IBM_cylinder[bx],0
mov IBM_sector[bx],1 ;always has label
mov ax,HDINF_label_ofs[si] ;get the correct buffer
mov IBM_xfer_ofs[bx],ax
mov IBM_xfer_seg[bx],ds
call hd_rom ;read partition table
or al,al
jnz hd_get_partition_ret ;oops, disk error.
;
push si
mov si,HDINF_label_ofs[si] ;inspect table
cmp XTLBL_id[si],0aa55h ;is it there at all?
jnz hd_bad_partition
lea si,XTLBL_part1[si] ;scan for cp/m
mov cx,4
hd_partition_hunt:
cmp XT_PT_system[si],HD_SYSID ; is it our magic code?
je hd_partition_found
add si,XT_PT_REC_SIZE ;else keep on looking
loop hd_partition_hunt
jmps hd_bad_partition ;else return the bad news
;
hd_partition_found: ;setup same iopb for label read
mov al,XT_PT_start_cylinder[si] ;adjust the disk address
mov ah,XT_PT_start_sector[si] ;fix up cylinder address
rol ah,1 ! rol ah,1 ! and ah,3
mov IBM_cylinder[bx],ax
mov hd_pstart_cyl,ax ;save to check later
mov IBM_sector[bx],4 ;cp/m label is always here
;
mov al,XT_PT_end_cylinder[si]
mov ah,XT_PT_end_sector[si]
rol ah,1 ! rol ah,1 ! and ah,3
mov hd_pend_cyl,ax ;save to check later
test XT_PT_start_head[si],0ffh ;should start at head 0
jnz hd_bad_partition ;else exit with error
mov al,XT_PT_start_sector[si] ;only 1 or 2 are legal
dec al
and al,not 0c1h ;ignore irrelevant bits
jnz hd_bad_partition
pop si ;else it must be ok
xor al,al
jmps hd_get_partition_ret ;so exit with success code
;
hd_bad_partition:
pop si
mov al,0ffh
hd_get_partition_ret:
ret
; reads cpm label into label area and checks for integrity
; iopb has already been set up by hd_get_partition
; returns 0 in al if succeeds, else nz.,
; CALLS: hd_rom,hd_check_label
hd_read_label:
mov bx,offset hd_rd_partition_iopb ;setup iopb
call hd_rom ;try to read label
or al,al ;disk error?
jnz hd_read_label_ret ;(probably MICROSOFT sabotage)
;
call hd_check_label ;check label's integrity
or al,al
jnz hd_read_label_ret ;label or programmer is strange
;
mov di,HDINF_label_ofs[si] ;update HDINF
mov al,0ffh ;assume fixed disk
cmp HDLB_cpm_cks[di],8000h ;fixed?
je hd_read_label1 ;yep, go ahead
not al ;can't win them all
hd_read_label1:
mov HDINF_fixed[si],al ;set fixed flag as needed
mov HDINF_logged[si],0ffh ;I pronounce thee "logged"
xor al,al
hd_read_label_ret:
ret
; verifies integrity of cp/m label block of disk si--> HDLB pointer of
; returns 0 if succeeds, else nz value in ax
; CALLS: NOTHING
hd_check_label:
push si ! mov si,HDINF_label_ofs[si] ;get pointer to label
;
push si ;checksum it first
mov cx,HD_LABEL_SIZE/2
xor bx,bx ;clear checksum accum.
hd_check_cks:
lodsw
add bx,ax
loop hd_check_cks ;verify its checksum
pop si
jnz hd_check_error ;exit if bad checksum
;
push si ! push es
push ds ! pop es
mov cx,length (hd_idstring) ;check the id string
lea si,HDLB_id[si]
mov di,offset hd_idstring
repe cmpsb
pop es ! pop si
mov ax,0 ! je hd_check_exit ;exit here if id matches
hd_check_error: ;else error
mov ax,-1
hd_check_exit:
pop si
ret
; correct the hard disk dpb's for concurrent version 2.0
; this changes physical sector size from 128 to 2048 bytes
; spt = old spt / 4; psh = 2; prm = 3
hd_patch_dpb:
push si ; save info ptr just in case
mov si,HDINF_label_ofs[si] ; point to label (and dpb's)
mov cl,2 ; for divide by 4
shr HDLB_cpm_spt[si],cl ; for larger physical sectors
mov ax,HDLB_cpm_off[si]
and ax,0003h ; save remainder from shift
mov HDLB_cpm_fudge[si],ax ; for retranslate to physical
mov cl,2
shr HDLB_cpm_off[si],cl ; group tracks by 4's
mov HDLB_cpm_psh[si],4 ; physical sector shift
mov HDLB_cpm_prm[si],15 ; physical sector mask
mov HDLB_map_spb[si],4 ; 512 * 4 = 2k block
pop si
ret
; hard disk physical read
hd_read:
call hd_xlate_request ; do block remap
mov bx,offset hd_rd_iopb ; read parameters
call hd_dma_check ; check for overflow
jc hd_read1 ; if no problem then
jmp hd_dma_io ; read right to dma
hd_read1:
call hd_local_io ; if dma boundary error
or al,al ; read to local first
jnz hd_read2 ; done if error
push es ; if read is good
push si ; then move to dma
mov si,offset hd_local_buf
les di,AUX_LONGWORD
mov cx,block_size/2
rep movsw
pop si
pop es
hd_read2:
ret
; hard disk physical write
hd_write:
call hd_xlate_request ; do block remap
mov bx,offset hd_wr_iopb ; go load iopb and do it
call hd_dma_check ; check for boundary error
jnc hd_write1 ; skip if dma ok
push ds ; if boundary trouble then
push es ; move data to local buffer
push si
mov di,offset hd_local_buf
push ds
pop es ; local destination
lds si,AUX_LONGWORD
mov cx,block_size/2
rep movsw
pop si
pop es
pop ds
call hd_local_io ; write from local buffer
jmps hd_write2 ; skip to error check
hd_write1:
call hd_dma_io
hd_write2:
or al,al
jnz hd_write_exit ; exit here if failed
;
push si
mov si,HDINF_label_ofs[si]
test HDLB_cpm_options[si],1
pop si
jz hd_write_exit ;no, just exit (al still valid)
;
mov bx,offset hd_vfy_iopb ;else run verify cycle
mov IBM_fn[bx],2 ;IBM read opcode
call hd_local_io
or al,al ;did read-after-write fail?
jnz hd_write_exit ;yes, no point in comparing
;
mov IBM_fn[bx],4 ;fake verify opcode for msgs
push es ;else compare buffers
push si
les di,AUX_LONGWORD ;dma segment:offset
mov si,offset hd_local_buf
mov cx,block_size/2
repe cmpsw
pop si
pop es
mov al,0 ;assume success
je hd_write_exit ;if matched, exit
mov IBM_errcode[bx],HD_VFY_ERR ;else fake a physical error
call hd_error_handler ;and see what operator wants
cmp al,'R' ;retry?
je hd_write ;yep, go back to the top
hd_write_exit:
ret
; translate global AUX_TRACK,AUX_SECTOR and hd_disk into remapped units
; and finds alternate block if that block is in remap table
; (updates global variables hd_req_block, hd_req_sec)
; CALLS: NOTHING
hd_xlate_request:
push si
mov si,HDINF_label_ofs[si] ;get label for this disk
;
mov ax,AUX_TRACK ;ax=CPM_TRACK-cpm_offset
sub ax,HDLB_cpm_off[si]
mul HDLB_cpm_spt[si] ; * cpm_spt
add ax,AUX_SECTOR ; + CPM_SECTOR
;
; using 2k physical sectors, ax now equals logical block number
;
mov cx,HDLB_map_cnt[si] ;see if that block is in table
jcxz hd_no_remap ;(skip if table is empty)
push es ! mov dx,ds ! mov es,dx
cld ! lea di,HDLB_map_table[si] ;by scanning table
repne scasw
pop es
jne hd_noremap ;no, leave block number alone
inc cx ! sub cx,HDLB_map_cnt[si] ;else calculate replacement
mov ax,HDLB_map_spare_end[si]
add ax,cx
hd_noremap:
mov hd_req_block,ax ;update parameter and exit
pop si
ret
; check dma addresses for boundary errors. set carry if bad
hd_dma_check:
mov ax,AUX_DMA_S ; get segment
mov cl,4 ; convert to offset
shl ax,cl ; ignoring ms nibble
add ax,AUX_DMA_O ; add in segment
add ax,block_size ; set carry on overflow
ret
; entry point for read or write. transfers to and from dma buffers
hd_dma_io:
mov ax,AUX_DMA_O
mov IBM_xfer_ofs[bx],ax ; save offset
mov ax,AUX_DMA_S
mov IBM_xfer_seg[bx],ax ; save segment
jmps hd_io
; entry point for local read or write
hd_local_io:
mov IBM_xfer_ofs[bx],offset hd_local_buf
mov IBM_xfer_seg[bx],ds ; local segment
; jmps hd_io ; fall through
; fills in the volume,head and cyl fields in passed IBM_IOPB from
; current request using CURRENT HDINF pointer and executes rom operation
; CALLS: hd_rom
hd_io:
push si
mov si,HDINF_label_ofs[si]
mov al,AUX_DRIVE
mov IBM_lun[bx],al
mov ax,hd_req_block ;back to IBM format
;
mul HDLB_map_spb[si] ;ibm_block=(block * remap_spb)
;
mov ch,0 ;for full divide
mov cl,HDLB_ibm_spt[si] ;ibm_rel_track=ibm_block/ibm_spt
xor dx,dx ! div cx ;CAUTION!! 16 bit result
;
inc dl ;save remainder as ibm sector
mov IBM_sector[bx],dl ; (except they start with 1)
;
mov dx,HDLB_cpm_off[si] ;get fake track offset
shl dx,1 ; and change it back to physical
shl dx,1 ; track number
add ax,dx ;correct offset
add ax,HDLB_cpm_fudge[si] ;remainder from offset shift
;
mov cl,HDLB_nheads[si] ;ibm_cyl= ibm_track/nheads
xor dx,dx ! div cx ;CAUTION!! 16 bit result
mov IBM_cylinder[bx],ax
mov IBM_head[bx],dl ;ibm_head=ibm_track % nheads
pop si
; jmps hd_rom ;fall through
; provides easy access to HDISK ROM
; call with ds:bx--> IBM_iopb structure as defined elsewhere
; returns zero set if succeeds, else non-zero
; CALLS: hd_error_handler
hd_rom:
push es ! push si ;save uda and HDINF_ptr then execute
mov cx,3 ;set retry count
;
hd_rom_retry: ;inner retry loop
push cx ! push bx ;save retry counter & IOPB
mov ah,IBM_fn[bx]
mov al,IBM_count[bx] ;load up registers for ROM call
mov dh,IBM_head[bx]
mov dl,IBM_lun[bx]
or dl,80h ;Next ANSI hard disk command standard!
mov cx,IBM_cylinder[bx] ; SCRewed-Up Microcomputer
xchg ch,cl ; Parameter Interface - SCRUMPI ?
ror cl,1 ! ror cl,1 ; (SERIOUSLY now, why would anyone do
and cl,0c0h ; something this way?)
or cl,IBM_sector[bx]
les bx,IBM_XFER_PTR[bx]
pushf ! callf HD_ROM_ENTRY ;fake an SWI call to ROM
pop bx ! pop cx ;restore IOPB ptr and retry counter
mov IBM_errcode[bx],ah ;save error return
mov al,0 ! jnc hd_rom1 ;and exit if no error
;
push cx ! push bx ! ;reset drive on any error ??
mov ah,0dh ;registers are still ok
pushf ! callf HD_ROM_ENTRY
pop bx ! pop cx
loop hd_rom_retry ;then try automatic retries first
;
mov al,IBM_errcode[bx] ;else get error code back and exit
hd_rom1: ;result shold be in al here
pop si ! pop es
or al,al ! jz hd_rom_done ;return if no error
test hd_init_flag,1 ;if during MPM initialization,
jz hd_rom_done ; return error immediately
call hd_error_handler ;else report it to operator
cmp al,'R' ! jz hd_rom ;does he wish to try more?
hd_rom_done:
ret ;else return to caller
; dummy ROM BIOS SWI used by hard disk ROM (always succeeds)
; CALLS: NOTHING
hd_dummy_int:
sti ! xor ax,ax ! retf 2
; Display hard disk error and gets operator's response.
; entry: al = error code
; exit: al = 00h for ignore
; FFh for accept
; 'R' for retry
hd_error_handler:
mov disk_error,al ; save for sub message
jmp do_disk_error ; in the floppy code
eject
;********************************************************
;* *
;* FIELD INSTALLABLE DEVICE DRIVER HOOKS *
;* *
;********************************************************
; FIDDS select disk
sel_fid:
mov aux_drive,cl ; save drive number
mov aux_code,dl ; and select code
sub ax,ax ; ax = 0 for select
jmps fidds_go ; interrupt away
read_fid:
call aux_param_set ; drive, track, sect, etc.
mov ax,2 ; ax = 2 for read
jmps fidds_go ; interrupt away
write_fid:
call aux_param_set ; same as for read
mov ax,4 ; ax = 4 for write
; jmps fidds_go ; interrupt away
; FIDDS take off point
fidds_go:
mov ax,num_flop
add ax,num_hard ; calc fidds offset
sub aux_drive,al ; zero base the drive code
mov dx,ds ; dx:bx -> param block
mov bx,offset aux_drive
int fidds_interrupt ; see you later...
ret
; Dummy FIDDS interrupt routine for non-disks
i_dummy_fidds:
sub ax,ax ; zero for no select
iret
; Interrupt to return the lowest numbered unused system
; flag to FIDDS, to be used for a flagwait to eternity.
i_fidds_flag:
;
mov al,0 ;; FORCE THE ZERO FLAG, FIDDS NOT SUPPORTED
mov cs:top_flag,al ;; AND MAKE SURE IT STAYS THAT WAY
;
; mov al,cs:top_flag ; get the next available
;
test al,al ; if it's zero,
jz i_f_flag_done ; then forget it
inc al ; increment to the next
cmp al,cs:n_flags ; if we've gone too far
jb i_f_flag_ok ; then reset to zero
mov al,0
i_f_flag_ok:
mov cs:top_flag,al ; for next time
i_f_flag_done:
iret
top_flag db last_flag ; used by i_fidds_flag
eject
reorg_hard_data equ offset $
dseg
org reorg_hard_data
; *** XT hard disk driver data segment ***
; this block of data is shared by the
; XT hard disk driver and the FIDDS hooks
aux_drive rb 1 ; corrected drive code
aux_code rb 1 ; select / rw multi-count
aux_track rw 1
aux_sector rw 1
aux_dma_o rw 1
aux_dma_s rw 1
aux_longword equ dword ptr aux_dma_o
aux_func rw 1 ; hd function code
max_sector rw 1 ; for mcnt loops
; general information variables
hd_ndisks db 0 ;amount of IRON found only!
hd_uda dw 0 ;stores UDA at entry to hd routines
hd_init_flag db 0 ;set at end of initialization
hd_pstart_cyl dw 0 ;used to cross-check labels
hd_pend_cyl dw 0 ; for consistency with each other
hd_function dw 0 ;used fo debug trace info only
; desired block buffer contents
; hd_req_vol is global AUX_DRIVE
hd_req_block dw 0 ;requested BLOCK / physical sector
; tables of pertinent information about each possible disk
; each contains an entry defined by data structure HDINF_vector
hd_disk_info:
hd_info0 db 0,0,0,0,0
dw 0,0,offset hd_label0
hd_info1 db 0,0,0,0,0
dw 0,0,offset hd_label1
; iopbs used to do ROM BIOS IO
; CAUTION: these are only templates, it is the user's responsibility
; to make sure they are FULLY filled in with meaningful information
hd_wr_iopb db 3,0,0,0 ;fn,err,lun,head
dw 0 ;cylinder
db 0,4 ;sector,count
dw 0,0 ;xfer offset,seg
hd_rd_iopb db 2,0,0,0 ;fn,err,lun,head
dw 0 ;cylinder
db 0,4 ;sector,count
dw 0,0 ;xfer offset,seg
hd_vfy_iopb db 2,0,0,0 ;fn,err,lun,head
dw 0 ;cylinder
db 0,4 ;sector,count
dw 0,0 ;xfer offset,seg
hd_rd_partition_iopb db 2,0,0,0 ;fn,err,lun,head
dw 0 ;cylinder
db 0,1 ;sector,count
dw 0,0 ;xfer offset,seg
; string used to verify label authenticity
hd_idstring db '(C) 1983 DIGITAL RESEARCH, INC.'
; pointer to ROS interrupt entry for xt hard disk
hd_rom_ofs rw 1
hd_rom_seg rw 1
HD_ROM_ENTRY equ dword ptr hd_rom_ofs
org offset $+1 and 0fffeh ;word align it
; allocation vectors, etc. for XT hard disks
hd_dph0 dw 0,0,0,0,HD_DPB_INIT
dw 0,0ffffh,0ffffh,0ffffh,0ffffh
hd_dph1 dw 0,0,0,0,HD_DPB_INIT
dw 0,0ffffh,0ffffh,0ffffh,0ffffh
dph_size equ 20
; this is a dummy dpb which is used by genccpm for
; dph allocation, check vector, and hash table calculations
HD_DPB_INIT dw 11h ; spt
db 05h ; bsh 4k blocks
db 1Fh ; blm "
db 01h ; exm
dw 0A20h ; dsm 10 meg maximum
dw 03FFh ; drm
dw 0FFFFh ; al0 and al1
dw 8000h ; cks fixed disk
dw 01h ; off
db 04h ; psh
db 0Fh ; phm
org offset $+15 and 0fff0h ;align them
; leave space for both label structures
hd_label0 rb 512
hd_label1 rb 512
HD_DPB0 equ byte ptr hd_label0 + offset HDLB_cpm_dpb
HD_DPB1 equ byte ptr hd_label1 + offset HDLB_cpm_dpb
org offset $+1 and 0fffeh ;word align them
; The XT hard disk now shares its buffer with the floppies
; Sector buffer used by read/write routines when requested
; multi sector I/O operation crosses a 64K page boundary.
; Also used to read floppy size code from track 0 sector 0.
hd_local_buf rb 0
local_buffer rb bytes_per_sector - 1
floppy_type rb 1
rest_of_buf rb block_size - bytes_per_sector


View File

@@ -0,0 +1,912 @@
eject ; Dec 13, 1983
; INIT
; ----
;************************************************
;* *
;* IBM PC SOFTWARE INTERRUPT STRUCTURE *
;* *
;************************************************
divide_interrupt equ 00h
single_step_interrupt equ 01h
nmi_interrupt equ 02h
one_byte_interrupt equ 03h
overflow_interrupt equ 04h
clock_interrupt equ 08h
keyboard_interrupt equ 09h
hd_hard_interrupt equ 0Dh
disk_interrupt equ 0Eh
equip_int equ 11h ; ROS equipment check int
mem_int equ 12h ; ROS memory check int
disk_int equ 13h ; ROS disk int
async_int equ 14h ; ROS async port int
print_int equ 17h ; ROS printer int
tick_interrupt equ 1Ch ; user tick vector
hd_code_interrupt equ 40h ; pass along to floppy
hd_param_interrupt equ 41h ; hard disk param pointer
os_interrupt equ 224 ; normal CCP/M-86 entry
debugger_interrupt equ 225 ; debugger entry to O.S.
flag_interrupt equ 228 ; to get an unused flag
fidds_interrupt equ 229 ; for attachamatic drives
xios_interrupt equ 230 ; for ver 1.0 back door
;********************************************************
;* *
;* XIOS INITIALIZATION ROUTINE *
;* *
;********************************************************
; 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 the SUP has called the RTM, CIO, MEM and
; BDOS initialization routines, and before SUP has created
; the RSP processes. Tests indicate that the DL register is
; preserved from the boot until INIT is entered, and this is
; used to pass the ROS code for the boot disk, which lets the
; INIT routine know where to look for SETUP data. This is,
; however a very undocumented feature, and may at some later
; date change, leaving SETUP high and dry. So now you know.
reorg16 equ offset $
cseg
org reorg16
INIT:
cli
call set_sys_vars ; set up system variables
call equip_check ; see what we've got
call check_hi_mem ; from C000 up
call ndp_init ; check for an 8087
call compaq ; do before any video setup
call fix_crt ; mono/color vs_ variables
call fix_disk_tables ; match disk tables to equip
call printer_init ; reset the parallel printers
call do_video ; initial window setup
call hd_init ; get hard disk partitions
call do_setup ; customize XIOS
call flop_off ; turn off the floppy motors
call try_mdisk ; done after setup data read
call trim_memory ; fix up the partition list
call do_config ; initial serial port init
call do_interrupts ; do all of the int stuff
call sign_on ; print signon message
retf ; initializaiton done
; set up initial system variables
set_sys_vars:
cld ;Sup saves DS,ES on INIT call
mov sysdat,ds ;save System Data Segment
mov bx,offset supmod ;save Sup entry double word
mov ax,[bx] ;get offset out of Sys Data Seg
mov cs:supervisor_o,ax ;save it
mov ax,2[bx] ;get the segment
mov cs:supervisor_s,ax ;save it
mov tod_hour,0 ;start with zero time
mov tod_day,0890h ;Jan 1, 1984
mov boot_disk,dl ;ros code for booter
ret
; check IBM equipment word, set number variables
equip_check:
int equip_int ; return equip word
mov dx,ax
mov cl,4
shr dx,cl ; shift down init_video
mov al,dl
and al,03h
mov init_video_mode,al ; save for crt setup
mov cl,2
shr dx,cl ; shift down floppy bits
mov ax,dx
and ax,03h ; mask for floppy
inc ax ; correct 0 based code
mov num_flop,ax
mov cl,3
shr dx,cl ; shift down port bits
mov ax,dx
and ax,07h ; mask for serial ports
mov num_port,ax
mov cl,5
shr dx,cl ; shift down printer bits
mov ax,dx
and ax,03h ; mask for printer
mov num_print,ax
mov ah,08 ; XT param check
mov dl,80h ; first hard disk code
int disk_int
jnc equip_ch1 ; skip if dl good
mov dl,0 ; else no hard disks
equip_ch1:
mov dh,0
mov num_hard,dx
mov bx,0ffeh ; off the screen
mov ax,mono_seg
call crt_check ; is the monochrome there?
mov num_mono,ax ; store the count 0 or 1
mov ax,color_seg
call crt_check ; is the color card there?
mov num_color,ax ; store the count 0 or 1
int mem_int ; check total memory size
mov num_mmkb,ax ; save kilobytes
mov cl,6
shl ax,cl ; change to paragraph count
mov memory_top,ax ; save for memory trim
mov lo_mem_top,ax ; keep a permanent copy
ret
; do a memory check on the crt ram
crt_check:
mov es,ax ; crt segment
mov ax,0DDB2h ; bit pattern
mov es:[bx],ax ; store it
sub ax,es:[bx] ; check it
jnz crt_chk1 ; and skip if not there
mov ax,blank ; once more for
mov es:[bx],ax ; good measure
sub ax,es:[bx]
jnz crt_chk1
inc ax ; only one
ret
crt_chk1:
sub ax,ax ; nobody home
ret
; Check for memory at or above C000:0000
check_hi_mem:
mov dx,0C000h ; starting segment
mov cx,12 ; 16k block count
mov ax,0DDB2h ; check pattern
ch_hi_mem1:
call ch_hi_word ; is there RAM ?
jz ch_hi_mem2 ; if so, skip
add dx,400h ; check next 16k
loop ch_hi_mem1 ; up to F000
ret ; if none there, return
ch_hi_mem2:
mov hi_mem_start,dx ; there is some memory
ch_hi_mem3:
add dx,400h ; up 16k
call ch_hi_word ; as long as it's good
jz ch_hi_mem3 ; keep on going
mov hi_mem_top,dx ; and save the top
ret
ch_hi_word:
sub si,si ; zero index
mov es,dx ; set check segment
mov es:[si],ax ; store check pattern
cmp es:[si],ax ; and see if it matches
ret ; return with zf set
; 8087 Numeric Data Processor initiation routine
ndp_init:
FNINIT ; init and check for 8087
xor ax,ax ; stall for time
mov ndp_control,ax ; and clear control word
FNSTCW ndp_control ; get 8087 control word
or ax,ndp_control ; test for 8087 presence
jz ndp_init_done ; if not there, skip
mov num_ndp,1 ; we've got one!
mov owner_8087,0 ; tell the system about it
mov ndp_int_off,nmi_interrupt * 4
mov ndp_int_seg,0 ; save the vector location
mov ndp_vec_off,offset i_ndp
mov ndp_vec_seg,cs ; and the interrupt vector
ndp_init_done:
ret
; if this is the compaq, change cursor, sync and sl_attrib
compaq:
mov ax,0F000h
mov es,ax ; ROM segment
mov di,0FFEAh ; ID offset
mov si,offset compaq_name
mov cx,6
repz cmpsb ; look for a match
jnz compaq_done
mov var_cursor,compaq_cursor
mov var_sync,0 ; no retrace sync
mov sl_attrib,0Fh ; enhanced white
mov alpha_str,offset compaq_str
compaq_done:
ret ; all for now
compaq_name db 'COMPAQ'
; set up the mono/color vs_ variables
fix_crt:
mov si,offset set_mono ; assume monochrome
mov di,offset z_sl_mono ; for status line too
cmp init_video_mode,03h ; if mono switched on
jz fix_crt1 ; then skip
mov si,offset set_color ; else color
mov di,offset z_sl_color ; and color status
fix_crt1:
call di ; set status line
mov dl,0 ; first vc number
fix_crt2:
call point_vs ; bx -> structure
call si ; mono or color
inc dl ; next vc
cmp dl,num_vir_cons ; through the last
jb fix_crt2
ret
; correct the disk tables to match physical configuration
fix_disk_tables:
mov ax,num_flop ; actual number of floppies
dec ax
mov sys_disk,al ; system disk = top flop
mov temp_disk,al ; tempory disk too
inc ax
shl al,1 ; convert to word index
mov di,ax ; point to first non-floppy
cmp num_hard,0 ; if no hard disks
jz fix_disk1 ; then skip
mov ax,offset hd_dph0 ; first hard dph
call fix_one_disk ; fix dph and jump tables
cmp num_hard,1 ; if only one hard disk
jz fix_disk1 ; then skip
mov ax,offset hd_dph1 ; second hard dph
call fix_one_disk ; fix dph and jump tables
fix_disk1:
mov dph_tbl[di],0 ; zero the extra entries
mov select_tbl[di],offset sel_fid
mov read_tbl[di],offset read_fid
mov write_tbl[di],offset write_fid
inc di
inc di ; two bytes per entry
cmp di,12 ; zap up to F:
jb fix_disk1
ret
; correct table entries for one hard disk
fix_one_disk:
mov dph_tbl[di],ax ; set dph
mov select_tbl[di],offset select_hd
mov read_tbl[di],offset read_hd
mov write_tbl[di],offset write_hd
inc di
inc di ; to next entry
ret
; reset all parallel printer ports
; set up list_out and list_stat tables
printer_init:
mov cx,num_print ; get the count
jcxz pr_par_done
sub dx,dx ; first is zero
pr_par_loop1:
mov ah,1 ; reset code
int print_int
inc dx ; next printer
loop pr_par_loop1
mov ax,40h ; look into base page
mov es,ax
mov si,8 ; printer address list
mov cx,num_print ; printer count
pr_par_loop2:
push cx
mov ax,es:[si] ; fetch printer address
mov di,-2 ; pre decrement for scan
mov cx,3 ; max printer count
pr_par_loop3:
inc di ; to next list data
inc di
cmp ax,list_data[di] ; if no match
loopnz pr_par_loop3 ; then keep looking
jnz pr_par_next ; until exausted
mov list_out[di],offset parallel_out
mov list_stat[di],offset parallel_stat
pr_par_next:
inc si
inc si ; next list entry
pop cx
loop pr_par_loop2
pr_par_done:
mov cx,num_port ; number of serial ports
jcxz pr_init_done ; skip if none
mov si,6 ; first serial index
pr_ser_loop:
mov list_out[si],offset serial_init
inc si ; stat needs nothing
inc si
loop pr_ser_loop ; 1 or 2 times
pr_init_done:
ret
; get all of the window stuff rolling
do_video:
cmp init_video_mode,03h ; if initial crt is mono
jz do_color ; then skip port init
mov dx,mono_port ; get the video chip port
mov si,offset mono_table ; initialization commands
mov ax,0029h ; video mode / color sel
call video_init ; send commands to port
do_color:
cmp init_video_mode,03h ; if initial crt is color
jnz init_v0 ; then skip port init
mov dx,color_port
mov si,offset color_table
mov ax,0029h ; video mode / color sel
call video_init
; Set up the virtual screen structures (one per virtual console)
; and blank out their screen save areas.
init_v0:
mov ax,genccpm_buf ;paragraph address of buffer
;space allocated by GENCCPM.
mov bx,offset first_vs
mov cx,num_vir_cons
init_v1:
mov vs_vc_seg,ax ; tell each vs_ where to find
add bx,size_vs ; its buffer segment
add ax,((crt_size+15) shr 4) * 2 ; segment size
loop init_v1
mov vc_map_seg,ax ; ownership map segment
mov es,ax ; now set up the initial map
sub di,di ; top left corner
mov cx,crt_size
mov al,(1 shl num_vir_cons)-1 ; all bits on
cld
rep stosb ; fill the map
;
mov bx,offset first_vs
mov cx,num_vir_cons
init_v2:
push cx
mov es,vs_vc_seg ; point to image
sub di,di
mov cx,crt_size
mov ax,blank ; to erase virtual images
rep stosw ; fill with blanks
pop cx
add bx,size_vs ; next virtual structure
loop init_v2
push ds
pop es ; local extra segment
mov di,offset vc_priority ; vc priority list
mov al,num_vir_cons-1
init_v3:
stosb ; lowest priority first
dec al
jns init_v3 ; do through zero
jmp new_monitor ; set up initial windows
; Cold start setup for XT hard disk driver
; needs to do these things:
; 1) setup floppy interrupt trap
; 2) save disk interrupt vector for far call usage
; 3) initialize HDINF vectors about physical drive char.
; 4) try to login first hard disk
; 5) decide who will be system disk and temp disk
;
; this code assumes that it is running in XIOS codeseg and that
; the floppy initialization has already been done
;
; a stack switch has been deleted here
;
hd_init:
push ds
push es
xor ax,ax
mov es,ax ;point to vector seg
hd_patch_step_rate: ;step option bits off
and es:byte ptr .hd_control_byte,0F8h or (not SLOW_SEEK)
les ax,es:.HD_PTR ;get hard disk int ptr
mov hd_rom_seg,es
mov hd_rom_ofs,ax ;to local link address
mov disk_int_seg,es
mov disk_int_off,ax ; save in setup block
mov dl,byte ptr num_hard ;dl=number of drives
test dl,dl ;if no hard drives
jz hd_init_done ; then done
mov si,offset hd_info0
call hd_init_hdinf ;set its info vector
dec dl ! jz hd_init1 ;if no more drives,skip
mov dl,81h ;ask about 2nd drive
mov ah,8
pushf ! callf HD_ROM_ENTRY
jc hd_init1 ;if error now, continue
mov si,offset hd_info1
call hd_init_hdinf ;setup #2's info vector
hd_init1:
mov AUX_DRIVE,0 ;try to login 1st drive
mov si,offset hdinfo0
call hd_login
or al,al ! jz hd_init_done ;if can't login, forget rest!
; login is successful, correct the dpb pointers, sysdisk, and tempdisk
mov si,offset hd_dph0 + 8 ; point to dpb pointer
mov word ptr [si],offset HD_DPB0
mov word ptr .dph_size[si],offset HD_DPB1
inc SYS_DISK ; SYS and TEMP disks now
inc TEMP_DISK ; default to first hard disk
hd_init_done:
mov hd_init_flag,0ffh ;set XIOS initialized flag
pop es ;(which enables error handler)
pop ds
ret
; subroutine sets up physical information vector about a drive
hd_init_hdinf:
mov HDINF_exists[si],0ffh ;it at least exists
inc dh ;change last hd to # of hds
mov HDINF_heads[si],dh
mov al,3fh ! and al,cl ;sectors is already # of spt
mov HDINF_spt[si],al
rol cl,1 ! rol cl,1
and cl,3 ! xchg ch,cl ;unpack max cyl address
mov HDINF_cyl[si],cx ;and save it
ret
; Do the SETUP customizing of the XIOS
do_setup:
push ds
pop es ; for local read
mov dl,boot_disk ; from whence we came
mov dh,0 ; head zero
mov cx,0002h ; assume floppy tr 0 sct 2
mov bx,setup_buf ; local buf for setup
cmp dl,00h ; if floppy boot
jz setup_read ; then read it
cmp dl,80h ; if not hard disk
jnz setup_no_go ; then something's wrong
mov si,offset hd_label0 ; if hard disk, then
mov ax,HDLB_pstart[si] ; find our partition
mov ch,al ; cylinder lsb's
ror ah,1
ror ah,1 ; cylinder msb's
or ah,03h ; add in sector 3
mov cl,ah ; save the mashed code
setup_read:
mov ax,0201h ; read one sector
int disk_int ; through the ros
jnc setup_go ; if no error
dec su_retry
jnz setup_read ; try again
setup_no_go:
ret ; until exausted
setup_go:
cmp su_check,0DDB2h ; check for secret code
jnz setup_no_go ; if bad, bag it
; at this point, the SETUP sector is a winner
mov ax,su_md_start
mov mdisk_start,ax ; where to try for mdisk
mov al,su_verf
mov verify_flag,al ; whether to verf after write
mov al,su_hdst ; head step code
mov fdc_spec1_var,al ; save for specify
call flop_specify ; set head step speed
cmp su_cf,0 ; check the config flag
jz setup_pfks ; if not saved, skip
mov ax,su_config
mov config_data,ax ; save both codes
setup_pfks:
cmp su_pf,0 ; check the pfk flag
jz setup_done ; if not saved, finished
mov di,offset pfk_tbl0 ; first pfk (es is local)
mov bx,num_vir_cons ; number of pfk tables
setup_pfk_loop:
mov si,offset su_pfk_tbl ; the saved values
mov cx,180 ; words per pfk table
rep movsw ; copy 'em
dec bx
jnz setup_pfk_loop ; for each console
setup_done:
ret
; turn off the floppy motors before taking over interrupts
flop_off:
mov dx,FDC_PORT
mov al,FDC_ON ; turn off floppy motors
mov MOTOR_FLAGS,al ; or they may stay on forever
out dx,al
ret
; set up mdisk variables and clear the memory
try_mdisk:
mov ax,mdisk_start ; try to start it here
mov dx,lo_mem_top ; main mem ceiling
cmp ax,dx ; if start is below
jb yes_mdisk_lo ; then it's a winner
mov dx,hi_mem_start ; now look above video mem
test dx,dx ; if dx=0 there's none
jz no_mdisk
cmp ax,dx ; if mdisk is below
jb no_mdisk ; then forget it
mov dx,hi_mem_top ; high memory ceiling
cmp ax,dx ; if start is below
jb yes_mdisk ; yes, but no trimming
no_mdisk:
mov md_dph,0 ; zap the dph
ret ; and we're done
yes_mdisk_lo:
mov memory_top,ax ; save for memory trim
call yes_mdisk ; set it up
mov ax,num_mdkb ; get mdisk size
sub num_mmkb,ax ; and reduce main mem
ret
yes_mdisk:
mov temp_disk,'M'-'A'
xchg ax,dx ; dx = starting segment
sub ax,dx ; ax = paragraph length
mov cl,6
shr ax,cl ; ax = 1k chunks of mdisk
mov num_mdkb,ax ; save for signon
shr ax,1 ; ax = 2k chunks of mdisk
mov bx,ax ; save for fill count
dec ax
mov dsm_md,ax ; save in the dpb
mov ax,0E5E5h ; CP/M erase bytes
yes_mdisk_fill:
mov es,dx ; set up destination
sub di,di ; di = 0
mov cx,1024
rep stosw ; blast 2k bytes of E5's
add dx,80h ; up 2k
dec bx
jnz yes_mdisk_fill
ret ; that's it
; trim the memory partion list to match physical memory
trim_memory:
mov cx,memory_top ; top segment address
mov bx,offset mfl ; memory free list root
trim_mem1:
mov si,bx ; save previous link
mov bx,md_link[bx] ; link to next
test bx,bx
jz trim_mem3 ; 0 => end of list
mov ax,md_start[bx] ; memory block start seg
add ax,md_length[bx]
cmp ax,cx ; past physical ?
jbe trim_mem1 ; if not, link to next
mov md_link[si],0 ; previous is now the last
mov si,bx ; save start of severed list
trim_mem2:
mov di,bx ; save last link
mov bx,md_link[bx] ; look for the end of the
test bx,bx ; severed list
jnz trim_mem2
mov ax,mdul ; save md unused root
mov mdul,si ; redo the root
mov md_link[di],ax ; and re-attach original
trim_mem3:
ret
; set up the async ports
do_config:
sub dx,dx ; dx -> port 0
mov al,config0_data ; baud rate, etc.
mov ah,0 ; init code
int async_int ; through the ros
inc dx ; port 1
mov al,config1_data
mov ah,0
int async_int
ret
; take care of all of the interrupt stuff
do_interrupts:
cli ; an interrupt now would be bad
mov si,offset int_save_tbl ; list of ints to save
test debug,true ; if we are debugging
jnz xios_int_save ; under CP/M
mov si,offset int_no_save ; if we're running free
xios_int_save:
sub dx,dx ; interrupt number counter
mov es,dx ; int vector base address
mov di,dx ; and offset
mov cx,256 ; number of interrupts
mov ax,offset i_unexpected ; unexpected int entry
mov bx,cs ; and segment
cld
int_save_loop:
cmp dx,[si] ; if this is one to save
jz int_save_one ; then skip the store
stosw ! xchg ax,bx ; else store offset
stosw ! xchg ax,bx ; and segment
jmps int_save_cont
int_save_one:
add di,4 ; skip this vector
inc si ! inc si ; to next table entry
int_save_cont:
inc dx ; next int number
loop int_save_loop ; through the whole table
; now set up our particular interrupts
mov si,offset int_tbl ; offsets and vectors
mov cx,int_tbl_len ; number of entries
int_setup_loop:
lodsw ; get destination
mov di,ax ; and stash it
lodsw ; get vector
stosw ; and store it
mov ax,cs ; all vectors to sysdat
stosw
loop int_setup_loop ; through the table
; now set up timer interrupts
mov ax,timer_60_hz
out timer_0_reg,al
xchg ah,al
out timer_0_reg,al
mov al,beep_cmnd ; set up the beep frequency
out timer_cmnd_reg,al ; send the command
mov ax,timer_1000_hz ; get the constant
out timer_2_reg,al
xchg ah,al
out timer_2_reg,al
in al,21h
and al,0feh
out 21h,al
sti ; now we can handle ints
ret ; to main line
; print the sign on message with equipment configuration
sign_on:
mov si,offset banner
call print_msg ; name, copyright, etc.
sign_on_loop:
cmp word ptr [si],0 ; if next equip ptr = 0
jz sign_on_done ; then we're done
push si
call print_equip ; print one item
pop si
add si,equip_record_len
jmps sign_on_loop ; back for another
sign_on_done:
lodsw ; clear the pointer
jmp print_msg ; final cr,lf,lf
print_equip:
lodsw
xchg ax,bx ; bx -> equip count
mov ax,[bx] ; ax = equip count
test ax,ax ; if count = 0
jz pr_equip_done ; then don't print
push ax ; save the count
call print_msg ; print the name first
pop ax
call print_num ; and then the count
pr_equip_done:
ret
print_msg:
lodsb ; fetch a character
pr_msg_loop:
push si
call print_char ; print just one
pop si
lodsb ; fetch another
test al,al
jnz pr_msg_loop ; when char = 0, done
ret
print_num:
mov dh,0 ; skip 0's till dh > 0
div one_hundred ; al = hund's, ah = rem
call print_digit ; print hundred's place
mov al,ah
cbw ; ready for divide
div ten
call print_digit ; print ten's place
mov al,ah
; jmps print_digit ; print one's place
print_digit:
push ax ; save the number
or dh,al ; have we started printing
jz pr_digit_done ; skip if 0 and no
push dx
or al,'0' ; make into ascii
call print_char ; and print it
pop dx
pr_digit_done:
pop ax ; restore number
ret
print_char:
mov cl,al ; char to cl
mov dl,0
jmp io_conout ; print it
eject
;************************************************
;* *
;* INITIALIZATION DATA AREA *
;* *
;************************************************
reorg17 equ offset $
dseg
org reorg17
memory_top rw 1 ; for memory_trim
config0_data db 43h ; 300 baud, 1, none, 8
config1_data db 43h ; same here
config_data equ word ptr config0_data
init_video_mode rb 1 ; which crt to default
db 0 ; pad
banner db '---------------------------------------------------',cr,lf
db 'Concurrent CP/M for the IBM PC and PC XT 01/01/84',cr,lf
db 'Serial No. XXXX-0000-654321 All Rights Reserved',cr,lf
db 'Copyright (C) 1982,83,84 Digital Research Inc.',cr,lf
db '---------------------------------------------------',cr,lf,lf
db 'Hardware Supported :',cr,lf,0
dw num_flop
db cr,lf,' Diskette Drive(s) : ',0
dw num_hard
db cr,lf,' Hard Disk(s) : ',0
dw num_print
db cr,lf,' Parallel Printer Port(s) : ',0
dw num_port
db cr,lf,' Serial Port(s) : ',0
dw num_ndp
db cr,lf,' Numeric Data Processor : ',0
dw num_mmkb
db cr,lf,' Main Memory (Kb) : ',0
last_eq dw num_mdkb
db cr,lf,' MDisk (Kb) : ',0
equip_record_len equ offset $ - offset last_eq
dw 0 ; equipment end
db cr,lf,lf,0 ; last message
one_hundred db 100 ; for print_number divisions
ten db 10
su_retry db 3 ; for sector read retries
; SETUP buffer
setup_buf equ offset $
su_check rw 1 ; check code
su_md_start rw 1 ; mdisk start segment
su_verf rb 1 ; floppy verify flag
su_hdst rb 1 ; floppy head step
org setup_buf + 60h
su_cf rb 2 ; config flag (+ assign flag)
su_pf rb 2 ; pfk flag (+ one byte)
su_config rw 1 ; both async init bytes
org setup_buf + 70h
su_pfk_tbl rb 360 ; one console's pfk's
org setup_buf + 512 ; save room for a whole sector
; Interrupts to save for debug mode
; these must be in increasing number order
int_save_tbl dw single_step_interrupt
dw one_byte_interrupt
dw hd_hard_interrupt
dw async_int
dw print_int
dw hd_param_interrupt
dw debugger_interrupt
dw 0FFFFh
int_no_save dw hd_hard_interrupt
dw hd_param_interrupt
dw 0FFFFh ; impossible code
; Interrupt setup table
int_tbl dw divide_interrupt*4, i_divide
dw nmi_interrupt*4, i_nmi
dw overflow_interrupt*4, i_overflow
dw clock_interrupt*4, i_clock
dw tick_interrupt*4, i_tick
dw keyboard_interrupt*4, i_keyboard
dw disk_interrupt*4, i_disk
dw hd_code_interrupt*4, hd_dummy_int
dw flag_interrupt*4, i_fidds_flag
dw fidds_interrupt*4, i_dummy_fidds
dw xios_interrupt*4, xios_v1_entry
int_tbl_len equ (offset $ - offset int_tbl) / 4