Category : Assembly Language Source Code
Archive   : EMAC15ES.ZIP

Output of file : MINTSCAN.ASM contained in archive : EMAC15ES.ZIP
;12-12-87 13:41:44 bum a lot of instructions out of scan_loop.
;12-07-87 20:09:39 add store_debug
;11-23-87 22:20:47 use scan_copy as the base of scan_xlat_table.
page ,132

include mint.def

HT equ 9
LF equ 10
CR equ 13

code segment byte public
code ends

data segment byte public
data ends

bufseg segment public
public prev_buffer, next_buffer, toptop, topbot, bottop, botbot, new_size
bufseg ends

data segment byte public
public data_bottop, data_topbot, data_botbot
define_buffer data_
data ends

data segment byte public

comment /******************************************************************


The MINT data structures are laid out in memory as given below.
First are the variables, followed by the neutral string. Next is the
block of free memory.

variables, neutral string ... free memory ... neutral string.

The neutral string:

The neutral string consists of a list of arguments. Each argument
begins with header which might be laid out as so in Pascal:
arg_header = record
marker : (active_marker, neutral_marker, comma_marker);
previous : ^arg_header;

The pointer, previous, points to the previous arg_header. The last one in the
list has a nil pointer. This will always be the #(ps) which is the outermost
function to be executed.

The neutral string during function execution:

Since we are interested in counting arguments from left to right,
not right to left, we need to reverse the pointers so that they point
from the first argument, to the second argument, to the third argument,
etc. At this point, [fbgn] points to the first argument (the name of the
funciton), and [fend] points past the last argument. To make argument
fetching more efficient, the last argument is followed by a null argument
which points to itself. This causes missing arguments to be fetched as
nulls, in according to the definition of the language.
Functions which return a value will build that value at either
[fbgn]-1 or [fend]-2, depending on whether or not they need to refer to
arguments supplied to the function. In general, single argument functions
will use [fbgn]-1, and multiple argument functions will use [fend]-2.

Neutral function results will eventually be moved to [fbgn]-1.

The active string:

The active string consists of a string of ASCII characters which
have not yet been scanned. Typically, only ASCII characters appear here,
although any eight bit value may occur.

The active string during function execution:

[actptr] points to the end of the active string. Active function
results are built as described above and then moved to the left of [actptr].
Actptr is then adjusted to point to the result just moved in. Primitives
check for memory overflow by comparing [fend] to [actptr]. If they come
closer than some magic constant, then the 'No Memory' error is given
and the idling string is reloaded.


public trace
public next_ids
extrn standard_ids: byte
public fbgn, fend
extrn lomem: byte

trace db 0 ;trace is initially off.
next_ids dw standard_ids
fbgn dw ?
fend dw ?
fcn_save db ?
prev_fcn dw ?

;some constant definitions

;the _mark constants mark where a particular type of string occurs in
; the linked list.

comma_marker equ 0 ;comma_marker must not have function_marker_mask set!
active_marker equ 1 ;active_marker must have function_marker_mask set!
neutral_marker equ 3 ;neutral_marker must have function_marker_mask set!
function_marker_mask equ 1

entry macro char, adr
org (offset scan_xlat_table)+char
db (offset adr) - (offset scan_copy)

db 0
scan_xlat_table db 256 dup (0)
;first, fill up the table with 'copy'
;next, put the proper addresses in the right spots,
entry HT,scan_ignore
entry CR,scan_ignore
entry LF,scan_ignore
entry '#',scan_sharp
entry '(',scan_lpar
entry ')',scan_rpar
entry ',',scan_comma
;finally, go to the end of the table.
org (offset scan_xlat_table)+(size scan_xlat_table)

purge entry

extrn function_name_table: word
extrn function_name_length: abs
extrn function_address: word

nomem_prompt db 'No memory!',0
fatal_prompt db 'No disk in drive or door open!',0
protected_prompt db 'Disk is write protected!',0

extrn stackp: byte

buffers_bad_msg db 'Buffers corrupted, reboot:','$'
buffers_be_msg db 'xx will say Buffers corrupted, reboot:','$'

data ends

code segment byte public

assume cs:code, ds:data, es:data

comment /*******************************************************************
The following is the MINT scan loop. This loop must be as fast as
possible because it is executed the most often. As a consequence, the
code is quite unstructured. However, the code follows the algorithm given in
the MINT language definition document.
During scan, si -> (points to) the active string, di -> the
neutral string, dx -> previous argument, and bp -> end of active string.
As we scan a character, we must branch to certain routines on certain
characters. To make best use of the 8086 instruction set, we have set up a
translate table. Therefore, the translate table, scan_xlat_table, contains an
offset from the beginning of the scan loop.

When the scan loop has finally found a function to be executed,
a jump is performed to that primitive (unknown primitives cause jump to dflt).
When the primitive is finished, it jumps (with the exception of hl) to one of
the 'return_???' functions. Each of the return_??? routines puts the returned
value in the proper place in the proper string (active or neutral), and jumps
back to scan.
The scan loop is repeatedly executed until there are no more functions
to be executed, or the available memory has been exhausted.

scan_copy: ;come here to copy a char from active to neutral
lodsb ;movsb loses because it doesn't load AL.
xlat ;al was char, is now offset
jmp ax

scan_ignore: ;come here to throw a char away from active.
dec di
jmp scan_loop

dec di ;uncopy the '('
mov cx,1
lodsb ;can't use movsb, because it doesn't load al
cmp al,'('
je scan_lpar_2
cmp al,')'
jne scan_lpar_1
cmp si,bp ;was this our sentinel?
je init_ids_jump_2 ;yes - we're gone.
loop scan_lpar_1
dec di ;remove final rpar
mov ax,offset scan_copy
jmp scan_loop
inc cx
jmp scan_lpar_1
jmp init_ids

;si -> neutral string
;di -> active string
;dx -> previous argument or function pointer.
dec di ;uncopy the ')'
cmp si,bp ;if we scan off the right end, init_ids
je init_ids_jump_2
call scan_rpar_sub
call buffer_check
jnz buffers_be_bad
call function_address[di-2]
extrn buffer_check: near
call buffer_check
jnz buffers_bad
mov bp,data_botbot
mov bx,offset scan_xlat_table ;->translate table
mov ax,offset scan_copy
jmp scan_loop
mov word ptr buffers_be_msg,ax
mov dx,offset buffers_bad_msg
mov ah,9
int 21h
mov ah,7
int 21h
jmp buffers_be_bad_1
mov dx,offset buffers_bad_msg
mov ah,9
int 21h
mov ah,7
int 21h
jmp buffers_bad

mov al,comma_marker
mov [di-1],al ;store marker where the character was copied.
mov ax,dx ;get previous pointer
mov dx,di ;save current (will soon be previous)
mov ax,offset scan_copy
jmp scan_loop

cmp word ptr [si],'(#' ;'##(' ?
je scan_two_sharps ;yes.
cmp byte ptr [si],'(' ;'#(' ?
jne scan_copy_j_1 ;no.
inc si
mov al,active_marker
jmp scan_mark
add si,2
mov al,neutral_marker
jmp scan_mark

public abort_fatal
add sp,22 ;magic number from Z-DOS II, page I.3
pop es ;restore our es and ds.
push es
pop ds
mov sp,offset stackp
sti ;enable interrupts again.
mov si,offset protected_prompt
cmp di,0 ;write protect?
je nomem_1
mov si,offset fatal_prompt
jmp short nomem_1
public nomem
mov sp,offset stackp
mov si,offset nomem_prompt
or al,al
je nomem_2
mov dl,al
mov ah,2
int 21h
jmp nomem_1
jmp init_ids

extrn init_ids: near
extrn buffer_free: near

public init_ids_continue, init_ids_first
mov ax,data_botbot ;get rid of the active string.
mov data_bottop,ax
mov data_topbot,offset lomem;get rid of the neutral string.
mov di,next_ids ;get the desired idling string.
mov si,di ;save a copy
mov next_ids,offset standard_ids ;reset to ids.
mov al,0
mov cx,-1
repne scasb ;find the terminating null.
not cx
mov ax,ds
call buffer_free
mov di,data_botbot
sub di,cx
dec di ;leave room for a sentinel
mov data_bottop,di
rep movsb
mov al,')' ;use an extra ')' as a sentinel
mov cx,256 ;get at least a little bit of room.
mov ax,ds
call buffer_free
mov si,data_bottop
mov di,data_topbot
mov dx,0
mov bp,data_botbot
mov bx,offset scan_xlat_table ;->translate table
mov ax,offset scan_copy
jmp scan_loop
jmp init_ids

public scan_rpar_sub
;store last argument mark
mov al,comma_marker
mov ax,di ;make final arg -> itself
comment @can't use slash***************************************************
We have a problem here. Currently, the pointers point backwards
to the previous function/arg. We want this function's pointers to point
forwards, so we can start at the active/neutral marker and count arguments

__ is a pointer, ^ is what it points to.

^ ^ !^ !^
! !! !! !!
\___/\____/! \/
mov fend,di
mov data_topbot,di
sub di,2 ;make di ->final pointer
mov data_bottop,si
cmp dx,0 ;if end of list, we must be running off
je init_ids_jump_1 ; the left end (too many rpars)
mov bx,dx ;get previous pointer.
mov dx,[bx] ;get the current pointer [previous pointer].
mov [bx],di ;store the next pointer.
mov di,bx ;save current pointer.
; bx, di -> current arg/fcn
; dx -> previous (to the left) arg/fcn
test byte ptr -1[bx],function_marker_mask
jz scan_rpar_1
mov al,-1[bx]
mov fcn_save,al ;remember the type of function.
mov prev_fcn,dx
mov fbgn,bx
call check_breakchar ;check for a break.
jnc got_break_char ;got it.
call trace_invoke ;destroys al
;remember that fbgn is really one more than the space taken by the function.
mov ax,[bx] ;get pointer to first arg.
sub ax,bx ;compute length of name
cmp ax,2 + mark_overhead ;two character function name?
jne default_to_cl ;no - must be default.
mov ax,2[bx] ;get function name.
extrn store_debug: near
call store_debug
mov di,offset function_name_table
mov cx,offset function_name_length
repne scasw
jne default_to_cl ;if not found, default
sub di,offset function_name_table
mov ax,'d*'
call store_debug
mov di,0
jmp init_ids

;return data routines here

public return_null
mov cx,0
call trace_result ;destroys al
mov si,data_bottop
mov di,fbgn
dec di
mov dx,prev_fcn

public return_string
;al=string number to return, bx=>list of strings.
add al,al
mov ah,0
add bx,ax
mov si,[bx]
mov cx,[bx+2]
sub cx,si
jmp return_sicx

public return_tos
;tos -> string, di -> byte after end of string
pop si
mov cx,di
sub cx,si
jmp short return_sicx

public return_arg_active
mov fcn_save,active_marker
;fall through to return_arg

public return_arg
;enter with cx = number of arg to return.
call getarg
;fall through to return_sicx

public return_sicx
;si -> string, cx = count.
cmp fcn_save,active_marker ;active or neutral
jne return_neutral
; jmp return_active ;fall through!

public return_active
;we need to move [si] count cx
; to [data_bottop-cx] through [data_bottop-1] reverse
;Then we return si = [data_bottop-cx], di=fbgn-1
call trace_result ;destroys al
jcxz return_nothing ;quick check for 0 chars.
mov di,data_bottop
dec di
add si,cx ;point si to end of string + 1.
dec si ;remember, it's postdecrement.
std ;reverse move
rep movsb
cld ;everybody assumes it's cleared.
inc di ;make di -> last byte moved.
mov si,di ;si -> what we just moved.
mov di,fbgn ;remove previous function.
dec di
mov dx,prev_fcn

public return_neutral
;we need to move [si] count cx
; to [fbgn-1] through [fbgn-1] - (cx - 1)
;Then we return si=data_bottop, di=[fbgn-1] - cx
call trace_result ;destroys al
jcxz return_nothing ;quick check for 0 chars.
mov di,fbgn
dec di
cmp di,si ;is it there already?
je return_neutral_1 ;yes, save some time.
rep movsb ;put it there.
;tricky time! If we performed the movsb, cx is zero, so we're doing
;nothing. If we took the jump to return_neutral_1, cx is the proper
;count, so di will point to the right place.
add di,cx
mov si,data_bottop
mov dx,prev_fcn

extrn trace_result: near
extrn trace_invoke: near

;utility subroutines

extrn check_breakchar: near

public getarg1, getarg
getarg1: mov cx,1

;fall through to getarg


;enter with cx = number of argument to get.
;exit with si -> argument, cx=length of argument.

comment /****************************************************************

The pointer after the last supplied argument points to itself, which allows
us to loop at getarg_loop until we think that we have found the argument. Of
course, if that argument has not been supplied, all that we've done is to chase
the last pointer a few times. As an aside, had you ever noticed that when the
amount of comments exceeds the amount of code, the code is likely to be
confusing? Well, this code is probably confusing.

mov si,fbgn
jcxz getarg_2 ;skip loop if count is zero.
mov si,[si] ;get our argument
loop getarg_loop
mov cx,[si] ;get cx=next argument
sub cx,si ;get cx=length of our argument
jcxz getarg_1 ;in case we ran into fend, it doesn't matter what si -> to.
sub cx,mark_overhead
add si,mark_overhead-1 ;make si-> text of argument.

code ends


  3 Responses to “Category : Assembly Language Source Code
Archive   : EMAC15ES.ZIP

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: