; File CCXIBM.ASM ; Kermit system dependent module for IBM-PC ;CHINESE ifdef MSDOS include msxibm.dat else include ccxibm.dat endif code segment public 'code' extrn comnd:near, dopar:near, defkey:near, lclyini:near extrn sleep:near, atsclr:near, scrseg:near,scrloc:near, scrsync:near extrn atoi:near, strlen:near, prtscr:near, scroff:near, scron:near extrn srchkb:near, srchkw:near, prompt:near, statc:near assume cs:code, ds:datas ; local initialization lclini proc near mov flags.comflg,1 ; assume COM1 for communications port ;;; call coms2 ; setup serial port modem.info for COM1 call model ; get model of IBM machine call lclyini ; let other modules initialize too... ret lclini endp ; this is called by Kermit initialization. It checks the ; number of disks on the system, sets the drives variable ; appropriately. Returns normally. DODISK PROC NEAR int mconf ; Get equipment configuration mov ah,al ; Store AL value for a bit and al,01H ; First, look at bit 0 jz dodsk0 ; No disk drives -- forget it mov al,ah ; Get back original value mov cl,6 ; Shift over bits 6 and 7 shr al,cl ; To positions 0 and 1 inc al ; Want 1 thru 4 (not 0 thru 3) dodsk0: mov drives,al ; Remember how many. ret DODISK ENDP ; The IBM PC's. If jr then redo IBM baud rate with jr's values. [jrd] model proc near mov isps2,0 ; PS/2 present indicator push es push ax ; get IBM model code at F000:FFFEh mov ax,0f000h ; address ROM mov es,ax mov al,byte ptr es:[0fffeh] ; get model id byte cmp al,0fdh ; PC jr? jne modelx ; ne = no push ds pop es ; set es to datas segment mov si,offset bddat ; regular IBM baud rate table mov di,offset jrbddat ; PCjr baud rate table mov cl,baudlen ; number of words to copy mov ch,0 cld rep movsw ; copy PCjr values to IBM table jmp short modelx model2: mov ah,0ch ; AT and PS/2 configuration call mov al,0 int 15h ; IBM Bios jc modelx ; c = no information cmp word ptr es:[bx+2],040fch ; PS/2 model 50? je model3 ; e = yes cmp word ptr es:[bx+2],050fch ; PS/2 model 60? je model3 ; e = yes cmp byte ptr es:[bx+2],0f8h ; PS/2 model 80? jne modelx ; ne = no model3: mov isps2,1 ; say real PS/2 for IRQ setting modelx: pop ax pop es ret model endp ; show the definition of a key. The terminal argument block (which contains ; the address and length of the definition tables) is passed in ax. ; Returns a string to print in AX, length of same in CX. ; Returns normally. Obsolete, name here for external reference only. showkey proc near ret ; return showkey endp ; SHOW MODEM, displays current status of lines DSR, CD, and CTS. ; Uses byte mdmhand, the modem line status register. [jrd] shomodem proc near mov ah,cmcfm ; get a confirm call comnd jmp r ; no confirm nop ; mov dx,offset msmsg7 ; no modem status for network mcmsg msmsg7,cmsmsg7 call getmodem ; get modem status mov mdmhand,al mov ah,prstr ; mov dx,offset msmsg1 ; modem ready msg mcmsg msmsg1,cmsmsg1 test mdmhand,20h ; is DSR asserted? jz shomd1 ; z = no ; mov dx,offset msmsg2 ; say not asserted mcmsg msmsg2,cmsmsg2 shomd1: int dos ; mov dx,offset msmsg3 ; CD asserted msg mcmsg msmsg3,cmsmsg3 test mdmhand,80h ; CD asserted? jz shomd2 ; z = no ; mov dx,offset msmsg4 ; say not asserted mcmsg msmsg4,cmsmsg4 shomd2: int dos ; mov dx,offset msmsg5 ; CTS asserted msg mcmsg msmsg5,cmsmsg5 test mdmhand,10h ; CTS asserted? jz shomd3 ; z = no ; mov dx,offset msmsg6 ; say not asserted mcmsg msmsg6,cmsmsg6 shomd3: mov ah,prstr int dos jmp rskp shomodem endp ; Get modem status and set global byte mdmhand. Preserve all registers. getmodem proc near ; gets modem status upon request cmp portin,-1 ; done any port yet? jne getmod1 ; ne = yes mov bl,flags.comflg ; pass current port ident call comstrt ; do SET PORT command now ret ; failed to set port nop nop getmod1:mov al,0 ; assume nothing is on cmp clone,'N' ; Network? je getmodx ; e = yes, no status cmp clone,'B' ; Bios? je getmod2 ; e = yes cmp flags.comflg,0 ; null port? je getmodx ; e = yes, no status mov dx,mst ; hardware inc dx ; uart status reg in al,dx jmp short getmodx getmod2:mov ah,3 ; ask Bios for modem status into al push dx mov dl,flags.comflg ; get port id cmp dl,'0' ; ascii? jb getmod3 ; b = no sub dl,'0' ; remove ascii bias getmod3:int rs232 pop dx getmodx:mov ah,0 ; return status in al ret getmodem endp ; Clear the input buffer. This throws away all the characters in the ; serial interrupt buffer. This is particularly important when ; talking to servers, since NAKs can accumulate in the buffer. ; Returns normally. CLRBUF PROC NEAR cli mov srcpnt,offset source ; receive circular buffer mov count,0 ; receive circular buffer sti call prtchr ; empty any intermediate (net) buffers nop ; got a char, clear again nop nop cli mov srcpnt,offset source ; receive circular buffer mov count,0 ; receive circular buffer mov xmtcnt,0 ; network output buffer count sti ret CLRBUF ENDP ; Clear to the end of the current line. Returns normally. ; Upgraded for Topview compatibility. [jrd] CLEARL PROC NEAR push ax push bx push dx mov ah,3 ; Clear to end of line mov bh,0 int video ; Get current cursor position into dx mov ax,dx ; Topview compatible clear line mov bh,ah ; same row mov bl,byte ptr low_rgt ; last column call atsclr ; clear from ax to bx, screen coord pop dx pop bx pop ax ret CLEARL ENDP ; This routine blanks the screen. Returns normally. ; Upgraded to Topview compatiblity. [jrd] CMBLNK PROC NEAR push ax push bx xor ax,ax ; from screen loc 0,0 mov bx,low_rgt ; to end of text screen (lower right corner) inc bh ; include status line call atsclr ; do Topview compatible clear, in msyibm pop bx pop ax ret CMBLNK ENDP ; Locate: homes the cursor. Returns normally. LOCATE PROC NEAR mov dx,0 ; Go to top left corner of screen jmp poscur LOCATE ENDP ; Position the cursor according to contents of DX: ; DH contains row, DL contains column. Returns normally. POSCUR PROC NEAR push ax push bx mov ah,2 ; Position cursor mov bh,0 ; page 0 int video pop bx pop ax ret POSCUR ENDP ; Delete a character from the terminal. This works by printing ; backspaces and spaces. Returns normally. DODEL PROC NEAR mov ah,prstr mov dx,offset delstr ; Erase weird character int dos ret DODEL ENDP ; Move the cursor to the left margin, then clear to end of line. ; Returns normally. CTLU PROC NEAR mov ah,prstr mov dx,offset clrlin int dos call clearl ret CTLU ENDP BEEP PROC NEAR push ax push cx mov al,10110110B ; Gen a short beep (long one losses data.) out timercmd,al ; set Timer to to mode 3 mov ax,1512 ; divisor, for frequency out timer2data,al ; send low byte first mov al,ah out timer2data,al in al,ppi_port ; get 8255 Port B setting or al,3 ; turn on speaker and timer out ppi_port,al ; start speaker and timer push ax mov ax,40 ; 40 millisecond beep, calibrated time call pcwait pop ax in al,ppi_port and al,0fch ; turn off speaker and timer out ppi_port,al pop cx pop ax ret BEEP ENDP ; write a line in inverse video at the bottom of the screen... ; the line is passed in dx, terminated by a $. Returns normally. putmod proc near push ax ; save regs push bx push cx push dx ; preserve message mov bl,scbattr ; screen attributes at Kermit init time and bl,77h ; get colors, omit bright and blink rol bl,1 ; interchange fore and background rol bl,1 rol bl,1 rol bl,1 mov bh,0 ; preset page 0 mov temp,bx ; temp = page 0, reverse video mov dx,low_rgt ; ending location is lower right corner inc dh ; of status line mov cx,dx ; start is status left side xor cl,cl ; left side mov ax,600h ; scroll to clear the line mov bh,byte ptr temp ; set inverse video attributes int video mov dx,low_rgt ; last text line inc dh ; status line xor dl,dl ; left side call poscur pop si ; get message back mov cx,1 ; only one char at a time xor bh,bh ; page 0 cld putmo1: lodsb ; get a byte cmp al,'$' ; end of string? je putmo2 push si ; save si push ax ; and the char call poscur inc dl ; increment for next write pop ax ; recover char mov ah,9 ; try this mov bx,temp ; page 0, inverse video int video pop si ; recover pointer jmp putmo1 putmo2: pop cx pop bx pop ax ret putmod endp ; clear the mode line written by putmod. Returns normally. clrmod proc near push ax ; save regs push bx push cx push dx mov ax,600h ; do a scroll up mov dx,low_rgt ; ending location is lower right corner inc dh ; of status line mov cx,dx ; start is status left side xor cl,cl ; left side ;************************ modified in Dec.15,1989 mov ax,cx mov bx,dx call atsclr ; mov bh,scbattr ; use screen attributes at Kermit init time ; int video ;************************ modified in Dec.15,1989 pop dx pop cx pop bx pop ax ret clrmod endp ; put a help message on the screen. This one uses reverse video... ; pass the message in ax, terminated by a null. Returns normally. puthlp proc near push bx ; save regs push cx push dx push si push ax ; preserve this cld mov bl,scbattr ; screen attributes at Kermit init time and bl,77h ; get colors, omit bright and blink rol bl,1 ; interchange fore and background rol bl,1 rol bl,1 rol bl,1 mov bh,0 ; preset page 0 mov temp,bx ; temp = page 0, reverse video mov si,ax ; point to it mov dh,1 ; init counter puthl1: lodsb ; get a byte cmp al,lf ; linefeed? jne puthl2 ; no, keep going inc dh ; count it jmp puthl1 ; and keep looping puthl2: cmp al,0 ; end of string? jne puthl1 ; no, keep going mov ax,600h ; scroll to clear window xor cx,cx ; from top left mov dl,4fh ; to bottom right of needed piece mov bh,70h ; inverse video mov bh,bl ; inverse video int video call locate ; home cursor mov bx,0070h ; bh = page 0, bl = inverse video mov bx,temp mov cx,1 ; one char at a time cld ; scan direction is forward pop si ; point to string again puthl3: lodsb ; get a byte cmp al,0 ; end of string? je puthl4 ; yes, stop push si ; save around bios call cmp al,' ' ; printable? jb puth21 ; b = no mov ah,9 ; write char at current cursor position int video ; do the Bios int 10h call inc dl ; point to next column jmp puth23 ; move cursor there puth21: cmp al,cr ; carriage return? jne puth22 ; ne = no xor dl,dl ; set to column zero jmp puth23 puth22: cmp al,lf ; line feed? jne puth23 inc dh ; go to next line puth23: mov ah,2 ; set cursor position to dx int video pop si ; restore pointer jmp puthl3 ; and keep going puthl4: mov dh,byte ptr low_rgt+1 ; go to last line inc dh xor dl,dl call poscur ; position cursor pop si pop dx pop cx pop bx ret puthlp endp ; begin Terminal set & status code ; SET Term parameters, especially for use with VT100 emulator. [jrd] ; Taken from work done originally by James Harvey IUPUI. ; VTS is called only by mssset to set terminal type and characteristics. ; Enter via direct jmp. Exit rskp for success, ret for failure. VTS proc near ; SET TERM whatever mov ah,cmkey ; Parse another keyword ; mov bx,offset vthlp ; Use this help mcmsgb vthlp,cvthlp mov dx,offset vttbl ; Use this table call comnd jmp r nop cmp bh,1H ; marker for terminal type? je vsetu0 ; e = yes cmp bh,2h ; marker for set term color? jne vset1 ; ne = no jmp vsetu2 ; e = yes vset1: cmp bh,3h ; marker for character set? je vsetu3 ; e = yes cmp bh,4h ; marker for roll back control? je vsetu4 ; e = yes cmp bh,10h ; marker for clear-screen? jne vset1a ; ne = no mov ah,cmcfm ; yes call comnd jmp r nop mov vtclear,1 ; set trigger for emulator startup jmp rskp ; return successfully vset1a: cmp bh,8h ; marker for graphics type? jne short vsetu1 ; ne = no, dispatch on bl jmp vsetu8 ; yes vsetu0: mov temp,bx ; set terminal type mov ah,cmcfm call comnd ; Get a confirm jmp r ; Didn't get a confirm nop mov bx,temp mov flags.vtflg,bl ; Set the terminal emulation type mov tekflg,0 ; clear Tek sub mode jmp rskp vsetu1: mov bh,0 ; remove marker bits in bh shl bx,1 ; Make bx a word index jmp vtrtns[bx] ; Dispatch vsetu3: mov ah,cmkey ; Set Term character set mov bx,0 ; Use character set table for help mov dx,offset chatab ; Use character set table call comnd jmp r ; failure nop mov temp,bx mov ah,cmcfm ; get a confirm call comnd jmp vsetu0 ; did not get a confirm nop mov ax,temp ; recover value mov vtemu.vtchset,al ; set default character set jmp rskp ; return successfully vsetu4: mov ah,cmkey ; Set Term Roll On/Off, auto roll back mov bx,0 ; Use on/off table as help mov dx,offset ontab ; Use on/off table call comnd jmp r ; bad keyword nop mov temp,bx mov ah,cmcfm ; get a confirm call comnd jmp r ; did not get a confirm nop mov bx,temp mov vtroll,bl ; set roll state (0=no auto rollback) jmp rskp ; return successfully ; Set Term Color foreground, background vsetu2: mov ah,cmtxt ; get number(s) after set term color ; mov dx,offset colhlp ; use this help mcmsg colhlp,ccolhlp mov bx,offset rdbuf ; temp buffer mov rdbuf,0 ; clear the buffer call comnd jmp r nop cmp ah,0 ; text given? jne vsetu2a ; ne = yes mov ah,prstr ; mov dx,offset erms41 ; say need more parameters mcmsg erms41,cerms41 int dos jmp rskp vsetu2a:mov bx,vtemu.att_ptr ; get address of attributes byte mov bl,[bx] ; get attributes mov byte ptr temp,bl ; save in work temp mov si,offset rdbuf ; si = place where atoi wants text jmp vsetu2b ; analyze colbad: mov ah,prstr ; not in range - complain and exit ; mov dx,offset colerr mcmsg colerr,ccolerr int dos jmp rskp vsetu2x:mov al,byte ptr temp ; get current attributes mov bx,vtemu.att_ptr ; get address of attributes byte mov [bx],al ; store attributes jmp rskp ; and return normally vsetu2b:mov dx,si ; analyze values call strlen ; current length of text to cx jcxz vsetu2x ; z = nothing left vsetu2c:cmp byte ptr[si],' ' ; scan off leading spaces jne vsetu2d ; ne = non-blank text found inc si ; look at next char loop vsetu2c ; cx characters to examine jcxz vsetu2x ; z = nothing left vsetu2d:mov ah,cl ; put length where atoi wants it call atoi ; convert text to numeric in ax jmp colbad ; no value available nop cmp ax,0 ; reset all? regular IBM CGA refresh je vsetu2j ; e = yes cmp ax,1 ; high intensity? je vsetu2k ; e = yes cmp ax,10 ; fast refresh? je vsetu2l ; e = yes cmp ax,30 ; check range jb colbad ; b = too small. complain cmp ax,37 jna vsetu2f ; 30-37 is foreground color cmp ax,40 jb colbad cmp ax,47 ; compare as unsigned jna vsetu2g ; 40-47 is background jmp colbad ; else error vsetu2h:inc si ; skip separator cmp byte ptr[si-1],0 ; ended on null? jne vsetu2b ; ne = no, do more jmp vsetu2x ; e = yes, exit vsetu2f:sub al,30 ; remove foreground bias and byte ptr temp,not 07H ; clear foreground bits mov bx,ax mov al,colortb[bx] ; get reversed bit pattern or byte ptr temp,al ; load new bits jmp vsetu2h vsetu2g:sub al,40 ; remove background bias and byte ptr temp,not 70H ; clear background bits mov bx,ax mov al,colortb[bx] ; get reversed bit pattern mov cl,4 ; rotate 4 positions rol al,cl or byte ptr temp,al ; load new bits jmp vsetu2h vsetu2j:mov refresh,0 ; Regular (slow) screen refresh mov byte ptr temp,07h ; clear all, set white on black jmp vsetu2h ; get next value vsetu2k:or byte ptr temp,08h ; set high intensity jmp vsetu2h ; get next value vsetu2l:mov refresh,1 ; Fast screen refresh jmp vsetu2h vsetu8: mov ah,cmkey ; Set Term graphics mov bx,0 ; Use graphics table as help mov dx,offset graftab ; Use graphics table call comnd jmp r ; bad keyword nop mov temp,bx mov ah,cmcfm ; get a confirm call comnd jmp r ; did not get a confirm nop mov bx,temp mov tekgraf,bl ; set Tek graphics board type jmp rskp ; return successfully ; SET Term flags. These are the (near) equivalent of VT100 Setup mode values. flgset: mov word ptr rdbuf,bx ; save index mov ah,cmkey ; Another keyword mov dx,vtable[bx] ; The table to use mov bx,0 ; Use default help call comnd jmp r nop mov temp,bx ; Save switch value mov ah,cmcfm ; Confirm it call comnd jmp r nop mov dx,temp ; Restore switch value mov bx,word ptr rdbuf ; And index shr bx,1 ; Make it a byte index mov al,vtsflg[bx] ; Get the flag cmp dx,0 ; Set or clear? je flgse1 ; Go clear it or vtemu.vtflgst,al ; Set the flag or vtemu.vtflgop,al ; in runtime flags too jmp rskp ; Give good return flgse1: not al ; Complement and vtemu.vtflgst,al ; Clear the indicated setup flag and vtemu.vtflgop,al ; Clear the indicated runtime flag jmp rskp ; Give good return ; SET Term Tabstops Clear ALL ; SET Term Tabstops Clear AT n1, n2, ..., nx ; SET Term Tabstops At n1, n2, ..., nx tabset: cld ; Make sure this is clear mov di,offset decbuf ; clear our temp work area here mov cx,132 ; 132 columns mov al,0 ; set "not touched" indicator rep stosb ; in all decbuf slots mov ah,cmkey ; Parse keyword ; mov bx,offset clrhlp ; help text mcmsgb clrhlp,cclrhlp mov dx,offset tabtab ; table call comnd jmp r nop mov clrset,2 ; code for set a tab cmp bl,0 ; Was it set or clear? jne tabse1 ; SET - go parse column number(s) mov clrset,1 ; code for clear at/all tab(s) mov ah,cmkey ; CLEAR - parse ALL or AT ; mov bx,offset clrhlp ; Use this help text mcmsgb clrhlp,cclrhlp mov dx,offset alltab ; Parse ALL or AT call comnd jmp r nop cmp bx,0 ; ALL? jne tabse1 ; ne = AT, clear at specific places mov ah,cmcfm ; Confirm the ALL call comnd jmp r nop mov al,1 ; ALL, means clear all tab stops mov cx,132 ; use 132 columns mov di,offset decbuf cld rep stosb jmp tabcpy ; update active & coldstart tabs tabse1: ; mov dx,offset clrhlp ; Tell them we want a column number mcmsg clrhlp,cclrhlp mov ah,cmtxt ; get text w/o white space mov bx,offset rdbuf ; temp buffer call comnd jmp r nop cmp ah,0 ; anything given? jne tabse2 ; ne = yes mov ah,prstr ; mov dx,offset erms41 ; say need more parameters mcmsg erms41,cerms41 int dos jmp rskp tabse2: mov si,offset rdbuf ; si = place where atoi wants text tabse3: mov dx,si call strlen ; cx = current length of text jcxz tabcpy ; z = nothing left mov ah,cl ; put length where atoi wants it call atoi ; convert text to numeric in ax jmp tabcpy ; no number available nop mov bx,ax ; for subscripting in code below dec bx ; put column in range 0-131 cmp bx,0 ; check range (1-132 --> 0-131) jl tbsbad ; l = too small. complain cmp bl,132-1 ; more than the right most column? jna tabse4 ; na = no, is ok tbsbad: mov ah,prstr ; not in range - complain and exit ; mov dx,offset tbserr mcmsg tbserr,ctbserr int dos jmp rskp tabse4: mov al,clrset ; get value for setting or clearing mov decbuf[bx],al ; store in tabs temp work array cmp byte ptr [si],':' ; start-column:spacing notation? jne tabse3 ; ne = no, get next tab stop inc si ; skip colon, do start:space analysis mov temp,bx ; save reg around atoi call mov dx,si ; string address for strlen call strlen ; get remaining string length into cx jcxz tabcpy ; z = no space value, all done here mov ah,cl ; ah = string length for atoi call atoi ; get space value into ax jmp tabcpy ; no number available nop mov bx,temp mov cx,ax ; "space" value cmp cx,0 ; zero spacing? jne tabse4a ; ne = no inc cx ; don't get caught with zero spacing tabse4a:mov al,clrset ; get tab set or clear indicator tabse5: add bx,cx ; new column value cmp bx,132-1 ; largest tab stop ja tabcpy ; a = done largest tab stop mov decbuf[bx],al ; store set or clear indicator jmp short tabse5 ; finish spacing loop, then do tabcpy tabcpy: mov cx,132 ; update all active tab stops mov si,vtemu.vttbst ; in terminal emulator's active buffer mov di,vtemu.vttbs ; and in the cold-start buffer mov bx,0 ; subscript tabcpy1:mov al,decbuf[bx] ; get a table entry into al or al,al ; what is the code? jz tabcpy3 ; z = do not touch cmp al,2 ; set a tab? je tabcpy2 ; e = set the tab mov byte ptr [bx+si],0 ; clear the tab mov byte ptr [bx+di],0 ; clear the tab jmp short tabcpy3 tabcpy2:mov byte ptr [bx+si],0ffh ; set the tab mov byte ptr [bx+di],0ffh ; set the tab tabcpy3:inc bx ; inc subscript loop tabcpy1 jmp rskp ; Give good return VTS endp ; end of Set Term things ; Terminal Status display, called within STAT0: in MSSSET VTSTAT proc near ; enter with di within sttbuf, save bx push bx ; mov bx,offset vtstbl ; table of things to show mcmsgb vtstbl,cvtstbl call statc ; status common code, in mssset nop nop nop pop bx ret ; return to STAT0: in MSSSET colstat proc near ; foreground/background color status push si ; mov si,offset colst1 mcmsgsi colst1,ccolst1 cld colstd1:lodsb cmp al,'$' ; end of string? je colstd2 ; e = yes stosb jmp short colstd1 colstd2:mov bx,vtemu.att_ptr ; pointer to attributes byte mov bl,byte ptr[bx] mov bh,0 push bx and bx,7 ; get foreground set mov al,colortb[bx] ; get reversed bit pattern add al,'0' ; add ascii bias stosb pop bx ; mov si,offset colst2 mcmsgsi colst2,ccolst2 colstd3:lodsb cmp al,'$' je colstd4 stosb jmp short colstd3 colstd4:mov cl,4 ; rotate 4 positions shr bl,cl and bx,7 ; get background set mov al,colortb[bx] ; get reversed bit pattern add al,'0' ; add ascii bias stosb pop si ret colstat endp ; Tabs Status display tabstat proc near ; display tabs ruler for Status push dx cld tabstd2:mov al,cr stosb tabsta0:mov si,vtemu.vttbst ; active tabs address, not shadow mov cl,byte ptr low_rgt ; loop screen width-1 times xor ch,ch ; clear high byte dec si ; dec for inc below xor ax,ax ; tens counter tabsta1:mov dl,'.' ; default position symbol inc si ; start with position 1 inc al cmp al,10 ; time to roll over? jb tabsta2 ; b = not yet mov al,0 ; modulo 10 inc ah mov dl,ah ; display a tens-digit add dl,'0' cmp dl,'9' ; larger than 90? jbe tabsta2 ; be = no sub dl,10 ; roll over to 0, 1, etc tabsta2:cmp byte ptr [si],0 ; is tab set? je tabsta3 ; e = no mov dl,'T' ; yes, display a 'T' tabsta3:push ax mov al,dl stosb pop ax loop tabsta1 ; loop til done (cx has count) pop dx ret tabstat endp filler proc near ; use space mov cx,20 mov al,' ' rep stosb ret filler endp VTSTAT endp ; end of Terminal set & status code ; Compute number of iterations needed in procedure pcwait inner loop ; to do one millisecond delay increments. Uses Intel 8253/8254 timer chip ; (timer #2) to measure elapsed time assuming 1.193182 MHz clock. ; Called by serini below. For IBM PC compatible machines. ; Regs preserved. 16 April 87 [jrd] pcwtst proc near push ax push cx push dx mov pcwcnt,256 ; software loop, initial value in al,ppi_port ; 8255 chip port B, 61h and al,0fch ; speaker off (bit 1), stop timer (bit 0) out ppi_port,al ; do it ; 10 = timer 2, 11 = load low byte then high byte, 010 = mode 2, 0 = binary mov al,10110100B ; command byte out timercmd,al ; timer command port, 43h xor al,al ; clear initial count for count-down out timer2data,al ; low order byte of count preset, to port 42h out timer2data,al ; high order byte, to the same place in al,ppi_port ; get 8255 setting mov dl,al ; remember it in dl and al,0fch ; clear our control bits or al,1 ; start counter now (Gate = 1, speaker is off) out ppi_port,al ; do it, OUT goes low ; this is the test loop mov ax,8 ; wait 8 millisec call pcwait ; call the software timer ; end test loop mov al,dl ; restore ppi port, stop timer out ppi_port,al in al,timer2data ; read count down value xchg al,ah ; save low order byte in al,timer2data ; get high order byte xchg ah,al ; put in correct sequence neg ax ; subtract from zero to get elapsed tics xor dx,dx ; clear high order divisor add ax,1193/2 ; round up adc dx,0 ; for very very slow machines mov cx,1193 ; tics per millisec div cx ; count / 1193 yields millisecs, quo=ax mov cx,ax ; retain whole number of milliseconds mov ax,pcwcnt ; get current pcwait inner loop count xor dx,dx ; clear high order field for division shl ax,1 rcl dx,1 shl ax,1 rcl dx,1 shl ax,1 rcl dx,1 ; dx:ax = current counter times 8 loops cmp cx,0 ; no millisec? (super speed machines) ja pcwtst1 ; a = some inc cx ; use at least one pcwtst1:div cx ; divide by observed milliseconds mov pcwcnt,ax ; store quotient as new inner loop counter pop dx pop cx pop ax ret pcwtst endp ;; Wait for the # of milliseconds in ax, for non-IBM compatibles. ;; Thanks to Bernie Eiben for this one. Modified to use adjustable ; inner loop counter (pcwcnt, adjusted by proc pcwtst) by [jrd]. pcwait proc near push cx pcwai0: mov cx,pcwcnt ; inner loop counter for 1 ms (240 @ 4.77 MHz) pcwai1: sub cx,1 ; inner loop takes 20 clock cycles jnz pcwai1 dec ax ; outer loop counter jnz pcwai0 ; wait another millisecond pop cx ret pcwait endp ; set the current port. ; Note: serial port addresses are found by looking in memory at 40:00h and ; following three words for COM1..4, resp. All UARTS are assumed to follow ; std IBM addresses relative to 03f8h for COM1, and actual address are offset ; from value found in segment 40h. Global byte flags.comflg is 1,2,3,4 for ; COM1..4, and is 'N' for Network. ; If address 02f8h is found in 40:00h then name COM1 is retained but COM2 ; addressing is used to access the UART and a notice is displayed. IRQ 3 ; or IRQ 4 is sensed automatically for any COMx port. COMS PROC NEAR mov dx,offset comptab ; table of legal comms ports mov bx,0 ; no extra help text mov ah,cmkey ; parse key word call comnd jmp r ; failed mov temp,bx cmp bl,'N' ; NetBios network? je comstrt ; yes, get another item for networks cmp bl,'U' ; Ungermann Bass net? je comstrt ; e = yes mov ah,cmcfm ; non-network call comnd ; Get a confirm jmp r ; Didn't get a confirm nop mov bx,temp COMSTRT:cmp portin,-1 ; first time here? jne comst1 ; ne = no mov portin,0 ; say have been here before comst1: mov temp,bx cmp bl,'N' ; NetBios network? jne comst2 ; ne = no jmp comsn ; yes, get another item for networks comst2: cmp bl,'U' ; Ungermann Bass net? jne comst3 ; ne = no jmp comsub comst3: cmp flags.comflg,'N' ; have been running on network? jne coms1b ; ne = no ; turn off sources of net interrupts mov bx,offset can ; cancel outstanding requests mov can.scb_cmd,ncancel ; set cancel op code cmp lposted,1 ; listen outstanding? jne coms1a ; ne = no mov can.scb_baddr,offset lsn ; cancel listen call session mov lposted,0 coms1a: cmp rposted,1 ; receive outstanding? jne coms1b ; ne = no mov can.scb_baddr,offset rcv ; cancel receive call session mov rposted,0 ; clear interlock flag coms1b: call serrst ; close current comms port mov bx,temp ; get port number/letter mov flags.comflg,bl ; remember port number mov bh,0 mov bl,flags.comflg ; get COMx number (1-4) push bx ; Set UART port addresses mov ax,bx ; get COMx (1-4) cmp al,'1' ; ascii numbered Bios port? jb coms3 ; b = no sub al,'0' ; remove ascii bias for portinfo coms3: dec ax ; count ports from zero mov bx,type prtinfo ; size of each portinfo structure mul bx ; times port number add ax,offset port1 ; plus start of COM1 mov portval,ax ; points to our current port struct pop bx ; restore registers cmp bl,' ' ; doing forced Bios? jb coms4 ; b = no, hardware mov clone,'B' ; set clone flag for Bios usage jmp rskp ; all done coms4: push bx ; save register push es mov ax,40h ; look at RS232_base [bx] in Bios area 40:00h mov es,ax dec bl ; count com1 as bl = 0, etc mov bh,0 ; clear high byte shl bx,1 ; make bx a word index mov ax,es:[bx] ; get modem base address into ax pop es pop bx or ax,ax ; is address zero? je comsf ; e = yes, no serial port comsc: ; hardware tests mov modem.mddat,ax ; set base address (also data address) 03f8h add ax,3 ; increment to command port 03fbh mov modem.mdcom,ax ; set line control register address mov brkadr,ax ; where to send break command add ax,2 ; increment to status port 03fdh mov modem.mdstat,ax ; set line-status port address call chkport ; get type of UART support (for Clone) jnc comsu ; nc = has a real 8250 uart add flags.comflg,'0' ; COMn to BIOSn push ax ; else tell user about Bios pathway push dx mov ah,prstr ; mov dx,offset biosmsg mcmsg biosmsg,cbiosmsg int dos pop dx pop ax jmp rskp comsu: call chkint ; find IRQ for the port jc comsf ; c = not found, an error conditon jmp rskp comsf: ; mov dx,offset badprt ; say port is not available mcmsg badprt,cbadprt mov ah,prstr int dos mov al,flags.comflg ; port ident character cmp al,' ' ; binary for COM series jae comsf1 ; ae = no mov dx,offset compt ; display COM int dos mov ah,conout mov dl,flags.comflg add dl,'0' ; display port number int dos jmp comsf3 comsf1: cmp al,'9' ; ascii numeric for Bios series? ja comsf2 ; a = no mov dx,offset biospt ; display BIOS int dos mov ah,conout mov dl,flags.comflg ; ascii port number int dos jmp comsf3 comsf2: ; mov dx,offset unkpt ; unknown type mcmsg unkpt,cunkpt int dos comsf3: mov flags.comflg,0 ; bad port, reassign to null port mov ah,prstr ; mov dx,offset badprt2 ; the rest of the message mcmsg badprt2,cbadprt2 mov ah,prstr int dos stc ; say error jmp rskp ; NetBios Network support comsn: mov ah,cmfile ; get a word (remote node name) mov dx,offset nambuf ; work buffer mov word ptr nambuf,0 ; insert terminator ; mov bx,offset nethlp ; help message mcmsgb nethlp,cnethlp call comnd ; get the name nop nop nop xchg ah,al ; put byte count in al xor ah,ah ; clear junk mov temp,ax ; save number of chars entered mov ah,cmcfm call comnd ; Get a confirm jmp r ; Didn't get a confirm nop call serrst ; reset serial port call chknet ; start network usage cmp pcnet,0 ; is network alive (non-zero)? jne comsn4 ; ne = yes stc jmp rskp ; return failure comsn4: mov portval,offset portn ; set Network port data structure address mov flags.comflg,'N' ; Set the comm port flag call chkport ; set type of port support clc ; return success jmp rskp ; End NetBios ; Ungermann-Bass terminal port [ohl +] comsub: mov ah,cmcfm call comnd ; Get a confirm jmp r ; Didn't get a confirm nop call serrst ; reset serial port call chkub ; check UB network presence jnc comsub1 ; nc = present stc jmp rskp ; return failure comsub1:call netclose ; better close NetBios parts NOW! mov portval,offset portn ; set Network port data structure address mov flags.comflg,'U' ; Set the comm port flag mov pcnet,2 ; network is present and active mov lclexit,offset ubclose ; address to close network call chkport ; get type of port support clc ; return success jmp rskp ; End Ungermann Bass [ohl -] COMS ENDP ; Check which Interrupt ReQuest line the port uses. Technique: allow interrupt ; on transmitter holding register empty, test for that condition first with ; IRQ 4 and then IRQ 3. Returns with IRQ values set and carry clear if success ; or carry set if failure. [jrd] chkint proc near cmp flags.comflg,2 ; COM1 or COM2? jbe chkin2 ; be = yes, use standard IRQ's mov modem.mddis,MDMINTC ; IRQ 4 test. mask to disable IRQ 4 mov modem.mden,MDMINTO ; mask to enable IRQ 4 mov modem.mdmeoi,20h ; use general in case we guess wrong mov modem.mdintv,MDMINTV ; IRQ 4 interrupt vector (0ch) mov intkind,0 ; clear interrupt cause call serini ; setup port for IRQ 4 jc chkint2 ; c = failure mov dx,modem.mddat inc dx ; interrupt enable reg (3f9h) mov al,2 ; set xmtr holding reg empty interrupt out dx,al mov ax,1 ; wait one millisec for interrupt call pcwait ; to occur test intkind,2 ; check cause of interrupt, ours? jz chkint2 ; z = no, try other IRQ call serrst ; reset port mov modem.mdmeoi,EOICOM ; use specific EOI for IRQ4 level clc ; this setup worked ret ; IRQ 3 test chkint2:call serrst ; reset port mov modem.mddis,MDINTC2 ; mask to disable IRQ 3 mov modem.mden,MDINTO2 ; mask to enable IRQ 3 mov modem.mdmeoi,20h ; use general in case we guess wrong mov modem.mdintv,MDINTV2 ; IRQ 3 interrupt vector mov intkind,0 ; clear interrupt cause call serini ; setup port for IRQ 3 jc chkin2 ; c = failure mov dx,modem.mddat inc dx ; interrupt enable reg (3f9h) mov al,2 ; set xmtr holding reg empty interrupt out dx,al mov ax,1 ; wait one millisec for interrupt call pcwait ; to occur test intkind,2 ; check cause of interrupt, ours? jz chkin2 ; z = no, so no interrupts for port call serrst ; reset port mov modem.mdmeoi,EOICOM2 ; use specific EOI for IRQ 3 level clc ; this setup worked ret chkin2: call serrst ; reset port, auto test did not work cmp flags.comflg,1 ; COM1? je chkin4 ; e = yes, use IRQ 4 cmp isps2,0 ; IBM PS/2 Model 50 or above? jne chkin3 ; ne = yes, other COMs use IRQ 3 cmp flags.comflg,3 ; COM2, COM3, or COM4? je chkin4 ; e = COM3, use IRQ 4 jmp short chkin3 ; else COM2 or COM4, use IRQ 3 chkin4: cmp modem.mddat,02f8h ; really COM2 material for PCjr? je chkin3 ; e = yes, use COM2 addresses mov modem.mdmeoi,EOICOM ; use specific EOI for IRQ4 level mov modem.mddis,MDMINTC ; IRQ 4 test. mask to disable IRQ 4 mov modem.mden,MDMINTO ; mask to enable IRQ 4 mov modem.mdintv,MDMINTV ; IRQ 4 interrupt vector (0ch) jmp short chkin5 chkin3: mov modem.mdmeoi,EOICOM2 ; use specific EOI for IRQ 3 level mov modem.mddis,MDINTC2 ; mask to disable IRQ 3 mov modem.mden,MDINTO2 ; mask to enable IRQ 3 mov modem.mdintv,MDINTV2 ; IRQ 3 interrupt vector chkin5: clc ret chkint endp ; Test presently selected serial port for having a real 8250 UART. ; Return carry clear and clone = 0 if 8250 present, ; else carry set and clone = 'B' for system Bios or ; carry set and clone = 'N' for network. ; Method is to check UART's Interrupt Identification Register for high ; five bits being zero; IBM does it this way. Assumes port structure ; has been initialized with addresses of UART. 21 Feb 1987 [jrd] ; 29 May 1987 Add double check by reading Line Status Register. [jrd] chkport proc near cmp portval,offset portn ; network? je chkporn ; e = yes push ax push dx cmp flags.comflg,0 ; undefined port? je chkpor1 ; e = yes, assume Bios clone mov dx,modem.mdcom ; address of UART line control reg (3FBh/2FBh) sub dx,1 ; Interupt Identification Register address in al,dx ; read UART's IIR test al,0f8h ; are any of high 5 bits set? jnz chkpor1 ; nz = yes, not an 8250 mov dx,modem.mdstat ; line status register in al,dx ; read to clear UART BI, FE, PE, OE bits jmp $+2 ; pause, for chip access timing in al,dx ; these bits should be cleared test al,8eh ; are they cleared? jnz chkpor1 ; nz = no, not an 8250 mov clone,0 ; clear clone flag pop dx pop ax clc ; clear carry (say 8250) ret chkpor1:pop dx pop ax mov clone,'B' ; set clone flag stc ; set carry (say no 8250) ret chkporn:push ax ; Networking mov al,flags.comflg ; get port letter mov clone,al pop ax stc ; set carry (say no 8250) ret chkport endp ;;;;;;;;;;;;;;;;;;;;;; end of part one of msxibm.asm ;;;;;;;;;;;;;;;;;;;;;; start part two of msxibm.asm ; Set the baud rate for the current port, based on the value ; in the portinfo structure. Returns normally. ; 21 Feb 1987 Add support for Bios calls (Clone) [jrd] DOBAUD PROC NEAR cmp portin,-1 ; port not used yet? jne dobd3 ; ne = no, go get rate mov bl,flags.comflg ; pass current port ident call comstrt ; do SET PORT command now ret ; failed to set port nop nop dobd3: push ax ; save some regs push bx push dx call chkport ; check port for real 8250 UART (clone = 0) cmp flags.comflg,'N' ; Netbios? je dobd1 ; e = yes, do nothing here cmp flags.comflg,'U' ; UB network? je dobd1 ; e = yes mov bx,portval ; pointer to port data structure mov temp,ax ; Don't overwrite previous rate mov ax,[bx].baud ; Check if new rate is valid shl ax,1 ; make a word index mov bx,offset bddat ; Start of table cmp clone,'B' ; running on clone? jne dobd0a ; ne = no mov bx,offset clbddat ; use Bios speed parameters for clone dobd0a: add bx,ax mov ax,[bx] ; The data to output to port cmp ax,0FFH ; Unimplemented baud rate jne dobd0 mov ah,prstr ; mov dx,offset badbd ; Give an error message mcmsg badbd,cbadbd int dos jmp dobd1 dobd0: cmp clone,0 ; running on a real uart? je dobd2 ; e = the real thing mov dx,0 ; assume port 1 Clone: find current port mov dl,flags.comflg ; get coms port (1..4) or dl,dl ; zero (undefined port)? jz dobd1 ; z = yes, just exit and dl,7 ; use lower three bits dec dl ; count ports as 0..3 for Bios mov ah,0 ; set serial port int rs232 ; Bios: set the parameters jmp dobd1 ; and exit dobd2: mov temp,ax ; Remember value to output mov dx,modem.mdcom ; LCR -- Initialize baud rate in al,dx ; get it mov bl,al ; make a copy or ax,80H ; turn on DLAB bit to access divisor part out dx,al mov dx,modem.mddat mov ax,temp ; set the baud rate divisor, low byte out dx,al inc dx ; next address for high part mov al,ah ; set high part of divisor out dx,al mov dx,modem.mdcom ; LCR again mov al,bl ; get original setting from bl out dx,al ; restore it dobd1: pop dx ; restore regs pop bx pop ax ret DOBAUD ENDP ; Get the current baud rate from the serial card and set it ; in the portinfo structure for the current port. Returns normally. ; This is used during initialization. ; 21 Feb 1987 Add support for Bios calls (Clone) [jrd] GETBAUD PROC NEAR cmp portin,-1 ; port not used yet? jne getb4 ; ne = no, go get rate mov bl,flags.comflg ; pass current port ident call comstrt ; do SET PORT command now ret ; failed to set port nop nop getb4: cmp clone,0 ; non-clone je getbud ; e = yes, real thing ret ; have semi-clone, no bios feedback getbud: push ax ; save some regs push bx push cx push dx mov dx,modem.mdcom ; Get current Line Control Register value in al,dx mov bl,al ; Save it or ax,80H ; Turn on to access baud rate generator out dx,al mov dx,modem.mddat ; Divisor latch inc dx in al,dx ; Get hi order byte mov ah,al ; Save here dec dx in al,dx ; Get lo order byte push ax mov dx,modem.mdcom ; Line Control Register mov al,bl ; Restore old value out dx,al pop ax cmp ax,0FFFFH ; Who knows what this is je getb2 mov bx,offset bddat ; Find rate's offset into table mov cl,0 ; Keep track of index getb0: cmp ax,[bx] je getb1 inc cl cmp cl,baudlen ; At the end of the list jge getb2 add bx,2 jmp getb0 getb1: mov ch,0 mov bx,portval mov [bx].baud,cx ; Set baud rate jmp getb3 getb2: mov ah,prstr ; mov dx,offset erms40 mcmsg erms40,cerms40 int dos getb3: pop dx ; restore regs pop cx pop bx pop ax ret GETBAUD ENDP ; Get Char from serial port buffer. ; skip returns if no character available at port, ; otherwise returns with char in al, # of chars in buffer in dx. ; Revised 22 May 1986, and again slightly 2 August 1986 by [jrd] ; 21 Feb 1987 Add support for Bios calls (Clone) [jrd] ; 25 April 1987 Add Netbios support, remove test for NUL and DEL. [jrd] PRTCHR PROC NEAR cmp holdscr,0 ; Holdscreen in effect? jne prtch0a ; ne = yes, do not read call chkxon ; see if we need to xon cmp clone,'B' ; running on a semi-clone? je prtch6 ; e = yes cmp clone,'N' ; running on NetBios network? je prtchn ; e = yes cmp clone, 'U' ; running Ungermann-Bass port? [ohl] jne prtch0 ; ne = no prtchn: test xofsnt,usron ; user level xoff sent? jnz prtch0a ; nz = yes, suppress reading here cmp count,mntrgl ; below low water mark? ja prtch1 ; a = no, read current buffer cmp clone,'U' ; UB network? jne prtchn1 ; ne = no call ubrecv ; do a UB receive jmp short prtch0 prtchn1:call receive ; do a NetBios receive (asynchronous) jc prtch0a ; c = failure prtch0: cmp count,0 ; any characters available? jnz prtch1 ; nz = yes, get one prtch0a:mov dx,0 ; return count of zero jmp rskp ; No data - check console prtch1: push si ; save si cli ; interrupts off, to keep srcpnt & count consistent mov si,srcpnt ; address of next available slot in buffer sub si,count ; minus number of unread chars in buffer cmp si,offset source ; located before start of buf? jae prtch2 ; ae = no add si,bufsiz ; else do arithmetic modulo bufsiz prtch2: mov al,byte ptr [si] ; get a character into al dec count ; one less unread char now sti ; interrupts back on now pop si mov dx,count ; return # of chars in buffer jmp prtch12 ; screen delivered characters prtch6: ; Semi-clone, use Bios calls mov dx,0 ; assume port 1 Clone: find current port mov dl,flags.comflg ; get port number (1..4) or dl,dl ; zero (no such port)? jz prtch8 ; z = yes, don't access it and dl,7 ; use low three bits dec dl ; address ports as 0..3 for Bios prtch7: mov ah,3 ; check port status int rs232 ; Clone Bios call test ah,mdminp ; data ready? jnz prtch9 ; nz = yes, get one prtch8: mov dx,0 ; return count of zero mov count,dx jmp rskp ; No data prtch9: mov ah,2 ; receive a char into al int rs232 ; Clone Bios call test ah,8ch ; timeout, framing error, parity error? jnz prtch8 ; nz = error, no char mov dx,1 mov count,dx ; one char received into al cmp al,flowoff ; acting on Xoff? jne prtch10 ; ne = no, go on cmp xofsnt,0 ; have we sent an outstanding XOFF? jne prtch8 ; ne = yes, ignore (possible echo) mov xofrcv,bufon ; Set the flag saying XOFF received jmp prtch8 ; and exit prtch10:cmp al,flowon ; acting on Xon? jne prtch12 ; ne = no, go on mov xofrcv,off ; Clear the XOFF received flag jmp short prtch8 ; no data to return prtch12:test flags.debug,logses ; debug mode? jnz prtch14 ; nz = yes, pass all chars cmp rxtable+256,0 ; translation turned off? jne prtch14 ; ne = table is on, pass all chars cmp al,0 ; NUL? je prtch13 ; e = yes, ignore it cmp tekflg,0 ; Tek emulation active? jne prtch14 ; ne = yes, pass DEL cmp al,DEL ; DEL char jne prtch14 ; ne = no, pass char prtch13:mov dx,0 jmp rskp ; no chars prtch14:ret ; return char in al PRTCHR ENDP ; Network Receive packet routine. Request a net packet with no-wait option. ; Return carry clear if success. If failure, reset serial port (Server mode ; reinits serial port) and return carry set. No entry setup needed. RECEIVE PROC NEAR ; receive network session pkt cmp pcnet,1 ; net ready yet? jbe receiv3 ; be = no, declare a broken session cmp rposted,1 ; is a request outstanding now? jae receiv4 ; ae = yes, don't do another mov rposted,1 ; say posting a receive now mov rcv.scb_length, length rcvbuf ; length of input buffer mov rcv.scb_cmd,nreceive+nowait ; receive, no wait push bx mov bx,offset rcv ; setup pointer to scb call session pop bx ; tests below SHOULD take time cmp rcv.scb_err,0 ; success? je receiv4 ; e = yes cmp rcv.scb_err,npending ; pending receive? je receiv4 ; e = yes push ax ; wait one millisec before retesting push cx mov ax,1 call pcwait pop cx pop ax cmp rcv.scb_err,06h ; message incomplete? je receiv4 ; e = is ok (response posted later) cmp rcv.scb_err,0 ; success now? (here for latency effects) je receiv4 ; e = yes cmp rcv.scb_err,18h ; session ended abnormally? jbe receiv3 ; e = yes, b = other normal errors push ax ; save regs around error display push dx mov ah,prstr ; mov dx,offset recmsg ; give error message mcmsg recmsg,crecmsg int dos mov al,rcv.scb_err ; get error code call hexout ; show error code pop dx pop ax ; Error return receiv3:mov pcnet,1 ; say session is broken call serrst ; reset serial port test flags.remflg,dserver ; server mode? jz receiv3a ; z = no call serini ; reinitialize it for new session receiv3a:stc ; say failure to receive ret receiv4:clc ; carry clear = success ret RECEIVE ENDP ; Network Receive post processing interrupt routine. ; Copy chars from rcvbuf to circular buffer source, act on xon/xoff, ; clear rposted interlock flag. At entry, CS is our code segment, ; es:bx points to scb, netbios stack, interrupts are off. RPOST PROC NEAR ; network receive post interrupt routine push ds ; transfers chars from net buf rcvbuf to push ax ; main circular buffer source push bx push cx push dx push si mov ax,datas ; reestablish datas segment mov ds,ax mov cx,rcv.scb_length ; get returned byte count jcxz rpost6 ; z = nothing there mov dh,flowon ; flow control characters, for quick ref mov dl,flowoff mov si,offset rcvbuf ; source of text mov bx,srcpnt ; address of buffer storage slot cld rpost1: lodsb ; get byte from rcvbuf to al or dx,dx ; doing flow control? jz rpost3 ; z = no mov ah,al ; get copy of character and ah,parmsk ; strip parity, if any, before testing cmp ah,dl ; acting on Xoff? jne rpost2 ; ne = Nope, go on cmp xofsnt,0 ; have we sent an XOFF? jne rpost5 ; ne = yes, ignore this XOFF char mov xofrcv,bufon ; Set the flag saying buffer XOFF received jmp rpost5 ; and skip this character rpost2: cmp ah,dh ; acting on Xon? jne rpost3 ; ne = no, go on mov xofrcv,off ; Clear the XOFF received flag jmp rpost5 ; and skip this character rpost3: mov byte ptr [bx],al ; store the new char in buffer "source" inc bx cmp bx,offset source + bufsiz ; beyond end of buffer? jb rpost4 ; b = not past end mov bx,offset source ; wrap buffer around rpost4: cmp count,bufsiz ; filled already? jae rpost5 ; ae = yes inc count ; no, add a char rpost5: loop rpost1 mov srcpnt,bx ; update pointer to next free slot rpost6: mov rposted,0 ; clear interlock flag pop si pop dx pop cx pop bx pop ax pop ds iret ; return from interrupt RPOST endp ; Ungermann-Bass NETCI port receive characters routine. Receive one or more ; characters. Calls the Rpost routine to transfer character to main source ; circular buffer. Return carry clear if success. UBRECV PROC near push ax push bx push cx push es mov ax, datas mov es, ax ; es:bx will point to rcvbuf mov ax, nciread ; function 1 (receive) port 0 [ohl] mov bx, offset rcvbuf mov cx, length rcvbuf int netci ; get characters [ohl] stc jcxz ubrec1 ; cx = z = nothing to do mov rcv.scb_length, cx ; prepare for rpost call [ohl] mov bx, offset rcv pushf ; simulate interrupt [ohl] push cs ; rpost is an interrupt routine (iret) call rpost ; do Near call with preset stack clc ubrec1: pop es pop cx pop bx pop ax ret UBRECV ENDP ; Put the char in AH to the serial port. This assumes the ; port has been initialized. Should honor xon/xoff. Skip returns on ; success, returns normally if the character cannot be written. ; 21 Feb 1987 Add support for Bios calls (Clone) [jrd] ; 25 April 1987 Add Netbios support [jrd] ; 16 May 1987 Add entry point OUTCH2 for non-flow controlled sending to ; prevent confusion of flow control logic at top of outchr; used by receiver ; buffer high/low water mark flow control code. [jrd] OUTCHR PROC NEAR cmp flowoff,0 ; Are we doing flow control je outch2 ; No, just continue cmp ah,flowoff ; sending xoff? jne outch1 ; ne = no mov xofsnt,usron ; indicate user level xoff being sent jmp outch1b outch1: cmp ah,flowon ; user sending xon? jne outch1b ; ne = no mov xofsnt,off ; say an xon has been sent (cancels xoff) outch1b:cmp xofrcv,off ; Are we being held (xoff received)? je outch2 ; e = no - it's OK to go on cmp flags.timflg,0 ; is timer off? je outch2 ; e = yes, no timeout period push cx ; save reg mov ch,trans.rtime ; receive timeout interval (sec) mov cl,0 ; convert to 4 millsec increments jcxz outch1c ; z = no timeout wanted outch1a:cmp xofrcv,off ; Are we being held (xoff received)? je outch1c ; e = no - it's OK to go on push ax mov ax,4 ; 4 millisec wait loop call pcwait pop ax loop outch1a ; and try it again mov xofrcv,off ; timed out, force it off and fall thru outch1c:pop cx ; end of flow control section ; OUTCH2 is entry point for sending without flow control OUTCH2: mov al,ah ; Parity routine works on AL call dopar ; Set parity appropriately mov ah,al ; Don't overwrite character with status outch3: cmp clone,0 ; real uart? je outch3a ; e = yes cmp clone,'N' ; network? je outch8 ; e = yes, using netbios cmp clone,'U' ; Ungermann Bass network? je outch8 ; e = yes jmp outch6 ; default for others ('B' clones) outch3a:push cx ; Save registers push dx sub cx,cx outch3b:mov dx,modem.mdstat ; Get port status in al,dx test al,20H ; Transmitter ready? jnz outch4 ; Yes jmp $+2 ; use time, prevent overdriving UART jmp $+2 loop outch3b jmp outch5 ; Timeout outch4: mov al,ah ; Now send it out mov dx,modem.mddat ; use a little time jmp $+2 out dx,al pop dx ; exit success pop cx jmp rskp outch5: pop dx ; exit failure pop cx ret ; finish up for semi-clones, use Bios calls outch6: push cx ; Clone: find current port push dx mov dx,0 ; assume port 1 mov dl,flags.comflg ; get port number (1..4) or dl,dl ; zero (no such port)? jz outch5 ; z = yes, don't access it and dl,7 ; use lower three bits dec dl ; address ports as 0..3 for Bios mov al,ah ; Now send it out mov ah,1 ; send char int rs232 ; Clone: bios send pop dx ; exit success pop cx jmp rskp outch8: ; Network sending, buffered and single char push bx mov bx,xmtcnt ; count of chars in buffer mov xmtbufx[bx],ah ; put char in buffer pop bx inc xmtcnt ; count of items in this buffer cmp xmtcnt,length xmtbuf ; is buffer full now? jae outch9 ; ae = buffer is full, send it now cmp ah,trans.seol ; end of packet? je outch9 ; e = yes, send buffer cmp ah,flowon ; flow control? je outch9 ; e = yes, always expedite cmp ah,flowoff ; ditto for flow off je outch9 cmp ttyact,0 ; are we in Connect mode? je outch10 ; e = no, wait for more before sending outch9: cmp clone, 'U' ; check for UB port [ohl] je outch12 ; e = yes [ohl] call send ; NetBios network send routine jc outch11 ; c = error outch10:jmp rskp ; good exit outch11:ret ; bad exit outch12:call ubsend ; UB network send [ohl] jmp rskp ; good exit [ohl] OUTCHR ENDP ; Network Send packet routine. Send xmt scb with no-wait option. Waits ; up to 6 seconds for current Send to complete before emitting new Send. ; Failure to Send resets serial port (Server mode allows reiniting of serial ; port). Returns carry clear for success, carry set for failure. ; Enter with xmtcnt holding length of data in xmtbuf to be sent. SEND PROC NEAR ; Network. Send session packet cmp pcnet,1 ; network ready yet? ja send0b ; a = net is operational je send0c ; e = net but no session, fail jmp send3a ; no net, fail send0c: jmp send3 ; net but no session send0b: cmp sposted,0 ; is a send outstanding now? je send1 ; e = no, go ahead push cx ; Timed test for old send being done mov ch,trans.rtime ; receive timeout other side wants mov cl,80h ; plus half a second shl cx,1 ; sending timeout * 512 send0: cmp sposted,0 ; is a send outstanding now? je send0a ; e = no, clean up and do send push cx ; save cx push ax ; and ax mov ax,2 ; wait 2 milliseconds call pcwait ; between retests pop ax pop cx ; loop counter loop send0 ; repeat test pop cx ; recover cx jmp send3a ; get here on timeout, can't send send0a: pop cx ; recover cx and proceed to send send1: cmp xmtcnt,0 ; number of items to send jne send1a ; ne = some clc ; else don't send null packets ret send1a: push bx ; save bx mov bx,xmtcnt ; buffer length mov xmt.scb_length,bx ; tell buffer length push cx push si push di push es push ds pop es ; set es to datas segment mov si,offset xmtbufx ; external buffer mov di,offset xmtbuf ; copy for network packets mov cx,bx ; buffer length shr cx,1 ; divide by two (words), set carry jnc send2 ; nc = even number of bytes movsb ; do single move send2: cmp cx,0 jle send2a ; le = none to do rep movsw ; copy the data send2a: pop es pop di pop si pop cx mov xmtcnt,0 ; say xmtbufx is available again mov xmt.scb_cmd,nsend+nowait ; send, don't wait for completion mov sposted,1 ; say send posted mov bx,offset xmt ; set pointer to scb call session pop bx ; recover pointer to scb ; success or failure? cmp xmt.scb_err,0 ; good return? je send4 ; e = yes cmp xmt.scb_err,npending ; pending? je send4 ; e = yes cmp xmt.scb_err,18h ; session ended abnormally? jbe send3 ; e = yes, b = other normal errors push ax push dx ; another kind of error, show message mov ah,prstr ; mov dx,offset sndmsg ; say send failed mcmsg sndmsg,csndmsg int dos mov al,xmt.scb_err ; show error code (hex) call hexout pop dx pop ax ; Error return send3: mov pcnet,1 ; say session is broken call serrst ; reset serial port test flags.remflg,dserver ; server mode? jz send3a ; z = no call nethangup ; Server: purge old NAKs etc send3a: call serini ; reinitialize it for new session stc ; set carry for failure to send ret send4: clc ret SEND ENDP ; Network Send packet completion interrupt routine. At entry CS is our ; code segment, es:bx points to scb, netbios stack, interrupts are off. SPOST PROC NEAR ; post routine for Send packets push ds push ax mov ax,datas mov ds,ax mov sposted,0 ; clear send interlock pop ax pop ds iret SPOST ENDP ; Ungermann-Bass NETCI port send packet routine. [ohl] +++ ; Enter with xmtcnt holding length of data in xmtbuf to be sent. ubsend proc near push ax push bx push cx push es mov cx, xmtcnt ; number of chars [ohl] jcxz ubsend1 ; dont send zero chars [ohl] mov bx, offset xmtbufx ; buffer address in es:bx [ohl] mov ax, datas mov es, ax ubsend2: mov ax, nciwrit ; write function, port 0 [ohl] int netci cmp cx,xmtcnt ; check that all characters sent [ohl] je ubsend1 ; e = yes [ohl] add bx, cx ; point to remaining chars [ohl] sub xmtcnt,cx ; count of remaining characters [ohl] mov cx,xmtcnt ; need count in cx too jmp short ubsend2 ; try again to send [ohl] ubsend1:mov xmtcnt,0 pop es pop cx pop bx pop ax ret ubsend endp ; [ohl] --- ; dispatch prebuilt network session scb, enter with bx pointing to scb. ; returns status in al (and ah too). Allows STARLAN Int 2ah for netint. SESSION PROC NEAR push es ; save es around call push ds pop es ; make es:bx point to scb in datas seg mov ax,exnbios ; funct 4 execute netbios, for Int 2ah int netint ; use network interrupt pop es ; saved registers ret ; exit with status in ax SESSION ENDP ; Make a virtual circuit Session, given preset scb's from proc chknet. ; For Server mode, does a Listen to '*', otherwise does a Call to indicated ; remote node. Updates vcid number in scb's. Shows success or fail msg. ; Updates network status byte pcnet to 2 if session is established. ; Does nothing if a session is active upon entry; otherwise, does a network ; hangup first to clear old session material from adapter board. This is ; the second procedure to call in initializing the network for usage. SETNET PROC NEAR ; Network, make a connection cmp lposted,1 ; Listen pending? je setne0 ; e = yes, exit now cmp pcnet,1 ; session active? jbe setne1 ; be = no setne0: ret ; No Session setne1: call nethangup ; clear old session material test flags.remflg,dserver ; Server mode? jz setne2 ; z = no, file xfer or Connect ; Server mode, post a Listen (async) mov lsn.scb_rname,'*' ; accept anyone mov ax,500 call pcwait ; 0.5 sec wait push bx mov lposted,1 ; set listen interlock flag mov lsn.scb_cmd,nlisten+nowait ; do LISTEN command, no wait mov bx,offset lsn call session pop bx ret setne2: ; Non-server (Client) mode push bx ; save reg test nettype,starlan ; STARLAN? jz setne2a ; z = no cmp xmt.scb_vrlen,0 ; yes, using long name support? je setne2a ; e = no push es ; save reg push ds pop es ; make es:bx point to xmt scb mov bx,offset xmt ; use xmt scb for the call mov xmt.scb_cmd,ncall ; CALL_ISN, vrname + vrlen are ready int 5bh ; STARLAN CALL Int 5bh, wait pop es ; restore regs pop bx jmp short setne3 ; finish up setne2a: ; Regular Netbios Call mov xmt.scb_cmd,ncall ; CALL, wait for answer mov bx,offset xmt ; setup scb pointer call session pop bx ; restore register setne3: push dx ; common Call completion, show status test xmt.scb_err,0ffh ; is there a non-zero return code? jnz setne3a ; nz = yes, do bad return or al,al ; check error return jnz setne3a ; nz = bad connection jmp short setne4 ; good connection so far setne3a: ; mov dx,offset nbadset ; say can't reach remote node mcmsg nbadset,cnbadset mov ah,prstr int dos call saynode ; show remote host node name jmp setne4c ; keep results of Call (vcid) setne4: mov al,xmt.scb_vcid ; local session number mov rcv.scb_vcid,al ; for receiver too mov can.scb_vcid,al ; for sending Breaks mov pcnet,2 ; say session has started test flags.remflg,dregular+dquiet ; regular or quiet display? jnz setne4c ; nz = yes, show only no-connect msg ; mov dx,offset ngodset ; say good connection mcmsg ngodset,cngodset mov ah,prstr int dos call saynode ; show remote host name setne4c:pop dx cmp pcnet,1 ; check connection again ja setne5 ; a = good so far stc ; set carry for failure ret setne5: clc ; carry clear for success ret SETNET ENDP saynode proc near ; display node name on screen, si=name ptr push ax push cx push dx push si mov ah,conout mov si,offset nambuf ; remote node string mov cx,64 ; up to 64 bytes long saynod1:cld lodsb ; get remote node name char into al mov dl,al int dos ; display it cmp al,' ' ; was it a space? jbe saynod2 ; be = yes, quit here loop saynod1 ; do all 16 chars saynod2:mov ah,prstr mov dx,offset crlf int dos pop si pop cx pop cx pop ax ret saynode endp LPOST PROC FAR ; Interrupt Post routine for Listen call push ds ; update vcid and calling node name in scb's push cx push es push si push di mov cx,datas ; reestablish datas segment mov ds,cx mov es,cx mov si,offset lsn.scb_rname ; copy remote name to rcv and xmt scbs push si mov di,offset rcv.scb_rname mov cx,8 ; 16 byte field cld rep movsw mov cx,8 pop si push si mov di,offset xmt.scb_rname rep movsw mov cx,8 pop si mov di,offset nambuf ; and to nambuf for display rep movsw mov cl,lsn.scb_vcid ; local session number mov rcv.scb_vcid,cl mov xmt.scb_vcid,cl mov can.scb_vcid,cl mov lposted,0 ; clear interlock flag mov pcnet,2 ; say net ready due to a Listen pop di pop si pop es pop cx pop ds iret ; return from interrupt LPOST ENDP NETHANGUP PROC NEAR ; disconnect network session, keep names cmp pcnet,0 ; network started? je nethang1 ; e = no cmp clone, 'U' ; Ungermann-Bass port? [ohl] je nethang2 ; e = yes [ohl] push bx ; NetBios network mov bx,offset can mov can.scb_cmd,ncancel ; set cancel op code mov can.scb_baddr,offset lsn ; cancel listens mov lposted,0 ; say no listen call session mov can.scb_baddr,offset rcv ; cancel receives call session mov rposted,0 ; say no receives posted mov can.scb_baddr,offset xmt ; cancel sends call session mov sposted,0 ; say no sends posted mov xmtcnt,0 ; reset output buffer counter mov xmt.scb_cmd,nhangup ; hangup, and wait for completion mov bx,offset xmt call session pop bx mov pcnet,1 ; say network but no session call serrst ; reset the serial port for reiniting nethang1:ret ; UB network [ohl] +++ nethang2:call ubclose ; close connection if any [ohl] mov xmtcnt,0 mov pcnet,1 ret ; [ohl] --- NETHANGUP ENDP ; Ungermann Bass. Do a disconnect from the current connection. ubclose proc near push ax push cx cmp nettype,netone ; UB network has been activated? jz ubclos1 ; z = no mov ax, ncistat ; get status [ohl] int netci cmp ch, 1 ; check if we have a connection [ohl] jne ubclos1 ; ne = no [ohl] mov ax, ncicont ; control function [ohl] mov cx, ncidis ; say disconnect [ohl] int netci ubclos2:call ubrecv ; read response from net cmdintpr[ohl] jnc ubclos2 ; continue till no chars [ohl] ubclos1:and nettype,not netone ; remove network type mov pcnet,0 ; say no network pop cx pop dx ret ubclose endp ; Called when Kermit exits. Name passed to mssker by proc chknet ; in word lclexit. NETCLOSE PROC NEAR ; close entire network connection cmp pcnet,0 ; network ever used? je netclo1 ; e = no, so don't touch it call nethangup ; close connections test nettype,netbios ; NetBios activated? jz netclo1 ; z = no push bx mov bx,offset xmt mov xmt.scb_cmd,ndelete ; delete our local Kermit name call session ; from net adapter board pop bx mov pcnet,0 ; say no network and nettype,not netbios ; remove network kind netclo1:ret NETCLOSE ENDP ; Start connection process to network. Obtains Network board local name ; and appends '.K' to form Kermit's local name (removed when Kermit exits). ; If no local name is present then use name 'mskermit.K'. ; Sets local name in scb's for xmt, rcv, lsn. (Does not need DOS 3.x) ; Sets NETDONE pointer to procedure netclose for Kermit exit. ; Verifies existance of interrupt 5ch support, verifies vendor specific ; support for BREAK and other features, sets network type bit in nettype, ; sets BREAK support in nsbrk, hangsup old session if new node name given, ; fills in local and remote node names and name number in scbs (including ISN ; names for STARLAN), and sets network status byte pcnet to 0 (no net) or ; to 1 (net ready). This is the first procedure called to init network usage. ; Byte count of new host name is in temp from COMS. chknet proc near cmp clone,'U' ; Ungermann Bass network? jne chknea ; ne = no mov pcnet,0 ; force reactivation of UB net chknea: cmp pcnet,2 ; session active now? jb chknec ; b = no cmp temp,0 ; byte count of new name, if any je chkneb ; e = none, resume old session ; mov dx,offset naskpmt ; prompt for New or Resume mcmsg naskpmt,cnaskpmt call prompt mov dx,offset nettab ; table of answers mov bx,0 ; help for the question mov ah,cmkey ; get answer keyword call comnd jmp r ; failed nop mov rdbuf,bl ; save keyword action value here mov ah,cmcfm ; get a confirm call comnd jmp r ; no confirm nop cmp rdbuf,0 ; New session? je chkneb ; e = yes clc ret ; resume old one chkneb: jmp chknet1 ; skip presence tests chknec: ; setup addresses and clear junk in scb's cmp pcnet,0 ; have we been here already? je chkned ; e = no jmp chknet1 ; yes, skip init part chkned: mov xmtcnt,0 ; say buffer is empty mov nsbrk,0 ; assume no BREAK across network and nettype,not netbios ; say no NetBios network yet push bx push es ; Test for Netbios presence, IBM way mov ah,35h ; DOS get interrupt vector mov al,netint ; the netbios vector int dos ; returns vector in es:bx mov ax,es cmp ax,0f000h ; rom bios segment?? jb chknee ; b = not likely, else Bios has mov ax,0 ; trapped this vector to dummy iret mov bx,0 ; fake null vector chknee: or bx,ax ; is vector present? pop es pop bx jz chknet0 ; z = no mov xmt.scb_cmd,7fh ; presence test, 7fh is illegal command code mov xmt.scb_err,0 ; clear response field push bx mov bx,offset xmt ; address of the session control block call session ; execute operation pop bx mov al,xmt.scb_err ; get response cmp xmt.scb_err,3 ; 'illegal function', so adapter is ready jne chknet0 ; ne = failure push bx push es ; Test for Netbios presence, IBM way mov ah,35h ; DOS get interrupt vector mov al,2ah ; the netbios vector 2ah int dos ; returns vector in es:bx mov ax,es cmp ax,0f000h ; rom bios segment?? jb chknef ; b = not likely mov ax,0 mov bx,0 ; fake null vector chknef: or bx,ax ; is vector present? pop es pop bx jz chknet0 ; z = no, no NetBios network or nettype,netbios ; say have NetBios network ; AT&T STARLAN board check (0ddh=magic #) mov ah,0 ; vendor installation check on int 2ah mov al,0 ; do error retry int 2ah ; session level interrupt cmp ah,0ddh ; 0ddh = magic number, success? jne chknet1 ; ne = no or nettype,starlan ; say using STARLAN, have int 2ah push bx push es ; Test for vector mov ah,35h ; DOS get interrupt vector mov al,5bh ; 5bh = STARLAN netbios ext'd vector int dos ; returns vector in es:bx mov ax,es or bx,ax ; is vector present? pop es pop bx jz chknet1 ; z = no mov nsbrk,1 ; network BREAK supported jmp chknet1 chknet0:mov pcnet,0 ; no network yet push ax push dx mov ah,prstr ; mov dx,offset nonetmsg ; say network is not available mcmsg nonetmsg,cnonetmsg int dos pop dx pop ax stc ; set carry for failure ret ; and exit now ; net ready to operate chknet1:cmp temp,0 ; byte count of new name, from COMS jne chkne1e ; ne = new name given jmp chknet2 ; nothing, so leave names intact chkne1e:cmp pcnet,2 ; is session active now? jb chkne1d ; b = no call nethangup ; hangup net to clear old connection chkne1d: ; start fresh connection push si push di push es push ds pop es ; make es:di point to datas segment cld mov cx,8 ; 16 bytes for a node name mov ax,' ' ; first, fill with spaces mov di,offset xmt.scb_rname ; remote name field, clear it rep stosw test nettype,starlan ; STARLAN? jz chkne1b ; z = no ; begin STARLAN section mov xmt.scb_vrname,0 ; STARLAN var length name ptr mov xmt.scb_vrname+2,0 ; segement of name mov xmt.scb_vrlen,0 ; and its length mov cx,temp ; count of characters in new name cmp cx,16 ; > 16 chars in remote node name? ja chkne1a ; a = yes, too long for Netbios mov al,'/' ; scan for slashes in name mov di,offset nambuf ; source of text push es ; save es around scan push ds pop es ; point es at datas segment cld repne scasb ; look for the slash pop es jne chkne1b ; ne = none, do regular Netbios name storage chkne1a: ; STARLAN ISN long remote name support mov xmt.scb_vrname,offset nambuf ; STARLAN var length name ptr mov xmt.scb_vrname+2,datas ; segment of remote name mov cx,temp ; get name length again (in cl) mov xmt.scb_vrlen,cl ; indicate its length jmp chkne1c ; copy blanks in remote name field ; end STARLAN section chkne1b:mov cx,temp ; Regular Netbios form, name length mov si,offset nambuf ; source of text mov di,offset xmt.scb_rname ; destination is remote name rep movsb ; copy text to transmitter's scb mov cx,16 chkne1c:mov cx,8 ; 8 words mov si,offset xmt.scb_rname ; from here mov di,offset rcv.scb_rname ; to receiver's scb also rep movsw pop es pop di pop si chknet2:cmp pcnet,0 ; started net? je chknet2c ; e = no ret ; else quit here chknet2c: mov ah,prstr ; mov dx,offset netmsg1 ; say checking node name mcmsg netmsg1,cnetmsg1 int dos push word ptr xmt.scb_rname ; save first two bytes (user spec) mov byte ptr xmt.scb_rname,'*' ; call to local name push bx mov xmt.scb_cmd,naustat ; get Network Adapter Unit status mov bx,offset xmt call session pop bx pop word ptr xmt.scb_rname ; restore remote name first two bytes chknet2a: push es ; save registers push di push si push cx push ds pop es ; set es:di to datas segment mov si,offset xmtbuf+60 ; where local name is returned (1st entry) cmp word ptr xmtbuf+58,0 ; is local name empty? jne chknet2b ; ne = no, use name from table mov si,offset deflname ; else use default local name chknet2b: mov di,offset xmt.scb_lname ; where to put it in scb mov cx,14 ; 16 bytes minus extension of '.K' cld ; append extension of '.K' to loc name chknet3:cmp byte ptr[si],' ' ; find first space (end of regular node name) jbe chknet4 ; be = found one (or control code) movsb ; copy local name to scb loop chknet3 ; continue though local name chknet4:cmp word ptr [di-2],'K.' ; is extension '.K' present already? je chknet4a;;;5 ; e = yes, nothing to add cmp word ptr [di-2],'k.' ; check lower case too je chknet4a;;;5 ; e = yes, nothing to add mov word ptr [di],'K.' ; append our extension of '.K' add di,2 ; step over our new extension sub cx,2 ; complete field with spaces add cx,2 ; 15th and 16th chars chknet4a:jcxz chknet5 ; z = nothing to add mov al,' ' ; space as padding rep stosb chknet5:mov si,offset xmt.scb_lname mov di,offset rcv.scb_lname ; put in receiver scb too mov cx,8 rep movsw mov cx,8 mov si,offset xmt.scb_lname mov di,offset lsn.scb_lname ; in Listen scb also rep movsw pop cx pop si pop di pop es chknet6: push bx ; Put our new local name in NAU mov xmt.scb_cmd,nadd ; ADD NAME, wait mov bx,offset xmt call session pop bx mov al,xmt.scb_err ; get error code cmp al,0 ; success? jne chknet60 ; [zqf] jmp chknet7 ; e = yes chknet60: cmp al,0dh ; duplicate name in local table? je chknet6a ; e = yes cmp al,16h ; name used elsewhere? je chknet6a ; e = yes cmp al,19h ; name conflict? je chknet6a ; e = yes push ax mov ah,prstr ; another kind of error ; mov dx,offset chkmsg1 ; say can't construct local name mcmsg chkmsg1,cchkmsg1 int dos pop ax call hexout ; display it (in al) mov ah,prstr mov dx,offset crlf int dos mov pcnet,0 ; say no connection today stc ; set carry for failure ret chknet6a: mov ah,prstr ; ask for another name ; mov dx,offset chkmsg2 ; prompt message mcmsg chkmsg2,cchkmsg2 int dos mov ah,conout ; show name itself push cx mov cx,16 ; 16 bytes in name field mov si,offset xmt.scb_lname chknet6c: lodsb ; get name char into al mov dl,al int dos mov byte ptr[si-1],' ' ; clear old name as we go loop chknet6c pop cx mov ah,prstr ; mov dx,offset chkmsg3 ; rest of prompt mcmsg chkmsg3,cchkmsg3 int dos mov ah,0ah ; read buffered line from stdin mov dx,offset xmtbuf+58 ; where to put text (xmtbuf+60=text) mov xmtbuf+58,15 ; buf capacity, including cr at end mov xmtbuf+59,0 ; say text in buffer = none int dos jc chknet6b ; c = error cmp xmtbuf+59,0 ; any bytes read? je chknet6b ; e = no, exit failure mov ah,prstr ; say rechecking name ; mov dx,offset netmsg1 mcmsg netmsg1,cnetmsg1 int dos jmp chknet2a ; go reinterpret name chknet6b: stc ; set carry for failure ret chknet7:mov pcnet,1 ; network is present (but not active) mov al,xmt.scb_num ; name number mov rcv.scb_num,al mov lsn.scb_num,al mov lclexit,offset netclose ; address to close network push ax push cx push dx ; mov dx,offset netmsg2 ; say net going mcmsg netmsg2,cnetmsg2 mov ah,prstr int dos mov si,offset rcv.scb_lname ; display our local name mov ah,conout mov cx,16 cld chknet9:lodsb ; byte from si to al mov dl,al int dos ; display it loop chknet9 mov ah,prstr mov dx,offset crlf ; add cr/lf int dos pop dx pop cx pop ax clc ; carry clear for success ret chknet endp ; ; [ohl] ++++ ; Verifies existance of interrupt 6Bh support, verifies vendor specific ; support for BREAK and other features, sets network type bit in nettype, ; sets BREAK support in nsbrk and sets network status byte pcnet to 0 ; (no net) or to 1 (net ready). This is the first procedure called to ; init Ungermann-Bass NETCI terminal port network usage. chkub proc near push bx push es ; Test for vector mov ah,35h ; DOS get interrupt vector mov al,6bh ; 6bh = Net/One command interpreter ; interface, with break support int dos ; returns vector in es:bx mov ax,es ; is vector in rom bios??? [jrd] cmp ax,0f000h ; rom bios starts here jb chkub2 ; b = not likely mov ax,0 ; yes, say no network mov es,ax ; fake a null vector mov bx,ax chkub2: mov ax,es or bx,ax ; is vector present? jz chkub0 ; z = no mov al,0ffh ; test value (anything non-zero) mov ah,2 ; function code for testing net board int netci or al,al ; al = 0 means board is ok jnz chkub0 ; nz = not ok pop es pop bx mov nsbrk,1 ; network BREAK supported or nettype,netone ; say have Net/One clc ; return success ret chkub0: pop es ; clean stack from above pop bx push ax push dx mov ah,prstr ; mov dx,offset nonetmsg ; say network is not available mcmsg nonetmsg,cnonetmsg int dos pop dx pop ax stc ; set carry for failure ret ; and exit now chkub endp ; [ohl] ---- hexout proc near ; display byte in al as hex value push ax ; all regs preserved push cx push dx mov cx,2 ; two nibbles hexout1:push cx ; save counter mov cl,4 ; high nibble ror al,cl ; put in low order field mov dl,al xchg ch,al ; save al byte in ch and dl,0fh ; four bits cmp dl,9 ; too big? jbe hexout2 ; be = no add dl,'A'-'9'-1 ; bump up to A-F hexout2:add dl,'0' mov ah,conout int dos xchg ch,al ; recover data byte pop cx loop hexout1 ; do second nibble mov dl,'H' ; add a final hex ident int dos pop ax pop cx pop dx ret hexout endp ; local routine to see if we have to transmit an xon chkxon proc near cmp flowon,0 ; doing flow control? je chkxo1 ; no, skip all this test xofsnt,usron ; did user send an xoff? jnz chkxo1 ; nz = yes, don't contradict it here test xofsnt,bufon ; have we sent a buffer level xoff? jz chkxo1 ; z = no, forget it cmp count,mntrgl ; below (low water mark) trigger? jae chkxo1 ; no, forget it mov ah,flowon ; ah gets xon and xofsnt,off ; remember we've sent the xon call outch2 ; send via non-flow controlled entry point nop nop nop ; in case it skips chkxo1: ret chkxon endp ; IHOSTS - Initialize the host by sending XON, or equivalent, and enter the ; cycle of clear input buffer, wait 1 second, test if buffer empty then exit ; else repeat cycle. Requires that the port be initialized before hand. ; Ihosts is used by the local send-file routine just after initializing ; the serial port. ; 22 March 1986 [jrd] ; 22 June 1986 Don't send null char if not using flow control. [jrd] IHOSTS PROC NEAR push ax ; save the registers push cx push dx mov xofrcv,off ; clear old xoff received flag mov xofsnt,off ; and old xoff sent flag mov ah,flowon ; put Go-ahead flow control char in ah or ah,ah ; check for null char jz ihosts1 ; z = null, don't send it call outchr ; send it (release Host's output queue) nop ; outchr can do skip return nop nop ihosts1:call clrbuf ; clear out interrupt buffer pop dx ; empty buffer. we are done here pop cx pop ax ret IHOSTS ENDP ; IHOSTR - initialize the remote host for our reception of a file by ; sending the flow-on character (XON typically) to release any held ; data. Called by receive-file code just after initializing the serial ; port. 22 March 1986 [jrd] ; 22 June 1986 Don't send null char if not using flow control. [jrd] IHOSTR PROC NEAR push ax ; save regs push cx mov xofrcv,off ; clear old xoff received flag mov xofsnt,off ; and old xoff sent flag mov ah,flowon ; put Go-ahead flow control char in ah or ah,ah ; check for null char jz ihostr1 ; z = null, don't send it call outchr ; send it (release Host's output queue) nop ; outchr can do skip return nop nop ihostr1:pop cx pop ax ret IHOSTR ENDP ; Send a break out the current serial port. Returns normally. ; Do both regular and long Break. 6 March 1987 [jrd] SENDBR PROC NEAR push cx ; Regular Break entry point mov cx,275 ; 275 milliseconds in regular Break call sendbw ; call worker routine to do it pop cx clc ; don't exit Connect mode ret SENDBL: push cx ; Long Break entry point mov cx,1800 ; 1.8 second long break call sendbw ; call worker routine to do it pop cx clc ; don't exit Connect mode ret ; worker - send Break for cx millisec sendbw: test clone,0ffh ; running on a semi-clone? jnz sendbw2 ; nz = yes, can't do this via Bios push ax push dx mov dx,brkadr ; Port address in al,dx ; Get current setting push ax ; save setting on the stack or al,brkval ; Set send-break bit(s) out dx,al ; Start the break mov ax,cx ; # of ms to wait call pcwait ; hold break for desired interval pop ax ; restore Line Control Register out dx,al ; Stop the break pop dx pop ax ret sendbw2: cmp clone, 'U' ; is it an UB NETCI port? [ohl] je sendbw6 ; e = yes [ohl] cmp clone,'N' ; is this a NetBios network port? jne sendbw4 ; ne = no cmp nsbrk,0 ; is network able to send a break? je sendbw4 ; e = no test nettype,starlan ; STARLAN: network break supported? jz sendbw4 ; z = no push bx push es ; save es around call push ds pop es ; make es:bx point to scb in datas segment mov bx,offset can ; use Cancel control block mov can.scb_cmd,netbrk ; send net Break command int 5bh ; use network Break interrupt pop es ; saved registers pop bx sendbw4:ret sendbw6: ; UB port send break [ohl] +++ push ax push cx mov ax, ncicont + 0 ; call control, use 0 for network port num [ohl] mov cl, ncibrk ; request break [ohl] int netci ; Net/One command interface int. (6Bh) [ohl] pop cx pop ax ret ; [ohl] --- SENDBR ENDP ; Initialization for using serial port. This routine performs ; any initialization necessary for using the serial port, including ; setting up interrupt routines, setting buffer pointers, etc. ; Doing this twice in a row should be harmless (this version checks ; a flag and returns if initialization has already been done). ; SERRST below should restore any interrupt vectors that this changes. ; ; Revised slightly by Joe R. Doupnik 22 Dec 1985 to prevent interrupts ; being enabled until we're done, to stop interrupts from occurring when ; TX holding buffer becomes empty (a useless interrupt for us), and to ; shorten the time between enabling interrupts and our exit. ; Returns carry clear if success, else carry set. SERINI PROC NEAR call pcwtst ; recalibrate pcwait loop timer cmp portin,0 ; Did we initialize port already? jle serin0 ; le = no, not yet jmp serin4 ; Yes, update flow and leave serin0: cmp portin,-1 ; have we run SET PORT or equiv? jne serin5 ; ne = yes, continue mov bl,flags.comflg ; pass current port ident call comstrt ; do SET PORT now ret ; failed, exit now nop nop serin5: cmp clone,0 ; non-clone je serin2 ; e = yes, real thing jmp serin3 ; else use Bios. Finish initialization serin2: cmp clone,'N' ; Network port? jne serin2a ; ne = no jmp serin3 ; yes serin2a:push bx push es in al,21H ; Interrupt controller mov savirq,al ; save state here for restoration or al,modem.mddis ; Inhibit IRQ 3 or IRQ 4 out 21H,al mov al,byte ptr modem.mdintv ; desired interrupt vector mov ah,35H ; Int 21H, function 35H = Get Vector int dos ; get vector into es:bx mov word ptr savsci,bx ; save address offset of original vector mov word ptr savsci+2,es ; and its segment mov al,byte ptr modem.mdintv ; interrupt number for IRQ 4 or IRQ 3 mov dx,offset serint ; offset of our interrupt routine push ds ; save ds around next DOS call mov bx,cs ; compose full address of our routine mov ds,bx ; segment is the code segment mov ah,25H ; set interrupt address from ds:dx int dos pop ds mov al,rs232 ; interrupt number for Bios serial port mov ah,35H ; get vector into es:bx int dos mov word ptr sav232,bx ; save offset mov word ptr sav232+2,es ; save segment mov dx,offset serdum ; offset of our interrupt routine push ds ; save ds around next DOS call mov bx,cs ; compose full address of our routine mov ds,bx ; segment is the code segment mov ah,25H ; set interrupt address from ds:dx int dos pop ds pop es pop bx mov portin,1 ; Remember port has been initialized cli ; Disable interrupts cld ; Do increments in string operations mov ax,modem.mdstat mov mst,ax ; Use this address for status mov ax,modem.mddat mov mdat,ax ; Use this address for data. mov al,modem.mdmeoi mov mdeoi,al ; Use to signify end-of-interrupt. in al,21H ; Set up 8259 interrupt controller and al,modem.mden ; Enable INT3 or INT4. (bit=0 means enable) out 21H,al ; rewrite interrupt mask byte mov dx,modem.mdcom ; Set up the serial card Line Control Reg. in al,dx ; get present settings mov savlcr,al ; save them for restoration mov al,3 ; 8 data bits. DLAB = 0 out dx,al mov dx,modem.mddat ; data and command port, read and flush any in al,dx ; char in UART's receive buffer inc dx ; increment to interrupt enable register 3f9h mov al,1 ; Set up interrupt enable register out dx,al ; for Data Available only add dx,3 ; increment to modem control register 3fch mov al,0bh ; assert DTR, RTS, not OUT1, and OUT2 out dx,al ; OUT2 high turns on interrupt driver chip sti ; Allow interrupts (AFTER next instr) jmp short serin4 ; finish up serin3: cmp flags.comflg,'N' ; Network? jne serin4 ; ne = no call setnet ; setup network session and pcnet flag jnc serin4 ; nc = success ret ; fail, carry set, leave portin at 0 serin4: push bx mov bx,portval ; get port data structure mov parmsk,0ffh ; parity mask, assume parity is None cmp [bx].parflg,parnon ; is it None? je serin1 ; e = yes mov parmsk,07fh ; no, pass lower 7 bits as data serin1: mov bx,[bx].flowc ; get flow control chars mov flowoff,bl ; xoff or null mov flowon,bh ; xon or null mov xofrcv,off ; clear xoff received flag pop bx mov portin,1 ; say initialized clc ; carry clear for success ret ; We're done SERINI ENDP ; Reset the serial port. This is the opposite of serini. Calling ; this twice without intervening calls to serini should be harmless. ; Moved push/pop es code to do quicker exit before interrupts enabled. [jrd] ; Returns normally. ; 22 June 1986 Leave OUT1 low to avoid resetting Hayes 1200B's. [jrd] ; 21 Feb 1987 Add support for Bios calls (Clone) [jrd] ; 17 May 1987 Redo for COM3/4 support [jrd] SERRST PROC NEAR cmp portin,0 ; Reset already? jg srst3 ; g = no ret ; e = yes, l=not used yet, just leave srst3: test clone,0ffh ; running on a non-compatible UART? jz srst4 ; z = no, real UART jmp srst1 ; nz = yes (Bios or Net) srst4: push word ptr savsci ; save original interrupt owner push word ptr savsci+2 ; offset and segment mov word ptr savsci,offset nulint ; redirect to our null routine mov ax,cs ; segment of null routine is code mov word ptr savsci+2,ax mov cx,0 ; loop counter srst2: mov dx,modem.mdstat ; status register in al,dx jmp $+2 ; chip access delay test al,40h ; both xmtr output registers empty? loopz srst2 ; z = no, wait for them a while mov dx,modem.mddat ; modem base address 3f8h add dx,1 ; point at int enable reg 3f9h mov al,0 out dx,al ; disable interrupts from this source jmp $+2 ; let stray interrupts occur now jmp $+2 add dx,2 ; point at Line Control Register 3fbh mov al,savlcr ; saved bit pattern and al,not 80h ; force DLAB bit to 0 out dx,al ; restore line control state ; clear modem's delta status bits and reassert DTR etc inc dx ; increment to modem control register 3fch mov al,03h ; reassert DTR,RTS,but not OUT1 and not OUT2 out dx,al ; OUT2 low to turn off interrupt drivers jmp $+2 ; pause, in case stray interrupt is generated jmp $+2 ; which is more than likely, hence nulint. add dx,2 ; modem status register 3feh in al,dx ; clear status register by reading it jmp $+2 mov mdmhand,al ; save here for Show Modem cli ; Disable interrupts in al,21H ; Interrupt controller or al,modem.mddis ; Inhibit IRQ 3 or IRQ 4 pop word ptr savsci+2 ; recover original int owner's addr pop word ptr savsci sti ; replace original IRQ intrpt vector push bx mov al,byte ptr modem.mdintv ; vector number to do mov dx,word ptr savsci ; offset part push ds mov bx,word ptr savsci+2 ; segment part mov ds,bx ; ds:dx has interrupt vector mov ah,25H ; set interrupt vector int dos ; replaced pop ds mov al,rs232 ; Bios serial port interrupt vector to restore mov dx,word ptr sav232 ; offset part push ds mov bx,word ptr sav232+2 ; segment part mov ds,bx mov ah,25h ; set interrupt vector int dos pop ds pop bx cli mov ah,savirq ; saved Interrupt state and ah,modem.mddis ; pick out our IRQ bit in al,21h ; get current intrpt controller state jmp $+2 xor al,modem.mddis ; remove our IRQ bit or al,ah ; set previous state out 21h,al ; reset IRQ 3 or 4 to original state sti ; non-UART processes srst1: cmp pcnet,0 ; a network active? je srst9 ; e = no cmp flags.comflg,'N' ; NetBios network? jne srst9 ; ne = no cmp rposted,0 ; receive outstanding? je srst9 ; e = no mov can.scb_baddr,offset rcv ; cancel receives push bx mov bx,offset can call session pop bx mov ax,1 call pcwait ; wait one millisec cmp rcv.scb_err,0 ; success? jne srst9 ; ne = no, leave interlock set mov rposted,0 ; say no receives posted srst9: mov portin,0 ; Reset flag ret ; All done SERRST ENDP ; Null interrupt routine, to handle strays nulint proc near push ax push dx mov al,20h ; general EOI mov dx,intcon1 ; to 8259 interrupt controller out dx,al ; clear controller chip pop dx pop ax iret nulint endp ; Dummy Interrupt 14H to defeat DOS interference with serial port when CTTY ; and Kermit use the port simultaneously. If ports differ then chain DOS to ; original Int 14H Bios code. Else return dummy status=ok reports and ; Backspace for Read, ignore char for Write. ; Entered with AH = function request, AL = char to be sent, DX = com port num ; CS is our code segment, DS is DOS's, SS is ours or DOS's, interrupts off. ; 25 June 1987 [jrd] SERDUM PROC FAR push ds ; preserve all registers push ax mov ax,seg datas ; get our data segment mov ds,ax mov al,flags.comflg ; get port id (COM1 = 1, COM2 = 2) and al,7 ; use lower three bits dec al ; DOS counts COM1 as 0, etc cmp dl,al ; referencing same port as Kermit is using? pop ax ; recover request parameters jne serdu1 ; ne = no, chain to Bios routine pop ds cmp ah,0 ; initialization request? je serdu3 ; e = yes, return dummy status=ok rpt cmp ah,1 ; send char in al? jne serdu2 ; ne = no mov ah,60h ; yes, set line status=ok in ah iret serdu2: cmp ah,2 ; receive char (and wait for it)? jne serdu3 ; ne = no, return dummy report mov al,bs ; yes, return ascii BS to DOS mov ah,0 ; ah = errors (none here) iret serdu3: mov ax,60b0h ; dummy status report:xmtr empty, CD, iret ; DSR, and CTS are on serdu1: pop tempdum ; save old ds push word ptr sav232+2 ; push Bios int 14H handler segment push word ptr sav232 ; push Bios int 14H handler offset push tempdum ; recover old ds pop ds ret ; do a ret far (chain to Bios) SERDUM ENDP ; Serial port interrupt routine. This is not accessible outside this ; module, handles serial port receiver interrupts. ; Revised on 22 May 1986, again 2 August 1986 to run at 38.4kb on PC's. ; Srcpnt holds offset, within buffer Source, where next rcv'd char goes. ; Count is number of chars now in buffer, and oldest char is srcpnt-count ; done modulo size of Source. All pointer management is handled here. ; Control-G char substituted for char(s) lost in overrun condition. ; Upgraded to read cause of interrupt from interrupt ident reg (accepts only ; data ready), chain to old interrupt if source is not our device. ; 9 Feb 1988 Add storage of interrupt cause in intkind. [jrd] SERINT PROC FAR push ax ; save registers push dx ; push ds mov ax,seg datas mov ds,ax ; address data segment mov dx,mdat ; modem base address add dx,2 ; increment to Interrupt Identification Reg in al,dx ; get interrupt cause mov intkind,al ; save cause here test al,1 ; interrupt available if this bit is zero jz srintc ; z = interrupt is from our source pop tempsci ; save old ds pop dx ; clean the stack and prepare for ret far pop ax ; to old int handler (same as a jump there) push word ptr savsci+2 ; old handler segment push word ptr savsci ; old handler offset push tempsci ; recover old ds pop ds ret ; do far return (chain to old handler) srintc: mov al,mdeoi ; allow interrupt controller to run out intcon1,al ; Send End-of-Interrupt to 8259 test intkind,4 ; data ready? jnz srint0a ; nz = yes, else ignore srint0: sti ; else turn on interrupts jmp retint ; and exit now (common jump point) srint0a:mov dx,mst ; Asynch status port in al,dx and al,mdmover ; select overrun bit mov ah,al ; save it for later mov dx,mdat in al,dx ; read the received character into al mov dh,al ; dh = working copy. Check null, flow cntl and dh,parmsk ; strip parity temporarily, if any ;;;;; jz srint0 ; if null ignore char srint0b:cmp flowoff,0 ; flow control active? je srint2 ; e = no cmp dh,flowoff ; acting on Xoff? jne srint1 ; ne = Nope, go on cmp xofsnt,0 ; have we sent an outstanding XOFF? jne srint0 ; ne = yes, ignore (possible echo) mov xofrcv,bufon ; Set the flag saying XOFF received jmp srint0 ; and exit srint1: cmp dh,flowon ; acting on Xon? jne srint2 ; ne = no, go on mov xofrcv,off ; Clear the XOFF received flag jmp srint0 ; and exit srint2: push bx ; save register or ah,ah ; overrun? jz srint2a ; z = no mov ah,al ; yes, save present char mov al,bell ; insert control-G for missing char srint2a:mov bx,srcpnt ; address of buffer storage slot mov byte ptr [bx],al ; store the new char in buffer "source" inc srcpnt ; point to next slot inc bx cmp bx,offset source + bufsiz ; beyond end of buffer? jb srint3 ; b = not past end mov srcpnt,offset source ; wrap buffer around srint3: cmp count,bufsiz ; filled already? jae srint4 ; ae = yes inc count ; no, add a char srint4: or ah,ah ; anything in overrun storage? jz srint4a ; z = no mov al,ah ; recover any recent char from overrun xor ah,ah ; clear overrun storage jmp srint2a ; yes, go store real second char srint4a:pop bx ; restore reg sti ; ok to allow interrupts now, not before cmp count,mntrgh ; past the high trigger point? jbe retint ; be = no, we're within our limit test xofsnt,bufon ; Has an XOFF been sent by buffer control? jnz retint ; nz = Yes mov al,flowoff ; get the flow off char (Xoff or null) or al,al ; don't send nul chars jz retint ; z = null, nothing to send call dopar ; Set parity appropriately mov ah,al ; Don't overwrite character with status push cx ; save reg xor cx,cx ; loop counter srint5: mov dx,modem.mdstat ; Get port status in al,dx test al,20H ; Transmitter ready? jnz srint6 ; nz = yes jmp $+2 ; use time, prevent overdriving UART loop srint5 ; else wait loop, cx times jmp srint7 ; Timeout srint6: mov al,ah ; Now send out the flow control char mov dx,modem.mddat jmp $+2 out dx,al mov xofsnt,bufon ; Remember we sent an XOFF at buffer level srint7: pop cx ; restore reg retint: pop ds pop dx pop ax iret SERINT ENDP DTRLOW PROC NEAR ; Global proc to Hangup the Phone or Network ; by making DTR and RTS low (phone). [jrd] mov ah,cmtxt ; allow text, to be able to display help mov bx,offset rdbuf ; dummy buffer ; mov dx,offset hnghlp ; help message mcmsg hnghlp,chnghlp call comnd ; get a confirm jmp r nop cmp clone,'0' ; running on a semi-clone? jb dtrlow2 ; b = no cmp clone,'4' jb dtrlow1 ; b = yes, can't access modem lines dtrlow2:call serhng ; drop DTR and RTS cmp taklev,0 ; in a Take file or macro? jne dtrlow1 ; ne = yes, no message mov ah,prstr ; give a nice message ; mov dx,offset hngmsg mcmsg hngmsg,chngmsg int dos dtrlow1:jmp rskp DTRLOW ENDP ; Hang up the Phone. Similar to SERRST except it just forces DTR and RTS low ; to terminate the connection. 29 March 1986 [jrd] ; 5 April 1987 Add 500 millisec wait with lines low before returning. [jrd] ; Calling this twice without intervening calls to serini should be harmless. ; If network then call nethangup procedure to hangup the session without ; losing local name information. ; Returns normally. serhng proc near ; clear modem's delta status bits and lower DTR & RTS cmp clone,'U' ; Ungermann Bass network? je shng2 ; e = yes cmp clone,'N' ; network? jne shng1 ; ne = no shng2: cmp pcnet,0 ; network operational? je shng1 ; e = no call nethangup ; break the session call serrst ; reset port so can be opened again ret shng1: call serrst ; reset port so serini can set DTR cli ; Disable interrupts push ax push dx mov dx,modem.mddat ; serial port base address add dx,4 ; increment to control register mov al,08h ; reassert OUT2, un-assert DTR,RTS,OUT1 out dx,al jmp $+2 add dx,2 ; increment to modem status register in al,dx ; Clear Status reg by reading it shngx: sti ; Enable interrupts mov ax,500 ; 500 millisec, for pcwait call pcwait ; keep lines low for at least 500 millisec pop dx pop ax clc ret serhng endp ; Jumping to this location is like retskp. It assumes the instruction ; after the call is a jmp addr. RSKP PROC NEAR pop bp add bp,3 push bp ret RSKP ENDP ; Jumping here is the same as a ret R PROC NEAR ret R ENDP code ends end