1277 lines
29 KiB
NASM
1277 lines
29 KiB
NASM
TITLE CXINIT - Initiate execution of a Lattice C program
|
||
SUBTTL Copyright 1982 by Lattice, Inc.
|
||
NAME CXINIT
|
||
INCLUDE DOS.MAC
|
||
.8087
|
||
|
||
;**** modified to add EGA support 10-20-86 dbs
|
||
|
||
;**** modified for PROTECTED MODE 7-29-87 tc
|
||
|
||
; ***** Lattice C v3.0 C.asm file modified for PCS *****
|
||
; (None of the original file is altered. Mods done thru conditional assembly.)
|
||
|
||
scheme equ 1
|
||
|
||
;**
|
||
;
|
||
; name CXINIT -- initiate execution of C program
|
||
;
|
||
; description This is the main module for a C program on the
|
||
; MS-DOS implementation. It performs various low-level
|
||
; initializations and then calls _main.
|
||
;
|
||
; This routine is stored as C.OBJ and is included as
|
||
; the first object module when linking. It is important
|
||
; to include it first so that the segment structure will
|
||
; be defined properly.
|
||
;
|
||
; NOTE: In order for the segment structure to work correctly,
|
||
; this module must be assembled with Version 3 or later of the
|
||
; Microsoft assembler. Specifically, older assemblers emit
|
||
; segments in alphabetical order, while Version 3 and later
|
||
; emit them in the order that they appear. The latter is
|
||
; required for this module.
|
||
;
|
||
;**
|
||
|
||
;**
|
||
;
|
||
; Assembly parameters
|
||
;
|
||
TAB EQU 09H ; tab character
|
||
|
||
;**
|
||
;
|
||
; External program references (P and L models)
|
||
;
|
||
IF LPROG
|
||
EXTRN _MAIN:FAR
|
||
EXTRN RBRK:FAR
|
||
ENDIF
|
||
|
||
;**
|
||
;
|
||
; Define data group
|
||
;
|
||
DGROUP GROUP DATA,UDATA,XSTACK
|
||
ASSUME DS:DGROUP
|
||
;**
|
||
;
|
||
; Define program segment
|
||
;
|
||
if scheme ;extra segment for Scheme
|
||
XGROUP GROUP PROGX
|
||
PROGX SEGMENT BYTE PUBLIC 'PROGX'
|
||
PROGX ENDS
|
||
endif
|
||
|
||
IF S8086
|
||
IF COM
|
||
PGROUP GROUP PROG,TAIL
|
||
ENDIF
|
||
IFDEF PROMEM ;;; Protected Mode
|
||
PROG SEGMENT PARA PUBLIC 'PROG'
|
||
ELSE
|
||
PROG SEGMENT BYTE PUBLIC 'PROG'
|
||
ENDIF
|
||
ASSUME CS:PROG
|
||
IF COM
|
||
ORG 100H
|
||
ENDIF
|
||
ENDIF
|
||
|
||
IF D8086
|
||
CODE SEGMENT BYTE PUBLIC 'CODE'
|
||
ASSUME CS:CODE
|
||
ENDIF
|
||
|
||
IF P8086
|
||
_CODE SEGMENT BYTE PUBLIC 'CODE'
|
||
ASSUME CS:_CODE
|
||
ENDIF
|
||
|
||
IF L8086
|
||
_PROG SEGMENT BYTE PUBLIC 'PROG'
|
||
ASSUME CS:_PROG
|
||
ENDIF
|
||
|
||
;**
|
||
;
|
||
; External program references (S and D models)
|
||
;
|
||
IF LPROG EQ 0
|
||
EXTRN _MAIN:NEAR
|
||
IFNDEF PROMEM ;;; Protected Mode - IGNORE
|
||
EXTRN RBRK:NEAR ; C library function
|
||
ENDIF
|
||
if scheme
|
||
extrn allmem:near ; C library function
|
||
extrn rsttimer:near,unfixint:near ; PCS routines
|
||
extrn zcuron:near,zclear:near,zputcur:near ; "
|
||
extrn xli_term:near ; PCS routine
|
||
IFDEF EXPMEM
|
||
extrn rlsexp:near ; PCS routine
|
||
ENDIF
|
||
endif
|
||
ENDIF
|
||
|
||
;**
|
||
;
|
||
; Execution begins here
|
||
;
|
||
PUBLIC CXINIT
|
||
|
||
CXINIT PROC FAR
|
||
CLI
|
||
IF COM
|
||
MOV AX,OFFSET PGROUP:TAIL ; set up DS/SS for .COM file
|
||
ADD AX,16
|
||
MOV CL,4
|
||
SHR AX,CL
|
||
MOV BX,CS
|
||
ADD AX,BX
|
||
MOV DS,AX
|
||
MOV SS,AX
|
||
MOV SP,OFFSET DGROUP:SBASE+STKRSV
|
||
ELSE
|
||
MOV AX,DGROUP ; set up DS/SS for .EXE file
|
||
MOV DS,AX
|
||
IFDEF PROMEM ;;; Protected Mode
|
||
mov ss,ax ; ss=ds
|
||
mov _ss,ax ; and save in _ss
|
||
mov bx, _base ; bx = base of stack (relative to DS)
|
||
add bx, _stack ; add # bytes of stack (default = 2048)
|
||
mov sp, bx ; and save in sp
|
||
mov _top, bx ; top of stack (relative to DS)
|
||
mov _sp,bx ; sp reset value
|
||
ELSE
|
||
MOV AX,XSTACK
|
||
MOV SS,AX
|
||
MOV SP,STKRSV
|
||
ENDIF
|
||
ENDIF
|
||
STI
|
||
if scheme
|
||
;
|
||
; Save address of our exit routine where C can use it
|
||
;
|
||
mov ax,offset xcexit
|
||
mov _onexit,ax
|
||
endif
|
||
;
|
||
; Compute program size in bytes and save in _PSIZE
|
||
;
|
||
IFDEF PROMEM ;;; Protected Mode
|
||
; _PSIZE actually has limit, should increment....
|
||
mov ax, 0ffffh ; Program size will always be 64kb
|
||
MOV WORD PTR _PSIZE,AX
|
||
xor ax, ax
|
||
MOV WORD PTR _PSIZE+2,AX
|
||
ELSE
|
||
MOV AX,DS
|
||
MOV CX,CS
|
||
SUB AX,CX
|
||
MOV CL,4
|
||
ROL AX,CL
|
||
MOV WORD PTR _PSIZE,AX
|
||
AND WORD PTR _PSIZE,0FFF0H
|
||
AND AX,15
|
||
MOV WORD PTR _PSIZE+2,AX
|
||
ENDIF
|
||
;
|
||
; Clear the uninitialized data area
|
||
;
|
||
XOR AX,AX
|
||
MOV DI,OFFSET DGROUP:UDATA
|
||
MOV CX,OFFSET DGROUP:SBASE
|
||
SUB CX,DI
|
||
SHR CX,1
|
||
JZ M0
|
||
PUSH ES
|
||
PUSH DS
|
||
POP ES
|
||
CLD
|
||
REP STOSW
|
||
POP ES
|
||
;
|
||
; Get DOS version number
|
||
;
|
||
M0: MOV AH,30H ; get DOS version number
|
||
INT 21H
|
||
OR AL,AL
|
||
JNZ M00
|
||
MOV AX,1 ; assume 1.00 if null return
|
||
M00: MOV WORD PTR _DOS,AX
|
||
MOV _PSP+2,ES ; set up pointer to prog seg prefix
|
||
;
|
||
; Examine the environment string area
|
||
;
|
||
ife scheme ; PCS ignores DOS environment strings
|
||
IF MSDOS EQ 1
|
||
CMP _DOS,2
|
||
JL M01
|
||
ENDIF
|
||
MOV AX,ES:[2CH]
|
||
MOV WORD PTR _ENV+2,AX ; set up pointer to environment
|
||
OR AX,AX
|
||
JZ M01 ; branch if null
|
||
PUSH ES ; determine number of bytes
|
||
CLD
|
||
LES DI,_ENV
|
||
XOR AX,AX
|
||
XOR BX,BX
|
||
MOV CX,7FFFH
|
||
M0A: REPNZ SCASB
|
||
JNZ M0C
|
||
INC BX
|
||
SCASB
|
||
JNZ M0A
|
||
MOV _ESIZE,DI ; save environment size
|
||
MOV _ENVC,BX ; save environment string count
|
||
CMP _DOS,3 ; include command name for DOS 3
|
||
JL M0B
|
||
ADD DI,2
|
||
REPNZ SCASB
|
||
JNZ M0C
|
||
M0B: ADD DI,2 ; make size an even number
|
||
AND DI,0FFFEH
|
||
MOV _XSIZE,DI
|
||
M0C: POP ES
|
||
endif
|
||
;
|
||
; Examine command line
|
||
;
|
||
M01: MOV SI,80H ; check command line
|
||
MOV CL,ES:[SI]
|
||
XOR CH,CH ; CX contains length of cmd line
|
||
|
||
JCXZ M12 ; branch if null
|
||
M1: INC SI ; scan for first arg
|
||
MOV AL,ES:[SI]
|
||
ife scheme ; PCS doesn't scan for special args
|
||
IF MSDOS EQ 1
|
||
CMP _DOS,2
|
||
JGE M10
|
||
CMP AL,'<'
|
||
JE M2 ; branch if input file name
|
||
CMP AL,'>'
|
||
JE M3 ; branch if output file name
|
||
ENDIF
|
||
M10: CMP AL,'='
|
||
JE M4 ; branch if stack size
|
||
endif
|
||
CMP AL,' '
|
||
JE M11 ; branch if white space
|
||
CMP AL,TAB
|
||
JNE M12 ; branch if normal arg
|
||
M11: DEC CX
|
||
JG M1
|
||
XOR CX,CX
|
||
M12: JMP M5 ; branch if no args found
|
||
ife scheme ; PCS doesn't scan for special args
|
||
IF MSDOS EQ 1
|
||
;
|
||
; Get input file name
|
||
;
|
||
M2: MOV DI,OFFSET DGROUP:_INAME
|
||
JMP M31
|
||
;
|
||
; Get output file name
|
||
;
|
||
M3: MOV DI,OFFSET DGROUP:_ONAME
|
||
;
|
||
; Save file name in data area
|
||
;
|
||
M31: XOR AH,AH
|
||
M32: DEC CX
|
||
JZ M33
|
||
INC SI
|
||
MOV AL,ES:[SI]
|
||
CMP AL,' '
|
||
JZ M33
|
||
CMP AL,TAB
|
||
JZ M33
|
||
MOV DS:[DI],AL
|
||
INC DI
|
||
INC AH
|
||
CMP AH,32
|
||
JE M34
|
||
JMP M32
|
||
M33: MOV BYTE PTR DS:[DI],0
|
||
JMP M11
|
||
M34: MOV DX,OFFSET DGROUP:NAMERR
|
||
JMP NEAR PTR XCABT
|
||
ENDIF
|
||
;
|
||
; Get stack and heap sizes from command line
|
||
;
|
||
M4: CALL GETNUM ; get stack size
|
||
JC M47 ; branch if error
|
||
TEST BX,BX
|
||
JZ M42A ; bypass if size is 0
|
||
CMP AL,'K'
|
||
JE M41
|
||
CMP AL,'k'
|
||
JNE M42 ; branch if not kilobytes
|
||
M41: TEST BX,0FFC0H
|
||
JNZ M47 ; error if size > 63
|
||
XCHG BH,BL ; multiply by 1024
|
||
SHL BH,1
|
||
SHL BH,1
|
||
DEC CX ; advance to next character
|
||
JZ M42
|
||
INC SI
|
||
MOV AL,ES:[SI]
|
||
M42: MOV _STACK,BX ; save stack size
|
||
M42A: TEST CX,CX
|
||
JZ M5 ; branch if end of command line
|
||
CMP AL,' '
|
||
JE M46 ; loop if blank
|
||
CMP AL,TAB
|
||
JE M46 ; loop if tab
|
||
CMP AL,'/'
|
||
JNE M47 ; branch if not slash
|
||
CALL GETNUM ; get heap size
|
||
JC M47 ; branch if error
|
||
XOR DX,DX ; long result is in DX,BX
|
||
TEST BX,BX
|
||
JZ M45 ; branch if size is 0
|
||
CMP AL,'K'
|
||
JE M43
|
||
CMP AL,'k'
|
||
JNE M44 ; branch if not kilobytes
|
||
M43: XCHG BH,BL ; multiply by 1024
|
||
ROL BX,1
|
||
ROL BX,1
|
||
MOV DX,BX
|
||
AND BX,0FC00H
|
||
AND DX,3FFH
|
||
DEC CX ; advance to next character
|
||
JZ M44
|
||
INC SI
|
||
MOV AL,ES:[SI]
|
||
M44: MOV WORD PTR _MNEED,BX ; save heap size
|
||
MOV WORD PTR _MNEED+2,DX
|
||
M45: TEST CX,CX
|
||
JZ M5 ; branch if end of command line
|
||
CMP AL,' '
|
||
JE M46 ; loop if blank
|
||
CMP AL,TAB
|
||
JNE M47 ; error if not tab
|
||
M46: JMP M11
|
||
M47: MOV DX,OFFSET DGROUP:STKERR ; abort if stack/heap size error
|
||
JMP NEAR PTR XCABT
|
||
endif ;matches IFE near M12:
|
||
;
|
||
; Set up the stack
|
||
;
|
||
ife scheme ; PCS ignores DOS environment strings
|
||
IF LDATA EQ 0
|
||
M5: MOV AX,_XSIZE ; reserve space for environment
|
||
ADD AX,15
|
||
AND AX,0FFFEH
|
||
ELSE
|
||
M5: XOR AX,AX
|
||
ENDIF
|
||
else
|
||
M5: xor ax,ax
|
||
endif
|
||
|
||
IFDEF PROMEM ;;; Protected Mode
|
||
mov bx,_top ; bx = top of stack
|
||
jmp M54a
|
||
M54: MOV DX,OFFSET DGROUP:MEMERR2 ; abort if error in releasing memory
|
||
JMP NEAR PTR XCABT
|
||
M54a:
|
||
ELSE
|
||
MOV BX,_STACK ; get stack size
|
||
SHR BX,1 ; make size even
|
||
ADD BX,BX
|
||
CMP BX,STKMIN
|
||
JA M51
|
||
MOV BX,STKMIN ; use default if too small
|
||
MOV _STACK,BX
|
||
M51: ADD BX,AX ; add environment size
|
||
JC M54 ; abort if overflow
|
||
MOV DX,ES:2 ; compute available paragraphs
|
||
if scheme
|
||
mov _paras,dx ; save no. paragraphs for Scheme
|
||
endif
|
||
MOV AX,SS
|
||
SUB DX,AX
|
||
TEST DX,0F000H
|
||
JNZ M52 ; branch if greater than 64Kbytes
|
||
SHL DX,1 ; convert to bytes
|
||
SHL DX,1
|
||
SHL DX,1
|
||
SHL DX,1
|
||
JMP M53
|
||
M52: MOV DX,0FFF0H ; use largest value
|
||
IF LDATA
|
||
M53: CMP DX,BX ; check if stack will fit
|
||
JA M55
|
||
ELSE
|
||
M53: CMP DX,BX ; check if stack will fit
|
||
JB M54
|
||
ADD BX,_BASE ; adjust S/P model for statics
|
||
JNC M55
|
||
ENDIF
|
||
M54: MOV DX,OFFSET DGROUP:MEMERR2 ; abort if error in releasing memory
|
||
JMP NEAR PTR XCABT
|
||
M55: CLI
|
||
MOV _TOP,BX ; set top-of-stack
|
||
MOV SP,BX ; set stack pointer
|
||
IF LDATA EQ 0
|
||
MOV AX,DS
|
||
MOV SS,AX
|
||
ENDIF
|
||
MOV _SS,SS
|
||
STI
|
||
ENDIF ; IFDEF PROMEM
|
||
|
||
;
|
||
; Set up memory allocation pointers
|
||
;
|
||
PUSH CX ; save command byte count
|
||
|
||
IFDEF PROMEM ;;; Protected Mode
|
||
; Assume small data model
|
||
MOV AX,SS ; ax=stack segment
|
||
MOV _MBASE+2,AX ; _mbase = top of stack
|
||
mov _mbase, bx ;
|
||
MOV _MNEXT+2,AX ; _mnext = _mbase
|
||
mov _mnext, bx ;
|
||
mov bx, 0fff0h ; Protected mode has a full 64K
|
||
sub bx, _top ; subtract out top of stack
|
||
MOV _MSIZE,BX ; _msize = size of heap pool
|
||
xor ax, ax
|
||
ELSE
|
||
ADD BX,15 ; compute mem pool base segment number
|
||
JC M54
|
||
MOV CL,4
|
||
SHR BX,CL
|
||
MOV AX,SS
|
||
ADD AX,BX
|
||
MOV _MBASE+2,AX
|
||
MOV _MNEXT+2,AX
|
||
MOV BX,ES:2 ; get top segment number
|
||
SUB BX,AX ; compute memory pool size
|
||
JBE M54 ; branch if insufficient memory
|
||
MOV CL,4 ; compute number of bytes
|
||
ROL BX,CL
|
||
MOV AX,BX
|
||
AND AX,15
|
||
AND BX,0FFF0H
|
||
MOV _MSIZE,BX
|
||
ENDIF
|
||
|
||
MOV _MSIZE+2,AX
|
||
|
||
PUSH ES ; reset memory pool
|
||
PUSH SI
|
||
CALL RBRK
|
||
POP SI
|
||
POP ES
|
||
OR AX,AX
|
||
JNZ M54 ; branch if not enough memory
|
||
|
||
if scheme
|
||
IFNDEF PROMEM ;;; Protected Mode - IGNORE
|
||
push si
|
||
call allmem ; allocate the rest of the data segment
|
||
; to C's heap (small model only);
|
||
; later, in "initmem", PCS will get the
|
||
; rest of MSDOS's memory for itself
|
||
pop si
|
||
or ax,ax
|
||
jnz m54
|
||
ENDIF
|
||
endif
|
||
|
||
POP DX ; restore command line length
|
||
;
|
||
; Put return address at top of stack
|
||
;
|
||
IF MSDOS EQ 1
|
||
PUSH ES ; return is to 1st word of prog prefix
|
||
XOR AX,AX
|
||
PUSH AX
|
||
MOV BP,SP ; BP contains stack linkage
|
||
ENDIF
|
||
;
|
||
; copy command line to stack
|
||
;
|
||
MOV CX,DX ; get residual command line length
|
||
SUB SP,CX ; allocate stack space
|
||
DEC SP
|
||
MOV DI,SP
|
||
MOV DX,SP ; save pointer to end of command
|
||
ADD DX,CX
|
||
JCXZ M71 ; skip if no bytes to move
|
||
PUSH DS ; move them
|
||
PUSH ES
|
||
POP DS
|
||
PUSH SS
|
||
POP ES
|
||
CLD
|
||
REP MOVSB
|
||
POP DS
|
||
M71: MOV BYTE PTR SS:[DI],0 ; append null byte
|
||
MOV WORD PTR _ARG,SP ; save arg array pointer
|
||
MOV WORD PTR _ARG+2,SS
|
||
;
|
||
; Move environment to stack for small data models
|
||
;
|
||
ife scheme ; PCS ignores DOS environment strings
|
||
IF LDATA EQ 0
|
||
MOV CX,_XSIZE ; get extended env size
|
||
JCXZ M72 ; branch if no environment
|
||
SUB SP,CX ; allocate stack space
|
||
MOV DI,SP
|
||
PUSH DS ; save important regs
|
||
PUSH ES
|
||
PUSH SI
|
||
PUSH SS ; set stack as destination seg
|
||
POP ES
|
||
MOV SI,WORD PTR _ENV+2
|
||
MOV WORD PTR _ENV,DI ; save relocated env pointer
|
||
MOV WORD PTR _ENV+2,SS
|
||
MOV DS,SI ; set env as source
|
||
XOR SI,SI
|
||
CLD ; move environment
|
||
REP MOVSB
|
||
POP SI ; restore regs and continue
|
||
POP ES
|
||
POP DS
|
||
ENDIF
|
||
endif
|
||
;
|
||
; Build argument vector
|
||
;
|
||
ife scheme ; PCS parses its own command line
|
||
M72: AND SP,0FFFEH ; make stack pointer even
|
||
MOV SI,DX ; prepare to scan command backwards
|
||
XOR CX,CX
|
||
PUSH CX ; push null terminator for argv
|
||
IF LDATA
|
||
PUSH CX
|
||
ENDIF
|
||
MOV BX,SP ; save this null byte address
|
||
M8: CMP SI,WORD PTR _ARG ; skip trailing white space
|
||
JNE M80
|
||
JMP M83
|
||
M80: DEC SI
|
||
CMP BYTE PTR SS:[SI],' '
|
||
JE M8
|
||
CMP BYTE PTR SS:[SI],TAB
|
||
JE M8
|
||
MOV BYTE PTR SS:[SI+1],0 ; install null terminator byte
|
||
MOV AL,0 ; indicate white space scan
|
||
CMP BYTE PTR SS:[SI],'"'
|
||
JNE M81 ; branch if not quoted arg
|
||
CMP SI,WORD PTR _ARG
|
||
JE M81
|
||
CMP BYTE PTR SS:[SI-1],'\'
|
||
JE M810 ; branch if \"
|
||
MOV AL,'"' ; indicate quote scan
|
||
MOV BYTE PTR SS:[SI],0 ; put null in place of quote
|
||
M81: CMP SI,WORD PTR _ARG
|
||
JE M82 ; branch if no more chars
|
||
DEC SI ; position to next char
|
||
CMP BYTE PTR SS:[SI],'"'
|
||
JNE M81C ; branch if not quote
|
||
CMP SI,WORD PTR _ARG
|
||
JE M81C ; branch if no more chars
|
||
CMP BYTE PTR SS:[SI-1],'\'
|
||
JNE M81C ; branch if not \"
|
||
M810: PUSH SI ; squish args to remove \
|
||
M81A: DEC SI
|
||
CMP SI,WORD PTR _ARG
|
||
JE M81B
|
||
MOV AH,SS:[SI-1]
|
||
MOV SS:[SI],AH
|
||
JMP M81A
|
||
M81B: INC WORD PTR _ARG
|
||
POP SI
|
||
JMP M81D
|
||
M81C: OR AL,AL
|
||
JNZ M82A ; branch if not white space scan
|
||
M81D: CMP BYTE PTR SS:[SI],' ' ; find next white space
|
||
JE M81E
|
||
CMP BYTE PTR SS:[SI],TAB
|
||
JNE M81
|
||
M81E: INC SI ; position to start of arg
|
||
IF LDATA
|
||
M82: PUSH SS ; add arg pointer to vector
|
||
PUSH SI
|
||
ELSE
|
||
M82: PUSH SI ; put arg pointer into argv
|
||
ENDIF
|
||
INC CX ; bump arg count
|
||
JMP M8 ; loop till all args done
|
||
M82A: CMP BYTE PTR SS:[SI],'"' ; come here to find starting quote
|
||
JNE M81 ; branch if not quote
|
||
MOV BYTE PTR SS:[SI],' ' ; replace it with white space
|
||
JMP M81E ; go save arg pointer
|
||
;
|
||
; Construct argv[0]
|
||
;
|
||
M83: CMP _DOS,2
|
||
JLE M84
|
||
MOV AX,WORD PTR _ENV ; use env trailer for DOS 3
|
||
ADD AX,_ESIZE
|
||
INC AX
|
||
INC AX
|
||
IF LDATA
|
||
PUSH [WORD PTR _ENV+2]
|
||
ENDIF
|
||
PUSH AX
|
||
JMP M85
|
||
IF LDATA
|
||
M84: PUSH SS ; use null byte for DOS 2
|
||
PUSH BX
|
||
ELSE
|
||
M84: PUSH BX ; use null byte for DOS 2
|
||
ENDIF
|
||
;
|
||
; Save argv information
|
||
;
|
||
M85: INC CX ; save arg count
|
||
MOV _ARGC,CX
|
||
IF LDATA
|
||
MOV WORD PTR _ARGV,SP ; save arg vector pointer
|
||
MOV WORD PTR _ARGV+2,SS
|
||
ELSE
|
||
MOV _ARGV,SP ; save arg vector pointer
|
||
ENDIF
|
||
|
||
else ; PCS parses its own command line
|
||
|
||
; -------------------------------------------------
|
||
; The PCS command line parser
|
||
; -------------------------------------------------
|
||
|
||
; The PCS command line can look as follows:
|
||
;
|
||
; PCS (... ...) atom atom (... ...) "string" atom ...
|
||
;
|
||
; which parses into PCS-INITIAL-ARGUMENTS as:
|
||
;
|
||
; ( "(... ...)" "atom" "atom" "(... ...)" "\"string\"" "atom" ... )
|
||
;
|
||
; Each command line argument is either an atom, list, or string.
|
||
; Each is treated as one argument for the argv vector, and each is
|
||
; converted to a string which becomes an element of PCS-INITIAL-ARGUMENTS.
|
||
;
|
||
; The command line parser is not a Scheme reader. It looks for blank-separated
|
||
; tokens, where a token can start with a ( and end with the matching ),
|
||
; start and end with a ", or just be a sequence of nonblanks. Backslashed
|
||
; delimiters are skipped over as you'd expect. We don't bother with | since
|
||
; that is a special character to DOS. The blanks between tokens are important
|
||
; since the parser replaces them with nulls to get things set up for C's
|
||
; argv vector, so situations like ...)(... won't be parsed correctly.
|
||
; The parser is implemented as a finite state automaton (FSA).
|
||
;
|
||
; The first command line argument has special meaning but that is
|
||
; handled in "smain.c".
|
||
;
|
||
|
||
; On entry to this section:
|
||
; ES = SS (but we don't care)
|
||
; SP = ptr to start of cmdline
|
||
; DX = ptr to cmdline eos (cmdline on stack with eos at its end)
|
||
|
||
M72:
|
||
push DS ; ES <- DS
|
||
pop ES
|
||
xor AX,AX ; AH is state, AL is current char
|
||
push AX ; push 0 at front of cmdline
|
||
; (we're going to parse backwards)
|
||
and SP,0FFFEH ; make SP even
|
||
mov SI,DX ; SI is index into cmdline
|
||
xor DX,DX ; DH is paren counter, DL is argc
|
||
cld
|
||
M72loop: dec SI ; get the next char from cmdline
|
||
cmp byte ptr SS:[SI-1],'\' ; is it singly escaped?
|
||
jne M72a ; no, jump
|
||
dec SI ; yes, back up to escape char
|
||
M72a: mov AL,SS:[SI]
|
||
mov CX,scan_size ; look it up in char table
|
||
mov DI,offset scan_table
|
||
repne scasb ; put into CX the "char class" for
|
||
; indexing into state table
|
||
mov AL,bytes_per_state ; do 2-D subscript into state table
|
||
mul AH ; ... row
|
||
shl CX,1 ; ... col
|
||
add AX,CX
|
||
mov BX,AX ; (BH=0 since subscript small enough)
|
||
mov AH,state[BX] ; get next state
|
||
mov BL,state+1[BX] ; do action routine
|
||
add BX,offset actions
|
||
jmp BX
|
||
|
||
actions label near ; start action routines ---------->
|
||
ar_out2: inc SI ; 1 char past token, back up
|
||
push SI ; push next argv
|
||
dec SI
|
||
inc DL ; incr argc
|
||
ar_skip: mov byte ptr SS:[SI],0 ; output a null char
|
||
ar_decr: jmp short M72loop
|
||
ar_lpar: dec DH ; decr paren count
|
||
js ar_err ; wrong paren to start with
|
||
jnz short M72loop ; on an inner paren, keep looking
|
||
mov AH,0 ; override state in table
|
||
ar_out1: push SI ; matched delimiter, push next argv
|
||
inc DL ; incr argc
|
||
jmp short M72loop
|
||
ar_rpar: inc DH ; incr paren count
|
||
jmp short M72loop
|
||
ar_err: mov DX,offset dgroup:parserr ; abort on error in cmdline parsing
|
||
jmp near ptr XCABT
|
||
ar_out_end: inc SI ; 1 char past token, back up
|
||
push SI ; push next argv
|
||
dec SI
|
||
inc DL ; incr argc
|
||
ar_end: ; end action routines <----------
|
||
|
||
continue: xor AX,AX ; argv[0] = \0
|
||
push AX
|
||
inc DL ; incr argc
|
||
mov _argv,SP ; save address of argv
|
||
mov DH,0
|
||
push DX
|
||
mov _argc,DX ; save argc
|
||
|
||
endif
|
||
|
||
;
|
||
; Build environment vector
|
||
;
|
||
ife scheme ; PCS ignores DOS environment strings
|
||
XOR AX,AX ; push null terminating pointer
|
||
PUSH AX
|
||
IF LDATA
|
||
PUSH AX
|
||
ENDIF
|
||
MOV AX,_ENVC ; allocate stack space for vector
|
||
ADD AX,AX
|
||
JZ M87 ; branch if null environment
|
||
SUB SP,AX
|
||
IF LDATA
|
||
SUB SP,AX
|
||
ENDIF
|
||
MOV SI,SP ; scan environment
|
||
LES DI,_ENV
|
||
XOR AX,AX
|
||
MOV CX,7FFFH
|
||
MOV DX,_ENVC
|
||
CLD
|
||
M86: MOV SS:[SI],DI
|
||
ADD SI,2
|
||
IF LDATA
|
||
MOV SS:[SI],ES
|
||
ADD SI,2
|
||
ENDIF
|
||
DEC DX
|
||
JLE M87
|
||
REPNE SCASB
|
||
JMP M86
|
||
M87: MOV WORD PTR environ,sp
|
||
IF LDATA
|
||
MOV WORD PTR environ+2,SS
|
||
ENDIF
|
||
endif
|
||
;
|
||
; initialize 8087 numeric data processor
|
||
;
|
||
IFNDEF PROMEM ;;; Protected Mode - IGNORE
|
||
FNINIT ; reset
|
||
FNSTSW _NDPSW ; get status
|
||
MOV AX,100 ; this is just for delay
|
||
MOV DX,AX
|
||
IMUL DX
|
||
TEST _NDPSW,0B8BFH ; 8087 will reset all these
|
||
JNZ M9
|
||
INC _NDP ; indicate ndp present
|
||
ENDIF
|
||
;
|
||
; set up args for _main and call it
|
||
;
|
||
M9: MOV _SP,SP ; save stack pointer reset value
|
||
PUSH DS ; make ES same as DS
|
||
POP ES
|
||
CALL _MAIN ; call C main
|
||
IF MSDOS EQ 1
|
||
CMP _DOS,2
|
||
JL M91 ; branch if DOS 1
|
||
ENDIF
|
||
MOV AX,4C00H ; exit with return code of 0
|
||
INT 21H
|
||
IF MSDOS EQ 1
|
||
M91: MOV SP,BP ;restore ptr to far return
|
||
RET ;return to MS-DOS
|
||
ENDIF
|
||
;**
|
||
;
|
||
; name GETNUM -- get a number from the command line
|
||
;
|
||
; description This function is used internally by the start-up routine
|
||
; while processing the command line.
|
||
;
|
||
;**
|
||
ife scheme
|
||
GETNUM PROC NEAR
|
||
XOR BX,BX ; reset accumulator
|
||
NUM1: DEC CX ; advance to next character
|
||
JZ NUM3 ; branch if end of command line
|
||
INC SI
|
||
MOV AL,ES:[SI]
|
||
CMP AL,'0'
|
||
JL NUM3 ; return if not decimal digit
|
||
CMP AL,'9'
|
||
JG NUM3 ; return if not decimal digit
|
||
SUB AL,'0' ; multiply accumulator by 10
|
||
ADD BX,BX
|
||
JC NUM2
|
||
MOV DX,BX
|
||
ADD BX,BX
|
||
JC NUM2
|
||
ADD BX,BX
|
||
JC NUM2
|
||
ADD BX,DX
|
||
JC NUM2
|
||
XOR AH,AH ; add this digit
|
||
ADD BX,AX
|
||
JNC NUM1 ; loop till done
|
||
NUM2: RET
|
||
NUM3: CLC ; clear carry to indicate no error
|
||
RET
|
||
GETNUM ENDP
|
||
endif
|
||
|
||
;**
|
||
;
|
||
; name XCABT -- Ignominious abort
|
||
;
|
||
; description This area is entered by direct jump with a message
|
||
; pointer in DS:DX. It sends the message to the
|
||
; console via DOS function 9 and then aborts.
|
||
;
|
||
ENTRY XCABT
|
||
MOV AH,9 ; print error message
|
||
INT 21H
|
||
MOV ES,_PSP+2
|
||
IF MSDOS EQ 1
|
||
CMP _DOS,2
|
||
JL A1
|
||
ENDIF
|
||
MOV AX,4C01H
|
||
INT 21H
|
||
IF MSDOS EQ 1
|
||
A1: PUSH ES
|
||
XOR AX,AX
|
||
PUSH AX
|
||
RET
|
||
ENDIF
|
||
|
||
if scheme
|
||
;
|
||
; Scheme wrapup - the C fn "exit" calls "_exit" which calls this hook routine
|
||
;
|
||
public xcexit
|
||
DOS equ 21h ; MSDOS function request interrupt
|
||
xcexit proc near
|
||
|
||
mov bp,sp ; don't lose our return address to _exit
|
||
|
||
IFDEF EXPMEM
|
||
call rlsexp ; Release Expanded Memory (if any)
|
||
ENDIF
|
||
|
||
call xli_term ; release all external programs
|
||
; allocated under XLI
|
||
IFNDEF PROMEM
|
||
push ES ; return Scheme heap to DOS
|
||
mov AH,49h
|
||
mov ES,first_dos
|
||
int DOS
|
||
pop ES
|
||
jnc MA
|
||
MOV DX,OFFSET DGROUP:MEMERR2 ; abort if error in releasing memory
|
||
JMP NEAR PTR XCABT
|
||
ENDIF
|
||
MA: call rsttimer ; Reset the timer interrupt, if necessary
|
||
call unfixint ; Restore the keyboard "patch" (MWH2)
|
||
call zcuron ; Turn cursor back on
|
||
mov AX,15 ; Load character attribute = white,enable
|
||
push AX
|
||
mov AX,80 ; 80 Columns
|
||
push AX
|
||
mov AX,25 ; 25 Rows
|
||
push AX
|
||
xor AX,AX ; Origin at 0,0
|
||
push AX
|
||
push AX
|
||
call zclear ; Clear the screen
|
||
xor AX,AX
|
||
push AX
|
||
push AX
|
||
inc cur_off ; keep cursor off for EGA modes
|
||
call zputcur ; Put cursor at 0,0
|
||
mov sp,bp ; reestablish the return address to _exit
|
||
ret
|
||
xcexit endp
|
||
|
||
endif
|
||
|
||
CXINIT ENDP
|
||
|
||
|
||
IFDEF PROMEM ;;; Protected Mode
|
||
|
||
;;; Reset heap break point (unix lingo)
|
||
;;;
|
||
;;; Set _mbase = _mnext = DS:_top
|
||
;;; Set _pool = 0:0, _melt = 0:0
|
||
public rbrk
|
||
RBRK PROC NEAR
|
||
push bp
|
||
mov bp,sp
|
||
;
|
||
; set up memory allocation pointers
|
||
;
|
||
mov bx, _top
|
||
add bx, 0fh ; round up to para
|
||
and bx, 0fff0h
|
||
mov _tsize, bx ; total size in paras
|
||
|
||
mov ax,ds
|
||
mov _mbase+2,ax ; long ptr to heap base
|
||
mov _mbase, bx
|
||
mov _mnext+2,ax ; long ptr to heap top
|
||
mov _mnext, bx
|
||
|
||
|
||
xor ax,ax
|
||
mov [_msize+2],ax ; number of bytes left in pool
|
||
mov _msize, ax
|
||
|
||
; Clear the 2nd level pool
|
||
mov [_melt],ax
|
||
mov [_pool],ax
|
||
mov [_melt+02],ax
|
||
mov [_pool+02],ax
|
||
pop bp
|
||
ret
|
||
RBRK ENDP
|
||
|
||
;
|
||
|
||
public lsbrk
|
||
lsbrk PROC NEAR
|
||
push bp
|
||
mov bp,sp
|
||
mov cx,[bp+6] ; requested size (high)
|
||
mov dx,[bp+4] ; requested size (low)
|
||
or cx, cx
|
||
jnz lsbrk_err1
|
||
mov cx, 0FFF0h
|
||
sub cx, _mnext ; available bytes
|
||
cmp cx, dx
|
||
jb lsbrk_err1
|
||
mov ax, _mnext ; next location in pool
|
||
add _mnext, dx ; bump by requested amount
|
||
mov bx, ax
|
||
pop bp
|
||
ret
|
||
|
||
lsbrk_err1:
|
||
xor ax,ax
|
||
mov bx, ax
|
||
pop bp
|
||
ret
|
||
lsbrk ENDP
|
||
|
||
|
||
COMMENT %
|
||
;; _RBRK (int desire)
|
||
;; desire is initial heap in paras
|
||
;; return 0 if OK, ffff otherwise
|
||
;;
|
||
public _rbrk
|
||
;;extrn _model:word
|
||
_rbrk proc near
|
||
push bp
|
||
mov bp,sp
|
||
push es
|
||
;mov word ptr [_oserr],_model
|
||
|
||
mov bx, 0 ;; what is really needed here? 128K?
|
||
mov [_tsize],bx ;; really total size with heap
|
||
|
||
mov ax,[_mbase] ;; reset heap allocator ptrs
|
||
mov [_mnext],ax
|
||
mov ax,[_mbase+02]
|
||
mov [_mnext+02] ,ax
|
||
xor ax,ax
|
||
pop es
|
||
pop bp
|
||
ret
|
||
_rbrk_error1:
|
||
mov [_oserr],ax
|
||
_rbrk_error:
|
||
mov ax,0ffffh
|
||
pop es
|
||
pop bp
|
||
ret
|
||
_rbrk endp
|
||
%
|
||
ENDIF
|
||
;**
|
||
;
|
||
; Dummy segment to establish top of program for small program models
|
||
;
|
||
IF S8086
|
||
IF COM
|
||
TAIL SEGMENT WORD 'PROG'
|
||
DW -1
|
||
TAIL ENDS
|
||
ENDIF
|
||
ENDIF
|
||
|
||
IF S8086
|
||
PROG ENDS
|
||
ENDIF
|
||
IF D8086
|
||
CODE ENDS
|
||
ENDIF
|
||
IF P8086
|
||
_CODE ENDS
|
||
OLDCODE SEGMENT BYTE ; This catches Version 2 code
|
||
DW 0CACAH
|
||
OLDCODE ENDS
|
||
ENDIF
|
||
IF L8086
|
||
_PROG ENDS
|
||
OLDPROG SEGMENT BYTE ; This catches Version 2 code
|
||
DW 0CACAH
|
||
OLDPROG ENDS
|
||
ENDIF
|
||
PAGE
|
||
;**
|
||
;
|
||
; DGROUP includes the segments named DATA, UDATA, and XSTACK. The startup
|
||
; routine initializes DS to point to this group, and DS must then be pre-
|
||
; served throughout the program. The segments within DGROUP are defined
|
||
; as follows:
|
||
;
|
||
; DATA => Contains all static (local and global) initialized items.
|
||
; UDATA => Contains all static (local and global) uninitialized items.
|
||
; XSTACK => Stack for the startup routine.
|
||
;
|
||
; During the startup routine, the initial stack (XSTACK) is replaced with
|
||
; one that has the correct size for program execution. This size is
|
||
; determined by examining the command line and the _STACK global item. Then
|
||
; for the S and P memory models, the stack is set up relative to DGROUP (i.e.
|
||
; stack items can addressed via DS). For the D and L models, the stack
|
||
; segment stands alone and can be up to 64K bytes.
|
||
;
|
||
; The heap (i.e. the space used by the memory allocator) resides above the
|
||
; stack and is also initialized by the startup routine. Any space not
|
||
; immediately needed for the heap (as defined by _MNEED) is returned to DOS.
|
||
;
|
||
; At the end of the startup routine, memory is organized in the following
|
||
; sequence:
|
||
;
|
||
; -- code --
|
||
; -- DATA --
|
||
; -- UDATA --
|
||
; -- stack --
|
||
; -- heap --
|
||
;
|
||
; FOR PROPER OPERATION OF THE STANDARD MEMORY ALLOCATOR, THIS SEQUENCE IS
|
||
; EXTREMELY IMPORTANT. IF YOU TAMPER WITH THE STARTUP ROUTINE OR INTRODUCE
|
||
; SEGMENTS AND CLASSES THAT DO NOT FOLLOW LATTICE CONVENTIONS, CHECK THE
|
||
; LOAD MAP CAREFULLY.
|
||
;
|
||
;**
|
||
|
||
|
||
;**
|
||
;
|
||
; Initialized data
|
||
;
|
||
DATA SEGMENT PARA PUBLIC 'DATA'
|
||
EXTRN _STACK:WORD
|
||
EXTRN _MNEED:DWORD
|
||
|
||
IFDEF PROMEM ;;; Protected Mode
|
||
EXTRN _POOL:WORD
|
||
EXTRN _MELT:WORD
|
||
ENDIF
|
||
|
||
PUBLIC _MODEL,_VER,_TOP,_BASE,_PSP,_MBASE,_MNEXT,_MSIZE,_DSIZE,_PSIZE
|
||
PUBLIC _ENV,_DOS,_TSIZE,_ESIZE,_XSIZE,_SS,_SP,_NDP,_NDPSW,_NDPCW
|
||
PUBLIC _FPA,_FPERR,_OSERR,_SIGFPE,_ARGV,_ARGC,_ENVC,environ
|
||
if scheme
|
||
extrn _onexit:word ; our hook into C exit fn
|
||
extrn cur_off:byte
|
||
public _paras,first_pa,first_dos
|
||
endif
|
||
IF S8086
|
||
_MODEL DW 0
|
||
ENDIF
|
||
IF P8086
|
||
_MODEL DW 1
|
||
ENDIF
|
||
IF D8086
|
||
_MODEL DW 2
|
||
ENDIF
|
||
IF L8086
|
||
_MODEL DW 3
|
||
ENDIF
|
||
_VER DB "LC 3.00",0
|
||
_DOS DB 0 ; DOS major version number
|
||
DB 0 ; DOS minor version number
|
||
_SS DW 0 ; stack segment number
|
||
_SP DW 0 ; SP reset value
|
||
_TOP DW 0 ; top of stack (relative to SS)
|
||
_BASE DW OFFSET DGROUP:SBASE ; base of stack (relative to DS)
|
||
_PSP DW 0 ; program segment prefix pointer
|
||
DW 0
|
||
_MBASE DW 0 ; base of memory pool
|
||
DW 0
|
||
_MNEXT DW 0 ; next available memory location
|
||
DW 0
|
||
_MSIZE DW 0 ; number of bytes left in pool
|
||
DW 0
|
||
_TSIZE DW 0 ; total size in paragraphs
|
||
_PSIZE DD 0 ; size of program in bytes
|
||
_DSIZE DW OFFSET DGROUP:SBASE ; size of static data in bytes
|
||
DW 0
|
||
IF LDATA
|
||
_ARGV DD 0 ; argument vector pointer
|
||
environ DD 0 ; environment vector pointer
|
||
ELSE
|
||
DD 0 ; *** DOS 2.00 trashing bug ***
|
||
_ARGV DW 0 ; argument vector pointer
|
||
environ DW 0 ; environment vector pointer
|
||
ENDIF
|
||
_ARGC DW 0 ; argument count
|
||
_ENVC DW 0 ; environment count
|
||
_ARG DD 0 ; far pointer to original arg array
|
||
_ENV DD 0 ; far pointer to original env array
|
||
_ESIZE DW 0 ; environment size in bytes
|
||
_XSIZE DW 0 ; extended env size in bytes
|
||
_FPA DQ 0 ; floating point accumulator
|
||
_FPERR DW 0 ; floating point error code
|
||
_NDP DB 0 ; non-zero if 8087 is installed
|
||
_NDPSW DW 0FFFFH ; 8087 status word
|
||
_NDPCW DW 0 ; 8087 control word
|
||
_OSERR DW 0 ; DOS error code
|
||
IF LPROG
|
||
_SIGFPE DD 0 ; Floating point error signal
|
||
ELSE
|
||
_SIGFPE DW 0 ; Floating point error signal
|
||
ENDIF
|
||
|
||
if scheme
|
||
|
||
_PARAS DW 0 ; # of paragraphs of memory available
|
||
FIRST_PA DW 0 ; seg# of first para. actually used for
|
||
; Scheme heap
|
||
FIRST_DOS DW 0 ; seg# of first para. from DOS for
|
||
; Scheme heap
|
||
|
||
; the characters sought by PCS's scanner
|
||
scan_table db 0,' ()"',0
|
||
scan_size equ $-scan_table
|
||
|
||
; the FSA transition table used to parse PCS's command line
|
||
; (This once included handling for vertical-bar delimited symbols, but
|
||
; DOS's use of | rendered it useless, so it was removed.)
|
||
|
||
start_state equ 0
|
||
list_state equ 1
|
||
atom_state equ 2
|
||
strg_state equ 3
|
||
end_state equ 4
|
||
err_state equ 5
|
||
|
||
state label byte
|
||
; initial state
|
||
db atom_state,ar_decr-actions ; any char
|
||
db strg_state,ar_decr-actions ; "
|
||
db list_state,ar_rpar-actions ; )
|
||
db err_state,ar_err-actions ; (
|
||
db start_state,ar_skip-actions ; blank
|
||
db end_state,ar_end-actions ; null
|
||
bytes_per_state equ $-state
|
||
; in list
|
||
db list_state,ar_decr-actions
|
||
db list_state,ar_decr-actions
|
||
db list_state,ar_rpar-actions
|
||
db list_state,ar_lpar-actions ; start_state also possible, see ar_lpar
|
||
db list_state,ar_decr-actions
|
||
db err_state,ar_err-actions
|
||
; in atom
|
||
db atom_state,ar_decr-actions
|
||
db atom_state,ar_decr-actions
|
||
db atom_state,ar_decr-actions
|
||
db atom_state,ar_decr-actions
|
||
db start_state,ar_out2-actions
|
||
db end_state,ar_out_end-actions
|
||
; in string
|
||
db strg_state,ar_decr-actions
|
||
db start_state,ar_out1-actions
|
||
db strg_state,ar_decr-actions
|
||
db strg_state,ar_decr-actions
|
||
db strg_state,ar_decr-actions
|
||
db err_state,ar_err-actions
|
||
|
||
; The exit and error states are not explicitly represented
|
||
; in the table, action routines deal with them.
|
||
|
||
endif
|
||
|
||
STKERR DB "Invalid stack size",0DH,0AH,"$"
|
||
MEMERR DB "Insufficient memory",0DH,0AH,"$"
|
||
if scheme
|
||
memerr2 db "Error in returning memory to DOS",0dh,0ah,"$"
|
||
parserr db "Error in parsing command line",0dh,0ah,"$"
|
||
endif
|
||
|
||
IF MSDOS EQ 1
|
||
PUBLIC _INAME,_ONAME
|
||
_INAME DB 32 DUP(0) ; input file name
|
||
_ONAME DB 32 DUP(0) ; output file name
|
||
NAMERR DB "Invalid I/O redirection",0DH,0AH,"$"
|
||
ENDIF
|
||
DATA ENDS
|
||
|
||
|
||
;**
|
||
;
|
||
; Uninitialized data
|
||
;
|
||
UDATA SEGMENT PUBLIC 'DATA'
|
||
UDATA ENDS
|
||
|
||
;**
|
||
;
|
||
; The stack segment is included to prevent the warning from the
|
||
; linker, and also to define the base (lowest address) of the stack.
|
||
;
|
||
STKRSV EQU 128 ; reserved stack size
|
||
STKMIN EQU 512 ; minimum run-time stack size
|
||
IF COM
|
||
XSTACK SEGMENT 'DATA'
|
||
ELSE
|
||
XSTACK SEGMENT STACK 'DATA'
|
||
ENDIF
|
||
SBASE DB STKRSV DUP (?)
|
||
XSTACK ENDS
|
||
|
||
END CXINIT
|
||
|