.........................................................................
.;             >>>>> 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.