......................................................................... .; >>>>> Please put the title here <<<<< .laser .topmargin 1" .margin 1" .bottommargin 1" .hlt bold .; pick one of the fonts, and comment out or erase the one you don't want .;font helvetica .font times .point 9.4285 .leading 1 .symbol on .bl | .; The following rulers work for Times-Roman and Helvetica .; Three columns per page (2 1/16") ..................................< AAA by SDH .center _Sequential File I/O !!!!!!!!_ Hot diggity dog! I believe we are ready to get into some simple file I/O via assembler! What we all have been waiting for, no? Before we continue, I suggest you make a real solid backup tape. Make that two (just in case). Make a warm boot tape too if you don't have one. Don't you just love this introduction????? .center _Learning from BASIC_ I assume that most of you know how to do simple file I/O from within BASIC. Let's take a look at the BASIC format and make some simple comparisons. Suppose there is a file called BOGUS.LST that you would like to read in from BASIC. You would have a BASIC source code line that would look something like this: _OPEN #1,"BOGUS.LST",INPUT_ _INPUT LINE #1,string_ Simple enough. The "#1" in BASIC lingo is your "channel number". For each unique file you want to access via BASIC, you need a unique channel number. I am assuming you are all familiar with this theory. When programming in assembler, we basically do the exact same procedures as the BASIC code above, but it just takes us a few more lines of code (but the lines are shorter in length, so I guess the tradeoff is fair). Our "channel number", however, is _not_ a number but a series of 150 octal bytes all in a row. This series of bytes is called a Dataset Driver Block, or a DDB. Time for a closer look. .center _The DDB_ To define a unique "channel number" for each unique file you would like to access or create, all you need to do is define a DDB - one for each file. You do this in the same manner as defining any other variable type. Yes, you guessed it, via the .OFDEF statement. AMOS even makes this definition a snap by supplying us with a "reserved symbol" _D.DDB_. The value of D.DDB is 150 (octal). Therefore, to define a DDB for a file, your line of code would look like this: _.OFDEF FILE1,D.DDB_ which is exacly equivalent to _.OFDEF FILE1,150_ .;ED - see me for "figure 1" Now take a look at figure 1. This is a snapshot of exactly how the DDB is broken down. Once we have established a DDB in our user memory partition via the .OFDEF statement, our next task is to "fill in the blanks" in the DDB. Not _all_ of the blanks need to be filled, just some of them - we will learn which ones when we examine the code below. _Setting Up the DDB_ Basically, all you do to open up a file or create a new (sequential) file is insert the correct values into a defined DDB, and then perform file I/O calls provided to you by the AMOS monitor calls. It is actually very simple (once someone has shown you how!). Here are the basic steps: ........................................................... .point 9 a) Create "DSK#:FILENAME.EXT[PPN]": * Place a RAD50 disk name in the DDB at D.DEV like #16003 ("DSK"). * Place an OCTAL drive number in the DDB at D.DRV like #1. * Place a RAD50 filename in D.FIL like #7337, #103070 ("BOGUS"). * Place a RAD50 extension in D.EXT like #47014 ("LST") * Place a word value PPN in D.PPN like #3402 (PPN [7,2]). b) Create a disk block buffer space via the INIT call. c) LOOKUP the file and see if it is there. d) OPEN the file for input, output, or append. e) CLOSE the file when done. ..................................< .point 9.4285 .center _Learning by a Simple Example_ I suppose I could ramble on about DDB's forever, but I think that you will learn faster by just examining a short file I/O example. The program below, ADDLF.M68, was created for a member who wanted to add a line-feed after every carriage return. The reason he wanted the line-feed addition was because he transfered over a PC file to the Alpha, and the PC treats RETURNS as (octal) ASCII 15, where as AMOS treats RETURNS as ASCII 15,12. Since he wanted to VUE the PC file on his Alpha, he needed the line-feed addition (the ASCII 12). .;ed, file is called AAA14.M68 in my account .center _Line-by-Line_ Now let's take a look at some of the lines in ADDLF. Since this is a "series-type" submission, I am only going to cover the new topics introduced in the code above. _.OFINI .OFDEF IDDB,D.DDB .OFDEF ODDB,D.DDB .OFSIZ IMPSIZ_ Here is where we define two DDB blocks in our user memory. Each of the DDBs has a variable name. IDDB is the _I_nput DDB, ODDB is the _O_utput DDB. IMPSIZ is a total of 320 octal bytes (two 150 byte blocks - yes octal 150 + octal 150 = octal 320!). _GETIMP IMPSIZ,A3 LEA A5,IDDB(A3) LEA A4,ODDB(A3)_ A3 is the dedicated address register that points to our variables in our user memory partition. LEA A5,IDDB(A3) has the effect of placing into register A5 the first memory address of the input DDB. The same theory applies to the output DDB, but A4 is used to point to this series of bytes. _TYPE <Enter....> KBD FSPEC @A5_ FSPEC is an AMOS monitor call that takes the ASCII string pointed to by A2 and converts this ASCII string into a RAD50 representation, AND places this representation into the DDB pointed to by the address register that follows the FSPEC call. Wow! The KBD call on the line above the FSPEC call has the effect of stopping the program and waiting for user input ASCII data - hopefully in a format like "BOGUS.LST" or "DSK2:A.AT[24,42]". A2 ends up pointing to the user data entered. Once this data has been entered, FSPEC will place the 4-byte RAD50 representation of the file into the DDB at an offset of D.FIL (D.FIL = 4), and place a 2-byte RAD50 representation of the extension in the DDB at an offset of D.EXT (D.EXT = 10). See figure one again to see where these values are actually placed. If you had entered in the DSK#: and [PPN], FSPEC would have also placed these values in the correct slots within the DDB. If you left off the DSK#:[PPN], the following occurs: D.DEV, D.DRV, and D.PPN all take on "default" values that indicate to AMOS to use the users current DSK#:[PPN] log location. Quite a hefty little monitor call, no? Step (a) discussed above is now complete. _INIT @A5_ The INIT call has two effects. The INIT call will locate the device driver necessary to communicate with the disk hardware (according to the D.DEV value) and place this driver address within the DDB at offset D.DVR. INIT will also search your user memory for a 512 byte block of free memory and "allocate" this block of memory as "in use". This is where the data for each disk block will be placed. The address of the first byte of the 512 byte block will then be placed in the DDB at D.BUF. Think of this second action as an invisible "GETIMP" call. Step (b) is now complete. _LOOKUP @A5_ _JNE BOOBOO_ The LOOKUP call will take the data contained in the DDB (pointed to by A5 in this case), scan the corresponding directory for this file, and see if it can locate the file. If the file exists, then the JNE (Jump if Not Equal) instruction will not occur, and the program continues to run with the line below the JNE instruction. If LOOKUP does not find the file, the program proceeds to the first instruction marked by the label BOOBOO. Step (c) is now complete. _OPENI @A5_ OPEN has three formats for sequential file I/O. They are OPENI (for open input), OPENA (for open append), and OPENO (for open for output). You may only open files for input or append if the sequential file already exists. OPENO has the effect of creating a new file in the directory - once the file is closed! For the above instruction, the OPENI call looks at the filespec contained in the DDB pointed to by A5 and opens up this file for input. _FILINB @A5_ FILIN grabs the "next" byte, word, or longword in a sequential file pointed to by the address following the FILIN call, and place this value into register D1. FILIN has three formats for sequential file reading. FILINL will the next longword of the file, FILINW will grab the next word, and FILINB will access the next byte in the sequential file. Since you must read sequential files sequentially (oh really?), all sequential file reading consists of a loop with the FILIN call to access the information on the disk. You never need to worry about block pointers - AMOS does this for you automatically. _TST IDDB+D.SIZ(A3) BEQ EOF_ This line has the BASIC equivalent of IF EOF(1) THEN GOTO END'OF'FILE. The TST call checks the value of the D.SIZ longword in the input DDB. If the last FILIN was at eof, the branch to EOF would occur. _FILOTB @A4_ This call is almost identical to the FILIN call, except the information is "written" out to the disk instead of "read". Again, AMOS takes care of the block pointers et. al. D1 should contain the byte, word, or longword that you would like to write out to the file. Notice that we are creating a new file with the FILOT call. Only when we CLOSE this file will the directory display this new file to us. _CMPB D1,#15 BNE BYB MOVB #12,D1 FILOTB @A4_ Here is the heart of the entire algorithm (program). If a carriage return was just sent out to the output file, we need to follow this character with a line feed (octal value 12). The CMPB D1,#15 checks to see if the last character was indeed a CR, and if so, we output a line feed right after the CR. _CLOSE @A5_ _CLOSE @A4_ The CLOSE monitor call expects a valid DDB pointer to follow the CALL instruction. If you do not close an input file before an EXIT, AMOS will do this for you with no nasty side effects. However, you MUST close an output file if you would like this file to be inserted into the directory. Yes, AMOS wrote out all of the information to blocks on your disk, but intil the CLOSE @A4 call, the directory will not show the file! Therefore, be sure to CLOSE all output files before any EXIT call. .center _A Wrap_ I know this may seem a bit much the first time through, so I will continue next month with another example similar to this. Do you think that after reading the above code you could create a DELLF.M68 file that would delete the line feeds of an AMOS VUE file to "pre-process" this file for PC transmission? See if you can figure that algorithm out - it is very similar to the ADDLF.M68 file above. Have fun, CLOSE your new output files, and I'll see you next month.