home *** CD-ROM | disk | FTP | other *** search
- PAGE 66,132
- TITLE HOST - Link communication device to console using interrupts
- ;
- ; Program to install resident communications interrupt driver,
- ; and link it into the keyboard/video interrupt routines.
- ;
- ; We must define a general interrupt handler for the COM1 port,
- ; which will input characters directly into BIOS's keyboard input
- ; (typeahead) buffer. This buffer is defined by pointers at offset
- ; 001A (start of buffer pointer), and 001C (end of buffer pointer).
- ; The data segment for all of this is 0040. There is a BIOS routine
- ; at F000:E875 which handles a 15-char circular buffer wrap-around.
- ; We should copy it. We must compare [001A] with [001C] after calling
- ; the buffer wrap routine to see if the buffer full. If so, it'd
- ; be nice to send a bell to the caller, but im not sure this is the
- ; place to do it (inside an interrupt routine).
- ;
- ; Note that we assume the COM1 port is already initialized (RS232 stuff)
- ;
- ; Note that this routine must also detect change of line status and
- ; have appropriate logic for indicating and handling loss of carrier,
- ; and other delta indications in the line status register.
- ;
- ; This takes care of the keyboard input end of the communication
- ; interface.
- ;
- ; For linking screen output, I guess we just intercept the old
- ; screen output 'interrupt' vector, located at 0000:0040-:0043.
- ; There is no reason to buffer the output, so the intercept routine
- ; will first output the byte to the UART (if UART accepting), then
- ; pass control on to the former screen handler.
- ;
- ; The record-type HOSTFLAGS contains duplex, CR/LF, bell enable, and
- ; timeout specification. Timeout determines iff forced disconnect for
- ; lack of user input enablad.
- hostflags record duplex:1,crlf:1,bell:1,cls:1,timeout:1
- discflags record rlsd:1,ri:1,dsr:1,cts:1
- ;
- comment %
- ; Structure HOSTCB contains terminal setting values:
- hostcb struc
- clsequence db 0,12 ; default cls sequence
- timeout_at db 128 ; halfway through the timeout scale default
- baudrate_lsb db 80h
- baudrate_msb db 1 ; default UART baud rate = 300 baud
- userid dt ? ; ten bytes? for user identification
- screen_size db 24 ; # of lines on screen
- screen_width db 80 ; # of chars per line on screen
- hostcb ends
- ;
- %
- ;
- inport macro port
- lea dx,port ; get port offset into dx
- in al,dx
- endm
- ;
- outport macro port
- lea dx,port ; get port offset into dx
- out dx,al
- endm
- ;
- ; Set up references to the system interrupt vector table:
- ;
- ;
- int_table segment at 0h
- org 10H*4 ; address of video driver vector
- int10 label word
- org 0CH*4 ; address of comm interrupt vector
- int0c label word
- int_table ends
- ;
- ; Set up references to BIOS keyboard and COM system table.
- ;
- ;
- sys_table segment at 40h
- rs232_base dw 4 dup(?)
- org 1Ah
- buffer_head dw ?
- buffer_tail dw ?
- kb_buffer dw 16 dup(?)
- kb_buffer_end label word
- org 71h
- bios_break db ?
- sys_table ends
- ;
- ;
- comgrp group host,com_init
- ;
- host segment
- assume cs:comgrp
- ;
- ; Proc to bump BIOS's keyboard buffer pointer, in DI.
- advance_kb proc far
- add di,2
- cmp di,offset kb_buffer_end
- jnz noneed
- mov di,offset kb_buffer ; wrap if at end of buffer
- noneed: ret
- advance_kb endp
- ;
- eoi equ 20h ; end of interrupt value for 8259 controller
- ;
- baud_data equ 0[BX] ; baud rate selector and data register in UART
- baud_msb_int equ 1[BX] ; baud rate selector MSB and interrupt enable
- interrupt_id equ 2[BX] ; interrupt identification register
- line_control equ 3[BX] ; line control register
- modem_control equ 4[BX] ; modem control register
- line_status equ 5[BX] ; line status register
- modem_status equ 6[BX] ; modem status register
- ;
- comint proc far
- assume cs:comgrp,ds:sys_table
- sti
- push ax
- push bx
- push cx
- push dx
- push si
- push di
- push ds
- push es
- mov ax,sys_table ; point to system table segment
- mov ds,ax
- mov bx,rs232_base ; get base port for COM1 device
- ;
- ; Begin code to interrogate interrupt situation: "Why were we called ?"
- ;
- ; Note that we have set interrupts for change in modem status, and
- ; receiver data-ready. This means either he hung up (or ?) or he
- ; just sent us a character. The interrupt-id register will tell us which.
- ;
- inport interrupt_id ; read the interrupt id register
- shr al,1 ; of course an interrupt occurred!
- and al,00000011b ; lower 2 bits active.
- jz delta_stat ; go interpret change in modm stat
- dec al ; test for transmitter hold empty.
- jz no_action ; we're not processing these here.
- dec al ; test for received char?
- jz get_com_char ; read char from com device.
- dec al ; Break or error received.
- jz brk_rcd
- no_action: mov al,eoi ; end-of-inerrupt code to al
- out 20h,al ; tell controller to cancel interrupt
- pop es
- pop ds
- pop di
- pop si
- pop dx
- pop cx
- pop bx
- pop ax
- iret ; return to previous task.
- ;
- ; Break was received (or there was a line error)
- ; IF break received, simulate console CONTROL-BREAK function.
- ;
- brk_rcd: inport line_status
- and al,00010000b ; break detected ?
- jz no_action ; we're ignoring other errors now.
- mov di,offset kb_buffer ; load kb_buffer start
- mov buffer_head,di ; reset buffer to empty
- mov buffer_tail,di
- mov bios_break,80h ; set break byte.
- int 1bh ; do break service
- jmp no_action ; return ourselves
- ;
- get_com_char: inport baud_data ; read byte from port
- mov di,buffer_tail ; end of kb buff
- mov si,di ; store it
- call advance_kb ; try to make space for it
- cmp di,buffer_head ; has buffer wrapped ?
- jz kb_full ; buffer full!
- xor ah,ah ; convert al -> ax
- mov [si],ax ; store it in BIOS keyboard buffer
- mov buffer_tail,di ; update buffer
- jmp no_action ; return to caller
- ;
- kb_full: jmp no_action ; we'll eventually BEEP the user here.
- ;
- delta_stat: inport modem_status
- mov cs:c_modem_status,al ; remember new modem status
-
- jmp no_action
- ;
- comint endp
- ;
- ;
- console_out proc far
- assume cs:host,ds:sys_table
- jmp begin_console ; jump around ID field
- console_id: db 'HOST' ; our identification field
- begin_console: cmp ah,14 ; is it 'print tty' function ?
- jz take_over ; yes, intercept it
- cmp ah,10 ; it is 'print over char' function ?
- jz take_over ; yes, lets intercept it too
- normal_tty: jmp cs:old_int10 ; go to normal int 10 handler
- take_over: push ax
- push di
- push si
- push dx
- push bx
- push cx
- push ds
- test cs:c_modem_status,10000000b ; is there a connection?
-
- jz bypass_user ; no, we wont try to send
- mov cl,al ; save value for output
- mov ax,sys_table
- mov ds,ax ; prepare to examine UART
- mov bx,rs232_base ; get base address of com port
- send_char: inport line_status ; see if ready to send byte.
- and al,00100000b ; test xmit hold reg empty ?
- jz send_char ; no, wait for it
- mov al,cl ; get back char to send
- outport baud_data ; output byte to com1 iff ready
- bypass_user: pop ds
- pop cx
- pop bx
- pop dx
- pop si
- pop di
- pop ax
- jmp normal_tty
- ;
- console_out endp
- ;
- old_int10 dd ? ; doubleword pointer to old int10
- ;
- ; Set up our internal status flags:
- c_modem_status discflags <?,?,?,?>
- ; Set up user parm area
- user_flags hostflags <1,1,1,1,1> ; user logon and termninal flags
- user_disc discflags <1,0,1,0> ; lines for continual test/disconnect
- ;user_parms hostcb <,,,,,,> ; user logon and terminal characteristics
- user_parms equ $
- ;
- ; Structure HOSTCB contains terminal setting values:
- clsequence db 0,12 ; default cls sequence
- timeout_at db 128 ; halfway through the timeout scale default
- baudrate_lsb db 80h
- baudrate_msb db 1 ; default UART baud rate = 300 baud
- userid dt ? ; ten bytes? for user identification
- screen_size db 24 ; # of lines on screen
- screen_width db 80 ; # of chars per line on screen
- ;
- end_com equ this byte ; return to dos, stay res.
- ;
- host ends
- ;
- com_init segment
- assume cs:com_init,ds:sys_table,es:comgrp
- host_init proc far
- ;
- ; We'll assume that the UART has been initialized for now
- ; we will install the interrupt driven keyboard link and
- ; the INT 10H intercept driver.
- ;
- cli ; off ints so dont get caught
- push ds ; save pointer to dos psp
- mov ax,50h ; offset to int21 in psp
- push ax
- mov ax,host ; point to driver segment
- mov es,ax
- mov ax,sys_table ; point to system table
- mov ds,ax
- mov bx,rs232_base ; get base of port addresses
- inport modem_status
- mov es:c_modem_status,al ; initialize out record of it
- mov al,00001011b ; set MCR for interrupts
- outport modem_control
- mov al,00001101b ; desired interrupt fields
- outport baud_msb_int ; enable those interrupts
- mov ax,int_table ; get interrupt table segment
- mov ds,ax
- assume ds:int_table
- mov dx,int10 ; get offset for old int10
- push dx
- push bx
- push ds
- xchg bx,dx ; put offset into bx
- mov ax,host ; address ID field
- mov ds,ax
- mov dx,[bx+3] ; look at our ID field1
- cmp dx,'HO' ; Could it be us ?
- jnz notus ; nop.
- mov dx,[bx+5] ; Look at field 2 to be sure
- cmp dx,'ST'
- notus: pop ds
- pop bx
- pop dx
- jnz itsok
- jmp reject_init ; We were already installed
- itsok: mov es:old_int10,dx ; fill in
- mov dx,int10+2 ; get segment for old int10
- mov es:old_int10+2,dx ; fill in
- mov dx,host ; get new seg for int10
- mov int10+2,dx ; store it
- mov dx,offset console_out ; get new offset for int10
- mov int10,dx ; store it
- mov dx,host ; get new seg for int0c
- mov int0c+2,dx ; store it
- mov dx,offset comint ; and offset
- mov int0c,dx ; and offset
- in al,21h ; read from IMR
- and al,0efh ; turn off b4=enable comm interrupts
- out 21h,al ; in 8259 controller
- mov al,0 ; retcode = 0
- mov dx,offset comgrp:end_com; point to end of program
- mov cl,4 ; for devide by 16
- shr dx,cl ; turn into # segments
- add dx,100 ; leave 1K for dos environ ????
- mov ah,31h ; tell DOS to save this pgm.
- jmp ok_init ; successful initialization
-
- ;
- exit_init: ret ; far ret to dos function call
- ;
- ok_init: sti ; re-enable the interrupts
- ; call print_inline
- ; db 'pcHOST 1.0 - 09/29/83 - M.R.',13,10
- ; db 'Communication console link loaded and initialized.'
- ; db 13,10,10,0
- jmp exit_init
- ;
- reject_init: sti ; re-enable the interrupts
- ; call print_inline
- ; db 'HOST driver is already installed.',13,10
- ; db 'Nothing done.',13,10,0
- ; pop ax ; pop off 'stay resident' return
- ; xor ax,ax ; make 'terminate overlay' return
- ; push ax
- jmp exit_init
- ;
- ; Print_Inline proc: Prints ASCIIZ text at offset on NEAR STACK.
- ; Returns to address following delimiting zero of ASCIIZ string.
- ; Resgisters destroyed: SI.
- print_inline proc
- pop si
- push ax
- check_char: mov al,[si]
- inc si
- or al,al
- jz msg_done
- call print_chr
- jmp check_char
- msg_done: pop ax ; get back ax
- push si ; is this the ONLY way to jump
- ret ; to an address pointed to by SI ?
- print_inline endp
- ;
- ; Print_Chr proc: prints char in AL via INT 10H.
- ; Registers destroyed: AX.
- print_chr proc
- mov ah,14 ; and AH for PRINT TTY function
- call int_10h ; print char to tty
- ret
- print_chr endp
- ;
- ; INT_10H proc: Intercept to the BIOS INT 10H
- ; but saving all registers. Also, set BX=0 to select current
- ; screen page. All registers except BX must be set prior call.
- ; Registers destroyed: None.
- int_10h proc
- push bx
- push ax
- mov bx,0 ; select screen zero
- push bp
- push di
- push si
- int 10h
- pop si
- pop di
- pop bp
- pop ax
- pop bx
- ret
- int_10h endp
- ;
- host_init endp
- com_init ends
- ;
- ;
- end host_init
-
-