;CONNECT.A86 - Connect current Virtual console to another virtual ; console. FOR MCDOS only. ; Written only for the sake of learning about MCDOS ; so could be full of bugs. ; ; Author : Alex Soya - Nov. 1984 ; ; Type a Control-Z to get out of Connect. ; ;CCPM function calls P_TERMCPM equ 0 ; Terminate and release all resources C_READ equ 1 ; Read a character from the Default Console C_WRITE equ 2 ; Write a character to the Default Console C_RAWIO equ 6 ; Direct Console I/O with Default Console C_WRITESTR equ 9 ; Print ASCII string to default console C_STAT equ 11 ; Obtain Status of Default Console DEV_WAITFLAG equ 132 ; Wait for a System Flag DEV_SETFLAG equ 133 ; Set a system Flag Q_OPEN equ 135 ; Open A system queue Q_WRITE equ 139 ; Write a message to a System Queue S_SYSDAT equ 154 ; Return address of System Data Segment P_PDADR equ 156 ; Return address of the process descriptor cr equ 13 lf equ 10 cntrl_Z equ 'Z'-40h OPTION equ 05dh ; Command line Option can be found here. BASEFLAG equ 40h ; BaseFlag + console # used for qbuffer QDEPTH equ 40 ; # of bytes in output Circular Que buffer ; Sysdat Offsets: ; ; SUPENTR equ 0h ; Supervisor entry offset XIOSSYS equ 28h ; Offset to Xios vector CSEG connect: pushf ! pop bx ; No interrupts during stack switches cli mov ax,ds mov ss,ax mov sp, offset stack push bx ! popf ; Now we have our own stack. mov cs:Word Ptr DSKEEP,DS ; Keep the Data Segment here call makesys ; make us a system process. call gtarg ; Get target console number jz valcon ; If Console NOT valid then mov dx, offset invmsg ; Write error message. mov cl, C_WRITESTR int 224 jmps qexit ; and terminate valcon: mov Byte Ptr TCON, al ; Save the Target console # mov Word Ptr VQNAM, dx ; Make proper Queue name mov dx, offset VINQPB ; open VINQ que for target console mov cl, Q_OPEN int 224 cmp ax,0h ; If Not Open then jz qopnd mov dx, offset nopmsg ; Write an error message mov cl, C_WRITESTR int 224 jmps qexit ; and terminate ; endif qopnd: call ovlxios ; overlay the xios jump vectors mainloop: call tready ;repeat main loop: jz local ; if (target = ready) then call tget ; get target character call lput ; put character to local console ; endif local: call lready jz mainloop ; if (local = ready) then call lget ; get local character cmp al,cntrl_Z ; check for abort jz exit ; if abort then exit call tput ; put character to target console jmps mainloop ;until abort exit: call restxios ; restore xios vectors qexit: call makeus ; make us a user process again mov cl,P_TERMCPM ; and terminate us int 224 DSKEEP dw 2 ;-----------------------------------------------------------------------; ; makesys: Set the current process as a systems process in PD flag ; ; field. ; ; ; ; Entry -> None ; ; Return <- none ; ; ; ; Regs used: ax, bx, cx, es ; ; ; ;-----------------------------------------------------------------------; ; ; ; makesys: mov cl,P_PDADR ; Obtain process descriptor address int 224 mov bx,ax ; bx := offset(PD); es := segment(PD) or es:word ptr 6[bx],1 ; PD.FLAG := PD.FLAG or SYSTEM ret ;end makesys ;-----------------------------------------------------------------------; ; makeus: Reset the system attribute in PD flag ; ; ; ; Entry -> None ; ; Return <- None ; ; ; ;-----------------------------------------------------------------------; ; ; ; makeus: mov cl,P_PDADR ; Obtain PD address int 224 mov bx,ax ; bx:= offset(PD); es:= segment(PD) and es:Word Ptr 6[bx],0feh ; mask PD.FLAG.System ret ;-----------------------------------------------------------------------; ;gtarg Get console number as option from command tail ; ; ; ; entry -> none ; ; ; ; Return <- DX = 2 byte ASCII value from buffer ( 0 - 99) ; ; AL = Integer 0 - 99 ; ; FLAG = Z if valid Option, NZ if Invalid option ; ; ; ; Regs used : AX,DX,CX ; ; ; ;-----------------------------------------------------------------------; ; ; ; gtarg: cmp Byte Ptr .OPTION,' ' ; If No Tail then No Option je nooption ;else mov dx, word ptr .OPTION ; DX := Option mov ax,dx ; AX := Option (in Ascii) sub al,'0' ; Convert AX to binary in AL cmp ah,'0' jb no_tens ; If second digit then cmp ah,'9' ja no_tens ; adjust sub ah,'0' mov ch,ah mov cl,10 mul cl ; make first digit the tens add al,ch ; AL = binary(DX) no_tens: ; end if cmp al,0 ; is option in range jb nooption ; Nope, so no option found cmp al,99 jg nooption xor ah,ah ; set flag ret nooption: or ah,0ffh ret ;-----------------------------------------------------------------------; ; ovlxios: Overlays the XIOS jump vectors in SYSDAT with that of ; ; xiosintcpt, Keeps the old XIOS jump vector at XIOS and ; ; fills in the SUPVSR segment. ; ; ; ; Entry -> None ; ; Return <- Fills in: XIOS, SYSDAT, SUPVSR (segment) and ; ; SYSDAT SEGMENT XIOS VECTOR ; ; ; ; Regs used: ax,bx,cx,ex ; ;-----------------------------------------------------------------------; ; ; ; ovlxios: mov cl,S_SYSDAT ; Get Sysdat Segment to ES int 224 pushf ; No interrupts while messing cli ; with Sysdat vectors mov ax,es:.XIOSSYS ; Get current Xios Offset mov cs:XIOS,ax ; and keep it here mov ax,es:.XIOSSYS+2 ; get segment of real xios mov XIOS+2,ax mov ax, offset xiosintcpt ; overlay xios interceptor offset mov es:.XIOSSYS,ax ; to Sysdat Xios vector offset mov ax,cs ; overlay xios interceptor segment mov es:.XIOSSYS+2,ax ; to Sysdat Xios vector segment mov ax,es:.SUPENTR+2 ; Get Segment of Supervisor mov SUPVSR+2,ax ; and keep it for us popf ; Interrupts OK again. ret ;-----------------------------------------------------------------------; ; tready: See if a character from the target console is ready, ie ; ; is a character in the circular queue buffer. ; ; ; ; Entry -> nothing ; ; ; ; Return <- Flag = NZ if char is ready, Z if not ; ; AL = FFh if char is ready, 0 if not ; ; ; ; Regs used: None ; ;-----------------------------------------------------------------------; ; ; ; tready: mov si, offset OUTQUE ; Here is our Que call qempt pushf jnz treadr ; If Writer waiting then cmp Byte Ptr BFULL,0ffh jne treadr mov Byte Ptr BFULL,0h ; Set Flag to indicate que empty mov dl, BASEFLAG ; Compute Flag for this console add dl, Byte Ptr TCON ; and set it. mov cl, DEV_SETFLAG int 224 treadr: popf ret ;-----------------------------------------------------------------------; ; tget: Get the target character, i.e. Take it out of the Queue buffer ; ; ; ; Entry -> None ; ; Return <- AL = Character ; ; ; ; Regs used: AX, SI, BX ; ;-----------------------------------------------------------------------; ; ; ; tget: mov si, offset OUTQUE ; This is the Que Buffer call qdel ; Go get the char ret ;-----------------------------------------------------------------------; ; lput: Send a character to local console ; ; ; ; Entry -> AL = Character ; ; Return <- none ; ; ; ; Regs used: AX, CX, DX ; ;-----------------------------------------------------------------------; ; ; lput: mov dl,al ; Character to send to DL mov cl, C_WRITE ; Use System C_Write function int 224 ret ;-----------------------------------------------------------------------; ; lready: Obtain status of local console ; ; ; ; Entry -> none ; ; Return <- AL = 01h if Char ready, 00h if not ready ; ; Flag = Z if not ready, NZ if ready ; ; ; ; Regs used: AX, BX, CX ; ;-----------------------------------------------------------------------; ; ; ; lready: mov cl,C_STAT ; Use System C_stat function int 224 or al,al ; Set Return Flag ret ;-----------------------------------------------------------------------; ; lget: Get a character from the local console ; ; ; ; Entry -> None ; ; Return <- AL = character ; ; ; ;-----------------------------------------------------------------------; ; ; lget: mov cl,C_RAWIO mov dl,0fdh int 224 ret ;-----------------------------------------------------------------------; ; tput: Send a character to target console. Does this by writing the ; ; character to the target consoles VINQn System Queuq. ; ; ; ; Entry -> AL = Character to send ; ; Return <- AX = AX on entry. ; ; ; ;-----------------------------------------------------------------------; ; ; ; tput: push ax mov ah,0 ; Null the High Byte mov Word Ptr Qbuf,AX ; Put the character into Queue buffer mov dx, offset VINQPB ; DX := Queue Parameter Block offset mov cl, Q_WRITE int 224 ; and send a message pop ax ret ;-----------------------------------------------------------------------; ; restxios: Restore the original XIOS jump vectors in SYSDAT page ; ; ; ; Entry -> Assumes original XIOS vectors stored at XIOS in Code ; ; Segment. ; ; Return <- None ; ; ; ;-----------------------------------------------------------------------; ; ; ; restxios: mov cl,S_SYSDAT ; Get Sysdat segment to ES int 224 pushf ; No interupts while messing cli ; With SYSDAT values mov ax,cs:XIOS ; Get Xios offset mov es:.XIOSSYS,ax ; and restore it in SYSDAT mov ax,cs:XIOS+2 ; Get Segment for Xios mov es:.XIOSSYS+2,ax ; and restore it too. popf ; Interrupts are ok again ret ; back to caller. ;-----------------------------------------------------------------------; ; xiosintcpt: Intercept call to XIOS and handle IO_CONOUT function ; ; for target console locally. ; ; ; ; Entry -> AL = XIOS function number ; ; CL = Character to send ; ; DL = Virtual console to send to ; ; ; ; Return <- Jumps to XIOS with all registers preserved ; ; Except Flag, which is not used by XIOS ; ; functions. ; ;-----------------------------------------------------------------------; ; ; xiosintcpt: push ds mov ds, Word Ptr DSKEEP ; Get this Data Segment cmp al,2 ; if XIOS function = 2 (Console Output) jne jxios ; and cmp dl, Byte Ptr TCON ; Console = Target Console jne jxios ; then call qinsert ; Insert char in queue jxios: pop ds jmpf cs:Dword Ptr XIOS ; Goto realxios ;-----------------------------------------------------------------------; ; qinsert: Insert a character into the que. If Que is full, wait until ; ; Some space has been made. Uses System Flags to wait for ; ; empty queue. ; ; ; ; Entry -> CL = Character ; ; Return <- none ; ; ; ; Regs used: All Registers preserved except flag Reg ; ;-----------------------------------------------------------------------; ; ; ; qinsert: push ax push bx ; Save'm push cx push dx push si push di push bp mov si, offset OUTQUE ; Get Circular Que offset call qfull ; If Que is full then jnz insrt ; Wait for empty queue flag push si push cx mov Byte Ptr BFULL,0ffh ; Tell Reader to set Flag when empty mov dl,BASEFLAG ; Compute flag for this console add dl, Byte Ptr TCON ; flag := BaseFlag + Console # mov cl, DEV_WAITFLAG ; lets wait for the flag call supif ; direct supervisor call pop cx pop si ; end if insrt: mov al,cl ; Character to send in al call queins ; insert Character into que pop bp pop di pop si pop dx pop cx pop bx ; restore them pop ax ret ;-----------------------------------------------------------------------; ; qempt: Check to see if Que is empty ; ; ; ; entry -> si = que offset ; ; ; ; return -> Que empty: al = 0, Z is Set ; ; Char in que: al = FF, Z not set ; ;-----------------------------------------------------------------------; qempt: push bx ; lets not destroy anything other than al mov bx,word ptr 2[si] ; get rear of que cmp word ptr[si],bx ; if front = rear then mov al, 0 je qstat ; que := empty mov al, 0ffh ; else que:= not empty qstat: pop bx or al,al ret ;-----------------------------------------------------------------------; ; qfull: test if que is full. ; ; ; ; entry -> si = que offset ; ; ; ; return -> NZ = ok, Z = que FULL ; ; ; ;-----------------------------------------------------------------------; qfull: mov bx,word ptr[si] ; if front + 1 = rear then call incqptr ; que=full cmp bx,word ptr 2[si] ret ; Z = full, NZ = ok ;-----------------------------------------------------------------------; ; incqptr: Increment the Que pointer. Takes care of wrap arround ; ; if end of que area is reached ; ; entry -> BX = pointer ; ; return -> BX = pointer + 1 increment ; ;-----------------------------------------------------------------------; ; ; incqptr:inc bx ; point to next que offset cmp bx,qdepth ; end of que area reached ? jne incd ; if not we are done xor bx,bx ; yeap... lets wrap incd: ret ;-----------------------------------------------------------------------; ; queins: insert a character into que. If que is full the character ; ; is just droped. ; ; ; ; entry -> al = character ; ; si = que offset ; ; ; ; return -> none ; ;-----------------------------------------------------------------------; queins: push ax call qfull ; find out if the que is full pop ax ; Z -> ok, NZ -> full je qind ; if que is full then loos data mov bx, word ptr[si] ; else que[front]=item mov byte ptr 4[si+bx],al call incqptr ; front = front + 1 mov word ptr[si],bx qind: ret ;-----------------------------------------------------------------------; ; qdel: delete a character from que ; ; ; ; entry -> si = que offset ; ; ; ; return -> al = character ; ;-----------------------------------------------------------------------; qdel: call qempt ; anything in the que ? je qdeld ; nope, so nothing we can get out... mov bx,word ptr 2[si] ; al = que[rear] mov al,byte ptr 4[si+bx] call incqptr ; rear = rear+1 mov word ptr 2[si],bx qdeld: ret ;-----------------------------------------------------------------------; ; supif: Call CCP/M Supervisor ; ; ; ; Entry -> CX = System call number ; ; DX = Parameter ; ; DS = Segment address if DX is an offset into a structure ; ; ES = User Data Area ; ; ; ; Return -> AX = BX = Return ; ; CX = Error Code ; ; ES = UDA Segment (as on Entry) ; ; ; ; Regs Used: DX, SI, DI, BP are NOT preserved.. ; ; ; ;-----------------------------------------------------------------------; ; ; supif: mov ch,0 ; Clear High byte of function number push es push ds mov ds, Word Ptr Sysdat mov si,.68h mov es,ds:10h[si] ; Get UDA of current process callf cs:dword ptr SUPVSR pop ds pop es ret XIOS dw 0 ; Offset address of XIOS SYSDAT dw 0 ; Segment of XIOS and System Data Segment SUPVSR dw 3 ; Entry to supervisor, offset = 3 dw 0 ; Segment filled in later DSEG Org 100h TCON db 0 ; Target Console Number BFULL db 0 ; Flag indicating buffer is full OUTQUE dw 0 ; front of que dw 0 ; rear of que rb qdepth ; here is the que area. INVMSG db ' Invalid Virtual Console Number.',cr,lf db ' usage: CONNECT nn',cr,lf db ' nn - Virtual Console number',cr,lf,'$' NOPMSG db ' Unable to open Input Queue.',cr,lf,'$' VINQPB dw 0,0,0 ; VINQnn Parameter Block dw offset Qbuf db 'VINQ' ; Primary Queue name VQNAM db ' ' ; Secondary Queue name (Console number) QBUF db 40h * 2 ; can have 40 messages times 2 bytes each rw 31 stack dw 0