Nichts ist ewig
Zugegeben, der Commodore 64 hat einige Nachteile. Aber warum sollte man sich damit abfinden? Alles kann mit dem ROM-Change-Programm verändert werden.

Jedem Computer, auch dem Commodore 64 wird ab Werk eine bestimmte Ausstattung an Software mit auf den Weg gegeben. Damit ist nicht die Demo-Diskette gemeint, sondern die fest im Computer eingebaute, auf PROMs gespeicherte Firmware. Sie sorgt dafür, daß der Commodore überhaupt auf Eingaben reagiert oder einen Basic-Befehl ausführt.
Der gesamte C 64 (und jeder andere Computer) ist eigentlich nichts anderes als eine Anzahl miteinander verdrahteter Baugruppen, die allein zu nichts fähig sind. Leben eingehaucht wird dem ganzen erst durch ein sehr wichtiges Programm, das sogenannte Betriebssystem. Dieses Programm initialisiert und verwaltet die gesamte Hardware. Beim Commodore 64 ist es genau 8 KByte lang und liegt im Bereich von $E000 bis $FFFF. Der zweite wichtige Festwertspeicher ist der Basic-Interpreter. Er ist ein Übersetzungsprogramm, das eine Anweisung in ein maschinengerechtes Signal umwandelt. Auch der Interpreter benötigt 8 KByte und liegt im Bereich von $A000 bis $C000. Jetzt fehlt nur noch der Charakter-Set von 4 KByte, der ebenfalls in einem eigenen ROM untergebracht ist.
Diese drei Programme sind für das äußere Erscheinungsbild und die Funktionalität des Commodore 64 verantwortlich. Hier eröffnet sich ein extrem interessanter Bereich der Programmierung. Dazu bieten sich zwei Wege an: Der erste Weg beruht auf der glücklichen Tatsache, daß es beim Commodore möglich ist, den RAM-Bereich unter einem ROM zu nutzen. In der Praxis sieht das folgendermaßen aus: Die Speicherinhalte des ROMs werden zum Bearbeiten in den darunter liegenden RAM-Bereich kopiert. Ob nun das RAM oder das ROM aktiv ist, entscheidet das 6510 Datenrichtungsregister (Speicherstelle 1). Vom Basic aus ist dieses Register allerdings nur dann zu verändern, wenn sowohl der Basic-Interpreter, als auch das Betriebssystem in das RAM kopiert wurden. Das geschieht entweder mit einer POKE-Schleife oder mit dem ROM-Change Programm. (Betriebssystem kopieren: FOR A = 57344 TO 65535: POKE A,PEEK (A): NEXT)
(Basic kopieren: FOR A = 40960 TO 49152: POKE A, PEEK (A) : NEXT). Der Normalwert dieses Registers ist 55 (probieren Sie es aus). Soll das RAM (für Basic) selektiert aktiv sein, muß hier der Wert 54 eingeschrieben werden. Für Basic und Betriebssystem zusammen beträgt der Wert 53. Alle Veränderungen des Basic und Betriebssystems sind dann aktiv. Bekannte Programme wie Quickcopy und viele Basic-Erweiterungen funktionieren nach diesem Prinzip.
Der zweite und wesentlich reizvollere Weg die Firmware zu beeinflussen ist, das Betriebssytem dauerhaft zu verändern. Dazu ist aber ein Eingriff im Computer notwendig, denn die oben beschriebenen Bausteine müssen gegen andere ausgetauscht werden. Wer also noch Garantie auf seinen Commodore hat, sollte besonders vorsichtig sein. Im ersten Teil dieses Artikels wollen wir, zum Einüben sozusagen, die Funktionstasten des Commodore 64 mit bestimmten, oft gebrauchten Werten belegen. Im zweiten Teil wird das Hypra-Load aus der Ausgabe 10/84 im Betriebssytem verankert. Der Nachteil dieser Änderungen soll nicht verschwiegen werden. Da alle neuen Funktionen natürlich Speicherplatz benötigen, müssen andere Teile des Betriebssystems entfallen. Wir haben uns entschlossen, die Kassettenroutinen ab $F800 herauszunehmen und zu überschreiben. Das Laden von Kassette wird dadurch unmöglich, es sei denn, das alte Betriebssystem wird parallelgeschaltet.

Bevor wir nun auf das Hilfsprogramm für diese Veränderungen, das ROM-Change-Programm, eingehen, sollen die Hardware-Voraussetzungen für die Änderung des Betriebssystems erklärt werden. Nach dem Öffnen des Computers finden wir auf der linken hinteren Seite drei kleine Bausteine, hinter denen auf der Platine die Bezeichnungen U3 bis U5 stehen. U3 ist der Basic-Interpreter, U4 das Betriebssytem, U5 das Charakter-ROM (Bild 1). Heute interessieren wir uns aber nur für den U4-Steckplatz. Wer Glück hat, findet dort einen Stecksockel. Wer Pech hat, muß seinen Händler oder einen Lötkolbenfachmann bitten, ihm hier einen Stecksockel einzulöten. Anstelle des dort befindlichen ROMs kann aber, und das ist die wesentlichste Veränderung, auch ein EPROM stecken. Am besten eignen sich hierzu die 2564-Typen, denn sie sind pinkompatibel zu den Commodore-ICs. Leider sind sie sehr schwer zu beschaffen. Im Normalfall wird aber wahrscheinlich ein 2764-EPROM Verwendung finden. Der Nachteil dieses EPROMs besteht in einer anderen Belegung der Anschlußpins. Hier hilft allerdings ein einfacher Adaptersockel. Dazu braucht man einen 24- und einen 28-Pin-Stecksockel. Diese beiden Sockel werden miteinander verdrahtet (Bild 2 und 3). Bild 2 zeigt die beiden Sockel mit den Beinen nach unten stehend. Der kleine Sockel steckt später im U4-Steckplatz, der größere Sockel trägt das geänderte Betriebssystem und steckt auf dem kleineren Sockel. Vor dem Einbau ist es aber ratsam, alle Kontakte auf richtigen Anschluß und Leitfähigkeit zu überprüfen. Schon ein einziger falscher Anschluß führt zum »Absturz« des gesamten Systems. Bild 4 zeigt, wie der neue Sockel mit der EPROM-Kerbe zur Gehäuse-Rückseite auf der Platine steht.
Nun aber zur Praxis, dem ROM-Change-Programm. Es ermöglicht das gefahrlose Ändern und Ausprobieren aller Umprogrammierungen. Dazu wird nach dem Start der selektierte ROM-Bereich (Betriebssytem oder Basic) ab Adresse $6000 ins RAM kopiert. Das geschieht mit einem kurzen Maschinenprogramm, das im Kassettenpuffer steht. Das Programm ist für den Betrieb mit einem Diskettenlaufwerk gedacht, läuft aber auch im Kassettenbetrieb, wenn ein eigener Monitor zum Laden und Abspeichern Verwendung findet. Nach dem Kopieren erscheint das Hauptmenü, von dem aus alle wichtigen Funktionen erreichbar sind. Die erste dient dem Einlesen von fest im Programm eingebauten DATA-Zeilen. In unserem Beispiel sind ab Zeilennummer 8000 die DATAs für die Funktionstasten einprogrammiert. Hier können natürlich auch eigene Werte stehen. Die erste Zahl gibt dabei die reelle Adresse an, ab der die DATAs geschrieben werden sollen (zum Beispiel 57612 = $E10C). Der Computer errechnet dann die entsprechende Stelle im RAM. Die zweite Zahl gibt an, wieviel Bytes übertragen werden sollen. Die dritte Zahl ist die Prüfsumme. Danach folgen die Programm-DATAs. Bei einem Prüfsummenfehler zeigt der Computer die falsche Prüfsumme an. Eigene Daten werden einfach an die vorhandenen DATAs angehängt. Die ersten drei Bytes müssen natürlich auch die obige Bedeutung haben. Als letzte Zeile muß stehen: DATA 0, da es sonst zu einem OUT OF DATA ERROR kommt. Der zweite Menüpunkt liest Maschinenprogramme direkt an die vorgesehene Stelle. Damit kann man beispielsweise ein Programmfile, das von einem Assembler erzeugt wurde, direkt einlesen. Wichtig ist, daß die Startadresse des Programms im ROM-Adreßbereich beziehungsweise im Bereich $6000 liegt. Der mit Punkt 3 wählbare Minimonitor hilft beim schnellen Ändern einzelner Bytes im hexadezimalen Zahlensystem. Die Startadresse entspricht dabei der Adresse im ROM. Der Minimonitor wird durch Eingabe einer Zahl größer $FF oder des Buchstabens X verlassen. Will man keine Speicherstelle ändern, genügt RETURN für die Übernahme des alten Wertes. Für größere Änderungen reicht dieser Minimonitor natürlich nicht mehr aus. Dazu wird mit Punkt 4 ein eigener Monitor aktiviert. Dieser muß allerdings vor dem Start des ROM-Change-Programms bereits geladen sein. Der Monitor darf im Bereich von $8000 bis $9FFF oder von $C000 bis $CFFF stehen. Zum Starten genügt das Eingeben der Startadresse des Monitors hexadezimal!). Mit den Menüpunkten 5 und 6 wird der Speicherbereich von $6000 bis $7FFF geladen beziehungsweise abgespeichert. Das Laden eines kompletten Betriebssystems dauert allerdings mehrere Minuten. Einer der wichtigsten Menüpunkte ist aber der siebte. Er startet das gerade veränderte oder geladene neue Betriebssystem (es wird natürlich zuerst in seinen richtigen Speicherbereich verschoben). Vor dem Ausprobieren dieser Funktion muß in jedem Fall der Punkt 5 angewählt werden, wenn die Änderungen nicht verlorengehen sollen. Es kann vorkommen, daß das Betriebsystem beim Starten »abstürzt«. Dann genügt ein RESET und die erneute Aktivierung des geänderten ROMs durch POKE 1,53.
2764 | 2364 | |
1,28,27,26 | → | 24 |
2 | → | 21 |
3 | → | 1 |
4 | → | 2 |
5 | → | 3 |
6 | → | 4 |
7 | → | 5 |
8 | → | 6 |
9 | → | 7 |
10 | → | 8 |
11 | → | 9 |
12 | → | 10 |
13 | → | 11 |
14 | → | 12,20 |
15 | → | 13 |
16 | → | 14 |
17 | → | 15 |
18 | → | 16 |
19 | → | 17 |
20,14 | → | nicht angeschl. |
21 | → | 19 |
22 | → | 20 |
23 | → | 18 |
24 | → | 22 |
25 | → | 23 |
1,28,27,26 | → | 24 |
Sind alle Tests im RAM positiv verlaufen, können wir uns an die Speicherung des neuen Betriebssytems auf EPROMs heranwagen. Das neue Betriebssystem wird von Diskette in den RAM-Bereich ab $6000 absolut, das heißt mit LOAD"Ihr Betriebssytem",8,1 geladen. Ab dieser Adresse beginnt auch die Programmierung des EPROMs. Sie endet bei $8000 (8 KByte). Das neue EPROM wird anschließend in den beschriebenen Stecksockel eingesetzt und in Steckplatz U4 befestigt. Fertig ist das Betriebssytem.
Was noch zu klären bleibt, ist die im ROM-Change-Programm bereits eingebaute Funktionstastenbelegung. Die Abfrage der Tastatur geschieht im Betriebssytem über den Interrupt. Wird eine Taste gedrückt, hält der Prozessor das laufende Programm an und springt in die Interrupt-Unterroutine bei $EA31. Dort wird festgestellt, welche Taste gedrückt wurde. Der ASCII-Code der Taste wird im Tastaturpuffer gespeichert. Das Funktionstastenprogramm greift an dieser Stelle ein, indem es den Wert der gedrückten Tasten mit den ASCII-Codes für die Funktionstasten (133-140) vergleicht. Stimmt der Wert nicht überein, so wird das Programm normal weitergeführt. Ansonsten vergleicht das Programm den Tastenwert mit einer Tabelle, in der die Belegungen für die Funktionstasten stehen. Hat das Programm die zur Funktionstaste gehörige Belegung gefunden, wird diese in den Tastaturpuffer geschrieben. Damit auch Programme, die auf einer Abfrage der Funktionstaste aufbauen, funktionieren, wurde eine Autoabschalt-Unterroutine mitprogrammiert. Kommt es dennoch einmal zu Schwierigkeiten, werden die Funktionstasten mit POKE 2,1 abgeschaltet und mit POKE 2,0 wieder aktiviert.
Abschließend sei nochmals darauf hingewiesen, daß alle Arbeiten an der Hardware des C 64 mit einem nicht geringen Zerstörungsrisiko verbunden sind. Wer also im Umgang mit Lötkolben und EPROMs nicht geübt ist, sollte sich an einen Fachmann wenden.
(Arnd Wängler/Ernst Schöberl/aa)
Beim Abtippen des Programms sind die Werte in Klammern am Ende einer Zeile nicht miteinzugeben. Sie sind erst für eine spätere Ausgabe von Bedeutung. Unterstrichene Buchstaben sind mit der Shift-Taste, überstrichene mit der Commodore-Taste einzugeben. Bei Ausdrücken in eckigen Klammern ist die jeweilige Taste zu drücken.
100 rem speicherplatz fuer rom vor basic schuetzen 110 poke 55,0:poke 56,96:clr 120 poke 53272,23:poke 53280,2 130 gosub 1130 140 if peek(53247)<>33 then 160 150 if peek(53246)=0 then gosub 3600:goto 500 155 gosub 3700:goto 500 160 print"{down}{down}":print" Druecken sie":print 170 print:print tab(7);"1 um Kernal ($E000-$FFFF)" 180 print:print tab(7);"2 um Basic ($A000-$BFFF)" 190 print:print:print tab(11);"in RAM ab $6000 zu kopieren." 200 get a$:if a$=""then 200 210 if a$="1"then poke 32767,255:gosub 3600:goto 240 220 if a$<>"2"then 200 230 gosub 3700 240 gosub 1600 300 poke k+8,int(an/256) 310 poke k+29,int(en/256) 340 sys k :rem schieberoutine 500 poke 53280,5:gosub 1130:print:poke 53247,0 505 print:print:print" Erweiterung des ROMs durch":print 506 print tab(8);"1: Data Zeilen einlesen" 510 print tab(8);"2: Programm von Disk" 520 print tab(8);"3: Hexmonitor" 530 print tab(8);"4: Eigenen Monitor" 540 print tab(8);"5: Abspeichern des neuen ROMs" 545 print tab(8);"6: Laden von geaendertem ROM" 546 print tab(8);"7: Starten des neuen ROMs" 547 print tab(8);"8: Programm beenden" 550 print:print:print tab(10);"Bitte waehlen sie!" 560 get a$:if a$=""then 560 570 i=val(a$):if i<1 or i>8 then 560 580 on i goto 1000,2000,3000,4000,5000,6000,2500,3500 999 rem data zeilen einlesen 1000 print"{clr}":print:print tab(10);"Lesen der Data-Zeilen":print:print 1010 gosub 1600 1020 read a:if a=0 then 500 1030 read b:rem anzahl der bytes 1032 read p1:rem pruefsumme 1033 p2=0 1035 d=a:gosub 1300:print"$";a$;"-";:d=a+b-1:gosub 1300:print"$";a$; 1040 for i=a-of to a-of-1+b 1050 read d:poke i,d 1051 p2=p2+d:if p2>65535 then p2=p2-65536 1053 next i 1055 if p2<>p1 then 1070 1057 print" ok" 1060 goto 1020 1070 print"pruefsumme falsch: ";p2 1080 get a$:if a$=""then 1080 1090 goto 1020 1100 stop 1130 print"{clr}":print tab(8);"Programm zum Aendern des":print 1140 print tab(12)"Betriebssystems":print:print 1150 print" geschrieben von Ernst Schoeberl (1984)" 1160 return 1199 rem hex in dez umwandlung 1200 d=0:for j1=0 to len(a$)-1 1210 b=asc(right$(a$,1)):a$=left$(a$,len(a$)-1) 1220 b=b-48:if b>9 then b=b-7 1230 if b<0 or b>16 then 1280 1240 d=d+b+16^j1 1250 next j1 1260 return 1280 d=-1:return 1299 rem dez in hex 1300 j2=int(log(d)/log(16)):a$="" 1310 for j1=j2 to 0 step-1 1320 k1=int(d/16^j1):d=d-k1*16^j1 1330 if k1>9 then k1=k1+7 1340 k1=k1+48:a$=a$+chr$(k1) 1350 next j1 1360 return 1600 restore:k=828:for i=k to k+32:read a:poke i,a:next i 1610 data 120,160,0,132,251,132,253,169,224,133,252,169,96,133,254,177,251,145 1620 data 253,200,208,249,230,252,230,254,165,252,201,0,208,239,96 1630 return 1999 rem programm von disk in rom einfuegen 2000 gosub 1130:print 2010 print tab(2);"Einfuegen eines Programms von Disk" 2020 print:print:print:input"Filename:";a$ 2030 open 1,8,0,a$ 2033 gosub 6100:i=asc(a$):gosub 6100:i=i+256*asc(a$):if st>0 then 6200 2035 print:print"found ";a$ 2036 if i<an then 2110 2040 get#1,a$:a$=a$+chr$(0) 2050 if st>0 then 2100 2060 poke i-of,asc(a$):i=i+1:goto 2040 2100 close 1:goto 500 2109 rem programm nicht in rombereich 2110 print"ROM von $";:d=an:gosub 1300:print a$;"-";:d=an+8191:gosub 1300:print a$ 2120 print"Startadresse des Programms bei $";:d=i:gosub 1300:print a$ 2130 input"Neue Startadresse (muss im ROM-Bereich liegen:";a$ 2140 gosub 1200:if d=-1 or d<an then 2100 2150 i=d:goto 2040 2499 rem rom starten 2500 gosub 1600 2510 poke k+8,160:poke k+29,0 2515 poke k+12,160 2520 sys k 2525 k=828 2530 poke k+8,96 2540 poke k+9,128 2550 poke k+12,int(an/256) 2560 sys k 2565 print"{clr}":print">Neues Rom aktiviert":print 2570 poke 1,53:input"Reset (j/n)";a$:if a$="j"then sys 64738 2580 goto 500 2999 rem monitor fuer einzelne speicherstellen 3000 print"{clr}":print"*** Monitor {SHIFT-*}{SHIFT-*}{SHIFT-*}":print:print 3010 input"Startadresse:";a$:gosub 1200 3020 i=d-of:if i=-1 then 3010 3030 d=i+of:gosub 1300:print a$;" ";:d=peek(i):gosub 1300:print a$;" "; 3040 input":";a$ 3050 if a$=chr$(13)then 3070 3060 gosub 1200:if d=-1 or d>255 then 3090 3065 poke i,d 3070 i=i+1:goto 3030 3090 print"Ende (j/n)" 3095 get a$:if a$="n"then 3010 3096 if a$<>"j"then 3095 3100 goto 500 3500 poke 53247,0:end 3599 rem werte fuer kernal ram 3600 an=14*4096:en=0:of=8*4096:poke 53246,0:return 3699 rem werte fuer basic rom 3700 an=10*4096:en=12*4096:of=4*4096:poke 53246,1:return 3999 rem eigener monitor 4000 print"{clr}":print:print" Eigener Monitor" 4010 print:print:input"startadresse (hex):";a$ 4020 if len(a$)>4 then 4000 4030 gosub 1200:if d=-1 then 4000 4040 sys d 4050 goto 500 4999 rem abspeichern des roms 5000 gosub 1130:print 5010 print tab(7);"Abspeichern des neuen ROMs" 5020 print:print:print:input"Filename:";a$ 5030 open 1,8,1,a$ 5035 print:print"saving ";a$ 5040 print#1,chr$(0);chr$(96); 5050 for i=6*4096 to 32767 5060 print#1,chr$(peek(i));:next i 5070 print#1,chr$(an-int(an/256)*256); 5080 print#1,chr$(an/256) 5090 close 1:goto 500 5999 rem geaendertes rom laden 6000 gosub 1330:print 6010 print tab(10);"Laden eines neuen ROMs" 6020 print:print:print:input"Filename:";a$ 6030 open 1,8,0,a$ 6040 gosub 6100:gosub 6100 6042 if st>0 then 6200 6043 print:print"loading ";a$ 6050 for i=6*4096 to 32767 6060 get#1,a$:a$=a$+chr$(0) 6070 poke i,asc(a$):next i 6080 gosub 6100:an=asc(a$):gosub 6100:an=an+256*asc(a$) 6090 of=an-6*4096:en=an+8192:close 1 6095 if en>65535 then en=0 6096 if an<14*4096 then poke 53246,1:goto 500 6097 poke 53246,0:goto 500 6100 get#1,a$:a$=a$+chr$(0):return 6200 close 1:print 6210 open 1,8,15 6220 if st=64 then 6250 6230 get#1,a$:print a$;:goto 6220 6250 close 1 6260 get a$:if a$=""then 6260 6270 goto 500 7990 rem ab hier datas 8000 data 64226,126,14014 8001 data 232,134,198,201,133,144,4,201,141,144,3,76,66,235,157,119,2,72,152 8002 data 72,160,0,196,2,208,13,185,41,251,221,119,2,240,11,200,192,176,208 8003 data 243,104,168,104,76,66,235,200,185,41,251,201,133,144,4,201,141,144 8004 data 238,236,137,2,176,233,157,119,2,232,134,198,76,15,251,133,76,207 8005 data 34,36,34,44,56,13,137,76,79,65,68,134,76,73,83,84,13,138,83,65,86 8006 data 69,135,82,85,78,13,139,83,89,83,136,63,70,82,69,40,48,41,13,140,83 8007 data 89,83,54,52,55,51,56,13,133,136 8010 data 60223,3,552 8011 data 76,226,250 10000 data 0