; Datei arithmetik.asm
; +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

;
; Ganzzahlarthmetik mit den Registern 
; R2 : Stellenanzahl beim Dividieren
; R6 : Stellenzähler beim Multiplizieren
; R3,4,5 : Akkumulator B
; R6,7,8 : Akkumulator A
; R2,3,4,5: Ergebnis bei 16-Bit-Multiplikation
; Weiter werden __keine__ Register verändert
; kleinster Wert 0
; maximaler Wert 256^3 -1 = 16.777.215 bei Addition, Subtraktion und Division
; maximaler Wert 256^4 -1 = 4.294.967.295 bei Multiplikation
;
; Interface
; ================================================================
; bmal10: Multipliziert die Zahl in Akku B mit 10
; bmal2:  Multipliziert die Zahl in Akku B mit 2
; addba:  Addiert die Akkuinhalte A und B -> B
; addbtemp1: Addiert den Inhalt von temp1 mit Akku B
; bdiva: akku b enthält den dividenden dann den quotienten
	; akku a enthält den divisor und dann den rest
	; die register r9:r11 dienen als zwischenspeicher (keine Veränderung)
	; r2 ist der Zähler für die durchgänge
	; R2 wird aus temp2 geladen, das die anzahl zu verarbeitender Stellen vorgibt
; asc2bin: erzeugt aus einer HEX-Ziffer den Nibble-wert
	; übergabe in temp2 als Ascii
	; rückgabe in temp2	als hexwert (lownibble)
; ascnib: erzeugt aus einem Hex-Nibble das zugehörige Ascii-Zeichen
	; Übergabe im Lownibble von temp1 als hex-wert
	; rückgabe in temp1 als ascii-zeichen
; ascbyte: macht aus einem Byte (Hexwert in temp1) zwei Ascii-Zeichen
	; Rückgabe in temp1:temp2 (H;L)
; bin2asc: akku b enthält die dazustellende Zahl (3 Bytes) oder den quotienten
	; akku a enthält nach dem Divisionsaufruf im lowbyte den 10er-Rest
	; Die Zahl wird in einen 0-terminierten ascii-string transformiert
	; und in buffer abgelegt. Die niederwertigste Ziffer steht am Anfang!
	; die Anzahl von Stellen (ohne die Schluss-Null) wird in temp2 zurückgegeben
; mulx16		Softwaremultiplikation R5:R4:R3:R2 <-  R25:R24 * R23:R22
;									   MSB  :  LSB <-  MSB:LSB * MSB:LSB
; divbr2_1024	Softwaredivision R5:R4:R3 <- R5:R4:R3:R2 / 1024 


; =================================================================
;
.cseg
bmal10:								; multipliziert die Zahl in hb,mb,lb mit 10
	rcall bmal2						; Akku B mal2
	mov la, lb						; in Akku A sichern
	mov ma, mb
	mov ha, hb
	rcall bmal2		
	rcall bmal2						; Akku B mal 4 also insgesamt mal 8
	rcall addba						; plus das doppelte von B macht 10 mal B
	ret

bmal2:								; multipliziert die Zahl in hb,mb,lb mit 2
	lsl lb							; einfaches Linksschieben mit Carry
	rol mb
	rol hb
	ret
	

addba:								; Akku A + Akku B -> Akku B mit carry
	add lb, la
	adc mb, ma
	adc hb, ha
	ret

subba:								; Akku A - Akku B -> Akku B mit carry
	sub lb, la
	sbc mb, ma
	sbc hb, ha
	ret

addbtemp1:							; Temp1 + Akku B -> Akku B
	push temp1						; temp1 und Temp2 sichern
	push temp2
	ldi temp2, 0					; 0 für die Addition ab dem 2. Byte
	add lb, temp1					; lb + temp1 ohne carry
	adc mb, temp2					; mb + 0 + c
	adc hb, temp2					; hb + 0 + c
	pop temp2						; temp2,1 restaurieren
	pop temp1
	ret
	
	; akku b enthält den dividenden dann denn quotienten
	; akku a enthält den divisor und dann den rest
	; die register r9:r11 dienen als zwischenspeicher (keine Veränderung)
	; r2 ist der Zähler für die durchgänge
	; R2 wird aus temp2 geladen, das die anzahl zu verarbeitender Stellen vorgibt
bdiva:
	push r9
	push r10
	push r11
	; vorbereitung
	mov stellen, temp2		; Arbeitsbreite einstellen
	mov r9, la				; Divisor sichern nach r11:r9
	mov r10, ma
	mov r11, ha
	clr la
	clr ma
	clr ha					; zwischenspeicher löschen
bdiva_0:
	lsl lb
	rol	mb	
	rol hb					; bit vom dividenden nach links rausschieben
	rol la
	rol ma
	rol ha					; bit in Rest rein schieben
	cp  la, r9
	cpc ma, r10
	cpc ha, r11				; rest mit divisor vergleichen
	brlo bdiva_1			; rest < divisor Bit0 vom Quotienten bleibt 0
	sub la, r9				; rest >= divisor, rest - divisor lowbyte
	sbc ma, r10				; middle
	sbc ha, r11				; highbyte
	inc lb					; Bit0 vom Quotienten wird 1
bdiva_1:
	dec stellen				; ein durchgang weniger
	brne bdiva_0			; noch nicht = 0, dann nächster Durchgang
	pop r11					; sonst fertig
	pop r10					; quotient ist in akku b (r5:r3)
	pop r9					; rest ist in akku a (r8:56)
	ret


	; erzeugt aus einer HEX-Ziffer den Nibble-wert
	; übergabe in temp2 als Ascii
	; rückgabe in temp2	als hexwert (lownibble)
asc2bin:
	cpi temp2, 0x3a			; <= Ascii-9?
	brlo hexit1
	andi temp2, 0b11011111 	; in Großbuchstaben konvertieren
	cpi temp2, 0x47			; <= Ascii-F
	brlo hexit0				; ja
	rjmp hexit3				; sonst -> fehler
hexit0:
	cpi temp2, 0x41			; < Ascii-A
	brlo hexit3				; -> fehler
	subi temp2, ('A'-10)	; Wert isolieren
	rjmp hexit2				; zurück ohne fehler
hexit1:
	cpi temp2, 0x30			; >= als Ascii-0
	brlo hexit3				; nein -> fehler
	andi temp2, 0x0f		; Ascii-codierung entfernen
hexit2:						; zurück mit Hexnibble (low) ohne Fehler (carry clear)
	clc
	ret
hexit3:						; zurück mit fehlermeldung (carry set)
	sec
	ret	


	; erzeugt aus einem Hex-Nibble das zugehörige Ascii-Zeichen
	; Übergabe im Lownibble von temp1 als hex-wert
	; rückgabe in temp1 als ascii-zeichen
ascnib:
           andi  temp1, 0x0f
		   cpi   temp1, 10
           brlt  ascnib_1
           subi  temp1, -( 'A' - '9' - 1 )
ascnib_1:
           subi  temp1, -'0'
           ret

	; macht aus einem Byte (Hexwert in temp1) zwei Ascii-Zeichen
	; Rückgabe in temp1:temp2 (H;L)
ascbyte:
	push	r18
	in		r18, sreg
	push temp1				; temp1 aufheben
	rcall 	ascnib			; Lownibble nach Ascii
	mov 	temp2, temp1		; halbes Ergebnis sichern
	pop 	temp1				; hextwert zurück
	swap 	temp1				; high nach Low
	rcall 	ascnib				; highnibble wandeln
	out		sreg, r18
	pop 	r18
	ret
						; 

	; akku b enthält die dazustellende Zahl (3 Bytes) oder den quotienten
	; akku a enthält nach dem Divisionsaufruf im lowbyte den 10er-Rest
	; sie wird in einen 0-terminierten ascii-string transformiert
	; und in buffer abgelegt. Die niederwertigste Ziffer steht am anfang!
	; die Anzahl von Stellen (ohne die schluss-Null) wird in temp2 zurückgegeben
bin2asc:
	push temp1				; Hilfsregister/ Transferregister
	push temp3
	clr temp3				; Temp3 ist der Zähler für die Ziffern
	ldi zl, low(buffer)
	ldi zh, high(buffer)	; Zeiger auf buffer
	ldi temp1, 10
	mov la, temp1
	clr ma
	clr ha
b2asc_0:
	ldi temp2, 24			; 24 bit arithmetik
	rcall bdiva
	mov temp1, la			; 10-er Rest nach temp1
	ori temp1, '0'			; ascii herstellen
	st z+, temp1			; in den buffer schreiben
	inc temp3
	ldi temp1, 10
	mov la, temp1
	clr ma
	clr ha
	cp lb, la
	cpc mb, ma
	cpc hb, ha
	brge b2asc_0
	mov temp1,lb
	ori temp1,'0'
	st z+, temp1
	inc temp3
	ldi temp1, 0
	st z, temp1
	mov temp2,temp3			; temp2 = anzahl ziffern + 1 (die Null)
	pop temp3
	pop temp1
	ret 


; Softwaremultiplikation R5:R4:R3:R2 <-  R25:R24 * R23:R22
; 					     MSB  :  LSB <-  MSB:LSB * MSB:LSB
;						 	  32 Bit <-   16 Bit * 16 Bit
mulx16:  
        push    r6      ; Register retten
		push 	r17
		in 		r17, sreg

        push    r16     ; 
        ldi     r16,16  ; 
        mov     r6,r16  ; R6 = Durchlaufzähler
        pop     r16     ; 
        clr     r5      ; Produkt_High löschen
        clr     r4      ; 
        mov     r2,r22   ; Produkt_Low = Multiplikator
        mov     r3,r23   ; 
        lsr     r3      ; Multiplikator_High rechts ins Carry
        ror     r2      ; Multiplikator_Low rechts von Carry
Mmulx16a:
		brcc   	Mmulx16b; Carry = 0: nicht addieren
        add     r4,r24   ;       = 1: Übertrag + Multiplikand
        adc     r5,r25   ; 
Mmulx16b:
		ror    	r5      ; Carry und Übertrag rechts
        ror     r4      ; Produkt_High rechts
        ror     r3      ; Multiplikator_High rechts 
        ror     r2      ; Multiplikator_Low rechts nach Carry
        dec     r6      ; Durchlaufzähler - 1
        brne    Mmulx16a; bis Zähler Null

		out 	sreg, r17
		pop		r17
        pop     r6      ; Register restaurieren       
		ret             ; zurück 

; Dividiert R5:R4:R3 <- R5:R4:R3:R2 durch 1024 
; aus 32 Bit werden 24 Bit mit Rundung
; d.h. eigentlich 10 mal (1024=2^10) rechts schieben
divbr2_1024:
        push    r16      ; Register retten
		push 	r17
		in 		r17, sreg

						; Schleife rechnet sich nicht bei 2 Durchläufen
						; mit 4 Befehlen, weil 1 Ladebefehl für Laufvariable
						; einmal 2 Takte für Verzweigungsbefehl einmal 1 Takt

		lsr 	R5		; Ganze Latte durch 2 dividieren
		ror 	R4
		ror 	R3
		ror 	R2
		lsr 	R5		; Ganze Latte noch einmal durch 2 dividieren
		ror 	R4		; die restlichen 8 mal rechts-schieben sparen
		ror 	R3		; wir uns, indem wir geschickt R2 ausblenden
		ror 	R2		; und für das Ergebnis nur R5:R3 = Akku B nutzen
						; -> gibt eine 3-Bytezahl wie für die Ausgabe benötigt
		
		ldi R16, 0x80	; statt dessen verwenden wir R2 um das Ergebnis zu runden
		cp		r2, r16	; vergleich von r2 mit 128
		brlo	divbr2_1024_99 ; kleiner -> fertig, abrunden -> nix addieren
		ldi		r16, 1	; sonst, aufrunden 
		add		r3,r16	; -> 1 addieren
		ldi 	r16,0	; und für das mittlere und
		adc		r4, r16	; oberste Byte auch noch den
		adc		r5, r16	; evtl. Überlauf aus den Summen berücksichtigen
						; also: r5;r4;r3 <- r5;r4;r3 + 0,0,1 mit Überlauf
divbr2_1024_99:
		out 	sreg, r17
		pop		r17
        pop     r16      ; Register zurück        
		ret 

; Dividieren und Multiplizieren mit 2er-Potenzen als Subroutinen mit 
; Angabe des Mulitplikator-Exponenten in r16

