; ASM.ASM
;
; This is a simple, single line, mini-assembler for an earlier
; version of the x86 processor. It converts strings containing valid 
; (old) x86 assembly code into a sequence of hex values corresponding 
; to the opcode and operands for the given instruction.

		.xlist
		include 	stdlib.a
		matchfuncs
		includelib	stdlib.lib
		.list




dseg		segment	para public 'data'


; Some sample statements to assemble:

Str1		byte	"load ax, 0",0
Str2		byte	"load ax, bx",0
Str3		byte	"load ax, ax",0
Str4		byte	"add ax, 15",0
Str5		byte	"sub ax, [bx]",0
Str6		byte	"store bx, [1000]",0
Str7		byte	"load bx, 2000[bx]",0
Str8		byte	"goto 3000",0
Str9		byte	"iflt ax, bx, 100",0
Str10		byte	"halt",0
Str11		byte	"This is illegal",0
Str12		byte	"load ax, store",0
Str13		byte	"store ax, 1000",0
Str14		byte	"ifeq ax, 0, 0",0



; Variables used by the assembler.

AsmConst	dw	0
AsmOpcode	db	0
AsmOprnd1	db	0
AsmOprnd2	db	0



		include	stdsets.a	;Bring in the standard char sets.



; Patterns for the assembler:

; Pattern is (
;	       (load|store|add|sub) reg "," operand |
;	       (ifeq|iflt|ifgt) reg1 "," reg2 "," const |
;	       (get|put) operand |
;	       goto operand |
;	       halt
;	     )
;
; With a few semantic additions (e.g., cannot store to a const).

InstrPat	pattern	{spancset, WhiteSpace,Grp1,Grp1}

Grp1		pattern	{sl_Match2,Grp1Strs, Grp2 ,Grp1Oprnds}
Grp1Strs	pattern	{TryLoad,,Grp1Store}
Grp1Store	pattern	{TryStore,,Grp1Add}
Grp1Add		pattern	{TryAdd,,Grp1Sub}
Grp1Sub		pattern	{TrySub}

; Patterns for the LOAD, STORE, ADD, and SUB instructions.

LoadPat		pattern	{MatchStr,LoadInstr2}
LoadInstr2	byte	"LOAD",0

StorePat	pattern	{MatchStr,StoreInstr2}
StoreInstr2	byte	"STORE",0

AddPat		pattern	{MatchStr,AddInstr2}
AddInstr2	byte	"ADD",0

SubPat		pattern	{MatchStr,SubInstr2}
SubInstr2	byte	"SUB",0

; Patterns for the group one (LOAD/STORE/ADD/SUB) instruction operands:

Grp1Oprnds	pattern	{spancset,WhiteSpace,Grp1reg,Grp1reg}
Grp1Reg		pattern	{MatchReg,AsmOprnd1,,Grp1ws2}
Grp1ws2		pattern	{spancset,WhiteSpace,Grp1Comma,Grp1Comma}
Grp1Comma	pattern	{MatchChar,',',0,Grp1ws3}
Grp1ws3		pattern	{spancset,WhiteSpace,Grp1Op2,Grp1Op2}
Grp1Op2		pattern	{MatchGen,,,EndOfLine}
EndOfLine	pattern	{spancset,WhiteSpace,NullChar,NullChar}
NullChar	pattern	{EOS}

Grp1Op2Reg	pattern	{MatchReg,AsmOprnd2}



; Patterns for the group two instructions (IFEQ, IFLT, IFGT):

Grp2		pattern	{sl_Match2,Grp2Strs, Grp3 ,Grp2Oprnds}
Grp2Strs	pattern	{TryIFEQ,,Grp2IFLT}
Grp2IFLT	pattern	{TryIFLT,,Grp2IFGT}
Grp2IFGT	pattern	{TryIFGT}

Grp2Oprnds	pattern	{spancset,WhiteSpace,Grp2reg,Grp2reg}
Grp2Reg		pattern	{MatchReg,AsmOprnd1,,Grp2ws2}
Grp2ws2		pattern	{spancset,WhiteSpace,Grp2Comma,Grp2Comma}
Grp2Comma	pattern	{MatchChar,',',0,Grp2ws3}
Grp2ws3		pattern	{spancset,WhiteSpace,Grp2Reg2,Grp2Reg2}
Grp2Reg2	pattern	{MatchReg,AsmOprnd2,,Grp2ws4}
Grp2ws4		pattern	{spancset,WhiteSpace,Grp2Comma2,Grp2Comma2}
Grp2Comma2	pattern	{MatchChar,',',0,Grp2ws5}
Grp2ws5		pattern	{spancset,WhiteSpace,Grp2Op3,Grp2Op3}
Grp2Op3		pattern	{ConstPat,,,EndOfLine}


; Patterns for the IFEQ, IFLT, and IFGT instructions.

IFEQPat		pattern	{MatchStr,IFEQInstr2}
IFEQInstr2	byte	"IFEQ",0

IFLTPat		pattern	{MatchStr,IFLTInstr2}
IFLTInstr2	byte	"IFLT",0

IFGTPat		pattern	{MatchStr,IFGTInstr2}
IFGTInstr2	byte	"IFGT",0


; Grp3 Patterns:

Grp3		pattern	{sl_Match2,Grp3Strs, Grp4 ,Grp3Oprnds}
Grp3Strs	pattern	{TryGet,,Grp3Put}
Grp3Put		pattern	{TryPut,,Grp3GOTO}
Grp3Goto	pattern	{TryGOTO}


; Patterns for the GET and PUT instructions.

GetPat		pattern	{MatchStr,GetInstr2}
GetInstr2	byte	"GET",0

PutPat		pattern	{MatchStr,PutInstr2}
PutInstr2	byte	"PUT",0

GOTOPat		pattern	{MatchStr,GOTOInstr2}
GOTOInstr2	byte	"GOTO",0



; Patterns for the group three (PUT/GET/GOTO) instruction operands:

Grp3Oprnds	pattern	{spancset,WhiteSpace,Grp3Op,Grp3Op}
Grp3Op		pattern	{MatchGen,,,EndOfLine}


; Patterns for the group four instruction (HALT).

Grp4		pattern	{TryHalt,,,EndOfLine}

HaltPat		pattern	{MatchStr,HaltInstr2}
HaltInstr2	byte	"HALT",0




; Patterns to match the four non-register addressing modes:

BXIndrctPat	pattern	{MatchStr,BXIndrctStr}
BXIndrctStr	byte	"[BX]",0

BXIndexedPat	pattern	{ConstPat,,,BXIndrctPat}

DirectPat	pattern	{MatchChar,'[',,DP2}
DP2		pattern	{ConstPat,,,DP3}
DP3		pattern	{MatchChar,']'}

ImmediatePat	pattern	{ConstPat}

; Pattern to match a hex constant:

HexConstPat	pattern	{Spancset, xdigits}




dseg		ends


cseg		segment	para public 'code'
		assume	cs:cseg, ds:dseg

store		macro	Where, What
		push	ds
		push	ax
		mov	ax, seg Where
		mov	ds, ax
		mov	Where, What
		pop	ax
		pop	ds
		endm


; Pattern matching routines for the assembler.

; Compare against the "LOAD" string.

TryLoad		proc	far
		push	dx
		push	si
		ldxi    LoadPat
		match2
		jnc	NoTLMatch

		store	AsmOpcode, 0

NoTLMatch:	pop	si
		pop	dx
		ret
TryLoad		endp



; Compare against the "STORE" string.

TryStore	proc	far
		push	dx
		push	si
		ldxi    StorePat
		match2
		jnc	NoTSMatch
		store	AsmOpcode, 1

NoTSMatch:	pop	si
		pop	dx
		ret
TryStore	endp


; Compare against the "ADD" string.

TryAdd		proc	far
		push	dx
		push	si
		ldxi	AddPat
		match2
		jnc	NoTAMatch
		store	AsmOpcode, 2

NoTAMatch:	pop	si
		pop	dx
		ret
TryAdd		endp


; Compare against the "SUB" string.

TrySub		proc	far
		push	dx
		push	si
		ldxi	SubPat
		match2
		jnc	NoTMMatch
		store	AsmOpcode, 3

NoTMMatch:	pop	si
		pop	dx
		ret
TrySub		endp



; Compare against the "IFEQ" string.

TryIFEQ		proc	far
		push	dx
		push	si
		ldxi	IFEQPat
		match2
		jnc	NoIEMatch
		store	AsmOpcode, 4

NoIEMatch:	pop	si
		pop	dx
		ret
TryIFEQ		endp



; Compare against the "IFLT" string.

TryIFLT		proc	far
		push	dx
		push	si
		ldxi	IFLTPat
		match2
		jnc	NoILMatch
		store	AsmOpcode, 5

NoILMatch:	pop	si
		pop	dx
		ret
TryIFLT		endp


; Compare against the "IFGT" string.

TryIFGT		proc	far
		push	dx
		push	si
		ldxi	IFGTPat
		match2
		jnc	NoIGMatch
		store	AsmOpcode, 6

NoIGMatch:	pop	si
		pop	dx
		ret
TryIFGT		endp




; Compare against the "GET" string.

TryGET		proc	far
		push	dx
		push	si
		ldxi	GetPat
		match2
		jnc	NoGMatch
		store	AsmOpcode, 7
		store	AsmOprnd1, 2

NoGMatch:	pop	si
		pop	dx
		ret
TryGET		endp




; Compare against the "PUT" string.

TryPut		proc	far
		push	dx
		push	si
		ldxi	PutPat
		match2
		jnc	NoPMatch
		store	AsmOpcode, 7
		store	AsmOprnd1, 3

NoPMatch:	pop	si
		pop	dx
		ret
TryPUT		endp




; Compare against the "GOTO" string.

TryGOTO		proc	far
		push	dx
		push	si
		ldxi	GOTOPat
		match2
		jnc	NoGMatch
		store	AsmOpcode, 7
		store	AsmOprnd1, 1

NoGMatch:	pop	si
		pop	dx
		ret
TryGOTO		endp




; Compare against the "HALT" string.

TryHalt		proc	far
		push	dx
		push	si
		ldxi	HaltPat
		match2
		jnc	NoHMatch
		store	AsmOpcode, 7
		store	AsmOprnd1, 0
		store	AsmOprnd2, 0

NoHMatch:	pop	si
		pop	dx
		ret
TryHALT		endp




; MatchReg checks to see if we've got a valid register value.  On entry,
; DS:SI points at the location to store the byte opcode (0, 1, 2, or 3) for
; a reasonable register (AX, BX, CX, or DX);  ES:DI points at the string
; containing (hopefully) the register operand, and CX points at the last
; location plus one we can check in the string.
;
; On return, Carry=1 for success, 0 for failure.  ES:AX must point beyond
; the characters which make up the register if we have a match.

MatchReg	proc	far

; ES:DI Points at two characters which should be AX/BX/CX/DX.  Anything
; else is an error.

		cmp	byte ptr es:1[di], 'X'	;Everyone needs this
		jne	BadReg
		xor	ax, ax			;886 "AX" register code.
		cmp	byte ptr es:[di], 'A'	;AX?
		je	GoodReg
		inc	ax
		cmp	byte ptr es:[di], 'B'	;BX?
		je	GoodReg
		inc	ax
		cmp	byte ptr es:[di], 'C'	;CX?
		je	GoodReg
		inc	ax
		cmp	byte ptr es:[di], 'D'	;DX?
		je	GoodReg
BadReg:		clc
		mov	ax, di
		ret

GoodReg:
		mov	ds:[si], al		;Save register opcode.
		lea	ax, 2[di]		;Skip past register.
		cmp	ax, cx			;Be sure we didn't go
		ja	BadReg			; too far.
		stc
		ret
MatchReg	endp



; MatchGen-	Matches a general addressing mode.  Stuffs the appropriate
;		addressing mode code into AsmOprnd2.  If a 16-bit constant
;		is required by this addressing mode, this code shoves that
;		into the AsmConst variable.

MatchGen	proc	far
		push	dx
		push	si

		ldxi	Grp1Op2Reg
		match2
		jc	MGDone

		ldxi	BXIndrctPat
		match2
		jnc	TryBXIndexed
		store	AsmOprnd2, 4
		jmp	MGDone

TryBXIndexed:
		ldxi	BXIndexedPat
		match2
		jnc	TryDirect
		store	AsmOprnd2, 5
		jmp	MGDone

TryDirect:
		ldxi	DirectPat
		match2
		jnc	TryImmediate
		store	AsmOprnd2, 6
		jmp	MGDone

TryImmediate:
		ldxi	ImmediatePat
		match2
		jnc	MGDone
		store	AsmOprnd2, 7

MGDone:
		pop	si
		pop	dx
		ret
MatchGen	endp



; ConstPat-	Matches a 16-bit hex constant.  If it matches, it converts
;		the string to an integer and stores it into AsmConst.

ConstPat	proc	far
		push	dx
		push	si
		ldxi	HexConstPat
		match2
		jnc	CPDone

		push	ds
		push	ax
		mov	ax, seg AsmConst
		mov	ds, ax
		atoh
		mov	AsmConst, ax
		pop	ax
		pop	ds
		stc

CPDone:		pop	si
		pop	dx
		ret
ConstPat	endp



; Assemble-	This code assembles the instruction that ES:DI points
;		at and displays the hex opcode(s) for that instruction.

Assemble	proc	near
		print
		byte	"Assembling: ",0
		strupr
		puts
		putcr

		ldxi	InstrPat
		xor	cx, cx
		match
		jnc	SyntaxError

; Quick check for illegal instructions:

		cmp	AsmOpcode, 7		;Special/Get instr.
		jne	TryStoreInstr
		cmp	AsmOprnd1, 2		;GET opcode
		je	SeeIfImm
		cmp	AsmOprnd1, 1		;Goto opcode
		je	IsGOTO

TryStoreInstr:	cmp	AsmOpcode, 1		;Store Instruction
		jne	NotStoreInstr

SeeIfImm:	cmp	AsmOprnd2, 7		;Immediate Addressing Mode
		jne	NotStoreInstr
		print
		db	"Syntax error: store/get immediate not allowed."
		db	"  Try Again",cr,lf,0
		jmp	ASMDone

IsGOTO:		cmp	AsmOprnd2, 7		;Immediate mode for GOTO
		je	InstrOkay
		print
		db	"Syntax error: GOTO only allows immediate mode.",cr,lf
		db	0
		jmp	ASMDone

NotStoreInstr:

InstrOkay:	mov	al, AsmOpcode
		shl	al, 1
		shl	al, 1
		or	al, AsmOprnd1
		shl	al, 1
		shl	al, 1
		shl	al, 1
		or	al, AsmOprnd2
		puth
		cmp	AsmOpcode, 4		;IFEQ instruction
		jb	SimpleInstr
		cmp	AsmOpcode, 6		;IFGT instruction
		jbe	PutConstant

SimpleInstr:	cmp	AsmOprnd2, 5
		jb      ASMDone
PutConstant:	mov	al, ' '
		putc
		mov	ax, ASMConst
		puth
		mov	al, ' '
		putc
		xchg	al, ah
		puth
		jmp	ASMDone

SyntaxError:	print
		db	"Syntax error in instruction."
		db	cr,lf,0


ASMDone:	putcr
		ret
Assemble	endp





Main		proc
		mov	ax, seg dseg		;Set up the segment registers
		mov	ds, ax
		mov	es, ax
		meminit


		lesi	Str1
		call	Assemble
		lesi	Str2
		call	Assemble
		lesi	Str3
		call	Assemble
		lesi	Str4
		call	Assemble
		lesi	Str5
		call	Assemble
		lesi	Str6
		call	Assemble
		lesi	Str7
		call	Assemble
		lesi	Str8
		call	Assemble
		lesi	Str9
		call	Assemble
		lesi	Str10
		call	Assemble
		lesi	Str11
		call	Assemble
		lesi	Str12
		call	Assemble
		lesi	Str13
		call	Assemble
		lesi	Str14
		call	Assemble


Quit:		ExitPgm
Main		endp
cseg		ends

sseg		segment	para stack 'stack'
stk		db	256 dup ("stack   ")
sseg		ends

zzzzzzseg	segment	para public 'zzzzzz'
LastBytes	db	16 dup (?)
zzzzzzseg	ends
		end	Main


