Copy Link
Add to Bookmark
Report

VLA 3/93 Introduction to ASSEMBLER (part 2)

DrWatson's profile picture
Published in 
atari
 · 9 Aug 2024

- ASMVLA01 - File I/O - 04/14/93 -

Lately we have been quite busy with school, so this second issue is a little behind schedule. But that's life... This little issue will quickly show off the DOS file functions: read, write, open, close, create & others. They are all pretty much the same, so there isn't a whole lot to go over. But, as a bonus, I'm going to throw in a bit about how to do a subroutine. Let's do the subroutine stuff first.

`Procedures' as they are called, are declared like this:

---------------------------------------------------------------------------- 

PROC TheProcedure

... ;do whatever..

ret ;MUST have a RET statement!
ENDP TheProcedure

----------------------------------------------------------------------------

In the procedure, you can do basically anything you want, just at the end of it, you say ret. You can also specify how to call the PROC by putting a NEAR or FAR after the procedure name. This tells the compiler whether to change segment AND offset, or just offset when the procedure is called. Note that if you don't specify, it compiles into whatever the default is for the current .MODEL (small = near, large = far)

---------------------------------------------------------------------------- 

PROC TheProc NEAR

...

ret ;this compiles to `retn' (return near- pops offset off
ENDP TheProc ; stack only)

OR

PROC TheProc FAR

...

ret ;compiles to `retf' pops both offset & segment off stack
ENDP TheProc ; pops offset first

----------------------------------------------------------------------------

That's basically all there is to that. Note that if you REALLY wanted to be tricky, you could do a far jump by doing this:

---------------------------------------------------------------------------- 
push seg TheProc
push offset TheProc
retf
----------------------------------------------------------------------------

This would "return" you to the beginning of the procedure "TheProc"... This code is just to illustrate a point. If you actually did something like this and compiled and executed it, it would bomb. Know why? What happens when it hits the `ret' in the PROC? Well it pops off the offset and puts it in IP and then pops the segment and puts it in CS. Who knows what was on the stack... will return to an unknown address and probably crash. (It DEFINITELY will not continue executing your code.)

Of course, the only stack operations are PUSH and POP. All they do is push or pop off the stack a word sized or a Dword sized piece of data. NEVER under ANY circumstance try to push a byte sized piece of data! The results are unpredictable. Well, not really, but just don't do it, ok?

There are also two commands that'll save you some time and code space:

PUSHA and POPA (push all and Pop all)

PUSHA pushes the general registers in this order:

AX, CX, DX, BX, SP, BP, SI, DI

POPA pops the general registers in this order:

DI, SI, BP, (sp), BX, DX, CX, AX

SP is different because popa does NOT restore the value of SP. It merely pops it off and throws it away.

For the 386+, pushad and popad push and pop all extended registers in the same order. You don't need to memorize the order, because you don't need to know the order until you go and get tricky. (hint: the location of AX on the stack is [sp + 14] - useful if you want to change what AX returns, but you did a pusha cause you wanted to save all the registers (except AX) Then you'd do a popa, and AX= whatever value you put in there.

----

Alright, now a slightly different topic: memory management

Ok, this isn't true by-the-book memory management, but you need to know one thing: Upon execution of a program, DOS gives it ALL memory up to the address A000:0000. This happens to be the beginning of the VGA buffer... Another thing you must know is that, if you used DOSSEG at the top of your file, the segment is the last piece of your program. The size of the segment is derived from the little command `STACK 200h' or whatever the value was that you put up there. The 200h is the number of bytes in the stack. To get the number of paragraphs, you'd divide by 16. Here's an example of how I can get a pointer to the first valid available segment that I can use for data:

---------------------------------------------------------------------------- 
mov ax,ss ;grab the stack segment
add ax,200h/16 ;add the size of the stack 200h/16 = 20h

;AX now contains the value of the first available segment the you can
; use.
----------------------------------------------------------------------------

This is very nice, because you can just plop your data right there and you have a 64k buffer you can use for anything you want.

Ok, say you want to find out how much memory is available to use. This would be done like this: (no surprises, I hope.)

---------------------------------------------------------------------------- 
mov ax,ss ;grab the stack segment
add ax,200h/16 ;add the size of the stack 200h/16 = 20h
mov bx,0A000h ;upper limit of the free memory
sub bx,ax ;bx= # of paragraphs available
----------------------------------------------------------------------------

Pretty darn simple. That's enough of the overhead that you must know to understand the included ANSI viewer (asm3.asm)

Now to the FILE I/O stuff...

Files can be opened, read from, written to, created, and closed. To open a file, all you need to do is give the DOS interrupt a name & path. All references to that file are done through what's known as a file handle. A file handle is simply a 16bit integer that DOS uses to identify the file. It's used more or less like an index into chart of pointers that point to a big structure that holds all the info about the file- like current position in the file, file type, etc.. all the data needed to maintain a file. The `FILES= 20' thing in your autoexec simply tells DOS how much memory to grab for those structures. ( Files=20 grabs enough room for 20 open files. )

ANYway, here's each of the important function calls and a rundown on what they do and how to work them.

---------------------------------------------------------------------------- 
FILE OPEN: Function 3Dh

IN:
ah= 3Dh
al= open mode

bits 7-3: Stuff that doesn't matter to us
bits 2-0: Access code
000 read only access
001 write only access
010 read and write access

DS:DX= pointer to the ASCIIZ filename
ASCIIZ means that its an ASCII string with a Zero on the end.

Returns:
CF=1 error occured
AX= error code- don't worry about what they are, if the carry
is set, you didn't open the file.

CF=0 no error
AX= File Handle ;you need to keep this- it's your only way to
; reference your file!

---- EXAMPLE ----

[...] ;header stuff

.CODE ;this stuff is used for all the examples

FileName db "TextFile.TXT",0
FileHandle dw 0
Buffer db 300 dup (0)
BytesRead dw 0
FileSize dd 0

[...] ;more stuff

mov ax,3d00h ; open file for read only
mov ax,cs
mov ds,ax ;we use CS, cause it's pointing to the CODE segment
; and our file name is in the code segment
mov dx,offset FileName
int 21h
jc FileError_Open

mov [FileHandle],ax

[...] ;etc...

----------------------------------------------------------------------------
FILE CLOSE: Function 3Eh

IN:
AH= 3Eh
BX= File Handle

RETURN:
CF=1 error occured, but who cares?

---- EXAMPLE ----

mov bx,[FileHandle]
mov ah,3eh
int 21h

----------------------------------------------------------------------------
FILE READ: Function 3Fh

IN:
AH= 3Fh
BX= File Handle
CX= Number of bytes to read
DS:DX= where to put data that is read from the file (in memory)

RETURN:
AX= number of bytes actually read- if 0, then you tried to read from
the end of the file.

---- EXAMPLE ----

mov bx,[FileHandle]
mov ax,cs
mov ds,ax
mov dx,offset buffer
mov ah,3Fh
mov cx,300
int 21h

mov [BytesRead],ax

----------------------------------------------------------------------------
FILE WRITE: Function 40h

IN:
AH= 40h
BX= File Handle
CX= Number of bytes to write
DS:DX= where to read data from (in memory) to put on disk

RETURN:
AX= number of bytes actually written- if not equal to the number of bytes
that you wanted to write, you have an error.

---- EXAMPLE ----

mov bx,[FileHandle]
mov ax,cs
mov ds,ax
mov dx,offset buffer
mov ah,40h
mov cx,[BytesRead]
int 21h

cmp cx,ax
jne FileError_Write

----------------------------------------------------------------------------
FILE CREATE: Function 3Ch

IN:
ah= 3Ch
cl= file attribute

bit 0: read-only
bit 1: hidden
bit 2: system
bit 3: volume label
bit 4: sub directory
bit 5: Archive
bit 6&7: reserved

DS:DX= pointer to the ASCIIZ filename
ASCIIZ means that its an ASCII string with a Zero on the end.

Returns:
CF=1 error occured
AX= error code- don't worry about what they are, if CF
is set, you didn't create the file.

CF=0 no error
AX= File Handle ;you need to keep this- it's your only way to
; reference your file!
---- EXAMPLE ----

mov ah,3ch
mov ax,cs
mov ds,ax ;we use CS, cause it's pointing to the CODE segment
; and our file name is in the code segment
mov dx,offset FileName
mov cx,0 ;no attributes
int 21h
jc FileError_Create

mov [FileHandle],ax

----------------------------------------------------------------------------
FILE DELETE: Function 41h

IN:
ah= 41h
DS:DX= pointer to the ASCIIZ filename

Returns:
CF=1 error occured
AX= error code- 2= file not found, 3= path not found
5= access denied

CF=0 no error

---- EXAMPLE ----

mov ah,41h ;kill the sucker
mov ax,cs
mov ds,ax
mov dx,offset FileName
int 21h
jc FileError_Delete

----------------------------------------------------------------------------
FILE MOVE POINTER: Function 42h

IN:
ah= 42h
BX= File Handle
CX:DX= 32 bit pointer to location in file to move to
AL= 0 offset from beginning of file
= 1 offset from curent position
= 2 offset from the end of the file

Returns:
CF=1 error occured
AX= error code- no move occured

CF=0 no error
DX:AX 32 bit pointer to indicate current location in file

---- EXAMPLE ----

mov ah,42h ;find out the size of the file
mov bx,[FileHandle]
xor cx,cx
xor dx,dx
mov al,2
int 21h

mov [word low FileSize],ax
mov [word high FileSize],dx ;load data into filesize

(or in MASM mode,

mov word ptr [FileSize],ax
mov word ptr [FileSize+2],dx

need I say why I like Ideal mode? )

----------------------------------------------------------------------------
FILE CHANGE MODE: Function 43h

IN:
ah= 43h
DS:DX= pointer to the ASCIIZ filename
al= 0
returns file attributes in CX
al= 1
sets file attributes to what's in CX

Returns:
CF=1 error occured
AX= error code- 2= file not found, 3= path not found.
5= access denied

CF=0 no error

---- EXAMPLE ---- Lets erase a hidden file in your root directory...

FileName db "C:\msdos.sys",0

[...]

mov ah,43h ;change attribute to that of a normal file
mov ax,cs
mov ds,ax
mov dx,offset FileName
mov al,1 ;set to whats in CX
mov cx,0 ;attribute = 0
int 21h

mov ah,41h ;Nuke it with the delete command
int 21h

----------------------------------------------------------------------------

Well, that's all for now. I hope this info is enough for you to do some SERIOUS damage... :) I just don't want to see any 'bombs' running around erasing the hidden files in the root directory, ok?

Anyway, go take a look at asm3.asm- it's a SIMPLE ansi/text displayer. It just opens the file, reads it all into a "buffer" that was "allocated" immediately after the stack & reads in the entire file (if it's < 64k) and prints out the file character by character via DOS's print char (fn# 2). Very simple and very slow. You'd need a better print routine to go faster... The quickest display programs would decode the ANSI on its own... But that's kinda a chore... Oh, well. Enjoy.

Draeden/VLA


Suggested projects:

  1. Write a program that will try to open a file, but if it does not find it, the program creates the file and fills it with a simple text message.
  2. Write a program that will input your keystrokes and write them directly to a text file.
  3. The write & read routines actually can be used for a file or device. Try to figure out what the FileHandle for the text screen is by writing to the device with various file handles. This same channel, when read from, takes it's data from the keyboard. Try to read data from the keyboard. Maybe read like 20 characters... CTRL-Z is the end of file marker.
  4. Try to use a file as `virtual memory'- open it for read/write access and write stuff to it and then read it back again after moving the cursor position.

ASM3.ASM

;   VERY, VERY simple ANSI/text viewer 
;
; Coded by Draeden [VLA]
;

DOSSEG
.MODEL SMALL
.STACK 200h
.CODE
Ideal

;===- Data -===

BufferSeg dw 0

ErrMsgOpen db "Error opening `"
FileName db "ANSI.TXT",0,8,"'$" ;8 is a delete character
;0 is required for filename
;(displays a space)
FileLength dw 0

;===- Subroutines -===

PROC DisplayFile NEAR
push ds

mov ax,cs
mov ds,ax
mov ax,3d00h ;open file (ah=3dh)
mov dx,offset FileName
int 21h
jc OpenError
mov bx,ax ;move the file handle into bx

mov ds,[BufferSeg]
mov dx,0 ;load to [BufferSeg]:0000
mov ah,3fh
mov cx,0FFFFh ;try to read an entire segments worth
int 21h

mov [cs:FileLength],ax

mov ah,3eh
int 21h ;close the file

cld
mov si,0
mov cx,[cs:FileLength]
PrintLoop:
mov ah,2
lodsb
mov dl,al
int 21h ;print a character

dec cx
jne PrintLoop

pop ds
ret

OpenError:
mov ah,9
mov dx,offset ErrMsgOpen
int 21h

pop ds
ret
ENDP DisplayFile

;===- Main Program -===

START:
mov ax,cs
mov ds,ax
mov bx,ss
add bx,200h/10h ;get past the end of the file
mov [BufferSeg],bx ;store the buffer segment

call DisplayFile

mov ax,4c00h
int 21h
END START

← previous
loading
sending ...
New to Neperos ? Sign Up for free
download Neperos App from Google Play
install Neperos as PWA

Let's discover also

Recent Articles

Recent Comments

Neperos cookies
This website uses cookies to store your preferences and improve the service. Cookies authorization will allow me and / or my partners to process personal data such as browsing behaviour.

By pressing OK you agree to the Terms of Service and acknowledge the Privacy Policy

By pressing REJECT you will be able to continue to use Neperos (like read articles or write comments) but some important cookies will not be set. This may affect certain features and functions of the platform.
OK
REJECT