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.
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.
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