PC-teknik, 5 p LABORATION FILHANTERING Laborationsansvariga: Anders Arvidsson Utskriftsdatum: 2004-09-14 Laborant(er):
1 Syfte Laborationen ska illustrera möjligheten att använda funktioner i DOS för att läsa och skriva filer. Dessutom kommer filformatet för bitmap att gås igenom. 2 Genomförande Laborationen genomförs individuellt eller i grupper om två studenter. Som hjälp används föreläsningsanteckningarna, Art of Assembler, Software Interrupts och lathunden. OBS! Föregående laboration ligger till grund för denna och måste vara avklarad innan denna påbörjas! I PingPong finns filer som används till vissa av uppgifterna. Lab-PM finns även i elektronisk form, vilket underlättar kopiering av programexempel. Redovisa delresultat för labhandledaren, som markerar detta med en signatur i ditt lab-pm. Det kan t ex vara lämpligt att redovisa vid varje rubriknivå 2. Redovisning av hela laborationen bör ske senast en vecka efter laborationstillfället. 2
3 Laborationsuppgifter Uppgifterna löses i Pacific C. Anges inget annat så ska uppgiften lösas med assembler inom #asm - #endasm. 3.1 Öppna, läsa och stänga filer I PingPong finns en C/asm-fil, filh.c, samt en textfil testfil.txt. Funktionerna för filhantering är genomgångna på föreläsningen. Se efter vilka funktioner för interrupt 21H som förekommer i filh.c och leta efter information om dessa i Software interrupts och AoA, kapitel 13. Utgå från main() och studera hela koden så du ser hur funktionerna används. Placera textfilen i lämplig katalog (se sökväg i koden) och testkör programmet. Förklara vad alla siffror betyder (ASCII-symboler mm) som uppträder på utskriften när programmet körs. Utskrift: Förklaring (eller ASCII): pos 0 = pos 1 = pos 2 = pos 3 = pos 4 = pos 5 = pos 6 = pos 7 = pos 8 = pos 9 = EOFtest = 3
3.2 Skapa en textfil 3.2.1 INT 21H Function 5BH Create new File Läs om ovanstående interrupt i dokument Software Interrupts i pingpong. Funktionen skapar en ny fil med filnamnet som pekas ut av DX. Om allt går som det ska fås en filehandle i AX. En filehandle är ett nummer som används för att hänvisa till filen. Om något går snett sätts carryflaggan och en felkod lagras i AX. Se nedanstående exempel. #include <stdio.h> char filename[]="g:\\testfil.txt"; int filehandle; char error; int error_nr; //Sökväg till filnamn (OBS! dubbla //backslash \\) //Variabel som lagrar filehandle //Sätts till 1 om något går fel //Variabel som innehåller felkod void createfile(void) #asm mov _error,#0,byte ;Nollställ felflagga mov ah,#05bh ;Create new file mov cx,#0 ;Normal mov dx,#_filename ;peka DX på filnamn int #21H ;Anropa och skapa fil jnc createstop ;Kolla om något gick fel mov _error_nr,ax ;Lagra felkod i error_nr mov _error,#1,byte ;Sätt felflagga createstop: mov #endasm _filehandle,ax void main(void) createfile(); if(error) printf( Fel nr %d vid createfile\n,error_nr); else printf( OK! Filehandle: %d vid createfile\n,filehandle); Skriv in programmet, kompilera och kör. Kontrollera att filen skapas. Kör programmet igen, fås något felmeddelande, i så fall vilket? Felet beror alltså på att filen redan finns. Du måste därför ta bort den varje gång du kör programmet för att inte få ett felmeddelande. Det kan t.ex. göras med doskommandot del g:\testfil.txt. I AoA kap 13 beskrivs interrupt 21H funktion 3CH Create File. Det som skiljer denna funktion mot funktion 5BH är att den inte ger något felmeddelande om filen redan finns. Den kommer helt enkelt att skriva över den gamla filen. 4
3.2.2 INT 21H Function 3EH Close File Ovanstående program är inte riktigt bra. Filen skapas och en filehandle fås. För att tala om för operativsystemet att filen inte används måste den stängas. Detta kan göras med dosinterrupt 21H funktion 3EH Close File. Läs om interruptet i dokument Software Interrupts och lägg till en funktion som stänger filen. 3.2.3 INT 21H Function 40H Write to File or Device Läs om interruptet i dokumentet Software Interrupts. Lägg sedan till nedanstående funktion till ditt program. char tkn; void writefile(void) #asm mov ah,#40h ;Write to File mov bx,_filehandle mov cx,#1 ;antal tecken som skall skrivas mov dx,#_tkn ;adress till tkn i dx mov _tkn,# A,byte ;tecknet A i tkn int #21H ;anropa interrupt #endasm mov ah,#40h ;Write to File mov cx,#1 ;antal tecken som skall skrivas mov dx,#_tkn ;adress till tkn i dx mov _tkn,# B,byte ;tecknet B i tkn int #21H ;anropa interrupt Moidifiera sedan huvudprogrammet enligt följande: void main(void) createfile(); if(error) printf( Fel nr %d vid createfile\n,error_nr); else printf( OK! Filehandle: %d vid createfile\n,filehandle); writefile(): closefile(); Öppna sedan filen testfil.txt med notepad eller något liknande program och kontrollera innehållet. 5
3.2.4 Från tangentbord till fil Gör ett program som skapar en fil, läser in ett antal tecken från tangentbordet och sparar dem i filen. Filen skall sedan stängas. Inläsning av tecken skall ske med dosinterrupt 21H Funktion 01H Character Input With Echo. Efter att ett tecken lästs in ska det skrivas till filen och sedan ska programmet vänta på inmatning av nästa tecken. Inläsning ska avslutas om en funktionstangent trycks ned. Då ska filen stängas med closefile. Kontrollera att programmet fungerar genom att öppna textfilen i en editor. 3.3 Läsa en textfil I detta avsnitt kommer du att skriva ett program som läser din nyss skapde fil och skriver ut den på skärmen. Läs därför om följande dosinterrupt i dokumentationen (AoA kap 13 och Software interrupts). INT 21H Function 3DH Open File INT 21H Function 3FH Read File or Device INT 21H Function 02H Character Output 3.3.1 Utskrift av fil Skriv ett program som öppnar filen som skapdes i föregående uppgift med Open File. Läs sedan in ett tecken med Read File och skriv ut det med Character Output. Läs sedan in ytterligare ett tecken och skriv ut. Detta upprepas tills End Of File nås, då skall filen stängas med Close File. Hur man detekterar EOF står i AoA kap. 13.3.8.4. 6
3.4 Bitmap 3.4.1 Filformat En bitmapfil kan indelas i fyra huvudblock: Filhuvud Infohuvud Palettinfo Bitmap data I filhuvudet finns bl.a. information om filstorlek och på vilken position i filen bitmapdatat börjar. Pos (byte nr i fil) Storlek (bytes) Standardvärde Syfte 0 2 0x4D42 Måste alltid vara BM för att visa att det är en bitmapfil. 2 4 -- Specificerar filstorlek 10 4 1078 Tal som visar offset från filens början till bitmapdata I infohuvudet finns diverse information om bildens storlek och antal färger. Infohuvudet börjar på position 14 i filen. I en normal bitmapfil börjar paletteinfo på position 54 i filen. Här definieras paletten, dvs vilka färger som finns till förfogande i bilden. Varje färg ligger lagrad som 4 byte innehållande intensiteterna för röd, grön och blå, samt en oanvänd byte. I den grafikmode som används kan 256 färger användas, men dessa väljs ur en palett bestående av 256 k (2 18 ) färger. Paletten skrivs till grafikkortets D/A-omvandlare som genererar färgerna på skärmen. (Se senare kodexempel.) Varje byte (i bitmap data) som bestämmer bildpunktens färg pekar sålunda ut en position i paletten, där en blandning av 6 bit från vardera färg finns definierad. Med varje bitmapbild följer en egen palett, vilket kan ge problem vid samtidig visning av flera bilder skapade i olika program. Själva bilden ligger i blocket bitmap data. Detta block börjar på position 1078 i en 256-färgers bitmap. Betrakta bilden nedan: 7
Detta är bilden som den visas på datorskärmen. När bilden lagras i filen lagras först den understa pixelraden från vänster till höger (rad 200 i en 320*200 bitmap). Sedan lagras rad 199 o.s.v. Det innebär att i filen ligger bilden lagrad enligt figuren nedan. Det är alltså viktigt att tänka på var man börjar rita upp den inlästa bilden. Mer om BMP-formatet (fast med felaktig uppgift om ordningen på färgerna) står att läsa på: http://www.fortunecity.com/skyscraper/windows/364/bmpffrmt.html 3.4.2 Uppgift 3 Du skall nu modifiera ett befintligt program så att en bitmapbild läses in och ritas upp på skärmen. Hämta bitmapfilen dilbert.bm från PingPong och lägg den på ditt konto under g:\pacific. (Filen är modifierad så att den inte går att öppna med ett vanligt ritprogram ens om man återställer filtillägget.) Hämta sedan filen bmpstud.c och öppna den i Pacific C. Programmet består av ett antal subrutiner och ett huvudprogram skrivet i assembler. Eftersom laborationen handlar om filhantering är resterande delar redan skrivna. Du behöver bara komplettera programmet med asm-kod för filhantering enligt specifikationen längre ner. (Börja gärna med att läsa om huvudprogrammet.) Programmet har följande grundstruktur: #include <stdio.h> int storees,oldes; //Variabler som lagrar pekare till extra-segment char filename[]="g:\\pacific\\dilbert.bm"; int filehandle; unsigned char rowdata[320]; //Pixeldata för en rad unsigned int pos; //Position för filpekare unsigned char row; //Visar vilken rad som skall skrivas //till bildminnet char palette[1024]; //array för palettdata int error; //Variabel för felkod void subrutiner(void) urn; #asm ;openfile - öppnar filen angiven av variabeln filename ; filehandle sparas i variabeln filehandle ; om något går fel lagras felkod i error ; annars sätts error till noll openfile: //*Kompletteras* 8
;seek - Flyttar filpekaren till en viss position i filen ; positionen pekas ut av variabeln pos seek: //*Kompletteras* ;readrow - Läser in en skärmrad (320 bytes) med bmp-data och lägger ; den i arrayen rowdata readrow: //*Kompletteras* ;closefile - Stänger filen som anges av filehandle closefile: //*Kompletteras* ;setgfx setgfx: //Finns i fil - Ställer in grafikmod 320*200, 256 färger ;setpalette - Hämtar paletten från bitmapfilen och lägger den i ; grafikminnet setpalette: //Finns i fil ;writerow - skriver en rad med pixeldata till bildminnet ; raden specifieras av variabeln row writerow: //Finns i fil ;resetgfx - Återställer grafikläge resetgfx: //Finns i fil #endasm void main(void) #asm call openfile ;öppna bitmap-fil cmp _error,#0,word ;kontrollera om något gått fel jne err call setgfx ;ställ in grafikläge 9
call setpalette ;ställ in palette mov _pos,#1078,word ;peka p första position i bmp-data call seek mov _row,#199,byte ;peka på sista rad på skärmen l1: call readrow ;läs in en rad med bmp-data call writerow cmp _row,#0,byte ;kolla om vi skrivit ut sista rad je stop ;i så fall - avsluta dec _row,byte ;annars - minska rad jmp l1 stop: mov ah,#0 ;vänta på knapptryckning int #16H err: #endasm call resetgfx ;Återställ grafikläge call closefile ;stäng fil if(error) printf( Fel nr %d vid openfile\n,error); getchar(); Här följer en genomgång av vilka subrutiner som skall kompletteras och i stora drag hur. Openfile: Använd dosinterrupt 21H funktion 3DH Open File för att öppna filen som anges av den globala variabeln filename. Glöm inte att spara undan filehandle. Om carry-flaggan sätts har något gått fel, lagra då felkod i den globala variabeln error. Tänk på att error annars skall nollställas. Seek: Läs om dosinterrupt 21H funktion 42H Seek i AoA kap. 13.3.8.6. Med detta interrupt kan man alltså ställa filpekaren på en viss position i filen. När nästa läsning av filen sker görs det från den aktuella positionen. Skriv subrutinen så att filpekaren ställs på den position som pekas ut av den globala variabeln pos. Tänk på att både cx och dx används för att bestämma position. Nollställ därför cx och lägg pos i dx. Readrow: Använd dosinterrupt 21H Funktion 3fH Read File till den här subrutinen. När rutinen anropas ska den läsa in 320 bytes data från bitmapfilen och lägga dem i arrayen rowdata[]. Closefile: Denna subrutin skall stänga bitmapfilen med dosinterrupt 21H funktion 3EH Close File. 10
Huvudprogrammet, main Huvudprogrammet börjar med att bitmapfilen öppnas och grafikläge och palett ställs in. Filpekaren ställs sedan på början av bitmap-datat genom att startpositionen lagras i pos och subrutinen seek anropas. Talet 199 lagras sedan i den globala variabeln row för att peka på den nedersta raden på skärmen (d v s den rad som lagrats först i bilden). En rad bitmap-data läses genom att subrutinen readrow anropas och sedan skrivs denna ut på skärmen med writerow. Därefter minskas row med ett och processen upprepas tills alla 200 raderna skrivits ut. Testkör och bli stum av beundran... Övriga kommentarer Studera gärna writerow ingående för att repetera pekare och hantering av grafikminne. Övriga delar av koden, t ex hur paletten sätts, kan studeras närmare om dessa delar behövs i kommande valfria programmeringsuppgift. 11