Magic Disk 64

home to index to html: MD9302-KURSE-FLOPPY-KURS_9.2.html
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
Valid HTML 4.0 Transitional Valid CSS!