Zeichen-Editor
Bei vielen Anwendungen ist es sinnvoll mit zwei verschiedenen Zeichensätzen zu arbeiten. Dieses Programm ermöglicht Ihnen, einen eigenen Zeichensatz zu erstellen, ohne den Original-Zeichensatz zu zerstören.
Programmiert man ein Videospiel, ein Textverarbeitungsprogramm oder will man einfach nur die üblichen Bildschirmzeichen etwas interessanter gestalten, bleibt einem nichts anderes übrig, als den normalen Zeichensatz aus dem ROM herauszuholen, ins RAM zu kopieren und dann in diesem kopierten Zeichensatz »herumzuPOKEn«.
Diese Tätigkeit ist aber — ähnlich wie bei der Konstruktion von Sprites — immer wieder eine mühsame Rechnerei. Deshalb habe ich mit einen komfortablen Zeichen-Editor zusammengestellt, der jede Rechnerei abnimmt. Mit seiner Hilfe ist das Definieren eigener Grafik-Zeichen ein Kinderspiel.
Nach dem Eintippen (und Abspeichern!) läßt man das Programm mit RUN starten. Nach einer kurzen Wartezeit, während der Großbuchstaben-Zeichensatz aus dem ROM ins RAM kopiert wird (und zwar in die Speicherzellen 51200 bis 53248, der Bildschirm beginnt dann bei Adresse 50176), erscheint das Menü mit einem Zeichenfeld links unten. (Wen es interessiert: das Maschinenprogramm, das den Zeichensatz ins RAM kopiert, beginnt bei der Adresse 828, also dem Anfang des Kassettenpuffers).
In diesem Zeichenfeld kann man nun mit den Cursortasten herumwandern, Sternchen (*) malen und gegebenenfalls mit der Space-Taste wieder löschen. Ein Sternchen im Zeichenfeld bedeutet einfach, daß hier ein Bit gesetzt wird, das dann später zur Berechnung des Zeichens dient. Jedes Graphik-Zeichen besteht ja aus 8 Bytes — und genau diese 8 Bytes stellt das Zeichenfeld symbolisch dar.
Taste 1: Berechnung des selbsterstellten Zeichens
Sobald man sein Zeichen gemalt hat, muß es berechnet werden. Hierfür ist im Menü der Programmpunkt 1 vorgesehen. Drückt man diese Taste, so wird gefragt: »Welche Taste?« Man drückt nun die Taste, der man das soeben erstellte Zeichen zuordnen möchte, und genau das Zeichen, das zu der gedrückten Taste gehört, wird nun durch das im Zeichenfeld definierte Zeichen ersetzt.
Ein Beispiel: Füllt man das Zeichenfeld ganz mit Sternchen aus, läßt das Feld berechnen und drückt dann die Taste »A«, so wird überall auf dem Bildschirm dort, wo eben noch ein »A« gestanden hat, ein reverses Quadrat erscheinen. Nette Spielchen kann man zum Beispiel mit der Space-Taste machen: dann wird nämlich überall da, wo ein Space auf dem Bildschirm ist (und das sind ja üblicherweise eine ganze Menge), das eben definierte Zeichen gedruckt. Auf diese Weise kann man den Bildschirm etwas interessanter gestalten.
Taste 2:
Sobald das neue Zeichen berechnet und ausgedruckt ist, kann man wieder beliebig im Zeichenfeld herumhantieren. Will man jedoch ein ganz anderes Zeichen konstruieren, so drückt man einfach Taste 2, und flugs ist das Zeichenfeld wieder »sauber« — das gerade definierte Zeichen wird dabei natürlich nicht gelöscht. Zusätzlich zum reinen Konstruieren von Grafik-Zeichen gibt es jedoch auch noch andere Programmfunktionen:
Taste 3: ROM-Zeichen auslesen
Mit Hilfe dieses Programmpunktes kann man sich ein beliebiges Zeichen aus dem ROM-Zeichensatz herholen. Hierbei wird das gewünschte Zeichen in das Zeichenfeld hineingePOKEt und die Daten dieses Zeichens rechts daneben ausgegeben. Selbst wenn Sie also den Buchstaben »A« als ein reverses Quadrat definiert haben sollten, so erscheint — sofern Sie Taste 3 und danach »A« drücken — auf dem Zeichenfeld das gute, alte »A« wieder (denn im ROM-Zeichensatz bleibt natürlich alles beim alten).
Taste 4: Eigene Zeichen auslesen
Natürlich können Sie nicht nur Zeichen aus dem ROM holen, sondern auch aus dem kopierten Zeichensatz, in dem Sie bisher munter herummanipuliert haben. Sie haben zum Beispiel aus dem »O« ein Smiley-Gesicht gemacht und wollen er gerne in vergrößerte Form wiedersehen, um etwa Korrekturen oder ähnliches vorzunehmen — bitte sehr! Drücken Sie die Taste 4, dann ein »O« und Ihr eigenes Zeichen steht im Zeichenfeld — samt den dazugehörigen Daten rechts nebenan.
Taste 8: Restore
Nun kann es vorkommen, daß Sie genug haben von Ihren eigenen Zeichen. Eigentlich wollten Sie jetzt ganz gern wieder die alten, »normalen« Zeichen anstelle der vielen Smiley-Gesichter und reversen Quadrate sehen.
Wenn Sie Taste 8 drücken, wird einfach der ROM-Zeichensatz wieder ins RAM kopiert; und da dieser Teil in Maschinensprache geschrieben ist, geht das ziemlich schnell vonstatten.
Will man übrigens nur ein einziges Zeichen wieder in den Ursprungszustand versetzen, so geht man am besten so vor: Man holt sich das (alte) Zeichen aus dem ROM (mit Taste 3) und läßt es mit Taste 1 wieder in den neuen Zeichensatz hineinkopieren. Mit dieser Methode kann man zum Beispiel auch einen Art Geheimcode entwickeln: Man tauscht einfach die Zeichen im ROM untereinander aus (statt eines A ein B, statt eines U ein X und so weiter). Nach einigem Umdefinieren bekommt man einen ganz eigenartigen Buchstabensalat auf dem Bildschirm.
Taste 9: Bild zeichnen
Nehmen wir an, Sie haben aus den etwas eintönigen Grafikzeichen des C 64 interessantere Zeichen zusammengebastelt: Leitern, Mauerwerke, Treppen und ähnliches. Sie würden aber ganz gerne sehen, wie diese Zeichen im Zusammenhang wirken; wie es etwa aussieht, wenn ein Leiterstück unter dem anderen steht, daneben ein Mauerwerk und so weiter. Dazu drücken Sie Taste 9 und können jetzt mit Ihren selbsterstellten Zeichen den ganzen Bildschirm vollmalen. Wenn Sie wieder ins Menü zurückwollen, drücken Sie einfach »Cursor Home« (steht auch auf dem Bildschirm).
Ein Hinweis zu diesem Programmpunkt: Der Cursor ist beim Bildmalen aus Gründen der Programmiervereinfachung nicht immer sehr gut sichtbar, manchmal »legt« er sich sogar ab, wenn er schnell über den Bildschirm bewegt wird. Das sollte Sie aber nicht weiter stören, schließlich ist diese Programmfunktion nur als Hilfe gedacht für einen schnellen Überblick.
Tasten 6 und 7: Abspeichern und Laden
Wenn Sie eigene Zeichen definiert haben, möchten Sie diesen Zeichensatz vielleicht abspeichern, um ihn später noch einmal verwenden zu können. Drücken Sie einfach Taste 6 und geben einen Dateinamen ein — der kopierte Zeichensatz mit Ihren selbsterstellten Zeichen wird nun auf Diskette gespeichert (nur der Großbuchstaben-Zeichensatz). Er nimmt dabei genau 44 Blöcke auf der Diskette ein. Mit Taste 7 können Sie schließlich einen abgespeicherten Zeichensatz wieder laden (der aktuelle Zeichensatz wird dabei überschrieben).
Sollten Sie einmal versehentlich 6 oder 7 gedrückt haben, so kommen Sie wieder ins Menü zurück, wenn Sie »N« + »RETURN« eingeben!
Taste 5: Daten eingeben
Eine letzte Programmfunktion bietet die Eingabe von Daten, die ein Zeichen definieren. Dies ist eine Alternative zum Konstruieren eines Zeichens im Zeichenfeld. Zu beachten ist, daß nur Zahlen von 0 bis 255 eingegeben werden können (dies wird allerdings vom Programm sichergestellt; negative und Zahlen größer als 255 werden vom Programm nicht angenommen). Nach Eingabe des 8. Bytes erscheint dann die übliche Frage »Welche Taste!« und nach Eingabe dieser Taste das bekannte Sternchenbild auf dem Zeichenfeld.
Die Erklärung der Taste 0 (Programmende) erübrigt sich wohl. Zu erwähnen ist hierbei nur, daß der kopierte Zeichensatz auch nach Beendigung des Programms weiterhin zur Bildschirmgestaltung benutzt wird; auch der Bildschirm selbst sitzt weiterhin an der Adresse 50176 (und nicht wie üblich, bei 1024). Wollen Sie also wieder einen ganz gewöhnlichen Bildschirm mit ganz gewöhnlichen Grafik-Zeichen haben, so geht das am einfachsten nur durch Aus- und Anschalten des Computers (auch die Run/Stop-Restore-Taste wurde im Programm blockiert).
Eine kleine Bemerkung am Rande zum eigenen Programmieren: Sehr oft kommt es ja vor, daß man einen Satz von einem Programm aus mitten in dem Bildschirm hineinschreiben will. Üblicherweise geht man so vor, daß man die Cursor-Steuerzeichen in die Print-Zeile so oft einfügt, bis der gesuchte Platz gefunden ist. Für Programmlistings ist das etwas unübersichtlich. Es gibt aber noch eine andere Methode, die ich in diesem Programm benutzt habe (und zwar am Anfang fast jedes Programmpunktes): will man zum Beispiel einen Buchstaben in die 14. Zeile, 5. Spalte schreiben, so gibt man zunächst POKE 214,13
ein, danach ein PRINT-Kommando und dann PRINTTAB(4)»X«.
In Adresse 214 steht nämlich die Zeilenposition des Cursors. Um diese richtig einzusetzen, muß man allerdings noch einen PRINT-Befehl nachschicken: Cursor in Zeile 13 + PRINT-Befehl ergibt dann Zeile 14!
(Volker Bühn / gk)Programmablauf-Plan:
Zeile | Beschreibung |
1-7 | Programmname und Adresse des Autors |
10-30 | Einlesen und Aufruf der Maschinenroutine, die den Zeichensatz vom ROM ins RAM an die Adresse 51200 kopiert |
40-140 | Menü und Zeichenfeld werden gezeichnet |
300-460 | Abfrage der Tastatur: 1. Zeichnen im Zeichenfeld 2. Aufruf der einzelnen Programmfunktionen |
500-520 | Unterprogramm zum Printen des Zeichenfeldes |
1000-1530 | Die einzelnen Unterprogramme zur Steuerung des Cursors, der Space-Taste und des Sternchens, um im Zeichenfeld ein Zeichen zu erzeugen. |
2000-8040 | Einzelne Programmfunktionen: |
2000-2120 | Berechnung eines erstellten Zeichens; Zuordnung dieses neuen Zeichens in den Zeichensatz; Printen der Daten des Zeichens |
3000-3150 | Ausgabe eines Zeichens aus dem ROM- Zeichensatz; Printen der Daten dieses Zeichens |
4000-4150 | Ausgabe eines Zeichens aus dem kopierten Zeichensatz, Printen der Daten dieses Zeichens |
5000-5050 | Neuer Bildschirm zum Zeichen mit dem selbsterstellten Zeichensatz Die Zeile 5050 dient zur Cursor- Steuerung und Ausdrucken eines Zeichens auf dem Bildschirm |
6000-6120 | Abspeichern eines selbsterstellten Zeichensatzes auf Diskette |
7000-7120 | Laden eines Zeichensatzes von Diskette |
8000-8040 | Eingabe von 8 Bytes zur Definition eines Zeichens In 8030 wird sichergestellt, daß keine Zahl größer als 8 Bit ist |
10000-10040 | POKEn des Maschinenprogramms nach 828 |
10100-10150 | DATA-Zeilen des Maschinenprogramms (in Hexcodes) |
Wichtige Programmzeilen:
Erstellung eines eigenen Zeichens:
2050-2070 | Hier werden die Bytes des selbsterstellten Zeichens aus dem Zeichenfeld gelesen, berechnet und in B(1) bis B(8) abgelegt. |
2080 | Die Bytes des Zeichens werden nun in die Stelle des Zeichensatzes gePOKEt, die der Benutzer durch die Tastenabfrage definiert hat. ROM-Zeichen: |
3050 | Verhindern von Interrupts |
3060-3070 | Im ROM-Zeichensatz wird das vom Benutzer festgelegte Zeichen in B(1) bis B(8) sowie C(1) bis C(8) abgelegt |
3080 | Zulassen von Interrupts |
3090-3110 | Die Bytes B(1) bis B(8) werden in das Zeichenfeld in Form von Sternchen (*) hinein gePOKEt |
Eigenes Zeichen holen: | |
4060-4070 | wie 3060-3070, nur daß im kopierten Zei chensatz gesucht wird |
4080-4100 | wie 3090-3110 Bild zeichnen: |
5050 | In den Adressen 209,210 und 211 steht die aktuelle Cursorposition. Dies verhindert, daß der Cursur auf den Bildschirm wie ein normales Zeichen gePRINTet wird. |
Liste der wichtigsten Variablen
Z | Position des anfänglichen Cursors im Zeichenfeld (ist immer 50776) |
PU$ NO$ |
Strings, um Punkte beziehungsweise Leerstellen zu PRINTen |
A | Variable, die die aktuelle Cursorposition im Zeichenfeld angibt |
G$ | Stringvariable zur Tastaturabfrage |
B(1) bis B(8) C(1) bis C(8) |
Arrays, die die 8 Bytes eines Zeichens enthalten |
G | Bildschirmcode eines Zeichens |
C | Cursorposition beim Bildzeichnen |
N$ | Name einer einzulesenden beziehungsweise abzuspeichernden Datei |
A$ | Hexcode aus den DATA-Zeilen |
HN,LN | Hi-Nibble beziehungsweise Lo-Nibble von A$ |
I,J,K | Schleifenvariable |
1 rem ************************ 2 rem * zeichen-editor/c64 * 3 rem * von volker buehn * 4 rem * b2,14 * 5 rem * 6800 mannheim * 6 rem * am 6.7.1984 * 7 rem ************************ 10 print"{clr}":print:print" bitte warten!":restore:poke657,128:poke792,134:poke793,234 20 gosub10000:rem einlesen der maschinenroutine 30 sys828 :rem aufruf derselben 40 poke53280,0:poke53281,0:print"{clr} {rvon}selbsterstellte zeichen{rvof}" 50 print:print"1 zeichen berechnen 6 abspeichern" 60 print"2 neues zeichen 7 laden" 70 print"3 rom-zeichen 8 restore" 80 print"4 zeichen holen 9 bild zeichnen" 90 print"5 datas eingeben 0 programmende" 100 z=50776 110 pu$="........ " 120 no$=" " 130 gosub510:iffl=1thenreturn 140 a=z:pokea,peek(a)or128 300 getg$:ifg$=""then300 310 ifg$="{up}"thengosub1020 320 ifg$="{down}"thengosub1120 330 ifg$="{rght}"thengosub1220 340 ifg$="{left}"thengosub1320 350 ifg$=" "thengosub1420 360 ifg$="*"thengosub1520 370 ifg$="1"thenpokea,peek(a)and127:goto2000 380 ifg$="2"thenrun100 390 ifg$="3"then3000 400 ifg$="4"then4000 405 ifg$="5"then8000 410 ifg$="6"then6000 420 ifg$="7"then7000 430 ifg$="8"then30 440 ifg$="9"then5000 450 ifg$="0"thenprint"{clr}":end 460 goto300 500 rem feld zeichnen 510 print"{home}":fori=1to14:print:next 520 fori=1to8:printpu$:next:return 1000 : 1010 rem cursor nach oben 1020 ifpeek(a-40)=32thenreturn 1030 pokea,peek(a)and127:a=a-40:pokea,peek(a)or128:return 1100 : 1110 rem cursor nach unten 1120 ifpeek(a+40)=32thenreturn 1130 pokea,peek(a)and127:a=a+40:pokea,peek(a)or128:return 1200 : 1210 rem cursor nach rechts 1220 ifpeek(a+1)=32thenreturn 1230 pokea,peek(a)and127:a=a+1:pokea,peek(a)or128:return 1300 : 1310 rem cursor nach links 1320 ifpeek(a-1)=32thenreturn 1330 pokea,peek(a)and127:a=a-1:pokea,peek(a)or128:return 1400 : 1410 rem space-taste im zeichenfeld 1420 ifpeek(a+1)=32thenpokea,174:return 1430 pokea,46:a=a+1:pokea,peek(a)or128:return 1500 : 1510 rem "*" im zeichenfeld 1520 pokea,42:ifpeek(a+1)=32thenpokea,peek(a)or128:return 1530 a=a+1:pokea,peek(a)or128:return 2000 rem ******************* 2001 rem *zeichen berechnen* 2002 rem ******************* 2010 poke214,14:print:printtab(14)"welche taste? "; 2020 getg$:ifg$=""then2020 2030 printg$:g=peek(z+28)*8:k=0:a=z 2040 printtab(10);no$:printtab(10);no$:print"{up}{up}{up}{up}" 2050 forj=1to8:fori=a+7toastep-1 2060 ifpeek(i)=42thenb(j)=b(j)+2^k 2070 k=k+1:nexti:a=a+40:k=0:nextj 2080 fori=51200+gto51207+g:k=k+1:pokei,b(k):next 2090 printtab(10);no$ 2100 printtab(15);b(1);b(2);b(3);b(4) 2110 printtab(15);b(5);b(6);b(7);b(8) 2120 poke(z+11),g/8:fori=1to8:b(i)=0:next:fl=0:goto140 3000 rem ************** 3001 rem * rom-zeichen* 3002 rem ************** 3010 gosub510 3020 poke214,14:print:printtab(14)"welche taste? "; 3030 getg$:ifg$=""then3030 3040 k=0:a=z:printg$:g=peek(z+28)*8 3050 poke56334,peek(56334)and254:poke1,peek(1)and251 3060 fori=53248+gto53255+g 3070 k=k+1:b(k)=peek(i):c(k)=b(k):next:k=0 3080 poke1,peek(1)or4:poke56334,peek(56334)or1 3090 forj=1to8:fori=7to0step-1 3100 ifb(j)>=2^ithenb(j)=b(j)-2^i:poke(a+7-i),42 3110 nexti:a=a+40:nextj 3120 print"{up}"tab(10);no$ 3130 printtab(15);c(1);c(2);c(3);c(4) 3140 printtab(15);c(5);c(6);c(7);c(8) 3150 fori=1to8:b(i)=0:next:goto140 4000 rem ************************* 4001 rem * eigenes zeichen holen * 4002 rem ************************* 4010 gosub510 4020 poke214,14:print:printtab(14)"welche taste? "; 4030 getg$:ifg$=""then4030 4040 printg$:g=peek(z+28)*8:k=0:a=z 4050 iffl=1then4080 4060 fori=51200+gto51207+g 4070 k=k+1:b(k)=peek(i):c(k)=b(k):next:k=0 4080 forj=1to8:fori=7to0step-1 4090 ifc(j)>=2^ithenc(j)=c(j)-2^i:poke(a+7-i),42 4100 nexti:a=a+40:nextj 4110 print"{up}"tab(10);no$ 4120 iffl=1then2080 4130 printtab(15);b(1);b(2);b(3);b(4) 4140 printtab(15);b(5);b(6);b(7);b(8) 4150 poke(z+11),g/8:fori=1to8:b(i)=0:next:goto140 5000 rem **************** 5001 rem * bild malen * 5002 rem **************** 5010 print"{clr}";:poke53280,0:poke53281,1:poke646,0:poke650,128 5020 print"[home=zurueck]" 5030 poke204,0:getg$:ifg$=""then5030 5040 ifg$="{home}"thenpoke204,1:poke646,6:poke650,0:goto40 5050 c=peek(209)+peek(210)*256+peek(211):pokec,peek(c)and127:printg$;:goto5030 6000 rem ************* 6001 rem * speichern * 6002 rem ************* 6010 print"{clr}":print:print" speichern des zeichensatzes" 6020 print:print"(input 'n' , wenn zurueck)" 6030 print:print:print" name der datei?" 6040 inputn$ 6050 ifn$="n"then40 6060 open2,8,2,n$+",s,w" 6070 close1:open1,8,15:input#1,oa:ifoa<>0then6010 6080 fori=51200to53248 6090 s%=peek(i) 6100 print#2,s% 6110 nexti 6120 close2:goto40 7000 rem ********* 7001 rem * laden * 7002 rem ********* 7010 print"{clr}":print:print" laden eins zeichensatzes" 7020 print:print"(input 'n', wenn zurueck)" 7030 print:print:print" name der datei?" 7040 inputn$ 7050 ifn$="n"then40 7060 open2,8,2,n$+",s,r" 7070 close1:open1,8,15:input#1,oa:ifoa<>0then7010 7080 fori=51200to53248 7090 input#2,s% 7100 pokei,s% 7110 nexti 7120 close2:goto40 8000 rem ****************** 8001 rem * datas eingeben * 8002 rem ****************** 8010 print"{clr}":print:print" daten fuer ein zeichen eingeben" 8020 fori=1to8:printi". byte":inputb(i):c(i)=b(i) 8030 ifb(i)>255orb(i)<0theni=i-1 8040 next:fl=1:gosub40:goto4010 10000 rem **************************** 10001 rem * maschinenroutine-eingabe * 10002 rem **************************** 10010 fori=0to58:reada$ 10020 hn=(asc(left$(a$,1))-48)*16:ifhn>144thenhn=hn-112 10030 ln=asc(right$(a$,1))-48:ifln>9thenln=ln-7 10040 hn=hn+ln:poke(828+i),hn:next:return 10100 data78,a9,31,85,01,a9,00,85,64,a9 10110 datad0,85,63,a9,c8,85,65,a2,10,a0 10120 data00,b1,62,91,64,c8,d0,f9,e6,63 10130 datae6,65,ca,d0,f2,a9,37,85,01,58 10140 dataa9,12,8d,18,d0,a9,94,8d,00,dd 10150 dataa9,c4,8d,88,02,20,44,e5,60