Schnelle Textausgabe

Sicherlich ist es Ihnen auch schon aufgefallen, daß die Textausgabe bei den meisten Anwendungen sehr langsam ist. Daß es auch schneller geht, kann man unter anderem bei Tempus sehen. Mit dieser Routine kann nun jeder Programmierer in seinen eigenen Programmen für eine schnelle Textausgabe sorgen.

Das Prinzip, mit dem der Text auf den Bildschirm gebracht wird, ist eigentlich sehr einfach. Ein einzelnes Zeichen aus dem Standard-Zeichensatz besteht aus 16 Bytes, die untereinander im Bildschirmspeicher stehen. Soll nun ein Zeichen auf den Bildschirm gebracht werden, so muß man nur die betreffenden 16 Bytes in den Bildschirmspeicher kopieren. Dazu ist es natürlich notwendig zu wissen, wo sich diese 16 Bytes für das auszugebende Zeichen befinden. Beim Aufruf der Routine wird unter anderem auch die Startadresse des Zeichensatzes mit übergeben. Die Adresse, ab der die 16 Bytes für ein einzelnes Zeichen stehen, berechnet sich folgendermaßen: Startadresse des Zeichensatzes + 16 * ASCII -Wert des Zeichens.

Der Aufruf der Routine müßte von jeder Programmiersprache aus möglich sein, die Maschinenroutinenaufrufe zuläßt. Es sind folgende Parameter zu übergeben: zuerst die Adresse des Strings, also die Adresse des ersten auszugebenden Zeichens. Dann wird die Länge des Strings übergeben. Das überrascht vielleicht einige, war aber nötig, um einerseits eine hohe Ausgabegeschwindigkeit zu erreichen, und anderseits den Aufruf von jeder Programmiersprache aus zu ermöglichen. Hiernach kommen die Cursorkoordinaten. Für die Cursor-Home-Position gilt: Zeile=1; Spalte=1. Jetzt wird die Zeichensatzadresse übergeben. Zuletzt kommt noch die Bildschirmadresse. Auch dies geschieht wieder aus Geschwindigkeitsgründen. Die Übergabe einer Variablen ist schneller als das Ermitteln der Bildschirmadresse innerhalb der Routine.

Der Zeichensatz ist 4096 Bytes (256 Zeichen * 16 Bytes) lang. Das erste Zeichen belegt die ersten 16 Bytes, das zweite die darauf folgenden usw... Möglicherweise ist es dem einen oder anderen hier schon aufgefallen, daß STAD- bzw. TEMPUS-Zeichensätze genauso lang sind. Sie sind nicht nur gleich lang, sondern sie haben auch noch das gleiche Format. Was natürlich sehr praktisch ist, weil sich diese Zeichensätze ebenfalls verwenden lassen.

Das Assembler-Listing

Zuerst werden die übergebenen Werte vom Stack geholt. Wer sich sicher ist, daß seine Strings niemals über den rechten Rand hinausgehen, der kann die Zeilen 19-23 ersatzlos streichen. Wer das im Voraus jedoch nicht so genau weiß, der muß diese Zeilen stehen lassen. Zu der Begrenzung auf maximal 80 Zeichen bzw. dem Abschneiden am rechten Rand haben wir uns entschlossen, da es eigentlich keine sinnvolle Anwendung für ein Hinausschreiben über den rechten Rand gibt. Durch diese Vereinfachung läuft die Routine natürlich auch etwas schneller.

Im folgenden werden Bildschirm- und Zeichensatzadresse des auszugebenden Zeichens berechnet und die 16 Bytes in den Bildschirmspeicher kopiert. Um ein Maximum an Geschwindigkeit zu erreichen, haben wir bei dem Kopiervorgang auf eine Schleife verzichtet.

Außer der END-Anweisung werden in dem Listing keine weiteren assemblerspezifischen Befehle verwendet. Den Text sollte also jeder Assembler compilieren können.

Die GFA-Basic-Listings

Als höhere Programmiersprache haben wir GfA-Basic wegen seiner großen Verbreitung gewählt. Listing Nummer 2 erzeugt den Zeichensatz und speichert ihn auf Diskette ab. Durch den DEFTEXT-Befehl lassen sich unterschiedliche Zeichensätze erstellen.

Das Listing 3 verdeutlicht zum einen den Aufruf der Routine (Die Art und Weise wie Langworte bzw. Worte von GfA-Basic übergeben werden, ist in der ST-Computer 1/88 schon genaustens beschrieben worden.), und zeigt den Geschwindigkeitsunterschied recht deutlich. Mit Hilfe der linken Maustaste kann man zwischen der normalen und der neuen Ausgaberoutine hin- und herschalten. Das 4. Listing speichert die Assembler-Routine auf Diskette ab, wodurch auch diejenigen, die über keinen Assembler verfügen, diese Routine nutzen können.

;
; Routine zur schnellen Ausgabe von Strings
;
;	1987 TK-Soft
; Thomas Moltzen 
; (K.E.Neugebauer)
; WasmannstraPe 9 
; 2000 Hamburg 60

	move.1	4(SP),A2 ;Adresse des Strings
	move.w 8(SP),D1 ;Länge des Strings 
	move.w 10(SP),D2 ;Cursor-Spalte 
	move.w 12(SP),D3 ;Cursor-Zeile 
	move.1 14(SP),D4	;Adresse des Zeichensatzes
	move.1 18(SP),A1 ;aktuelle Bildschirmadresse 
	subq	#1,D2	;Anpassung an
	subq	#1,D3	;' Print At	'
	subq #1, D1 ;Zähler anpassen 
	move.w #80,D7 ;begrenzt String 
	sub. w	D2, D7	;auf
	cmp	D7, D1	; eine
	ble.s OK	; Zeilenlänge
	move.w D7,D1 ; (hier gleich 80 Zeichen)
OK:	mulu #1280,D3 ;Umrechnung der Cursor-
	adda.w D2,A1	; Koordinaten in Bildschirm
	adda.w D3,A1	; adresse für Stringausgabe
	clr	D6	; Zähler für Stringlänge=0
	
Schreiben: clr	D5	;wegen Byte-Verarbeitung (ungerade Adressen) 
	move.b	(A2)+,D5	;akt. Zeichen i.String
	lsl	#4,D5	;Multiplikation mit 16
	move.1	D4,A0	;Anfangsadresse des Zeichensatzes 
	adda.w D5,A0	; plus
Text_ausgabe: move.b (A0)+, (A1) ;Diese Routine schreibt die Daten
	move.b (A0)+,80(A1) ;aus dem selbst-erstellten Zeichen-
	move.b (A0)+,160 (A1) ;satz auf den Bildschirm, 
	move.b (A0)+,240(A1)	;Der Zeichensatz ist so abgelegt, 
	move.b (A0)+,320(A1)	;daß die 16 Graphikbytes für 
	move.b (A0)+,400(A1)	;je ein ASCII-Zeichen hintereinander 
	move.b (A0)+,480(A1)	;stehen,
	move.b (A0)+,560(A1)	;
	move.b (A0)+,640(A1)	;
	move.b (A0)+,720(A1)	;
	move.b (A0)+,800(A1)	;
	move.b (A0)+,880(A1)	;
	move.b (A0)+,960(A1)	;
	move.b (A0)+,040(A1)	;
	move.b (A0)+,120(A1)	;
	move.b (A0),1200(A1)	;
	addq.w #1,A1	;Adresse für Stringausgabe erhöhen 
	dbra	D1,Schreiben
	rts	;Rücksprung

	end

Listing 1

'
' Dieses File erzeugt die Zeichensatzdatei 'Z_SATZ.DAT', 
' auf die die Assembler-Routine zugreift.
'
Screen%=Xbios(2)	!Startadresse des Bildschirms.
Zeichen_satz$=Space$(4096) IReserviert Speicher. 
Z_satz_adresse%=Varptr(Zeichen_satz$) ! Ermittelt Speicher-Adresse. 
For I%=0 To 255	!0-255 ==>Zeichenanzahl.
	Deftext 1,0,0,13	!Legt den Zeichensatz fest.
	Text 1,13,Chr$(I%)	!Ausgabe des Zeichens.
	For Z%=0 To 15	!Auslesen der Bytewerte des
		Poke Z_satz_adresse%+Zz%,Peek(Screen%+Z%*80) ! dargestellten Zeichens und 
		Inc Zz%	! Ablage im reservierten Speicher
	Next Z%
Next I%	!Nächstes Zeichen.
Bsave "Z_SATZ . DAT", Z_satz_adresse%,4096 ! Speichern des Zeichensatzes.

Listing 2

@Init
'
Do
	Repeat
		@Text_normal(Zz%)	!Textausgabe normal.
		@Zz	!Erhöht Buchstaben-Zähler
	Until Mousek=l	!Bis Mousk=1
	@Pause 
	Repeat
		@Text_fast(Zz%)	!Textausgabe schnell.
		@Zz	!Erhöht Buchstaben-Zähler.
	Until Mousek=1	!Bis Mousek=1
	@Pause 
Loop
'
Procedure Text_normal(Zz%)
	For Z%=2 To 24 
		Print At(2,Z%);Text$(Zz%) !Ausgabe von Zeile2-24 
	Next Z%
Return
'
Procedure Text_fast(Zz%)
	Local Maschinen_routine%
	Maschinen_routine%=Varptr(Maschinen_routine$) !Ermittelt Adresse.
	For Z%=2 To 24 
		' Adresse der Routine,Adresse des Strings,
		' Länge des Strings,X-Pos,Y-Pos,Zeichensatzesadresse, 
		' Bildschirmadresse 
		Void C:Maschinen_routine%(L:Varptr(Text$(Zz%)),W:Len(Text$(Zz%)),W:2,W:Z%,L:Varptr(Zeichen_satz$),L:Screen%)
	Next Z%
Return
'
Procedure Zz 
	Inc Zz%
	If Zz%>26 !Alle Buchstaben wurden gezeigt,
		Zz%=1	!also wieder von vorne beginnen.
	Endif
Return
'
Procedure Pause
	Repeat	!Wartet auf Mousek=0
	Until Mousek=0 
Return
'
Procedure Init
	Hidem	!Maus ausschalten.
	Dim Text$(26)	!Array für Demo-Alphabet.
	For Z%=1 To 26 !Die Buchstaben werden dem
		Let Text$(Z%)=String$(78,64+Z%) ! Array zugewiesen.
	Next Z%
	Screen%=Xbios (2) ! aktueller Bildschirm. 
	Zeichen_satz$=Space$ (4096) !Reserviert Speicher. 
	Bload "Z_SATZ.DAT",Varptr(Zeichen_satz$) !Lädt Zeichensatz.
	Maschinen_routine$=Space$(140) !Reserviert Speicher. 
	Bload "STRING.B",Varptr(Maschinen_routine$) !Lädt Maschinen-Routine.
Return

Listing 3

'
' Dieses File erzeugt die Maschinendatei 'STRING.B',
' die auch vom Assembler erzeugt werden kann.
'
string$=SPACE$(140)
RESTORE string 
FOR z%=0 TO 139 
	READ wert%
	POKE VARPTR(string$)+z%,wert%
NEXT z%
BSAVE "STRING.B",VARPTR(string$),140 
string:
DATA 036,111,000,004,050,047,000,008,052,047,000, 010,054,047,000,012,040,047
DATA 000,014,034,111,000,018,083,066,083,067,083,065, 062,060,000,080,158,066
DATA 178,071,111,002,050,007,198,252,005, 000,210,194, 210,195,066,070,066,069
DATA 026,026,233,077,032,068,208,197,018,152,019,088, 000, 080,019,088,000,160
DATA 019,088,000,240,019,088,001,064,019, 088, 001,144, 019,088,001,224,019,088
DATA 002,048,019,088,002,128,019,088,002,208,019,088, 003,032,019,088,003,112
DATA 019,088,003,192,019,088,004,016,019,088,004,096, 019,080,004,176,082,073
DATA 081,201,255,180,078,117,000, 000, 000, 000, 000, 000,000,000

Listing 4


Thomas Moltzen K.E.Neugebauer
Aus: ST-Computer 10 / 1988, Seite 86

Links

Copyright-Bestimmungen: siehe Über diese Seite