org 100h

start:
	; wczytaj dwie liczby
	call	wczytaj
	call	wczytaj
	; przygotuj liczby do obliczeń
	call	opprep
	call	wypisze
	; wykonaj obliczenie na podstawie wczytanego operatora
	pop	ax
	mov	dx, powrot
	push	dx
	cmp	ax, '+'
		je opadd
	cmp	ax, '-'
		je opsub
	cmp	ax, '*'
		je opmul
	cmp	ax, '/'
		je opdiv
	cmp	ax, ':'
		je opdiv
	jmp	blad
	blad:	; błąd o nie rozpoznanym operatorze
		mov	dx, msg2
		push	dx
		call wypiszs
	jmp koniec

	powrot:	; adres powrotny operacji
		call	wypiszw

koniec:	; koniec programu
	mov	ax, 4C00h
	int	21h

	; wypisuje ostatnią liczbę na stosie z znakiem minus
wypiszl:
	pop	bx
	pop	ax
	push	bx

	mov	dx, ax
	shr dx, 15

	cmp	dx, 0
	je	wypiszlz

	cmp	ax, 32768
	je	wypiszlz
	push	ax
	mov	ah, 2
	mov	dl, '-'
	int	21h
	pop	ax

	wypiszlz:
	call	usunznak
	mov	cx, 0
	mov	bx, [podst]

	cyfry0:
		xor	dx, dx
		div	bx
		push	dx
		inc	cx
	cmp	al, 0
	ja	cyfry0

	xor	dx, dx
	mov	ah, 2
	cyfry1:
		pop	dx
		add	dl, 48
		int	21h
	loop cyfry1
ret

; usuwa znak liczby
usunznak:
	shl	ax, 1
	shr	ax, 1
ret

; zmienia znak liczby na przeciwny
zmienznak:
	push	dx
	mov	dx, ax
	shr	dx, 15
	not	dx
	shl	dx, 15
	call	usunznak
	or	ax, dx
	pop	dx
ret

; sprawdza czy po operacji nie doszło do przepełnienia
miesci:
	push	dx
	mov	ax, 0
	jc	miescik
	shl	dx, 1
	jc	miescik
	miescik:
	pop	dx
ret

; porównuje dwa znaki
znakipor:
	; zapamiętaj porównywane liczby
	push	ax
	push	bx
	; pobierz bit znaku pierwszej liczby
	shr	ax, 15
	shl	ax, 1
	; pobierz bit znaku drugiej liczby
	shr	bx, 15
	; połącz wyniki
	mov	cx, 0
	or	cx, ax
	or	cx, bx
	; pobierz zapamiętane liczby oraz adres powrotny
	pop	bx
	pop	ax
	pop	dx
	push	cx	; wrzuć obliczoną wartość
	push	dx	; wrzuć z powrotem adres powrotny
	; wyzeruj bity znaku na obu liczbach
	shl	ax, 1
	shr	ax, 1
	shl	bx, 1
	shr	bx, 1
ret

; ustal znak na podstawie porównania znaków i maski wyników danej operacji
znakpisz:
	pop	ax
	pop	cx
	push	ax
	shr	bx, cl
	and	bx, 1
	shl	bx, 15
	or	dx, bx
ret

; wypisz tekst, odpowiednik ah = 9
wypiszs:
	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

; wypisuje wynik w postaci wyrażenia matematycznego
wypiszw:
	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

; wczytuje operator i kopiuje wartości, które zostaną użyte do obliczeń
opprep:
	mov	dx, msg1
	push	dx
	call wypiszs

	pop	cx

	mov	ah, 1
	int	21h
	mov	ah, 0
	push	ax

	mov	si, sp
	mov	dx, [si + 4]
	push	dx
	mov	dx, [si + 2]
	push	dx
	push	ax
	push	cx
ret

; wykonaj dodawanie
opadd:
	; jeśli są to pobierz liczby
	pop	dx
	pop	bx
	pop	ax
	push	dx
	call	znakipor	; porównaj znaki
	pop	cx
	mov	dx, ax
	; wybierz sposób obliczeń w zależności od porównania znaków
	cmp	cx, 0
	je	opadd0
	cmp	cx, 3
	je	opadd0
	jmp	opadd12

	opadd0:	; obie liczby dodatnie, więc zwykłe dodawanie
	push	cx
	add	dx, bx
	jnc	opaddk
	jmp	opadde	; jeśli przepełnienie to wypisz błąd

	opadd12:	; dodawanie liczb o różnych znakach
	xor	ax, ax
	cmp	dx, bx
	ja	opadd12b
	mov	ax, dx
	mov	dx, bx
	mov	bx, ax
	mov	ax, 1
	opadd12b:
	shl	ax, 2
	or	cx, ax
	push	cx
	sub	dx, bx
	jmp	opaddk

	opadde:	; wypisywanie błędu podczas operacji dodawania
	mov	dx, emsg0
	push	dx
	call wypiszs
	jmp koniec

	opaddk:
	call	miesci	; sprawdź czy wynik się zmieścił
	jc	opadde
	mov	bx, 172	; maska wyników operacji
	call	znakpisz	; przypisz znak końcowy operacji
	pop	ax
	push	dx	; dodaj wynik na stos
	push	ax
ret

; wykonaj odejmowanie
opsub:
	pop	dx
	pop	ax	; pobierz obie liczby
	pop	bx
	call	zmienznak	; zmień znak drugiego argumentu
	push	dx
	push	ax	; dodaj liczby na stos
	push	bx
	call	opadd	; wykonaj dodawanie
	pop	ax	; pobierz wynik
	pop	dx
	push	ax	; wrzuć wynik pod adresem powrotu
	push	dx
ret

; wykonaj mnożenie
opmul:
	; pobierz liczby
	pop	cx
	pop	bx
	pop	ax
	push	cx
	call	znakipor
	xor	dx, dx
	; mul	ax, bx	; proste mnożenie, nie uwzględnia liczb ujemnych

	; wlasne mnozenie bez MUL
	opmul0:
		shr	bx, 1

		jnc	opmul1
		add	dx, ax

		opmul1:
		shl	ax, 1
	cmp	bx, 0
	jne	opmul0
	jnc	opmulk

	opmule:	; błąd podczas mnożenia
	mov	dx, emsg2
	push	dx
	call wypiszs
	jmp	koniec

	opmulk:
	call	miesci	; sprawdź czy wynik się zmieścił
	jc	opmule
	mov	bx, 6	; maska wyników operacji
	call	znakpisz	; przypisz znak końcowy operacji
	pop	cx
	push	dx	; dodaj wynik na stos
	push	cx
ret

; wykonaj dzielenie
opdiv:
	; sprawdź czy jest odpowiednio dużo argumentów dla dzielenia
	push	ax
	mov	ax, 2
	call ileTest
	pop	ax
	; pobierz porównanie znaków oraz liczby
	pop	cx
	pop	bx
	pop	ax
	push	cx
	call	znakipor
	; sprawdź czy nie jest to dzielenie przez zero !
	cmp	bx, 0
	je	opdive
	xor	dx, dx
	; div	bx
	; własne dzielenie
	; ile potęg 2 w dzielniku
	mov	cx, 0
	opdivz:
		shr	bx, 1
		jc	opdivy
		inc	cx
	jmp opdivz

	; usuń wszystkie potęgi 2 z dzielnej i dzielnika
	opdivy:
		shl	bx, 1
		or	bx, 1
		shr	ax, cl

		mov	dx, ax
		cmp	bx, 1
		je	opdivk

		xor	dx, dx
	; policz ile dzielnik mieści się w dzielonej liczbie
	opdiv0:
		cmp	ax, bx
		jb	opdivk
		sub	ax, bx
		inc	dx
	jmp	opdiv0

	opdive:	; błąd obliczeń
	mov	dx, emsg3
	push	dx
	call wypiszs
	jmp koniec

	opdivk:
	mov	bx, 6	; maska wyników operacji
	call	znakpisz	; przypisz znak końcowy operacji
	pop	cx
	push	dx	; dodaj wynik na stos
	push	cx
ret

; wypisz enter
wypisze:
	mov	dx, ent1
	push	dx
	call	wypiszs
ret

; wczytaj liczbę
wczytaj:
	mov	si, sp
	mov	ax, [si]
	sub	sp, 2
	mov	di, sp
	mov	[di], ax

	xor	ax, ax
	push	ax

	xor	bx, bx
	mov	cx, [podst]

	wczytaj0:
		; wypisz aktualną wartość liczby
		pop	ax
		push	ax
		push	bx

		shl	ax, 15
		or	bx, ax
		push	bx

		mov	dx, msg0
		push	dx
		call	wypiszs

		call	wypiszl

		mov	dx, cmsg0b
		push	dx
		call	wypiszs

		pop	bx

		; pobierz kolejną cyfrę
		mov	ah, 1
		int	21h

		xor	dx, dx
		mov	dl, al
		sub	dl, 48
	cmp	dx, 253	; minus - zmiana znaku liczby
	je	wczytajm
	cmp	dx, 216	; wczytany znak backspace
	je	wczytaj1
	cmp	dx, 221	; wczytany znak enter
	je	wczytajk
	mov	cx, [podst]
	cmp	dx, cx
	jae	wczytaji
		push	bx
		push	dx
		mov	ax, bx
		xor	dx, dx
		mul	cx	; mnóż liczbę przez 10
		pop	dx
		mov	bx, ax
		add	bx, dx	; dodaj nową cyfrę
		mov	dx, bx
		call	miesci	; sprawdź czy się mieści, jeśli nie to wczytaj zapamiętaną wartość
		jc	wczytajp
		pop	dx
		jmp	wczytaj0
	wczytajp:
		pop	bx	; przepełnienie, wróć do poprzedniej wartości
		jmp	wczytaj0
	wczytaj1:
		mov	cx, [podst]
		mov	ax, bx
		xor	dx, dx
		div	cx	; skasuj ostatnią cyfrę liczby
		mov	bx, ax
	jmp	wczytaj0
	wczytaji:	; jeśli nierozpoznany znak to go zamaskuj spacją
		push	bx
		mov	dx, cmsg0a
		push	dx
		call	wypiszs
		pop	bx
	jmp	wczytaj0
	wczytajm:	; zmień znak wczytanej liczby
		pop	ax
		not	ax
		and	ax, 1
		push	ax
	jmp	wczytaj0
	wczytajk:	; zapisz liczbę na stosie pod adresem pworotu
		pop	ax

		mov	di, sp
		add	di, 2

		shl	ax, 15	; dopisz do wyniku znak minus, jeśli ustawiony
		or	bx, ax
		mov	[di], bx
ret

ent1	db	10,13,36
podst	dw	10	; podstawa obliczeń
; inne komunikaty
msg0	db 13,"Podaj liczbe: $"
msg1	db "Podaj znak operacji: $"
msg2	db "Nie rozpoznano operatora!$"
cmsg0a	db 8,32,8,36
cmsg0b	db 32,32,8,8,36
emsg0	db	"Nie mozna wyswietlic wyniku operacji: Wynik nie miesci sie na 16 bitach!$"
emsg2	db	"Nie mozna wyswietlic wyniku mnozenia: Wynik nie miesci sie na 16 bitach!$"
emsg3	db	"Nie mozna wykonac dzielenia: Dzielnik wynosi 0!$"