org 100h

start:
	mov	[stack], sp
	call	wczytaj	; wczytaj pierwszą liczbę
	call	wczytaj	; wczytaj drugą liczbę
	call	opprep	; wczytaj operator
	call	wypisze	; wypisz enter
	
	pop	ax
	mov	dx, powrot	; wrzuć adres powrotny
	push	dx	; wrzuć adres powrotny na stos
	; wybierz odpowiednią operacje
	cmp	ax, '+'
		je opadd
	cmp	ax, '-'
		je opsub
	cmp	ax, '*'
		je opmul
	cmp	ax, '/'
		je opdiv
	cmp	ax, ':'
		je opdiv
	jmp	blad	; wyświetl błąd jeśli nie rozpozano operatora
	blad:	; błąd o nie rozpoznaniu operatora
		mov	dx, msg2
		push	dx
		call wypiszs
	jmp koniec
	
	powrot:	; punkt powrotu z procedur operacji arytmetycznych
		call	wypiszw	; wypisz wynik
	
koniec:
	mov	ax, 4C00h
	int	21h

wypiszl:	; wypisz liczbę - wartość pobierana jest ze stosu
	pop	bx
	pop	ax
	push	bx

	mov	cx, 0
	mov	bx, 10

	cyfry0:	; rozłóż na cyfry
		xor	dx, dx
		div	bx
		push	dx
		inc	cx
	cmp	al, 0
	ja	cyfry0

	xor	dx, dx
	mov	ah, 2
	cyfry1:	; wypisz cyfry
		pop	dx
		add	dl, 48
		int	21h
	loop cyfry1
ret

wypiszs:	; wypisz tekst - adres pobierany jest ze stosu
	pop	ax
	pop	bx
	push	ax

	mov	cx, 0
	wypiszs0:
		mov	ah, 2
		mov	si, cx
		mov	dl, [bx + si]
		cmp	dl, 36
		je	wypiszsk
		int	21h
		inc cx
		jmp	wypiszs0
	wypiszsk:
ret

wypiszw:	; wypisuje wynik i zwalnia pamięć (na stosie od wierzchołka kolejno są wrzucone pierwsza liczba, druga liczba, operator oraz wynik)
	mov	si, sp
	mov	dx, [si + 8]
	push	dx
	call	wypiszl
	
	mov	dx, [si + 4]
	mov	ah, 2
	int	21h
	
	mov	dx, [si + 6]
	push	dx
	call	wypiszl
	
	mov	dx, 61
	mov	ah, 2
	int	21h
	
	mov	dx, [si + 2]
	push	dx
	call	wypiszl
	
	pop	bx
	add	sp, 8
	push	bx
ret

opprep:	; wczytaj operator
	mov	dx, msg1
	push	dx
	call wypiszs
	
	pop	cx
	
	mov	ah, 1
	int	21h
	mov	ah, 0
	push	ax
	
	mov	si, sp	; wrzuć na stos wczytane liczby, aby wykonać na nich operacje
	mov	dx, [si + 4]
	push	dx
	mov	dx, [si + 2]
	push	dx
	push	ax
	push	cx
ret

opadd:	; wykonaj operację dodawania - pobierane są dwie ostatnie wartości na stosie przygotowane przez opprep
	pop	dx
	pop	bx
	pop	ax
	add	ax, bx
	jnc	opaddk	; rozpoznaj przypadek przepełnienia
	
	mov	dx, emsg0
	push	dx
	call wypiszs
	jmp koniec
		
	opaddk:
	push	ax
	push	dx
ret

opsub:	; wykonaj operację odejmowania - pobierane są dwie ostatnie wartości na stosie przygotowane przez opprep
	pop	dx
	pop	bx
	pop	ax
	sub	ax, bx
	jns	opsubk	; rozpoznaj przypadek, gdy wynik jest ujemny
	
	mov	dx, emsg1
	push	dx
	call wypiszs
	jmp koniec
		
	opsubk:
	push	ax
	push	dx
ret

opmul:	; wykonaj operację mnożenia - pobierane są dwie ostatnie wartości na stosie przygotowane przez opprep
	pop	cx
	pop	bx
	pop	ax
	xor	dx, dx
	mul	bx
	jnc	opmulk	; wykryj przepełnienie
	
	mov	dx, emsg2
	push	dx
	call wypiszs
	jmp koniec
		
	opmulk:
	push	ax
	push	cx
ret

opdiv:	; wykonaj operację dzielenia - pobierane są dwie ostatnie wartości na stosie przygotowane przez opprep
	pop	cx
	pop	bx
	pop	ax
	
	cmp	bx, 0
	je	opdive
	xor	dx, dx
	div	bx
	jmp	opdivk	; wykryj dzielenie przez 0
	
	opdive:
	mov	dx, emsg3
	push	dx
	call wypiszs
	jmp koniec
		
	opdivk:
	push	ax
	push	cx
ret

wypisze:	; wypisuje enter
	mov	dx, ent1
	push	dx
	call	wypiszs
ret

wczytaj:	; wczytuj liczbe
	mov	si, sp
	mov	ax, [si]
	sub	sp, 2	; alokuj miejsce na stosi
	mov	di, sp
	mov	[di], ax
	
	mov	dx, msg0
	push	dx
	call	wypiszs ;wypisz komunikat
	
	xor	bx, bx
	mov	cx, 10

	wczytaj0:
		mov	ah, 1	; wczytaj cyfrę
		int	21h
		
		xor	dx, dx
		mov	dl, al
		sub	dl, 48
	cmp	dx, 216	; jeśli backspace to usuń ostatnią cyfrę
	je	wczytaj1
	cmp	dx, 221	; jeśli enter zakończ wczytywanie
	je	wczytajk
	cmp	dx, 10	; jeśli znak to nie cyfry, ani enter, ani backspace to przejdź do obsługi wyjątku
	jae	wczytaji
		; dołącz wczytaną wartość do aktualnej wartości
		push	dx
		mov	ax, bx
		xor	dx, dx
		mul	cx
		pop	dx
		mov	bx, ax
		add	bx, dx
	jmp	wczytaj0
	wczytaj1:	; usuwa ostatnią cyfrę z aktualnego wyniku
		push	bx
		mov	dx, cmsg0b
		push	dx
		call	wypiszs
		pop	bx
		
		mov	ax, bx
		xor	dx, dx
		div	cx
		mov	bx, ax
	jmp	wczytaj0
	wczytaji:	; usuwa ostatni wpisany znak z konsoli
		push	bx
		mov	dx, cmsg0a
		push	dx
		call	wypiszs
		pop	bx
	jmp	wczytaj0
	wczytajk:	; zapisuje wynik na zaalokowanej przestrzeni na stosie
		mov	di, sp
		add	di, 2
		mov	[di], bx
ret

ent1	db	10,13,36	;enter
podst	dw	10	; podstawa wczytywanej liczby
stack	dw	0	; oryginalny wierzchołek stosu (niewykorzystany, ale pozwoliłby napisać procedurę zwalaniającą stos w dowolnym miejscu w programie)
; komunikaty o dane
msg0	db "Podaj liczbe: $"
msg1	db "Podaj znak operacji: $"
msg2	db "Nie rozpoznano operatora!$"
; komunikaty specjalne
cmsg0a	db 8,32,8,36
cmsg0b	db 32,8,36
; komunikaty błędów
emsg0	db	"Nie mozna wyswietlic wyniku dodawania: Wynik nie miesci sie na 16 bitach!$"
emsg1	db	"Nie mozna wykonac odejmowania: Program nie obsluguje liczb ujemnych$"
emsg2	db	"Nie mozna wyswietlic wyniku mnozenia: Wynik nie miesci sie na 16 bitach!$"
emsg3	db	"Nie mozna wykonac dzielenia: Dzielnik wynosi 0!$"