In this post I explore file input and output using Action!.
Action! has several functions for getting things into and out of files. In the post dealing with Print and Put I left out the device specific versions. The addition Print and Put procedure specifically dealing with files (or technically devices) are:
- PutD – Outputs a single byte to a device
- PutDE – Outputs a single byte and EOL to a device
- PrintD – Outputs a string (char array) to a device
- PrintDE – Outputs a string (char array) and EOL to a device
- PrintBD – Outputs a byte value to a device
- Print BDE – Outputs a byte value and EOL to a device
- PrintCD – Outputs a card value to a device
- PrintCDE – Outputs a card value and EOL to a device
- PrintID – Outputs an int value to a device
- PrintIDE – Outputs an int value and EOL to a device
For input routines dealing with devices (or files), there are these additional commands:
- GetD – Gets a single byte from a device
- InputBD – Gets a byte value from a device
- InputCD – Gets a card value from a device
- InputID – Gets an int value from a device
- InputSD – Gets a string from a device
- InputMD – Gets a string of max length x from a device
As you can see the commonality between them all is the “D” in the command suffix. This applies to both the print (output) and input (input) routines.
The input and output routines are useless unless you know how to open and close a device (or file) for reading and writing. Action! provides Open and Close procedures to facilitate that.
The OPEN command opens a device (or file) for reading and/or writing and takes several parameters. The first is the device number to open. You can use 1 through 6. 7 is the keyboard and is off limits. The second parameter is the device (or file) to open. You can open a device such as the printer with “P:” or a file on disk using the Dx:FILENAME.EXT specification. The third parameter is the mode to open the device (or file) in. The two most important values are 4 (read) and 8 (write), but there are others – you can look them up in many Atari or Action! books. The final parameter is an auxiliary byte to pass to the device.
The CLOSE command closes a previously opened device (or file) and takes one parameter, which is the device number to close.
To demonstrate the output and input abilities I wrote a small program that writes some values to a file, clears the values, then reads them back in. The values are displayed at the start, after clearing, and after reading.
One other thing this program introduces is replacing the default Error vector with a custom error procedure. This can be done for any of the built-ins. Very cool!
Source Breakdown
Here I breakdown the source code first, then present it complete.
Start of the program.
; Start MODULE
Declaring the globals needed for working with the custom error procedure. A CARD value is needed to save the original Error vector. A BYTE value is needed to store the last error value encountered.
; Variable to save Error vector CARD cErrOrig BYTE bgError=[0]
This is the custom error routine. It needs to accept a byte value as a parameter which is the error number encountered. This custom routine saves the error value to the global bgError for use in other code. The routine prints an ERROR message with the error number, and a description if known. The conditional for the description is another new construct which I’ll cover in the next post. The conditional in this case prints a descriptive message for errors 170 and 130.
; Custom Error routine PROC CustError(BYTE bError) ; Set global error number bgError=bError ; Print custom error message PrintF("%EERROR: %B ") if bError=170 then PrintE("-File not found!") elseif bError=130 then PrintE("-Invalid device!") fi RETURN
Here I initialize the main routine. Two character arrays are initialized to 15 bytes in size, with cStr receiving the initial value of “Inverse ATASCII”. And a BYTE named bIn is initialized to the value 65:
; Main routine PROC Main() CHAR ARRAY cFile(15),cStr(15)="Inverse ATASCII" BYTE bIn=[65]
This block of code saves the original Error vector, then assigns the custom routine to the Error vector:
; Save and reassign error vector cErrOrig=Error Error=CustError
This section just sets up the screen.
; Setup screen Poke(82,0) Graphics(0) PutE() PrintE("-[File I/O]-----------------------------")
This prompts for a filename from the user, then gets it and stores it in cFile. 15 characters max which is big enough to hold the drive, filename, and extension as Dx:FILENAME.EXT :
; Get filename Print("Filename?") InputS(cFile)
Now I print out the initial values of the variables:
PutE() PrintE("Default variables.") PrintF("String: %S%E",cStr) PrintF("Byte : %B%E",bIn)
This is the code that opens the user specified file for writing on device channel 4:
; Open file for writing Open(4,cFile,8,0)
This section first checks to see if an error was encountered opening the file. If an error had been encountered the custom error routine would have save the error number to the global bgError. The important piece of this block is the PutD and PrintD. These commands output the BYTE (bIn) into the file, and the string (cStr) to device channel 4 (which is a disk file, but could have been a printer or RS-232 port, etc). It then closes the device.
; If no error, proceed if bgError=0 then PutE() PrintE("File opened for write.") ; Write data to file PutD(4,bIn) PrintD(4,cStr) ; Close file Close(4) PrintE("File closed."); fi
Now I reset the variables. The string (cStr) is reset to blank. The byte (bIn) is reset to 0. Afterwards, I print the values out to prove they were reset:
; Reset variables sCopy(cStr," ") bIn=0 ; Show variables cleared PutE() PrintE("Cleared variables.") PrintF("String: %S%E",cStr) PrintF("Byte : %B%E",bIn)
Next I open the file reading, again on device channel 4:
; Open file for reading Open(4,cFile,4,0)
This section of code is very similar to the write section. It first checks if an error was encountered during the open. The important pieces are the GetD and InputSD commands which input the BYTE (bIn) and the string (cStr) from device channel 4 (which is a file in this case). It then closes the device:
If bgError=0 then PutE() PrintE("File opened for reading.") ; Read data from file bIn=GetD(4) InputSD(4,cStr) ; Close file Close(4) PrintE("File closed.") fi
This displays the values of the variables now that they have been read in from the device (file) – just to prove they were read as they should have been:
; Show variables after read PutE() PrintE("Variables read.") PrintF("String: %S%E",cStr) PrintF("Byte : %B%E",bIn)
The program end:
RETURN
Complete Source
; Start MODULE ; Variable to save Error vector CARD cErrOrig BYTE bgError=[0] ; Custom Error routine PROC CustError(BYTE bError) ; Set global error number bgError=bError ; Print custom error message PrintF("%EERROR: %B ") if bError=170 then PrintE("-File not found!") elseif bError=130 then PrintE("-Invalid device!") fi RETURN ; Main routine PROC Main() CHAR ARRAY cFile(15),cStr(15)="Inverse ATASCII" BYTE bIn=[65] ; Save and reassign error vector cErrOrig=Error Error=CustError ; Setup screen Poke(82,0) Graphics(0) PutE() PrintE("-[File I/O]-----------------------------") ; Get filename Print("Filename?") InputS(cFile) PutE() PrintE("Default variables.") PrintF("String: %S%E",cStr) PrintF("Byte : %B%E",bIn) ; Open file for writing Open(4,cFile,8,0) ; If no error, proceed if bgError=0 then PutE() PrintE("File opened for write.") ; Write data to file PutD(4,bIn) PrintD(4,cStr) ; Close file Close(4) PrintE("File closed."); fi ; Reset variables sCopy(cStr," ") bIn=0 ; Show variables cleared PutE() PrintE("Cleared variables.") PrintF("String: %S%E",cStr) PrintF("Byte : %B%E",bIn) ; Open file for reading Open(4,cFile,4,0) ; If no error, proceed If bgError=0 then PutE() PrintE("File opened for reading.") ; Read data from file bIn=GetD(4) InputSD(4,cStr) ; Close file Close(4) PrintE("File closed.") fi ; Show variables after read PutE() PrintE("Variables read.") PrintF("String: %S%E",cStr) PrintF("Byte : %B%E",bIn) RETURN
Results
The program running:
Looking at the file created you can see the byte (bIn, value 65) as the “A” at byte 0000, followed by the string (cStr, value “Inverse ATASCII”) in the next 15 bytes:
Next I’ll look at conditionals.