FizzBuzz using MSX / Z80 assembler

By Eugeny_Brychkov

Paragon (1166)

Eugeny_Brychkov's picture

23-05-2018, 19:04

Today I have got a message from some employment agency asking if I want to apply to the position (related to support/programming), and if I am interested, go and answer questions for the "online" interview. When opening the interview website, I realized that I do not want to take it as I will be asked to write a lot without predictable result out of my control. I recalled the times when I was in touch with Corssover and they were asking me to perform some test through HackerRank, which I did not take because I thought it is loss of time and I do not want myself being involved when time is ticking out of my control.

Thus I searched over the internet how programmers are being hired, and found many posts and articles that it is very effective to use FizzBuzz exercise, which is extremely simple, to immediately find out if candidate is able to think logically and program (not just code).

Looked through a number of sites, found out a lot of implementations in various high level languages, claiming to be the best optimized in terms of number of chars, in terms of speed, in terms of being beautiful etc.

But no sites, and no participants actually provided examples in assembler, which must be beautiful from code size and performance perspective! And as an exercise without time and source code size constraints, I decided to make the application.

Here's the source code.

; FizzBuzz for MSX designed 23-May-2018 by Eugeny Brychkov


CHPUT	equ	00A2H			; put a character to console

;=========== Main code ==============
	ld	a,01d			; 2 / 7 (8)	A=starting/current number
	ld	de,0305h		; 3 / 10 (11)	D=mod3 counter, E=mod5 counter
	ld	b,0100d			; 2 / 7 (8)	B=number of cycles

	ld	c,0h			; 2 / 7 (8)	fizz or/and buzz flag reset
	dec	d			; 1 / 4 (5)	mod3 counter depleted
	jr	nz,skip3		; 2 / 7 (8) or 12 (13)
					;		do not print "Fizz", go check for "Buzz"

	inc	c			; 1 / 4 (5)	flag increment, 0 no more
	ld	d,03d			; 2 / 7 (8)	reset mod3 counter
	call	putstr			; 3 / 17 (18)	print "Fizz" with trailing space
	db	"Fizz ",0		; 6

	dec	e			; 1 / 4 (5)	mod5 counter depleted
	jr	nz,skip5		; 2 / 7 (8) or 12 (13)
					;		do not print "Buzz", checking flag

	inc	c			; 1 / 4 (5)	flag increment, 0 no more
	ld	e,05d			; 2 / 7 (8)	reset mod5 counter
	call	putstr			; 3 / 17 (18)	print "Buzz" with no trailing space
	db	"Buzz",0		; 5

	inc	c			; 1 / 4 (5)
	dec	c			; 1 / 4 (5)	decrease and increase C to identify zero
	call	z,printa		; 3 / 17 (18) or 10 (11)
					;		print current number if flag is zero

	call	putstr			; 3 / 17 (18)	print CR/LF
	db	0dh,0ah,0		; 3

	inc	a			; 1 / 4 (5)
	djnz	cycle			; 2 / 13 (14) or 8 (9)
					;		loop until B is zero (A reaches 101)
	ret				; --- 56 bytes in total
					; (excluding I/O subroutines)

;=========== I/O routines ==============
; PUTSTR: print inline string
; in:	string inline the code, null terminated
; out:	nothing
; regs:	AF', BC', DE', HL', IX, IY (through CALLF)
	ex	(sp),hl			; get pointer to the inline string, preserve HL 
	push	af			; preserve A and F
	ld	a,(hl)			; get character from the string
	inc	hl			; increment pointer in the string
	or	a			; terminating null char?
	jr	z,prsret		; yes, return to after the inline string

	rst	30h			; print character through CALLF
	db	080h			; slot 0 (subslot 0 if expanded)
	jr	pstrlp			; loop until null char
	pop	af			; restore A and F
	ex	(sp),hl			; restore HL, place address to return to into stack

; PRINTA: print A in decimal format
; in:	A=value to print
; out:	nothing
; regs:	AF', BC', DE', HL', IX, IY (through CALLF)
	push	af			; preserve A
	push	de			; preserve E
	call	div10			; lowest digit
	ld	d,e			; into D
	call	div10			; middle digit in E, and highest digit in A
	or	a			; highest digit is 0?
	push	af			; (preserve highest digit into stack)
	call	nz,putdig		; print highest digit is not zero
	pop	af			; get highest digit into A from the stack
	or	e			; OR highest digit with middle digit,
					; with ZF=Z if both highest and middle are 0s
	ld	a,e			; load A with middle digit again
	call	nz,putdig		; and print it if highest or middle digits not zero
	ld	a,d			; load lowest digit
	call	putdig			; and always display it
	pop	de
	pop	af			; restore registers

; PUTDIG: print decimal digit
; in:	A=digit value to print (0-9)
; out:	nothing
; regs:	AF', BC', DE', HL', IX, IY (through CALLF)
	add	a,'0'
	rst	30h			; perform printout of decimal digit through CALLF
	db	080h

; DIV10: divide by 10
; in:	A=value to divide
; out:	A=quotient, E=remainder
; regs:	none
	push	bc
	ld	bc,05a0h		; 5 cycles, first subtractor is 160
	ld	e,0			; remainder
	sub	c
	jr	nc,bit0
	add	a,c
	rl	e			; shift inverted value of CY from previous operation; if CY
					; was reset (subtraction positive), shift 1 in, if negative
					; (addition was surpassing max for unsigned) shift 0 in
	srl	c			; shift mask one bit right
	djnz	d10loop
	; now need to exchange contents of E and A
	ld	b,a
	ld	a,e
	ld	e,b
	pop	bc

	end	start

I have proven it to be working properly Smile

The full design and testing took about 3 hours (with coffee breaks LOL! ), with readable layout and comments for other programmer readers.

Login or register to post comments

By Grauw

Ascended (9282)

Grauw's picture

24-05-2018, 00:22

Ha, nice! Big smile

Reminds me of something similar I did a long time ago, I implemented the Tiger hashing algorithm for MSX, not because it was useful (or difficult), but just because I could :D. I did get an honourable mention on their website :P.