]> 4ch.mooo.com Git - 16.git/blob - 16/PCGPE10/ASM3.TXT
reverted my open watcom to 1.9 an recompiled everything~
[16.git] / 16 / PCGPE10 / ASM3.TXT
1 \r
2                   - ASMVLA01 - File I/O - 04/14/93 -\r
3 \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
10 \r
11 `Procedures' as they are called, are declared like this:\r
12 \r
13 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\r
14 \r
15 PROC TheProcedure\r
16 \r
17     ...             ;do whatever..\r
18     \r
19     ret             ;MUST have a RET statement!\r
20 ENDP TheProcedure\r
21 \r
22 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\r
23 \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
30 \r
31 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\r
32 \r
33 PROC TheProc NEAR\r
34 \r
35     ...\r
36 \r
37     ret             ;this compiles to `retn' (return near- pops offset off\r
38 ENDP TheProc        ; stack only)\r
39 \r
40     OR\r
41 \r
42 PROC TheProc FAR\r
43 \r
44     ...\r
45 \r
46     ret             ;compiles to `retf' pops both offset & segment off stack\r
47 ENDP TheProc        ; pops offset first\r
48 \r
49 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\r
50 \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
53 \r
54 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\r
55     push    seg TheProc\r
56     push    offset TheProc\r
57     retf\r
58 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\r
59 \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
67 \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
72 \r
73     There are also two commands that'll save you some time and code space:\r
74 \r
75 PUSHA and POPA (push all and Pop all)\r
76 \r
77     PUSHA pushes the general registers in this order:\r
78 \r
79 AX, CX, DX, BX, SP, BP, SI, DI\r
80 \r
81     POPA pops the general registers in this order:\r
82 \r
83 DI, SI, BP, (sp), BX, DX, CX, AX\r
84 \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
87 \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
94 \r
95     ÄÄÄÄ\r
96 \r
97     Alright, now a slightly different topic: memory management\r
98 \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
108 \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
112 \r
113     ;AX now contains the value of the first available segment the you can\r
114     ; use.\r
115 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\r
116 \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
119 \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
122 \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
129 \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
132 \r
133         Now to the FILE I/O stuff...\r
134 \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
144 \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
147 \r
148 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\r
149 FILE OPEN: Function 3Dh\r
150 \r
151  IN:\r
152     ah= 3Dh\r
153     al= open mode\r
154 \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
160 \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
163 \r
164  Returns:\r
165         CF=1 error occured\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
168 \r
169         CF=0 no error\r
170             AX= File Handle ;you need to keep this- it's your only way to\r
171                             ; reference your file!\r
172 \r
173   ÄÄÄÄ EXAMPLE ÄÄÄÄ\r
174 \r
175     [...]   ;header stuff\r
176 \r
177     .CODE           ;this stuff is used for all the examples\r
178 \r
179   FileName  db "TextFile.TXT",0\r
180   FileHandle dw 0\r
181   Buffer    db  300 dup (0)\r
182   BytesRead dw  0\r
183   FileSize  dd  0\r
184 \r
185     [...]   ;more stuff\r
186 \r
187     mov     ax,3d00h    ; open file for read only\r
188     mov     ax,cs\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
192     int     21h\r
193     jc      FileError_Open\r
194 \r
195     mov     [FileHandle],ax\r
196 \r
197     [...]   ;etc...\r
198 \r
199 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\r
200 FILE CLOSE: Function 3Eh\r
201 \r
202   IN:\r
203     AH= 3Eh\r
204     BX= File Handle\r
205 \r
206   RETURN:\r
207     CF=1 error occured, but who cares?\r
208   \r
209   ÄÄÄÄ EXAMPLE ÄÄÄÄ\r
210 \r
211     mov     bx,[FileHandle]\r
212     mov     ah,3eh\r
213     int     21h\r
214 \r
215 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\r
216 FILE READ: Function 3Fh\r
217 \r
218   IN:\r
219     AH= 3Fh\r
220     BX= File Handle\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
223     \r
224   RETURN:\r
225     AX= number of bytes actually read- if 0, then you tried to read from\r
226         the end of the file.\r
227 \r
228   ÄÄÄÄ EXAMPLE ÄÄÄÄ\r
229 \r
230     mov     bx,[FileHandle]\r
231     mov     ax,cs\r
232     mov     ds,ax\r
233     mov     dx,offset buffer\r
234     mov     ah,3Fh\r
235     mov     cx,300\r
236     int     21h\r
237 \r
238     mov     [BytesRead],ax\r
239 \r
240 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\r
241 FILE WRITE: Function 40h\r
242 \r
243   IN:\r
244     AH= 40h\r
245     BX= File Handle\r
246     CX= Number of bytes to write\r
247     DS:DX= where to read data from (in memory) to put on disk\r
248     \r
249   RETURN:\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
252         \r
253   ÄÄÄÄ EXAMPLE ÄÄÄÄ\r
254 \r
255     mov     bx,[FileHandle]\r
256     mov     ax,cs\r
257     mov     ds,ax\r
258     mov     dx,offset buffer\r
259     mov     ah,40h\r
260     mov     cx,[BytesRead]\r
261     int     21h\r
262 \r
263     cmp     cx,ax\r
264     jne     FileError_Write\r
265 \r
266 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\r
267 FILE CREATE: Function 3Ch\r
268 \r
269  IN:\r
270     ah= 3Ch\r
271     cl= file attribute\r
272 \r
273         bit 0: read-only\r
274         bit 1: hidden\r
275         bit 2: system\r
276         bit 3: volume label\r
277         bit 4: sub directory\r
278         bit 5: Archive\r
279         bit 6&7: reserved\r
280 \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
283 \r
284  Returns:\r
285         CF=1 error occured\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
288 \r
289         CF=0 no error\r
290             AX= File Handle ;you need to keep this- it's your only way to\r
291                             ; reference your file!\r
292   ÄÄÄÄ EXAMPLE ÄÄÄÄ\r
293 \r
294     mov     ah,3ch\r
295     mov     ax,cs\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
300     int     21h\r
301     jc      FileError_Create\r
302 \r
303     mov     [FileHandle],ax\r
304 \r
305 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\r
306 FILE DELETE: Function 41h\r
307 \r
308  IN:\r
309     ah= 41h\r
310     DS:DX= pointer to the ASCIIZ filename\r
311     \r
312  Returns:\r
313         CF=1 error occured\r
314             AX= error code- 2= file not found, 3= path not found\r
315                     5= access denied\r
316 \r
317         CF=0 no error\r
318 \r
319   ÄÄÄÄ EXAMPLE ÄÄÄÄ\r
320 \r
321     mov     ah,41h      ;kill the sucker\r
322     mov     ax,cs\r
323     mov     ds,ax\r
324     mov     dx,offset FileName\r
325     int     21h\r
326     jc      FileError_Delete\r
327 \r
328 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\r
329 FILE MOVE POINTER: Function 42h\r
330 \r
331  IN:\r
332     ah= 42h\r
333     BX= File Handle\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
338       \r
339  Returns:\r
340         CF=1 error occured\r
341             AX= error code- no move occured\r
342 \r
343         CF=0 no error\r
344             DX:AX 32 bit pointer to indicate current location in file\r
345             \r
346   ÄÄÄÄ EXAMPLE ÄÄÄÄ\r
347 \r
348     mov     ah,42h      ;find out the size of the file\r
349     mov     bx,[FileHandle]\r
350     xor     cx,cx\r
351     xor     dx,dx\r
352     mov     al,2\r
353     int     21h\r
354     \r
355     mov     [word low FileSize],ax\r
356     mov     [word high FileSize],dx ;load data into filesize\r
357 \r
358     (or in MASM mode, \r
359 \r
360             mov word ptr [FileSize],ax\r
361             mov word ptr [FileSize+2],dx\r
362 \r
363     need I say why I like Ideal mode? )\r
364 \r
365 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\r
366 FILE CHANGE MODE: Function 43h\r
367 \r
368  IN:\r
369     ah= 43h\r
370     DS:DX= pointer to the ASCIIZ filename\r
371     al= 0\r
372         returns file attributes in CX\r
373     al= 1 \r
374         sets file attributes to what's in CX\r
375     \r
376  Returns:\r
377         CF=1 error occured\r
378             AX= error code- 2= file not found, 3= path not found.\r
379                     5= access denied\r
380 \r
381         CF=0 no error\r
382 \r
383   ÄÄÄÄ EXAMPLE ÄÄÄÄ Lets erase a hidden file in your root directory...\r
384 \r
385   FileName db   "C:\msdos.sys",0\r
386 \r
387     [...]\r
388 \r
389     mov     ah,43h          ;change attribute to that of a normal file\r
390     mov     ax,cs\r
391     mov     ds,ax\r
392     mov     dx,offset FileName\r
393     mov     al,1            ;set to whats in CX\r
394     mov     cx,0            ;attribute = 0\r
395     int     21h\r
396 \r
397     mov     ah,41h          ;Nuke it with the delete command\r
398     int     21h\r
399 \r
400 ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\r
401 \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
405 \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
413 \r
414     Draeden/VLA\r
415 \r
416 \r
417     Suggested projects:\r
418 \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
421         text message.\r
422 \r
423     2)  Write a program that will input your keystrokes and write them\r
424         directly to a text file.\r
425 \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
432 \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
435         cursor position.\r
436 \r
437 \r
438 \r
439 ÚÄÄÄÄÄÄÄÄÄÄÂÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ\r
440 ³ ASM3.ASM ³\r
441 ÀÄÄÄÄÄÄÄÄÄÄÙ\r
442 \r
443 ;   VERY, VERY simple ANSI/text viewer\r
444 ;\r
445 ;   Coded by Draeden [VLA]\r
446 ;\r
447 \r
448     DOSSEG\r
449     .MODEL SMALL\r
450     .STACK 200h\r
451     .CODE\r
452     Ideal\r
453 \r
454 ;===- Data -===\r
455 \r
456 BufferSeg   dw  0\r
457 \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
462 FileLength dw 0\r
463 \r
464 ;===- Subroutines -===\r
465 \r
466 PROC DisplayFile NEAR\r
467     push    ds\r
468 \r
469     mov     ax,cs\r
470     mov     ds,ax\r
471     mov     ax,3d00h    ;open file (ah=3dh)\r
472     mov     dx,offset FileName\r
473     int     21h\r
474     jc      OpenError\r
475     mov     bx,ax       ;move the file handle into bx\r
476 \r
477     mov     ds,[BufferSeg]\r
478     mov     dx,0            ;load to [BufferSeg]:0000\r
479     mov     ah,3fh\r
480     mov     cx,0FFFFh       ;try to read an entire segments worth\r
481     int     21h\r
482 \r
483     mov     [cs:FileLength],ax\r
484 \r
485     mov     ah,3eh\r
486     int     21h             ;close the file\r
487 \r
488     cld\r
489     mov     si,0\r
490     mov     cx,[cs:FileLength]\r
491 PrintLoop:\r
492     mov     ah,2\r
493     lodsb\r
494     mov     dl,al\r
495     int     21h         ;print a character\r
496 \r
497     dec     cx\r
498     jne     PrintLoop\r
499     \r
500     pop     ds\r
501     ret\r
502 \r
503 OpenError:\r
504     mov     ah,9\r
505     mov     dx,offset ErrMsgOpen\r
506     int     21h\r
507 \r
508     pop     ds\r
509     ret\r
510 ENDP DisplayFile\r
511 \r
512 ;===- Main Program -===\r
513 \r
514 START:\r
515     mov     ax,cs\r
516     mov     ds,ax\r
517     mov     bx,ss\r
518     add     bx,200h/10h     ;get past the end of the file\r
519     mov     [BufferSeg],bx  ;store the buffer segment\r
520 \r
521     call    DisplayFile\r
522 \r
523     mov     ax,4c00h\r
524     int     21h\r
525 END START\r
526 \r