2 - ASMVLA01 - File I/O - 04/14/93 -
\r
4 Lately we have been quite busy with school, so this second issue is a
\r
5 little behind schedule. But that's life... This little issue will quickly
\r
6 show off the DOS file functions: read, write, open, close, create & others.
\r
7 They are all pretty much the same, so there isn't a whole lot to go over.
\r
8 But, as a bonus, I'm going to throw in a bit about how to do a subroutine.
\r
9 Let's do the subroutine stuff first.
\r
11 `Procedures' as they are called, are declared like this:
\r
13 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
\r
19 ret ;MUST have a RET statement!
\r
22 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
\r
24 In the procedure, you can do basically anything you want, just at the
\r
25 end of it, you say ret. You can also specify how to call the PROC by putting
\r
26 a NEAR or FAR after the procedure name. This tells the compiler whether
\r
27 to change segment AND offset, or just offset when the procedure is called.
\r
28 Note that if you don't specify, it compiles into whatever the default is for
\r
29 the current .MODEL (small = near, large = far)
\r
31 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
\r
37 ret ;this compiles to `retn' (return near- pops offset off
\r
38 ENDP TheProc ; stack only)
\r
46 ret ;compiles to `retf' pops both offset & segment off stack
\r
47 ENDP TheProc ; pops offset first
\r
49 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
\r
51 That's basically all there is to that. Note that if you REALLY wanted to
\r
52 be tricky, you could do a far jump by doing this:
\r
54 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
\r
58 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
\r
60 This would "return" you to the beginning of the procedure "TheProc"...
\r
61 This code is just to illustrate a point. If you actually did something like
\r
62 this and compiled and executed it, it would bomb. Know why? What happens
\r
63 when it hits the `ret' in the PROC? Well it pops off the offset and puts
\r
64 it in IP and then pops the segment and puts it in CS. Who knows what was
\r
65 on the stack... will return to an unknown address and probably crash. (It
\r
66 DEFINATELY will not continue executing your code.)
\r
68 Of course, the only stack operations are PUSH and POP. All they do is
\r
69 push or pop off the stack a word sized or a Dword sized piece of data. NEVER
\r
70 under ANY circumstance try to push a byte sized piece of data! The results
\r
71 are unpredictable. Well, not really, but just don't do it, ok?
\r
73 There are also two commands that'll save you some time and code space:
\r
75 PUSHA and POPA (push all and Pop all)
\r
77 PUSHA pushes the general registers in this order:
\r
79 AX, CX, DX, BX, SP, BP, SI, DI
\r
81 POPA pops the general registers in this order:
\r
83 DI, SI, BP, (sp), BX, DX, CX, AX
\r
85 SP is different because popa does NOT restore the value of SP. It merely
\r
86 pops it off and throws it away.
\r
88 For the 386+, pushad and popad push and pop all extended registers in
\r
89 the same order. You don't need to memorize the order, because you don't
\r
90 need to know the order until you go and get tricky. (hint: the location of
\r
91 AX on the stack is [sp + 14] - useful if you want to change what AX returns,
\r
92 but you did a pusha cause you wanted to save all the registers (except AX)
\r
93 Then you'd do a popa, and AX= whatever value you put in there.
\r
97 Alright, now a slightly different topic: memory management
\r
99 Ok, this isn't true by-the-book memory management, but you need to know
\r
100 one thing: Upon execution of a program, DOS gives it ALL memory up to the
\r
101 address A000:0000. This happens to be the beginning of the VGA buffer...
\r
102 Another thing you must know is that, if you used DOSSEG at the top of your
\r
103 file, the segment is the last piece of your program. The size of the segment
\r
104 is derived from the little command `STACK 200h' or whatever the value was
\r
105 that you put up there. The 200h is the number of bytes in the stack. To get
\r
106 the number of paragraphs, you'd divide by 16. Here's an example of how I can
\r
107 get a pointer to the first valid available segment that I can use for data:
\r
109 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
\r
110 mov ax,ss ;grab the stack segment
\r
111 add ax,200h/16 ;add the size of the stack 200h/16 = 20h
\r
113 ;AX now contains the value of the first available segment the you can
\r
115 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
\r
117 This is very nice, because you can just plop your data right there
\r
118 and you have a 64k buffer you can use for anything you want.
\r
120 Ok, say you want to find out how much memory is available to use. This
\r
121 would be done like this: (no suprises, I hope.)
\r
123 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
\r
124 mov ax,ss ;grab the stack segment
\r
125 add ax,200h/16 ;add the size of the stack 200h/16 = 20h
\r
126 mov bx,0A000h ;upper limit of the free memory
\r
127 sub bx,ax ;bx= # of paragraphs available
\r
128 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
\r
130 Pretty darn simple. That's enough of the overhead that you must know
\r
131 to understand the included ANSI viewer (asm3.asm)
\r
133 Now to the FILE I/O stuff...
\r
135 Files can be opened, read from, written to, created, and closed. To open
\r
136 a file, all you need to do is give the DOS interrupt a name & path. All
\r
137 references to that file are done through what's known as a file handle. A
\r
138 file handle is simply a 16bit integer that DOS uses to identify the file.
\r
139 It's used more or less like an index into chart of pointers that point to
\r
140 a big structure that holds all the info about the file- like current position
\r
141 in the file, file type, etc.. all the data needed to maintain a file.
\r
142 The `FILES= 20' thing in your autoexec simply tells DOS how much memory to
\r
143 grab for those structures. ( Files=20 grabs enough room for 20 open files. )
\r
145 ANYway, here's each of the important function calls and a rundown on what
\r
146 they do and how to work them.
\r
148 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
\r
149 FILE OPEN: Function 3Dh
\r
155 bits 7-3: Stuff that doesn't matter to us
\r
156 bits 2-0: Access code
\r
157 000 read only access
\r
158 001 write only access
\r
159 010 read and write access
\r
161 DS:DX= pointer to the ASCIIZ filename
\r
162 ASCIIZ means that its an ASCII string with a Zero on the end.
\r
166 AX= error code- don't worry about what they are, if the carry
\r
167 is set, you didn't open the file.
\r
170 AX= File Handle ;you need to keep this- it's your only way to
\r
171 ; reference your file!
\r
175 [...] ;header stuff
\r
177 .CODE ;this stuff is used for all the examples
\r
179 FileName db "TextFile.TXT",0
\r
181 Buffer db 300 dup (0)
\r
187 mov ax,3d00h ; open file for read only
\r
189 mov ds,ax ;we use CS, cause it's pointing to the CODE segment
\r
190 ; and our file name is in the code segment
\r
191 mov dx,offset FileName
\r
195 mov [FileHandle],ax
\r
199 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
\r
200 FILE CLOSE: Function 3Eh
\r
207 CF=1 error occured, but who cares?
\r
211 mov bx,[FileHandle]
\r
215 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
\r
216 FILE READ: Function 3Fh
\r
221 CX= Number of bytes to read
\r
222 DS:DX= where to put data that is read from the file (in memory)
\r
225 AX= number of bytes actually read- if 0, then you tried to read from
\r
226 the end of the file.
\r
230 mov bx,[FileHandle]
\r
233 mov dx,offset buffer
\r
240 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
\r
241 FILE WRITE: Function 40h
\r
246 CX= Number of bytes to write
\r
247 DS:DX= where to read data from (in memory) to put on disk
\r
250 AX= number of bytes actually written- if not equal to the number of bytes
\r
251 that you wanted to write, you have an error.
\r
255 mov bx,[FileHandle]
\r
258 mov dx,offset buffer
\r
264 jne FileError_Write
\r
266 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
\r
267 FILE CREATE: Function 3Ch
\r
276 bit 3: volume label
\r
277 bit 4: sub directory
\r
281 DS:DX= pointer to the ASCIIZ filename
\r
282 ASCIIZ means that its an ASCII string with a Zero on the end.
\r
286 AX= error code- don't worry about what they are, if CF
\r
287 is set, you didn't create the file.
\r
290 AX= File Handle ;you need to keep this- it's your only way to
\r
291 ; reference your file!
\r
296 mov ds,ax ;we use CS, cause it's pointing to the CODE segment
\r
297 ; and our file name is in the code segment
\r
298 mov dx,offset FileName
\r
299 mov cx,0 ;no attributes
\r
301 jc FileError_Create
\r
303 mov [FileHandle],ax
\r
305 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
\r
306 FILE DELETE: Function 41h
\r
310 DS:DX= pointer to the ASCIIZ filename
\r
314 AX= error code- 2= file not found, 3= path not found
\r
321 mov ah,41h ;kill the sucker
\r
324 mov dx,offset FileName
\r
326 jc FileError_Delete
\r
328 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
\r
329 FILE MOVE POINTER: Function 42h
\r
334 CX:DX= 32 bit pointer to location in file to move to
\r
335 AL= 0 offset from beginning of file
\r
336 = 1 offset from curent position
\r
337 = 2 offset from the end of the file
\r
341 AX= error code- no move occured
\r
344 DX:AX 32 bit pointer to indicate current location in file
\r
348 mov ah,42h ;find out the size of the file
\r
349 mov bx,[FileHandle]
\r
355 mov [word low FileSize],ax
\r
356 mov [word high FileSize],dx ;load data into filesize
\r
360 mov word ptr [FileSize],ax
\r
361 mov word ptr [FileSize+2],dx
\r
363 need I say why I like Ideal mode? )
\r
365 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
\r
366 FILE CHANGE MODE: Function 43h
\r
370 DS:DX= pointer to the ASCIIZ filename
\r
372 returns file attributes in CX
\r
374 sets file attributes to what's in CX
\r
378 AX= error code- 2= file not found, 3= path not found.
\r
383 ÄÄÄÄ EXAMPLE ÄÄÄÄ Lets erase a hidden file in your root directory...
\r
385 FileName db "C:\msdos.sys",0
\r
389 mov ah,43h ;change attribute to that of a normal file
\r
392 mov dx,offset FileName
\r
393 mov al,1 ;set to whats in CX
\r
394 mov cx,0 ;attribute = 0
\r
397 mov ah,41h ;Nuke it with the delete command
\r
400 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
\r
402 Well, that's all for now. I hope this info is enough for you to do some
\r
403 SERIOUS damage... :) I just don't want to see any 'bombs' running around
\r
404 erasing the hidden files in the root directory, ok?
\r
406 Anyway, go take a look at asm3.asm- it's a SIMPLE ansi/text displayer.
\r
407 It just opens the file, reads it all into a "buffer" that was "allocated"
\r
408 immediatly after the stack & reads in the entire file (if it's < 64k) and
\r
409 prints out the file character by character via DOS's print char (fn# 2).
\r
410 Very simple and very slow. You'd need a better print routine to go faster...
\r
411 The quickest display programs would decode the ANSI on its own... But that's
\r
412 kinda a chore... Oh, well. Enjoy.
\r
417 Suggested projects:
\r
419 1) Write a program that will try to open a file, but if it does not
\r
420 find it, the program creates the file and fills it with a simple
\r
423 2) Write a program that will input your keystrokes and write them
\r
424 directly to a text file.
\r
426 3) The write & read routines actually can be used for a file or device.
\r
427 Try to figure out what the FileHandle for the text screen is by
\r
428 writing to the device with various file handles. This same channel,
\r
429 when read from, takes it's data from the keyboard. Try to read data
\r
430 from the keyboard. Maybe read like 20 characters... CTRL-Z is the
\r
431 end of file marker.
\r
433 4) Try to use a file as `virtual memory'- open it for read/write access
\r
434 and write stuff to it and then read it back again after moving the
\r
439 ÚÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
\r
443 ; VERY, VERY simple ANSI/text viewer
\r
445 ; Coded by Draeden [VLA]
\r
458 ErrMsgOpen db "Error opening `"
\r
459 FileName db "ANSI.TXT",0,8,"'$" ;8 is a delete character
\r
460 ;0 is required for filename
\r
461 ;(displays a space)
\r
464 ;===- Subroutines -===
\r
466 PROC DisplayFile NEAR
\r
471 mov ax,3d00h ;open file (ah=3dh)
\r
472 mov dx,offset FileName
\r
475 mov bx,ax ;move the file handle into bx
\r
478 mov dx,0 ;load to [BufferSeg]:0000
\r
480 mov cx,0FFFFh ;try to read an entire segments worth
\r
483 mov [cs:FileLength],ax
\r
486 int 21h ;close the file
\r
490 mov cx,[cs:FileLength]
\r
495 int 21h ;print a character
\r
505 mov dx,offset ErrMsgOpen
\r
512 ;===- Main Program -===
\r
518 add bx,200h/10h ;get past the end of the file
\r
519 mov [BufferSeg],bx ;store the buffer segment
\r