; =====> 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 ; 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 , 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 ; 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 ; 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 ; 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 ; push arguments for error call C_call not_flui,,Load_ES ; call error routine restore ; 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 ; 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 ; push arguments to "cons" C_call cons,,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 ; 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 ; 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 ; push arguments C_call alloc_bl,,Load_ES ; call: alloc_block(®, 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 sub SI,2 pushm C_call disassem,,Load_ES pushm 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 ; 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 ; 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 ; 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 ; 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 ; save our state around C call pushm ; list elt, key, operation C_call arith2,,Load_ES ; do = popm ; get C args off stack popm ; 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 ; 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 ; 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 ; 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 ; 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 ; 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 ; 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 ; save our state around C call pushm ; list elt, key, operation C_call arith2,,Load_ES ; do = popm ; get C args off stack popm ; 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 ; 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 ;****** 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 ; 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 ; 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(®) * ;* where ® - 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