Task

Implement a    HQ9+    interpreter or compiler.




putch:	equ	2	; Write character
puts:	equ	9	; Write string
fopen:	equ	15	; Open file
fread:	equ	20	; Read record
setdma:	equ	26	; Set DMA address
fcb:	equ	5Ch	; FCB for first file on command line
	org	100h
	;;;	Open source file given on command line
	lxi	d,fcb
	mvi	c,fopen
	call	5	; Open file
	inr	a	; A=FF = error
	lxi	d,efile
	jz	s_out	; If error, print error message and stop
	lxi	d,src	; Start reading file at src
	;;;	Load the entire source file into memory	
block:	push	d	; Set DMA address to next free location
	mvi	c,setdma
	call	5
	lxi	d,fcb	; Read 128-byte record
	mvi	c,fread
	call	5
	pop	d	; Advance pointer by 128 bytes
	lxi	h,128
	dad	d
	xchg
	dcr	a	; A=1 = end of file
	jz	block	; If not EOF, read next block
	xchg
	mvi	m,26	; Terminate last block with EOF byte to be sure
	lxi	b,src	; BC = source pointer
ins:	ldax	b	; Get current instruction
	cpi	26	; If EOF, stop
	rz
	ori	32	; Make lowercase
	push	b	; Keep source pointer
	cpi	'h'	; H=hello
	cz	hello
	cpi	'q'	; Q=quine
	cz	quine
	cpi	'9'	; 9=bottles
	cz	botls
	cpi	'+'	; +=increment
	cz	incr
	pop	b	; Restore source pointer
	inx	b	; Next instruction
	jmp 	ins
	;;;	Increment accumulator
incr:	lxi	h,accum
	inr	m
	ret
	;;;	Print "Hello, World"
hello:	lxi	d,histr
	jmp	s_out
	;;;	Print the source
quine:	lxi	h,src	; Pointer to source
qloop:	mov	a,m	; Load byte
	cpi	26	; Reached the end?
	rz		; If so, stop
	push	h	; Otherwise, keep pointer
	mov	e,a
	mvi	c,putch	; Print character
	call	5
	pop	h	; Restore pointer
	inx	h	; Next byte
	jmp	qloop
	;;;	99 bottles of beer
botls:	mvi	e,99	; 99 bottles
bverse:	call	nbeer	; _ bottle(s) of beer
	lxi	h,otw	; on the wall
	call	bstr
	call	nbeer	; _ bottle(s) of beer
	lxi	h,nl	; \r\n
	call	bstr
	lxi	h,tod	; Take one down and pass it around
	call	bstr
	dcr	e	; Decrement counter
	push	psw	; Keep status
	call 	nbeer	; _ bottle(s) of beer
	lxi	h,otw
	call	bstr	; on the wall
	lxi	h,nl	; \r\n
	call	bstr
	pop	psw	; restore status
	jnz	bverse	; If not at 0, next verse
	ret
nbeer:	push	d	; keep counter
	call	btlstr	; _ bottle(s)
	lxi	d,ofbeer
	call	s_out	; of beer
	pop 	d
	ret
bstr:	push	d	; keep counter
	xchg		; print string in HL
	call	s_out
	pop	d	; restore counter
	ret
	;;;	Print "N bottle(s)"
btlstr:	push	d	; Keep counter
	mov	a,e	; Print number
	call 	num
	lxi	d,bottle
	call	s_out	; Print " bottle"
	pop 	d	; Restore counter
	dcr	e	; If counter is 1,
	rz		; then stop,
	mvi	e,'s'	; otherwise, print S
	mvi	c,putch
	jmp	5
	;;;	Print number (0-99) in A
num:	ana	a	; If 0, print "no more"
	lxi	d,nomore
	jz	s_out
	mvi	b,'0'-1	; Tens digit
nloop:	inr	b	; Increment tens digit
	sui	10	; Subtract 10
	jnc	nloop
	adi	'0'+10	; Ones digit
	lxi	d,snum-1
	stax	d	; Store ones digit
	mov	a,b	; Tens digit zero?
	cpi	'0'
	jz	s_out	; If so, only print ones digit
	dcx	d	; Otherwise, store tens digit
	stax	d
s_out:	mvi	c,puts	; Print result
	jmp	5
efile:	db	'File error.$'
histr:	db	'Hello, world!',13,10,'$'
	db	'..'
snum:	db	'$'
nomore:	db	'No more$'
bottle:	db	' bottle$'
ofbeer:	db	' of beer$'
otw:	db	' on the wall',13,10,'$'
tod:	db	'Take one down and pass it around'
nl:	db	13,10,'$'
accum:	db	0 	; Accumulator
src:	equ	$	; Program source