home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Columbia Kermit
/
kermit.zip
/
archives
/
cpm86.tar.gz
/
cpm86.tar
/
c86xap.a86
< prev
next >
Wrap
Text File
|
2011-08-09
|
19KB
|
744 lines
; * * * * * * * * * * * * * * * version 2.9 * * * * * * * * * * * * * * *
; [36] Add calls to support Concurrent CP/M.
; [34] Make BREAK be correct length (250 ms).
; * * * * * * * * * * * * * * * version 2.7 * * * * * * * * * * * * * * *
; [30d] Add SET PORT command, currently unimplemented.
; [30c] Isolate all machine dependencies in KERIO.
; [30a] Add keyboard DEL key alteration for APC
; RonB, 04/18/84
; * * * * * * * * * * * * * * * version 2.6 * * * * * * * * * * * * * * *
; [28e] Switch to local stack on interrupts.
; RonB, 03/28/84
; * * * * * * * * * * * * * * * version 2.4 * * * * * * * * * * * * * * *
; [20b] Add PRTBRK to send break & set correct clock rate for NEC.
; [20d] Add a pseudo time-out to PRTOUT so it doesn't loop forever.
; RonB,03/02/84
; [19a] Add XON/XOFF type flow control
; [19b] Clear screen and beginning and end of program.
; [19e] Add PRTBRK to send break to port (Rainbow only)
; [19g] Put in EQU for clock rate for timing loops.
; Rg, 2/84 <Oc.Garland%CU20B@Columbia-20>
; * * * * * * * * * * * * * * * version 2.3 * * * * * * * * * * * * * * *
; [par] Added calls to set parity, strip parity on input if
; other than none parity is called for.
; JD, 2/84
; * * * * * * * * * * * * * * * version 2.2 * * * * * * * * * * * * * * *
; [2] Add a de-initialization routine for the serial port, to restore
; changed interrupt vectors, etc.
; RonB,12/23/83
; [1] Add I/O support for the NEC Advanced Personal Computer
; RonB,12/23/83
; * * * * * * * * * * * * * * * version 2.0 * * * * * * * * * * * * * * *
; This module contains all the low level communications port I/O
; routines.
; Here are the I/O routines for the NEC APC.
CSEG $
; Clock rate *10 for timing loops ;[19g]
clckrt equ 49 ;[19g] 4.9 Mhz ;[20b]
; Interrupt vector locations, in data segment 0
mnioff equ 84h ;sio interrupt offset
mniseg equ 86h ;sio interrupt segment
; 8259 Interrupt controller (master)
iccmd equ 20h ;interrupt command register
icmask equ 22h ;interrupt mask register
; 8259 commands and masks
icEOI equ 20h ;end of interrupt (command)
ictmof equ 08h ;disable timer (mask)
icmnof equ 02h ;disable RS232 (mask)
; 8253-5 Interval Timer
tmdata equ 2bh ;baud set (chan 1)
tmcmd equ 2fh ;baud timer command port
; 8253 Timer commands
tmch1 equ 76h ;select & init timer channel 1
; 8251A USART controller
mndata equ 30h ; data port
mnsts1 equ 32h ;in status port
mnsts2 equ 34h ;in nec special status port
mncmd equ 32h ;out command port
mnmsk equ 34h ;out interrupt mask port
mntdc equ 36h ;out transmit disable port
; 8251 status port 1 bits
mninp equ 02h ;receive ready value
mnout equ 01h ;send ready value
mndsr equ 80h ;data set ready
; 8251 status port 2 bits
mncts equ 04h ;clear to send
; 8251 initialization instructions
; command instructions
ctxe equ 01h ;transmit enable
cdtr equ 02h ;dtr signal high
crxe equ 04h ;receive enable
cbrk equ 08h ;send break
cerr equ 10h ;error reset
crts equ 20h ;rts signal high
cmode equ 40h ;reset - go to mode instruction format
chunt equ 80h ;hunt for sync characters
; mode instructions
m1x equ 01h ;baud rate factor: 1x
m16x equ 02h ; 16x
m64x equ 03h ; 64x
m5d equ 00h ;data bits: 5
m6d equ 04h ; 6
m7d equ 08h ; 7
m8d equ 0Ch ; 8
mpn equ 00h ;parity: none
mpo equ 10h ; odd
mpe equ 30h ; even
m1s equ 40h ;stop bits: 1
m15s equ 80h ; 1.5
m2s equ 0C0h ; 2
; 8251 interrupt mask port bits
txmsk equ 01h ;disable transmit complete interrupt
rxmsk equ 02h ;disable receive complete interrupt
tbemsk equ 04h ;disable transmit buffer empty interrupt
outlmt EQU 1000H ;Number of times to check output status
; before giving up on send. ;[20d]
; dispatch: Under Concurrent CP/M, releases the processor for other
; use when waiting for either the receive or the send status
; routines to indicate success.
dispatch:
push ax
push bx
push cx
mov cl,8Eh ; P-Dispatch
int 224
pop cx
pop bx
pop ax
ret
; Test if port is ready to send next char. Returns RSKP if ready.
; Trashes dx.
outwt: cmp floctl, floxon ;are we doing flow-control? [19a] start
jne outwt1 ;no - go on
cmp xofrcv, true ;are we being held?
je outwt3 ;yes - return status not ready
outwt1: push ax
mov dx,mnsts1
in al,dx
and al,mndsr+mnout
sub al,mndsr+mnout
jnz outwt2
mov dx,mnsts2
in al,dx
and al,mncts
jnz outwt4
outwt2: pop ax
outwt3: call dispatch ;let other CCPM processes have a chance ;[36]
ret
outwt4: pop ax
jmp rskp
; Output data to port. Trashes DX and prints char in AL.
outchr: mov dx,mndata
out dx,al
ret
; Output the character in AL, checking first to make sure the port is clear.
prtout: call dopar ;[par] set parity
push dx
push cx ;[20d] begin
mov cx,outlmt
prtou2: call outwt ;Wait until the port is ready
loop prtou2 ; or too much time has passed.
nop
call outchr ;Output it.
pop cx ;[20d] end
pop dx
ret
; Test if data is available from port.
instat: cmp mnchrn,0 ;Any chars in the buffer?
jne inst2
call dispatch ;let other CCPM processes have a chance ;[36]
ret
inst2: jmp rskp
; Input data from port. Preserves all registers and returns char in
; AL. Gets the char from the ring buffer. Assumes a char is
; already there.
inchr: push bx
cli ;Disable interrupts while were are playing.
dec mnchrn ;Decrement the number of chars in the buffer.
mov bx,mnchop ;Get the pointer into the buffer.
inc bx ;Increment to the next char.
cmp bx,offset mnchrs+mnchnd ;Past the end?
jb inchr2
mov bx, offset mnchrs ;If so wrap around to the start.
inchr2: mov mnchop,bx ;Save the updated pointer.
mov al,[bx] ;Get the character.
sti ;All done, we can restore interrupts.
pop bx
cmp parflg,parnon ;[par] no parity?
je inchr3 ;[par] yup, don't bother stripping
and al,7fh ;[par] checking parity, strip off
inchr3: cmp floctl, floxon ;do flow-control? [19a] start
je inchr4 ;If yes jump
ret
inchr4: cmp xofsnt, true ;Have we sent an XOFF
je inchr5 ;Jump if yes
ret
inchr5: cmp mnchrn, mntrg1 ;Under the low trigger point?
jb inchr6 ;yes - jump
ret
inchr6: push ax ;save current character
mov al, xon
call prtout ;send an XON
mov xofsnt, false ;turn off the flag
pop ax ;get back character
ret ; [19a] end
mnax dw 0 ;Storage in CSEG ;[28e] begin
mnsp dw 0 ; for use by interrupt handler
mnsseg dw 0
mndseg dw 0
; This routine handles the interrupts on input.
mnint: cli
mov cs:mnax, ax ;Save interrupt stack location.
mov ax, sp
mov cs:mnsp, ax
mov ax, ss
mov cs:mnsseg, ax
mov ax, cs:mndseg ;Switch to our internal stack.
mov ss, ax
mov sp, offset mnstk
push ds ;Save all registers.
push es
push bp
push di
push si
push dx
push cx
push bx
mov ds, ax ;Get our data segment address.
call mnproc ;Process the character.
mov dx, iccmd
mov al, icEOI ;signal end of interrupt to controller
out dx, al
pop bx ;Restore all registers.
pop cx
pop dx
pop si
pop di
pop bp
pop es
pop ds
mov ax, cs:mnsp ;Restore the original stack.
mov sp, ax
mov ax, cs:mnsseg
mov ss, ax
mov ax, cs:mnax
iret ;Return from the interrupt. ;[28e] end
; This routine (called by MNINT) gets a char from the serial port
; and puts it in the ring buffer.
mnproc: mov dx,mnsts1
in al,dx ;Get the port status.
and al,mninp ;Is a character waiting?
jnz mnpro2 ; Yes, go take care of it.
ret ; No, just a false alarm.
mnpro2: mov dx,mndata
in al,dx ;Read the char.
cmp floctl, floxon ;are we doing flow-control ? [19a] start
jne mnpr2b ;no - go on
cmp al, xoff ;is it an XOFF?
jne mnpr2a ;no - go on
mov xofrcv, true ;set the flag
ret
mnpr2a: cmp al, xon ;an XON?
jne mnpr2b ;no
mov xofrcv, false ;clear the flag
ret ; [19a] end
mnpr2b: cmp mnchrn,mnchnd ;Is the buffer full?
je mnperr ;If so, take care of the error.
inc mnchrn ;Increment the character count.
mov bx,mnchip ;Get the buffer input pointer.
inc bx ;Increment it.
cmp bx,offset mnchrs+mnchnd ;Past the end?
jb mnpro3
mov bx, offset mnchrs ;Yes, point to the start again.
mnpro3: mov mnchip,bx ;Save the pointer.
mov [bx],al ;Put the character in the buffer.
cmp floctl, floxon ;do flow-control? [19a] start
je mnpro4 ;If yes jump
ret
mnpro4: cmp xofsnt, true ;Have we sent an XOFF
jnz mnpro5
ret ;return if we have
mnpro5: cmp mnchrn, mntrg2 ;Past the High trigger point?
ja mnpro6 ;yes - jump
ret
mnpro6: mov al, xoff
call prtout ;send an XOFF
mov xofsnt, true ;set the flag
ret ; [19a] End
mnperr: ret ;Just return on an error for now.
; prtbrk - send a break ; [20b] start
prtbrk: ;
mov dx,mncmd ;break goes to command port
mov al,cbrk+crts+cerr+crxe+cdtr+ctxe ;add break to normal command
out dx,al
mov ax, 275 ;.. for 275 millisec's [34]
call mswait ; [34]
mov al,crts+cerr+crxe+cdtr+ctxe ;RTS & DTR high, Rx & Tx enabled
out dx,al ;return to normal setting
ret ; [19e] end
mswait: ; [34] start
mov cx,5*clckrt ; inner loop count for 1 millisec.
mswai1:
sub cx,1 ;** inner loop takes 20 clock cycles
jnz mswai1 ;**
dec ax ; outer loop counter
jnz mswait ; wait another millisecond
ret ; [34] end
; serini - This routine initializes all devices that need it.
; Called at the start of the program.
serini: cmp mninit,0FFh ; must only do this initialization once
je serin2
mov mninit,0FFh
push es
; Make DEL key return a 7F code rather than the 18 (control-X) ;[30a] begin
; that it ordinarily does. This involves modifying a key
; conversion table in the BIOS. Version 1.107 and later
; contain a pointer to this table at 40:256C, but earlier
; versions which do not are still in common use, so I will
; try to find it with a direct search.
cld
mov ax, 40h ;BIOS segment
mov es, ax
mov di, 2500h ;Start of BIOS
mov al, 0FCh ;Key gives this value
mov ah, 18h ;Translated to this value
mov cx, -1
serina: repnz scasb ;Look for the key value
or cx, cx ;Zero if search failed
jz serinb ; If so, do nothing
cmp es:[di], ah ;Translation value next?
jne serina ; If not, keep looking
mov kbpat, di ;Save the location we're patching.
mov al, 7Fh ;Insert a DEL character
mov es:[di], al
serinb: ;...and continue ;[30a] end
mov dx,icmask
in al,dx ;get current interrupt mask
mov mnxmsk,al ;save it for restore
; NEC recommends that the timer be turned off during interrupt-driven
; serial I/O, but this disables the clock and keyboard repeat. I have
; not had any bad results from leaving it enabled. I will leave the
; disabling code here in case something develops. -- RonB
; or al,ictmof+icmnof;mask off timer and sio interrupts
or al,icmnof ;mask off sio interrupt
out dx,al
mov ax,ds ;save data segment in cseg
mov cs:mndseg,ax ; for use by the interrupt handler
mov ax,0 ;point to zero page to replace
mov es,ax ;the sio interrupt vector
mov ax,es:.mniseg ;after first saving the current vector
mov mnxseg,ax
mov ax,es:.mnioff
mov mnxoff,ax
cli
mov ax,cs
mov es:.mniseg,ax
mov ax,offset mnint
mov es:.mnioff,ax
sti
call stmode ;set mode & baud to defaults
call stbaud
mov dx,mntdc
mov al,00h ;enable transmission of data
out dx,al
mov dx,mndata ;dummy read to clear buffer
in al,dx
mov dx,mnmsk
mov al,txmsk+tbemsk ;set interrupt mask (enable read int)
out dx,al
mov dx,icmask
in al,dx ;enable sio interrupts
and al,not icmnof
out dx,al
pop es
serin2: ret
; serfin - this routine is used to "undo" what serini has done, called
; just before exiting back to cp/m.
serfin:
cmp mninit,0FFh ;check if initialization has been done
jne serfn2 ;if not, don't de-initialize
mov mninit,0
push es
; Unpatch the keyboard conversion table ;[30a] begin
; Restore control-X value for DEL key from our 7F value.
les di, dword ptr kbpat ;Get the patch location
or di, di ;Did we patch it at all?
jz serfia ; If not, skip this
mov kbpat, 0 ;Show no longer patched
mov al, 18h ;Restore control-X value
mov es:[di], al
serfia: ;...and continue ;[30a] end
cli
mov dx,icmask
mov al,mnxmsk ;restore the old interrupt mask
out dx,al
mov ax,0
mov es,ax
mov ax,mnxseg ;restore sio interrupt vector
mov es:.mniseg,ax
mov ax,mnxoff
mov es:.mnioff,ax
sti
pop es
serfn2: ret
; This routine clears the serial port input buffer. It is called to
; clear out excess NAKs that can result from server mode operation.
cfibf: mov mnchrn, 0 ;Say no characters in the buffer.
mov mnchip, OFFSET mnchrs-1+mnchnd ;Reset input pointer.
mov mnchop, OFFSET mnchrs-1+mnchnd ;Reset output pointer.
ret
; set the parity, number of data bits, and number of stop bits
stmode: mov dx,mncmd
mov al,0 ;recommended reset procedure:
out dx,al ;three 0's followed by a cmode
mov al,0
out dx,al
mov al,0
out dx,al
mov al,cmode ;enable mode setting
out dx,al
mov al,m1s ;1 stop, no parity, 8 data, 16x baud
add al,mpn ;Note: these adds are distinct to
add al,m8d ; allow the 8251 time to reset
add al,m16x
out dx,al
mov al,crts+cerr+crxe+cdtr+ctxe ;RTS & DTR high, Rx & Tx enabled
out dx,al
ret
; set the baud rate
stbaud: mov al,mnbaud ;get the baud rate information
cmp al,12 ;check for valid range (0-12)
ja stb02
mov bx,offset baudtb;get address of baud rate table
add al,al ;compute word offset
mov ah,0
add bx,ax
mov dx,tmcmd
mov al,tmch1 ;select timer channel 1
out dx,al
mov dx,tmdata
mov ax,[bx] ;get value
out dx,al ;output low byte
mov al,ah
out dx,al ;output high byte
stb02: ret
dseg $
; Serial port default parameters
mnbaud db 6 ;300 baud
; Interval Timer values (assumes 16x baud rate mode)
baudtb dw 0C00h ;50 baud 0
dw 0800h ;75 baud 1
dw 0600h ;100 baud 2
dw 0574h ;110 baud 3
dw 0400h ;150 baud 4
dw 0300h ;200 baud 5
dw 0200h ;300 baud 6
dw 0100h ;600 baud 7
dw 0080h ;1200 baud 8
dw 0040h ;2400 baud 9
dw 0020h ;4800 baud 10
dw 0010h ;9600 baud 11
dw 0008h ;19200 baud 12
mninit db 0 ;set to 0FFh if initialization has been done
mnxmsk db 0 ;8259 interrupt mask storage
mnxseg dw 0 ;system sio interrupt vector
mnxoff dw 0
mnchnd equ 512 ;Size of circular buffer.
mnchrs rb mnchnd ;Circular character buffer for input.
mnchip dw mnchrs-1+mnchnd ;Input pointer into character buffer.
mnchop dw mnchrs-1+mnchnd ;Output pointer into character buffer.
mnchrn dw 0 ;Number of chars in the buffer.
mntrg1 equ 128 ;[19a] Low trigger point for Auto XON/XOFF
mntrg2 equ 384 ;[19a] High trigger point for Auto XON/XOFF
floctl db 1 ;[19a] If floctl=floxon do Auto XON/XOFF logic
xofsnt db 0 ;[19a] set if XOFF was sent
xofrcv db 0 ;[19a] set if XOFF was recieved
rw 32 ;Interrupt stack ;[28e]
mnstk dw 0 ;bottom of stack ;[28e]
kbpat dw 0000h,0040h ;patch location for DEL key ;[30a]
CSEG $
; The following routines do the SET and SHOW for the machine dependent
; features of Kermit. At present there are only two: baud rate setting
; and port selection.
; This is the SET BAUD rate subcommand
bdset: mov dx, offset bdtab
mov bx, offset bdhlp
mov ah, cmkey
call comnd
jmp r
mov temp1, bx
mov ah, cmcfm
call comnd ;Get a confirm.
jmp r ; Didn't get a confirm.
mov bx, temp1
mov mnbaud, bl ;Set the baud rate table index.
call stbaud
jmp rskp
; This is the SET PORT subcommand (not implemented in APC)
prtset: mov ah, cmcfm
call comnd ;Get a confirm.
jmp $+3 ; Didn't get a confirm.
mov dx, offset infms6 ;Tell user it's not implemented
call tcrmsg
jmp rskp
; The following procedures implement the SHOW command for the system
; dependent features of baud rate and port selection.
shobd: mov dx, offset bdst ;Baud rate string.
call tcrmsg
mov al, mnbaud ;Print the keyword corresponding to the
mov bx, offset bdtab ; current value of mnbaud.
call tabprt
ret
shoprt: ret ;Port selection not implemented.
DSEG $
bdtab db 13 ;Thirteen entries ;[6] begin
db 3,'100$'
dw 0002H
db 3,'110$'
dw 0003H
db 4,'1200$'
dw 0008H
db 3,'150$'
dw 0004H
db 5,'19200$'
dw 000CH
db 3,'200$'
dw 0005H
db 4,'2400$'
dw 0009H
db 3,'300$'
dw 0006H
db 4,'4800$'
dw 000AH
db 2,'50$'
dw 0000H
db 3,'600$'
dw 0007H
db 2,'75$'
dw 0001H
db 4,'9600$'
dw 000BH ;[6] end
bdhlp db cr,lf,' 50 100 150 300 1200 4800 19200'
db cr,lf,' 75 110 200 600 2400 9600$'
; The following routines do screen control. These are isolated here because
; the screen control sequences are likely to vary from system to system, even
; though the Rainbow and APC (the only systems implemented to date) both use
; ANSI sequences for this purpose.
CSEG $
; POSCUR - positions cursor to row and col (each 1 byte) pointed to by dx.
poscur: mov bx, dx ;Do ANSI cursor positioning.
mov cl, 10
mov al, [bx] ;Get row value
sub ah, ah
div cl ;units digit in ah, tens digit in al
add ax, '00' ;Convert both to ASCII
mov word ptr anspos+2, ax ;Save reversed (al,ah)
mov al, 1[bx] ;Do same for column value
sub ah, ah
div cl
add ax, '00'
mov word ptr anspos+5, ax
mov dx, offset anspos ;Print cursor positioning string.
call tmsg
ret
; CLRSCR - homes cursor and clears screen.
clrscr: mov dx, offset apccls
call tmsg
ret
; CLRLIN - clears from cursor to end of line.
clrlin: mov dl, cr ;Go to beginning of line
call bout
clreol: mov dx, offset ansclr ;Clear from cursor to end of line
call tmsg
ret
; REVON - turns on reverse video display
revon: mov dx, offset ansron
call tmsg
ret
; REVOFF - turns off reverse video display
revoff: mov dx, offset ansrof
call tmsg
ret
; BLDON - turns on bold (highlighted) display
bldon: ret
; BLDOFF - turns off bold (highlighted) display
bldoff: ret
DSEG $
anspos db esc,'[00;00H$' ;Position cursor to row and column
apccls db 1Eh,1Ah,'$' ;Home cursor and clear screen
ansclr db esc,'[K$' ;Clear from cursor to end of line
ansron db esc,'[7m$' ;Turn on reverse video
ansrof db esc,'[m$' ;Turn off reverse video
; Here tab expansion is done if necessary. If not, just return retskp.
cseg $
dotab: cmp dl, tab ;A tab?
je dotab1
jmp rskp ;No, just proceed.
dotab1: mov curbuf, 2 ;Report cursor position command.
mov dx, offset curbuf
mov cl, 7
int 220 ;Special BIOS function.
mov al, curbuf+2 ;Column position in binary (0-79).
and al, 07h ;Number of spaces needed
mov cx, 0008h ; = 8 - (col mod 8)
sub cl, al
dotab2: push cx
mov dl, ' '
call dbout2
pop cx
loop dotab2
ret
dseg $
curbuf db 2,0,0 ;command, row, column of cursor position ;[7]
delstr db 10O,10O,'$' ;Delete string.
system db ' NEC Advanced Personal Computer$' ;[1][20a]