# binary_add.s
# add 2 binary numbers
# ..
# David Bozarth - 14 March 2004
# last modified - 15 March 2004 v0.3

	.data
hello:	
	.string "\nEnter ones & zeros up to 32 characters:\n"
errmsg:
	.string "\nInvalid input - exiting.\n"
newl:
	.string "\n"	
pre_aug:	
	.string "  "
pre_add:	
	.string "+ "
pre_sum:	
	.string "= "
pre_out:
	.string "  "
pass_flag:
	.byte 0
sum_flag:
	.byte 0
buffer:	
	.space 32
	.space 1
		
	.text
	.align 4	
	.globl main
	
main:	
	push	%ebp		# save caller's base pointer
	movl	%esp, %ebp	# establish our base pointer

	pushl   %ebx		# save regs.
	pushl	%ecx
	pushl	%edx

get_string:			# PROMPT FOR STRING INPUT
	movl	$hello, %ebx
helloop:
	cmpb	$0, (%ebx)	# check for end of string
	je	xhelloop	
	pushl	$1		# 3rd parm (#chars) for write()
	pushl	%ebx		# 2nd parm (prompt string adx) for write()
	pushl	$1		# 1st parm (stdin) for write()
	call	write
	addl	$12, %esp	# clear stack
	incl	%ebx
	jmp	helloop
xhelloop:	
				# RECEIVE STRING INPUT	
	movl	$buffer, %ebx	# adx of buffer
	pushl	$1		# 3rd parm (#chars) for read()
	pushl	%ebx		# 2nd parm (destination adx) for read()
	pushl	$0		# 1st parm (stdin) for read()
	call	read
	addl	$12, %esp	# clear stack
loop_rd:
	cmpb	$'\n', (%ebx)	# if <Enter> key
	je	eol		# .. then exit loop
	incl	%ebx		# else next byte
	pushl	$1		# 3rd parm (#chars) for read()
	pushl	%ebx		# 2nd parm (destination adx) for read()
	pushl	$0		# 1st parm (stdin) for read()
	call	read
	addl	$12, %esp	# clear stack
	jmp	loop_rd
eol:
	movl	$0, (%ebx)	# replace newline with null
	movl	$buffer, %ebx	# point to buffer base
	addl	$32, %ebx	# point to one past end of buffer
	movb	$0, (%ebx)	# ensure buffer max length 32

				# CONVERT STRING TO INT
	movl	$0, %eax	# accumulator for integer
	movl	$1, %edx	# shift index for next accumulator bit
	movl	$buffer, %ebx	# adx of buffer base
pre_conv:
	cmpb	$0, (%ebx)	# if The Terminator appears
	je	xpre_conv	# .. then I'll be back 
	incl	%ebx		# else point to next byte
	jmp	pre_conv
xpre_conv:
	decl	%ebx		# go back one to look for data
	pushl	$buffer		# adx of buffer
conv_int:	
	cmpl	%ebx, (%esp)	# while (buffer pointer) >= (buffer base)
	ja	xconv_int
	movb	(%ebx), %cl	# get byte
	cmpb	$0x30, %cl	# if zero
	je	done_bit	# .. then don't set accumulator bit
	cmpb	$0x31, %cl	# else trap error
	jne	error 
	orl	%edx, %eax	# .. and set accumulator bit
done_bit:	
	shll	%edx		# shift left on accumulator bit index
	decl	%ebx		# point to next byte
	jmp	conv_int
xconv_int:
	addl	$4, %esp	# fix stack pointer		
	movl	$pass_flag, %ebx	# address of flag
	cmpb	$0, (%ebx)	# test for second pass
	jne	begin_sum
	pushl	%eax		# save augend on stack
	movb	$0xff, (%ebx)	# set flag to indicate second pass
	jmp	get_string

begin_sum:			# augend on stack, addend in %eax
	movl	$pass_flag, %ebx
	movb	$0, (%ebx)	# reset pass-flag
	movl	$pre_aug, %ebx	# prepare augend prefix for output 
	movb	(%ebx), %cl	# CAUTION this in 3 places & a rig
	movl	$pre_out, %ebx	# .. overwrites 1 char, not string
	movb	%cl, (%ebx)	# .. depends on 4 strings look-alike
				# .. (see .data section)
	pushl	%eax		# protect accumulated addend from write()
				# print a newline
	pushl	$1		# 3rd parm (#chars) for write()
	pushl	$newl		# 2nd parm (source adx) for write()
	pushl	$1		# 1st parm (stdout) for write()
	call	write
	addl	$12, %esp	# clear stack
	popl	%eax		# restore accumulated addend after write()
	jmp	loop_pass	
loop_sum:	
	addl	(%esp), %eax	# form sum
	addl	$4, %esp	# fix stack pointer
	movl	$pre_sum, %ebx	# prepare sum prefix for output 
	movb	(%ebx), %cl
	movl	$pre_out, %ebx
	movb	%cl, (%ebx)
	jmp	do_string
loop_pass:
	popl	%ebx		# pop augend (1st pass) or addend (2nd pass)
	pushl	%eax		# push addend (1st pass) or augend (2nd pass)
	movl	%ebx, %eax	# position operand for output

do_string:			# CONVERT INT TO STRING	
	movl	$buffer, %ebx	# adx of buffer
	movl	$0x80000000, %edx	# shift index for next accumulator bit
conv_str:
	cmpl	$0, %edx	# if The Terminator appears
	je	xconv_str	# .. then exit
	movl	%eax, %ecx	# copy integer to bit-test register
	andl	%edx, %ecx	# check state of current bit
	cmpl	$0, %ecx	# if zero
	je	wrt_zero	# .. then write ascii zero to next buffer byte
	movb	$0x31, (%ebx)	# else write ascii one
	jmp	wrt_one
wrt_zero:		
	movb	$0x30, (%ebx)
wrt_one:
	shrl	%edx		# shift right on accumulator bit index
	incl	%ebx		# point to next byte
	jmp	conv_str
xconv_str:
				# OUTPUT BUFFERED STRING	
				# print line prefix
	pushl	%eax		# protect accumulated operand from write()
	movl	$pre_out, %ebx	# adx of prefix
	pushl	$1		# 3rd parm (#chars) for write()
	pushl	%ebx		# 2nd parm (prompt string adx) for write()
	pushl	$1		# 1st parm (stdin) for write()
	call	write
	addl	$12, %esp	# clear stack
	popl	%eax		# restore accumulated operand after write()
	incl	%ebx
sumout:
	cmpb	$0, (%ebx)	# check for end of string
	je	xsumout
	pushl	%eax		# protect accumulated operand from write()
	pushl	$1		# 3rd parm (#chars) for write()
	pushl	%ebx		# 2nd parm (prompt string adx) for write()
	pushl	$1		# 1st parm (stdin) for write()
	call	write
	addl	$12, %esp	# clear stack
	popl	%eax		# restore accumulated operand after write()
	incl	%ebx
	jmp	sumout
xsumout:
				# print data
	pushl	%eax		# protect accumulated operand from write()
	movl	$buffer, %ebx	# adx of buffer
	
	pushl	$1		# 3rd parm (#chars) for read()
	pushl	%ebx		# 2nd parm (destination adx) for read()
	pushl	$1		# 1st parm (stdin) for write()
	call	write
	addl	$12, %esp	# clear stack
	popl	%eax		# restore accumulated operand after write()
loop_wr:
	cmpb	$0, (%ebx)	# if null-terminator
	je	nul_wr		# .. then exit loop
	incl	%ebx		# else next byte
	pushl	%eax		# protect accumulated operand from write()
	pushl	$1		# 3rd parm (#chars) for write()
	pushl	%ebx		# 2nd parm (destination adx) for write()
	pushl	$1		# 1st parm (stdin) for write()
	call	write
	addl	$12, %esp	# clear stack
	popl	%eax		# restore accumulated operand after write()
	jmp	loop_wr
nul_wr:
				# print newline

	pushl	%eax		# protect accumulated operand from write()
	pushl	$1		# 3rd parm (#chars) for write()
	pushl	$newl		# 2nd parm (source adx) for write()
	pushl	$1		# 1st parm (stdout) for write()
	call	write
	addl	$12, %esp	# clear stack
	popl	%eax		# restore accumulated operand after write()
	
				# PRINT LOOP CONTROL
	movl	$sum_flag, %ebx
	cmpb	$0, (%ebx)	# if done printing sum
	jne	xprint		# .. then printing is finished

	movl	$pass_flag, %ebx
	cmpb	$0, (%ebx)	# if done with first pass
	je	second_pass	# .. then do second pass to print addend
	
	movl	$sum_flag, %ebx	# else set sum flag
	movb	$0xff, (%ebx)	# .. and print sum
	jne	loop_sum
	
second_pass:
	movb	$0xff, (%ebx)	# set pass flag
	movl	$pre_add, %ebx	# prepare addend prefix for output 
	movb	(%ebx), %cl
	movl	$pre_out, %ebx
	movb	%cl, (%ebx)
	jmp	loop_pass
	
xprint:				# DONE PRINTING
				# print some newlines
	pushl	$1		# 3rd parm (#chars) for write()
	pushl	$newl		# 2nd parm (source adx) for write()
	pushl	$1		# 1st parm (stdout) for write()
	call	write
	addl	$12, %esp	# clear stack

	pushl	$1		# 3rd parm (#chars) for write()
	pushl	$newl		# 2nd parm (source adx) for write()
	pushl	$1		# 1st parm (stdout) for write()
	call	write
	addl	$12, %esp	# clear stack

	movl	$0, %eax	# return value goes here
	jmp	exit
	
error:				# ERROR HANDLER
	addl	$4, %esp	# fix stack
	movl	$errmsg, %ebx	# prepare to print message
erloop:	
	cmpb	$0, (%ebx)
	je	xerror
	pushl	$1		# 3rd parm (#chars) for write()
	pushl	%ebx		# 2nd parm (source adx) for write()
	pushl	$1		# 1st parm (stdout) for write()
	call	write
	addl	$12, %esp	# clear stack
	incl	%ebx
	jmp	erloop
xerror:
	movl	$1, %eax	# non-zero return value indicates error
				
exit:				# ALL DONE
	popl	%edx		# restore regs.
	popl	%ecx
	popl	%ebx
	
	movl	%ebp, %esp	# restore stack pointer
	popl	%ebp		# restore caller's base pointer
	ret

