; =====> 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 ,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 ;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 ,,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