1707 lines
53 KiB
NASM
1707 lines
53 KiB
NASM
; =====> PRO2REAL.ASM
|
||
; PC Scheme Protected Mode -> Real Mode Interface
|
||
; (c) 1987 by Texas Instruments Incorporated -- all rights reserved
|
||
;
|
||
; This Module contains code which interfaces to external programs via
|
||
; either the External Language Interface (XLI), Software Interrupt,
|
||
; or the Real Procedure Call (RPC). The RPC is specific to protected
|
||
; mode scheme only, and is used to implement XLI.
|
||
|
||
; Author: Terry Caudill (from Bob Beal's original source)
|
||
; History:
|
||
; rb 3/20/87 - original
|
||
; tc 8/7/87 - to work in protected mode scheme
|
||
; tc 10/13/87 - cleanup
|
||
|
||
|
||
page 84,120
|
||
name EXTPROG
|
||
title PC Scheme External Program Interface
|
||
.286c
|
||
|
||
|
||
subttl Includes and Local Equates
|
||
page
|
||
|
||
include scheme.equ
|
||
include sinterp.arg
|
||
include xli.equ
|
||
include xli_pro.mac
|
||
include rpc.equ
|
||
|
||
;
|
||
; Dos function requests
|
||
;
|
||
DOS equ 021h ; Dos Function Request
|
||
DELETE_SEG equ 04900h ; Delete Segment
|
||
REAL_INTRP equ 0E3h ; Issue Real Interrupt - from AIA
|
||
BLOCK_XFER equ 0EC00h ; Block Transfer - from AIA
|
||
ALLOC_REAL equ 0E802h ; Create Real Data Seg - from AIA
|
||
CREATE_WIN equ 0E803h ; Create Real Window - from AIA
|
||
|
||
|
||
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 rpc_handle
|
||
public REAL_MODE_BUFFER,REAL_BUF_OFFSET,REAL_BUF_SELECTOR
|
||
public REAL_BUF_PARA,REAL_BUF_TOP
|
||
public C_fn
|
||
public mem_entry,mem_table
|
||
; external variables
|
||
extrn ctl_file:word,pcs_sysd:word
|
||
extrn regs:word
|
||
extrn vid_mode:word,char_hgt:word
|
||
|
||
;
|
||
; The following data definitions are used in communication with real
|
||
; mode procedures and the real procedure call (RPC) mechanism provided
|
||
; in OS/286 by AI Architects.
|
||
;
|
||
|
||
rpc_real db "realschm.exe",0 ; Name of RPC file to load
|
||
rpc_real_len equ $-rpc_real
|
||
|
||
rpc_handle db 0 ; Handle to real mode scheme routines
|
||
rpc_loaded db 0 ; Flag to note if rpc load was successful
|
||
rpc_saved_sp dw ? ; Saved stack pointer
|
||
|
||
REAL_MODE_BUFFER equ $ ; selector and offset of real mode
|
||
REAL_BUF_OFFSET dw 0 ; offset of real mode buffer
|
||
REAL_BUF_SELECTOR dw 0 ; segment selector of real mode buffer
|
||
REAL_BUF_PARA dw 0 ; segment address of real mode buffer
|
||
REAL_BUF_TOP dw 0 ; note buffer top
|
||
|
||
;
|
||
; The following are xli filenames which must be loaded and used by pcs
|
||
;
|
||
io_exe db "realio.exe" ,0 ;EXE file providing I/O support
|
||
io_exe_len equ $-io_exe
|
||
|
||
graph_exe db "graphics.exe" ,0 ;EXE file providing graphics support
|
||
graph_exe_len equ $-graph_exe
|
||
|
||
trig_exe db "newtrig.exe" ,0 ;EXE file providing trig support
|
||
trig_exe_len equ $-trig_exe
|
||
|
||
;
|
||
; The following table is used to load the system files required by pcs. The
|
||
; xli system files are order dependent.
|
||
sys_files equ $
|
||
;system xli files, order is dependent (see rpc.equ and realschm.asm)
|
||
dw io_exe,io_exe_len ;io support - xli system file
|
||
dw graph_exe,graph_exe_len ;graphics support - xli system file
|
||
;normal xli files
|
||
normal_files equ $
|
||
dw trig_exe,trig_exe_len ;trig file - normal xli file
|
||
dw 0
|
||
;
|
||
; If the above files cannot be found, issue this message and abort scheme
|
||
;
|
||
FILERR db 0Dh,0Ah,"Fatal Error - unable to load file "
|
||
FILNAM db 20 dup (0)
|
||
|
||
;
|
||
; The following table contains gateways from the prog segment to the
|
||
; progx segment. The order is dependent on
|
||
; Table of RPC functions currently defined. Calling any of these functions
|
||
; requires synchronization with the real mode routine.
|
||
;
|
||
FAR_RPC equ $
|
||
frpc_bid equ $-FAR_RPC
|
||
dw init_rpc,progx ; bid real procedure
|
||
frpc_init equ $-FAR_RPC
|
||
dw xpcinit,progx ; get machine type
|
||
frpc_setcrt equ $-FAR_RPC
|
||
dw xsetcrt,progx ; set crt interrupt
|
||
frpc_resetcrt equ $-FAR_RPC
|
||
dw xresetcrt,progx ; reset crt interrupt
|
||
frpc_ldall equ $-FAR_RPC
|
||
dw load_all,progx ; load xli files
|
||
frpc_unld equ $-FAR_RPC
|
||
dw unload_all,progx ; unload xli files
|
||
frpc_xesc equ $-FAR_RPC
|
||
dw xesc,progx ; perform xesc call
|
||
|
||
;
|
||
; The following hooks are used to call routines in the PROG segment
|
||
; from the PROGX segment. See the far_C routine in this module.
|
||
;
|
||
C_fn dw ?
|
||
C_retadr dw ? ; Used to call C routines from PROGX
|
||
dw ?
|
||
|
||
;
|
||
; Mem_table is used to hold selectors to real memory which must be allocated
|
||
; over the life of an xli call. At present, the memory is allocated so that
|
||
; xli routines may access far strings. See SSR within.
|
||
;
|
||
mem_entry dw 0 ;entry into memory table
|
||
mem_table dw N_ARGS dup (0) ;record memory allocated during xli call
|
||
|
||
;
|
||
; The following structures allow xesc and sw-int to share code
|
||
;
|
||
xesc_func db ? ;0 = sw-int, 1 = xesc
|
||
error_return dw ? ;address of error handler
|
||
|
||
which_func dw swi_txt,xli_txt ;will be indexed by xesc_func above
|
||
swi_txt db 'SW-INT',0
|
||
xli_txt db 'XCALL',0
|
||
|
||
;
|
||
; Error return values for software interrupt
|
||
;
|
||
SWI_ERR_ARGN_BAD_TYPE equ 1 ; Bad argument passed to sw-int
|
||
SWI_ERR_VALUE_BAD_TYPE equ 2 ; Bad type passed to sw-int
|
||
SWI_ERR_BIG_TO_32_BITS equ 3 ; Number to large for sw-int
|
||
|
||
swi_errs dw swi_arg0,swi_arg1,swi_arg2
|
||
|
||
;
|
||
; Software Interrupt error messages
|
||
;
|
||
swi_arg0 db 'Invalid argument to SW-INT',0
|
||
swi_arg1 db 'Invalid return value for SW-INT',0
|
||
swi_arg2 db 'Argument to SW-INT too large to fit in 32 bits',0
|
||
|
||
;
|
||
; Protected Mode Fatal type errors
|
||
;
|
||
cr_win db 'CREATE WINDOW',0
|
||
al_seg db 'ALLOCATE SEGMENT',0
|
||
dl_seg db 'DELETE SEGMENT',0
|
||
rl_int db 'ISSUE REAL INTERRUPT',0
|
||
|
||
;
|
||
; Gate to abort code in sc.asm
|
||
;
|
||
|
||
|
||
data ends
|
||
|
||
|
||
subttl Progx code segment definitions
|
||
page
|
||
|
||
; external routines
|
||
extrn alloc_fl:near,int2long:near,long2int:near,alloc_bl:near
|
||
extrn getbase:near
|
||
extrn chg_vmode:near
|
||
extrn pro_erro:near
|
||
|
||
progx segment para public 'PROGX'
|
||
assume cs:xgroup,ds:dgroup,es:dgroup,ss:dgroup
|
||
|
||
extrn xcabt:far
|
||
|
||
public init_rpc,xpcinit,xsetcrt,xresetcrt,xesc,load_all,unload_all
|
||
public ssr
|
||
public do_floarg,do_fixarg,do_bigarg,do_strarg
|
||
public do_floval,do_intval,do_TFval,do_strval
|
||
public softint,swi_strarg,swi_strval
|
||
|
||
|
||
|
||
subttl RPC interface routines
|
||
page
|
||
|
||
; INIT_RPC
|
||
; Load the real mode portion of scheme and save the handle in rpc_handle.
|
||
; Then call the rpc routine to return the real address of a buffer which
|
||
; will be used on subsequent rpc requests. This buffer is mapped to a
|
||
; protected mode selector and stored in REAL_BUF_SELECTOR.
|
||
;
|
||
; The transaction buffer for an rpc must be pointed to by DS:DX. Note that
|
||
; we build this buffer up on the local stack.
|
||
;
|
||
init_rpc proc far
|
||
push bp
|
||
sub sp,80 ;allocate transaction buffer
|
||
mov bp,sp ;should be large enough for filename
|
||
cld
|
||
mov di,pcs_sysd ;di => system directory pathname
|
||
mov cx,64 ;cx = max length
|
||
mov al,0
|
||
repne scasb ;scan pathname for eos character (=0)
|
||
jcxz ini_10 ;jump if none
|
||
dec di ;di => end of pathname
|
||
ini_10:
|
||
mov cx,di
|
||
sub cx,pcs_sysd ;cx = length of system directory
|
||
mov di,sp ;di => stack (transaction buffer)
|
||
mov si,pcs_sysd ;si => pcs-sysdir
|
||
rep movsb ;copy system directory into buffer
|
||
mov al,'\' ;follow directory name with \
|
||
stosb
|
||
mov si,offset rpc_real
|
||
mov cx,rpc_real_len
|
||
rep movsb ;follow directory w/real proc filename
|
||
|
||
;Initialize real procedure call
|
||
mov dx,sp ;ds:dx => real procedure filename
|
||
mov ah,RPC_INIT ;load and init real procedure
|
||
int DOS ;extended Dos call for Protected mode
|
||
jnc ini_20 ;continue if no error encountered
|
||
|
||
mov ax, offset rpc_real ;ax => file that couldn't load
|
||
mov cx,rpc_real_len ;cx => length of filename
|
||
jmp fatal_file_err ;jump to fatal error handler
|
||
|
||
ini_20:
|
||
mov rpc_handle,al ;save handle to real procedure
|
||
inc rpc_loaded ;note real procedure loaded
|
||
|
||
; Obtain communication buffer for subsequent RPC calls
|
||
mov dx,bp ;ds:dx => transaction buffer
|
||
mov word ptr [bp],RPCRETBUF ;return real buffer opcode
|
||
mov cx,8 ;pass 8 bytes
|
||
mov bx,cx ;expect 8 bytes returned
|
||
mov ah,RPC ;issue Real Procedure Call
|
||
int DOS ;extended Dos call for Protected mode
|
||
;ignore return status
|
||
|
||
mov dx,[bp]+2 ;get length of buffer
|
||
sub dx,2 ;calc top of stack
|
||
mov REAL_BUF_TOP,dx ; and save
|
||
mov si,sp
|
||
add si,4 ;ds:si => real address of buffer
|
||
mov ax,[si]+2 ;get paragraph address
|
||
mov REAL_BUF_PARA,ax ; and save
|
||
;ds:si=> offset,seg of real buffer, dx=length
|
||
call map_real_mem ;map real address to protected selector
|
||
mov REAL_BUF_SELECTOR,ax ; and save
|
||
|
||
add sp,80 ;now clean up the stack
|
||
pop bp
|
||
ret ;and return
|
||
init_rpc endp
|
||
|
||
; XPCINIT
|
||
; Determine the machine type and perform machine specific initialization.
|
||
; Call the real mode routine to perform initialization functions via the
|
||
; RPC mechanism.
|
||
;
|
||
; Input: none
|
||
; Output: return status, pc machine type, and video mode are returned
|
||
; in the communications buffer accessed by REAL_MODE_SELECTOR.
|
||
;
|
||
xpcinit proc far
|
||
push RPCTYPE ; Type code
|
||
mov dx,sp ; ds:dx => arg buffer
|
||
mov cx,2 ; cx = # arg bytes passed
|
||
mov bx,cx ; bx = # result bytes expected
|
||
mov al,rpc_handle ; Handle to real mode part
|
||
mov ah,RPC ; Real Procedure Call
|
||
int DOS ; Extended Dos call for Protected mode
|
||
; Check for errors here
|
||
pop ax ; ignore return status
|
||
; Get the return values from the real mode buffer
|
||
MOVE_ARGS_FROM_BUF <PC_MAKE,VID_MODE,CHAR_HGT>,REAL_MODE_BUFFER
|
||
|
||
mov ax,ds
|
||
mov es,ax ; restore extra seg reg
|
||
ret ; and return
|
||
xpcinit endp
|
||
|
||
|
||
; XSETCRT
|
||
; Take over the real mode crt interrupt handler during a dos-call so that
|
||
; display will not be written to.
|
||
;
|
||
; Input: none
|
||
; Output: screen output will be inhibited
|
||
;
|
||
xsetcrt proc far
|
||
push RPCTAKCRT ; Take over crt interrupt handler
|
||
mov dx,sp ; ds:dx => arg buffer
|
||
mov cx,2 ; cx = # arg bytes passed
|
||
mov bx,cx ; bx = # result bytes expected
|
||
mov al,rpc_handle ; Handle to real mode part
|
||
mov ah,RPC ; Real Procedure Call
|
||
int DOS ; Extended Dos call for Protected mode
|
||
pop ax ; ignore return status
|
||
ret ; and return
|
||
xsetcrt endp
|
||
|
||
|
||
; XRESETCRT
|
||
; Restore the original crt interrupt handler after a dos call so that the
|
||
; display can once again be written to.
|
||
;
|
||
; Input: none
|
||
; Output: screen output will be restored
|
||
;
|
||
xresetcrt proc far
|
||
push RPCRSTCRT ; Restore crt interrupt handler
|
||
mov dx,sp ; ds:dx => arg buffer
|
||
mov cx,2 ; cx = # arg bytes passed
|
||
mov bx,cx ; bx = # result bytes expected
|
||
mov al,rpc_handle ; Handle to real mode part
|
||
mov ah,RPC ; Real Procedure Call
|
||
int DOS ; Extended Dos call for Protected mode
|
||
; Check for errors here
|
||
pop ax ; ignore return status
|
||
ret ; and return
|
||
xresetcrt endp
|
||
|
||
|
||
|
||
subttl RPC interface routines to XLI
|
||
page
|
||
|
||
; LOAD_ALL
|
||
; A portion of the XLI routines is in real mode and is communicated with
|
||
; via the Real Procedure Call (RPC). Data must be passed to the real mode
|
||
; routine via the real buffer REAL_MODE_BUFFER
|
||
;
|
||
; Any errors encountered are currently ignored.
|
||
|
||
l_save struc
|
||
exe_name dw ? ;index to start of exe name
|
||
handle dw ? ;file handle
|
||
l_len db ? ;marker for size of local area
|
||
l_save ends
|
||
|
||
load_all proc far
|
||
push bp
|
||
sub sp,l_len ;allocate local storage
|
||
mov bp,sp
|
||
|
||
; calc length of pathname
|
||
cld
|
||
mov di,pcs_sysd
|
||
mov cx,64 ;max length of pathname
|
||
mov al,0
|
||
repne scasb ;look for eos character (=0)
|
||
jcxz la_10 ;jump if none
|
||
dec di
|
||
la_10:
|
||
mov cx,di
|
||
sub cx,pcs_sysd ;cx = length of pcs-sysdir
|
||
; copy pcs-sysdir into transaction buffer
|
||
push cx ;tempsave length
|
||
RESET_REAL_BUFFER_OFFSET ;ensure start at buffer start
|
||
MOVE_ARGS_TO_BUF <1>,REAL_MODE_BUFFER,autoincr ;system file first
|
||
add di,2 ;save space for exe index
|
||
pop cx ;restore length
|
||
mov si,pcs_sysd ;ds:si addresses pcs-sysdir
|
||
MOVE_TO_REAL_BUF autoincr ;move to real memory buffer
|
||
|
||
mov al,'\' ;append \ onto pcs-sysdir name
|
||
MOVE_BYTE_TO_BUF al,,autoincr
|
||
;save index to exe filename
|
||
mov [bp].exe_name,di ;save offset after pcs-sysdir
|
||
mov bx,di ;save offset after pcs-sysdir
|
||
mov di,2
|
||
MOVE_ARGS_TO_BUF <bx> ;save index to exe file
|
||
|
||
mov di,bx ;position offset for .EXE name
|
||
;save control filename to transaction buffer
|
||
mov bx,ctl_file ;get address of ctl file
|
||
cmp byte ptr [bx],'-' ;user override normal xli files?
|
||
jne sysload ; no, jump
|
||
mov word ptr normal_files,0 ; Yes, don't load normal xli files
|
||
inc ctl_file ; bump ptr to name
|
||
sysload:
|
||
; load all system files - di should not be modified in following loop
|
||
mov si,offset sys_files
|
||
loadfile:
|
||
push si ;save offset into file table
|
||
mov cx,ds:[si+2] ;cx = length
|
||
mov si,ds:[si] ;si => filename
|
||
MOVE_TO_REAL_BUF ;copy filename to buffer
|
||
push RPCLDEXE ;RPC request code to load EXE
|
||
mov dx,sp ;ds:dx => rpc request code
|
||
mov cx,2 ;cx = # arg bytes passed
|
||
mov bx,cx ;bx = # arg bytes returned
|
||
mov al,rpc_handle ;al = handle
|
||
mov ah,RPC ;Issue Real Procedure Call
|
||
int DOS ;Issue extended dos funcall
|
||
pop ax ;ah = flags, al= return status
|
||
pop si ;restore index into file table
|
||
sahf ;load flags
|
||
jnc load_10 ;no carry, proceed
|
||
mov cx,ds:[si+2] ;cx = length
|
||
mov ax,ds:[si] ;si => filename
|
||
jmp fatal_file_err ;go report error
|
||
load_10:
|
||
add si,4 ;address next entry
|
||
cmp word ptr ds:[si],0 ;any more entrys?
|
||
jne loadfile ; yes, loop
|
||
userload:
|
||
xor di,di ;address system flag
|
||
MOVE_ARGS_TO_BUF <0> ;indicate user defined xli
|
||
mov di,[bp].exe_name ;di = index to exe name
|
||
; open XLI control file
|
||
mov dx,ctl_file ;dx = address of filename
|
||
mov ax,FR_OPEN ;dos function - open file
|
||
int DOS
|
||
mov [bp].handle,ax ;save handle
|
||
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 di,[bp].exe_name ;es:di => buffer after pathname
|
||
mov bx,[bp].handle ;bx = file handle
|
||
next_char:
|
||
push 0 ;allocate place on stack
|
||
mov dx,sp ;dx = address of buffer
|
||
mov cx,1 ;read one character
|
||
mov ax,FR_READ ;dos function - read file
|
||
int DOS ;ignore errors
|
||
pop dx ;retrieve character
|
||
jnc la_20 ;jump if no error, else
|
||
;suddenly can't read control
|
||
;file, close it and exit
|
||
close:
|
||
mov bx,[bp].handle ;bx = file handle
|
||
mov ax,FR_CLOSE ;dos functions - close file
|
||
int DOS ;ignore errors
|
||
close1:
|
||
add sp,l_len ;adjust stack
|
||
pop bp
|
||
ret ;return
|
||
|
||
la_20: cmp ax,0 ;at eof?
|
||
jz close ;yes, jump
|
||
; we've read a character
|
||
cmp dl,0Dh ;carriage return?
|
||
je got_file ;yes, jump
|
||
cmp dl,' ' ;blank or control char?
|
||
jle next_char ;yes, skip it
|
||
MOVE_BYTE_TO_BUF dl,,autoincr ;move character to buffer
|
||
jmp next_char
|
||
; we've read a complete filename, go load it
|
||
got_file:
|
||
MOVE_BYTE_TO_BUF 0 ;form ASCZII string
|
||
|
||
push RPCLDEXE ;RPC request code to load EXE
|
||
mov dx,sp ;ds:dx => rpc buffer
|
||
mov cx,2 ;cx = # arg bytes passed
|
||
mov bx,cx ;bx = # arg bytes returned
|
||
mov al,rpc_handle ;al = handle
|
||
mov ah,RPC ;Issue Real Procedure Call
|
||
int DOS ;Issue extended dos funcall
|
||
pop ax ;bump result arg from stack
|
||
sahf ;ah = flags
|
||
jnc next_file ;jump if no errors
|
||
xor ah,ah ;clear flags from result
|
||
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
|
||
|
||
|
||
; UNLOAD_ALL
|
||
; Call the real mode routine to unload all exe files.
|
||
;
|
||
; Upon exit:
|
||
; All previously bid xli programs will be released from real memory.
|
||
;
|
||
unload_all proc far
|
||
push RPCUNLDALL ; RPC request code to unload all exe's
|
||
mov dx,sp ; ds:dx => arg buffer
|
||
mov cx,2 ; cx = # arg bytes passed
|
||
mov bx,2 ; bx = # result bytes expected
|
||
mov al,rpc_handle ; Handle to real mode part
|
||
mov ah,RPC ; Real Procedure Call
|
||
int DOS ; Extended Dos call for Protected mode
|
||
pop ax ; ignore errors
|
||
ret
|
||
unload_all endp
|
||
|
||
; FATAL_FILE_ERR
|
||
; We are unable to load a system file in real mode, and cannot
|
||
; continue with scheme. The routine XCABT (in sc.asm) will output
|
||
; a message (via DOS function 9) to the console and abort. Our
|
||
; io may not be available at the time of this error.
|
||
;
|
||
; On entry:
|
||
; ax => filename we are trying to load
|
||
; cx = length of filename
|
||
;
|
||
public fatal_file_err
|
||
fat_err proc near
|
||
fatal_file_err label near
|
||
mov bx,ss
|
||
mov ds,bx
|
||
mov es,bx ;ds,es,ss = data segment
|
||
mov si,ax ;ds:si addresses filename
|
||
mov di,offset FILNAM ;es:di addresses message
|
||
rep movsb ;move filename into message
|
||
mov byte ptr es:[di],"$" ;terminate byte
|
||
cmp rpc_loaded,0 ;have we gotten past rpc load?
|
||
je fat_exit ; no, exit
|
||
call unload_all ; yes, ensure all xli's unloaded
|
||
fat_exit:
|
||
mov dx,offset FILERR ;ds:dx => message
|
||
jmp pgroup:xcabt ;exit to DOS
|
||
fat_err endp
|
||
|
||
; FATAL_PRO_ERR
|
||
; A protected mode operation has failed. Call pro_error in serror.c to
|
||
; output an error message and attempt a scheme-reset.
|
||
; a scheme reset.
|
||
;
|
||
; On entry:
|
||
; ax = error number
|
||
; bx => function call name
|
||
; cx => operation being performed (sw-int, xcall, etc.)
|
||
;
|
||
pro_err proc near
|
||
fatal_pro_err label near
|
||
push bp
|
||
mov bp,sp ;set up stack for call
|
||
push ss
|
||
pop ds ;ensure ds = data segment
|
||
push ax ;error number
|
||
push bx ;function call
|
||
push cx ;routine
|
||
mov C_fn, offset pgroup:pro_erro
|
||
call far ptr far_C ;control will not return here
|
||
pro_err endp
|
||
|
||
|
||
; XESC
|
||
; Handler for the "%xesc" opcode.
|
||
;
|
||
; On entry:
|
||
; AX = length of xesc call (= inst length - 1)
|
||
; ES:SI = pointer to bytecode arguments of the %xesc opcode
|
||
;
|
||
; 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#
|
||
;
|
||
; Description:
|
||
; A buffer is built for an RPC call to the real mode handler for
|
||
; an external subroutine call (XCALL). The buffer is built in a
|
||
; buffer in the real mode routine as follows:
|
||
;
|
||
; +----------------------------------------+
|
||
; | Routine name length (1 word) |
|
||
; | Routine name (above length) |
|
||
; | |
|
||
; | Number of XCALL Arguments (1 word) |
|
||
; | |
|
||
; | Type of Arg1 (1 word) |
|
||
; | Arg1 (type dependent) |
|
||
; | . |
|
||
; | . |
|
||
; | . |
|
||
; | Type of Argn (1 word) |
|
||
; | Argn (type dependent) |
|
||
; +----------------------------------------+
|
||
;
|
||
; After calling the real mode handler, the buffer will contain
|
||
; result info and return values. See the structure "xesc_result"
|
||
; for a description of the buffer upon return.
|
||
;
|
||
|
||
;
|
||
; This following data will be allocated locally within xesc
|
||
;
|
||
local_save struc
|
||
; following is used to store return data from xli routines
|
||
xesc_status dw ? ; return status
|
||
xesc_vtype dw ? ; type of value being returned
|
||
xesc_value dw 4 dup (?) ; return value
|
||
; following is local data used in building xli call
|
||
saved_si dw ? ; segment offset of vm bytecode
|
||
saved_es dw ? ; segment address of vm bytecode
|
||
first_arg dw ? ; first actual argument
|
||
arg_count dw ? ; number of args (len,name are not args)
|
||
rvreg dw ? ; vm register to hold return value
|
||
local_save ends
|
||
|
||
arg_ptr equ saved_si ; alias for current argument pointer
|
||
ssr_status equ xesc_status ; ssr return status (will be -1)
|
||
ssr_argnum equ xesc_vtype ; argument requested (zero based) by ssr
|
||
ssr_len equ xesc_value ; length requested
|
||
ssr_offset equ xesc_value+2 ; real mode offset to store arg
|
||
ssr_seg equ xesc_value+4 ; real mode segment to store arg
|
||
result_buf_len equ saved_si-xesc_status ; length of result buffer
|
||
|
||
|
||
xesc proc far
|
||
push bp ;save callers bp
|
||
sub sp,rvreg+2 ;reserve for local storage
|
||
mov rpc_saved_sp,sp ;save off stack pointer
|
||
mov bp,sp ; and update BP
|
||
|
||
mov xesc_func,1
|
||
lea bx,xesc_err_exit ; Set up error handler for xesc
|
||
mov error_return,bx
|
||
|
||
mov [bp].saved_es,es ;save segaddr of arguments
|
||
inc si ;bump past name to first arg
|
||
mov [bp].saved_si,si ; and save
|
||
mov [bp].first_arg,si
|
||
dec si
|
||
|
||
sub ax,2 ;calc # args (not incl. name)
|
||
mov [bp].arg_count,ax ; and save
|
||
|
||
RESET_REAL_BUFFER_OFFSET ;ensure start at zero
|
||
|
||
;
|
||
; Move the string name to the real mode buffer
|
||
;
|
||
xor bh,bh
|
||
mov bl,byte ptr es:[si] ;BX is reg# of name string
|
||
lea bx,regs[bx] ;VM reg @
|
||
mov [bp].rvreg,bx ; save as return register
|
||
mov si,[bx].C_page
|
||
cmp ptype[si],STRTYPE*2 ;is it a string?
|
||
je xesc_15 ;yes, jump
|
||
cmp ptype[si],SYMTYPE*2 ;is it a symbol?
|
||
je xesc_10 ;yes, jump
|
||
mov ax,XLI_ERR_NAME_BAD_TYPE ;error: name not string, symbol
|
||
jmp xesc_err_exit
|
||
;
|
||
; Warning : DS is not used for the local data segment in the following code
|
||
;
|
||
xesc_10:
|
||
%LoadPage ds,si ;page# in SI -> para# in DS
|
||
mov si,ss:[bx].C_disp ;DS:SI is symbol object @
|
||
mov cx,[si].sym_len ;get symbol object length
|
||
sub cx,sym_ovhd ;subtract symbol's overhead
|
||
add si,sym_ovhd ;skip past overhead
|
||
jmp short xesc_25
|
||
xesc_15: %LoadPage ds,si ;page# in SI -> para# in DS
|
||
mov si,ss:[bx].C_disp ;DS:DI is string object @
|
||
mov cx,[si].str_len ;get string object length
|
||
cmp cx,0 ;is it positive?
|
||
jge xesc_20 ;yes, jump; normal string
|
||
add cx,str_ovhd*2 ;no, assume short string
|
||
;rather than really long string
|
||
;and make positive
|
||
xesc_20: sub cx,str_ovhd ;subtract string's overhead
|
||
add si,str_ovhd ;skip past overhead
|
||
xesc_25:
|
||
push ds
|
||
push si ;temp save string ptr
|
||
push cx ;and length
|
||
|
||
mov ax,ss ;get local data seg
|
||
mov ds,ax
|
||
|
||
MOVE_ARGS_TO_BUF cx,REAL_MODE_BUFFER,autoincr ;move length to buf
|
||
|
||
pop cx
|
||
pop si
|
||
pop ds ;ds:si => string ptr
|
||
|
||
MOVE_TO_REAL_BUF autoincr ;move string to buf
|
||
|
||
;
|
||
; Warning : DS is not used for the local data segment in the above code
|
||
;
|
||
mov ax,ss
|
||
mov ds,ax ;restore data segment
|
||
;
|
||
; Move argument count to real mode buffer
|
||
;
|
||
mov bx,[bp].arg_count
|
||
MOVE_ARGS_TO_BUF bx,,autoincr,save ;move #args to buffer
|
||
|
||
;
|
||
; Move the xesc arguments to the real mode buffer.
|
||
;
|
||
cmp bx,0 ;any arguments?
|
||
je xloop_done ; no, jump
|
||
xesc_loop:
|
||
les si,dword ptr [bp].arg_ptr ;es:si => argument
|
||
inc [bp].saved_si ;bump for next time thru
|
||
xor bh,bh
|
||
mov bl,byte ptr es:[si] ;pick up arg
|
||
lea bx,regs[bx] ;BX is VM reg @
|
||
mov si,[bx].C_page ;get its page#
|
||
mov si,ptype[si] ; and type
|
||
push si ;save around following
|
||
;move type info to buffer
|
||
MOVE_ARGS_TO_BUF si,REAL_MODE_BUFFER,autoincr
|
||
; Dispatch on argument type
|
||
pop si ;restore type #
|
||
call cs:word ptr do_arg[si] ;process argument (by type)
|
||
dec [bp].arg_count ;any more args left
|
||
jnz xesc_loop ; yes, loop
|
||
xloop_done:
|
||
RESET_REAL_BUFFER_OFFSET ;reset buffer ptr for later
|
||
;
|
||
; Now issue the RPC call, real routine knows where the buffer is
|
||
;
|
||
push 0 ;dummy word
|
||
push RPCXESC ;RPC REQUEST CODE
|
||
xesc_57:
|
||
mov dx,sp ;DS:DX = transaction buffer
|
||
mov cx,4
|
||
mov bx,cx ;DX = length of result
|
||
mov al,rpc_handle
|
||
mov ah,RPC ;Issue RPC
|
||
int DOS ;Extended Dos func
|
||
pop ax ;get return status
|
||
mov sp,bp ;dump args off stack
|
||
or ax,ax ;error during xesc call?
|
||
je normal ; no, continue
|
||
cmp ax,XLI_ERR_NO_SUCH_NAME ;calling an unknown xli func?
|
||
jne xesc_null_err_exit ; no, return error
|
||
mov bx,[bp].rvreg ;load bx with name requested
|
||
jmp xesc_err_exit ;and return with error
|
||
|
||
; We're back with a return value--unless it's a special service call.
|
||
; At this point, ES:DI should point to buffer.
|
||
normal: cld
|
||
|
||
mov si,sp ;store data on stack (ds:si)
|
||
les di,dword ptr REAL_MODE_BUFFER ;address real buffer (es:di)
|
||
mov cx,result_buf_len ;cx = length
|
||
MOVE_FROM_REAL_BUF ;move return data to local stack
|
||
|
||
mov ax,[bp].xesc_status ;get return status
|
||
or ax,ax ;Check status
|
||
jl ssr ; <0 = SSR
|
||
; 0 = normal return
|
||
mov di,[bp].xesc_vtype ;get return value type
|
||
cmp di,N_RV*2 ;out of range?
|
||
jb xesc_70 ; no, jump
|
||
cmp di,RV_ERR*2 ;xli program error?
|
||
jne xesc_65 ; no, jump
|
||
mov si,bp ;
|
||
add si,xesc_value ;DS:SI => return value
|
||
mov bx,[bp].rvreg ;bx = return reg address
|
||
call do_strval ;go get the error message
|
||
mov ax,XLI_ERR_EXTERNAL_ERROR ;ax=error indication
|
||
mov bx,[bp].rvreg ;bx = return reg address
|
||
jmp xesc_err_exit ;bx=message
|
||
xesc_65:
|
||
mov ax,XLI_ERR_VALUE_BAD_TYPE ;unkown return type
|
||
jmp xesc_null_err_exit ;return error
|
||
xesc_70:
|
||
mov si,bp
|
||
add si,xesc_value ;DS:SI => return value
|
||
mov bx,[bp].rvreg ;bx = return reg address
|
||
call cs:word ptr do_val[di] ;process return value
|
||
mov ax,0 ;AX=0 says no errors
|
||
|
||
xesc_null_err_exit:
|
||
lea bx,nil_reg ;"nil irritant" for some errors
|
||
; ax = error indicator (0 = no error), bx=irritant
|
||
xesc_err_exit label near
|
||
mov cx,mem_entry ;any entries in mem_table?
|
||
jcxz xesc_ex10 ;no, jump
|
||
push ax ;tempsave error indicators
|
||
push bx
|
||
xor bx,bx
|
||
mov mem_entry,bx ;see if any real mode segments
|
||
xesc_ex05:
|
||
mov es,mem_table[bx] ;get entry in mem_table
|
||
mov ax,DELETE_SEG ;delete the real mode segment
|
||
int dos
|
||
jnc xesc_ex07
|
||
mov bx,offset dl_seg
|
||
mov cx,offset xli_txt
|
||
jmp fatal_pro_err ;control will not return here
|
||
xesc_ex07:
|
||
inc bx
|
||
inc bx ;address next entry
|
||
loop xesc_ex05 ;go release next one
|
||
pop bx ;restore error indicators
|
||
pop ax
|
||
; at this point, ax = error number, bx = irritant (if error)
|
||
xesc_ex10:
|
||
mov sp,rpc_saved_sp ;clean up stack
|
||
add sp,rvreg+2
|
||
pop bp ;restore callers bp
|
||
ret ;return
|
||
; SSR
|
||
; A real procedure has issued a System Service Request (SSR). Currently,
|
||
; this means to pass a string to the real procedure. The result buffer
|
||
; indicates the argument from the %xesc call requested (0 based), the
|
||
; length of the string, and the real mode segment/offset to place the
|
||
; string. This routine copies the data into the real routine's address
|
||
; space, and returns.
|
||
;
|
||
|
||
ssr label near
|
||
mov si,[bp].first_arg ;arg list pointer
|
||
add si,[bp].ssr_argnum ;now address arg desired
|
||
mov es,[bp].saved_es ;ES:SI addresses the arg
|
||
mov bl,byte ptr es:[si] ;get reg #
|
||
xor bh,bh
|
||
lea bx,regs[bx] ;BX is reg@
|
||
|
||
mov si,[bx].C_disp ;si = string object offset
|
||
mov bx,[bx].C_page ;bx = string object page #
|
||
%LoadPage es,bx ;es:si => string object
|
||
inc si ;skip over tag
|
||
cld
|
||
lods word ptr es:[si] ;get string's length
|
||
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
|
||
;
|
||
; es:si => string, ax = length
|
||
;
|
||
mov dx,[bp].ssr_len ;get length of dest string
|
||
or dx,dx ;if non-zero
|
||
jnz ss_10 ; then jump
|
||
;
|
||
; A length of zero indicates that the xli routine wants to address far
|
||
; strings. Allocate real memory and put the real segment address into
|
||
; the transaction buffer. PRO2REAL will move the string to real memory.
|
||
; The real memory selector is saved in mem_table, and released when we
|
||
; exit this xesc call.
|
||
;
|
||
push ax ;save length
|
||
push si
|
||
push es ;save ptr to string
|
||
xor cx,cx
|
||
mov dx,ax ;cx:dx = string length
|
||
mov ax,ALLOC_REAL ;Allocate real segment
|
||
int dos ;Allocate real segment
|
||
jnc ss_07
|
||
mov bx,offset al_seg
|
||
mov cx,offset xli_txt
|
||
jmp fatal_pro_err ;control will not return here
|
||
ss_07:
|
||
; ax=selector, bx=para address
|
||
push ax ;tempsave selector
|
||
les di,dword ptr REAL_MODE_BUFFER
|
||
add di,ssr_seg ;address of real buffer (es:di)
|
||
MOVE_ARGS_TO_BUF bx ;save segment to real mode
|
||
mov dx,cx ;dx = length
|
||
pop ax ;restore selector
|
||
; save real memory selector in table
|
||
mov bx,mem_entry ;get entry number
|
||
inc mem_entry ;bump number of entries
|
||
shl bx,1 ;index into memory table
|
||
mov mem_table[bx],ax ;save selector there
|
||
|
||
pop es
|
||
pop di ;es:di => string to copy
|
||
pop dx ;restore length
|
||
jmp ss_25
|
||
; We have a string length here, set ds:si to point to the real memory
|
||
; address. PRO2REAL will create a real window over this area, and copy
|
||
; the string to it.
|
||
ss_10:
|
||
cmp ax,dx ;string len >= buffer len?
|
||
jae ss_20 ;yes, jump
|
||
mov dx,ax ;dx = #chars to copy
|
||
ss_20:
|
||
mov di,si ;es:di = string to copy
|
||
mov si,bp
|
||
add si,ssr_offset ;ds:si => real memory address
|
||
xor ax,ax ;use ds:si to map address
|
||
ss_25:
|
||
call pro2real ;copy to real memory
|
||
|
||
push cx
|
||
push RPCXLISSR
|
||
jmp xesc_57
|
||
|
||
xesc endp
|
||
|
||
|
||
; SOFTINT
|
||
; Handler for the "software interrupt"
|
||
;
|
||
; Use:
|
||
; call SOFTINT 7,op,intnum,return-type,ax,bx,cx,dx
|
||
; where all arguments are pcs registers
|
||
;
|
||
; On exit:
|
||
; The first register will contain the returned value
|
||
;
|
||
; Description:
|
||
; All args are interrogated to determine the length of a buffer
|
||
; required to hold the args. A buffer is allocated in real mode
|
||
; (via function E8), the args are then copied into the buffer,
|
||
; and the software interrupt is issued. Upon return, the return
|
||
; value is processed, the buffer is deallocated, and the first
|
||
; register is set with the return value.
|
||
|
||
;
|
||
; This following data will be allocated locally within SWINT
|
||
;
|
||
local_save struc
|
||
; Following is the machine state block for Issue Real Interrupt request
|
||
msb_ax dw ? ; ax register for interrupt
|
||
msb_bx dw ? ; bx register for interrupt
|
||
msb_cx dw ? ; cx register for interrupt
|
||
msb_dx dw ? ; dx register for interrupt
|
||
msb_si dw ? ; si register for interrupt
|
||
msb_di dw ? ; di register for interrupt
|
||
msb_flags dw ? ; flags register for interrupt
|
||
msb_ds dw ? ; ds register for interrupt
|
||
msb_es dw ? ; es register for interrupt
|
||
; The following local data contains ptrs into the real segment
|
||
selector dw ? ; selector for real segment
|
||
buf_ptr dw ? ; local pointer into real segment
|
||
msb_ptr dw ? ; local pointer into msb
|
||
stop dw ? ; temp data
|
||
work_spc dd ? ; temp working storage
|
||
; Following definitions define the stack upon call
|
||
caller_bp dw ? ; callers bp
|
||
farret dd ? ; far return address
|
||
dummy dw ? ; %esc first arg = # operands
|
||
arg4 dw ? ; arg4 = dx
|
||
arg3 dw ? ; arg4 = cx
|
||
arg2 dw ? ; arg4 = bx
|
||
arg1 dw ? ; arg4 = ax
|
||
ret_type dw ? ; return type
|
||
intnum dw ? ; interrupt number
|
||
op dw ? ; op-code
|
||
local_save ends
|
||
|
||
softint proc far
|
||
push bp ;save callers bp
|
||
sub sp,caller_bp ;allocate local storage
|
||
mov bp,sp ;and update BP
|
||
|
||
and xesc_func,0 ;note sw-int
|
||
lea bx,swi_err_exit ;error handler for sw-int
|
||
mov error_return,bx
|
||
|
||
; Sum up the space required to hold all the arguments
|
||
|
||
mov si,bp
|
||
add si,arg4-2 ;SI => args
|
||
mov [bp].stop,si ;save for later
|
||
mov di,bp
|
||
add di,msb_dx ;DI => regs in msb
|
||
mov cx,4 ;CX = number of args
|
||
xor dx,dx ;DX = space required
|
||
sum_spc:
|
||
push di ;temp save di
|
||
add si,2 ;address arg
|
||
mov bx,[si] ;get vm reg
|
||
mov di,[bx].C_page ;get its page#
|
||
cmp ptype[di],STRTYPE*2 ;Is it a string?
|
||
jne sum_010 ; no, jump
|
||
%LoadPage es,di ; yes,
|
||
mov di,[bx].C_disp ; es:di => string
|
||
inc di ; skip tag
|
||
mov ax,es:[di] ; get string object length
|
||
cmp ax,0 ; is it positive?
|
||
jge sum_005 ; yes, jump; normal string
|
||
add ax,str_ovhd*2 ; no, short string
|
||
sum_005: sub ax,str_ovhd ; subtract overhead
|
||
inc ax ; add 1 for null terminator
|
||
jmp short sum_020
|
||
sum_010:
|
||
mov ax,4 ;non-string at least 4 bytes
|
||
cmp ptype[di],FLOTYPE*2 ;floating point object?
|
||
jne sum_020 ; no, jump
|
||
add ax,4 ; yes, floats are 8 bytes
|
||
sum_020:
|
||
pop di ;msb register ptr
|
||
mov ds:[di],ax ; save length of object
|
||
sub di,2 ; next msb register ptr
|
||
add dx,ax ;sum space required
|
||
loop sum_spc ;and loop
|
||
|
||
; CX:DX = space required to buffer the args, SI => arg 1 at this point
|
||
|
||
mov ax,ALLOC_REAL ;Create real segment
|
||
int DOS ;Extended Dos Function request
|
||
jnc swi_07
|
||
mov bx,offset al_seg
|
||
mov cx,offset swi_txt
|
||
jmp fatal_pro_err ;control will not return here
|
||
swi_07:
|
||
mov [bp].selector,ax ;save segment selector
|
||
mov es,ax ;es = real buffer selector
|
||
mov [bp].msb_ds,bx ;save para address in msb
|
||
mov [bp].msb_es,bx ;save para address in msb
|
||
mov [bp].buf_ptr,0 ;pointer within real segment
|
||
mov [bp].msb_ptr,bp ;pointer into msb regs
|
||
|
||
; Move each arg into the buffer, SI => arg1 at this point
|
||
;
|
||
swi_020:
|
||
cmp si,[bp].stop ;all args processed?
|
||
je swi_025 ; yes, jump
|
||
|
||
std
|
||
lods word ptr [si] ;pick up arg
|
||
mov bx,ax ;save in BX
|
||
|
||
mov di,[bp].msb_ptr ;di = ptr to reg in msb
|
||
add [bp].msb_ptr,2 ; set for next time
|
||
mov cx,ds:[di] ;cx = length of object
|
||
mov ax,[bp].buf_ptr ;ax = ptr into buffer
|
||
add [bp].buf_ptr,cx ; set for next time
|
||
mov ds:[di],ax ;update msb reg with buf ptr
|
||
mov di,ax ;es:di => buffer
|
||
|
||
; Dispatch on argument type
|
||
push si ;tempsave arg ptr
|
||
mov si,[bx].C_page ;get page#
|
||
mov si,ptype[si] ; and type
|
||
; BX=page #, CX=length, ES:DI=>buffer
|
||
call cs:word ptr do_arg[si] ;Handle each object.
|
||
pop si ;restore arg ptr
|
||
jmp swi_020
|
||
|
||
; At this time all args are in the buffer, Issue the sofware interrupt
|
||
|
||
swi_025:
|
||
cld
|
||
mov bx,[bp].intnum ;get reg holding int
|
||
mov ax,[bx].C_disp ;AL = interrupt number
|
||
mov dx,bp ;DS:DX => machine state block
|
||
mov bx,msb_es+2 ;# bytes which may change
|
||
mov ah,REAL_INTRP ;AH = Issue Real Interrupt
|
||
int DOS ;Extended Dos Function Request
|
||
jnc swi_27
|
||
mov bx,offset rl_int
|
||
mov cx,offset swi_txt
|
||
jmp fatal_pro_err ;control will not return here
|
||
swi_27:
|
||
; We're back from software interrupt, lets get return value
|
||
|
||
mov bx,[bp].ret_type ;get vm reg
|
||
mov di,[bx].C_disp
|
||
shl di,1 ;make index into valu table
|
||
cmp di,N_RV*2 ;return value out of range?
|
||
jb swi_070
|
||
;bx = reg holding return type
|
||
mov ax,SWI_ERR_VALUE_BAD_TYPE ;ax = error indicator
|
||
jmp swi_err_exit
|
||
swi_070:
|
||
; now go convert the return values
|
||
mov si,bp ;ds:si => address of ret value
|
||
mov bx,[bp].op ;bx = return register
|
||
call cs:word ptr do_val[di] ;handle one type of return value
|
||
mov ax,0 ;AX=0 says no errors
|
||
; ax= error indicator (if nonzero, bx = irritant)
|
||
swi_err_exit label near
|
||
push ax ;push error number
|
||
push bx ;push irritant
|
||
mov es,[bp].selector
|
||
mov ax,DELETE_SEG ;Delete Real Segment
|
||
int DOS ;Extended Dos Function
|
||
jnc swi_077
|
||
mov bx,offset dl_seg
|
||
mov cx,offset swi_txt
|
||
jmp fatal_pro_err ;control will not return here
|
||
swi_077:
|
||
pop cx ;cx = irritant
|
||
pop ax ;ax = error indication
|
||
mov bx,ax ; move to bx
|
||
dec bx ; form index
|
||
js swi_ret ;negative - no error
|
||
shl bx,1 ;form index
|
||
mov bx,swi_errs[bx] ;bx => error message
|
||
mov ax,1 ;note non-restartable
|
||
; ax= error indicator (if nonzero bx=message address, cx = irritant)
|
||
swi_ret:
|
||
mov sp,bp
|
||
add sp,caller_bp
|
||
pop bp
|
||
ret
|
||
softint endp
|
||
|
||
|
||
subttl Code segment: Copy arguments to xfer buffer
|
||
page
|
||
|
||
|
||
;; Jump tables to handle arguments to the %xesc call
|
||
; 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
|
||
|
||
; On entry to all the argument handler routines:
|
||
; ES:DI = pointer to real mode buffer to store data
|
||
; BX = address of VM reg with page:offset of Scheme object
|
||
; SI = Type of operand code
|
||
;
|
||
; On exit:
|
||
; CX = number of bytes moved to the buffer pointed to by ES:DI
|
||
|
||
;
|
||
; Process list argument
|
||
;
|
||
do_lstarg label near ;looking for false only
|
||
cmp [bx].C_page,NIL_PAGE*2
|
||
je do_lst01
|
||
jmp do_errarg
|
||
do_lst01:
|
||
xor ax,ax
|
||
jmp do_log
|
||
|
||
;
|
||
; Process fixnum argument
|
||
;
|
||
do_fixarg label near
|
||
mov ax,[bx].C_disp ;get the fixnum data
|
||
shl ax,1 ;deal with sign bit
|
||
sar ax,1 ;ax = 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
|
||
MOVE_ARGS_TO_BUF <dx,ax>,,autoincr,save
|
||
ret ;and return
|
||
;
|
||
; Process float argument
|
||
;
|
||
do_floarg label near
|
||
push ds ;preserve data seg
|
||
|
||
mov si,[bx].C_page ;get float's page #
|
||
mov ax,[bx].C_disp ; and offset
|
||
%LoadPage ds,si
|
||
mov si,ax ;ds:si => float
|
||
inc si ;bump past header
|
||
mov cx,8 ;cx = length of float
|
||
MOVE_TO_REAL_BUF autoincr,save ;move float to buffer
|
||
pop ds ;restore data seg
|
||
ret ;and return
|
||
|
||
;
|
||
; Process bignum argument
|
||
;
|
||
do_bigarg label near
|
||
; Stage the conversion to longint on the stack
|
||
sub sp,4 ;allocate stack space for long
|
||
mov ax,sp ;note its address
|
||
; ok to add to stack here because we've reserved space above.
|
||
push es ;save regs around call
|
||
push di
|
||
push bp
|
||
mov bp,sp
|
||
push bx ;push VM reg@
|
||
push ax ;push buffer@
|
||
mov C_fn,offset pgroup:int2long ;convert bignum to long
|
||
call far ptr far_C
|
||
pop bx ;dump buffer@
|
||
pop bx ;restore VM reg@
|
||
pop bp ;restore bp
|
||
pop di ; di
|
||
pop es ; es
|
||
; above cleans stack up from calling C routine
|
||
cmp ax,0 ;did bignum convert OK?
|
||
je do_big5 ;yes, jump
|
||
; there was an error in converting the number
|
||
mov ax,XLI_ERR_BIG_TO_32_BITS ;ax = error # (default xli)
|
||
cmp xesc_func,0 ;performing xli function?
|
||
jne do_bigerr ; yes, jump
|
||
mov ax,SWI_ERR_BIG_TO_32_BITS ;ax = error # (for sw-int)
|
||
; ax=error number, bx=irritant
|
||
do_bigerr:
|
||
jmp error_return
|
||
do_big5:
|
||
mov si,sp ;ds:si => long int
|
||
mov cx,8 ;cx = length
|
||
MOVE_TO_REAL_BUF autoincr,save ;move float to buffer
|
||
add sp,4 ;clean up stack
|
||
ret ;and return
|
||
|
||
;
|
||
; Process symbol argument
|
||
;
|
||
do_symarg label near ;looking for true only
|
||
cmp [bx].C_page,T_PAGE*2
|
||
jne do_errarg
|
||
cmp [bx].C_disp,T_DISP
|
||
jne do_errarg
|
||
mov ax,1
|
||
jmp do_log
|
||
|
||
;
|
||
; Process string arguments
|
||
;
|
||
do_strarg label near
|
||
or xesc_func,0 ;doing xesc?
|
||
jz swi_strarg ; no, jump
|
||
MOVE_ARGS_TO_BUF <-1>,,autoincr,save ; yes, indicate string
|
||
ret
|
||
swi_strarg: ;move string to swint buffer
|
||
push ds ;preserve regs
|
||
push si
|
||
mov ax,[bx].C_disp ;get offset
|
||
mov si,[bx].C_page ;get page #
|
||
%LoadPage ds,si
|
||
mov si,ax ;ds:si => string
|
||
inc si ;skip tag
|
||
cld
|
||
lods word ptr [si] ;get length
|
||
or ax,ax ;is it positive?
|
||
jge swi_str05 ;yes, jump; normal string
|
||
add ax,str_ovhd*2 ;no, short string
|
||
swi_str05:
|
||
sub ax,str_ovhd ;subtract overhead
|
||
mov cx,ax ;CX = length of string
|
||
MOVE_TO_REAL_BUF autoincr ;move string across
|
||
mov ax,ss
|
||
mov ds,ax
|
||
push cx ;save # bytes just written
|
||
MOVE_BYTE_TO_BUF 0,,autoincr ;write out null terminator
|
||
pop cx
|
||
inc cx ;cx = total # bytes written
|
||
pop si ;restore preserved regs
|
||
pop ds
|
||
ret
|
||
|
||
do_errarg label near
|
||
mov ax,XLI_ERR_ARGN_BAD_TYPE ;ax = error # (default xli)
|
||
cmp xesc_func,0 ;performing xli function?
|
||
jne do_errerr ; yes, jump
|
||
mov ax,SWI_ERR_ARGN_BAD_TYPE ;ax = error # (for sw-int)
|
||
; ax = error number, bx=irritant
|
||
do_errerr:
|
||
jmp error_return
|
||
|
||
|
||
subttl Code segment: Copy return value back into Scheme
|
||
page
|
||
|
||
;; Jump tables to handle values returned from the real routine
|
||
; 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
|
||
|
||
|
||
; On entry to all the value handler routines:
|
||
; BX = result register address
|
||
; DS:SI = pointer to return value
|
||
|
||
;
|
||
; Process integer return value
|
||
;
|
||
do_intval proc near
|
||
do_int10:
|
||
push bp
|
||
mov bp,sp ;get BP set for C call
|
||
or xesc_func,0 ;doing xesc?
|
||
jnz doint_05 ; yes, jump
|
||
push [si] ;si=> msb_ax on stack. remember
|
||
push [si]+2 ;lattice's return conventions
|
||
jmp doint_07
|
||
doint_05: push [si]+2 ;push longint
|
||
push [si]
|
||
doint_07: push bx ;push vm reg address
|
||
mov C_fn,offset pgroup:long2int ;allocate integer
|
||
call far ptr far_C ;C longint -> PCS integer
|
||
;(bignum or fixnum)
|
||
mov sp,bp ;pop C args
|
||
pop bp ;restore callers bp
|
||
ret ; and return
|
||
do_intval endp
|
||
|
||
;
|
||
; Process true/false return value
|
||
;
|
||
do_TFval proc near
|
||
mov cx,0
|
||
or xesc_func,0 ;doing xesc?
|
||
jnz dotf_05 ; yes, jump
|
||
mov ax,[si]+2 ;si=> msb_ax on stack. remember
|
||
jmp dotf_07 ;lattice's return convention
|
||
dotf_05: mov ax,[si] ;get value
|
||
dotf_07: or ax,ax ;zero?
|
||
jz do_TF10 ; yes (false object)
|
||
mov ax,T_DISP ; no (true object)
|
||
mov cx,T_PAGE*2
|
||
do_TF10:
|
||
mov [bx].C_disp,ax
|
||
mov [bx].C_page,cx
|
||
ret
|
||
do_TFval endp
|
||
|
||
;
|
||
; Process float return value
|
||
;
|
||
do_floval proc near
|
||
push bp
|
||
mov bp,sp
|
||
or xesc_func,0 ;doing xesc?
|
||
jnz doflo_05 ; yes, jump
|
||
push [si] ;si=> msb_ax on stack. remember
|
||
push [si]+2 ;lattice's return conventions
|
||
push [si]+4 ;and push args appropriately.
|
||
push [si]+6
|
||
jmp doflo_07
|
||
doflo_05: push [si]+6 ;push float values
|
||
push [si]+4
|
||
push [si]+2
|
||
push [si]
|
||
doflo_07: push bx ;push vm return reg
|
||
mov C_fn,offset pgroup:alloc_fl ;allocate float
|
||
call far ptr far_C ;C double -> PCS flonum
|
||
mov sp,bp ;pop args from stack
|
||
pop bp
|
||
ret
|
||
do_floval endp
|
||
|
||
;
|
||
; Process string return values
|
||
;
|
||
do_strval proc near
|
||
or xesc_func,0 ;doing xesc?
|
||
jz swi_strval ; no, jump
|
||
;
|
||
; Do it for xli
|
||
;
|
||
push bp
|
||
mov bp,sp
|
||
|
||
mov cx,[si] ;get string length
|
||
cmp cx,16380 ;string length short enough?
|
||
jbe do_stv15 ;yes, jump
|
||
mov cx,16380 ;no, truncate at max
|
||
do_stv15:
|
||
; allocate the space for the return value string object
|
||
push cx ;save length for later
|
||
push si ; pointer to buffer
|
||
push bx ; return value VM reg
|
||
push bp
|
||
mov bp,sp ;get BP set for C call
|
||
push cx ;push length
|
||
push STRTYPE ;push type
|
||
push bx ;push return value VM reg @
|
||
mov C_fn,offset pgroup:alloc_bl ;allocate block
|
||
call far ptr far_C ;go do it
|
||
mov sp,bp ;pop C args
|
||
pop bp
|
||
pop bx ;return VM reg
|
||
mov di,[bx].C_disp
|
||
mov bx,[bx].C_page
|
||
%LoadPage es,bx
|
||
add di,3 ;es:si => destination
|
||
pop si
|
||
add si,2 ;ds:si => real mode address
|
||
pop dx ;dx = length
|
||
call real2pro ;xfer from real mem to pro mem
|
||
mov sp,bp ;clean up stack
|
||
pop bp ;restore caller's bp
|
||
ret ;and return
|
||
;
|
||
; Do it for software interrupt
|
||
;
|
||
swi_strval:
|
||
|
||
push ds ;tempsave ds
|
||
mov si,[bp].msb_ax
|
||
mov ds,[bp].selector ;DS:SI points to string
|
||
|
||
push ss
|
||
pop es
|
||
mov di,bp
|
||
add di,work_spc ;ES:DI => destination
|
||
|
||
mov ax,BLOCK_XFER ;grab one byte and test zero
|
||
mov cx,1
|
||
mov dx,0FFFFh
|
||
swi_str01:
|
||
inc dx ;# bytes read
|
||
int DOS ;xfer 1 byte
|
||
inc si ;next byte to read
|
||
cmp byte ptr es:[di],0 ;is it zero?
|
||
jne swi_str01 ;no, get next char
|
||
swi_stv15:
|
||
pop ds ;restore ds
|
||
push dx ;save length for later
|
||
;
|
||
; allocate the space for the return value string object
|
||
;
|
||
mov ax,[bp].op ;get return vm reg
|
||
|
||
push bp ;tempsave around call
|
||
mov bp,sp ;get BP set for C call
|
||
push dx ;push length
|
||
push STRTYPE
|
||
push ax ;push vm reg
|
||
mov 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 bp
|
||
|
||
mov bx,[bp].op ;return value VM reg
|
||
mov di,[bx].C_disp
|
||
mov bx,[bx].C_page
|
||
%LoadPage es,bx ;ES:DI is dest object @
|
||
add di,3 ;skip past string's overhead
|
||
|
||
mov si,[bp].msb_ax
|
||
mov ds,[bp].selector ;DS:SI is string in buffer
|
||
pop cx ;CX = length
|
||
mov ax,BLOCK_XFER ;copy into scheme heap
|
||
int DOS ;Extended Dos function call
|
||
|
||
mov ax,ss
|
||
mov ds,ax
|
||
ret
|
||
do_strval endp
|
||
|
||
|
||
do_errval proc near
|
||
mov ax,XLI_ERR_VALUE_BAD_TYPE
|
||
jmp error_return
|
||
do_errval endp
|
||
|
||
|
||
public pro2real,real2pro,map_real_mem
|
||
; REAL2PRO
|
||
;
|
||
; On entry:
|
||
; DS:SI => address of real mode buffer
|
||
; ES:DI => scheme heap
|
||
; DX = length
|
||
;
|
||
; On exit:
|
||
; CX is number of chars xfered
|
||
|
||
real2pro proc near
|
||
push ds ; save data segment
|
||
call map_real_mem ; create real window (selector in ax)
|
||
; Error Checks here
|
||
mov cx,dx ; cx = length
|
||
; WARNING: DS addresses real memory below
|
||
mov ds,ax ; real mode selector
|
||
xor si,si ; ds:si = source (real data)
|
||
mov ax,BLOCK_XFER ; do block xfer
|
||
int DOS
|
||
mov ax,ds
|
||
mov es,ax ; es = mapped selector
|
||
mov ax,DELETE_SEG ; Delete Segment
|
||
int DOS
|
||
jnc r2p_next
|
||
xor bx,bx
|
||
mov bl,ss:xesc_func
|
||
shl bx,1
|
||
mov cx,ss:which_func[bx]
|
||
mov bx,offset dl_seg
|
||
jmp fatal_pro_err ;control will not return here
|
||
r2p_next:
|
||
; WARNING: DS does not address scheme's data segment above
|
||
pop ds ; restore data segment
|
||
ret
|
||
real2pro endp
|
||
|
||
|
||
; PRO2REAL
|
||
; Copy data from protected mode memory to real mode memory. If ax is
|
||
; non-zero, then it already contains a real selector where we can move
|
||
; the data - in this case we don't create a real window and delete the
|
||
; segment selector after the copy.
|
||
;
|
||
; On entry:
|
||
; if AX = 0
|
||
; then DX = length
|
||
; DS:SI => address of real mode buffer
|
||
; ES:DI => scheme heap
|
||
; else
|
||
; AX = selector to real mode buffer
|
||
; DX = length
|
||
; ES:DI => scheme heap
|
||
;
|
||
; On exit:
|
||
; CX is number of chars xfered
|
||
|
||
pro2real proc near
|
||
push ds ; callers data segment
|
||
push ax ; indicator
|
||
push di ; offset to data
|
||
or ax,ax ; do we have a selector already?
|
||
jnz p2r_010 ; yes, don't create real window (jump)
|
||
call map_real_mem ; no, create real window
|
||
; selector returned in ax
|
||
; Error Checks here
|
||
p2r_010:
|
||
mov cx,dx ; cx = length
|
||
; WARNING: DS addresses scheme heap below
|
||
mov bx,es
|
||
mov ds,bx
|
||
pop si ; ds:si = source (in scheme heap)
|
||
|
||
mov es,ax ; real mode selector
|
||
xor di,di ; es:di = destination (in real mode)
|
||
mode_xfer:
|
||
mov dx,ax ; tempsave selector
|
||
mov ax,BLOCK_XFER ; do block xfer
|
||
int DOS
|
||
pop ax ; restore indicator
|
||
or ax,ax ; was a selector passed in?
|
||
jnz mode_xf01 ; yes, then don't delete it
|
||
mov es,dx ; es = mapped selector
|
||
mov ax,DELETE_SEG ; Delete Segment
|
||
int DOS
|
||
jnc mode_next
|
||
xor bx,bx
|
||
mov bl,ss:xesc_func
|
||
shl bx,1
|
||
mov cx,ss:which_func[bx]
|
||
mov bx,offset dl_seg
|
||
jmp fatal_pro_err ;control will not return here
|
||
mode_next:
|
||
; WARNING: DS does not address scheme's data segment above
|
||
mode_xf01:
|
||
mov ax,ds
|
||
mov es,ax ; restore ptr to scheme heap
|
||
pop ds ; restore data segment
|
||
ret
|
||
pro2real endp
|
||
|
||
; MAP_REAL_MEM
|
||
; Map a real memory address into a selector for use in protected memory.
|
||
;
|
||
; DS:SI => address of real mode buffer
|
||
; DX = length
|
||
;
|
||
; On exit:
|
||
; Carry flag set on error
|
||
; AX = selector for real memory or error if carry flag set
|
||
;
|
||
; Regs used: ax,bx,cx,si - all destroyed
|
||
|
||
map_real_mem proc near
|
||
; create real mode window
|
||
xor ax,ax
|
||
mov cx,4 ; shift count
|
||
mov bx,[si]+2 ; bx = real segment address
|
||
mov al,bh ; create 32 bit address in SI:BX
|
||
shr ax,cl
|
||
shl bx,cl ; shift for physical mem calc
|
||
add bx,[si] ; add effective memory address
|
||
jnc mr_25
|
||
inc ax ; SI:BX = real memory address
|
||
mr_25:
|
||
mov si,ax ; si:bx = real memory address
|
||
xor cx,cx ; CX:DX = length
|
||
mov ax,CREATE_WIN ; Create Window function request
|
||
int DOS ; Return selector in AX
|
||
jnc mr_ret
|
||
xor bx,bx
|
||
mov bl,ss:xesc_func
|
||
shl bx,1
|
||
mov cx,ss:which_func[bx]
|
||
mov bx,offset cr_win
|
||
jmp fatal_pro_err ;control will not return here
|
||
mr_ret:
|
||
ret
|
||
map_real_mem endp
|
||
|
||
progx ends
|
||
|
||
|
||
|
||
subttl Prog segment code definitions
|
||
page
|
||
|
||
prog segment byte public 'PROG'
|
||
assume cs:pgroup
|
||
extrn next_SP:near,src_err:near
|
||
extrn fix_intr:near
|
||
public pcinit,set_crtint,reset_crtint,xli_ldall,xli_term,xli_xesc
|
||
|
||
; PC_INIT
|
||
; Perform initializations, some of which are PC specific.
|
||
;
|
||
pcinit proc near
|
||
call bid_rpc ;bid the real mode code
|
||
cmp pcs_sysd,0 ;have we found the system directory?
|
||
jz pcini_00 ; no, skip loading of xli
|
||
call xli_ldall ; yes, load xli stuff
|
||
pcini_00:
|
||
call pc_init ;get specific pc info
|
||
call fix_intr ;take over interrupts
|
||
ret ;return to caller
|
||
pcinit endp
|
||
|
||
|
||
; The following routines are gateways to routines in the progx segment
|
||
; for real procedure calls (RPC) and external language interface (XLI).
|
||
; Note that the progx routines are jumped to via the FAR_RPC table, however
|
||
; they return to the caller of this routine because we fix up the stack.
|
||
;
|
||
bid_rpc proc near
|
||
mov bx,frpc_bid ;initialize real procedure
|
||
jmp rpc_call
|
||
pc_init:
|
||
mov bx,frpc_init ;get machine type
|
||
jmp rpc_call
|
||
set_crtint:
|
||
mov bx,frpc_setcrt ;set crt interrupt
|
||
jmp rpc_call
|
||
reset_crtint:
|
||
mov bx,frpc_resetcrt ;reset crt interrupt
|
||
jmp rpc_call
|
||
xli_ldall:
|
||
mov bx,frpc_ldall ;load xli files
|
||
jmp rpc_call
|
||
xli_term:
|
||
mov bx,frpc_unld ;unload xli files
|
||
jmp rpc_call
|
||
xli_xesc:
|
||
mov bx,frpc_xesc ;perform xesc
|
||
jmp rpc_call
|
||
rpc_call:
|
||
pop dx ;pop return address
|
||
push prog ;push segment return
|
||
push dx ;then offset
|
||
jmp dword ptr FAR_RPC+[bx] ;jump to progx routine
|
||
ret ;we'll never return here
|
||
bid_rpc 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.
|
||
public far_C
|
||
far_C proc far
|
||
push ds ;C likes ES=DS
|
||
pop es
|
||
pop C_retadr ;get far @ off stack so C sees its args
|
||
pop C_retadr+2
|
||
call [C_fn]
|
||
push C_retadr+2
|
||
push C_retadr
|
||
ret ;C returns with return value in AX..DX
|
||
far_C endp
|
||
|
||
prog ends
|
||
|
||
end
|
||
|