org 100h

start:
	; wypisz komunikat
	mov	dx, msg0
	push	dx
	call	wypiszs
	call	wypisze
	; wczytaj dane do obliczeń ONP
	mov	ah, 10
	mov	dx, buff
	int	21h
	; wypisz bufor
	mov	ah, 9
	mov	dx, buff
	add	dx, 2
	int	21h
	call	wypisze
	; przejdź do obliczeń
	jmp	wczytaj

blad:	; jeśli wystąpi błąd to program wraca tutaj
	mov	dx, emsgm
	push	dx
	call	wypiszs
	jmp	koniec

powrot:	; tutaj wraca program jeśli obliczenia się powiodą
	; jeśli na stosie nie został tylko wynik to zwróć błąd
	mov	bx, [ile]
	cmp	bx, 1
	jne	blad
	; jeśli nie ma błędu to wypisz odpowiedni komunikat i wynik
	mov	dx, msg1
	push	dx
	call	wypiszs
	call	wypiszl

koniec:	; zakończ działanie 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	ax
	push	dx
	mov	ax, 0
	jc	miescik
	shl	dx, 1
	jc	miescik
	miescik:
	pop	dx
	pop	ax
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

; wykonaj dodawanie
opadd:
	; sprawdź czy jest odpowiednio dużo argumentów dla dodawania
	push	ax
	mov	ax, 2
	call ileTest
	pop	ax
	; 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:
	; sprawdź czy jest odpowiednio dużo argumentów dla oedjmowania
	push	ax
	mov	ax, 2
	call ileTest
	; ileTest zmniejsza odpowiednio ilość liczb na stosie, ale musimy zwiększyć, bo chcemy odjąć przez dodawanie
	mov	ax, [ile]
	inc	ax
	mov	[ile], ax
	pop	ax

	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:
	; sprawdź czy jest odpowiednio dużo argumentów dla mnożenia
	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
	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

; oblicz silnie
opsil:
	; sprawdź czy jest odpowiednio dużo argumentów dla silni
	push	ax
	mov	ax, 1
	call ileTest
	pop	ax

	pop	cx
	pop	bx
	push	cx

	mov	cx, 0
	mov	ax, 1

	shl	bx, 1	; pobierz znak argumentu do flagi C
	jnc	opsilp
	mov	dx, emsg5	; błąd silnia nie dla liczb ujemnych
	push	dx
	call wypiszs
	jmp	koniec

	opsilp:
	shr	bx, 1	; dokończ usuwanie bitu znaku
	; oblicz w pętli silnie
	opsil0:
		cmp	cx, bx
		je	opsilk

		inc	cx
		xor	dx, dx
		mul	cx

		call	miesci	; spradź czy wynik nie doszło do przepełnienia
		jc	opsile
	jmp	opsil0

	opsile:	; błąd obliczeń, wyjście poza zakres
	mov	dx, emsg4
	push	dx
	call wypiszs
	jmp	koniec

	opsilk:
	pop	cx
	push	ax	; dodaj wynik na stos
	push	cx
ret

; wykonaj potęgowanie
oppot:
	; sprawdź czy jest odpowiednio dużo argumentów dla potęgowania
	push	ax
	mov	ax, 2
	call ileTest
	pop	ax
	; pobierz porównanie znaków oraz liczby
	pop	ax
	pop	cx
	pop	bx
	push	ax

	mov	ax, 1
	; wykonaj potęgowanie
	oppot0:
		cmp	cx, 0	; jeśli potęga wynosi 0 przejdź do zapamiętania wyniku
		je	oppotk
		dec	cx

		push	ax	; wrzuć aktualny wynik
		mov	ax, [ile]	; należy pamiętać o zwiększeniu liczby argumentów na stosie
		inc	ax
		mov	[ile], ax

		push	bx	; wrzuć potęge
		call	rejsave	; zapisz rejestry (op mul modyfikuje je bez przywracania ich stanu pierwotnego)
		call	opmul
		call	rejload	; wczytaj rejestry
		pop	ax	; zdejmij aktualny wynik potęgowania ze stosu
		;xor	dx, dx
		;mul	bx
	jmp	oppot0

	oppotk:
	pop	cx
	push	ax	; dodaj wynik na stos
	push	cx
ret

; wypisz enter
wypisze:
	mov	dx, ent1
	push	dx
	call	wypiszs
ret

; rozpoznaj cyfrę
rozp:
	mov	cl, 1
	cmp	ch, 48
		jb	rozpk
	shl	cl, 1
	cmp	ch, 58
		jb	rozpk
	shl	cl, 1
	rozpk:
ret

; wczytaj liczby i argumenty z bufora
wczytaj:
	; czyść rejestry
	xor ax, ax
	mov	bx, 1
	xor	cx, cx
	xor	dx, dx

	wczytaja:
		inc	bx

		mov dh, dl
		mov	dl, [buff + bx]	; kolejny znak

		mov	ch, dl
		call	rozp	; rozpoznaj cyfrę
		cmp	cl, 2
		jne	dlznak
		; jeśli cyfra to zapamiętaj w danych
		dlcyfr:
			push	bx
			push	dx
			xor	dx, dx
			mov	bx, 10
			mul	bx
			pop	dx
			push	dx
			mov	dh, 0
			sub	dl, 48
			add	ax, dx
			pop	dx
			pop	bx

			call	rejsave
			cmp	dh, '-'
			jne	dalej
			push	ax
			mov	ax, [zminus]
			not	ax
			and	ax, 1
			mov	[zminus], ax
			pop	ax
			mov	dh, 0
			je wczytajk

		; spróbuj rozpoznać znak minus przed liczbą
		dlznak:
			mov	ch, dh
			call	rozp
			cmp	cl, 2
			jne dalej

			push	bx
			xor	bx, bx
			mov	bx, [zminus]
			shl	bx, 15
			or	ax, bx
			pop	bx
			push	ax

			mov	ax, [ile]
			inc	ax
			mov	[ile], ax

			xor	ax, ax
			mov	[zminus], ax
			jmp dalej

		; rozpoznaj operator
		dalej:
			call	rejsave
			mov	ax, wczytajk
			push	ax	; adresem powrotnym jest sprawdzenie czy to nie koniec danych
			cmp	dh, '+'
				je opadd
			cmp	dh, '-'
				je opsub
			cmp	dh, '*'
				je opmul
			cmp	dh, ':'
				je opdiv
			cmp	dh, '/'
				je opdiv
			cmp	dh, '!'
				je opsil
			cmp	dh, '^'
				je oppot
			ret

		wczytajk:
			call	rejload
			cmp	dl, 13	; jeśli koniec danych to wróć
				je powrot
	jmp wczytaja	; wróć, aby wczytać kolejny znak

; zapisz rejestry na końcu stosu
rejsave:
	mov	[stack], sp
	mov	sp, 10
	push	ax
	push	bx
	push	cx
	push	dx
	mov	sp, [stack]
ret

; wczytaj rejestry
rejload:
	mov	[stack], sp
	mov	sp, 2
	pop	dx
	pop	cx
	pop	bx
	pop	ax
	mov	sp, [stack]
ret

; sprawdź ile jest argumentów na stosie
ileTest:
	push	bx
	mov	bx, [ile]
	cmp	ax, bx	; jeśli za mało argumentów to zwróć błąd
	ja	blad
	sub	bx, ax	; jeśli odpowiednia ilość to zdejmij wskazaną przez operator liczbę argumentów
	inc	bx	; zwiększ o 1, ponieważ na koniec zostanie wrzucony wynik
	mov	[ile], bx
	pop	bx
ret

ent1	db	10,13,36	; enter
podst	dw	10	; podstawa wczytywanych liczb
; inne komunikaty
msg0	db	"Podaj rownanie ONP:$"
msg1	db	"Wynik to: $"
cmsg0a	db	8,32,8,36
cmsg0b	db	32,32,8,8,36
emsg0	db	"Nie mozna wykonac operacji atytmetycznej: Wynik nie miesci sie na 16 bitach!$"
emsg2	db	"Nie mozna wykonac operacji mnozenia: Wynik nie miesci sie na 16 bitach!$"
emsg3	db	"Nie mozna wykonac dzielenia: Dzielnik wynosi 0!$"
emsg4	db	"Nie mozna wykonac operacji silni: Wynik nie miesci sie na 16 bitach!$"
emsg5	db	"Silnie mozna liczyc tylko dla liczb naturalnych!$"
emsg6	db	"Nie mozna wykonac operacji potegowania: Wynik nie miesci sie na 16 bitach!$"
emsgm	db	"Podane rownanie ONP jest nieprawidlowe$"
; zmienna do pamiętania minusa podczas wczytywania
zminus	dw	0
; bufor do zapamiętania danych
buff	db	255
	db	0
	times 256 db '$'
; wierzchołek stosu
stack	dw	0
; ile argumentów na stosie
ile	dw	0