In dieser Reihenfolge werden die Blocks des Track 18 IMMER belegt. Der Wert $FF am Ende der Liste steht hier als Endmar- kierung der Liste, die später beim Be- schreiben der Dirblocks von Bedeutung ist. Nun müssen wir erst einmal die Sek- tornummer des ersten freien Dirblocks ermitteln:
ok lda #19 ;Von der Gesamt- sektorzahl (19) für Track 18 sec ;die Anzahl der sbc dirfree ;freien Dirblocks subtrahieren (=Anzahl belegte Blocks) und als tay ;Index in Y-Reg. lda blktab,y ;Sektornr. lesen sta dirstrt ;und speichern. sty dsindex ;Index speichern
Nun wollen wir das File auf der Zieldis- kette anlegen. Hierzu schreiben wir es zunächst ganz normal mittels der WFILE- Routine. Hierbei sollen jedoch nur die Bytes geschrieben werden, die nicht mehr in die Dirblocks passen. Wir müssen nun also die Anfangsadresse des zu schrei- benden Files berechnen. Da die ersten beiden Bytes eines Datenblocks immer als Zeiger auf den nächsten Datenblock die- nen, müssen wir also den Wert DIR- FREE*254 zur FILEMEM-Adresse hinzuaddie- ren, um unsere Anfangsadresse zu erhal- ten:
ldx dirfree ;DIRFREE in X laden (=HiByte Startadr.). txa ;u. in Akku holen dex ;HiByte-1 asl ;Akku*2 eor #$ff ;und invertieren clc ;mit adc #1 ;Öbertrag. adc #<(filemem) ;LoByte von bcc m5 ;FILEMEM inx ;addieren. m5 sta $fd ;unde ablegen txa ;HiByte holen clc ;und mit HiByte adc #>(filemem) ;FILEMEM add. sta $fe ;u. ablegen jsr wfile ;File schreiben
Nachdem die Anfangsadresse berechnet und in $FD/$FE abgelegt wurde, wird die WFI- LE-Routine aufgerufen. Die Endadresse muß in $F9/$FA stehen, wo sie noch von der RFILE-Routine enthalten ist (wird von ihr als Lesezeiger verwedet). Ist das File nun geschrieben, so müssen wir nur noch seinen Fileeintrag aus dem Directory heraussuchen, Track und Sektor des ersten freien Dirblocks eintragen und die fehlenden DIRFREE*254 anfängli- chen Bytes in den Dirblocks unterzubrin- gen:
jsr openio ;Kanäle öffnen jsr chgent ;Eintrag suchen und ändern. jsr wblocks ;DirBlocks schreiben. jsr closeio ;Kanäle schließen
lda #<(text4) ;"File install- ldy #>(text4) ;liert"
errout jsr strout ;ausgeben. eo1 jsr inkey ;Auf Tastendruck beq eo1 ;warten. jmp main ;Und neu starten
Soviel also zu unserer Steuerroutine. Kommen wir nun zu den Unterfunktionen: 2) STRIEC Diese Routine gibt einen Zeichenstring, dessen Adresse in Akku und Y-Register steht, an den Befehlskanal aus. Der String muß mit einem Nullbyte beendet sein: striec sta $fb ;Adresse in Zei- sty $fc ;ger ablegen.
lda #8 ;Floppy auf jsr listen ;Befehlskanal lda #$6f ;empfangsbereit jsr seclst ;machen. ldy #0 ;String lesen siloop1 lda ($fb),y ;und senden. bne si1 lda #8 ;Floppy zurück- jmp unlist ;setzen u. Ende. si1 jsr iecout ;Zeichen senden iny ;und Zeiger+1 bne siloop1 inc $fc jmp siloop1
3) SENDCOM Diese Routine wird dazu verwandt, einen Floppybefehl zu senden. Hierbei unter- scheidet sie sich jedoch von der Routine STRIEC. Es wird nämlich nicht nur der Zeiger des Befehls übergeben (in X und Y-Register), sondern auch ein Wert im Akku, der vor dem Senden in ASCII umge- wandelt und an den Befehlsstring in X/Y angehängt wird. Der gesamte Befehl wird dabei bei COMBUFF abgelegt und dann mit- tels STRIEC an die Floppy gesandt: sendcom pha ;Akku retten stx scloop1+1 ;Stringzeiger sty scloop1+2 ;setzen
ldy #0 ;Und String scloop1 lda $c000,y ;nach COMBUFF beq sc1 ;umkopieren. sta combuff,y iny jmp scloop1 sc1 sty mem0 ;Zeiger retten, pla ;Akku holen, jsr i2a ;konvertieren. ldy mem0 ;Zeiger zurückh. ldx #0 ;und in ASCII scloop2 lda numbuff,x ;konvertierte beq sc2 ;Zahl an COMBUFF sta combuff,y ;anhängen. inx iny jmp scloop2 sc2 lda #13 ;CR anhängen sta combuff,y iny lda #0 ;Endmarkierung sta combuff,y ;anhängen
lda #<(combuff);und Inhalt von ldy #>(combuff);COMBUFF an jmp striec ;Floppy senden. 4) WDUMMY Kommen wir nun zur Routine "WDUMMY". Wir sollten zunächst einmal klären, wozu sie benötigt wird. Wie Sie oben sahen, wird sie aufgerufen, noch BEVOR irgendendet- was anderes getan wird. Hierbei tut sie eigentlich nichts anderes, als ein File mit dem Namen in NAME und MEM0 auf der Zieldiskette anzulegen und gleich darauf wieder zu löschen. Das deshalb notwen- dig, um sicherzustellen, daß auch die richtige Anzahl an Dirblocks als 'be- legt' gekennzeichnet ist. Sollten näm- lich genau 8 (oder 16,24,etc.) Files auf der Zieldiskette vorhanden sein, so kann es zu Problemen kommen. Beim Schreiben eines neuen Files muß das DOS dann näm- lich einen neuen Dirblock hinzufügen, der ab dann nicht mehr zur Datenspeiche- rung benutzt werden kann. Damit es dabei keine Konfrontationen gibt, wird das File also sporadisch schon einmal ange- legt und direkt danach wieder gelöscht. Der neue DirBlock wird dann nicht wieder freigegeben. Der Eintrag bleibt nämlich erhalten, es wird lediglich der Filetyp 'DEL' an das Programm vergeben. Hier nun also die Routine:
wdummy lda mem0 ;Filename ldx #<(name) ;in NAME ldy #>(name) ;und MEM0 jsr setnam ;setzen. lda #1 ;Fileparameter ldx #8 ;"1,8,1" für ldy #1 ;("PRG saven") jsr setpar ;setzen. jsr open ;File öffnen. ldx #1 ;Kanal 1 als jsr ckout ;Ausgabefile jsr bsout ;Byte ausgeben. jsr clrch ;Standardkanäle zurücksetzen. lda #1 ;File wieder jsr close ;schließen. jsr opencom ;Befehlskanal öffnen lda #<(name-2);Name-2 als ldy #>(name-2);Adresszeiger jsr striec ;senden. lda #1 ;Befehlskanal jmp close ;schließen.
Nun kann ich Ihnen auch den Grund zei- gen, warum der Filename im Sourcecode abgelegt wird. Hier ist nämlich vor dem eigentlichen Namen auch noch der Text "S:" abgelegt. Wenn wir nun NAME-2 an STRIEC übergeben, so enspricht das einem Scratchbefehl für den Filenamen ("S:NA- ME"). Da STRIEC ein Nullbyte als Endmar- kierung verlangt, wurde die GETIN- Routine so ausgelegt, daß sie nach dem letzten eingelesenen Zeichen ein solches Byte in den NAME-Puffer schreibt. Hier nun die Source-Definition des Filena- menspuffers. Für den Namen werden 17 Bytes reserviert, da ja maximal 16 Zei- chen plus die Endmarkierung 0 vonnöten sind: .text "s:" name .byte 0,0,0,0,0,0,0,0 .byte 0,0,0,0,0,0,0,0,0
5) GETNEED Diese Routine wird benutzt, um die An-
zahl der vom Quellfile benötigten Blocks zu ermitteln. Hierbei wird zunächst die Startadresse des Filespeichers subtra- hiert, um die effektive Länge in Bytes zu ermitteln. Hiernach wird das High- Byte mit zwei multipliziert. Dies ist nämlich die Anzahl Bytes, die Aufgrund des Wegfalls der ersten beiden Bytes eines Datenblocks zu der Anzahl Lo-Bytes addiert werden muß. Ist dieser Wert grö- ßer als 256, so wird ein Block mehr ge- braucht. Jetzt wird noch die Anzahl der Low-Bytes hinzuaddiert. Gibt es auch hier einen Öberlauf, so muß wieder ein Block hinzuaddiert werden. Letztendlich muß wieder ein Block hinzugefügt wer- den, da die letzten Bytes, selbst wenn es weniger als 254 sind, dennoch einen ganzen Block belegen:
getneed ldx #0 ;NEED stx need ;löschen. lda $f9 ;Endadr. d. Quellfiles (von ldx $fa ;RFILE noch da) lesen. sec ;und Startadresse sbc #<(filemem);subtrahieren. bcs rf1 ;Ergebnis wird in dex ;MEM1 (LoByte) rf1 sta mem1 ;und txa ;MEM2 (HiByte) sec ;abgelegt. sbc #>(filemem) sta mem2 rol ;HiByte*2 bcc cn1 ;Wenn<256, weiter inc need ;Sonst Blocks+1 cn1 clc ;LoByte addieren adc mem1 beq cn2 bcc cn2 inc need ;Bei Öberlauf Blocks+1 cn2 inc need ;Und nochmal Blocks+1 lda mem2 ;Und Hi-Byte clc ;addieren. adc need ;und in NEED cn3 sta need ;ablegen. rts
6) GETDISKF Kommen wir nun zur Routine zum Feststel- len der freien Blocks der Diskette. Die- ser Wert ist normalerweise nicht direkt aus dem DiskHeaderBlock zu erfahren. Die einzige Möglichkeit wäre, die Blockbele- gungen der einzelnen Tracks aus der BAM zu lesen und aufzusummieren. Aber auch hier wollen wir uns eines kleinen Tricks bedienen. Wird eine Diskette nämlich initialisiert (was wir beim Üffnen des Befehlskanals schon tun), so liest die Floppy die BAM der Diskette automatisch ein und berechnet die Anzahl der freien Blocks von selbst. Diese Anzahl wird dann in den Bytes $02FA (Lo) und $02FC (Hi) des Floppyspeichers abgelegt. Was liegt also näher, als diesen Wert di- rekt, über den Memory-Read-Befehl der Floppy auszulesen. Und nichts anderes tut GETDISKF:
getdskf lda #<(com5) ;Memory-Read ldy #>(com5) ;Befehl jsr striec ;senden. lda #8 ;Floppy sende- jsr talk ;bereit auf lda #$6f ;Befehlskanal jsr sectlk ;machen. jsr iecin ;Lo-Byte lesen sta dskfree+0 ;und sichern. jsr iecin ;Byte überlesen. jsr iecin ;Hi-Byte lesen sta dskfree+1 ;und sichern. lda #8 ;Floppy zurück- jmp untalk ;setzen.
Zusätzlich hierzu muß noch das entspre- chende Memory-Read-Kommando im Source- text abgelegt werden (3 Zeichen ab Adresse $02FA lesen):
com5 .text "m-r" .byte 250,2,3,13,0
7) GETDIRF Nun kommen wir zur Routine zum Ermitteln der freien Directoryblocks. Hier können wir keinen Umweg gehen, sondern müssen die BAM direkt auslesen. Da das Direc- tory ausschließlich in Block 18 steht, genügt es, das erste Byte des BAM- Eintrags für Track 18 auszulesen. Dieses Byte steht an Position 72 des DiskHea- derBlocks. Wir müssen also den Block 18/0 in den Puffer lesen, und den Puf- ferzeiger auf 72 setzen um an die gewünschte Information zu kommen. Gleichzeitig berechnet diese Routine die Anzahl der insgesamt verfügbaren Blocks auf Diskette. Hierzu müssen wir ledi- glich den Inhalt von DSKFREE und DIRFREE addieren und in ALLFREE ablegen.
getdirf lda #<(com6) ;"Block 18/0 ldy #>(com6) ;lesen" jsr striec ;senden. lda #<(com7) ;"Pufferzeiger ldy #>(com7) ;auf Byte 72" jsr striec ;senden. lda #8 ;Floppy sende- jsr talk ;bereit auf lda #$62 ;Pufferkanal jsr sectlk ;machen jsr iecin ;Wert lesen sta dirfree ;und sichern. ldx dskfree+1 ;Hi-Byte lesen clc ;Lo-Byte zu adc dskfree+0 ;DIRFREE bcc gf1 ;addieren. inx gf1 sta allfree+0 ;und in ALLFREE
stx allfree+1 ;ablegen.
lda #8 ;und Floppy jmp untalk ;zurücksetzen.
Auch hier benötigen wir zwei Sourcecode- texte für die Diskkommandos:
com6 .text "u1 2 0 18 0" ;Sekt. 18/0 .byte 13,0 ;lesen com7 .text "b-p 2 72" ;Pufferzgr. .byte 13,0 ;auf 72.
8) CHGENT Kommen wir nun zu einer der wichtigsten Routinen unseres Programms. Sie durch- sucht die Directoryblocks nach unserem Fileeintrag, liest aus ihm den Track/Sektor des ersten 'normalen' Da- tenblocks aus und legt ihn in den Adres- sen FTRACK und FSEKTOR ab. Desweiteren wird hier dann Track und Sektor des er- sten, freien Directoryblocks eingetragen und der Block wieder auf die Diskette zurückgeschrieben. Auch diese Routine benutzt die Sektorentabelle BLKTAB, um die Reihenfolge der Directroyblocks zu ermitteln. Desweiteren benötigt sie noch eine weitere Tabelle, in der die An- fangspositionen der einzelnen Fi- leeinträge eines Directoryblocks abge- legt sind. Diese Tabelle heißt ENTTAB und sieht folgendermaßen aus:
enttab .byte 2,34,66,98,130,162 .byte 194,226
Kommen wir nun jedoch zur eigentlichen Routine. Sie besteht im Prinzip aus zwei ineinander verschachtelten Schleifen, die nacheinander die einzelnen Dirblocks einlesen und jeden einzelnen Eintrag mit dem Namen in NAME vergleichen. Wird der Name gefunden, so werden die Schleifen verlassen. Dann wird nochmals auf diesen Eintrag positioniert, und zwar so, daß der Bufferpointer direkt auf die zwei Bytes für Starttrack und -sektor zeigt und dort die Werte für den ersten freien Dirblock eingertragen (Track 18, Sektor in DIRSTRT). Abschließend wird dieser Directoryblock inklusive der Änderung wieder auf die Diskette zurückgeschrie- ben:
chgent lda #<(text6) ;"Suche ldy #>(text6) ;Eintrag" jsr strout ;ausgeben lda #1 ;Blockzähler sta mem1 ;initialisieren celoop1 lda #8 ;Floppy zurück- jsr untalk ;setzen. ldy mem1 ;Blkzähler holen lda blktab,y ;Sekt.nr. lesen, sta mem3 ;ablegen, ldx #<(com1) ;und Sektor lesen ldy #>(com1) jsr sendcom inc mem1 ;Blkzähler+1 lda #0 ;Eintragszähler sta mem2 ;initialisieren celoop2 ldy mem2 ;Eintr.z. holen cpy #8 ;Mit 8 vgl. beq celoop1 ;Ja, also näch- sten Block lesen lda enttab,y ;Nein, also Pos. lesen, sta mem4 ;ablegen, ldx #<(com2) ;und Pufferzeiger ldy #>(com2) ;positionieren. jsr sendcom inc mem2 ;Eintr.z+1 lda #8 ;Floppy zum jsr talk ;Senden auf lda #$62 ;Pufferkanal jsr sectlk ;bewegen. jsr iecin ;Filetyp holen. cmp #$82 ;u.m. "PRG" vgl. bne celoop2 ;Wenn <>, dann direkt weiter. jsr iecin ;Sonst Track sta ftrack ;und Sektor jsr iecin ;holen und ab- sta fsector ;legen.
Bitte nun Teil 3 des Floppy-Kurses laden