Auf das »!« kommt es an
Das folgende Programm wurde aus der Not geboren. Es erleichtert das Laden von Diskette und macht das umständliche Laden und Listen des Directory überflüssig. Nebenher lernen Sie eine Reihe nützlicher Maschinenroutinen kennen.
![](92-1.jpg)
Geht es Ihnen auch so: Ich weiß nie genau, ob das Programm, das ich laden willl, nun »Disk Copy V 1.0« oder »Disk Copy V1.0« heißt. Versuche ich es mit »Disk*«, lade ich mit Sicherheit »Disk Monitor«.
Es hilft also nichts: Ich lade das Directory, liste es und — ärgere mich, weil das Programm, das ich laden wollte, ganz oben steht und beim Scrollen verschwindet. Also nochmal »LIST«, dann mit dem Cursor in die richtige Reihe fahren, »LOAD« eingeben, Cursor hinter den Programmnamen, »,8« eintippen. Der Computer antwortet mit einem verächtlichen »SYNTAX ERROR«, weil ich den Doppelpunkt mal wieder vergessen habe…
Vielleicht stelle ich mich besonders dumm an, aber es sollte auch anders gehen. Für Tätigkeiten, die meine geistigen Fähigkeiten übersteigen, gibt es doch Computer! Aus diesem Gedanken entstand das Programm.
Es wird mit »LOAD"!",8,1« geladen, startet automatisch, liest das Directory ein und schreibt hinter jedes Programm eine Nummer. Wenn der Bildschirm voll ist, wartet es auf meine Eingabe. Tippe ich »+«, bekomme ich die nächsten Programme angezeigt. Tippe ich aber die Programmnummer und dahinter ein »L«, wird das entsprechende Programm automatisch geladen. Handelt es sich um ein Maschinenprogramm, das absolut geladen werden muß (mit »,8,1«), tippe ich hinter die Nummer ein »A«.
So funktioniert es
Da ich nicht auf einen Autostart verzichten wollte, mußte das Programm vollständig in Maschinensprache geschrieben werden. Außerdem mußte es sehr kurz sein, denn ich hatte nur den Bereich von Speicherstelle 256 bis 600 ($0100 — $0258) zur Verfügung. Zu allem Unglück benötigen die Betriebssystemroutinen, die das Programm verwendet, auch noch Platz in diesem Bereich (Prozessorstack). Deshalb mußte ich auf alles verzichten, was nicht unbedingt nötig war. Es gibt also kein Menü für den Benutzer, ebenso keine Möglichkeit, das Programm zu unterbrechen, wenn man nichts laden will. Man kann nur »0L« eingeben, dann springt es mit einer Fehlermeldung ins Basic zurück. Trotzdem ist der verfügbare Speicher bis auf das letzte Byte belegt.
Die folgende Programmbeschreibung ist zwangsläufig sehr theoretisch. Das Programm läuft ja aber auch, wenn man sie nicht versteht. Machen Sie sich aber ruhig einmal die Mühe. Vielleicht können Sie doch den einen oder anderen nützlichen Tip bekommen. Wem es zu kompliziert wird, der kann bei den »Hinweisen zum Abtippen« weiterlesen.
Nehmen Sie also das Assembler-Listing zur Hand und folgen Sie mir in die Tiefen der Maschinensprache… Zur besseren Übersicht habe ich die einzelnen Teile beschrieben und immer die entsprechenden Zeilennummern dahintergesetzt.
Im Programmkopf bis Zeile 320 werden die Betriebssystemroutinen und die Variablen definiert. Sie sind nochmals im Kasten erläutert. Als Startadresse wird $010A festgelegt, der Maschinencode kann aber nicht direkt dorthin assembliert werden, sonst würde ja der Autostart anlaufen. Deshalb wird er zunächst in den Bereich $C00A — $C158 geschrieben.
Das Programm selbst beginnt mit Zeile 1510. Die Routine »DIRIN« lädt das Directory in den Speicher. Dazu benutzen wir die Betriebssystemroutine »LOAD«. Diese braucht zur Vorbereitung die Routinen SETLFS und SETNAM. SETLFS bestimmt die logische Filenmmer (01), die Geräteadresse (08) und die Sekundäradresse (0 für Lesen). SETNAM übergibt den Filenamen, in unserem Falle »$«. LOAD muß schließlich noch wissen, wohin das Directory geladen werden soll. Diese Adresse (Anfang des Basic-Speichers, steht in ($2B/$2C) wird im X- und Y-Register übergeben. Um die richtigen Koppeladressen zu erhalten, muß anschließend noch die Basic-Routine aufgerufen werden, die das für uns erledigt ($A533). Jetzt steht unser Directory genauso im Speicher wie sonst und wartet darauf, auf dem Bildschirm ausgegeben zu werden.
Die Ausgabe kann aber nicht wie beim normalen Listen erfolgen, denn wir wollen ja hinter jedem Namen eine Nummer ausgeben. Also müssen wir das selbst in die Hand nehmen. Mit einem kühnen Sprung geht es deshalb jetzt nach DIROUT. Sehen wir uns nun den Speicherausdruck eines Directory etwas genauer an:
Es geht los bei $0801. Dort steht die Adresse des nächsten Eintrages $081F. Wie immer steht im Speicher zuerst das Low-Byte und dahinter das High-Byte. Es folgt der Diskettenname, der in diesem Falle mit dem des Verfassers auffallend übereinstimmt. Der erste Eintrag beginnt also mit Adresse $081F. Dort finden wir wieder die Adresse des nächsten Eintrages ($083F). Jeder Eintrag hat übrigens die gleiche Länge, nämlich 32 Byte. Wir werden davon später noch Gebrauch machen.
Die 2 Byte hinter der Koppeladresse enthalten die Länge des Programms in Blöcken, in unserem Falle also $0002. Nun kommen ein paar Leerzeichen ($20). Wieviele, hängt davon ab, ob die Blocklänge ein, zwei oder drei Ziffern benötigt. Die Namen sollen ja beim Listen schließlich ordentlich untereinander stehen. Jetzt folgt der Filename, von Anführungszeichen eingeschlossen. Sie haben es sicher schon bemerkt: Das Directory dieser Diskette hat als erstes Programm das »!« wie sich das für anständige Disketten gehört!
Jetzt wieder einige Leerzeichen und schon ist der nächste Fileeintrag erreicht. Das geht so weiter bis eine Koppeladresse erreicht wird ($089D in Adresse $087F), die aus dem Rahmen fällt. In Adresse $089D stehen nämlich zwei jämmerliche Nullen. So, sagt sich das Betriebssystem, jetzt ist aber Schluß. Und es hat wie so oft recht…
Zurück zu unserem Programm. Es soll ja jetzt ein Directory auf den Bildschirm ausgeben, und zwar schöner, als es das Betriebssystem jemals könnte. Wir initialisieren also unsere Variable USE mit dem Basic-Anfang ($0801). In Zeile 390 beginnt eine Schleife, die immer eine Zeile ausgibt. Zuerst holen wir uns die Koppeladresse und speichern sie in NEXT (Zeile 410 — 460). Als nächstes müssen wir die 2 Byte mit der Blocklänge in eine ordentliche Dezimalzahl umwandeln. Das erledigt für uns die Basic-Routine »NUMOUT« (470 — 520). Jetzt brauchen wir nur noch den Namen mit allen Leerzeichen davor und dahinter, auszugeben (530 — 580).
Eigentlich müßte nun die Programmnummer ausgegeben werden, aber damit warten wir noch. Aus folgendem Grund: Wir wollen diese Schleife ja für jede Zeile benützen, auch für die letzte. In der steht aber die »BLOCKS FREE«-Meldung und dahinter darf ja keine Nummer mehr erscheinen. Wir müssen also vorher prüfen, ob wir schon die letzte Zeile erreicht haben.
Dazu übertragen wir die Adresse des nächsten Files, die wir in NEXT aufbewahrt haben, nach USE. Zeigt USE jetzt auf eine Null, sind wir fertig und springen ans Ende unserer Routine, nach DIR4 (590 — 650). Ist das aber noch nicht der Fall, geben wir unsere Programmnummer aus. Den Cursor stellen wir dazu auf Spalte 27 ($1B) und benutzen wieder NUMOUT, um FILENR als Dezimalzahl auszugeben (660 — 700). Eine Zeile wäre geschafft! Im folgenden bereiten wir uns auf die nächste Filenummer vor und springen wieder nach oben zu DIR1 zurück. Sollten aber schon 20 Zeilen auf dem Bildschirm stehen, merken wir uns im ENDFLG, daß wir noch nicht fertig sind und verlassen die Ausgaberoutine vorerst. Erinnern Sie sich, wir wollten vermeiden, daß der Bildschirm ohne unseren ausdrücklichen Befehl wegscrollt (710 — 800).
Der Bildschirm hat sich inzwischen gefüllt, unsere Nummern stehen fein säuberlich hinter den Programmen, nun sollte uns ein freundlich blinkender Cursor dazu animieren, dem Computer mitzuteilen, wie es weitergehen soll. Also hinein in die Routine WAHL
Das Wichtigste erledigt hier die Betriebssystemroutine CHRIN. Sie läßt den Cursor blinken, bis »RETURN« gedrückt wird, schreibt die eingegebenen Zeichen auf den Bildschirm und liest sie anschließend vom Bildschirm wieder ab, damit wir sie schön der Reihe nach bearbeiten können. Wir speichern alle Zeichen zunächst im BUFFER und sehen uns dann das letzte Zeichen genauer an (820 — 900). Als erstes prüfen wir, ob ein »+« eingegeben wurde. Wenn ja, setzen wir FILEANZ wieder auf Null, und, je nachdem ob das ENDFLG Ende signalisiert oder nicht, auch die FILENR. Anschließend erfolgt der Rücksprung in die Directory-Ausgabe (910 — 980).
Wurde kein »+« eingegeben, prüfen wir weiter auf »L« beziehungsweise »A«. In Abhängigkeit davon setzen wir das ABSFLG (990 — 1070). Jetzt müssen wir herausfinden, welche Programmnummer geladen werden soll. Dazu müssen wir die ein oder zwei Dezimalzahlen in ein Hexbyte umwandeln, weil unser Computer nun mal nichts anderes versteht. Andererseits ist es ja nicht einzusehen, daß wir uns auf sein mathematisches Niveau herabbegeben und unsere Zahlen demnächst als Hex- oder noch schlimmer als Binärzahlen eingeben. Das Umwandeln ist ja gar nicht so schwierig. Wir holen uns die Einerziffer und zählen dann so oft 10 ($0A) dazu, wie die Zehnerziffer angibt. Da der Computer sich unsere Ziffern aber nicht als 0,1…9 merkt, sondern als $30, $31… $3A, müssen wir jeweils die oberen 4 Bits ausmaskieren. Das war’s denn auch schon. In FILENR steht zur Belohnung tatsächlich die gewünschte Filenummer mundgerecht für unseren Computer (1080 — 1200).
Jetzt haben wir das Schlimmste — fast — hinter uns. Wir brauchen nur noch den passenden Filenamen zu suchen. Den holen wir uns aus dem Directory. In bewährter Weise benutzen wir unsere Variable USE. Um den Diskettennamen zu überlesen, zählen wir zum Basic-Anfang in $2B/$2C 35 ($23) dazu. Damit liegen wir so ungefähr richtig, auf jeden Fall vor dem ersten Namen. Erinnern sie sich, jeder Eintrag im Directory belegt genau 32 Byte. Allerdings beginnen die Filenamen nicht immer so schön regelmäßig wie in unserem Beispiel, immer an der selben Stelle. Wir müssen den Anfang des Namens also noch genau suchen. Vorher brauchen wir aber erst einmal die richtige Stelle im Directory. Wir addieren zu USE jetzt daher so oft 32 ($20), wie in FILENR angegeben (1250 — 1400).
Jetzt müssen wir in unserem Programm ein Stück überspringen, weil die folgenden Bytes wegen des Autostarts auf 2 gesetzt werden müssen. Weiter geht es mit LAD2 in Zeile 1780. Vorher steht allerdings im Programm noch die Subroutine FINDA.E, was so viel wie »Finde den Anfang beziehungsweise das Ende des Namens« bedeutet. Sie durchsucht den Text, auf den USE zeigt, nach einem Anführungszeichen und übergibt im Akku, wieviel Zeichen es bis dahin sind (1680 — 1750).
Diese Zahl addieren wir zu USE — eins mehr, denn das Anführungszeichen selbst gehört ja nicht mit zum Namen. Mit FINDA.E erhalten wir im Akku die Länge des Namens, so wie es die Routine SETNAM, die wir oben schon benutzt haben, verlangt. Noch ein kurzer Sprung nach SETLFS, wo wir in Abhängigkeit vom ABSFLG als Sekundäradresse Null oder Eins übergeben. Den Rest erledigt die Basic-Routine BASICLOAD (1780—1940). Geschafft, die schönste Zeile im Assemblerlisting ist erreicht: .EN heißt ENDE.
Sicher, es war nicht ganz einfach, aber wenn Sie mir bis hierhin gefolgt sind, haben Sie eine ganze Reihe kleiner Routinen gelernt, wie sie in jedem Maschinenprogramm gebraucht werden.
- NUMOUT ($BDCD) — wandelt zwei Hexbytes in eine Dezimalzahl um und gibt sie aus. (HByte im Akku, LByte im X-Reg)
- SETLFS ($FFBA) — übergibt logische Filenummer (Akku), Geräteadresse (X-Reg) und Sekundäradresse (Y-Reg).
- SETNAM ($FFBD) — übergibt den Filenamen. Akku enthält die Länge des Namens, X-Reg das LByte, Y-Reg das HByte der Adresse, an der der Name beginnt.
- LOAD ($FFD5) — lädt das durch SETLFS und SETNAM bezeichnete File an die Adresse, die im X-Reg (LByte) und im Y-Reg (HByte) angegeben ist. Der Akku enthält 0 für LOAD und 1 für VERIFY.
- CHRIN ($FFCF) — holt Zeichen von dem in $99 festgelegten Eingabegerät. Der Cursor blinkt, bis RETURN eingegeben wird. Die Zeichen werden dann vom Bildschirm gelesen und im Akku übergeben.
- CHROUT ($FFD2) — gibt das ASCII-Zeichen im Akku aus.
Variable
- BUFFER — ist ein Zwischenspeicher.
- USE und NEXT — enthalten die Adresse des gegenwärtigen beziehungsweise nächsten Directory-Eintrages.
- FILENR — ist die aktuelle Filenummer im Directory.
- ABSFLG — ist ein Flag, das normales oder absolutes Laden signalisiert.
- FILEANZ — enthält die Anzahl der auf dem Bildschirm ausgedruckten Files.
- ENDFLG — ist ein Flag, das signalisiert, ob das Ende des Directory bereits erreicht ist.
Hinweise zum Abtippen
Das Programm kann nicht an die Stelle geschrieben werden, an der es endgültig stehen soll, weil sonst ja der Autostart anlaufen würde. Wir schreiben es daher zuerst nach 49152 ($C000). Tippen Sie zuerst den Basic-Lader ein. Nach RUN muß der Text aus Zeile 30 erscheinen, sonst haben Sie einen Fehler gemacht. Legen Sie nun eine neue Diskette ein, und drücken Sie eine Taste. Das Programm wird nun auf die Diskette geschrieben, allerdings mit der falschen Startadresse. Schalten Sie danach bitte den Computer aus und wieder ein. Um die falsche Startadresse zu ändern, geben Sie das Programm »Startadresse« ein und starten Sie es mit RUN. Jetzt wird die Adresse geändert, und Sie haben das »!« lauffähig auf Ihrer Diskette. Um es auf andere Disketten zu übertragen, verwenden Sie ein Kopierprogramm, zum Beispiel »Super Copy«.
Und dann lehnen Sie sich zurück, geben LOAD »!«,8,1 ein und genießen die Früchte Ihrer Arbeit.
(Dietrich Weineck/rg)10 fori=49162to49495:reada:pokei,a:s=s+a 20 next:ifs<>35775thenprint"fehler in datas":stop 30 print"{clr}bitte legen sie eine neue diskette ein. {down}diskette wird formatiert !" 40 geta$:ifa$=""then40 50 open1,8,15,"n0:menu,mn":close1 60 print"{clr}{down}{down}{down}poke43,0:poke44,192:poke45,90:poke46,193" 70 print"{down}{down}save"chr$(34)"!"chr$(34)",8" 80 print"{home}"; 90 fori=631to641:pokei,13:next:poke198,10 100 data165,43,133,57,165,44,133,58,169,13,32,210,255,160,0,177,57,133,59,200 110 data177,57,133,60,200,177,57,170,200,177,57,32,205,189,160,3,200,177,57 120 data240,5,32,210,255,208,246,165,59,133,57,165,60,133,58,160,1,177,57,240 130 data23,169,27,133,211,169,0,166,251,32,205,189,230,251,230,253,165,253 140 data201,21,144,183,169,128,133,254,169,13,32,210,255,162,0,32,207,255,157 150 data64,3,232,201,13,208,245,202,202,189,64,3,201,43,208,12,169,0,133,253 160 data36,254,48,144,133,251,16,132,201,76,208,6,169,0,133,252,240,8,201,65 170 data208,208,169,128,133,252,202,189,64,3,41,15,133,251,202,48,16,189,64 180 data3,41,15,170,24,165,251,105,10,133,251,202,208,247,166,251,24,165,43 190 data105,35,133,57,165,44,133,58,202,240,111,24,165,57,105,32,133,57,144 200 data244,230,58,176,240,36,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2 210 data2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,169,1,162,208 220 data160,1,32,189,255,162,8,160,0,32,186,255,152,133,253,133,251,166,43 230 data164,44,32,213,255,32,51,165,76,10,1,160,0,177,57,201,34,240,3,200,208 240 data247,152,96,32,37,2,56,101,57,133,57,32,37,2,166,57,164,58,32,189,255 250 data169,1,162,8,160,0,132,10,36,252,16,2,160,1,32,186,255,76,111,225
10 open1,8,15 20 open2,8,2,"#" 30 print#1,"u1 2 0 17 0" 40 print#1,"b-p 2 3" 50 print#2,chr$(1) 80 print#1,"u2 2 0 17 0" 70 close2:close1
0020;********************** 0030;* * 0040;* LADEPROGRAMM ! * 0050;* MAI 1984 * 0060;* N.MANN & D.WEINECK * 0070;* FLEETRADE 40 * 0080;* 2800 BREMEN * 0090;* TEL. 0421 / 493090 * 0100;* * 0110;********************** 0120; 0130; 0140NUMOUT .DE $BDCD 0150BASICLOAD .DE $E16F 0160SETLFS .DE $FFBA 0170SETNAM .DE $FFBD 0180LOAD .DE $FFD5 0190CHRIN .DE $FFCF 0200CHROUT .DE $FFD2 0210; 0220BUFFER .DE $0340 0230USE .DE $39 0240NEXT .DE $3B 0250FILENR .DE $FB 0260ABSFLG .DE $FC 0270FILEANZ .DE $FD 0280ENDFLG .DE $FE 0290; 0300 .BA $010A 0310 .MC $C00A 0320 .OS 0330; 0340; 0350DIROUT LDA *$2B 0360 STA *USE 0370 LDA *$2C 0380 STA *USE+1 0390DIR1 LDA #$0D 0400 JSR CHROUT 0410 LDY #$00 0420 LDA (USE),Y 0430 STA *NEXT 0440 INY 0450 LDA (USE),Y 0460 STA *NEXT+1 0470 INY 0480 LDA (USE),Y 0490 TAX 0500 INY 0510 LDA (USE),Y 0520 JSR NUMOUT 0530 LDY #$03 0540DIR2 INY 0550 LDA (USE),Y 0560 BEQ DIR3 0570 JSR CHROUT 0580 BNE DIR2 0590DIR3 LDA *NEXT 0600 STA *USE 0610 LDA *NEXT+1 0620 STA *USE+1 0630 LDY #$01 0640 LDA (USE),Y 0650 BEQ DIR4 0660 LDA #$1B 0670 STA *$D3 0680 LDA #$00 0690 LDX *FILENR 0700 JSR NUMOUT 0710 INC *FILENR 0720 INC *FILEANZ 0730 LDA *FILEANZ 0740 CMP #$15 0750 BCC DIR1 0760 LDA #$80 0770DIR4 STA *ENDFLG 0780 LDA #$0D 0790 JSR CHROUT 0800; 0810; 0820WAHL LDX #$00 0830WAHL1 JSR CHRIN 0840 STA BUFFER,X 0850 INX 0860 CMP #$0D 0870 BNE WAHL1 0880 DEX 0890 DEX 0900 LDA BUFFER,X 0910 CMP #'+' 0920 BNE WAHL2 0930 LDA #$00 0940 STA *FILEANZ 0950 BIT *ENDFLG 0960 BMI DIR1 0970 STA *FILENR 0980 BPL DIROUT 0990WAHL2 CMP #'L' 1000 BNE WAHL3 1010 LDA #$00 1020 STA *ABSFLG 1030 BEQ WAHL4 1040WAHL3 CMP #'A' 1050 BNE WAHL 1060 LDA #$80 1070 STA *ABSFLG 1080WAHL4 DEX 1090 LDA BUFFER,X 1100 AND #$0F 1110 STA *FILENR 1120 DEX 1130 BMI LADEN 1140 LDA BUFFER,X 1150 AND #$0F 1160 TAX 1170 CLC 1180WAHL5 LDA *FILENR 1190 ADC #$0A 1200 STA *FILENR 1210 DEX 1220 BNE WAHL5 1230; 1240; 1250LADEN LDX *FILENR 1260 CLC 1270 LDA *$2B 1280 ADC #$23 1290 STA *USE 1300 LDA *$2C 1310 STA *USE+1 1320LAD1 DEX 1330 BEQ LAD2 1340 CLC 1350 LDA *USE 1360 ADC #$20 1370 STA *USE 1380 BCC LAD1 1390 INC *USE+1 1400 BCS LAD1 1410; 1420; 1430TEXT1 .BY '$' 1440; 1450; 1460 .BY 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1470 .BY 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1480 .BY 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1490; 1500; 1510DIRIN LDA #$01 1520 LDX #L,TEXT1 1530 LDY #H,TEXT1 1540 JSR SETNAM 1550 LDX #$08 1560 LDY #$00 1570 JSR SETLFS 1580 TYA 1590 STA *FILEANZ 1600 STA *FILENR 1610 LDX *$2B 1620 LDY *$2C 1630 JSR LOAD 1640 JSR $A533 1650 JMP DIROUT 1660; 1670; 1680FINDA.E LDY #$00 1690CMP1 LDA (USE),Y 1700 CMP #$22 1710 BEQ CMP2 1720 INY 1730 BNE CMP1 1740CMP2 TYA 1750 RTS 1760; 1770; 1780LAD2 JSR FINDA.E 1790 SEC 1800 ADC *USE 1810 STA *USE 1820 JSR FINDA.E 1830 LDX *USE 1840 LDY *USE+1 1850 JSR SETNAM 1860 LDA #$01 1870 LDX #$08 1880 LDY #$00 1890 STY *$0A 1900 BIT *ABSFLG 1910 BPL LAD3 1920 LDY #$01 1930LAD3 JSR SETLFS 1940 JMP BASICLOAD 1950 .EN