mirror of
https://github.com/SEPPDROID/Digital-Research-Source-Code.git
synced 2025-10-26 09:54:20 +00:00
Upload
Digital Research
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user