In dieser Reihenfolge werden die Blocks
des Track 18 IMMER belegt. Der Wert $ FF
am Ende der Liste steht hier als Endmarkierung der Liste, die später beim Beschreiben der Dirblocks von Bedeutung
ist. Nun müssen wir erst einmal die Sektornummer 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 Zieldiskette 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 schreibenden Files berechnen. Da die ersten
beiden Bytes eines Datenblocks immer als
Zeiger auf den nächsten Datenblock dienen, müssen wir also den Wert DIR-FREE*254 zur FILEMEM-Adresse hinzuaddieren, um unsere Anfangsadresse zu erhalten:
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änglichen Bytes in den Dirblocks unterzubringen:
jsr openio ;Kanäle öffnen jsr chgent ;Eintrag suchen und ändern. jsr wblocks ;DirBlocks schreiben. jsr closeio ;Kanäle schließen
lda #<( text4) ;" File installldy #>( 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 Zeisty $ 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 unterscheidet 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 umgewandelt und an den Befehlsstring in X/ Y
angehängt wird. Der gesamte Befehl wird
dabei bei COMBUFF abgelegt und dann mittels 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 notwendig, um sicherzustellen, daß auch die
richtige Anzahl an Dirblocks als ' belegt' gekennzeichnet ist. Sollten nämlich 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ämlich einen neuen Dirblock hinzufügen, der ab dann nicht mehr zur Datenspeicherung benutzt werden kann. Damit es dabei
keine Konfrontationen gibt, wird das
File also sporadisch schon einmal angelegt 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 zeigen, 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 Endmarkierung 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 Filenamenspuffers. Für den Namen werden 17 Bytes reserviert, da ja maximal 16 Zeichen 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 subtrahiert, 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 gebraucht. 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 werden, 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 Feststellen der freien Blocks der Diskette. Dieser Wert ist normalerweise nicht direkt
aus dem DiskHeaderBlock zu erfahren. Die
einzige Möglichkeit wäre, die Blockbelegungen 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 $02 FA ( Lo) und $02 FC
( Hi) des Floppyspeichers abgelegt. Was liegt also näher, als diesen Wert direkt, ü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 Sourcetext abgelegt werden (3 Zeichen ab
Adresse $02 FA 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 Directory 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 DiskHeaderBlocks. Wir müssen also den Block
18/0 in den Puffer lesen, und den Pufferzeiger 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 lediglich 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 Sourcecodetexte 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 durchsucht die Directoryblocks nach unserem
Fileeintrag, liest aus ihm den
Track/ Sektor des ersten ' normalen' Datenblocks aus und legt ihn in den Adressen FTRACK und FSEKTOR ab. Desweiteren
wird hier dann Track und Sektor des ersten, 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 Anfangspositionen der einzelnen Fileeinträge eines Directoryblocks abgelegt 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ückgeschrieben:
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