pcs/realschm.asm

1357 lines
43 KiB
NASM
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

; =====> REALSCHM.ASM
; PC Scheme Real Procedures for Protected Mode Scheme
; (c) 1987 by Texas Instruments Incorporated -- all rights reserved
; Author: Terry Caudill
; History:
; tc 8/07/87 - to work in protected mode scheme (real mode side)
; tc 10/16/87 - modified to use local stack as transaction buffer
page 84,120
name PCSXLI
title PC Scheme External Language Interface
.286c ;; Utilize the expanded 80286 instruction set
include xli.mac
include xli.ref
include xli.equ
subttl Stack and Data segment definitions
page
stksize equ 20000
stack segment para stack 'STACK'
s_base db stksize dup (0)
stack ends
data segment para public 'DATA'
public callers_ds,callers_dx
public load_table,work_area,active_exe
;
; Registers which should be saved due to RPC call
;
trans_buf equ $
callers_dx dw 0
callers_ds dw 0
return_ss dw 0
return_sp dw 0
return_bp dw 0
result_buffer dw 0
;
; jump table for specified function requests. this table is position
; dependent - see rpc.equ and pro2real.asm
;
first_sys_func equ 20 ;max number of rpc functions
next_avail_sys dw 0 ;next location in sys_func
rpc_func dw ret_buffer ;0 - return stack buffer address
dw pctype ;1 - return pc type and graphics info
dw load_exe ;2 - load xli file
dw unload_all ;3 - unload all xli files
dw xesc ;4 - perform xternal escape function
dw ssr_return ;5 - Special Service return
dw takeover_crt ;6 - takeover crt int handler (for exec)
dw restore_crt ;7 - restore system crt int handler
dw 11 dup (unknown_func) ;9 - 19
sys_func dd unknown_func,prog ;20
dd unknown_func,prog ;21
dd unknown_func,prog ;22
dd unknown_func,prog ;23
dd unknown_func,prog ;24
;
; The following data structures support the XLI interface
;
; Various tables
load_table dw N_EXE dup (0) ;PSP addresses (segment)
fb_table dd N_EXE dup (0) ;file block addresses (offset,segment)
pb_table dd N_EXE dup (0) ;parm block addresses (offset,segment)
state_table state N_EXE dup (<>) ;child's regs at point it called us
status_table label word ;records .EXE state (MSBy) and index (LSBy)
x = 0
rept N_EXE
dw x
x = x+1
endm
; Parameter block for EXEC function request
zero equ $ ;a constant zero
exec_pblock dw 0 ;env@ (use Scheme's)
dw zero,seg zero ;cmd line@ (don't care)
dd -1 ;FCB@'s (don't care)
dd -1
; Working storage (during a given call to the external routine)
align 16,data
work_area label word ;for dealing with PCS data values
db PAD_SIZE*N_ARGS dup (0) ;during xesc, non-strings go here
; other information required during an xesc call
work_info xesc_struc <> ;general info
swap_table swap_struc N_ARGS dup (<>) ;records swap state for each XCALL arg
bid_name dw 0 ;pointer used for bidding child
; the child currently active or being loaded
active_exe dw 0 ;(same format as status table)
; State (context) information
; child's registers upon calling PCS
save_ax dw 0 ;actually, we ignore ax..di entries
save_bx dw 0
save_cx dw 0
save_dx dw 0
save_si dw 0
save_di dw 0
save_ds dw 0
save_es dw 0
save_ss dw 0
save_sp dw 0
save_bp dw 0
; our registers upon calling child
pcs_state state <> ;our state at point of calling child
pc_make dw 1 ;pc type 1 = tipc
crt_sav dw 0,0 ;location to save crt interrupt
data ends
prog segment para public 'PROG'
assume cs:prog,ds:data,es:data,ss:stack
public load_exe,bid_child,c2p_handler,c2p_terminate
public xesc,unload_all,find_open_spot,table_search
public do_floarg,do_fixarg,do_bigarg,do_strarg
public do_floval,do_intval,do_TFval,do_strval
public unload_exe,unload_all
; RPC_STARTUP
; This routine will be started initially by the protected mode
; application. Return the address of the message handler routine
; in DS:DX.
rpc_init proc far
rpc_startup:
mov ax,cs
mov ds,ax
mov dx,offset rpc_handler
ret
rpc_init endp
; RPC_HANDLER
; Main control routine for calls to real procedures from protected mode
; scheme. When an RPC is issued, we will get control here.
;
; Upon entry:
; ds:dx => transaction buffer which contains a request. Typically,
; transaction_buffer[0] is an op code, which is used as
; an index into the RPC_FUNC table to determine the actual
; routine to call. Following locations in the transaction
; buffer can be used to pass other parameters and are
; dependent on the function called.
; Upon exit:
; Transaction_buffer[0] should contain an error indication. 0 = no error
;
rpc_handler proc far
int 3 ;for debugging purposes
pusha ;save callers state
; First of all, lets instantiate our own data segment and save off the
; address of the transaction buffer.
mov ax,ds
mov es,ax ;es => transaction buffer
mov ax,data
mov ds,ax ;ds => our local data
mov callers_ds,es ;save off transaction buffer address
mov callers_dx,dx
mov di,dx
mov bx,es:[di] ;bx = the request opcode
mov word ptr es:[di],0 ;default return value to zero (o.k.)
;handlers must reset for errors.
cmp bx,first_sys_func ;normal rpc function request?
jb rpc_h010 ;yes, jump
; Opcodes >= first_sys_func reflect calls to system xli routines, and require
; arguments to be passed on the stack. Protected mode routines stuff the local
; stack segment (defined by STACK above) with the arguments before issueing
; the RPC. The code below must now instantiate the local stack and call a
; handler in the sys_func table above.
mov return_ss,ss ;save current stack segment
mov return_sp,sp ;save current stack pointer
mov return_bp,bp ;save current base pointer
mov ax,stack ;get local stack
mov ss,ax ; and instantiate
mov sp,es:[di]+2 ;transaction_buffer[2] = stack pointer
mov bp,sp ;base pointer = stack pointer
sub bx,first_sys_func ;calc index into sys_func table
shl bx,1
shl bx,1
call dword ptr sys_func+[bx] ;call the routine
mov ss,return_ss ;restore stack used upon entry
mov sp,return_sp
mov bp,return_bp
les di,dword ptr trans_buf ;restore access to transaction buffer
mov es:[di],ax ;transaction_buffer[0] = return status
jmp rpc_hret ;return to protected mode routine
; We have a normal rpc call. Our local stack segment may have been stuffed with
; parmameters by the protected mode routine, so lets use it as our extra
; segment (AIA provides a stack segment with the rpc and we can just use it as
; our stack).
;
rpc_h010:
mov ax,stack
mov es,ax
xor di,di ;es:di => pro2real communication buffer
shl bx,1 ;convert func code to index
call rpc_func+[bx] ;call function
rpc_hret:
popa ;restore callers regs
ret ;return to protected mode
rpc_handler endp
; UNKNOWN_FUNC
; This routine is called when we get an undefined op-code. Return a
; negative one to the protected mode routine as an error indicator.
;
unknown_func proc near
les di,dword ptr trans_buf ;es:di => transaction buffer
mov word ptr es:[di],-1 ;return error condition
ret
unknown_func endp
; RET_BUFFER
; Return address of local stack segment. This segment will be used by the
; protected mode routines as a communication buffer between real and protected
; mode. It will be used by other RPC function requests for passing args and
; returning values. For system xli calls, it will be instantiated as the
; stack (see above rpc_handler).
ret_buffer proc near
les di,dword ptr trans_buf ;es:di => transaction buffer
mov es:[di]+2,stksize ;return length of communication buffer
mov ax,offset s_base ;ax = stack base
mov es:[di]+4,ax ;return as communication buffer offset
mov ax,stack ;get buffer segment
mov result_buffer,ax ; and save for later
mov es:[di]+6,ax ;return communication buffer segment
ret
ret_buffer endp
; PCTYPE
; Determine type of PC we are running on and initialize screen.
;
; Upon Entry:
; es:di => communication buffer
;
; Upon Exit:
; Communication_buffer[0] = Machine Type
; 1 for TIPC or Business Pro in TI mode
; FF for IBM-PC
; FE for IBM-PC/XT
; FD for IBM-PC/jr
; FC for IBM-PC/AT or B-P in IBM mode
; F8 for PS2 Model 80
; 0 for undeterminable
; Communication_buffer[2] = Video Mode
; Communication_buffer[4] = Character Height
;
pctype proc near
push es ; save comm buffer
push ds ; save local data seg
mov ax,0FC00h ; move paragraph address of copyright
pc_002: mov es,ax ; notice into ES
xor di,di ; Clear DI; 0 is lowest address in ROM @ES:
xor bx,bx ; Flag for "PC_MAKE"
mov cx,40h ; This'll be as far as I go...
mov al,'T' ; look for beginning of "Texas Instruments"
cli ; Stop interrupts - bug in old 8088's
again:
repne scas byte ptr es:[di] ; SEARCH
or cx,cx ; Reach my limit?
jz short pc_005 ; quit if we've exhausted search
cmp byte ptr es:[di],'e' ; make sure this is it
jne again ; use defaults if not found
cmp byte ptr es:[di]+1,'x' ; really make sure this is it
jne again
push ds
mov ds,bx ; 0->DS for addressing low mem.
inc bx ; BX==1 => TIPC
mov ax,ds:word ptr [01A2h] ; If TIPC then what kind?
pop ds ; get DS back
add al,ah ; checkout vector 68 bytes 2 & 3
cmp al,0F0h ; if AL==F0 then TIPC=Business Pro
jne pc_010 ; jump if not a B-P
in al,068h ; Read from port
push ax ; Save for later
and al,0FBh ; Enable CMOS
out 068h,al ; Write back out
mov dx,8296h ; I/O address for B-P's mode byte
in al,dx ; TI or IBM Mode on the B-P?
cmp al,0 ; if not zero then B-P emulates a TIPC
pop ax ; Restore original port value
out 068h,al ; and write back out
jne pc_010 ; jump if TIPC else IBM machine code is
; where it should be.
jmp short pc_007
pc_005: mov ax,es
cmp ah,0FEh ; test for segment offset FE00
jae pc_007 ; two checks made? if so, jump
add ah,2 ; go back and check segment offset
jmp pc_002 ; FE00
pc_007: mov ax,0F000h
mov es,ax
mov al,byte ptr es:0FFFEh ; IBM's machine code is @F000:FFFE
cmp al,0f0h ; Is this suckah an IBM?
jb pc_010 ; Jump if AL is below F0 (BX will be 0)
mov bl,al
pc_010:
sti ; Turn interrups back on
cmp bx,1 ; TIPC?
jne not_ti ; no, jump
; We have a tipc, initialize the graphics
push 0DF01h
pop es ; clear graphics planes
xor di,di
mov byte ptr es:[di],0AAh ; set red palette
mov byte ptr es:[di]+16,0CCh ; set green palette
mov byte ptr es:[di]+32,0F0h ; set blue palette
push 0DF82h
pop es
mov byte ptr es:[di],040h ; turn text on
mov ax,3 ; ax = video mode
; bx = pc type code
mov cx,8 ; cx = character height
jmp pc_020
; We have an ibm, (assumed) get current video mode
not_ti:
push bx ; save pc type code around bios calls
mov ax,0500h ; set active display page (for alpha modes)
int 10h ; bios int
mov ah,15 ; get current video mode
int 10h ; bios int
xor ah,ah ; ax = video mode
pop bx ; bx = pc type code
mov cx,8 ; cx = character height
cmp ax,16 ; if video mode = 16
jle pc_020 ; then
mov cx,14 ; reset character height
pc_020:
pop ds ; restore local data seg
pop es ; restore communication buffer
xor di,di
mov word ptr es:[di]+0,bx ; put PC_MAKE in transaction buffer
mov word ptr es:[di]+2,ax ; ditto video mode
mov word ptr es:[di]+4,cx ; ditto char height
;
; and just for something different ... lets try some interrupts
;
TI_PBI equ 05Dh ; TI Program Break Interrupt
IBM_PBI equ 01Bh ; IBM Program Break Interrupt
GET_VEC equ 035h
SET_VEC equ 025h
mov pc_make,bx ;save pc type
mov al,TI_PBI ;default ti program break int
cmp bx,1 ;are we tipc?
je vec_01 ; yes, jump
mov al,IBM_PBI ; no, get ibm pbi
vec_01:
mov ah,GET_VEC ;get vector
fix_010:
push ds ;tempsave data seg
mov dx,offset pbi_brk ;dx=offset of handler
mov cx,cs
mov ds,cx ;ds:dx => handler
int 21h
pop ds ;restore data seg
ret
pbi_brk:
int 3 ;lets just break and
iret ;ignore for now
pctype endp
ibm_crtint equ 010h
ti_crtint equ 049h
; Install new routine at the CRT DSR interrupt
;
takeover_crt proc near
int 3
push es
push ds ; save segments
mov ah,035h ;ah = get int vector address
mov al,ibm_crtint ;al = ibm crt interrupt
cmp pc_make,1 ;is it an ibm?
jne take_010 ; yes, jump
mov al,ti_crtint ;al = ti crt interrupt
take_010:
push ax ;save around dos int
int 21h ;get interrupt vector
mov crt_sav+2,es
mov crt_sav,bx ;save existing interrupt vector
pop ax ;restore int
mov ah,025h ;ah = set int vector, al = int number
mov dx,offset crtdsr
push cs
pop ds ;ds:dx => new interrupt handler
int 21h ;set interrupt vector
pop ds
pop es
ret
takeover_crt endp
;
; This routine restores the original routine for the CRT DSR interrupt
;
restore_crt proc near
int 3
push ds ;tempsave data segment
mov ah,025h ;ah = set int vector address
mov al,ibm_crtint ;al = ibm crt interrupt
cmp pc_make,1 ;is it a ibm?
jne restore_010 ; yes, jump
mov al,ti_crtint ;al = ti crt interrupt
restore_010:
mov dx,crt_sav
mov ds,crt_sav+2 ;ds:dx => system interrupt handler
int 21h ;set interrupt vector
pop ds ;restore data segment
ret
restore_crt endp
;
; This is the do-nothing routine installed at the CRT DSR interrupt
;
crtproc proc far
crtdsr:
sti
mov ax,0
iret
crtproc endp
; LOAD_EXE
; Load an XLI file as a child process, setting up all the necessary hooks
; so that it can be called via an xesc, or system xli call.
;
; Upon Entry:
; ES:DI => communication buffer. The structure ld_args (defined below)
; indicates the structure of the buffer.
; Upon Exit:
; The first word in the transaction buffer will be set as follows:
; The high order byte will contain a flags byte where
; success = carry clear
; failure = carry set
; The low order byte will contain the error
; 0 = no open slots
; <> 0 = EXEC failure code
ld_args struc ;structure of transaction buffer for load exe
sysflag dw ? ;1 = system flag, 0 = user defined
exe_index dw ? ;offset to exe name within pathname
pathname db ? ;pcs-sysdir pathname
ld_args ends
load_exe proc
; if we succeed, state=EXE_NONE
call find_open_spot ;this sets active_exe
mov ax,0
jc le_exit ;no open slots
; set state=EXE_TSR for time between EXEC and TSR
load_index itself
mov bh,EXE_TSR
mov active_exe,bx
cmp es:[di].sysflag,1 ;loading system .EXE?
je le_5 ;yes, look only in pcs-sysdir
mov ax,es:[di].exe_index ;get address of filename only
mov bid_name,ax ;try current directory first
call bid_child
jnc le_10 ;bid succeeded, jump
le_5:
mov ax,pathname ;try looking in pcs-sysdir
add ax,di
mov bid_name,ax
call bid_child
jc le_exit ;bid failed, jump
; child is ready, set state=EXE_NORM
le_10: load_index itself
mov bh,EXE_NORM
mov ax,bx
load_index status_table
mov status_table[bx],ax
clc
le_exit:
lahf ;load flags into ah
les di,dword ptr trans_buf ;es:di => transaction buffer
mov es:[di],ax ;move result to rpc buffer
ret
load_exe endp
;BID_CHILD
; Given a filename in bid_name, initialize it under XLI.
;
; Upon Entry:
; ES:bid_name => pathname of the file to bid
;
; Upon Exit:
; AX = EXEC status
;
; Assume AX..SI are destroyed; DS,ES,SS,SP,BP,DI are preserved.
bid_child proc
push di
push ds ;save parent's state
push es
push bp
save_parent
mov cs:stk_seg,ss
mov cs:stk_offset,sp
mov dx,bid_name
mov ax,es
mov ds,ax ;DS:DX = parm block
mov bx,data
mov es,bx
lea bx,exec_pblock ;ES:BX = Asciiz pathname
mov ax,FR_EXEC
int 21h
; The following are external entry points accessible by the child.
biddbg: jmp tsr_done ; --- THE BIG 4 --- (not for child's use)
jmp c2p_handler ; --- THE BIG 4 --- for XCALL's
jmp c2p_terminate ; --- THE BIG 4 --- for child termination
tsr_done: cli
mov ss,cs:stk_seg
mov sp,cs:stk_offset
sti
pop bp
pop es
pop ds
pop di
ret
stk_seg dw 0 ;bootstrap parent's state after EXEC
stk_offset dw 0 ;from here
bid_child endp
subttl Code segment: Child->Parent Handler
page
;C2P_HANDLER
; This routine is invoked from the child program bid in BID_CHILD. Upon
; entry we are executing in the child's environment. The relevant stack
; stack entries at this point are:
; SS:SP (top) -> IP ;child's far return address
; CS
; length ;child's length; for TSR
; PSP@ ;child's PSP@
; //// ;(the rest of the stack)
; The first time called, set up the linkage such that we can get back
; to the routine via the xesc functionality.
c2p_handler label near
resume_parent
load_index itself
cmp bh,EXE_TSR ;first call (performing TSR)
je c2_10 ; yes, jump
jmp normal ; no, normal call - rejoin xesc
c2_10:
load_index state_table
lea bx,state_table[bx]
mov es,[bx].st_ss
mov bp,[bx].st_sp ;ES:BP is child's SS:SP
mov ax,es:[bp].cs_psp ;get child's PSP off its stack
load_index load_table
mov load_table[bx],ax ;save it
push ds ;-----> DS set to child's PSP
mov ds,ax
mov ax,ds:fb_ptr ;get file block @
mov cx,ds:fb_ptr+2
mov dx,ds:env_ptr ;get env block @ (seg addr)
pop ds ;<-----
load_index fb_table
mov word ptr fb_table[bx],ax ;save it
mov word ptr fb_table+2[bx],cx
push es ;tempsave child's SS:SP on stack
push bp
mov bp,ax
mov es,cx ;ES:BP is file block @
mov ax,es:[bp].fb_pb
mov cx,es:[bp].fb_pb+2 ;get parm block @
load_index pb_table
mov word ptr pb_table[bx],ax ;save it
mov word ptr pb_table+2[bx],cx
;
test word ptr es:[bp].fb_flags,FB_SYSINT ;system callable?
jz c2_40 ; no, jump
mov ax,es:[bp].fb_sysint_addr
mov cx,es:[bp].fb_sysint_addr+2 ;cx:ax is entry point
mov bx,next_avail_sys ;bx = next avail location
inc next_avail_sys ;bump next avail location
shl bx,1 ;make index
shl bx,1
mov word ptr sys_func+[bx],ax ;save location in table
mov word ptr sys_func+[bx+2],cx
c2_40:
int 3
test word ptr es:[bp].fb_flags,FB_KEEPENV
;keep child's env block?
jnz c2_50 ;yes, jump
dos_fr FR_RELMEM,,,,,dx ;no, release it for child
c2_50: pop bp
pop es
mov dx,es:[bp].cs_len ;get child's length off its stack
; we're ready to TSR the child
dos_fr FR_TSR,,,dx
; we don't drop through -----------------------------------------
subttl Code segment: Child termination
page
;C2P_TERMINATE
; After the child has performed its wrapup, it calls this routine
; to deallocate its memory and make its spot in the load table available.
c2p_terminate label near
mov ax,data ;we needn't save child's context now
mov ds,ax
restore_parent
load_index load_table ;release the child
dos_fr FR_RELMEM,,,,,load_table[bx]
jc ct_err
load_index itself ;mark its spot as available
xor bh,bh
mov ax,bx
load_index status_table
mov status_table[bx],ax
jmp normal1 ;rejoin unload_exe
ct_err: mov bx,XLI_ERR_RELMEM
jmp xli_err_exit
subttl Code segment: xesc
page
;XESC
; This is the handler for the "%xesc" opcode.
;
; On entry:
; ES:DI => Communication Buffer set up by protected mode routine.
;
; +-----------------------------------------------+
; | Routine name length (1 word) |
; | Routine name (above length) |
; | . |
; | . |
; | Number of Arguments (1 word) |
; | Type of Arg1 (1 word) |
; | Arg1 (type dependent) |
; | . |
; | . |
; | . |
; | Type of Argn (1 word) |
; | Argn (type dependent) |
; +-----------------------------------------------+
;
; On exit:
; Communication buffer will contain return status, type, and value
;
; +-----------------------------------------------+
; | Return Status (1 word) |
; | Return Value Type (1 word) |
; | Return Value (type dependent) |
; | . |
; | . |
; | . |
; +-----------------------------------------------+
;
; Return Status will either be 0 for normal return, or -1
; for a special service request.
;
; Return Value Type should be from 0 to N_RV (4) which are
; defined return types, or RV_ERR (10) which allows the
; external program to send back an error message.
;
; Note: Return status for xesc is actually returned in the transaction
; buffer at TRANSACTION_BUFFER[0].
; Buffer definition for passing data back to protected mode
xesc_result struc
xesc_status dw ?
xesc_vtype dw ?
xesc_value dw ?
xesc_result ends
xesc proc near
mov return_sp,sp ;save stack in case errors
mov ax,ES:[di] ;get string length
mov work_area.srch_slen,ax ;save length of string data
add di,2
mov work_area.srch_sptr,di ;save address of string data
mov work_area.srch_sptr+2,es
add di,ax ;point past string
mov ax,word ptr es:[di] ;AX = number of args
mov work_info.xs_nargs,ax ;set up number args passed
add di,2
mov work_info.xs_pc,di ;and save in local area
mov work_info.xs_pc+2,es
; Look for a match.
call table_search ;is there a match?
;(sets active_exe if so)
jnc xesc_10 ;yes, jump
mov bx,XLI_ERR_NO_SUCH_NAME ;error: no such name loaded
jmp xesc_err_exit
xesc_10: mov dx,ax ;tempsave selector
; There was a match.
; Collect the info we'll need to guide us thru xesc call.
load_index fb_table
mov bp,word ptr fb_table[bx]
mov es,word ptr fb_table+2[bx] ;ES:BP is file block @
mov ax,es:[bp].fb_id ;get XLI ID
cmp ax,XLI_ID ;compare to our version
je xesc_15 ;if equal, continue
mov bx,XLI_ERR_BAD_VERSION ; else note out of sync
jmp xesc_err_exit
xesc_15: mov ax,es:[bp].fb_flags ;flags
mov work_info.xs_flags,ax
load_index pb_table
mov bp,word ptr pb_table[bx]
mov es,word ptr pb_table+2[bx] ;ES:BP is parm block @
mov work_info.xs_pb_segment,es ;parm block's segment address
lea ax,es:[bp].pb_rv
mov work_info.xs_rvptr,ax
mov work_info.xs_rvptr+2,es ;return value's address
mov es:[bp].pb_rv,0 ;zero out return value
mov es:[bp].pb_rv+2,0
mov es:[bp].pb_rv+4,0
mov es:[bp].pb_rv+6,0
mov es:[bp].pb_rvtype,SWI_TF ;set ret value's type to T/F
mov es:[bp].pb_ss,0 ;zero out special service
add ax,8
mov work_info.xs_args,ax ;first arg's address
mov work_info.xs_args+2,es
mov work_info.xs_local,offset work_area ;work area address
mov work_info.xs_local+2,seg work_area
; Begin initializing child's parameter block.
mov es:[bp].pb_select,dx ;store selector into parm block
mov work_info.xs_select,dx
; Move the xesc arguments to their places for the xesc call.
mov cx,0
xesc_20: cmp cx,work_info.xs_nargs ;any left?
je xesc_50 ;no, jump
push cx ;tempsave current arg#
mov bx,cx ;BX = current arg#
shl bx,1 ; make index into swap table
shl bx,1
mov ax,work_info.xs_args ;get arg address
mov word ptr swap_table[bx].sw_offset,ax ;and save for later
mov si,work_info.xs_pc
mov es,work_info.xs_pc+2 ;ES:SI points to arg type
mov di,word ptr es:[si]
inc si
inc si ;ES:SI points to arg
; Dispatch on argument type
call cs:word ptr do_arg[di] ;handle one type of object
add work_info.xs_local,PAD_SIZE ;incr XLI-local ptr
;(maintain alignment)
pop cx ;restore current arg#
inc cx
jmp xesc_20
xesc_50:
call_child 1 ;Call the child.
; We're back with a return value--unless it's a special service call.
normal: cld
load_index pb_table
mov bp,word ptr pb_table[bx]
mov es,word ptr pb_table+2[bx] ;ES:BP is parm block @
cmp es:[bp].pb_ss,0 ;any special services?
je xesc_60 ;no, jump
jmp ssr ;special service. This will
;return from protected mode
;to ssr_return before calling
;the child again at xesc_50
; Now we're really back with the return value
xesc_60: mov di,es:[bp].pb_rvtype
mov work_area.xs_rvtype,di ;return value's type
cmp di,RV_ERR ;external-pgm error return?
jne xesc_65 ;no, jump
mov si,work_info.xs_rvptr
mov es,work_info.xs_rvptr+2 ;ES:SI points to return value
;(external-pgm error message)
shl di,1 ;return type to return
call do_strval ;build the string
jmp xesc_75 ;and return
xesc_65: cmp di,N_RV ;return value out of range?
jb xesc_70 ;no, jump
mov bx,XLI_ERR_VALUE_BAD_TYPE
jmp xesc_err_exit
xesc_70: shl di,1
mov si,work_info.xs_rvptr
mov es,work_info.xs_rvptr+2 ;ES:SI point to return value
call cs:word ptr do_val[di] ;handle one type of return value
xesc_75:
mov sp,return_sp ;clean up stack and return
ret
; This file's error exit processing. Reset the stack so that we return
; correctly. BX should be set with an error code before jumping here.
xli_err_exit:
xesc_err_exit:
les di,dword ptr trans_buf ;es:di => rpc buffer
mov es:[di],bx ;return status
mov sp,return_sp ;clean up stack and return
ret
subttl Code segment: Special Services
page
; "Swap" special service
; On entry, ES:BP is parm block pointer.
ssr label near
; mov bx,es:[bp].pb_ss ;get dispatch number
; cmp bx,SS_SWAP
; je ssr_swap
; jmp ss_exit
ssr_swap:
mov ax,es:[bp].pb_ss_args ;AX = arg#
test work_info.xs_flags,FB_NEAR ;near data?
jnz ssr_10 ; yes, jump
; far data
xor bx,bx ;BX = length (null)
mov cx,bx ;CX = offset (null)
mov dx,bx ;DX = segment(null)
jmp ss_15
; near data
ssr_10:
mov bx,es:[bp].pb_ss_args+2 ;BX = length
mov cx,es:[bp].pb_ss_args+4 ;CX = destination offset
mov work_info.xs_dest,cx ; save for return trip
mov dx,work_info.xs_pb_segment ;DX = destination segment
ss_15:
mov di,stack
mov es,di
xor di,di ;ES:DI => result buffer
mov es:[di].xesc_status,-1 ;SSR request
mov es:[di].xesc_status+2,ax ;arg #
mov es:[di].xesc_status+4,bx ;length
mov es:[di].xesc_status+6,cx ;offset address
mov es:[di].xesc_status+8,dx ;segment address
mov sp,return_sp ;clean up stack
ret ;and return to protected mode
;routine to copy the string
ssr_return label near
mov return_sp,sp
les di,dword ptr trans_buf ;load rpc buffer
mov ax,es:[di]+2 ;get # args copied
load_index pb_table
mov bp,word ptr pb_table[bx]
mov es,word ptr pb_table[bx]+2
mov es:[bp].pb_ss,0 ;Clear ss field for normal exit
mov bx,es:[bp].pb_ss_args ;Get arg#
mov es:[bp].pb_ss_args,ax ;Update # chars copied
shl bx,1
shl bx,1 ;index into swap table
mov bp,word ptr swap_table[bx].sw_offset ;ES:BP =>arg's loc in parm block.
test work_info.xs_flags,FB_NEAR ;near data?
jnz ssr_r05 ; yes, jump
; far data
push es ;tempsave
mov bx,stack
mov es,bx
xor di,di ;es:di => result buffer
mov ax,es:[di]+6 ;ax = offset of string
mov bx,es:[di]+8 ;bx = segment of string
pop es ;restore
mov word ptr es:[bp],ax ;put far @ in parm block
mov word ptr es:[bp+2],bx
jmp xesc_50
; near data
ssr_r05:
mov ax,work_info.xs_dest
mov es:[bp],ax ;put near @ in parm block
jmp xesc_50
;; Jump tables
; indexed by argument type (standard PCS type tag)
do_arg dw do_fixarg ;0=list (#f only)
dw do_fixarg ;1=fixnum
dw do_floarg ;2=flonum
dw do_bigarg ;3=bignum
dw do_fixarg ;4=symbol (#t only)
dw do_strarg ;5=string
dw do_errarg ;6 the rest we don't care about
dw do_errarg ;7
dw do_errarg ;8
dw do_errarg ;9
dw do_errarg ;10
dw do_errarg ;11
dw do_errarg ;12
dw do_errarg ;13
dw do_errarg ;14
dw do_errarg ;15
; indexed by value type (SW-INT return types)
do_val dw do_intval ;0=integer
dw do_TFval ;1=true/false
dw do_strval ;2=string
dw do_floval ;3=flonum
xesc endp
subttl Code segment: Copy arguments into place for child
page
; On entry to all the argument handler routines:
; BX = pointer to VM reg with page:offset of Scheme object
do_floarg proc near
test work_info.xs_flags,FB_NEAR ;near flag on?
jz do_flo10 ;no, jump
; Set up destination address
; near
mov cx,work_info.xs_args ;dest is in child
mov dx,work_info.xs_args+2
mov work_info.xs_dest,cx
mov work_info.xs_dest+2,dx
jmp short do_flo20
; far
do_flo10: mov cx,work_info.xs_local ;dest is in XLI-local area
mov dx,work_info.xs_local+2
mov work_info.xs_dest,cx
mov work_info.xs_dest+2,dx
; Copy the flonum data
do_flo20:
mov di,work_info.xs_dest
push ds ;tempsave DS around copy
push es
mov es,work_info.xs_dest+2 ;ES:DI points to dest
pop ds ;DS:SI is Scheme object @
mov cx,8
rep movsb
pop ds ;restore our DS
test work_info.xs_flags,FB_NEAR ;near flag on?
jz do_flo30 ;no, jump
; Copy pointer to data
; near (no copy needed--data is in child's space)
mov cx,8 ;incr arg@ past copied data
jmp short do_flo32
; far (pointer in child points to data in XLI space)
do_flo30: sub di,8 ;back up dest @
mov cx,di
mov dx,es
mov bp,work_info.xs_args
mov es,work_info.xs_args+2 ;ES:BP points to arg position
mov es:[bp],cx
mov es:[bp]+2,dx ;copy pointer there
; Increment arg pointer by an appropriate amount.
mov cx,4 ;incr arg@ past copied ptr
do_flo32: test work_info.xs_flags,FB_PAD ;pad flag on?
jz do_flo35 ;no, skip
mov cx,PAD_SIZE
do_flo35: add work_info.xs_args,cx
add work_info.xs_pc,10 ;update arg counter
do_flo40: ret
do_floarg endp
do_bigarg proc near
mov ax,es:[si] ;move longint to regs
mov dx,es:[si]+2
test work_info.xs_flags,FB_NEAR ;near flag on?
jz do_big20 ;no, jump
mov bp,work_info.xs_args
mov es,work_info.xs_args+2 ;ES:BP points to dest
mov es:[bp],ax ;copy LSBy to child
mov cx,2
test work_info.xs_flags,FB_INT ;is 16-bit integer flag on?
jz do_big15 ;no, jump
; is the longint small enough for an int?
cmp dx,0 ;DX should be either
;all 0's or all 1's
je do_big32 ;we can safely truncate
xor dx,0FFFFh ;complement DX
cmp dx,0 ;try again
je do_big32 ;we can safely truncate
mov bx,XLI_ERR_BIG_TO_16_BITS ;error: bignum too big
;to become int
jmp xesc_err_exit
do_big15: mov es:[bp]+2,dx ;copy MSBy to child
mov cx,4
jmp short do_big32
; far (pointer in child points to data in XLI-local space)
do_big20:
; Copy either the longint or a pointer to it.
mov bp,work_info.xs_args
mov es,work_info.xs_args+2 ;ES:BP points to dest
mov es:[bp],ax ;copy to child
mov es:[bp]+2,dx
mov cx,4 ;incr arg@ past longint
;or pointer to longint
; Increment arg pointer by an appropriate amount.
do_big32: test work_info.xs_flags,FB_PAD ;pad flag on?
jz do_big35 ;no, skip
mov cx,PAD_SIZE
do_big35: add work_info.xs_args,cx
add work_info.xs_pc,6 ;update arg counter
do_big40: ret
do_bigarg endp
do_fixarg proc near
mov ax,es:[si] ;move longint to regs
mov dx,es:[si]+2
mov bp,work_info.xs_args
mov es,work_info.xs_args+2 ;ES:BP points to dest
test work_info.xs_flags,FB_NEAR ;near flag on?
jz do_fix20 ;no, jump
; near (copy int to child's space)
mov es:[bp],ax ;copy int to child
mov cx,2 ;incr arg@ past int
test work_info.xs_flags,FB_INT ;is 16-bit integer flag on?
jnz do_fix30 ;yes, jump
mov es:[bp]+2,dx ;no, copy high order 16 bits
mov cx,4 ;incr arg@ past longint
jmp short do_fix30
; far (pointer in child points to data in XLI-local space)
do_fix20: mov bx,work_info.xs_local
mov [bx],ax
mov [bx]+2,dx
mov ax,work_info.xs_local ;move far ptr to int
;or longint to child
mov cx,work_info.xs_local+2
mov es:[bp],ax
mov es:[bp]+2,cx
mov cx,4 ;incr arg@ past ptr to int
; Increment arg pointer by an appropriate amount
do_fix30: test work_info.xs_flags,FB_PAD ;pad flag on?
jz do_fix35 ;no, skip
mov cx,PAD_SIZE
do_fix35: add work_info.xs_args,cx
add work_info.xs_pc,6 ;update arg counter
do_fix40: ret
do_fixarg endp
do_xxerr: jmp do_errarg ;conditional jumps
;are too short
do_strarg proc near
add work_info.xs_pc,4
mov bp,work_info.xs_args
mov es,work_info.xs_args+2 ;ES:BP is arg @
mov word ptr es:[bp],0
xor cx,cx
test work_info.xs_flags,FB_NEAR ;Near data?
jnz do_str50
mov cx,2
mov word ptr es:[bp+2],0
do_str50:
add cx,2
test work_info.xs_flags,FB_PAD ;padding on?
jz do_str65
mov cx,PAD_SIZE
do_str65: add work_info.xs_args,cx
ret
do_strarg endp
do_errarg proc near
mov bx,XLI_ERR_ARGN_BAD_TYPE
jmp xesc_err_exit
do_errarg endp
subttl Code segment: Copy return value back into Scheme
page
; On entry to all the value handler routines:
; ES:SI = pointer to return value
; DI = return type
;
do_floval proc near
push ds ;save for this routine
mov cx,result_buffer
xor dx,dx ;buffer for return values
test work_info.xs_flags,FB_NEAR ;is near flag on?
mov ax,es
mov ds,ax ;ds now addresses result
jnz do_flv10 ;yes, jump
; far
mov ax,[si] ;get ptr to number
mov bx,[si]+2
mov si,ax
mov ds,bx ;DS:SI points to number
; near
do_flv10:
mov ax,di ;save return type
mov es,cx
mov di,dx ;ES:DI points to result buffer
mov es:[di].xesc_status,0 ; set return status
mov es:[di].xesc_vtype,ax ; set return type
add di,xesc_value ; now address value field
cld
mov cx,8
rep movsb ; move float to buffer
pop ds
ret
do_floval endp
do_TFval proc near
mov ax,es:[si] ;get value
or ax,es:[si]+2 ;all bytes must = 0 to be nil
or ax,es:[si]+4
or ax,es:[si]+6
;
mov es,result_buffer
xor si,si ;ES:SI points to result buffer
mov es:[si].xesc_status,0 ; set return status
mov es:[si].xesc_vtype,di ; set return type
mov es:[si].xesc_value,ax ; set return value
ret
do_TFval endp
do_intval proc near
test work_info.xs_flags,FB_NEAR ;near flag on?
jnz do_int10 ;yes, jump
; far
mov ax,es:[si] ;get ptr to number
mov dx,es:[si]+2
mov si,ax
mov es,dx ;ES:BP points to number
; near
do_int10: mov ax,es:[si] ;get number
mov dx,es:[si]+2
test work_info.xs_flags,FB_INT ;16-bit integer flag on?
jz do_int20 ;no, jump
cwd ;yes, propagate sign
do_int20:
mov es,result_buffer
xor si,si ;ES:SI points to result buffer
mov es:[si].xesc_status,0 ; set return status
mov es:[si].xesc_vtype,di ; set return type
mov es:[si].xesc_value,ax ; set return value
mov es:[si].xesc_value+2,dx
ret
do_intval endp
do_strval proc near
mov ax,es:[si]
test work_info.xs_flags,FB_NEAR ;is near flag on?
jz do_stv10 ; no, jump
mov dx,work_info.xs_pb_segment ;DX:AX = string ptr
mov cx,es:[si]+2 ;CX = string length
jmp short do_stv15
do_stv10: mov dx,es:[si]+2 ;DX:AX = string ptr
mov cx,es:[si]+4 ;get string length
do_stv15: mov bx,16380 ;BX is max string length
cmp cx,bx ;is CX short enough
jbe do_stv20 ;yes, jump
mov cx,bx ;no, truncate at max
; DX:AX = string ptr, CX = string length, DI = return type
do_stv20:
mov es,result_buffer
xor si,si ;ES:SI points to result buffer
mov es:[si].xesc_status,0 ; return status
mov es:[si].xesc_vtype,di ; return type
mov es:[si].xesc_value,cx ; length
mov es:[si].xesc_value+2,ax ; pass string pointer back
mov es:[si].xesc_value+4,dx
ret
do_strval endp
do_errval proc near
mov bx,XLI_ERR_VALUE_BAD_TYPE
jmp xesc_err_exit
do_errval endp
subttl Code segment: unload_exe
page
; Given active_exe, release it from memory and make its spot available again.
unload_exe proc near
load_index state_table
mov es,word ptr state_table[bx].st_ss
mov bp,word ptr state_table[bx].st_sp ;ES:BP is child's SS:SP
mov es:[bp].cs_psp,0 ;set PSP@ to 0, our signal
;to child to wrap things up
call_child 2 ;call child one last time
normal1: ret
unload_exe endp
subttl Code segment: unload_all
page
; This routine is called during PCS termination. It notifies each
; child to do any wrapup, then the child will do its final call to us,
; where we release it.
unload_all proc near
mov active_exe,0
ua_10: cmp active_exe,N_EXE ;looked at all entries?
je ua_exit ;yes, jump
load_index status_table
mov bx,status_table[bx]
cmp bh,EXE_NONE ;is slot empty?
jne ua_20 ;no, jump
ua_15: inc active_exe ;incr to next entry
jmp ua_10
ua_20: call unload_exe ;deallocate entry
jmp ua_15
ua_exit: ret
unload_all endp
subttl Code segment: table_search
page
; We need to find a matching string. From it we'll know
; which child has it and what value it maps to.
; On entrance:
; work_area.srch_sptr is the seg:offset of the Scheme string (data proper)
; work_area.slen is the string's length
; On exit:
; if success: AX = selector value
; active_exe = xxnnh, where n is the child
; carry clear
; if fail: carry set
; AX..DI,ES,BP are destroyed.
table_search proc near
cld ;to be safe
mov work_area.srch_exe,0
ts_10: cmp work_area.srch_exe,N_EXE ;looked at them all?
jne ts_15 ;no, jump
; No child had a match. Return with carry set.
stc
jmp ts_exit
ts_15: mov bx,work_area.srch_exe
mov active_exe,bx
load_index status_table
mov ax,status_table[bx]
cmp ah,0 ;is this an open spot?
jne ts_20 ;no, jump
ts_next: inc work_area.srch_exe ;increment to next spot
jmp short ts_10
; We have a loaded file. Figure out where its lookup table is.
ts_20: load_index fb_table
mov bp,word ptr fb_table[bx]
mov es,word ptr fb_table+2[bx] ;ES:BP is file block @
mov ax,es:[bp].fb_lut
mov dx,es:[bp].fb_lut+2
mov di,ax
mov es,dx ;ES:DI is lookup table @
mov ah,0 ;AH will be selector value
mov al,'/' ;AL is name delimiter
; Find the next name in the lookup table.
ts_30: cmp byte ptr es:[di],al ;looking at last delimiter?
je ts_next ;yes, jump
mov si,di ;SI points at current name
mov cx,0FFh
repne scasb ;look for name delimiter
jcxz ts_next ;jump, should've found it by now
mov dx,di ;DI, DX point at next name
mov cx,di
sub cx,si
dec cx ;CX is length of name in table
cmp work_area.srch_slen,cx ;are lengths equal?
jne ts_40 ;no, jump
; We matched lengths. See if the strings themselves match.
mov di,si ;get current name @ back in DI
push ds ;tempsave our DS
mov si,work_area.srch_sptr
mov ds,work_area.srch_sptr+2 ;DS:SI is Scheme string @
repe cmpsb
pop ds ;restore our DS
je ts_match ;jump if match
; The current table name didn't match.
ts_40: inc ah ;increment selector value
mov di,dx ;restore next name @ to DI
jmp ts_30
; We matched. Active_exe has child#, replace it with the corr. status value.
; Calculate the selector value (0-based) of the name and return it in AX.
; Clear carry.
ts_match: mov al,ah
xor ah,ah
load_index status_table
mov bx,status_table[bx]
mov active_exe,bx
clc
ts_exit: ret
table_search endp
subttl Code segment: find_open_spot
page
; Find an open spot in the load_table. Clear carry and set the LSBy of
; active_exe with the child# if we succeeded, else set carry.
find_open_spot proc near
push bx
push cx
mov cx,N_EXE
mov bx,0
fi_loop: cmp byte ptr status_table[bx]+1,EXE_NONE
je fi_found
inc bx
inc bx
dec cx
cmp cx,0
jne fi_loop
stc ;set carry if no available entries
jmp short fi_exit
fi_found: mov bx,N_EXE
sub bx,cx
mov active_exe,bx
clc ;an open entry: clear carry, set active_exe
fi_exit: pop cx
pop bx
ret
find_open_spot endp
prog ends
end rpc_startup