pcs/svars.asm

958 lines
40 KiB
NASM
Raw Normal View History

2023-05-20 05:57:06 -04:00
; =====> SVARS.ASM
;****************************************
;* TIPC Scheme '84 Runtime Support *
;* Interpreter -- Variable Operations *
;* *
;* (C) Copyright 1984, 1985, 1988 *
;* Texas Instruments Incorporated. *
;* All rights reserved. *
;* *
;* Date Written: 24 July 1984 *
;* Modification History: *
;* ?? 10/22/85 - ?? *
;* rb 2/ 5/88 - MEMV, ASSV use EQV's *
;* definition of number equality *
;* (which is "=", *not* "equal"). *
;* *
;****************************************
include scheme.equ
include sinterp.mac
include sinterp.arg
DGROUP group data
data segment word public 'DATA'
assume DS:DGROUP
m_fluid db "LD-FLUID",0
m_setfl db "SET-FLUID!",0
m_set_gl db "SET!-GLOBAL",0
m_fl_p db "FLUID-BOUND?",0
m_ve_al db "MAKE-VECTOR",0
m_vec_s db "VECTOR-SIZE",0
m_vecf db "VECTOR-FILL!",0
m_mkvt_a dw m_ve_al ; address of "MAKE-VECTOR"
m_one dw 1 ; a constant "one" (1)
m_three dw 3 ; a constant "three" (3)
m_toobig dw VECTOR_SIZE_LIMIT_ERROR ; numeric error code
data ends
PGROUP group prog
prog segment byte public 'PROG'
assume CS:PGROUP
var_int proc near
; Entry points defined in "sinterp.asm"
extrn next:near ; Top of interpreter
extrn next_PC:near ; Reload ES,SI at top of interpreter
extrn next_SP:near ; mov SP,BP before next_PC
extrn src_err:near ; "source operand error" message display
extrn sch_err:near ; Link to Scheme debugger
;************************************************************************
;* Macro support for global/fluid variable lookup *
;************************************************************************
load macro environ,err_msg,reg_p
local x,y
lods word ptr ES:[SI] ; load dest reg, constant number
save <SI> ; save current location pointer
mov BL,AL ; copy destination register number
mov DI,BX ; into TIPC register DI
mov BL,AH ; isolate constant number
IFIDN <reg_p>,<REG>
mov SI,reg0_pag+[BX] ; load page number from symbol operand reg
mov AX,reg0_dis+[BX] ; likewise for the displacement
ELSE
mov AX,BX ; BX <- constant number * 3
shl AX,1
add BX,AX
add BX,CB_dis ; add offset for start of code block
xor AX,AX
mov AL,ES:[BX].cod_cpag ; load symbol's page number
mov SI,AX
mov AX,ES:[BX].cod_cdis ; load symbol's displacement
ENDIF
cmp byte ptr ptype+[SI],SYMTYPE*2 ; reg hold a symbol pointer?
jne y ; if not, jump to error handler
push DI ; save register number
mov DX,SI ; copy symbol's page number into DX
mov DI,environ&_pag ; load fluid environment pointer
mov SI,environ&_dis
;;; LoadPage ES,DI
;;; mov ES,pagetabl+[DI] ; load paragraph address for env. header
mov BX,DI ; BX <= page number
call lookup ; search the environment for symbol
cmp BX,0 ; symbol found?
pop BX ; restore register number
je x ; if symbol not found, jump
mov AX,ES:[DI].cdr ; load symbol's value pointer
mov reg0_dis+[BX],AX ; and store into register
mov AL,ES:[DI].cdr_page
mov byte ptr reg0_pag+[BX],AL
jmp next_PC
; symbol not found-- return '***unbound***
x: mov CX,offset environ&_reg ; load address of environment reg
corrpage DX ; adjust page number for call to C routine
add BX,offset reg0 ; compute address of destintatin register
pushm <BX,CX,AX,DX> ; push page, displacement, env, dest reg
C_call sym_unde,,Load_ES ; call: symbol_undefined(pg,ds,env,dest)
;***x: mov reg0_dis+[BX],UN_DISP
;*** mov byte ptr reg0_pag+[BX],UN_PAGE*2
restore <SI> ; load next instruction's offset and
sub SI,3 ; back up PC to retry fluid load
jmp sch_err ; Link to Scheme debugger
; error-- register doesn't contain a symbol
y: lea BX,err_msg
jmp src_err ; display error message
endm
;************************************************************************
;* AL AH *
;* Fluid lookup FLUID dest,const *
;* *
;* Purpose: Interpreter support for fluid variable lookup *
;************************************************************************
public ld_fluid
ld_fluid: load FNV,m_fluid,CONST
;************************************************************************
;* AL AH *
;* Fluid lookup-register operand FLUID-R dest,sym *
;* *
;* Purpose: Interpreter support for fluid variable lookup *
;************************************************************************
public ld_fl_r
ld_fl_r: load FNV,m_fluid,REG
purge load
;************************************************************************
;* AL AH *
;* set-fluid! ST-FLUID src,const *
;* *
;* Purpose: Interpreter support for fluid assignment. *
;************************************************************************
public st_fluid
st_fluid: lods word ptr ES:[SI] ; load source reg and constant number
save <SI> ; save current value of location pointer
push AX ; save symbol/value register numbers
mov BL,AH
mov AX,BX ; BX <- constant number * 3
shl AX,1
add BX,AX
add BX,CB_dis ; add in starting offset of code block
xor AX,AX
mov AL,ES:[BX].cod_cpag ; load pointer to search symbol
mov DI,AX
cmp byte ptr ptype+[DI],SYMTYPE*2 ; really a symbol?
jne setfl_er ; if not, jump
mov DX,DI ; copy symbol's page number
mov AX,ES:[BX].cod_cdis ; load symbol's displacement
mov DI,FNV_pag ; load pointer to fluid environment
mov SI,FNV_dis
;;; LoadPage ES,DI
;;; mov ES,pagetabl+[DI]
mov BX,DI ; Page number
call lookup ; search fluid environment for symbol
cmp BX,0 ; symbol found in fluid environment?
je setfl_nf ; if not, error (jump)
pop AX ; restore operands
mov BL,AL ; copy source register number
mov AL,byte ptr reg0_pag+[BX] ; set cdr of fluid var entry
mov ES:[DI].cdr_page,AL ; to value in register
mov AX,reg0_dis+[BX]
mov ES:[DI].cdr,AX
jmp next_PC ; return to interpreter
; error-- symbol register doesn't contain a symbol pointer
setfl_er: mov BX,offset m_setfl ; load error message text
jmp src_err ; jump to "source error" routine
; error-- symbol not fluidly bound
setfl_nf: pop CX ; restore instruction's operands
xor CH,CH ; clear high order byte (constant number)
add CX,offset reg0 ; compute address of source register
corrpage DX ; convert page number to C's notation
pushm <CX,AX,DX> ; push arguments for error call
C_call not_flui,,Load_ES ; call error routine
restore <SI> ; back up location pointer to retry
sub SI,3 ; the set-fluid! operation
jmp sch_err ; link to Scheme debugger
; fluid-bound? FLUID? reg
public fluid_p
fluid_p: lods byte ptr ES:[SI] ; load the register number for test
save <SI> ; save the current location pointer
mov BX,AX ; copy register number of symbol
mov AX,reg0_dis+[BX]
mov DX,reg0_pag+[BX]
mov DI,DX
cmp byte ptr ptype+[DI],SYMTYPE*2 ; symbol pointer?
jne fl_p_er ; if not, error (jump)
mov DI,FNV_pag
mov SI,FNV_dis
;;; LoadPage ES,DI
;;; mov ES,pagetabl+[DI]
push BX
mov BX,DI ; Page number
call lookup
cmp BX,0
pop BX
je fl_p_nf
; symbol is fluidly bound-- return 't
mov AL,T_PAGE*2
mov byte ptr reg0_pag+[BX],AL
mov AX,T_DISP
mov reg0_dis+[BX],AX
jmp next_PC
; symbol not in fluid environment-- return 'nil
fl_p_nf: xor AX,AX
mov byte ptr reg0_pag+[BX],AL
mov reg0_dis+[BX],AX
jmp next_PC
; error-- operand of (fluid-bound? obj) is not a symbol
fl_p_er: lea BX,m_fl_p
jmp src_err ; display error message
;************************************************************************
;* AL AH *
;* Bind fluid variable BIND-FL const,src *
;* *
;* Purpose: Interpreter support for binding (creating and defining) *
;* fluid variables *
;* *
;* Note: At entry to this routine, ES is set to point to the beginning *
;* of the page containing the current code block. *
;************************************************************************
public bind_fl
bind_fl: lods word ptr ES:[SI] ; load src register, constant number
mov BL,AH ; copy the source register number
lea DI,reg0+[BX] ; and compute its address
; tmp_reg <- symbol
mov BL,AL ; BX <- constant number * 3
mov AX,BX
shl AX,1
add BX,AX
add BX,CB_dis ; add displacement of current code block
xor AX,AX
mov AL,ES:[BX].cod_cpag ; copy the symbol pointer into the
mov tmp_page,AX ; temporary register
mov AX,ES:[BX].cod_cdis
mov tmp_disp,AX
; cons(tmp_reg, tmp_reg, value)
mov AX,offset tmp_reg ; load address of temporary register
pushm <DI,AX,AX> ; push arguments to "cons"
C_call cons,<SI>,Load_ES ; create (cons symbol value)
; cons(FNV, tmp_reg, FNV)
mov AX,offset tmp_reg ; load address of temporary register
mov BX,offset FNV_reg ; load addr of fluid environment register
pushm <BX,AX,BX> ; push arguments to "cons"
C_call cons ; create (cons (cons symbol value) FNV)
jmp next_SP ; return to interpreter
;************************************************************************
;* Unbind fluid variable UNBIND-FL const *
;* *
;* Purpose: Interpreter support for unbinding (deleting) fluid *
;* variables *
;* *
;* Description: The fluid environment is maintained as an a-list, so *
;* dropping fluids consists of cdr-ing down the list for *
;* the required number of elements. *
;************************************************************************
public unbind_f
unbind_f: lods byte ptr ES:[SI] ; load the count of fluids to drop
mov DX,ES ; save code block's paragraph address
mov CX,AX ; copy the drop count into CX
mov BL,byte ptr FNV_pag ; load the fluid environment pointer
mov DI,FNV_dis
unb_fl: LoadPage ES,BX
;;; mov ES,pagetabl+[BX] ; load entry's paragraph address
mov BL,ES:[DI].cdr_page ; load cdr field of entry
mov DI,ES:[DI].cdr
loop unb_fl ; continue cdr'ing for desired count
mov byte ptr FNV_pag,BL ; re-define the fluid environment
mov FNV_dis,DI ; register
mov ES,DX ; restore code block paragraph address
jmp next ; return to interpreter
;************************************************************************
;* Allocate vector VEC-ALLOCATE dest *
;* *
;* Purpose: Interpreter support for the allocation of vector data *
;* objects. *
;* *
;* Note: Vectors are set to zero after they are allocated to insure *
;* that all entries are valid Scheme pointers. Zeroing a *
;* vector effectively sets all the entries to nil. *
;* If an array were not initialized, the garbage collector *
;* would interpret any leftover data as pointers, and *
;* might cause the Scheme Virtual Machine to go off the *
;* deep end. *
;************************************************************************
public vec_allo
vec_allo: lods byte ptr ES:[SI] ; load destination register number
save <SI> ; save the location pointer
mov BX,AX ; and copy it to TIPC register BX
add BX,offset reg0
cmp byte ptr [BX].C_page,SPECFIX*2 ; is size a fixnum?
jne ve_al_er ; if not, error (jump)
mov AX,[BX].C_disp ; load immediate value from register
shl AX,1 ; and sign extend it
sar AX,1
cmp AX,0 ; value positive?
jl ve_al_er ; if not, error (jump)
cmp AX,10921 ; check against maximum vector size
ja v_toobig ; if too many elements, error (jump)
mov CX,AX ; AX <- AX * 3 (multiply number of
shl AX,1 ; elements by size of pointer)
add AX,CX
mov CX,VECTTYPE ; load type of block to allocate
pushm <AX,CX,BX> ; push arguments
C_call alloc_bl,,Load_ES ; call: alloc_block(&reg, type, size)
pop BX ; recover address of reg holding vector ptr
mov AX,[BX].C_page ; fetch page number from destination reg
corrpage AX ; correct for C callable routine
pushm <[BX].C_disp,AX> ; push page and displacement
C_call zero_blk ; call: zero_blk(page, disp)
jmp next_SP ; return to interpreter
; ***Error-- invalid source operand for vec-alloc***
ve_al_er: mov SI,[BX].C_page ; load operand's page number
cmp byte ptr ptype+[SI],BIGTYPE*2 ; is it a bignum?
je v_toobig ; if so, print "vector too big" message
lea BX,m_ve_al ; otherwise, print "source operand"
jmp src_err ; error message
; ***Error-- vector too large***
v_toobig: restore <SI>
sub SI,2
pushm <SI,m_mkvt_a>
C_call disassem,,Load_ES
pushm <tmp_adr,m_toobig,m_one>
C_call set_nume
jmp sch_err
;************************************************************************
;* Vector size VECTOR-SIZE dest *
;* *
;* Purpose: Interpreter support for the vector-size function to return *
;* the number of elements in a vector data object. *
;* *
;* Description: The number of elements in a vector data object is *
;* determined by dividing the number of bytes (obtained *
;* from the block header of the vector data object) by the *
;* number of bytes in a pointer (3), and subtracting the *
;* overhead of the block header (3 bytes). *
;************************************************************************
public vec_size
vec_size: lods byte ptr ES:[SI] ; load destination register number
mov BX,AX ; and copy into TIPC register BX
save <SI> ; save the location pointer
mov SI,reg0_pag+[BX] ; load page number field of register
cmp ptype+[SI],VECTTYPE*2 ; is object a vector?
jne vec_s_er ; if not, error (jump)
mov DI,reg0_dis+[BX] ; load displacement of vector
LoadPage ES,SI
;;; mov ES,pagetabl+[SI] ; load vector's page paragraph address
mov AX,ES:[DI].vec_len ; load size of object (in bytes),
xor DX,DX ; extend to double word,
mov CX,3 ; load divisor of three,
idiv CX ; divide no. bytes by pointer size
dec AX ; subtract off block overhead
mov reg0_dis+[BX],AX ; store number of elements
mov byte ptr reg0_pag+[BX],SPECFIX*2 ; set tag=fixnum
jmp next_PC ; return to interpreter
; ***error-- operand doesn't point to a vector data object***
vec_s_er: lea BX,m_vec_s
jmp src_err ; display error message
;************************************************************************
;* AL AH *
;* vector fill vec-fill vect,val*
;* *
;* Purpose: Scheme intepreter support for the vector-fill operation *
;************************************************************************
public vec_fill
vec_fill: lods word ptr ES:[SI] ; load operands
save <SI> ; save location pointer
xor BX,BX
mov BL,AL ; copy number of register containing vector
mov DI,reg0_dis+[BX] ; load vector pointer
mov BL,byte ptr reg0_pag+[BX]
cmp byte ptr ptype+[BX],VECTTYPE*2 ; is it really a vector?
jne vecf_err ; if not, error (jump)
LoadPage ES,BX
;;; mov ES,pagetabl+[BX] ; load page address of vector's page
mov BL,AH ; copy pointer to fill value
mov AX,reg0_dis+[BX] ; load value to fill array
mov DL,byte ptr reg0_pag+[BX]
mov CX,ES:[DI].vec_len ; load vector length (in bytes) and
sub CX,BLK_OVHD ; subtract off overhead for block header
jle vecf_fin ; if zero length vector, we're done
vecf_lp: mov ES:[DI].vec_page,DL ; store value into current element
mov ES:[DI].vec_disp,AX ; of vector
add DI,PTRSIZE ; increment pointer into vector
sub CX,PTRSIZE ; decrement array size
jg vecf_lp ; if more elements to define, loop (jump)
vecf_fin: jmp next_PC ; return to Scheme interpreter
vecf_err: lea BX,m_vecf
jmp src_err
;************************************************************************
;* AL AH *
;* (memq obj,list) MEMQ dest,src*
;* *
;* Purpose: Scheme interpreter support for the memq primitive *
;************************************************************************
; Support for SHIFT-BREAK-- restart operation
memq_sb: push m_three ; indicate instruction length = 3
C_call restart ; link to Scheme debugger
public memq
memq: lods word ptr ES:[SI] ; load operands
save <SI> ; save the current location pointer
mov BL,AL ; compute the destination register
memq_x: lea DI,reg0+[BX] ; address in TIPC register DI
mov AL,byte ptr [DI].C_page ; copy search object pointer
mov DX,[DI].C_disp ; into AL,DX (page, disp, respectively)
mov BL,AH ; copy pointer to search list
mov SI,reg0_dis+[BX] ; load contents of "list" register
mov BL,byte ptr reg0_pag+[BX]
jmp memq_go
memq_nxt: cmp byte ptr s_break,0 ; has shift-break been depressed?
jne memq_sb ; if interrupt, jump
mov BL,ES:[SI].cdr_page ; load cdr field and continue
mov SI,ES:[SI].cdr ; search
memq_go: cmp BL,0 ; nil pointer?
je memq_f ; if so, return nil (jump)
cmp byte ptr ptype+[BX],LISTTYPE*2 ; "list" object a list cell?
jne memq_f ; if not, return nil (jump)
LoadPage ES,BX
;;; mov ES,pagetabl+[BX] ; load paragraph address of list cell
cmp DX,ES:[SI].car ; does displacement field of car match obj?
jne memq_nxt ; if not, test next element in list (jump)
cmp AL,ES:[SI].car_page ; does page field of car match obj?
jne memq_nxt ; if not, test next element in list (jump)
; match found-- return pointer to current list cell
mov byte ptr [DI].C_page,BL ; set destination register to point
mov [DI].C_disp,SI ; to current list cell
jmp next_PC ; return to interpreter
; no match-- return 'nil
memq_f: xor AX,AX ; put null value into destination register
mov byte ptr [DI].C_page,AL
mov [DI].C_disp,AX
jmp next_PC ; return to interpreter
;************************************************************************
;* AL AH *
;* (memv key,list) MEMV dest,src *
;* key, list *
;* *
;* Purpose: Scheme interpreter support for the memv primitive *
;************************************************************************
memv_sb: jmp memq_sb ; shift-break support-- link to debugger
public memv
memv: lods word ptr ES:[SI] ; load operands
save <SI> ; save the current location pointer
mov BL,AL ; compute the destination register
mov DI,reg0_pag+[BX] ; load page number of search object
; The following 3 lines are sufficient for MEMV if EQV doesn't require
; an = test for numbers and only checks types instead. All the remaining
; code for MEMV is to handle =.
; test attrib+[DI],FLONUMS+BIGNUMS+STRINGS
; jz memv_x ; unless one of above types, use "memq"
; jmp short memv_y ; otherwise, use full "member" test
test attrib[DI],FIXNUMS+FLONUMS+BIGNUMS+STRINGS
jz memv_x ; unless one of above types, use "memq"
test attrib[DI],FIXNUMS+FLONUMS+BIGNUMS
jz memv_y ; for strings do "member" test
; key is a number
lea DI,reg0[BX] ; DI=address of VM reg containing key
mov BL,AH
lea SI,reg0[BX] ; SI=address of VM reg containing list
push [SI].C_page ; tempsave "list" VM reg
push [SI].C_disp
jmp short memv_nxt
memv_x: jmp memq_x ; these damn short relative jumps!!
memv_y: jmp member_x
; this list element didn't match, go to the next element
memv_more: cmp s_break,0 ; shift-break (IBM: control-break) pressed?
jne memv_sb ; yes, do break
mov BX,[SI].C_disp ; cdr our way down list
mov AL,ES:[BX].cdr_page
mov AH,0
mov [SI].C_page,AX
mov AX,ES:[BX].cdr
mov [SI].C_disp,AX
; loop over each element in the list
memv_nxt: mov BX,[SI].C_page
cmp BX,NIL_PAGE ; at end of list?
je memv_f ; yes, jump
cmp byte ptr ptype[BX],LISTTYPE*2 ; looking at a cons?
jne memv_f ; no, jump
LoadPage ES,BX ; get cons into memory
mov BX,[SI].C_disp ; ES:BX=address of cons cell
mov BL,ES:[BX].car_page
mov BH,0
test attrib[BX],FIXNUMS+FLONUMS+BIGNUMS ; is list elt numeric?
jz memv_more ; no, jump
; key and list element are both numeric
mov tmp_reg.C_page,BX
mov BX,[SI].C_disp
mov BX,ES:[BX].car
mov tmp_reg.C_disp,BX
lea BX,tmp_reg
; begin comparison of key and list element
cmp byte ptr [DI].C_page,SPECFIX*2 ; is key a fixnum?
jne memv_float ; no, jump
cmp byte ptr [BX].C_page,SPECFIX*2 ; is list elt a fixnum?
jne memv_float ; no, jump
; both key and list element are fixnums
mov AX,[BX] ; AX=list elt
mov DX,[DI] ; DX=key
shl AX,1
shl DX,1
cmp AX,DX ; same number?
jne memv_more ; no, jump
; we have a match, copy list object-pointer to VM register containing key
memv_t: mov AX,[SI].C_disp
mov [DI].C_disp,AX
mov AX,[SI].C_page
mov [DI].C_page,AX
jmp short memv_f1
; we have no match, copy '() to VM register containing key
memv_f: xor AX,AX
mov [DI].C_page,AX
mov [DI].C_disp,AX
memv_f1: pop [SI].C_disp ; restore original contents "list" VM reg
pop [SI].C_page
jmp next_PC ; return to interpreter
; key and list element are not both fixnums, do = operation
memv_float: mov AX,EQ_OP
pushm <ES,DI,SI> ; save our state around C call
pushm <BX,DI,AX> ; list elt, key, operation
C_call arith2,,Load_ES ; do =
popm <SI,SI,SI> ; get C args off stack
popm <SI,DI,ES> ; restore our state
cmp AX,0 ; AX negative means "error"
jge memv_flo2 ; nope
jmp sch_err ; yes, go to error handler
memv_flo2: jg memv_t ; AX positive means "true"
jmp memv_more ; no match, go to next list element
;************************************************************************
;* AL AH *
;* (member key,list) MEMBER dest,src *
;* key, list *
;* *
;* Purpose: Scheme interpreter support for the member primitive *
;************************************************************************
memb_sb: jmp memq_sb ; shift-break support-- link to debugger
public member
member: lods word ptr ES:[SI] ; load operands
save <SI> ; save the current location pointer
mov BL,AL
mov DI,reg0_pag+[BX] ; load search object's page number
test attrib+[DI],FIXNUMS+SYMBOLS+CONTINU+CLOSURE+PORTS+CODE+CHARS
jz member_x ; if not one of these, use "equal?" compare
jmp memq_x ; otherwise, use "memq" test
member_x: lea DI,reg0+[BX] ; address in TIPC register DI
mov CL,byte ptr [DI].C_page ; load pointer to object in CL:DX
mov DX,[DI].C_disp
mov BL,CL
mov CH,byte ptr ptype+[BX] ; load type code of search object
mov BL,AH ; copy pointer to search list
mov SI,reg0_dis+[BX] ; load contents of "list" register
mov BL,byte ptr reg0_pag+[BX]
jmp memb_go
memb_mor: mov AX,BX
mov BL,ES:[SI].car_page
cmp CH,byte ptr ptype+[BX]
jne memb_nxt
pushm <AX,CX,DX,SI> ; save registers across call
xor AX,AX
mov AL,ES:[SI].car_page
mov [BP].temp_reg.C_page,AX ; temp_reg <- (car list)
mov AX,ES:[SI].car
mov [BP].temp_reg.C_disp,AX
lea BX,[BP].temp_reg ; load address of temporary register
pushm <BX,DI> ; push arguments
C_call sequal_p,,Load_ES ; call: sequal_p(&dest,&src)
pop DI ; retrieve destination register address
add SP,WORDINCR ; dump other arguments from stack
popm <SI,DX,CX,BX> ; restore registers
LoadPage ES,BX ; restore page paragraph address
cmp AX,0 ; were values equal?
jne memb_fnd ; if so, jump
memb_nxt: cmp s_break,0 ; has shift-break key been depressed?
jne memb_sb ; if interrupt, jump
mov BL,ES:[SI].cdr_page ; load cdr field and continue
mov SI,ES:[SI].cdr ; search
memb_go: cmp BL,0 ; nil pointer?
je memb_f ; if so, return nil (jump)
cmp byte ptr ptype+[BX],LISTTYPE*2 ; "list" object a list cell?
jne memb_f ; if not, return nil (jump)
LoadPage ES,BX
;;; mov ES,pagetabl+[BX] ; load paragraph address of list cell
cmp DX,ES:[SI].car ; does displacement field of car match obj?
jne memb_mor ; if not, test next element in list (jump)
cmp CL,ES:[SI].car_page ; does page field of car match obj?
jne memb_mor ; if not, test next element in list (jump)
; "eq" match found-- return pointer to current list cell
memb_fnd: mov byte ptr [DI].C_page,BL ; set destination register to point
mov [DI].C_disp,SI ; to current list cell
jmp next_PC ; return to interpreter
; no match-- return 'nil
memb_f: xor AX,AX ; put null value into destination register
mov byte ptr [DI].C_page,AL
mov [DI].C_disp,AX
jmp next_PC ; return to interpreter
;************************************************************************
;* AL AH *
;* (assq obj,list) ASSQ obj,list*
;* *
;* Purpose: Scheme interpreter support for the assq primitive *
;************************************************************************
public assq
assq: lods word ptr ES:[SI] ; load operands
save <SI> ; save the location pointer
assq_go: mov BL,AH ; copy the list register number
mov SI,reg0_pag+[BX]
cmp ptype+[SI],LISTTYPE*2 ; is second operand a list?
jne assq_err ; if not, error(?) (jump)
LoadPage ES,SI
mov DI,SI ; Save page number
;;; mov ES,pagetabl+[SI] ; load list page's paragraph address
mov SI,reg0_dis+[BX] ; load pointer to list operand
mov BL,AL ; load object register number
mov DX,reg0_pag+[BX] ; load pointer to search object
mov AX,reg0_dis+[BX]
push BX ; save destination register number
mov BX,DI ; Pass the page number
call lookup ; search list for eq? comparison of obj
pop SI ; restore destination register number
mov byte ptr reg0_pag+[SI],BL ; store result of search in
mov reg0_dis+[SI],DI ; the destination register
jmp next_PC ; return to interpreter
; ***second operand is not a list-- return nil***
assq_err: mov BL,AL ; copy destination register number
xor AX,AX
mov byte ptr reg0_pag+[BX],AL ; store nil into destination
mov reg0_dis+[BX],AX ; register
jmp next_PC ; return to interpreter
;************************************************************************
;* AL AH *
;* (assv key,alist) ASSV key,alist *
;* *
;* Purpose: Scheme interpreter support for the assv primitive *
;************************************************************************
assv_sb: jmp memq_sb ; shift-break support-- link to debugger
public assv
assv: lods word ptr ES:[SI] ; load operands
save <SI> ; save the location pointer
mov BL,AL ; get number of VM register containing key
mov DI,reg0_pag+[BX] ; load key's page number
; The following 3 lines are sufficient for ASSV if EQV doesn't require
; an = test for numbers and only checks types instead. All the remaining
; code for ASSV is to handle =.
; test attrib+[SI],FLONUMS+BIGNUMS+STRINGS ; one of these?
; jz assq_go ; if not one of above, use assq (jump)
; jmp short assoc_go ; if one of the above, use assoc
test attrib[DI],FIXNUMS+FLONUMS+BIGNUMS+STRINGS
jz assv_x ; unless one of above types, use "assq"
test attrib[DI],FIXNUMS+FLONUMS+BIGNUMS
jz assv_y ; for strings do "assoc" test
; key is a number
lea DI,reg0[BX] ; DI=address of VM reg containing key
mov BL,AH
lea SI,reg0[BX] ; SI=address of VM reg containing list
push [SI].C_page ; tempsave "alist" VM reg
push [SI].C_disp
jmp short assv_nxt
assv_x: jmp assq_go ; these damn short relative jumps!!
assv_y: jmp assoc_go
; this list element didn't match, go to the next element
assv_more: cmp s_break,0 ; shift-break (IBM: control-break) pressed?
jne assv_sb ; yes, do break
mov BX,[SI].C_page
LoadPage ES,BX ; get toplevel cons back into memory
mov BX,[SI].C_disp ; ES:BX=address of toplevel cons cell
mov AL,ES:[BX].cdr_page ; cdr down the alist
mov AH,0
mov [SI].C_page,AX
mov AX,ES:[BX].cdr
mov [SI].C_disp,AX
; loop over each element in the list
assv_nxt: mov BX,[SI].C_page
cmp BX,NIL_PAGE ; at end of list?
je assv_f ; yes, jump
cmp byte ptr ptype[BX],LISTTYPE*2 ; looking at a cons?
jne assv_f ; no, jump
LoadPage ES,BX ; get toplevel cons into memory
mov BX,[SI].C_disp ; ES:BX=address of toplevel cons cell
push BX ; tempsave it
mov BL,ES:[BX].car_page
mov BH,0
cmp byte ptr ptype[BX],LISTTYPE*2 ; is car of toplevel cons also a cons?
je assv_down ; yes, jump
assv_pop: pop BX ; normalize stack
assv_more1: jmp assv_more ; look at next toplevel cons
assv_down: mov DX,BX
pop BX ; (ES:BX=address of toplevel cons again)
mov BX,ES:[BX].car ; DX:BX=object ptr to 2nd level cons
LoadPage ES,DX ; ES:BX=address of 2nd level cons cell
push BX ; tempsave it
mov BL,ES:[BX].car_page
mov BH,0
test attrib[BX],FIXNUMS+FLONUMS+BIGNUMS ; is its car numeric?
jz assv_pop ; no, jump
mov tmp_reg.C_page,BX ; yes, move car ptr into tmp_reg
pop BX ; (ES:BX=address of 2nd level cons again)
mov BX,ES:[BX].car
mov tmp_reg.C_disp,BX
lea BX,tmp_reg ; BX=address of tmp_reg
; begin comparison of key and list element
cmp byte ptr [DI].C_page,SPECFIX*2 ; is key a fixnum?
jne assv_float ; no, jump
cmp byte ptr [BX].C_page,SPECFIX*2 ; is list elt a fixnum?
jne assv_float ; no, jump
; both key and list element are fixnums
mov AX,[BX] ; AX=list elt
mov DX,[DI] ; DX=key
shl AX,1
shl DX,1
cmp AX,DX ; same number?
jne assv_more1 ; no, jump
jmp short assv_t
; we have no match, copy '() to VM register containing key
assv_f: xor AX,AX
mov [DI].C_page,AX
mov [DI].C_disp,AX
assv_f1: pop [SI].C_disp ; restore original contents "alist" VM reg
pop [SI].C_page
jmp next_PC ; return to interpreter
; we have a match, copy list object-pointer to VM register containing key
assv_t: mov BX,[SI].C_page
LoadPage ES,BX
mov BX,[SI].C_disp ; ES:BX=address of toplevel cons
mov AX,ES:[BX].car ; move car of this cons to dest. register
mov [DI].C_disp,AX
mov AL,ES:[BX].car_page
mov AH,0
mov [DI].C_page,AX
jmp assv_f1 ; return to interpreter
; key and list element are not both fixnums, do = operation
assv_float: mov AX,EQ_OP
pushm <ES,DI,SI> ; save our state around C call
pushm <BX,DI,AX> ; list elt, key, operation
C_call arith2,,Load_ES ; do =
popm <SI,SI,SI> ; get C args off stack
popm <SI,DI,ES> ; restore our state
cmp AX,0 ; AX negative means "error"
jge assv_flo2 ; nope
jmp sch_err ; yes, go to error handler
assv_flo2: jg assv_t ; AX positive means "true"
jmp assv_more ; no match, go to next list element
;************************************************************************
;* AL AH *
;* (assoc obj,list) ASSOC obj,list*
;* *
;* Purpose: Scheme interpreter support for the assoc primitive *
;* *
;* Register Usage: DX - address of destination register *
;* ES:SI - pointer to current list cell *
;************************************************************************
public assoc
assoc: lods word ptr ES:[SI] ; load operands
save <SI> ; save the location pointer
mov BL,AL ; copy search object's register number
mov SI,reg0_pag+[BX] ; load search object's page number
test attrib+[SI],FIXNUMS+SYMBOLS+CONTINU+CLOSURE+PORTS+CODE+CHARS
jz assoc_go
jmp assq_go ; if one of the above, use assq (jump)
assoc_go: mov DX,BX ; copy obj's reg number into TIPC reg DX
add DX,offset reg0 ; compute address of search obj register
mov BL,AH ; copy list register number
mov SI,reg0_dis+[BX] ; load displacement pointer of "list"
mov BL,byte ptr reg0_pag+[BX] ; load page number of "list"
assoc_lp: cmp BL,0 ; end of list? (nil pointer?)
je assoc_nf ; if end of list, jump
cmp byte ptr ptype+[BX],LISTTYPE*2 ; is list operand a list?
jne assoc_er ; if not, error(?) (jump)
LoadPage ES,BX
mov AX,BX ;****** SAVE PAGE *********
;;; mov ES,pagetabl+[BX] ; load list page's paragraph address
mov BL,ES:[SI].car_page ; load page number of car
cmp byte ptr ptype+[BX],LISTTYPE*2 ; does car point to list cell?
jne assoc_nl ; if not a list cell, jump
mov DI,ES:[SI].car ; load displacement pointer of car field
pushm <AX,SI> ;****** REALLY SAVE PAGE****
LoadPage ES,BX
;;; mov ES,pagetabl+[BX]
xor AX,AX
mov AL,ES:[DI].car_page ; copy car field into tmp_reg
mov tmp_page,AX
mov AX,ES:[DI].car
mov tmp_disp,AX
mov AX,offset tmp_reg
pushm <DX,AX> ; push arguments to call
C_call sequal_p,,Load_ES ; compare equality of the two pointers
add SP,WORDINCR ; dump tmp_reg address
pop DX ; restore obj/dest register address
popm <SI,BX> ; restore ES,SI registers
LoadPage ES,BX ;********** Restore Para Address *****
cmp AX,0 ; were pointers equal?
jne assoc_t ; if equal, jump
assoc_nl: xor BX,BX ; clear high order byte of BX
mov BL,ES:[SI].cdr_page ; follow cdr field
mov SI,ES:[SI].cdr
cmp byte ptr s_break,0 ; has the shift-break key been depressed?
je assoc_lp ; if no shift-break, loop
jmp memq_sb ; if interrupt, jump to debugger support
; pointers "equal"-- return pointer to car field of current list cell
assoc_t: mov DI,DX ; copy destination register address to DI
mov AL,ES:[SI].car_page ; return cdr field of list cell
mov byte ptr [DI].C_page,AL
mov AX,ES:[SI].car
mov [DI].C_disp,AX
jmp next_PC ; return to interpreter
; end of search, or error detected-- return nil
assoc_er:
assoc_nf: mov DI,DX ; copy destination register address to DI
mov byte ptr [DI].C_page,NIL_PAGE*2 ; store nil pointer into
mov [DI].C_disp,NIL_DISP ; destination register
jmp next_PC ; return to interpreter
var_int endp
;************************************************************************
;* Lookup Symbol is Assoc List *
;* *
;* Purpose: To search a linked list for a given pointer *
;* *
;* Description: The list to be searched has the following format: *
;* *
;* +--------+--------+ +--------+-------+ *
;* +-->|symbol->|value ->| +-->|symbol->|value->| *
;* | +--------+--------+ | +--------+-------+ *
;* | | *
;* +---+----+--------+ +---+----+--------+ *
;* | o | o----+----...----->| o | (nil) | *
;* +--------+--------+ +--------+--------+ *
;* *
;* The symbol portion of the list entries are compared against the *
;* search symbol for an identical match. When found, a pointer to *
;* the matched symbol's symbol-value entry is returned. If the *
;* symbol is not found, a value of nil is returned. *
;* *
;* Registers upon entry: AX - search symbol's displacement *
;* BX - page number of list to search *
;* DL - search symbol's page number *
;* SI - displacement within page number *
;* of list to search *
;* *
;* Registers on exit: BL - page number of cell whose car is the *
;* search symbol, or zero if not found *
;* DI - displacement of list cell found, or nil *
;* ES:[DI] - points to cell found *
;************************************************************************
public lookup
lookup proc near
lookloop:
mov CX,BX ; Save Page number
LoadPage ES,BX ; Load Paragraph address of page
mov BL,ES:[SI].car_page ; load car of next list cell in the list
cmp byte ptr ptype+[BX],LISTTYPE*2 ; is car a list cell?
mov DI,ES:[SI].car
jne look_err ; if not a list cell, jump
LoadPage ES,BX
;;; mov ES,pagetabl+[BX] ; load paragraph address of its page
cmp AX,ES:[DI].car ; does car's disp match search symbol's?
jne look_nf ; if not, keep searching (jump)
cmp DL,ES:[DI].car_page ; does car's page match search symbol's?
je look_fnd ; if so, we've got a match (jump)
; no match-- continue through linked list
look_nf: mov BX,CX ; restore page number
LoadPage ES,BX
mov BL,ES:[SI].cdr_page ; load the cdr field
cmp byte ptr ptype+[BX],LISTTYPE*2 ; is cdr another list cell?
jne look_err ; if not, error(?)
mov SI,ES:[SI].cdr
cmp BX,0 ; is cdr nil?
jne lookloop ; if not, branch
xor DI,DI ; make BX:DI nil
look_fnd: ret ; return pointer to caller
;
look_err: xor BX,BX ; create a nil pointer to return
xor SI,SI
ret
lookup endp
;************************************************************************
;* C-callable Fluid Variable Lookup *
;* *
;* Purpose: To retrieve the fluid binding for a variable. *
;* *
;* Calling Sequence: stat = fluid_lookup(&reg) *
;* where &reg - address of the register containing *
;* the symbol to be looked up. *
;* On exit, "reg" contains the *
;* current binding for the symbol, *
;* if found. *
;* stat - search status: TRUE=symbol found *
;* FALSE=symbol not found *
;* *
;* Note: If the call to "lookup" doesn't find the desired symbol, it *
;* will return a nil pointer. It is correct to always *
;* return the cdr of the pointer "lookup" returns, since *
;* the cdr of nil is itself nil-- a valid value. *
;************************************************************************
fl_lk_ar struc
dw ? ; caller's BP
dw ? ; caller's ES
dw ? ; return address
fl_lk_rg dw ? ; register address
fl_lk_ar ends
public fluid_lo
fluid_lo proc near
push ES ; save caller's ES
push BP ; and BP
mov BP,SP
; load pointer to search symbol in DL:AX
mov BX,[BP].fl_lk_rg ; load register address
mov AX,[BX].C_disp
mov DL,byte ptr [BX].C_page
; load pointer to search list (fluid environment) in ES:[SI]
mov BX,FNV_pag
mov SI,FNV_dis
;;; LoadPage ES,BX
;;; mov ES,pagetabl+[BX]
; search the fluid environment for the symbol
call lookup
; store "cdr" of returned cell into register
mov SI,[BP].fl_lk_rg
mov AL,ES:[DI].cdr_page
mov byte ptr [SI].C_page,AL
mov AX,ES:[DI].cdr
mov [SI].C_disp,AX
; set return code (BX=0 if symbol not found) and return
mov AX,BX
pop BP ; restore caller's BP
pop ES ; and ES
ret ; return to caller
fluid_lo endp
prog ends
end