PRINT USING mit der USR-Funktion
Haben Sie sich auch schon über die unformatierte Zahlenausgabe Ihres C 64 geärgert? Dann nehmen Sie in Zukunft die folgende USR-Funktion, um für Ihren C 64 ein PRINT USING zu erhalten.
Wie allgemein bekannt und vielfach bemängelt, bietet das Commodore-Basic keinen PRINT USING-Befehl. Doch gerade bei kommerziellen Problemen kann auf eine Druckaufbereitung von Zahlen nicht verzichtet werden. Eine Rechnung, in der nicht einmal die Dezimalpunkte untereinander stehen, ist eben unübersichtlich und keine Reklame. Für die Druckaufbereitung gibt es verschiedene Lösungen.
Die beste Lösung ist ein Maschinenprogramm. Interessierte Maschinenprogrammierer finden den Quellcode zu dieser Routine in Listing 1. Doch zunächst stellt sich die Frage, wie ein Maschinenprogramm für die Druckaufbereitung aufgerufen werden soll. Offensichtlich ist die USR-Funktion am geeignetsten, da sie sowohl in einer PRINT- als auch in einer PRINT#- oder einer LET-Anweisung verwendet werden kann. Der Aufruf der Funktion erfolgt durch USR(X),L,NK. Dabei ist X die Zahl, die aufbereitet werden soll, L die Gesamtfeldlänge der aufbereiteten Zahl einschließlich Vorzeichen und Dezimalkomma und NK die Anzahl der darzustellenden Nachkommastellen. Die USR-Funktion wandelt zunächst die Zahl X in einen ASCII-String um und berechnet die Stringlänge und die Anzahl der Nachkommastellen. Wenn bei der Wandlung von X der Interpreter die Exponentialform wählt, dann wird die Exponentialdarstellung zunächst in die Fließkommadarstellung umgewandelt. Danach werden die Nachkommastellen aufbereitet. Fehlende Nachkommastellen werden durch angehängte Nullen ergänzt. Müssen Nachkommastellen abgeschnitten werden, dann wird die Zahl gerundet, wenn die erste abgeschnittene Dezimalstelle größer oder gleich 5 ist. Wenn die Anzahl der gewünschten Nachkommastellen null ist, dann wird die Zahl als ganze Zahl (Integer) ohne Dezimalkomma aufbereitet. Wegen der kaufmännischen Anwendung wird der Dezimalpunkt durch ein Dezimalkomma ersetzt. Nach der Aufbereitung der Nachkommastellen wird durch Voranstellen von Leerzeichen der String auf die erforderliche Länge gebracht. Ist der String nach der Aufbereitung der Nachkommastellen schon länger als gewünscht, dann wird er nicht mehr verändert, sondern in voller Länge ausgegeben, um einen Datenverlust zu verhindern.
Das hier vorgestellte Maschinenprogramm verwendet nur relative Sprünge — außer bei den Aufrufen der Betriebssystemroutinen. Daher kann sich jeder Anwender das Programm ohne Änderungen in den Speicherbereich laden, der ihm am geeignetsten erscheint. Als Stringpuffer wird der Bereich ab $100 benutzt. DashatzurFolge, daß die GETSTR-Routine diesen String nicht in den Stringbereich kopiert. Der Stringbereich wird also nicht unnötig belastet. Eine Wertzuweisung A$=USR(X),L,NK ist dadurch aber auch nicht möglich, da die nächste Stringfunktion den Bereich ab $100 wieder überschreibt. Wenn druckaufbereitete Werte einer Variablen zugewiesen werden sollen, dann muß die Anweisung A$= " " +USR(X),L,NK oder A$=(USR(X),L,NK)+" " lauten, da dann das Ergebnis der Stringverknüpfung in den Stringbereich kopiert wird und der Variablen A$ dauerhaft zugewiesen ist.
Vor dem ersten Aufruf der USR-Funktion muß jetzt noch in Adresse 785 (Low-Byte) und 786 (High-Byte) die Startadresse der USR-Funktion hinterlegt werden. Listing 2 zeigt das Basic-Ladeprogramm für die USR-Funktion. Die Ladeadresse können Sie selbst bestimmen. Das Ladeprogramm setzt die Startadresse der USR-Funktion in den Speicherstellen 785 und 786 entsprechend. Zur Verdeutlichung der Anwendung der USR-Funktion enthält das Ladeprogramm verschiedene Druckaufbereitungen der Zahl r. Das Ergebnis des Beispiels ist in Bild 1 wiedergegeben.
(Dr. Michael Irskens/ah)
1000 ; f o r m a t r o u t i n e
1010 ; -------------------------
1020 ;
1030 ; dr.m.irskens
1040 ; leveser allee 13
1050 ; 3061 hespe
1060 ;
1070 ;
1080 space equ 32 ; ' '
1090 komma equ 44 ; ','
1100 e equ 69 ; 'e'
1110 punkt equ 46 ; '.'
1120 null equ 48 ; '0'
1130 minus equ 45 ; '-'
1140 ckcom equ $aefd ; prueft auf komma
1150 getstr equ $b487 ; string aus stringpuffer in stringbereich
1160 numtest equ $ad8d ; ergebnis auf numerisch pruefen
1170 facstr equ $bddd ; fac in string wandeln
1180 getbyt equ $b79e ; byte holen
1190 string equ $100 ; string-puffer
1200 temp equ $02 ; temporaerer speicher
1210 nk equ $57 ; fliesskomma-akku#3
1220 flen equ $58
1230 *equ $c000 ;startadresse
1240 fo10 jsr numtest ;auf numerisch pruefen
1250 jsr facstr ;umwandeln numerisch->alpha
1260 jsr ckcom ;auf komma pruefen
1270 jsr getbyt ;feldlaenge holen
1280 stx flen
1290 jsr ckcom ;auf komma pruefen
1300 jsr getbyt ;nachkommastellenzahl holen
1310 stx nk
1320 pla ; ruecksprungadresse vom stack entfernen,
1330 pla ; damit kein numtest durchgefuehrt wird
1340 ;
1350 ; pruefen auf darstellung im e-format
1360 ; x-register equ stringlaenge
1370 ; y-register equ anzahl vorkommastellen
1380 ; y-register equ 0, wenn keine nachkommastellen gefunden
1390 fo19 ldx #$ff
1400 ldy #0
1410 fo20 inx
1420 lda string,x ;zeichen aus puffer laden
1430 beq fo30 ;0->stringende
1440 cmp #e ;vergleich auf 'e'
1450 beq fo201 ;e-format umwandeln
1460 cmp #punkt ;vergleich auf '.'
1470 bne fo20
1480 txa
1490 tay ;anzahl vorkommastellen in y-register
1500 bne fo20
1510 ;
1520 ; umwandeln e-format
1530 ;
1540 fo201 lda string+2
1550 cmp #punkt
1560 bne fo22
1570 ;
1580 ; dezimalpunkt entfernen
1590 ;
1600 dex
1610 ldy #1
1620 fo21 iny
1630 lda string+1,y
1640 sta string,y
1650 bne fo21
1660 ;
1670 ; stellenverschiebung errechnen
1680 ;
1690 fo22 lda string+2,x ; zehnerstelle exponent
1700 and #$0f ; ziffer errechnen
1710 asl a ; *2
1720 sta temp
1730 asl a ; *4
1740 asl a ; *8
1750 adc temp ; 8* + 2* equ 10*
1760 adc string+3,x; +einerstelle ascii
1770 sbc #47 ; ascii 0 abziehen
1780 ldy string+1,x ; vorzeichen exponent
1790 cpy #minus
1800 beq fo24
1810 adc #3
1820 stx temp
1830 sbc temp
1840 tay
1850 lda #null
1860 fo23 sta string,x ; string mit nullen erweitern fuer + exponent
1870 inx
1880 dey
1890 bne fo23
1900 lda #0
1910 sta string,x ; neues stringende
1920 beq fo19
1930 ;
1940 ; exponent<0: vornullen ergaenzen
1950 ; string um a-reg.+1 stellen verschieben
1960 fo24 sta temp
1970 lda #0
1980 sta string,x ; "e" durch 0 ersetzen
1990 txa
2000 clc
2010 adc temp
2020 tay
2030 fo25 lda string,x
2040 beq fo26 ; stringende uebertragen
2050 cmp #$30 ; vergleich auf ziffer
2060 bcs fo26 ; ziffer
2070 lda #null
2080 bne f027
2090 fo26 dex
2100 fo27 sta string,y
2110 dey
2120 bne fo25
2130 lda #punkt
2140 sta string+1
2150 bne fo19
2160 ;
2170 ; dezimalpunkt durch komma ersetzen
2180 ; y-register: anzahl vorkommazeichen
2190 ;
2200 fo30 tya
2210 beq fo50 ;nachkommastellen ergaenzen
2220 lda nk
2230 bne fo45 ; nachkommastellen
2240 tya
2250 tax
2260 lda string+1,x
2270 bne fo901 ; keine nachkommastellen, aber runden
2280 fo45 lda #komma ; '.' gefunden
2290 sta string,y ;durch ',' ersetzen
2300 bne fo71
2310 fo50 cpy nk ;nachkommastellen ergaenzen
2320 beq fo100
2330 lda #komma
2340 sta string,x
2350 inx
2360 bne fo79
2370 fo71 sty temp ;anzahl nachkommastellen errechnen
2380 sec
2390 txa
2400 sbc temp
2410 sec
2420 sbc #1 ;1 abziehen fuer dezimalpunkt
2430 cmp nk ;vergleichen mit sollanzahl
2440 beq fo100 ;anzahl gleich
2450 bcs fo90 ;anzahl > soll
2460 tay ;anzahl < soll
2470 fo79 lda #null
2480 fo80 sta string,x ;string mit nullen ergaenzen
2490 inx
2500 iny
2510 cpy nk
2520 bne fo80
2530 fo85 lda #0
2540 sta string,x
2550 ;
2560 ; nachkommastellen aufbereitet
2570 ; gesamtfeldlaenge korrigieren
2580 ;
2590 ; x-reg feldlaenge aktuell
2600 ; flen gesamtlaenge soll
2610 fo100 lda string+1
2620 cmp #$30
2630 bcs fo109 ; 1.zeichen ist eine ziffer
2640 inx
2650 txa
2660 tay
2670 fo105 lda string-1,y
2680 sta string,y
2690 dey
2700 bne fo105
2710 lda #null ; null vor komma ergaenzen
2720 sta string+1
2730 fo109 cpx flen
2740 bcs fo130 ;feldueberlauf oder feldlaenge gleich
2750 ldy flen
2760 fo110 lda string,x;string verschieben
2770 sta string,y
2780 dey
2790 dex
2800 bpl fo110
2810 lda #space ;leerzeichen
2820 fo120 sta string,y ;rest mit leerzeichen fuellen
2830 dey
2840 bpl fo120
2850 fo130 lda #<string ;startadresse string laden in a/y
2860 ldy #>string
2870 jmp getstr
2880 fo90 sec
2890 sbc nk
2900 sta temp ;anzahl ueberfluessiger nachkommastellen
2910 txa
2920 sec
2930 sbc temp ;neue stringlaenge
2940 tax
2950 ;
2960 ; beim abschneiden von nachkommastellen runden
2970 ;
2980 lda string,x
2990 fo901 cmp #$35
3000 bcc fo85
3010 txa
3020 tay ; index letztes zeichen
3030 fo91 dey
3040 beq fo92 ; string eine 1 voranstellen
3050 lda string,y
3060 cmp #$30 ; vergl < "0"
3070 bcc fo91
3080 clc
3090 adc #1 ; ziffer um eins erhoehen
3100 cmp #$3a ; ueberlauf abfragen
3110 sta string,y ; veraendertes zeichen speichern
3120 bne fo85 ; kein uebertrag
3130 lda #null ; null laden
3140 sta string,y
3150 bne fo91 ; naechste ziffer erhoehen
3160 fo92 txa ; string um eine stelle verschieben fuer "1"
3170 tay
3180 fo94 lda string,y
3190 sta string+1,y
3200 dey
3210 bne fo94
3220 lda #$31 ; "1" laden
3230 sta string+1
3240 inx ; feldlaenge erhoehen
3250 bne fo85
Listing 1. PRINT USING-Quellprogramm
10 rem ************************************************************************
11 rem * f o r m a t r o u t i n e *
12 rem ************************************************************************
13 rem * copyright: *
14 rem * dr.m.irskens *
15 rem * leveser allee 13 *
16 rem * 3061 hespe *
17 rem ************************************************************************
18 rem * *
19 rem * aufruf: *
20 rem * usr(x),fl,nk *
21 rem * x = zu formatierender numerischer wert *
22 rem * fl = feldlaenge der formatierten zahl *
23 rem * nk = anzahl nachkommastellen der formatierten zahl *
24 rem * das ergebnis der usr-funktion ist der formatierte string. dieser *
25 rem * kann einer stringvariablen zugewiesen oder ausgedruckt werden. *
26 rem * beispiele: *
27 rem * a$=(usr(xy),10,2)+"" *
28 rem * print usr(xy),10,2;" dm" *
29 rem * *
30 rem * besonderheiten: *
31 rem * wenn nachkommastellen durch das formatieren abgeschnitten werden, *
32 rem * so wird die zahl gerundet. *
33 rem * zahlendarstellungen im e-format werden ebenfalls verarbeitet. *
34 rem * wenn die zahl in der angegebenen feldlaenge nicht dargestellt *
35 rem * werden kann, dann wird die zahl in der notwendigen feldlaenge dar- *
36 rem * gestellt, um einen zifferverlust zu vermeiden. *
37 rem * *
38 rem * vor aufruf der funktion muessen folgende befehle einmalig durchge- *
39 rem * fuehrt werden: *
40 rem * poke 785,0 : low-byte der startadresse *
41 rem * poke 786,192 : high-byte der startadresse = 49152 *
42 rem * *
43 rem * die routine ist voll verschieblich und kann daher an jeder stelle im *
44 rem * speicher liegen. die adressen der usr-funktion sind dann entsprechend*
45 rem * anzupassen. *
46 rem * *
47 rem ************************************************************************
100 s=0:input "startadresse";b
110 for i=b to b+335
120 read a
130 s=s+a:poke i,a
140 next i
150 if s<>36986 then print "{rvon} fehler in den datazeilen {rvof}":stop
160 poke 785,b-256*int(b/256):poke 786,b/256
170 print " alles ok. "
199 rem testbeispiele
200 for e=0 to 9
210 f=~*10^e
220 a$=(usr(f),11,0)+""
230 print a$;usr(f),13,1;usr(f),14,2
240 next e
250 end
299 rem datazeilen
300 data32,141,173,32,221,189,32,253,174,32,158,183,134,88,32,253,174,32
301 data158,183,134,87,104,104,162,255,160,0,232,189,0,1,240,117,201,69,240
302 data8,201,46,208,242,138,168,208,238,173,2,1,201,46,208,12,202,160,1
303 data200,185,1,1,153,0,1,208,247,189,2,1,41,15,10,133,2,10,10,101,2,125
304 data3,1,233,47,188,1,1,192,45,240,23,105,3,134,2,229,2,168,169,48,157
305 data0,1,232,136,208,249,169,0,157,0,1,240,168,133,2,169,0,157,0,1,138
306 data24,101,2,168,189,0,1,240,8,201,48,176,4,169,48,208,1,202,153,0,1
307 data136,208,236,169,46,141,1,1,208,129,152,240,18,165,87,208,7,152,170
308 data189,1,1,208,119,169,44,153,0,1,208,12,196,87,240,40,169,44,157,0
309 data1,232,208,16,132,2,56,138,229,2,56,233,1,197,87,240,19,176,72,168
310 data169,48,157,0,1,232,200,196,87,208,247,169,0,157,0,1,173,1,1,201,48
311 data176,17,232,138,168,185,255,0,153,0,1,136,208,247,169,48,141,1,1,228
312 data88,176,20,164,88,189,0,1,153,0,1,136,202,16,246,169,32,153,0,1,136
313 data16,250,169,0,160,1,76,135,180,56,229,87,133,2,138,56,229,2,170,189
314 data0,1,201,53,144,179,138,168,136,240,24,185,0,1,201,48,144,246,24,105
315 data1,201,58,153,0,1,208,157,169,48,153,0,1,208,229,138,168,185,0,1,153
316 data1,1,136,208,247,169,49,141,1,1,232,208,131