1371 lines
44 KiB
NASM
1371 lines
44 KiB
NASM
; =====> XLI.ASM
|
||
; PC Scheme External Language Interface (XLI)
|
||
; (c) 1987,1988 by Texas Instruments Incorporated -- all rights reserved
|
||
; Author: Bob Beal
|
||
; History:
|
||
; rb 3/20/87 - original
|
||
; rb 2/ 2/88 - check XLI ID;
|
||
; added external-program error return
|
||
;
|
||
|
||
|
||
page 84,120
|
||
name PCSXLI
|
||
title PC Scheme External Language Interface
|
||
|
||
include scheme.equ
|
||
include xli.equ
|
||
include xli.ref
|
||
include xli.mac
|
||
|
||
subttl Group and Constant definitions
|
||
page
|
||
pgroup group prog
|
||
xgroup group progx
|
||
dgroup group data
|
||
|
||
subttl Data segment definitions
|
||
page
|
||
|
||
data segment para public 'DATA'
|
||
assume ds:dgroup
|
||
|
||
public load_table,work_area,active_exe ;??? for debugging
|
||
|
||
; 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
|
||
exe_name label byte ;a filename from XLI's control file
|
||
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
|
||
exe_name1 dw offset exe_name ;pointer into exe_name to filename.extension
|
||
;(i.e. points past directory prefix)
|
||
bid_name dw 0 ;pointer into exe_name; used for bidding child
|
||
; the child currently active or being loaded
|
||
active_exe dw 0 ;(same format as status table)
|
||
; system .EXE information
|
||
sysflag db 0 ;0=user .EXE; 1=system .EXE
|
||
;look for system .EXE's only in pcs-sysdir
|
||
pcs_exe db 'newtrig.exe',0 ;PCS system .EXE files
|
||
pcs_exe_len equ $-pcs_exe
|
||
|
||
; 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
|
||
; the SP just after the indirect call through "gate";
|
||
; error recovery is done with:
|
||
; mov SP,gate_SP
|
||
; ret
|
||
; which returns to the instruction following the indirect call
|
||
gate_SP dw 0
|
||
|
||
|
||
data ends
|
||
|
||
|
||
subttl Code segment: load_all
|
||
page
|
||
|
||
; external variables
|
||
extrn ctl_file:word,pcs_sysd:word
|
||
extrn regs:word
|
||
; external routines
|
||
extrn alloc_fl:near,int2long:near,long2int:near,alloc_bl:near
|
||
extrn getbase:near
|
||
|
||
progx segment para public 'PROGX'
|
||
assume cs:xgroup,ds:dgroup,es:dgroup,ss:dgroup
|
||
|
||
public %xli_gate
|
||
public load_all,load_exe,bid_child,c2p_handler,c2p_terminate
|
||
public xesc1,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,update_swap_table
|
||
|
||
|
||
; This routine is called when PCS first comes up. External programs are
|
||
; loaded first (by this file) and then the Scheme heap is allocated (initmem).
|
||
; Any errors encountered are ignored. If we run of memory, initmem
|
||
; should see it too and notify smain to abort.
|
||
|
||
load_all proc
|
||
; First copy into exe_name buffer the pcs-sysdir name.
|
||
cld
|
||
push ds ;make ES=DS
|
||
pop es
|
||
mov di,pcs_sysd
|
||
mov cx,64 ;max length of pathname
|
||
mov al,0
|
||
repne scasb ;look for eos character (=0)
|
||
jcxz la_5 ;jump if none
|
||
dec di
|
||
la_5: mov cx,di
|
||
sub cx,pcs_sysd ;get length of pcs-sysdir
|
||
;without eos character
|
||
mov si,pcs_sysd
|
||
mov di,offset exe_name
|
||
rep movsb ;copy pcs-sysdir name (- eos)
|
||
;into exe_name buffer
|
||
mov al,'\' ;append \ onto pcs-sysdir name
|
||
dec di
|
||
cmp [di],al ;is '\' last char of pcs-sysdir?
|
||
je la_7 ;yes; write it over itself
|
||
inc di ;no; move past end, then write
|
||
la_7: stosb
|
||
mov exe_name1,di ;exe_name1 points to next
|
||
;open position in exe_name
|
||
; load system .EXE files
|
||
mov bx,ctl_file
|
||
cmp byte ptr [bx],'-' ;suppress loading system .EXE's?
|
||
jne sysload ;no
|
||
inc ctl_file ;yes, move past marker
|
||
jmp short userload ;and skip loading system .EXE's
|
||
sysload: mov si,offset pcs_exe ;get first system .EXE name
|
||
mov di,exe_name1
|
||
mov cx,pcs_exe_len
|
||
rep movsb ;copy into buffer after
|
||
;pcs-sysdir name
|
||
mov sysflag,1 ;set sysflag
|
||
call load_exe ;load the file
|
||
; open XLI control file
|
||
userload: mov sysflag,0
|
||
dos_fr FR_OPEN,,,ctl_file,ds
|
||
mov bx,ax ;put handle in BX
|
||
jnc next_file ;jump if no open errors
|
||
jmp close1 ;can't open file, exit
|
||
; read in next filename off the control file and append it to
|
||
; the pcs-sysdir name
|
||
next_file: mov dx,exe_name1 ;init buffer @
|
||
next_char: dos_fr FR_READ,bx,1,dx,ds ;read 1 character
|
||
jnc la_10 ;jump if no error, else
|
||
;suddenly can't read control
|
||
;file, close it and exit
|
||
close: dos_fr FR_CLOSE,bx
|
||
close1: ret
|
||
|
||
la_10: cmp ax,0 ;at eof?
|
||
jz close ;yes, jump
|
||
; we've read a character
|
||
mov si,dx
|
||
cmp byte ptr [si],' ' ;blank?
|
||
je next_char ;yes, skip it
|
||
cmp byte ptr [si],0Dh ;carriage return?
|
||
je got_file ;yes, jump
|
||
cmp byte ptr [si],' ' ;control char?
|
||
jl next_char ;yes, skip it
|
||
inc dx ;point to next buffer position
|
||
jmp next_char
|
||
; we've read a complete filename, go load it
|
||
got_file: mov byte ptr [si],0 ;form ASCIIZ string
|
||
call load_exe ;bid it
|
||
jnc next_file ;jump if no errors
|
||
cmp ax,0 ;any open slots?
|
||
je close ;no, jump
|
||
cmp ax,2 ;file found?
|
||
je next_file ;no, jump
|
||
cmp ax,8 ;ran out of memory?
|
||
jne next_file ;no, jump; ignorable error
|
||
jmp close ;yes
|
||
|
||
load_all endp
|
||
|
||
; Given exe_name, this routine loads the child into any available open slot.
|
||
; On exit:
|
||
; success: carry clear
|
||
; failure: carry set
|
||
; AX = 0 : no open slots
|
||
; AX <> 0 : EXEC failure code
|
||
load_exe proc
|
||
push ax
|
||
push bx
|
||
; 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 sysflag,1 ;loading system .EXE?
|
||
je le_5 ;yes, look only in pcs-sysdir
|
||
mov ax,exe_name1 ;try current directory first
|
||
mov bid_name,ax
|
||
call bid_child
|
||
jnc le_10 ;bid succeeded, jump
|
||
le_5: mov ax,offset exe_name ;try again with pcs-sysdir prefix
|
||
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: pop bx
|
||
pop ax
|
||
ret
|
||
load_exe endp
|
||
|
||
;le_err: cmp ax,0
|
||
; je le_exit ;it's not up, just exit
|
||
; cmp ax,8
|
||
; jne fail1
|
||
; mov ax,XLI_ERR_NO_MEMORY
|
||
; jmp xli_err_exit
|
||
;fail1: cmp ax,2
|
||
; jne fail2
|
||
; mov ax,XLI_ERR_NO_SUCH_FILE
|
||
; jmp xli_err_exit
|
||
;fail2: mov ax,XLI_ERR_BAD_EXEC
|
||
; jmp xli_err_exit
|
||
;le_err1: cmp xli_up,0 ;can't do usual error handling if
|
||
; ;system's not up yet
|
||
; je le_exit ;it's not up, just exit
|
||
; mov ax,XLI_ERR_NO_AVAILABLE_SLOTS
|
||
; jmp xli_err_exit
|
||
|
||
; Given a filename in "exe_name", initialize it under XLI.
|
||
; The EXEC status is returned.
|
||
; Assume AX..DI,ES are destroyed; DS,SS,SP,BP are preserved.
|
||
bid_child proc
|
||
push ds ;save parent's state
|
||
push bp
|
||
save_parent
|
||
mov cs:stk_seg,ss
|
||
mov cs:stk_offset,sp
|
||
dos_fr FR_EXEC,<offset exec_pblock>,,bid_name,ds,ds
|
||
|
||
; 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 ds
|
||
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
|
||
|
||
; On entry to this routine PCS is executing in the child's environment.
|
||
; The relevant 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)
|
||
|
||
c2p_handler label near
|
||
resume_parent
|
||
load_index itself
|
||
cmp bh,EXE_NORM ;normal call from child?
|
||
jne c2_10 ;no, jump; could be TSR
|
||
jmp normal ;yes, jump--rejoin xesc
|
||
c2_err: mov ax,XLI_ERR_SYNC_ERR
|
||
jmp xli_err_exit
|
||
c2_10: cmp bh,EXE_TSR ;first call from child? (before TSR)
|
||
jne c2_err ;no, jump
|
||
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_KEEPENV
|
||
;keep child's env block?
|
||
jnz c2_20 ;yes, jump
|
||
dos_fr FR_RELMEM,,,,,dx ;no, release it for child
|
||
c2_20: 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
|
||
|
||
; 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,dgroup ;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 ax,XLI_ERR_RELMEM
|
||
jmp xli_err_exit
|
||
|
||
subttl Code segment: xesc
|
||
page
|
||
|
||
; This is the handler for the "%xesc" opcode.
|
||
;
|
||
; On entry:
|
||
; AX = length of xesc call (= inst length - 1)
|
||
; ES:SI = pointer to bytecode containing the (reg# x 4) of
|
||
; the %xesc name string
|
||
; On exit:
|
||
; normal: the VM reg that contained the name string on entry
|
||
; will contain the page:offset of the return value;
|
||
; there may be side effects in strings that were arguments to %xesc
|
||
; BX = 0 (no errors)
|
||
; error: BX = error#
|
||
|
||
xesc proc near
|
||
xesc1: cld
|
||
sub ax,2 ;adjust to #args *to %xesc*
|
||
;(len, name are not args)
|
||
mov work_info.xs_nargs,ax ;save actual #args
|
||
; Get from register# (actually, reg x 4) to lookup string.
|
||
lods byte ptr es:[si]
|
||
mov bl,al
|
||
xor bh,bh ;BX is reg# x 4 of name string
|
||
mov work_info.xs_pc,si ;save bytecode@
|
||
mov work_info.xs_pc+2,es
|
||
mov work_info.xs_rvreg,bx ;save reg# x 4;
|
||
;return value goes here
|
||
lea bx,regs[bx] ;reg# x 4 -> VM reg @
|
||
mov si,[bx].C_page
|
||
cmp ptype[si],STRTYPE*2 ;is it a string?
|
||
je xesc_5 ;yes, jump
|
||
cmp ptype[si],SYMTYPE*2 ;is it a symbol?
|
||
je xesc_3 ;yes, jump
|
||
mov ax,XLI_ERR_NAME_BAD_TYPE ;error: name not string, symbol
|
||
jmp xesc_err_exit
|
||
xesc_3: %LoadPage es,si ;page# in SI -> para# in ES
|
||
mov bp,[bx].C_disp ;ES:BP is symbol object @
|
||
mov ax,es:[bp].sym_len ;get symbol object length
|
||
sub ax,sym_ovhd ;subtract symbol's overhead
|
||
add bp,sym_ovhd ;skip past overhead
|
||
jmp short xesc_9
|
||
xesc_5: %LoadPage es,si ;page# in SI -> para# in ES
|
||
mov bp,[bx].C_disp ;ES:BP is string object @
|
||
mov ax,es:[bp].str_len ;get string object length
|
||
cmp ax,0 ;is it positive?
|
||
jge xesc_8 ;yes, jump; normal string
|
||
add ax,str_ovhd*2 ;no, assume short string
|
||
;rather than really long string
|
||
;and make positive
|
||
xesc_8: sub ax,str_ovhd ;subtract string's overhead
|
||
add bp,str_ovhd ;skip past overhead
|
||
xesc_9: mov work_area.srch_slen,ax ;save length of string data
|
||
mov work_area.srch_sptr,bp ;save address of string data
|
||
mov work_area.srch_sptr+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,work_info.xs_rvreg ;get the name
|
||
lea bx,regs[bx]
|
||
mov ax,XLI_ERR_NO_SUCH_NAME ;error: no such name loaded
|
||
jmp xesc_ext_err_exit ;use alternate error point
|
||
;so name gets printed with
|
||
;error message
|
||
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 ;XLI ID
|
||
cmp ax,XLI_ID
|
||
je xesc_15
|
||
mov ax,XLI_ERR_BAD_VERSION
|
||
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,work_info.xs_nargs ;CX is #args
|
||
;xesc_20: cmp cx,0 ;any left?
|
||
; jcxz xesc_50 ;no, jump
|
||
mov cx,0 ;CX is current arg#
|
||
xesc_20: cmp cx,work_info.xs_nargs ;any left?
|
||
je xesc_50 ;no, jump
|
||
push cx ;tempsave current arg#
|
||
mov si,work_info.xs_pc
|
||
mov es,work_info.xs_pc+2 ;ES:SI is bytecode@
|
||
lods byte ptr es:[si]
|
||
mov work_info.xs_pc,si ;save next bytecode @
|
||
mov bl,al
|
||
xor bh,bh ;BX is reg# x 4
|
||
; Put the reg# and current arg @ into swap table
|
||
mov ax,bx
|
||
xchg bx,cx ;BX is current arg#
|
||
shl bx,1
|
||
shl bx,1 ;make index into swap table
|
||
mov word ptr swap_table[bx].sw_reg,ax ;save VM reg# x 4
|
||
mov ax,work_info.xs_args
|
||
mov word ptr swap_table[bx].sw_offset,ax ;save arg@
|
||
mov bx,cx ;restore reg# x 4
|
||
; Dispatch on argument type
|
||
lea bx,regs[bx] ;BX is VM reg @
|
||
mov di,[bx].C_page ;get its page#
|
||
mov di,ptype[di] ;and its type
|
||
; push cx
|
||
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#
|
||
; dec cx
|
||
inc cx
|
||
jmp xesc_20
|
||
|
||
; Everything's ready. Call the child.
|
||
xesc_50: call update_swap_table
|
||
call_child 1
|
||
|
||
; 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
|
||
call ssr ;do special service and
|
||
jmp xesc_50 ;return immediately back
|
||
;across the interface
|
||
; 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 bp,work_info.xs_rvptr
|
||
mov es,work_info.xs_rvptr+2 ;ES:BP points to return value
|
||
;(external-pgm error message)
|
||
call cs:word ptr do_val[SWI_STR*2] ;build string
|
||
mov bx,work_info.xs_rvreg
|
||
lea bx,regs[bx] ;BX=addr of reg with error string
|
||
mov ax,XLI_ERR_EXTERNAL_ERROR ;AX=XLI error code
|
||
jmp short xesc_ext_err_exit
|
||
|
||
xesc_65: cmp di,N_RV ;return value out of range?
|
||
jb xesc_70 ;no, jump
|
||
mov ax,XLI_ERR_VALUE_BAD_TYPE
|
||
jmp xesc_err_exit
|
||
xesc_70: shl di,1
|
||
mov bp,work_info.xs_rvptr
|
||
mov es,work_info.xs_rvptr+2 ;ES:BP point to return value
|
||
call cs:word ptr do_val[di] ;handle one type of return value
|
||
lea bx,nil_reg ;CX says "nil irritant"
|
||
mov ax,0 ;BX=0 says no errors
|
||
ret
|
||
|
||
; This file's error exit processing. Reset the stack so that we return
|
||
; immediately to the gate. BX should be set with an error code before
|
||
; jumping here.
|
||
xli_err_exit:
|
||
xesc_err_exit: ;AX contains error#
|
||
lea bx,nil_reg ;BX is "nil irritant"
|
||
; Another exit label. This allows both AX (XLI error code) and BX
|
||
; (the address of the VM register with the "irritant") to be set beforehand.
|
||
xli_ext_err_exit:
|
||
xesc_ext_err_exit:
|
||
mov sp,gate_sp ;return to gate
|
||
ret
|
||
|
||
|
||
;; Jump tables
|
||
; indexed by argument type (standard PCS type tag)
|
||
do_arg dw do_lstarg ;0=list (#f only)
|
||
dw do_fixarg ;1=fixnum
|
||
dw do_floarg ;2=flonum
|
||
dw do_bigarg ;3=bignum
|
||
dw do_symarg ;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
|
||
mov si,[bx].C_page ;get object's page#
|
||
%LoadPage es,si ;swap it in
|
||
mov si,[bx].C_disp ;ES:SI is Scheme object @
|
||
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: inc si ;incr past tag
|
||
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
|
||
do_flo40: ret
|
||
do_floarg endp
|
||
|
||
do_bigarg proc near
|
||
mov si,[bx].C_page ;get object's page#
|
||
%LoadPage es,si ;swap it in
|
||
mov si,[bx].C_disp ;ES:SI is Scheme object @
|
||
; Stage the conversion to longint in XLI space.
|
||
mov bp,sp
|
||
push bx ;push VM reg@
|
||
push work_info.xs_local ;push local buffer@
|
||
mov work_info.C_fn,offset pgroup:int2long
|
||
call far ptr far_C
|
||
mov sp,bp
|
||
cmp ax,0 ;did bignum convert OK?
|
||
je do_big5 ;yes, jump
|
||
mov ax,XLI_ERR_BIG_TO_32_BITS ;error: bignum too big
|
||
;to become longint
|
||
jmp xesc_err_exit
|
||
do_big5: test work_info.xs_flags,FB_NEAR ;near flag on?
|
||
jz do_big20 ;no, jump
|
||
; near (copy longint to child's space)
|
||
mov bp,work_info.xs_local
|
||
mov es,work_info.xs_local+2 ;ES:BP points to XLI-local longint
|
||
mov ax,es:[bp] ;move longint to regs
|
||
mov dx,es:[bp]+2
|
||
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 ax,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: mov ax,work_info.xs_local ;move ptr to longint to regs
|
||
mov dx,work_info.xs_local+2
|
||
; 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
|
||
do_big40: ret
|
||
do_bigarg endp
|
||
|
||
do_fixarg proc near
|
||
; Stage the conversion to int in XLI space
|
||
mov ax,[bx].C_disp ;get the fixnum data
|
||
shl ax,1 ;deal with sign bit
|
||
sar ax,1 ;AX is 16-bit signed int
|
||
; True and false are treated as the numbers 1 and 0, respectively.
|
||
; Boolean-argument processing merges into integer processing at this point.
|
||
do_log: cwd ;DX:AX is 32-bit signed int
|
||
; Copy the int data to the proper place
|
||
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
|
||
do_fix40: ret
|
||
do_fixarg endp
|
||
|
||
do_lstarg proc near ;looking for false only
|
||
cmp [bx].C_page,NIL_PAGE*2
|
||
jne do_xxerr
|
||
mov ax,0
|
||
jmp do_log
|
||
do_lstarg endp
|
||
|
||
do_xxerr: jmp do_errarg ;conditional jumps
|
||
;are too short
|
||
|
||
do_symarg proc near ;looking for true only
|
||
cmp [bx].C_page,T_PAGE*2
|
||
jne do_xxerr
|
||
cmp [bx].C_disp,T_DISP
|
||
jne do_xxerr
|
||
mov ax,1
|
||
jmp do_log
|
||
do_symarg endp
|
||
|
||
do_strarg proc near
|
||
mov si,[bx].C_page
|
||
%LoadPage es,si ;load string into memory
|
||
mov si,[bx].C_disp ;ES:SI is Scheme object @
|
||
test work_info.xs_flags,FB_NEAR ;near flag on?
|
||
jz do_str10 ;no, jump
|
||
; near (can't copy string because we don't have its address--
|
||
; put nil address into parm block)
|
||
mov bp,work_info.xs_args
|
||
mov es,work_info.xs_args+2 ;ES:BP is arg @
|
||
mov word ptr es:[bp],0
|
||
mov cx,2
|
||
jmp short do_str60
|
||
; far (we can copy the string's address, but need to check
|
||
; on earlier strings' swap status)
|
||
do_str10:
|
||
mov ax,si
|
||
add ax,STR_OVHD
|
||
mov dx,es ;DX:AX is string data @
|
||
mov bp,work_info.xs_args
|
||
mov es,work_info.xs_args+2 ;ES:BP is arg @
|
||
mov es:[bp],ax
|
||
mov es:[bp]+2,dx
|
||
mov cx,4
|
||
; Increment arg pointer by an appropriate amount
|
||
do_str60: test work_info.xs_flags,FB_PAD ;padding on?
|
||
jz do_str65 ;no, jump
|
||
mov cx,PAD_SIZE
|
||
do_str65: add work_info.xs_args,cx
|
||
ret
|
||
do_strarg endp
|
||
|
||
do_errarg proc near
|
||
mov ax,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:BP = pointer to return value
|
||
|
||
do_floval proc near
|
||
test work_info.xs_flags,FB_NEAR ;is near flag on?
|
||
jnz do_flv10 ;yes, jump
|
||
; far
|
||
mov ax,es:[bp] ;get ptr to number
|
||
mov dx,es:[bp]+2
|
||
mov bp,ax
|
||
mov es,dx ;ES:BP points to number
|
||
; near
|
||
do_flv10: mov dx,es:[bp]+6 ;get double in registers
|
||
mov cx,es:[bp]+4
|
||
mov bx,es:[bp]+2
|
||
mov ax,es:[bp]
|
||
mov bp,sp ;get BP set for C call
|
||
push dx ;push double
|
||
push cx
|
||
push bx
|
||
push ax
|
||
mov bx,work_info.xs_rvreg ;push return value VM reg@
|
||
lea bx,regs[bx]
|
||
; lea bx,reg1 ;temporary
|
||
push bx
|
||
mov work_info.C_fn,offset pgroup:alloc_fl
|
||
call far ptr far_C ;C double -> PCS flonum
|
||
mov sp,bp ;pop C args
|
||
ret
|
||
do_floval endp
|
||
|
||
do_TFval proc near
|
||
mov cx,0
|
||
mov ax,es:[bp] ;get value
|
||
or ax,es:[bp]+2 ;all bytes must = 0 to be nil
|
||
or ax,es:[bp]+4
|
||
or ax,es:[bp]+6
|
||
jz do_TF10 ;yes (false object)
|
||
mov ax,T_DISP ;no (true object)
|
||
mov cx,T_PAGE*2
|
||
do_TF10:
|
||
mov bx,work_info.xs_rvreg ;push return value VM reg@
|
||
lea bx,regs[bx]
|
||
; lea bx,reg1 ;temporary
|
||
mov [bx].C_disp,ax
|
||
mov [bx].C_page,cx
|
||
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:[bp] ;get ptr to number
|
||
mov dx,es:[bp]+2
|
||
mov bp,ax
|
||
mov es,dx ;ES:BP points to number
|
||
; near
|
||
do_int10: mov ax,es:[bp] ;get number
|
||
mov dx,es:[bp]+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 bp,sp ;get BP set for C call
|
||
push dx ;push longint
|
||
push ax
|
||
mov bx,work_info.xs_rvreg ;push return value VM reg@
|
||
lea bx,regs[bx]
|
||
; lea bx,reg1 ;temporary
|
||
push bx
|
||
mov work_info.C_fn,offset pgroup:long2int
|
||
call far ptr far_C ;C longint -> PCS integer
|
||
;(bignum or fixnum)
|
||
mov sp,bp ;pop C args
|
||
ret
|
||
do_intval endp
|
||
|
||
do_strval proc near
|
||
; allocate the space for the return value string object
|
||
mov di,2 ;DI=offset of length in
|
||
;return value field (near)
|
||
test work_info.xs_flags,FB_NEAR ;is near flag on?
|
||
jnz do_stv10 ;yes, jump
|
||
mov di,4 ;different offset for far
|
||
do_stv10: mov cx,es:[bp][di] ;get string's length
|
||
mov bx,16380 ;BX is max string length
|
||
cmp cx,bx ;is CX short enough?
|
||
jbe do_stv15 ;yes, jump
|
||
mov cx,bx ;no, truncate at max
|
||
do_stv15: push cx ;tempsave length
|
||
mov bp,sp ;get BP set for C call
|
||
push cx ;push length
|
||
mov ax,STRTYPE
|
||
push ax ;push type
|
||
mov bx,work_info.xs_rvreg
|
||
lea bx,regs[bx]
|
||
push bx ;push return value VM reg @
|
||
mov work_info.C_fn,offset pgroup:alloc_bl
|
||
call far ptr far_C ;allocate string object;
|
||
;"alloc_block" takes care
|
||
;of overhead matters
|
||
mov sp,bp ;pop C args
|
||
pop cx ;restore length
|
||
mov bx,work_info.xs_rvreg
|
||
lea bx,regs[bx] ;BX is return value VM reg @
|
||
mov di,[bx].C_disp
|
||
mov es,[bx].C_page
|
||
%LoadPage es,es ;ES:DI is dest object @
|
||
add di,3 ;skip past string's overhead
|
||
; copy string data into string object
|
||
push es ;tempsave ES
|
||
mov si,work_info.xs_rvptr
|
||
mov es,work_info.xs_rvptr+2 ;ES:SI points to return value
|
||
;field in parameter block
|
||
mov ax,es:[si]
|
||
mov dx,work_info.xs_pb_segment ;DX:AX is src string @ (near)
|
||
test work_info.xs_flags,FB_NEAR ;is near flag on?
|
||
jnz do_stv50 ;yes, jump
|
||
mov dx,es:[si]+2 ;DX:AX is src string @ (far)
|
||
do_stv50: pop es ;restore ES
|
||
push ds ;tempsave our DS
|
||
mov si,ax ;ES:DI is dest string @
|
||
mov ds,dx ;DS:SI is src string @
|
||
rep movsb ;copy string
|
||
pop ds ;restore our DS
|
||
ret
|
||
do_strval endp
|
||
|
||
do_errval proc near
|
||
mov ax,XLI_ERR_VALUE_BAD_TYPE
|
||
jmp xesc_err_exit
|
||
do_errval endp
|
||
|
||
subttl Code segment: Special Services
|
||
page
|
||
|
||
; On entry, ES:BP is parm block pointer.
|
||
|
||
ssr proc near
|
||
mov bx,es:[bp].pb_ss ;get dispatch number
|
||
cmp bx,SS_SWAP
|
||
je ss1
|
||
jne ss_exit ;note we don't fall thru
|
||
|
||
ss_normal_exit:
|
||
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
|
||
ss_exit: ret
|
||
|
||
; the conditional jumps can't jump far enough, hence this table
|
||
ss1: jmp ssr_swap
|
||
|
||
; "Swap" special service
|
||
|
||
ssr_swap: mov bx,es:[bp].pb_ss_args ;get arg#
|
||
mov cx,es:[bp].pb_ss_args+2 ;get dest. length
|
||
shl bx,1
|
||
shl bx,1
|
||
push bx ;tempsave index into swap table
|
||
test work_info.xs_flags,FB_NEAR ;is near flag on?
|
||
jnz ss_10 ;yes, jump
|
||
;
|
||
; Far -------------------
|
||
;
|
||
; ss_args+0: swap table index corr. to arg# (already in BX)
|
||
;
|
||
; ss returns:
|
||
; in ss_args+0: true length of string
|
||
; in pb args: far @ to string
|
||
;
|
||
mov bx,word ptr swap_table[bx].sw_reg ;BX is reg# x 4
|
||
lea bx,regs[bx] ;BX is reg@
|
||
mov si,[bx].C_page ;get object's page#
|
||
%LoadPage es,si ;load object into memory
|
||
mov si,[bx].C_disp ;ES:SI is string object @
|
||
inc si ;skip over tag
|
||
mov ax,es:[si] ;get string's length
|
||
inc si ;skip over length
|
||
inc si
|
||
cmp ax,0 ;a short string?
|
||
jge ss_5 ;no, jump
|
||
add ax,str_ovhd*2 ;yes
|
||
ss_5: sub ax,str_ovhd ;subtract off overhead
|
||
mov di,es ;DI:SI is string data @
|
||
mov es,work_info.xs_pb_segment ;ES:BP is parameter block @
|
||
mov es:[bp].pb_ss_args,ax ;put string length in ss_args
|
||
pop bx ;restore swap table index
|
||
mov bp,word ptr swap_table[bx].sw_offset ;ES:BP points to this arg's
|
||
;location in parameter block
|
||
mov es:[bp],si ;put far @ in parm block
|
||
mov es:[bp]+2,di
|
||
jmp ss_normal_exit
|
||
;
|
||
; Near -------------------
|
||
;
|
||
; ss_args+0: swap table index corr. to arg# (already in BX)
|
||
; +2: length (already in CX)
|
||
; +4: near ptr
|
||
;
|
||
; ss returns:
|
||
; in ss_args+0: length used in copying
|
||
; in pb args: near @ to string
|
||
;
|
||
ss_10: mov ax,es:[bp].pb_ss_args+4 ;get dest @
|
||
mov work_info.xs_dest,ax
|
||
mov ax,work_info.xs_pb_segment
|
||
mov work_info.xs_dest+2,ax
|
||
mov bx,word ptr swap_table[bx].sw_reg ;get reg# x 4 corr. to arg
|
||
lea bx,regs[bx] ;BX is reg@
|
||
mov si,[bx].C_page
|
||
%LoadPage es,si ;load object into memory
|
||
mov si,[bx].C_disp ;ES:SI is string object @
|
||
inc si ;skip over tag
|
||
mov ax,es:[si] ;get string's length
|
||
inc si ;skip over length
|
||
inc si
|
||
cmp ax,0 ;a short string?
|
||
jge ss_15 ;no, jump
|
||
add ax,str_ovhd*2 ;yes
|
||
ss_15: sub ax,str_ovhd ;subtract off overhead
|
||
cmp ax,cx ;string len >= buffer len?
|
||
jae ss_20 ;yes, jump
|
||
mov cx,ax ;CX is #chars to copy
|
||
ss_20: push ds ;tempsave our DS
|
||
push es
|
||
mov di,work_info.xs_dest
|
||
mov es,work_info.xs_dest+2 ;ES:DI is dest @
|
||
pop ds ;DS:SI is src @
|
||
rep movsb ;copy string
|
||
pop ds ;restore our DS
|
||
load_index pb_table
|
||
mov bp,word ptr pb_table[bx]
|
||
mov es,word ptr pb_table[bx]+2 ;ES:BP is parm block @
|
||
mov es:[bp].pb_ss_args,ax ;put #chars copied in ss_args
|
||
pop bx ;restore swap table index
|
||
mov bp,word ptr swap_table[bx].sw_offset ;ES:BP points to this arg's
|
||
;location in parameter block
|
||
mov ax,work_info.xs_dest
|
||
mov es:[bp],ax ;put near @ in parm block
|
||
jmp ss_normal_exit
|
||
|
||
ssr endp
|
||
|
||
subttl Code segment: update_swap_table
|
||
page
|
||
|
||
update_swap_table proc near
|
||
; for small models, PCS may indeed be swapping, but we don't care, as data
|
||
; is copied to the external program on its first reference and
|
||
; remains always available to the program since the pointer in the parm block
|
||
; points into the program's own space, not PCS's
|
||
test work_info.xs_flags,FB_NEAR ;is near flag on?
|
||
jnz ust_exit ;yes, exit
|
||
mov cx,0 ;CX is argument counter
|
||
ust_10: cmp cx,work_info.xs_nargs
|
||
jge ust_exit
|
||
mov bx,cx
|
||
shl bx,1
|
||
shl bx,1 ;BX is swap table index
|
||
push bx ;tempsave index
|
||
mov bx,word ptr swap_table[bx].sw_reg ;get reg# x 4 of argument
|
||
lea bx,regs[bx] ;BX is reg@
|
||
mov di,[bx].C_page ;get object's page#
|
||
mov di,ptype[di] ;then its type
|
||
cmp di,STRTYPE*2 ;is it a string?
|
||
je ust_40 ;yes, jump
|
||
pop bx ;no, discard index
|
||
ust_30: inc cx ;incr to next argument
|
||
jmp ust_10
|
||
ust_40: mov ax,[bx].C_page ;get page#
|
||
mov bp,sp ;set up BP for calling C
|
||
push ax ;push page#
|
||
mov work_info.C_fn,offset pgroup:getbase ;this routine's not in
|
||
;C but does use its calling
|
||
;conventions
|
||
call far ptr far_C
|
||
mov sp,bp ;pop C args
|
||
pop bx ;restore swap table index
|
||
; If carry is clear, the argument is in memory already.
|
||
; The address in the parm block should be OK since an object
|
||
; coming into memory has its address updated at the time of the
|
||
; swap. Swapping does not cause a GC, so GC's shouldn't relocate
|
||
; an address. That leaves zeroing the addresses of objects
|
||
; that were swapped out.
|
||
jnc ust_30 ;object's in memory, jump
|
||
; Carry set means object is swapped out. Zero the argument's
|
||
; pointer in the parm block.
|
||
mov bp,word ptr swap_table[bx].sw_offset
|
||
mov es,work_info.xs_pb_segment ;ES:BP points to this arg's
|
||
;location in the parm block
|
||
mov word ptr es:[bp],0 ;zero offset part of pointer
|
||
mov word ptr es:[bp]+2,0 ;zero segment part of pointer
|
||
jmp ust_30
|
||
;
|
||
ust_exit: ret
|
||
|
||
update_swap_table 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
|
||
|
||
subttl Far/near linkage to XLI
|
||
page
|
||
|
||
; Near linkage
|
||
|
||
; All other routines in this file are accessed through this one.
|
||
; On entry, BX contains an index into a jump table of routines to execute.
|
||
%xli_gate proc far
|
||
shl bx,1 ;get index on word boundary
|
||
mov gate_sp,sp
|
||
sub gate_sp,2
|
||
; We adjusted the SP for the return address which the call in the next
|
||
; instruction will put on the stack.
|
||
; Error recovery by nested routines is done by restoring this SP value and
|
||
; then returning, which will bring them back to just after the call.
|
||
call cs:gate[bx]
|
||
ret
|
||
|
||
; jump table
|
||
gate dw load_all,xesc,unload_all
|
||
|
||
%xli_gate endp
|
||
|
||
subttl Debugging XLI from PCS
|
||
page
|
||
|
||
; If PCS is run under DEBUG, executing (%xli-debug <0 or 1>) will
|
||
; execute the following code, which either installs an INT 3 (=1) or NOPs (=0).
|
||
; When INT 3 is executed, DEBUG is called. This provides a way for
|
||
; writers of external routines to get a hook at execution time into
|
||
; their code for debugging. Also, the value returned in AX is the PROGX offset
|
||
; of the jumps accessed from the external program.
|
||
|
||
; Registers destroyed: AX
|
||
|
||
%xlidbg proc far
|
||
or ax,ax
|
||
jz dbgoff
|
||
mov al,cs:dbgint ;install INT 3 instruction
|
||
mov xlidbg1,al
|
||
mov al,cs:dbgint+1
|
||
mov xlidbg1+1,al
|
||
jmp short dbgexit
|
||
dbgoff: mov al,cs:dbgnop ;install 2 NOP instructions
|
||
mov xlidbg1,al
|
||
mov xlidbg1+1,al
|
||
dbgexit: mov ax,offset biddbg ;return address of jump table
|
||
;following EXEC of child
|
||
ret
|
||
|
||
; data for above routine
|
||
dbgint label byte
|
||
int 3 ;INT 3 instruction
|
||
dbgnop label byte
|
||
nop ;NOP instruction
|
||
|
||
%xlidbg endp
|
||
|
||
progx ends
|
||
|
||
|
||
; Far linkage *to* XLI
|
||
|
||
prog segment byte public 'PROG'
|
||
assume cs:pgroup
|
||
|
||
public xli_init,xli_xesc,xli_term
|
||
|
||
; We preserve DS,ES,BP. AX..DI are destroyed.
|
||
xli_init proc near
|
||
mov bx,0
|
||
xli_10: push bp ;we use ES:BP a lot
|
||
push es
|
||
call %xli_gate ;cross over into PROGX segment
|
||
pop es
|
||
pop bp
|
||
ret
|
||
xli_xesc: mov bx,1
|
||
jmp xli_10
|
||
xli_term: mov bx,2
|
||
jmp xli_10
|
||
xli_init endp
|
||
|
||
; Far linkage *from* XLI
|
||
; (all the memory allocation routines are written in C).
|
||
; The caller of this should have set BP from SP before pushing the C args,
|
||
; then restore SP from BP afterwards to remove them from the stack.
|
||
; We don't preserve ES across the call.
|
||
|
||
far_C proc far
|
||
push ds ;C likes ES=DS
|
||
pop es
|
||
pop work_info.C_retadr ;get far @ off stack so C sees its args
|
||
pop work_info.C_retadr+2
|
||
call [work_info.C_fn]
|
||
push work_info.C_retadr+2
|
||
push work_info.C_retadr
|
||
ret ;C returns with return value in AX..DX
|
||
far_C endp
|
||
|
||
; Far linkage to XLI debug hook
|
||
|
||
; stack:
|
||
; saved BP
|
||
; return address (near call)
|
||
; arg (0=turn off, 1=turn on debug)
|
||
|
||
; AX,BX returns PROGX offset of the jump table following the EXEC of the child.
|
||
; This should be the same offset value as in the DOS terminate address vector
|
||
; in the child's PSP.
|
||
|
||
public xlidbg
|
||
|
||
xlidbg proc near
|
||
push bp
|
||
mov bp,sp ;after this instruction, stack matches comments
|
||
mov ax,[bp]+4
|
||
call %xlidbg
|
||
pop bp
|
||
ret
|
||
xlidbg endp
|
||
|
||
prog ends
|
||
end
|
||
|