Skip to content

Latest commit

 

History

History
830 lines (642 loc) · 24.7 KB

assembly_cursus_6.md

File metadata and controls

830 lines (642 loc) · 24.7 KB

De assembly cursus gaat vrolijk verder...

     ASSEMBLY (6)

Zoals ik de vorige keer al zei ga ik het deze keer hebben over .LIB-files. Omdat dit nogal een uitgebreid onderwerp is, beslaat deze tekst 3 delen. Het eerste deel behandelt wat theorie en een klein stukje van de source van de LIB-maker, deel twee gaat over de rest van die source en deel 3 van deze cursus legt uit hoe je .LIB-files moet gebruiken in je eigen programma's.

  WAT ZIJN LIB-FILES?

Een LIB-file is niets anders dan een file waarin alle files die een programma gebruikt zitten (vandaar LIB, van LIBrary). Een goed voorbeeld van deze files is bijvoorbeeld Smumpkin 3, Muzax 3 en de Final Fantasy Slide Show die op disk A van FD 35 stond.

   WAAROM LIB-FILES?

Waarom zou je LIB-files gebruiken als het gebruiken van gewone files veel makkelijker is? Hier is een aantal redenen voor, namelijk:

-Het neemt minder ruimte in beslag: Stel je hebt 4 files van (ik noem maar wat) 256 bytes. Als deze files los in de directory zouden staan, namen ze ieder (op een diskette) ��n kilobyte in beslag, dus met z'n allen is dat 4 kB. In een LIB-file nemen ze echter maar 1 kB in beslag, omdat de files aan elkaar worden geplakt.

-Het is sneller: Telkens als een nieuwe file wordt geopend, moet de computer de juiste filenaam in de directory zoeken. Dit kost aardig wat tijd, zeker als de directory helemaal vol staat en de te zoeken file de laatste file uit de directory is. Een andere manier om snel te laden is alles op sector zetten. Dit neemt wel minder ruimte in beslag dan gewoon de bestanden op diskette houden, maar LIB-files zijn altijd nog effici�nter.

-Er passen (in theorie) oneindig veel files in: In een directory passen (normaal gesproken) slechts 112 files. Het aantal files in een LIB-file is echter oneindig, voor zover de diskruimte het toelaat tenminste.

-Het is veel netter dan gewoon alle files op disk kwakken: Een directory vol met bestanden is niet echt netjes, zeker als je meerdere programma's op ��n diskette zet.

        NADELEN

Er zijn natuurlijk ook wat nadelen aan het gebruik van LIB-files, namelijk:

-De directory van een LIB-file moet permanent in het geheugen staan (hoeft eigenlijk niet, maar het is wel v��l sneller). Een erg groot nadeel is dit niet, want meestal zal je directory niet meer dan 1 kB in beslag nemen. De routine die straks volgt gebruikt per file 16 bytes, dus met 64 files heb je 1 kB vol. Gebruik je echter de effici�ntste methode, dan kun je in 1 kB ruim 200 files zetten! Dit lijkt mij meer dan genoeg.

-Het is wat extra programmeerwerk. Dit valt in de praktijk echter best wel mee, zelfs ik had er geen moeite mee. Dit geldt natuurlijk alleen voor de eerste keer, want je volgende programma's kun (moet) je gebruik maken van dezelfde routine's.

DE OPZET VAN HET PROGRAMMA

Ik heb gekozen voor een programma dat onder DOS werkt, om de simpele reden dat daar gewoon meer geheugen beschikbaar is. Verder verwacht het programma de bestanden op drive A en komt de LIB-file op drive B. Dit is echter zeer makkelijk aan te passen. Harddisk-gebruikers zullen gewoon alles op ��n drive doen, net zoals mensen met maar ��n diskdrive (tenzij je een lekker grote ramdisk aan kunt maken). Ook zijn er geen foutmeldingen. Tot slot staan de namen van de bestanden die in de LIB-file moeten komen in de source zelf, evenals de naam van de aan te maken LIB-file. Dit heb ik gedaan om de source niet al te lang te laten worden, want een command-line interpreter kost aardig wat regeltjes. Ook is het niet de bedoeling dat ik alles voorkauw, en natuurlijk ben ik lui! Een andere geldige reden is dat de ruimte van de command-line aardig beperkt is. De beste oplossing zou gewoon zijn om alle bestanden in een tekstfile te zetten en de naam daarvan door te geven aan het programma. Maar goed, dat mogen jullie zelf allemaal wel doen.

       DE SOURCE

Okay, dan komt nu het programma zelf.

            org     #0100

bdos: equ #05 setdta: equ 26 ; zet lees/schrijf-adres open_file: equ 15 ; open file close_file: equ 16 ; sluit file create_file: equ 22 ; maak file aan read_block: equ 39 ; laad van disk write_block: equ 38 ; schrijf naar disk

entry_length: equ 11 + 2 + 3

Het programma werkt onder MSX-DOS, dus een org #0100 is op z'n plaats (alhoewel dit wel weggelaten kan worden bij GEN80. Zo is het echter voor jezelf wel duidelijk dat het een DOS-programma is). Verder worden wat naampjes voor disk- gebruik gedefinieerd en de lengte die de naam en data van een bestand in de directory moet innemen. Dit is hier in totaal 16 bytes, namelijk 11 voor de filenaam, 2 voor de lengte en 3 voor de plaats in de LIB-file. Dit heeft echter als beperking dat de maximale grootte van een file die in de LIB-file moet komen 64kB is. Deze grootte zul je echter alleen maar overschrijden bij samplekits voor de Moonsound, dus zo'n ramp is dat ook weer niet. Ik ga verder...

         MAIN

            call    count_files     ; tel aantal files
            ld      (files),bc

            call    calc_length     ; bereken lengte
                                    ; directory
            ld      (dir_length),hl

            call    make_dir        ; maak directory

            call    save_dir        ; bewaar directory

            call    store_files     ; bewaar files

            ld      de,fcb2
            ld      c,close_file
            call    bdos            ; klaar!
            ret

Dit is de hoofdlus van het programma. De routine count_files telt het aantal files die in de LIB-file moeten komen, calc_length berekent de lengte van de directory, make_dir maakt de directory aan, save_dir schrijft de directory weg en store_files copi�ert alle files naar de LIB-file. Tenslotte wordt de LIB-file gesloten en zijn we klaar.

      COUNT FILES

; Tel aantal files ; In: - ; Uit: BC: aantal files ; Verandert: AF,BC,DE,HL count_files: ld hl,file_names ld bc,0 ld de,11

count_files1: ld a,(hl) or a ret z inc bc add hl,de jr count_files1

Deze routine telt het aantal files in de directory. Dit wordt gedaan door te kijken of de byte op adres HL een 0 is. Als dat zo is, zijn alle files geteld en anders wordt BC verhoogd en wordt de zooi herhaald.

   CALCULATE LENGTH

; Bereken lengte directory ; In: BC: aantal files ; Uit: HL: lengte directory ; Verandert: AF, BC, DE, HL calc_length: ld hl,3 ld de,entry_length

calc_length1: ld a,b or c ret z add hl,de dec bc jr calc_length1

Deze routine berekent de lengte van de directory. Dit wordt gedaan door dom de waarde entry_length herhaald op te tellen en vervolgens er 3 bij op te tellen. De eerste 2 bytes van de directory worden namelijk gebruikt om het aantal files in de directory aan te geven en de laatste byte van de directory wordt gebruikt om aan te geven dat alle files geweest zijn. Natuurlijk kan het berekenen sneller gedaan worden door een vermenigvuldigingsroutine te gebruiken, maar dit was eventjes wat sneller om te programmeren (ja, ik ben lui!).

De routine om de directory te maken is behoorlijk lang, daarom zal ik tussen de regels door wat opmerkingen maken en niet pas aan het einde.

    MAKE DIRECTORY

; Maak directory ; In: HL: lengte directory ; Uit: - ; Verandert: AF, BC, DE, HL make_dir: ld (getal1),hl ld hl,0 ld (getal1+2),hl

            ld      hl,file_names
            ld      de,directory
            ld      bc,(files)

Dit stukje initialiseert de routine. Op het adres 'getal1' staat de offset waar de files na de directory van de LIB-file beginnen. Verder wordt het adres waar de filenamen staan geladen (HL), het adres waar de directory moet komen geladen (DE) en het aantal files geladen (BC).

make_dir1: push bc

            push    hl
            push    de

            ld      bc,11
            ld      de,fname1
            ldir            ; filenaam naar FCB

            pop     de
            pop     hl
            ld      bc,11
            ldir            ; filenaam naar directory

Register BC hebben we pas op het einde weer nodig dus PUSH'en we die even weg. Vervolgens moet de filenaam twee keer gecopi�erd worden, namelijk naar de FCB en naar de uiteindelijke directory.

            push    hl

            push    de
            ld      de,fcb1
            ld      c,open_file
            call    bdos
            ld      de,fcb1
            ld      c,close_file
            call    bdos
            pop     de
            ld      hl,(length1)

De file wordt geopend en de lengte wordt opgevraagd.

            ld      a,l
            ld      (de),a
            inc     de
            ld      a,h
            ld      (de),a
            inc     de

De lengte van de file wordt in de directory gezet.

            ld      ix,getal1      ; bewaar positie in file
            ld      a,(ix)
            ld      (de),a
            inc     de
            ld      a,(ix+1)
            ld      (de),a
            inc     de
            ld      a,(ix+2)
            ld      (de),a
            inc     de

De plek waar de file in het bestand staat wordt in de directory gezet. Dit is een 24-bits getal, daarom wordt dit eventjes met register IX gedaan.

            push    de

            ld      iy,getal2
            ld      (iy),l
            ld      (iy+1),h
            ld      ix,getal1
            call    add_32bit

De lengte van de file wordt opgeteld bij de positie in de LIB-file. Hiervoor wordt een 32-bits optel-routine gebruikt, welke 'getal1' en 'getal2' gebruikt als parameters en het resultaat stopt in 'getal1'.

            pop     de
            pop     hl
            pop     bc
            dec     bc
            ld      a,b
            or      c
            jp      nz,make_dir1
            xor     a
            ld      (de),a
            ret

Nu worden alle registers teruggehaald en wordt er een lusje gemaakt om de boel te herhalen. Als alle files geweest zijn, wordt er nog even een 0 weggeschreven en dan is deze routine klaar.

LEES VERDER IN DEEL 2 ��De assembly cursus gaat vrolijk verder...

    ASSEMBLY (6)-2

We gaan weer verder waar we gebleven waren...

    SAVE DIRECTORY

; Bewaar directory ; In: - ; Uit: - ; Verandert: AF, BC, DE, HL save_dir: ld hl,lib_name ld de,fname2 ld bc,11 ldir

            ld      de,fcb2
            ld      c,create_file
            call    bdos            ; cre�er file

            call    intfcb2

            ld      de,files
            ld      c,setdta
            call    bdos

            ld      de,fcb2
            ld      c,write_block
            ld      hl,(dir_length)
            call    bdos            ; schrijf directory weg
            ret

Deze routine bewaart de net aangemaakte directory. Eerst wordt de naam van de library naar het FCB gecopi�erd. Vervolgens wordt de file gecre�erd en wordt het FCB geinitialiseerd voor het schrijven. Daarna wordt het Disk Transfer Address gezet op het adres waar het aantal files staat en tot slot wordt de directory weggeschreven.

     STORE FILES

; Bewaar files in .LIB-file ; In: - ; Uit: - ; Verandert: AF, BC, DE, HL store_files: ld bc,(files) ld hl,file_names

Het aantal files wordt in register BC geladen en het adres waar de filenamen staan in register HL.

store_files1: push bc

            ld      bc,11
            ld      de,fname1
            ldir
            push    hl

            call    clrfcb1

            ld      c,open_file
            ld      de,fcb1
            call    bdos

            call    intfcb1

De filenaam wordt gecopi�erd naar het FCB, het FCB wordt vervolgens schoongemaakt en daarna wordt de file geopend. Tot slot wordt het FCB ge�nitialiseerd.

            ld      hl,(length1)
            call    copy_file       ; kopieer file

            ld      de,fcb1
            ld      c,close_file
            call    bdos

            pop     hl
            pop     bc
            dec     bc
            ld      a,b
            or      c
            jp      nz,store_files1
            ret

In register HL komt de lengte van de file en die wordt vervolgens gecopi�erd door de routine copy_file. De file wordt daarna gesloten en dan wordt het zooitje herhaald.

       COPY FILE

; Copieer file naar .LIB-file ; In: HL: lengte file ; Uit: - ; Verandert: AF, BC, DE, HL copy_file: push hl ld c,setdta ld de,directory call bdos pop hl

Het lees/schrijfadres wordt op het adres gezet waar de directory stond.

copy_file1: ld de,32768 xor a sbc hl,de ; file >32768 bytes? jp c,copy_file2 ; Nee, copieer rest

            push    hl
            ld      hl,32768
            ld      de,fcb1
            ld      c,read_block
            call    bdos

            ld      hl,32768
            ld      de,fcb2
            ld      c,write_block
            call    bdos
            pop     hl
            jp      copy_file1

Er wordt gekeken of de file groter is dan 32 kB. Als dat zo is, dan wordt er 32 kB ingeladen en vervolgens wegge- schreven. Daarna wordt de routine herhaald.

copy_file2: add hl,de push hl ld de,fcb1 ld c,read_block call bdos pop hl ld de,fcb2 ld c,write_block call bdos ret

De resterende data van de file wordt ingelezen en weggeschreven

; Tel twee 32-bits getallen op ; In: IX: adres getal 1 ; IY: adres getal 2 ; Uit: getal 1 gevuld met resultaat ; Verandert: BC,DE,HL add_32bit: ld l,(ix+0) ld h,(ix+1)

            ld      e,(iy+0)
            ld      d,(iy+1)

            ld      c,(ix+2)
            ld      b,(ix+3)
            add     hl,de
            jr      nc,add_32bit1
            inc     bc

add_32bit1: ld (ix+0),l ld (ix+1),h

Deze routine telt twee 32-bits getallen bij elkaar op. In IX staat het adres van het eerste getal en in IY het adres van het tweede getal. Register HL en DE worden geladen met het eerste word van de 32-bits waarde en register BC met het tweede word van het eerste getal. Daarna wordt DE bij HL opgeteld en als er een overflow plaats vindt, wordt het tweede word met ��n verhoogd. Tot slot wordt het eerste word weggeschreven.

            ld      l,(iy+2)
            ld      h,(iy+3)
            add     hl,bc
            ld      (ix+2),l
            ld      (ix+3),h
            ret

Het tweede word van het tweede getal wordt in register HL geladen en wordt bij het tweede word van het eerste getal opgeteld en vervolgens opgeslagen.

; Maak FCB 1 schoon ; In: - ; Uit: - ; Verandert: AF, BC, DE, HL clrfcb1: ld hl,fcbdat1 ld bc,25 xor a ld (hl),a ld de,fcbdat1 + 1 ldir ret

Deze routine maakt het eerste FCB snel schoon met een truukje met LDIR. Het eerste adres wordt namelijk eerst op 0 gezet en het bestemmingsadres wordt dan ��n hoger dan het beginadres. Met LDIR wordt vervolgens het FCB gevuld met nullen.

; Initialiseer FCB 1 ; In: - ; Uit: - ; Verandert: AF, HL intfcb1: ld hl,0 ld (fcb1 + 12),hl ld (fcb1 + 33),hl ld (fcb1 + 35),hl xor a ld (fcb1 + 32),a inc hl ld (fcb1 + 14),hl ret

Deze routine initialiseert het eerste FCB voor het lezen. Het current block (fcb1 + 12) wordt op 0 gezet, evenals random record (fcb1+32/33/35). Tot slot wordt de record- lengte op 1 gezet.

; Initialiseer FCB 2 ; In: - ; Uit: - ; Verandert: AF, HL intfcb2: ld hl,0 ld (fcb2 + 12),hl ld (fcb2 + 33),hl ld (fcb2 + 35),hl xor a ld (fcb2 + 32),a inc hl ld (fcb2 + 14),hl ret

Deze routine doet hetzelfde als de routine intfcb1.

getal1: defw 0,0 ; opslagplaats voor de twee getal2: defw 0,0 ; 32-bits getallen

dir_length: defw 0 ; lengte van directory

; Het eerste FCB fcb1: defb 1 ; drive (0=default, 1 = A:) fname1: defm " " fcbdat1: defb 0,0,0,0 length1: defb 0,0,0,0,0,0,0,0,0,0,0 defb 0,0,0,0,0,0,0,0,0,0,0

; Het tweede FCB fcb2: defb 2 ; drive (0=default, 2 = B:) fname2: defm " " defb 0,0,0,0,0,0,0,0,0,0,0,0,0 defb 0,0,0,0,0,0,0,0,0,0,0,0,0

; Naam van de te maken .LIB-file lib_name: defm "???????????"

; Lijst met files die in de .LIB-file moeten file_names: defm "???????????"

            defb    0       ; 0 = laatste gehad

files: defw 0 ; bewaarplaats aantal files directory: nop

Vul bij lib_name de aan te maken LIB-file in en bij file_names de bestanden die je erin wilt hebben. De lengte van de naam is 11 letters en er mag GEEN '.' gebruikt worden. Na de laatste file moet een 'defb 0' staan, om aan te geven dat er geen files meer komen.

LEES VERDER IN DEEL 3 ��De assembly cursus gaat vrolijk verder...

    ASSEMBLY (6)-3

We gaan weer verder waar we gebleven waren...

Tot slot bespreek ik de routine's om de LIB-files in je eigen programma's te gebruiken (m��r wil ik ook niet meer doen). Deze routine's zijn stukken simpeler, daarom besteed ik ook maar ��n tekst aan dit gedeelte.

Er zijn drie routine's nodig, namelijk een routine die de file opent en de directory inleest, een routine die een file opzoekt in de directory en de pointers goed zet en een routine die een (gedeelte van een) file inlaadt.

       DE SOURCE

bdos: equ #f37d fopen: equ 15 ; open file fclose: equ 16 ; sluit file rdblk: equ 39 ; lees blok setdta: equ 26 ; zet lees/schrijfadres

; Routine's voor het gebruik van een .LIB-file init_dir: jp load_dir ; laad directory in file_find: jp search_file ; Zoek file load_file: jp load_block ; Laad file in

Dit zijn de routine's die het inlezen van de directory, het zoeken van een file in de directory en het lezen van een file uitvoeren.

    LOAD DIRECTORY

; Laad directory ; In: HL: Adres filename ; DE: Bestemmingsadres directory load_dir: push de push de push hl

            call    clrfcb

            pop     hl

            ld      bc,11
            ld      de,fname
            ldir

            pop     de
            ld      c,setdta
            call    bdos

Het FCB wordt schoongemaakt, de naam van de LIB-file wordt in het FCB gezet en vervolgens wordt het lees/schrijf-adres gezet.

            ld      de,fcb
            ld      c,fopen
            call    bdos

            call    intfcb

            ld      hl,2
            ld      de,fcb
            ld      c,rdblk
            call    bdos

De file wordt geopend, het FCB wordt ge�nitialiseerd en het aantal files in de directory wordt gelezen (eerste 2 bytes).

            pop     hl
            ld      c,(hl)
            inc     hl
            ld      b,(hl)

            ld      hl,1
            ld      de,11 + 3 + 2   ; lengte entry

calc_length: add hl,de dec bc ld a,b or c jp nz,calc_length

Het aantal files komt in BC terecht en de lengte van de directory wordt berekend. Dit kan wel beter met een echte vermenigvuldigingsroutine, maar dit was eventjes sneller programmeren.

            ld      de,fcb
            ld      c,rdblk
            call    bdos            ; Laad directory in
            ret

De directory wordt geladen en klaar is de routine.

      SEARCH FILE

; Zoek file op in directory en stel record number in ; In: HL: adres te laden file-naam ; DE: adres directory ; Uit: HL: lengte file ; A: 0 indien gelukt, 255 indien mislukt search_file: push hl push de

            call    cmp_str

            or      a
            jp      z,search_file1

De file wordt vergeleken met de huidige file in de directory. Als A nul is, is de file gevonden.

            pop     de
            ld      hl,11 + 3 + 2
            add     hl,de
            ex      de,hl   ; DE = positie in directory
            pop     hl
            jp      search_file

De pointer naar de directory wordt verhoogd en de routine herhaalt zichzelf.

search_file1: ex de,hl ; HL = adres in directory

            ld      e,(hl)
            inc     hl
            ld      d,(hl)  ; laad lengte
            inc     hl
            push    de

De lengte van de file wordt uitgelezen en gePUSHed.

            ld      de,fcb + 33
            ld      bc,3
            ldir            ; Record Number naar FCB

            xor     a
            ld      (de),a

De positie in de file wordt in het FCB gezet.

            pop     hl      ; lengte in HL

            pop     de
            pop     de
            ret

De lengte van de file komt in HL en de routine is klaar.

  LOAD (PART OF) FILE

; Lees een (gedeelte van een) file in ; In: HL: aantal te laden bytes ; Opmerking: zelf bestemmingsadres goedzetten load_block: ld de,fcb ld c,rdblk jp bdos

Geen uitleg nodig lijkt mij.

; Vergelijk strings met elkaar ; In: HL: adres eerste string ; DE: adres tweede string ; Uit: A: 0 = gelijk, anders = ongelijk cmp_str: ld b,11

cmp_str1: ld a,(de) cp (hl) ret nz inc hl inc de djnz cmp_str1 xor a ret

Vergelijk twee strings met elkaar. Ook hier geen uitleg nodig, lijkt mij.

; Initialiseer FCB ; in: niets ; uit: niets ; Verandert: alles clrfcb: ld hl,fcbdat ld bc,25 xor a ld (hl),a ld d,h ld e,l inc de ldir ret

Zie vorige tekst.

; init fcb ; in: niets ; uit: niets ; Verandert: af, hl intfcb: ld hl,0 ld (fcb+12),hl ; bloknummer op 0

            ld      (fcb+33),hl     ; random block op 0
            ld      (fcb+35),hl

            xor     a
            ld      (fcb+32),a      ; current record op 0

            inc     hl
            ld      (fcb+14),hl     ; bloklengte op 1
            ret

Zie vorige tekst.

; file control block fcb: defb 0 ; drive fname: defm "???????????" ; filename fcbdat: defb 0,0 ; current block defb 0,0 ; block lengte length: defb 0,0,0,0 ; file lengte defb 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0

Ook in deze routine's zijn natuurlijk weer verbeteringen mogelijk. Zo zou je een error kunnen laten geven als een file niet gevonden wordt, al is dat niet echt nodig als je een programma uitbrengt.

De sources staan natuurlijk ook op disk. De library-maker heet MAKELIB.GEN en de library-lezer heet USELIB.GEN. Het is niet verboden om de sources in je eigen programma's te gebruiken, maar het is beter als je je eigen routine's maakt. Tot de volgende keer!

Arjan Bakker