Dividieren in Assembler

Moderator: Rockford

Antworten
Benutzeravatar
Dr. Irata
Beiträge: 946
Registriert: 24.08.2021 14:40
Has thanked: 113 times
Been thanked: 275 times
Kontaktdaten:

Dividieren in Assembler

Beitrag von Dr. Irata »

Hallo!
Hier eine kleine Routine, um in Assembler zu dividieren.
Zunächst baue ich mal zur Veranschaulichung die Routine in Basic und setze sie dann in Assembler um.
Ich nutze bewußt keine Fließkommazahlen in Assembler, da in den meisten Anwendungen die Genauigkeit ausreicht (z.B. in Spielen). Wenn man es genauer braucht, dann kann man ja entweder mit Rest arbeiten, oder halt den Weg über Fließkommazahlen gehen.
Hier zunächst der Basic-Code:
X = A/B

Code: Alles auswählen

5 Graphics 0
10 Input A
20 Input B
30 A=A-B
40 If A<0 Then Goto 70
50 X=X+1
60 Goto 30
70 Print X
Das läßt sich nun leicht in Assembler umsetzen...

Benutzeravatar
Dr. Irata
Beiträge: 946
Registriert: 24.08.2021 14:40
Has thanked: 113 times
Been thanked: 275 times
Kontaktdaten:

Re: Dividieren in Assembler

Beitrag von Dr. Irata »

... und hier nun die Division in Assembler (8Bit Wertebereich 255-0):

var3 = var1/var2

Code: Alles auswählen

			.proc div
	lp1		
			sec
			lda var1
			sbc var2
			sta var1
			bcc weiter
			inc var3
			jmp lp1
	weiter
			rts
			.endp

slx
Beiträge: 136
Registriert: 18.06.2021 23:16
Has thanked: 95 times
Been thanked: 12 times
Kontaktdaten:

Re: Dividieren in Assembler

Beitrag von slx »

Wenn man var1 weiter verwenden will, kann man auch ohne Zwischenspeichern von var1 rechnen. Der eigentliche "Divisions-Loop" läuft ohne Zwischenspeichern schneller. (Samt Initialisierung der Division komme ich mit Variablen außerhalb der Zero Page auf 367 statt 942 cycles; z.B. braucht INX nur 2 cycles, INC 5-6; wenn man nicht damit rechnet, dass die Division meist ein Ergebnis von 1 oder 2 hat, dann rechnet es sich auch dann, wenn man X sichern muss).

Wenn man den Rest der Division haben will (var1 modulo var2) dann kann man bei beiden Verfahren am Ende var1 wieder addieren und hat den Rest dann im Akkumulator.

Beim Beispiel oben müsste man noch darauf achten, dass var3 initialisiert wird, weil die Subroutine sonst bei jedem Aufruf weiterzählt.

Code: Alles auswählen

lp1		
			ldx #$ff
			sec
			lda var1
loop			
			inx
			sbc var2
			bcs loop
			stx var3   	
weiter
			adc var2	; optional
					; macht die letzte Subtraktion, mit der man unter 0 gekommen ist, wieder rückgängig 
					; und hinterlässt den Divisionsrest im Akkumulator

			rts
			.endp
(X wird auf #$FF initialisiert, damit es beim ersten Durchlaufen des Loops auf 0 gesetzt wird. Würde man das INX nach dem SBC setzen, dann würde es auch hochzählen, wenn man mit dem SBC unter 0 kommt und das Ergebnis wäre eins zu hoch.)

Fürs Cycle-Zählen habe ich übrigens den Online-Assembler auf masswerk.at verwendet.

Das 1:1-Übertragen aus BASIC birgt mE das Risiko, dass man in Assembler mögliche Vereinfachungen (wie das Zählen mit X oder Y) leichter übersieht.

Benutzeravatar
Dr. Irata
Beiträge: 946
Registriert: 24.08.2021 14:40
Has thanked: 113 times
Been thanked: 275 times
Kontaktdaten:

Re: Dividieren in Assembler

Beitrag von Dr. Irata »

... lieben Dank für die Optimierung!
Bestimmte Sachverhalte zunächst in Basic umzusetzen und dann 1:1 in Assembler umzusetzen macht dann Sinn, wenn man es einfach erklären möchte für diejenigen, die noch nicht so firm mit Assembler sind.
Für mich selber nutze ich das inzwischen nur noch in den Fällen, wo ich einen hartnäckigen Denkfehler zu haben scheine. solch einen Fehler zu erkennen und zu eliminieren fällt mir dann ggf. leichter. Das geht aber nur für kleine Routinen. Danach optimiere ich es, wenn es zeitlich nötig ist.
In diesem Fall sind die Optimierungen natürlich immer nötig, da man wenig CPU-Zeit für eine Division verbrauchen will.
Ich werde dann diese optimierte Routine ins Atari-Wiki so übernehmen!

Liebe Grüße
Peter

p.s. Toll, daß wir uns hier so gut austauschen und jeder sich hier einbringt!!!!

Antworten

Wer ist online?

Mitglieder in diesem Forum: Google [Bot] und 1 Gast