ACE Programmer's Reference Guide

by Craig Bruce -- for Release #16 -- Rev. February 9, 1997.


ACE is a program for the Commodore 128 and Commodore 64 that provides a
command shell environment that is similar to that of Unix.  It is still in
the development stage, but enough of it is complete to be useful.  BTW,
"ACE" means "Advanced Computing Environment" (well, advanced for the

So what is ACE all about?  Well, originally I tried a very ambitious project
of writing a multitasking operating system for the 128.  It got it partially
working, but it was much too fragile and incomplete to be released.  It was
a white-elephant project.  So, then then it came to me that I was aiming
much too high.  What I needed was a much simpler system, one that would give
the type of programming interface and built-in features that I wanted, but
one that was close enough to the Commodore Kernal that it would not require
much of a programming effort to hack together a minimal implementation.  And
thus, there was ACE-128 Release #1.  And I saw it was good.

What I wanted was a system that would be easier to program than the
Commodore Kernal with all its weird and wonderful device types, non-existent
memory management, and single-application design.  The first important
feature of this environment was to be able to pass arguments from a command
line to an application program by typing them on the command line of the
shell.  It is so annoying to load up a program in BASIC, run it, and have it
ask you for filenames in some highly inconvenient way.

Another important system feature is to make near and far memory management
part of the system, and to make far memory convenient to use for storing
massively bulky data.  And so it was.  Also, we want to use custom device
drivers.  Commodore didn't really come through with the device drivers it
provided.  They are all REALLY SLOW.  And so that was, also, although more
custom device drivers are needed.  We want to have the capability of making
programs work together, rather than having programs that are totally
incompatible.  This functionality is still under construction.  Programs
work together in this uni-tasking environment by allowing a program to
execute another program as a sub-task, and then having control return to the
calling program upon exit.  Finally, we want some good quality applications
and a powerful command shell.  This is still being worked on and progress is
coming slowly.  Oh, almost forgot; we also want all programs to work on both
the C64 and C128, and they do.


This section describes the interface between user programs and the ACE
kernel.  I am very careful throughout this interface specification about
revealing any internal details that you do not strictly need to know.  The
interface with ACE is not specified in terms of absolute addresses; to aid
in portability and extensibility, all interfaces are specified in terms of
symbolic assembler labels.  Most of the ACE code is currently written for
the Buddy-128 assembler with some of it written for the ACE assembler.
Also, because these interface absolute addresses are subject to change from
version to version of the kernel, executables compiled for use with an old
version of ACE may not work with a new version.


There are four zero-page variables used for passing arguments in most system
calls.  They are as follows:

-------  -----   -----------
zp           2   zeropage pointer
zw           2   zeropage word
mp           4   memory pointer
syswork     16   system work area / arguments

The first two, "zp" and "zw" are used in many calls.  They store simple
16-bit values; "zp" usually stores pointers to strings in application
memory.  The "mp" variable is 32-bits in length and is used exclusively for
passing far memory pointers for use with the far memory routines.  All three
of these variables will remain unchanged inside of system call unless they
will contain a return value.  "syswork" is a 16-byte array used mainly when
there are too many arguments for other variables to hold, and all non-input
and non-output bytes of "syswork" are subject to change by the kernel.  All
input arguments placed in the "syswork" locations will be preserved unless
otherwise indicated.


There are several non-zeropage variables for storing system status and
return values:

----------      -----   -----------
errno               1   error number code returned by failed system calls
aceArgc             2   argument count for current process
aceArgv             2   argument vector address for current process
aceMemTop           2   highest address, plus one, that user prog can use
aceDirentBuffer   storage for directory entries read from disk
aceDirentLength     -   really a constant: length in bytes of "aceDirentBuffer"
aceDirentBytes      4   bytes in file (usually inexact)
aceDirentDate       8   date of file in "YY:YY:MM:DD:HH:MM:SS:TW" format
aceDirentType       4   type of file in null-terminated string
aceDirentFlags      1   flags of file, "drwx*-et" format
aceDirentUsage      1   more flags of file, "ulshm---" format
aceDirentNameLen    1   length of name of file
aceDirentName      17   null-terminated name of file
aceExitData       256   storage for exit status from the last called prg

ERRNO: "errno" is used to return error codes from system calls.  When a
system call ends in error, it sets the carry flag to "1", puts the error
code in "errno", and returns to the user program, after undoing any system
work completed at the time the error is encountered and aborting the
operation.  An error code number is stored in binary in the single-byte
"errno" location. The symbolic names for the possible error codes are given
in the next section. If no error occurs in a system call, the carry flag
will be cleared on return from the call.  Note that not all system calls can
run into errors, so not all set the carry flag accordingly.

ARGC: "aceArgc" is a two-byte unsigned number.  It gives the number of
arguments passed to the application by the program (usually the command
shell) that called the application.  The first argument is always the name
of the application program, so the count will always be at least one.  Other
arguments are optional.

ARGV: "aceArgv" is a two-byte RAM0 pointer.  Pay attention.  This pointer
points to the first entry of an array of two-byte pointers which point to
the null-terminated strings that are the arguments passed to the application
program by the caller.  (A null-terminated string is one that ends with a
zero byte).  To find the address of the N-th argument to an application,
multiply N by two, add the "aceArgv" contents to that, and fetch the pointer
from that address.  In this scheme, the ever-present application name is the
0-th argument.  The argv[argc] element of the argument vector will always
contain a value of $0000, a null pointer.

MEM-TOP: "aceMemTop" is a two-byte RAM0 pointer.  This points to one byte
past the highest byte that the application program is allowed to use.  All
application programs are loaded into memory at address "aceAppAddress" (next
section), and all memory between the end of the progam code and "aceMemTop"
can be used for temporary variables, file buffers, etc.  The main problem
with this approach is that there are no guarantees about how much memory
your application will get to play with.  Many applications, such as simple
file utilities, can simply use all available memory for a file buffer, but
other programs, such as a file compressor, may have much greater demand for
"near" memory.

DIRENT-BUFFER: "aceDirentBuffer" is a buffer used for storing directory
information read with the "dirread" system call, and is "aceDirentLength"
bytes long.  Only a single directory entry is (logically) read from disk at
a time.  The individual fields of a read directory entry are accessed by the
fields described next.  This field is also used for returning disk name
information and the number of bytes free on a disk drive (see the "dirread"
system call).

DIRENT-BYTES: "aceDirentBytes" is a four-byte (32-bit) unsigned field.  As
always, the bytes are addressed from least significant to most significant.
This field gives the number of bytes in the file.  Note that this value may
not be exact, since Commodore decided to store sizes in disk blocks rather
than bytes.  For devices that report only block counts (i.e., every disk
device currently supported), the number of bytes returned is the number of
blocks multiplied by 254.  This field, as well and the other dirent fields
are absolute addresses, not offsets from aceDirentBuffer.

DIRENT-DATE: "aceDirentDate" is an eight-byte array of binary coded decimal
values, stored from most significant digits to least significant.  The first
byte contains the BCD century, the second the year, and so on, and the last
byte contains the number of tenths of seconds in its most significant nybble
and a code for the day-of-week in its least significant nybble.  Sunday has
code 0, Monday 1, etc., Saturday 6, and a code of 7 means "unknown".  This
is the standard format for all dates used in ACE.  This format is abstracted
as "YY:YY:MM:DD:HH:MM:SS:TW".  For disk devices that don't support dates,
this field will be set to all zeroes, which can be conveniently interpreted
as the NULL date, negative infinity, or as the time that J.C. was a
seven-year-old boy.

DIRENT-TYPE: "aceDirentType" is a three-character (four-byte) null-
terminated string.  It indicates what type the file is, in lowercase
PETSCII.  Standard types such as "SEQ" and "PRG" will be returned, as well
and other possibilities for custom device drivers.

DIRENT-FLAGS: "aceDirentFlags" is a one-byte field that is interpreted as
consisting of eight independent one-bit fields.  The abstract view of the
fields is "drwx*-et".  "d" means that the item is a subdirectory (otherwise
it is a regular file), "r" means the item is readable, "w" means the item is
writable, and "x" means the item is executable.  The "x" option is really
not supported currently.  "*" means the item is improperly closed (a "splat"
file in Commodore-DOS terminology).  The "-" field is currently undefined.
"e" means that the value given in the "aceDirentBytes" field is actually
exact, and "t" means the file should be interpreted as being a "text" file
(otherwise, its type is either binary or unknown).  The bit fields are all
booleans; a value of "1" means true, "0", false.  The "d" bit occupies the
128-bit position, etc.

DIRENT-USAGE: "aceDirentFlags" is a one-byte field very much like
"aceDirentFlags".  The abstract view of the fields is "ulshm---".  "u"
indicates whether the directory entry is used (current) or not (deleted).
The directory system calls will not read a directory entry that is deleted,
so you will never see this bit not set; it is used internally.  "l"
indicates whether the directory entry is for an actual file (==0, normal) or
a "link" (==1) to another file.  The "s" and "h" bits indicate which type a
link is:  "soft" or "hard", respectively.  These both work similarly to Unix
hard and soft links.  The "m" flag indicates whether a file has been
modified since the last time that a backup program cleared the "m" bit for
the file, allowing incremental backups.

DIRENT-NAME-LEN: "aceDirentNameLen" is a one-byte number.  It gives the
number of characters in the filename.  It is present for convenience.

DIRENT-NAME: "aceDirentName" is a 16-character (17-byte) null-terminated
character string field.  It gives the name of the file or directory or
disk.  Filenames used with ACE are limited to 16 characters.

EXIT-DATA: "aceExitData" is a 256-byte array.  It is the 256-byte buffer
allocated for user programs to give detailed return information upon exiting
back to their parent program.  See the "exit" system call.  User programs
are allowed to read and write this storage.  An example use of this feature
would be a compiler program returning the line number and character
position, and description of a compilation error to a text editor, so the
editor can position the cursor and display the error message for user
convenience.  The implementation of this feature may need to change in
future versions of ACE.


There are several symbolic constants that are used with the ACE system

SYMBOL                   DESCRIPTION
-------------------      -------------------------
aceAppAddress            the start address of applications
aceID1                   the id characters used to identify ACE applications
aceID2                   ...
aceID3                   ...
aceMemNull               the far memory type code used to indicate null ptrs
aceMemREU                far mem type code for Ram Expansion Unit memory
aceMemRL                 far mem type code for direct-access RAMLink memory
aceMemInternal           far mem type code for internal memory
aceErrStopped            error code for syscall aborted by STOP key
aceErrTooManyFiles       err: too many files already opened to open another
aceErrFileOpen           err: don't know what this means
aceErrFileNotOpen        err: the given file descriptor is not actually open
aceErrFileNotFound       err: named file to open for reading does not exist
aceErrDeviceNotPresent   err: the specified physical device is not online
aceErrFileNotInput       err: file cannot be opened for reading
aceErrFileNotOutput      err: file cannot be opened for writing
aceErrMissingFilename    err: pathname component is the null string
aceErrIllegalDevice      err: the specified device cannot do what you want
aceErrWriteProtect       err: trying to write to a disk that is write-protected
aceErrFileExists         err: trying to open for writing file that exists
aceErrFileTypeMismatch   err: you specified the file type incorrectly
aceErrNoChannel          err: too many open files on disk drive to open another
aceErrInsufficientMemory err: ACE could not allocate the memory you requested
aceErrOpenDirectory      err: you are trying to open a dir as if it were a file
aceErrDiskOnlyOperation  err: trying to perform disk-only op on char device
aceErrNullPointer        err: trying to dereference a null far pointer
aceErrInvalidFreeParms   err: bad call to "aceMemFree": misaligned/wrong size
aceErrFreeNotOwned       err: trying to free far memory you don't own
aceErrInvalidWindowParms err: invalid window dimensions were given
aceErrInvalidConParms    err: invalid console parameters were given
aceErrInvalidFileMode    err: opening a file for other-than "r","w", or "a"
aceErrNotImplemented     err: system call or option is not (yet) implemented
aceErrBloadTruncated     err: a bload operation stopped before exceeding limit
aceErrPermissionDenied   err: attempt to read or write a file without perms
aceErrNoGraphicsSpace    err: graphics area is not available for operation
aceErrBadProgFormat      err: specified program file has wrong format
chrBEL                   character code: bell
chrTAB                   character code: tab
chrBOL                   character code: beginning of line (return)
chrCR                    character code: carriage return (newline)
chrVT                    character code: vertical tab (down, linefeed)
chrBS                    character code: backspace (del)
chrCLS                   character code: clear screen (form feed)
stdin                    file descriptor reserved for stdin input stream
stdout                   file descriptor reserved for stdout output stream
stderr                   file descriptor reserved for stderr output stream

"aceAppAddress", as discussed before, is the address that application programs
are loaded into memory at.  They must, of course, be assembled to execute
starting at this address.

The "aceMem" group of constants are for use with the "aceMemAlloc" system
call, except for "aceMemNull", which may be used by application programs for
indicating null far pointers.  The "aceMemAlloc" call allows you to specify
what types of memory you are willing to accept.  This is important because
the difference types of memory have different performance characteristics.
ACE will try to give you the fastest memory that is available.  Ram
Expansion Unit memory has startup and byte-transfer times of about 60 us
(microseconds) and 1 us, respectively.  This is the fastest type of far
memory.  Internal memory has a startup time of 24 us and a byte-transfer
time of between 7 and 14 us (depending on whether accessing RAM0 or RAM1+).
Direct-access RAMLink memory has a startup time of 200 us and a byte-
transfer time of 8.5 us.  If you have an REU plugged into the RAM port of
your RAMLink, then the startup time of 320 us and a byte-transfer time of
2.7 us.  All these times are for the C128 in 2 MHz mode.

The "aceErr" group gives the error codes returned by system calls.  The
error codes are returned in the "errno" variable.  Not all possible error
codes from Commodore disk drives are covered, but the important ones are.
The "chr" group gives the character codes that have special control
functions when printed using the "write" system call (below).  Finally, the
"std" files group give the symbolic file descriptor identifiers of the
default input, output, and error output file streams.


All system calls are called by setting up arguments in specified processor
registers and memory locations, executing a JSR to the system call address,
and pulling the return values out of processor registers and memory


NAME   :  open
PURPOSE:  open a file
ARGS   :  (zp) = pathname
          .A   = file mode ("r", "w", "a", "W", or "A")
RETURNS:  .A   = file descriptor number
          .CS  = error occurred flag
ALTERS :  .X, .Y, errno

Opens a file.  The name of the file is given by a pointer to a null-
terminated string, and may contain device names and pathnames as specified
in the ACE user documentation.  The file mode is a PETSCII character.  "r"
means to open the file for reading, "w" means to open the file for writing,
and "a" means to open the file for appending (writing, starting at the end
of the file).  An error will be returned if you attempt to open for reading
or appending a file that does not exist, or if you attempt to open for
writing a file that does already exist.  On the other hand, calling with the
capital letters "W" and "A" mean to force a write or append if needed, if
the file either already exists or does not already exist, respectively.

The function returns a file descriptor number, which is a small unsigned
integer that is used with other file calls to specify the file that has been
opened.  File descriptors numbered 0, 1, and 2 are used for stdin, stdout,
and stderr, respectively.  The file descriptor returned will be the minimum
number that is not currently in use.  These numbers are system-wide (rather
than local to a process as in Unix), and this has some implications for I/O
redirection (see the "aceFileFdswap" call below).

Restrictions: only so many Kernal files allowed to be open on a disk device,
and there is a system maximum of open files.  You will get a "too many
files" error if you ever exceed this limit.  Also, because of the nature of
Commodore-DOS, there may be even tighter restrictions on the number of files
that can be simultaneously open on a single disk device, resulting in a "no
channel" error.  Note that this call checks the status channel of Commodore
disk drives on each open, so you don't have to (and should not anyway).

If the current program exits either by calling "exit" or simply by doing the
last RTS, all files that were opened by the program and are still open will
be automatically closed by the system before returning to the parent
NAME   :  close
PURPOSE:  close an open file
ARGS   :  .A   = File descriptor number
RETURNS:  .CS  = error occurred flag
ALTERS :  .A, .X, .Y, errno

Closes an open file.  Not much to say about this one.
NAME   :  read
PURPOSE:  read data from an open file
ARGS   :  .X   = File descriptor number
          (zp) = pointer to buffer to store data into
          .AY  = maximum number of bytes to read
RETURNS:  .AY  = (zw) = number of bytes actually read in
          .CS  = error occurred flag
          .ZS  = EOF reached flag
ALTERS :  .X, errno

Reads data from the current position of an open file.  Up to the specified
maximum number of bytes will be read.  You should not give a maximum of zero
bytes, or you may misinterpret an EOF (end of file).  The buffer must be at
least the size of the maximum number of bytes to read.  The data are not
interpreted in any way, so it is the programmer's responsibility to search
for carriage return characters to locate lines of input, if he so desires.
However, for the console the input is naturally divided up into lines, so
each call will return an entire line of bytes if the buffer is large
enough.  There are no guarantees about the number of bytes that will be
returned, except that it will be between 1 and the buffer size.  So, if you
wish to read a certain number of bytes, you may have to make multiple read

The call returns the number of bytes read in both the .AY register pair and
in (zw), for added convenience.  A return of zero bytes read means that the
end of the file has been reached.  An attempt to read beyond the end of file
will simply give another EOF return.  End of file is also returned in the .Z
flag of the processor.
NAME   :  write
PURPOSE:  write data to an open file
ARGS   :  .X   = file descriptor number
          (zp) = pointer to data to be written
          .AY  = length of data to be written in bytes
RETURNS:  .CS  = error occurred
ALTERS :  .A, .X, .Y, errno

Writes data at the current position of an open file.  For writing to the
console device (where many text files will end up being displayed
eventually), the following special control characters are interpreted:

---------   ---------   ----   -----------
$07         7           BEL    ring the bell
$09         9           TAB    move cursor to next 8-char tab stop
$0a         10          BOL    move cursor to beginning of current line
$0d         13          CR     go to start of next line (newline)
$11         17          VT     go down one line (linefeed)
$14         20          BS     non-destructive backspace
$93         147         CLS    clear the screen and home the cursor
NAME   :  aceFileLseek
PURPOSE:  seek to the given file position
ARGS   :  .X   = file descriptor number
          .A   = origin of the seek: 0=start of file, 1=cur pos, 2=end of file
         [sw+0]= position to go to
RETURNS: [sw+0]= resulting file position
          .CS  = error occurred flag
ALTERS :  .A, .X, .Y, errno

Seeks to the given file position, relative to the given base, which can be
either the start of the file, the current position in the file, or the end
of the file.  In the case of the current position, a two's-complement offset
is given.  The file positions are 32-bit quantities.  The position returned
is always relative to the start of the file.  This call will only work with
special device drivers which are actually designed to randomly access
files.  Currently, there are no such devices.
NAME   :  aceFileBload
PURPOSE:  binary load
ARGS   :  (zp) = pathname
          .AY  = address to load file
          (zw) = highest address that file may occupy, plus one
RETURNS:  .AY  = end address of load, plus one
          .CS  = error occurred flag
ALTERS :  .X, errno

Binary-load a file directly into memory.  If the file will not fit into the
specified space, an error will be returned and the load truncated if the
device supports truncation; otherwise, important data may be overwritten.
NAME   :  aceFileRemove
PURPOSE:  delete a file
ARGS   :  (zp) = pathname
RETURNS:  .CS  = error occurred flag
ALTERS :  .A, .X, .Y, errno

Delete the named file.
NAME   :  aceFileRename
PURPOSE:  rename a file or directory
ARGS   :  (zp) = old filename
          (zw) = new filename
RETURNS:  .CS  = error occurred flag
ALTERS :  .A, .X, .Y, errno

Renames a file or directory.  If a file with the new name already exists,
then the operation will be aborted and a "file exists" error will be
returned.  On most devices, the file to be renamed must be in the current
directory and the new name may not include any path, just a filename.
NAME   :  aceFileInfo
PURPOSE:  give information about file/device
ARGS   :  .X   = file descriptor number
          .A   = info-type flags ($00 for these returns,$01=ready,$02=dirinfo)
RETURNS:  .A   = device type code (0=console, 1=char-dev, 2=disk-dev)
          .X   = number of columns on device
          .Y   = number of rows per "page" of device
          .CS  = error occurred flag
ALTERS :  errno

This call returns information about the device of an open file.  There are
four possible values for the device type code: 0==console, 1==character-
oriented device, and 2==disk device.  The number of rows and columns per
"page" of the device are also returned.  For the console, this will be the
current window size.  For a character-oriented device, it will be the
natural size (typically 80 columns by 66 rows), and for a disk, it will be
40 columns in 64 mode or 80 columns in 128 mode, both by 66 rows.

If you call with a type of $01 in the accumulator, the return information
will be in the accumulator only telling whether the file is "ready" or not,
where the interpretation of "ready" is device-dependent.  The $80 bit tells
whether the device is ready for a read operation (i.e., a read won't
"block"), and $40 tells whether the device is ready for a write operation
(i.e., won't block on the first byte).

If you call with a type of $02 in the accumulator, then the information
about the file is returned as above, plus the "aceDirent" information about
the file is also filled in if the file descriptor is for a disk file (if the
information is accessible).

If you call with a type of $03, then (zp) is interpreted as pointing to a
pathname string, and the info about the file referred to by that pathname
returned in the same way as for type $02 above.  (Here, the .X input
argument is ignored).
NAME   :  aceFileIoctl
PURPOSE:  perform special io-device control operations
ARGS   :  .X   = file descriptor number
          .A   = operation code
          ...  = any other arguments (operation-dependent)
RETURNS:  .A   = status code
          .CS  = error-occurred flag
ALTERS :  .X, .Y, errno

Performs device-specific io-control operations.  This is described in more
detail in section 3.10.
NAME   :  aceFileSelect
PURPOSE:  wait for one of multiple input sources to become ready
ARGS   :  .A   = console sources: $80=keyboard, $40=mouse, $20=joy1, $10=joy2,
                                  $02=priority, $01=timeout
          .X   = number of file descriptors
         (sw+0)= old mouseX
         (sw+2)= old mouseY
          sw+3 = old mouse buttons
          sw+4 = old joystick1 status
          sw+5 = old joystick2 status
         (sw+6)= jiffy timeout
          sw+8 = file descriptor 1 (and so on, to sw+16 = file descriptor 8)
RETURNS:  .A   = console-source bit or $00
          .X   = file descriptor or $ff
          .CS  = error-occurred flag
ALTERS :  .Y, errno

This call waits for one of a multiple number of input sources to become
ready for reading.  The keyboard and a console input file are treated
slightly differently here, as console input readiness includes still-unread
characters in the input-line buffer in addition to the keyboard buffer.  For
the mouse and joysticks, you have to supply the old statuses and the system
will determine them to be "ready" when the current status changes from the
status you gave.  You can also specify a timeout period in jiffies.  You
specify whether these console items should be waited for by setting bits in
the accumulator.  If you're not waiting for something, then you don't have
to set its "sw+0 to sw+7" value.

You can also wait on file descriptors (presumably to the console, a modem,
the network device, etc., as disk files will always report "ready").  You
put the file-descriptor numbers into "sw+8" onward for as many file
descriptors you want to wait on, up to eight of them, and you load the
number of file descriptors into the .X register.

The console inputs are always scanned in order of keyboard, mouse, joy1, and
joy2 for priority and the file descriptors are scanned in the order you give.
The $02 bit (priority) of the accumulator tells whether the console inputs
(with it set) or the file-descriptor inputs (with the priority bit clear)
should be scanned first.  For better round-robin fairness, you might toggle
this bit every time you make this system call.

The system will then wait for one of the input sources to become ready and
will return with one of them to you.  If a console input was chosen, then
the corresponding bit to the event will be returned in the accumulator,
including $01 if the timeout occurred.  Otherwise, the accumulator will
return with $00 and the .X register will return with the file descriptor
number (not the index into the given array) of the ready file (otherwise,
.X wil be $ff).
NAME   :  aceFileBlock

0==filename to device translation
1==block info: block size, format, sides, tracks, sectors, sector-count codes,
   block numbering, header block
2==read: block no, data
3==write: block no, data buffer


NAME   :  aceDirOpen
PURPOSE:  open a directory for scanning its directory entries
ARGS   :  (zp) = directory pathname
RETURNS:  .A   = file descriptor number
          .CS  = error occurred flag
ALTERS :  .X, .Y, errno

This call opens a directory for reading its entries.  It returns a "file"
descriptor number to you to use for reading successive directory entires
with the "aceDirRead" call.  The pathname that you give to this call must be
a proper directory name like "a:" or "c:2//c64/games/:", ending with a colon
character.  You can have directories from multiple devices open for reading
at one time, but you cannot have the directory of one device open multiple
times.  Also note that you cannot pass wildcards to this call; you will
receive the entire directory listing.
NAME   :  aceDirClose
PURPOSE:  close a directory opened for scanning
ARGS   :  .A   = file descriptor number
RETURNS:  .CS  = error occurred flag
ALTERS :  .A, .X, .Y, errno

Closes a directory that is open for reading.  You can make this call at any
point while scanning a directory; you do not have to finish scanning an
entire directory first.
NAME   :  aceDirRead
PURPOSE:  read the next directory entry from an open directory
ARGS   :  .X   = file descriptor number
RETURNS:  .Z   = end of directory flag
          .CS  = error occurred flag
          aceDirentBuffer = new directory entry data
ALTERS :  .A, .X, .Y, errno

Reads the next directory entry from the specified open directory into the
system interface global variable "aceDirentBuffer" described earlier.  After
opening a directory for reading, the first time you call this routine, you
will receive the name of the disk (or directory).  The "aceDirentNameLen"
and "aceDirentName" fields are the only ones that will contain information;
the rest of the fields should be ignored.

Each subsequent call to this routine will return the next directory entry in
the directory.  All of the "dirent" fields will be valid for these.

Then, after all directory entries have been read through, the last call will
return a directory entry with a null (zero-length) name.  This corresponds
to the "blocks free" line in a Commodore disk directory listing.  The
"aceDirentBytes" field for this last entry will be set to the number of
bytes available for storage on the disk.  On a Commodore disk drive, this
will be the number of blocks free multiplied by 254.  After reading this
last entry, you should close the directory.

At any time, if something bizarre happens to the listing from the disk that
is not considered an error (I don't actually know if this is possible or
not), then the .Z flag will be set, indicating the abrupt ending of the
directory listing.
NAME   :  aceDirIsdir
PURPOSE:  determine whether the given pathname is for a file or a directory
ARGS   :  (zp) = pathname
RETURNS:  .A   = device identifier
          .X   = is-a-disk-device flag
          .Y   = is-a-directory flag
          .CS  = error-occurred flag
ALTERS :  errno

Given a properly formatted directoryname or filename, this routine will
return whether the name is for a file or a directory, whether the device of
the file or directory is a disk or character device, and the system
identifier for the device.  The two flags return $FF for true and $00 for
false.  The device identifier is superfluous for now, but a "devinfo" call
may be added later.  Note that this call does not necessarily indicate
whether the file/directory actually exists or not.
NAME   :  aceDirChange
PURPOSE:  change the current working directory
ARGS   :  (zp) = new directory pathname
          .A   = home flag ($00=given pathname, $80=goto home directory)
RETURNS:  .CS  = error occurred flag
ALTERS :  .A, .X, .Y, errno

Changes the current working directory to the named directory if called with
a "home flag" value of $00.  Too bad the Commodore Kernal doesn't have a
similar call.  Unlike the "cd" shell command, the argument has to be a
properly formatted directory name.  Note that only directories in native
partitions on CMD devices are supported by this command; the 1581's crummy
idea of partitions is not supported.

If the given "home flag" is $80, then this call changes the current working
directory back to the "home" directory that is defined in the ".acerc" file
as the initial directory.
NAME   :  aceDirMake
PURPOSE:  create a new directory
ARGS   :  (zp) = pathname of new directory
RETURNS:  .CS  = error occurred flag
ALTERS :  .A, .X, .Y, errno

Creates a new directory.  I'm not sure, but I think that the current
directory has to be the parent directory of the directory you want to
create.  This may be required by CMD devices, which will be the lowest
common denominator for directory support.  [Note: currently, this call only
accepts a "flat" directory name].
NAME   :  aceDirRemove
PURPOSE:  delete an empty existing directory
ARGS   :  (zp) = pathname of empty directory to remove
RETURNS:  .CS  = error-occurred flag
ALTERS :  .A, .X, .Y, errno

Deletes an existing directory.  The directory must be empty (have no
directory entries) in order for this command to succeed.  Again, I am pretty
sure that you have to be "in" the parent directory of the one to be deleted,
since this is probably required by CMD devices.  [Note: currently, this call
only accepts a "flat" directory name].
NAME   :  aceDirName
PURPOSE:  return specified system directory name/search path
ARGS   :  .A   = dir/path: 0=curDir, 1=homeDir, 2=execSearchPath,
                           3=configSearchPath, 4=tempDir
          (zp) = string buffer
RETURNS:  .CS  = error-occurred flag
ALTERS :  .A, .X, .Y, errno

Returns the null-terminated string for the requested directory or search
path.  An argument of 0 means to return the current directory; 1 means to
return the home directory; 2, the search path that is used to find
executable programs; 3, the search path that is used to find configuration
files (usually of the form ".xxxrc"); and 4, the directory to store
temporary files.

Actually, search paths (arguments 2 and 3) are really a sequence of null-
terminated strings (with each string representing one component of the whole
path) terminated with an empty string.  This call should not cause any disk
I/O to occur, so it can be called without hesitating about the overhead.
The given string-buffer pointer must point to enough storage to hold the
result sting(s).  For the current directory, it should be at least 81
characters in length, for the other directories, 32 characters, and for the
search paths, 64 characters.


This section describes the system calls that are available to application
programmers for full-screen applications.  These calls are intended to be
general enough to handle different screen hardware (the VIC and VDC chips
and the VIC soft-80-column bitmap screen, and possibly others).  These calls
are also designed to be efficient as possible, to discourage progammers from
attempting to bypass using them.  Bypassing these calls would be a bad

The calls are designed around the C-128/PET concept of a window.  There is
only one active window on the display at a time, which may be is large as
the entire screen or as small as a 1x1 character cell.  This window is very
cheap to setup and tear down.  An application can have multiple windows on
the screen by switching the active window around.

In the calls below, all mention "sw" in the arguments and return values
refer to the "syswork" array.  For many calls, there is a "char/color/
high-attribute" argument.  This argument determines which parts of a screen
location will be modified.  There are three components (bytes) to each
screen location: the character code, the color code, and the
special-attributes.  The character code is exactly the same as the PETSCII
code for the character that you want to display (unlike the screen-code
arrangement that Commodore chose).  There are 128 individual characters in
the normal PETSCII positions, and 128 reversed images of the characters in
the most sensible other positions.  The codes are as follows:

-----------   -----------
$00-$1f       reverse lowercase letters
$20-$3f       digits and punctuation
$40-$5f       lowercase letters
$60-$7f       reverse graphics characters
$80-$9f       reverse uppercase letters
$a0-$bf       graphics characters
$c0-$df       uppercase letters
$e0-$ef       reverse digits and punctuation

But note that you can't necessarily count on the reversed characters being
present with extended font sets; exotic other characters may be present in
those positions instead.

There are sixteen color codes, occupying the upper and lower nybbles of the
color byte.  The lower nybble specifies the foreground color of the
corresponding character, and the upper nybble, the background color.  The
VIC and VDC displays don't support background colors per-character, so the
background color nybble is always ignored and the screen color is used
instead.  The color codes are RGBI codes, as follows:

CODE(dec)   (hex)   (bin)   DESCRIPTION
---------   -----   -rgbi   -----------
        0      $0   %0000   black
        1      $1   %0001   dark grey
        2      $2   %0010   blue
        3      $3   %0011   light blue
        4      $4   %0100   green
        5      $5   %0101   light green
        6      $6   %0110   dark cyan on VDC, medium grey on VIC-II
        7      $7   %0111   cyan
        8      $8   %1000   red
        9      $9   %1001   light red
       10      $a   %1010   purple
       11      $b   %1011   light purple on VDC, orange on VIC-II
       12      $c   %1100   brown
       13      $d   %1101   yellow
       14      $e   %1110   light grey
       15      $f   %1111   white

Finally, there are the special-attribute bits.  Not all displays support
attributes, and not all displays that support attributes support all of the
attributes.  For displays that don't support attributes directly, some other
action may be taken instead, like changing the display color, when you use
the "aceWinPut" call.  The attributes have the following meanings (only four
bits are used; the others are ignored but should always be set to zero):

BIT VALUE   (dec)   (hex)   DESCRIPTION
-avub----   -----   -----   -----------
%10000000     128     $80   alternate characterset (italic)
%01000000      64     $40   reverse character
%00100000      32     $20   underline
%00010000      16     $10   blink

These values are additive (or, should I say, "or-ative"); you can use any
combination of them at one time.  Normally, you may wish to leave the high-
attribute bits alone, unless you take the values to give them from the color
palettes (next section).

Most screen operations allow you to select which of character, color, and/or
attributes you wish to modify.  Characters and colors can be selected
independently of each other, but attributes should only be selected when
color is also selected, as colors and attributes generally "ride together",
although on the soft-80 screen, attributes "ride with" the characters.
Also, when you select color but not attributes, then attributes are
interpreted as if you had selected them but with a value of $00 (all
attributes off).  To specify which of you wish to have changed, set bits in
the "char/color/attribute" argument to system calls.  The flags have the
following values.  They are or-ative as well:

BIT VALUE   (dec)   (hex)   DESCRIPTION
-cah-----   -----   -----   -----------
%10000000     128     $80   modify character
%01000000      64     $40   modify color
%00100000      32     $20   modify attribute bits

The screen calls that deal with placing characters on the screen refer to
screen locations using absolute addresses of locations in screen memory.
This scheme is used for increased efficiency.  You can obtain information
about the absolute screen address of the top left-hand corner of the current
window and the number of screen addresses between successive rows, to figure
out screen addresses for your applications.  For added convenience, there is
a call which will accept row and column numbers and return the corresponding
absolute screen address.  Each successive column of a row has an absolute
screen address that is 1 higher than the previous, for all displays.

The screen-control system calls are as follows:

NAME   :  aceWinScreen
PURPOSE:  set the screen size
ARGS   :  .A   = number of text rows required, minimum
          .X   = number of text columns required, minimum
RETURNS:  .A   = number of text rows you get
          .X   = number of text columns you get
          .CS  = error occurred flag (requested size cannot be given)
ALTERS :  .Y, errno

This call selects an appropriate display device, screen, and layout for
displaying text.  You ask for the minimum number of rows and columns you
require on the screen, and the call returns to you what you receive.  If the
system cannot match your minimum requirements, an error will be returned,
and the current screen will be unchanged.  The clock speed of the processor
will be changed to match the screen selected, if appropriate.  If you pass
either number of rows or columns as 0, then the system default value for the
current screen type will be used.  If you pass either parameter having value
255, then the system will use the maximum possible value.
NAME   :  aceWinMax
PURPOSE:  set window to maximum size
ARGS   :  
ALTERS :  .A, .X, .Y

Sets the current window to cover the entire screen.  No errors are possible.
NAME   :  aceWinSet
PURPOSE:  set dimensions of window
ARGS   :  .A   = number of rows in window
          .X   = number of columns in window
          sw+0 = absolute screen row of top left corner of window
          sw+1 = absolute screen column of top left corner of window
RETURNS:  .CS  = error occurred flag
ALTERS :  .A, .X, .Y, errno

Sets the current window to the size you specify.  You will get an error
return if the window will not fit on the screen or of it does not contain at
least one character.  The absolute screen row and column values start from
NAME   :  aceWinSize
PURPOSE:  return dimensions of window
ARGS   :  
RETURNS:  .A   = number of rows in window
          .X   = number of columns in window
          sw+0 = absolute screen row of top left corner of window
          sw+1 = absolute screen column of top left corner of window
         (sw+2)= screen address of top left corner
          sw+4 = screen address increment between successive rows on screen

Returns information about the current window.  The row-increment value is
the number of character positions between successive physical rows on the
screen.  The increment between successive positions on the same line is
always 1.  No errors are possible.
NAME   :  aceWinCls
PURPOSE:  clear window
ARGS   :  .A   = char:$80/color:$40/attribute:$20 modification flags
          .X   = character fill value
          .Y   = color fill value
          sw+6 = attribute fill value
ALTERS :  .A, .X, .Y

This call "clears" the current window by filling it with the character/
color/attributes you specify.  You can use the char/color/attr to limit
what gets cleared.  
NAME   :  aceWinPos
PURPOSE:  return screen address of given row and col
ARGS   :  .A   = row
          .X   = column
RETURNS: (sw+0)= screen memory address of position
ALTERS :  .A, .X, .Y

Given a row and column in the current window, returns the corresponding
absolute screen-memory location for use with other calls.  No errors are
checked for or returned, so garbage in, garbage out.
NAME   :  aceWinPut
PURPOSE:  put characters and color onto screen
ARGS   :  .A   = char:$80/color:$40/attribute:$20 modification flags
          .X   = length of character string
          .Y   = color
         (sw+0)= absolute screen address to start putting data at
         (sw+2)= character string pointer
          sw+4 = fill character
          sw+5 = total field length
          sw+6 = attribute flags
ALTERS :  .A, .X, .Y

Puts text onto the screen.  The output region is given by the absolute
starting screen address and the total field length.  This region must be
contained on one line of the current window, or bad things will happen.
Alternatively, you can put data to the screen in a region that is completely
outside of the current window, provided that it is contained on one physical
line of the display.  A pointer to the characters to be printed is given, as
well as the length of the character array.  Control characters in this
string are ignored; they are poked literally onto the screen, including the
null character.  The length of the character string must be less than or
equal to the total length of the field.  Remaining spaces in the field will
be filled in with the "fill character".

The color of the total field length will be filled in with "color".  You can
use the "char/color/attr" modification flags to specify what is to be
changed.  If you were to, for example, specify that only the characters are
to be put (and not colors nor attributes), then the call would execute
NAME   :  aceWinGet
PURPOSE:  get characters and colors from screen into memory
ARGS   :  .A   = char:$80/color:$40/attribute:$20 modification flags
          .X   = length to get
         (sw+0)= absolute screen address to start getting from
         (sw+2)= character-storage pointer
         (sw+4)= color-storage pointer
         (sw+6)= attribute-storage pointer
ALTERS :  .A, .X, .Y

This call fetches characters, colors, and/or attributes from the screen into
the memory you specify.  Handling colors and attributes independently is a
bit inefficient, but there is no other good way out of this if we want to
support many display types.
NAME   :  aceWinScroll
PURPOSE:  scroll window
ARGS   :  .A   = flags: char:$80/color:$40/attribute:$20 + $08=up + $04=down
          .X   = number of rows to scroll up/down
          sw+4 = fill character
          sw+6 = fill attribute
          .Y   = fill color
ALTERS :  .A, .X, .Y

Scrolls the contents of the current window up or down.  You can scroll any
number of rows at a time.  After scrolling, the bottom (or top) rows will be
filled with the fill character and color (the attribute to fill with will
always be all off).  You can limit whether the characters and/or colors are
to be scrolled by using the "flags" byte in the usual way, except that the
"color" flag also implies that "attribute" (since you would not normally
want to scroll them separately, and it would be a lot of work).  Scrolling
only the characters, for example, will normally be twice as fast as
scrolling both characters and attributes.  Whether to scroll up or down is
specified also using bits in the "flags" field, as indicated in the input
arguments above.  If you specify multiple scroll directions in one call,
your requests will be carried out, but the screen will end up as it was,
with the top and bottom N liness cleared.
NAME   :  aceWinCursor
PURPOSE:  activate/deactivate cursor
ARGS   : (sw+0)= screen address to place cursor
          .A   = enable flag ($ff=cursor-on / $00=cursor-off)
          .Y   = color to show cursor in
ALTERS :  .A, .X, .Y

Displays or undisplays the cursor at the given screen address.  This call
returns immediately in either case.  No errors are returned.  Do not display
anything in or scroll the window while the cursor is being displayed, do not
display the cursor twice, and do not undisplay the cursor twice in a row or
bad things will happen.  Actually, the screen-address argument will be
ignored if you are undisplaying the cursor, so there is no need to provide
it in that case.  When the system starts, the cursor will be in its
undisplayed state (duh!).  You also get to specify the color you want the
cursor to be shown in.
NAME   :  aceWinPalette
PURPOSE:  get standard color palette for current screen
ARGS   :  
RETURNS:  sw+0 = main character color
          sw+1 = cursor color
          sw+2 = status character color
          sw+3 = separator character color
          sw+4 = highlight character color
          sw+5 = alert character color
          sw+6 = screen border color
          sw+7 = screen background color
ALTERS :  .A, .X, .Y

Returns the palette of colors that are recommended to be used in
applications.  These colors are chosen by the user in the system
configuration, so they can be interpreted as being what the user wants and
expects applications to use.  A different selection is made by the user for
each different screen type, and the palette returned will be for the screen
type currently in use.  Eight colors are included in the palette, and you
may interpret their meaning according to the application.  The suggested
usages are given in the return arguments listed above.  I know that a lot of
people out there like to use every color available, but there is a point
where the use of color stops conveying useful information and starts to look
like "angry fruit salad".
NAME   :  aceWinChrset
PURPOSE:  set/get character images/palette codes for the current character set
ARGS   :  .A   = flags: $80=put, $40=get, $20=chr/palette, $10=full/rvs,
                        $08=8-bit, $04=4-bit, $02=main, $01=alternate)
          .X   = character code/palette position to start from
          .Y   = number of chars to modify ($00 means 256)
          (zp) = data pointer
RETURNS:  .A   = flags: what's available, $10,$08,$04,$02,$01
ALTERS :  .X, .Y

Description too complicated for me to get into right now.
Out flags tells what exists, both put&get means ignore full/rvs.
Read the source code for more details.
NAME   :  aceWinOption
PURPOSE:  set/get character window/screen options
ARGS   :  .X   = option number to get/set
                 (1=screen color, 2=border color, 3=cursor style,
                  4=cursor-blink speed, 5=screen rvs, 6=cpu speed,
                  7=alter palette)
          .CS  = set option (.CC=get)
          .A   = value
          .Y   = extra value if needed
RETURNS:  .A   = return value of option
          .CS  = error-occurred flag
ALTERS :  .X, .Y, errno

You can use this call to set/get a number of screen options.  If you call
with the carry flag clear, you will only read the option, and if you call
with the carry flag set, you will both set and read the new option value.
You may not always get the option you wanted to set (because of hardware
limitations).  The .X register selects which option is to be set/gotten.  If
the call returns with the carry flag set, it means either that you have
requested an illegal option/value or that the requested option isn't
available for the current screen (errno).

Option #1 is the screen color.  The active screen color goes into the lower
nybble of the accululator.  Option #2 is the screen border color.  The
active color goes into the bottom of the accumulator, but for the VDC
screen, which has no border, it will be unchanged and always read as being
black.  Option #3 is the cursor style.  The style code goes into the
accumulator: $00=flashing block, $01=solid block, $02=flashing underline,
$03=solid underline.  The display driver will do the best it can with the
screen hardware.  Option #4 is the cursor blink speed, and the flash speed
in jiffies goes into the accumulator (the time to flash on and to flash
off.  The flash-on and flash-off times are always equal, and equal to the
given value).

Option #5 is to reverse the screen.  A value of $00 in the accumulator means
that the screen isn't reveresed, and a value of $ff means that it is.
Option #6 is to set the CPU speed.  Arguably, this option doesn't really
belong with the screen drivers, but it's here anyway.  The number of MHz
goes into the accumulator.  Option #7 is to read/set the color palette.  The
palette position to read/change goes into .Y and the color goes into the
accumulator.  The palette changes will be in effect for the current display
driver for the full run of the system.


The calls in this section refer to the system "console", which includes the
screen and keyboard.  The console-related calls are at a higher level than
the calls in the previous section.

NAME   :  aceConWrite
PURPOSE:  write data to console
ARGS   :  (zp) = data to print
          .AY  = bytes of data to print
          .X   = initial prescroll & exit mode: $00=off, $01+=presc, $ff=ex-sc
RETURNS:  .X   = required scrolling: $00=none
          (zp) = data still to print, if not completed
          .AY  = bytes still to print, if not completed
ALTERS :  .A, .X, .Y

This call is the same as the "write" system call, except this always writes
to the console, and no errors are possible, if you call it with .X==$00.  If
.X equals any other value, then the screen will be scrolled up that many
rows before printing begins and ...
This feature is provided so that console-printing applications can implement
scrollback buffers.
NAME   :  aceConPutlit
PURPOSE:  write literal character to console
ARGS   :  .A   = character
ALTERS :  .A, .X, .Y, errno

This call is the same as "write"ing a single character to the console,
except that the control characters are not interpreted but are displayed
literally instead.
NAME   :  aceConPos
PURPOSE:  set cursor location
ARGS   :  .A   = row
          .X   = column
RETURNS:  .CS  = error encountered flag
ALTERS :  .A, .X, .Y

This call will set the screen location that the next console "read" or
"write" system call will operate from.  If the "cursor" position is outside
the boundaries of the current window on the screen, an error will be
NAME   :  aceConGetpos
PURPOSE:  get current cursor location
ARGS   :  
RETURNS:  .A   = row
          .X   = column

This call returns the current location of the console cursor.
NAME   :  aceConInput
PURPOSE:  inputs a line from the console
ARGS   :  (zp) = input buffer pointer / initial string pointer
          .Y   = number of characters in initial string
RETURNS:  .Y   = number of entered characters
          .CS  = error
ALTERS :  .A, .X

No description.
NAME   :  aceConStopkey
PURPOSE:  check if stop key is being held down
ARGS   :  
RETURNS:  .CS  = stop key pressed
ALTERS :  .A, .X, .Y, errno

Indicates whether the STOP (RUN/STOP) key is currently being held down by
the user.  If so, carry flag is set on return (and clear if not).  If the
stop key is discovered to be pressed by this call, then the keyboard buffer
will also be cleared.
NAME   :  aceConGetkey
PURPOSE:  get a key code from the keyboard buffer
ARGS   :  
RETURNS:  .A   = keyboard character
          .X   = shift pattern

Waits for the user to type a key (or takes a previous keystroke from the
keyboard buffer).  Regular characters are returned in their regular PETSCII
codes, but there are many special control keystrokes.  I still haven't
figured out what all of the special codes should be, but all 256 possible
character values will be covered.  Special codes like "page up", etc. should
help in standardizing control keystrokes for applications.  Note that these
definitions of keycodes is only suggested; your full-screen application can
interpret them however it wants.  The key code is returned in the
accumulator.  No errors are possible.

The tables below summarize the meanings of the various key codes.  Not all
of the C64 keys have been decided yet.  Note that the keys for "@" to "_",
used in association with shifting keys, are "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_".
("\" means the Pound key, "^" means the Uparrow key, and "_" means the
Backarrow key). "CT" means Control, "SH" means Shift, "AL" means Alternate,
and "CO" means Commodore.  On the C64, Alternate is obtained by holding down
the Commodore and Control keys simultaneously, and "CS" below means to hold
down the Commodore and Shift keys simultaneously.  The "CS" combination is
used to make the functions of keys only provided on the C128's extended
keyboard available on the C64.

CODE(s)   C128 KEY(s)    C64 KEY(s)     DESCRIPTION
-------   ------------   ------------   -----------
$20-$3f   SPACE to "?"   SPACE to "?"   Regular numbers and punctuation
$40-$5f   "@" to "_"     "@" to "_"     Regular lowercase letters
$60-$7f   AL-@ to AL-_       Alternate keys
$a0-$bf   CO-@ to CO-_   CO-@ to CO-_   Commodore keys
$c0       SH-*           SH-*           Back-quote (`)
$c1-$da   SH-A to SH-Z   SH-A to SH-Z   Regular uppercase letters
$db       SH-+           SH-+           Left curly brace ({)
$dc       SH-\ (Pound)   SH-\ (Pound)   Vertical bar (|)
$dd       SH-- (Minus)   SH-- (Minus)   Right curly brace (})
$de       SH-Uparrow     SH-Uparrow     Tilda (~)
$df       SH-Backarrow   SH-Backarrow   House character (DEL on most systems)
$e0-$ef   CT-@ to CT-_   CT-@ to CT-_   Control keys

CODE(s)   C128 KEY(s)    C64 KEY(s)     DESCRIPTION
-------   ------------   ------------   -----------
$01       CT-RETURN      CT-RETURN      End of file
$02       SH-TAB         CS-R           Backtab
$03       STOP           STOP           Stop some operations
$04       HELP           CS-H           Context-sensitive help
$05       CT-2           CT-2           White
$06       SH-LEFT        CS-B           Word left
$07       SH-LINEFEED    CS-P           Menu exit
$08       CO-DEL         CO-DEL         Rubout character under cursor
$09       TAB            CS-T           Tab
$0a       LINEFEED       CS-L           Menu
$0b       SH-RIGHT       CS-N           Word right
$0c       CO-UP          CS-W           Goto top of document
$0d       RETURN         RETURN         Return
$0e       SH-ESCAPE      CS-D           Window control
$0f       CO-DOWN        CS-Z           Goto bottom of document
$10       CO-LEFT        CS-A           Goto beginning of line
$11       DOWN           DOWN           Cursor down
$12       CT-9           CT-9           Rvs
$13       HOME           HOME           Home
$14       DEL            DEL            Backspace
$15       CO-RIGHT       CS-S           Goto end of line
$16       CT-UP          CS-I           Page up
$17       CT-DOWN        CS-M           Page down
$18       CT-TAB         CS-Y           Tab set
$19       CT-LEFT        CS-J           Page left
$1a       CT-RIGHT       CS-K           Page right
$1b       ESCAPE         CS-E           Escape
$1c       CT-3           CT-3           Red
$1d       RIGHT          RIGHT          Cursor right
$1e       CT-6           CT-6           Green
$1f       CT-7           CT-7           Blue

CODE(s)   C128 KEY(s)    C64 KEY(s)     DESCRIPTION
-------   ------------   ------------   -----------
$80       CT-F1          CT-F1          Function key 9
$81       CO-1           CO-1           Orange/Purple(?)
$82       CT-F3          CT-F3          Function key 10
$83       SH-STOP        SH-STOP        Run
$84       SH-HELP        CS-G           Context-insensitive help
$85       F1             F1             Function key 1
$86       F3             F3             Function key 3
$87       F5             F5             Function key 5
$88       F7             F7             Function key 7
$89       SH-F1          SH-F1          Function key 2
$8a       SH-F3          SH-F3          Function key 4
$8b       SH-F5          SH-F5          Function key 6
$8c       SH-F7          SH-F7          Function key 8
$8d       SH-RETURN      SH-RETURN      
$8e       CT-F5          CT-F5          Function key 11
$8f       CT-F7          CT-F7          Function key 12
$90       CT-1           CT-1           Black
$91       UP             UP             Cursor up
$92       CT-0           CT-0           Rvs off
$93       SH-HOME        SH-HOME        Clear screen
$94       SH-DELETE      SH-DELETE      Insert one space
$95       CO-2           CO-2           Brown
$96       CO-3           CO-3           Light red
$97       CO-4           CO-4           Dark gray
$98       CO-5           CO-5           Medium gray/dark cyan(?)
$99       CO-6           CO-6           Light green
$9a       CO-7           CO-7           Light blue
$9b       CO-8           CO-8           Light gray
$9c       CT-5           CT-5           Magenta
$9d       LEFT           LEFT           Cursor left
$9e       CT-8           CT-8           Yellow
$9f       CT-4           CT-4           Cyan

The shift pattern is recorded when keys are put into the keyboard buffer,
and returned in the .X register by this call.

BIT VALUE   (dec)   (hex)   DESCRIPTION
---------   -----   -----   -----------
%00100000      32     $20   Extended key (C128 / Commodore+Shift on C64)
%00010000      16     $10   Caps Lock
%00001000       8     $08   Alternate
%00000100       4     $04   Control
%00000010       2     $02   Commodore
%00000001       1     $01   Shift
NAME   :  aceConKeyAvail
PURPOSE:  check if any key is available in the keyboard buffer
ARGS   :  
RETURNS:  .A   = peeked keyboard character
          .X   = peeked shift pattern
          .Y   = keyboard type ($00=basic, $80=extended)
          .CC  = key is available (.CS if not)

Returns whether a key is available in the keyboard buffer or not in the
carry flag.  If there is a key, then the key and the shift pattern will be
returned in the .A and .X registers, but not removed from the buffer so that
they will be returned on the next "getkey" call.

The .Y register returns a code indicating what type of keyboard the machine
has.  A value of $00 means that only a "basic" keyboard is available, and a
value of $80 means that an "extended" keyboard is in use.  The C64 has a
"basic" keyboard and the C128 has an "extended" keyboard.  However, do not
use this routine to tell whether you are running on a C64 or a C128, since
alternate keyboards may be supported in the future.
NAME   :  aceConKeyMat
PURPOSE:  set up a new keyboard matrix
ARGS   :  (zp) = new keymatrix pointer
ALTERS :  .A, .X, .Y

The key-matrix format is described in the technical manual.
NAME   :  aceConMouse
PURPOSE:  read the buttons and position of the mouse
ARGS   :  
RETURNS:  .A   = mouse-button status: $80=left, $40=right, $20=middle
          .Y   = mouse-present flag: $80=yes
         (sw+0)= mouse X position
         (sw+2)= mouse Y position
ALTERS :  .A, .X, .Y

Returns the current mouse status.  Reading the buttons isn't currently
implemented, as isn't the mouse-present flag.
NAME   :  aceConJoystick
PURPOSE:  read the inputs of the two joysticks
ARGS   :  
RETURNS:  .A   = button mask for joy1: $10=fire,$8=right,$4=left,$2=down,$1=up
          .X   = button mask for joy2: $10=fire,$8=right,$4=left,$2=down,$1=up
          .Y   = joysticks present: $80=joy1, $40=joy2

Not yet implemented.  But note that the kernel will remap the meanings of
"joy1" and "joy2" according to the system configuration.  You will normally
want to use "joy1" if you are running a single-joystick application.
NAME   :  aceConOption

1=console-put mask,
2=character color, 3=character attributes, 4=fill color, 5=fill attribute,
6=cursor color, 7=force cursor wrap, 8=shift-keys for scrolling,
9=mouse scaling, 10=key-repeat delay, 11=key-repeat rate


NAME   :  aceGrScreen
PURPOSE:  activates the graphics screen
ARGS   :  .A   = graphics type ($01=regular monochrome, $02=highest-res mono)
          .X   = border color
          .Y   = background(high nybble) + foreground(low nybble) colors
RETURNS:  .A   = number of pixel columns, divided by eight
         (sw+0)= number of pixel rows
          .X   = X to Y aspect ratio (1 or 2)
          .CS  = error

This call activates the graphics screen.  On the C64, the graphics screen is
always the 320x200 hires bitmap screen, the same on that is used for the
Soft-80 display.  The Soft-80 display must be allocated in the system
configuration or the call will fail.  The aspect ratio for this display will
be returned as 1:1 (.X=1).  On the C128, this call will activate the hires
mode of the VDC display.  If your VDC has only 16K of RAM or if it is not an
NTSC machine, then a 640x200 display will be activated and it will have a
2:1 (.X=2) aspect ratio, since it takes two X-axis pixels to cover the same
display area as one Y-axis pixel.  If your NTSC VDC has 64K of RAM and if
the graphics type selected is for the highest-resolution display, then a
640x491 hires interlaced display will be activated, with a 1:1 aspect

Only a monochrome display is available in all modes, and the colors of the
foreground ("1"-valued) and background ("0"-valued) pixels will be set the
foreground and background colors given in the .Y register.
NAME   :  aceGrExit
PURPOSE:  scroll window
ARGS   :  
ALTERS :  .A, .X, .Y

This call deactivates the graphics screen and returns you to the the last-
used text display mode.  Your application must make this call before it
NAME   :  aceGrFill
PURPOSE:  scroll window
ARGS   :  .A   = fill bit value
ALTERS :  .A, .X, .Y

This call fills the entire graphics screen with the given bit pattern.  All
of the "1" bits will activate foreground colors and the "0" bits,
background.  The eight bits will be displayed side-by-side on each pixel row
of the display, and all pixel rows will display the same thing.  To "clear"
the graphics screen, select a fill bit pattern of $00.
NAME   :  aceGrOp
PURPOSE:  perform graphics operation on region of bitmapped screen
ARGS   :  .A   = operation flags:
                $80=get, $40=put, $20=copy, $10=fill,$8=mask,$4=and,$2=xor,$1=or
          .X   = X position, divided by eight, to start operation
         (sw+0)= Y position to start operation
          .Y   = number of columns to operate on, divided by eight
         (sw+2)= number of rows to operate on
         (sw+4)= source pointer / value
         (sw+6)= destination pointer
         (sw+8)= mask pointer
          sw+10= number of interlace columns divided by eight
ALTERS :  .A, .X, .Y, "sw" array

This call performs the specified graphics operation(s) on the specified area
of the bitmap screen.  The possible operations are to get image data from
the bitmap screen and put it into memory, to put image data from memory onto
the screen, to copy image data from one area of the bitmap screen to another
area (not implemented yet), and to fill an area of the bitmap screen with a
given byte value.  The put operation can have its data modified by a given
bitmap mask and can operate in AND, XOR, or OR modes.

Now, part of the reason for folding all of these operations into one system
call is that you can perform more than one simultaneously.  For example, you
can get and put at the same time.  The data will be fetched first, of
course, before the new data is put onto the screen.  You can get and fill
simultaneously, too.

The put operation uses (sw+4) as a source pointer to the bitmap that you
have stored in memory.  Each byte of the display data will be displayed on
the screen in eight side-by-side pixel positions, and bytes in the display
data go from left to right, top to bottom.  Or, if you specify the fill
operation, then the value in location sw+4 will be used as the fill byte.

The put operation can also use a mask bitmap, pointed to (sw+8).  For each
"1" value in the mask bitmap, the corresponding pixel of the existing display
bitmap background will be erased; otherwise, the corresponding background
pixel will be retained.

The get operation puts its data starting at the (sw+6) destination pointer.

The interlace value is used for all bitmaps stored in memory and on the
screen.  This tells how many columns larger the bitmaps in memory are than
the area on the screen.  Using these, you can display only a "window" of the
bitmap that you have in memory.  Normally, this argument should be zero.


This section describes calls that are used to control the execution of
processes (active programs).  From within one program, you can call for the
execution of another program, have it execute, and then return to the
calling program.  Since only one program is allowed in memory at a time,
some special problems arise.

NAME   :  aceProcExec
PURPOSE:  execute external program as a child process
ARGS   :  (zp) = program name of executable
          (zw) = start address of argument vector
          .AY  = number of arguments
          .X   = reload from file or volatile storage (pages+, $00=file)
          [mp] = pointer to far memory volatile storage
         [sw+0]= "std" file redirections
RETURNS:  .A   = exit code
          .X   = number of bytes in "aceExitData" used
          (zp) = given argument count
          (zw) = given argument vector pointer
          [mp] = pointer to far memory volatile storage
          .CS  = error occurred flag
ALTERS :  .Y, errno

Calling this routine will cause a new "frame" to be set up on the "system
stack" (lowering the available application area memory a little), the
specified program to be loaded into memory over top of the current one, the
new program to be executed, the old program to be reloaded from whatever
disk unit it came from originally upon exit of the new program, and control
to be returned to the old process with the return values from the executed
program.  This is a complicated procedure and many things can go wrong.

The first thing that a process that wants to call another program must do is
set up the arguments to be passed in.  All arguments must be null-terminated
strings.  These arguments are to be put into high memory, starting from one
less than the location pointed to by "aceMemTop" and working downward.  It
does not matter in which order the strings are placed, as long as they are
all grouped together.  Then, immediately below the strings comes the vector
of two-byte RAM0 pointers that point to the strings.  This array must be in
order, with the lowest entry pointing to the first (zero subscript) string,
etc., the second highest entry pointing to the last string, and the highest
entry containing the value $0000.  An asciigram follows:

|           |
|           | <--(aceMemTop)
|           |
| string    |
|           |         : collection of null-terminated strings
|  contents |
|           |
|           |
|   $0000   |         : argv[N] : null argument pointer
| strptrN-1 |         : argv[N-1]
| strptrN-2 |         : argv[N-2]
.           .
.           .
| strptr 1  |         : argv[1] : first actual argument
| strptr 0  | <--(zw) : argv[0] : filename of program to be executed
|           |

The first entry should indicate the filename or command name of the program
being executed, and the subsequent arguments are the actual input arguments
to the program being called.  The address of the first argument vector table
entry is loaded into (zw), and the number of arguments is loaded into .AY.
Note that this value also includes the command name, so if, for example, you
were to call program "wc" to count two filenames "hello" and "goodbye", then
you would pass an argument count of 3.  The name pointed to by "argv[0]"
does not actually have to be the literal command name, but the one pointed
to by (zp) does.  If a relative executable name is given in (zp), then the
search path will be used to locate the executable.  Oh, don't screw up the
organization of the arguments or bad things will happen; there is no
structure checking.

After setting up the arguments, you'll want to set up any redirections of
stdin, stdout, or stderr you'll be needing.  Because there is only one open
file table in the whole uni-tasking system, you'll have to manipulate
existing entries using the "aceFileFdswap" system call described earlier.
The open file table is inherited by the child process.  Note that if it
closes any of the open files it inherited, then they are also closed to your
use also.  If the child accidentally leaves open any files it opened, they
will be closed by the system before you are reactivated.

Finally, before the call is made, you have to save any volatile local
information into "far" memory.  All application zeropage and application
area memory will be modified by the called program, so you must save
whatever you will need to continue after the return to be able to continue.
As mentioned earlier, all of the "far" memory that a parent program owns
will be safe, so you can save your volatile information there, in any format
you wish.  All you have to do is save the pointer to the far memory into the
[mp] pointer.  Upon return of the child process, the value you put into [mp]
will be restored, and you can then restore your volatile information out of
far storage.  If you wish to save no volatile information, then you can just
leave garbage in the [mp] value, since it will not be interpreted by the

Alright, so now you call the "aceProcExec" primitive, the child program is
loaded, executed, and it returns.

At this time, the parent program (that's you) is reloaded from wherever it
was loaded originally and you are returned to the instruction immediately
following the "jsr aceProcExec", with your processor stack intact but the
rest of your volatile storage invalid.  Even if there is an error return
(carry flag set), your volatile storage will still need to be restored,
since the application area may have been overwritten before the error was
discovered.  In the case of an error return, the child process will not have
been executed.  If the system is unable to reload the parent program (you),
then an error return is given to your parent, and so on, as far back as
necessary.  (This is a minor exception to the rule that an error return
indicates that a child didn't execute; in this case, the child didn't

You are also returned an "exit code", which will have application-specific
meaning, although standard programs (e.g., shell script) interpret the value
as: 0==normal exit, anything else==error exit.  The X register is also set
to indicate the amount of "aceExitData" that is used, to allow for more
complicated return values.
NAME   :  aceProcExecSub
PURPOSE:  execute internal subroutine as a separate process
ARGS   :  (zp) = address of subroutine
          (zw) = address of argument vector
          .AY  = argument count
          [mp] = far-memory pointer
         [sw+0]= "std" file redirections
RETURNS:  .A   = exit code
          .X   = number of bytes in "aceExitData" used
          (zp) = given argument count
          (zw) = given argument vector pointer
          [mp] = given far-memory pointer
          .CS  = error occurred flag
ALTERS :  .Y, errno

This call is very similar to "exec", except that it calls an internal
subroutine rather than an external program.  Thus, you don't have to save or
restore your volatile storage, or worry about loading the child or reloading
the parent.  You do, however, set up the arguments and file redirections as
you would for a full "aceProcExec".
NAME   :  aceProcExit
PURPOSE:  exit current program, return to parent
ARGS   :  .A   = exit code
          .X   = number of bytes in "aceExitData" used

This call causes the current program to exit back to its parent. A program
that exits simply by returning to its environment will give back an exit
code of 0, which should be interpreted as a normal return.  If you wish to
indicate a special return, you should use some exit code other than zero.
Many utilities will interpret non-zero error codes as actual errors and may
abort further operations because of this.

You may set up a return data in "aceExitData", up to 255 bytes worth, and
load the number of bytes used into .X if you wish.  It is recommended that
the first field of this data be a special identifier code so programs that
cannot interpret your data will not try.  You cannot give any far pointers
in your return data, since all far memory allocated to you will be freed by
the system before returning to your parent.


The calls given in this section are to be used for accessing "far" memory in
ACE, which includes all REU, RAMLink, RAM1 and above, and sections of RAM0
that are not in the application program area.  Applications are not allowed
to access "far" memory directly, because the practice of bypassing the
operating system would undoubtedly lead to serious compatibility problems
(can you say "MS-DOS"?).

All of these calls use a 32-bit pointer that is stored in the zero-page
argument field "mp" (memory pointer).  This field is to be interpreted as
consisting of low and high words.  The low word, which of course comes
first, is the offset into the memory "bank" that is contained in the high
word.  Users may assume that offsets within a bank are continuous, so
operations like addition may be performed without fear on offsets, to access
subfields of a structure, for example.  You may not, however, make any
interpretation of the bank word.  An application should only access far
memory that it has allocated for itself via the "aceMemAlloc" call.

NAME   :  aceMemZpload
PURPOSE:  load zeropage storage from far memory
ARGS   :  [mp] = source far memory pointer
          .X   = destination zero-page address
          .Y   = transfer length
RETURNS:  .CS  = error occurred flag
ALTERS :  .A, .X, .Y, errno

Load zero-page locations with the contents of far memory.  "mp", of course,
gives the address of the first byte of far memory to be retrieved.  The X
register is loaded with the first address of the storage space for the data
on zero page.  It must be in the application zero-page space.  The Y
register holds the number of bytes to be transferred, which, considering
that transfers must be to the application zero-page storage, must be 126
bytes or less.  This routine will return a "reference through null pointer"
if [mp] contains a null pointer.
NAME   :  aceMemZpstore
PURPOSE:  store zeropage data to far memory
ARGS   :  .X   = source zero-page address
          [mp] = destination far memory pointer
          .Y   = transfer length
RETURNS:  .CS  = error occurred flag
ALTERS :  .A, .X, .Y, errno

This routine is the complement of "zpload"; this transfers data from zero
page to far memory.  The arguments and restrictions are the same as
NAME   :  aceMemFetch
PURPOSE:  load near RAM0 storage from far memory
ARGS   :  [mp] = source far memory pointer
          (zp) = destination RAM0 pointer
          .AY  = transfer length
RETURNS:  .CS  = error occurred flag
ALTERS :  .A, .X, .Y, errno

This routine will fetch up to 64K of data from far memory into RAM0 memory
where it can be accessed directly by the processor.  The arguments should
mostly speak for themselves.  You should not fetch into RAM0 memory that is
not specifically allocated to the application.  You will get an error if you
try to use a null far pointer.
NAME   :  aceMemStash
PURPOSE:  store near RAM0 data to far memory
ARGS   :  (zp) = source RAM0 pointer
          [mp] = destination far memory pointer
          .AY  = transfer length
RETURNS:  .CS  = error occurred flag
ALTERS :  .A, .X, .Y, errno

This is the complement of "fetch" and operates analogously, except that it
transfers data from RAM0 to far memory.
NAME   :  aceMemAlloc
PURPOSE:  allocate pages of far memory to current process
ARGS   :  .A   = requested number of pages to be allocated
          .X   = starting "type" of memory to search
          .Y   = ending "type" of memory to search, inclusive
RETURNS:  [mp] = far memory pointer to start of allocated memory
          .CS  = error occurred flag
ALTERS :  .A, .X, .Y, errno

This routine allocates a given number of contiguous far-memory pages for use
by the application, and returns a pointer to the first byte of the first
page.  On calling, the accumulator contains the number of pages to allocate
(a page is 256 contiguous bytes aligned on a 256-byte address (i.e., the low
byte of a page address is all zeros)).

The X and Y registers contain the start and end "types" of far memory to
search for the required allocation.  The possible types are mentioned in the
System Constants section.  The numeric values for the "aceMem" constants are
arranged in order of accessing speed.  So, if your application has speed
requirements that dictate, for example, that RAMLink memory should not be
used, then you would call "aceMemAlloc" with a search range of .X=0 to
.Y=aceMemInternal.  If you wanted to say you are willing to accept any
memory the system can give to you, you would specify .X=0 to .Y=255.  The
values of 0 and 255 will be converted to the fastest and slowest memory
available.  ACE will give you the fastest type of memory, from what you
specify as acceptable, that it can.

This routine will then search its available free memory for a chunk fitting
your specifications.  If it cannot find one, the routine will return a
"insufficient memory" error and a null pointer.  Note that this error may
occur if there is actually the correct amount of memory free but just not in
a big enough contiguous chunk.  If successful, this routine will return in
"mp" a pointer to the first byte of the first page of the allocated memory.

If you call a subprogram with the "aceProcExec" call while the current
program is holding far memory, that far memory will be kept allocated to
your program and will be safe while the child program is executing.  If you
don't deallocate the memory with "aceMemFree" before exiting back to your
parent program, then the system will automatically deallocate all memory
allocated to you.  So, have no fear about calling "exit" if you are in the
middle of complicated far memory manipulation when a fatal error condition
is discovered and you don't feel like figuring out what memory your program
owns and deallocating it.

Some applications will want to have the most amount of memory to work with,
and if there is free space in the application program area that the program
is not using directly, then you may want to use that as "far" memory.  To do
this, you will need to write your own stub routines that manage page
allocation and deallocation requests to the near memory, and calls the
"aceMemAlloc" and "aceMemFree" routines to manage the far memory.  The
"sort" program distributed with ACE does this.  Please note that you CANNOT
simply free the unused memory of the application program area and expect the
system to manage it.  Bad stuff would happen.

Some applications will want to have a byte-oriented memory allocation
service rather than a page-oriented service.  You can build a byte-oriented
service on top of the page-oriented service in your application programs
that manage memory for the application and ask the system for pages whenever
more memory is required by the application.  Note that this still means that
allocated memory will be freed automatically when an application exits.  The
"sort" program implements this byte-oriented service, so you can check its
source code to see how this is done (or to simply cut and paste the code
into your own program).
NAME   :  aceMemFree
PURPOSE:  free pages of far memory allocated to current process
ARGS   :  [mp] = far memory pointer to start of memory to be freed
          .A   = number of pages to be freed
RETURNS:  .CS  = error occurred flag
ALTERS :  [mp], .A, .X, .Y, errno

This deallocates memory that was allocated to a process by using the
"aceMemAlloc" system call.  You will get an error return if you try to
deallocate memory that you don't own.
NAME   :  aceMemStat
PURPOSE:  get "far" memory status plus process id
ARGS   :  .X   = zero-page address to store status information
RETURNS:  .A   = current process id
         [.X+0]= amount of "far" memory free
         [.X+4]= total amount of "far" memory
ALTERS :  .X, .Y

This call returns the current process id, the number of bytes of far memory
currently free, and the total amount of far memory.


NAME   :  aceTimeGetDate
PURPOSE:  get the current date and time
ARGS   : (.AY) = address of buffer to put BCD-format date into
ALTERS :  .A, .X, .Y

Returns the current date and time in the BCD format described in the
paragraph on "aceDirentDate".  It puts it into the at-least-eight-byte
storage area pointed to by (.AY).
NAME   :  aceTimeSetDate
PURPOSE:  set the current date and time
ARGS   : (.AY) = address of date in BCD format
ALTERS :  .A, .X, .Y

Sets the current date and time in the system.  (.AY) points to the BCD date
string whose format is discussed in the paragraph on "aceDirentDate".  No
validity checking is performed on the date given.
NAME   :  aceTimeJif
PURPOSE:  get the number of software "jiffies" since the system started
ARGS   :  .X   = zero-page address to put jiffy count
RETURNS: [0+.X]= 32-bit jiffy count
ALTERS :  .A, .X, .Y

Returns the number of software jiffies since the system started.  Can be
used to measure computation-intensive operations.  Note that I/O may
interfere with the jiffy count's accuracy.


NAME   :  aceMiscUtoa
PURPOSE:  convert unsigned 32-bit number to a decimal PETSCII string
ARGS   :  .A   = minimum length for return string
          .X   = zero-page address of 32-bit number
          (zp) = pointer to string buffer to store string
RETURNS:  .Y   = length of string
ALTERS :  .A, .X

This is a utility call in the kernel.  It is really not necessary for it to
be in the kernel, but so many programs make use of it that it makes sense
for it to be factored out.  You give a pointer to a 32-bit unsigned value in
zero page memory, a pointer to a buffer to store that string that is at
least as long as necessary to store the value plus the null-character
terminator that will be put on the end of the string, and a minimum length
value for the string.  If the number requires fewer digits than the minimum
length, the string will be padded with spaces on the left.  Since a 32-bit
quantity can only contain an maximum of ten decimal digits, the string
buffer will only need to be a maximum of eleven bytes in size.
NAME   :  aceMiscIoPeek
PURPOSE:  do a peek into the I/O space ($D000-$DFFF)
ARGS   :  (zw) = I/O-space address
          .Y   = offset from (zw)
RETURNS:  .A   = peeked value
          .CS  = error if operation not supported

Does a peek into the system I/O-address space.  This is a pretty ugly call,
but you should use this rather than peeking into the space directly because
application programs aren't supposed to directly peek into there at all.
NAME   :  aceMiscIoPoke
PURPOSE:  do a peek into the I/O space ($D000-$DFFF)
ARGS   :  (zw) = I/O-space address
          .Y   = offset from (zw)
          .A   = value to poke
RETURNS:  .CS  = error if operation not supported

Does a poke into the system I/O-address space.  This is a pretty ugly call,
but you should use this rather than poking into the space directly because
application programs aren't supposed to directly peek into there at all.


#$41. Send Commodore-DOS command to Commodore-DOS disk device.

NAME   :  aceMiscCmdOpen
PURPOSE:  open command channel to Commodore disk drives
ARGS   :  (zp) = device name
RETURNS:  .A   = file descriptor number
          .CS  = error occurred flag
ALTERS :  .X, .Y, errno

This "cmd" set of system calls really should not be present, but they will
be needed until the full complement of disk-utility system calls are
implemented.  It is really not recommended that any application program rely
on these calls being around very long.  This call opens the command channel
on the named device (standard ACE device name string) and returns the file
descriptor number to use thereafter.  This call is considered to be "ugly"
and I am going to make every effort to remove it.

NAME   :  aceMiscCmdClose
PURPOSE:  close command channel to Commodore disk drives
ARGS   :  .A   = file descriptor number
RETURNS:  .CS  = error occurred flag
ALTERS :  .A, .X, .Y, errno

This closes an opened command channel to a disk drive.  Closing the status
will NOT affect any other open files on the disk unit at the time.  This
call is considered to be "ugly" and I am going to make every effort to
remove it.

NAME   :  aceMiscCmdSend
PURPOSE:  send command over command channel to Commodore disk drives
ARGS   :  .X   = file descriptor number
         (.AY) = pointer to null-terminated command string
RETURNS:  .CS  = error occurred flag
ALTERS :  .A, .X, .Y, errno

This sends a command string to a disk drive.  Since a null-terminated string
representation is used, not all Commodore/CMD-DOS commands can be sent, but
the important ones can be.  This call is considered to be "ugly" and I am
going to make every effort to remove it.

NAME   :  aceMiscCmdStatus
PURPOSE:  receive current status from command channel of Commodore disk drives
ARGS   :  .X   = file descriptor number
         (.AY) = pointer to buffer for null-terminated status string
RETURNS:  .A   = status code in binary
          .CS  = error occurred
ALTERS :  .X, .Y, errno

This returns the status of a disk drive in a string as well as the binary
disk status number in the accumulator.  The given status buffer must be at
least 50 or so characters long (whatever is the longest possible disk status
string).  This call is considered to be "ugly" and I am going to make every
effort to remove it.

NAME   :  aceModemCheck
PURPOSE:  check the buffer status of the modem device driver
ARGS   :  
RETURNS:  .AY  = number of buffered receive bytes
          .X   = number of buffered transmit bytes
          .ZC  = number of buffered receive bytes is not zero

This call allows you to poll the modem device to determine whether there are
any characters that can by received by using the "read" call.  This call is
provided because the "read" call is blocking and will not return until at
least one character is available.  Note that more characters than are
returned by this call may be available to be read because more characters
can be received at any time that a file to the modem device is opened.  This
function will disappear and be sucked into the "aceFileIoctl" call in the

NAME   :  aceModemParms
PURPOSE:  set / check the communication parameters for the modem
ARGS   :  .A   = baudrate and format to set
          .X   = flag to set ($ff) or only check ($00) the modem parameters
RETURNS:  .A   = baudrate and format that is now set
ALTERS :  .X, .Y

This call sets and returns the modem communication parameters, or, if the .X
register is passed with a value of $00, this call takes no action and
returns the existing communication parameters.  The high bit of the
parameters byte selects the format: "0" == 8N1 (8 data bits, No parity, 1
stop bit) and "1" == 7E1 (7 data bits, Even parity, 1 stop bit).  No other
formats are available since all other formats are considered to be useless.
The lower nybble selects the current baud rate according to the following

bits   hex   dec      baud
----   ---   ---    ------
0000     0     0        50
0001     1     1       110
0010     2     2       134.5
0011     3     3       300
0100     4     4       600
0101     5     5      1200
0110     6     6      2400
0111     7     7      4800
1000     8     8      9600
1001     9     9     19.2k
1010     a    10     38.4k
1011     b    11     57.6k
1100     c    12    115.2k   (available on Fast C128 and SuperCPU)
1101     d    13    230.4k   (available on SuperCPU only)
1110     e    14    460.8k   (not implemented, but theoretically attainable)
1111     f    15    921.6k   (    by the SuperCPU)

This function will disappear and be sucked into the "aceFileIoctl" call in
the future.

NAME   :  aceModemStat
PURPOSE:  check the operating statistics of the modem device driver
ARGS   :  .X   = zero-page statistics buffer
RETURNS:  .A   = status register of modem device (format of a 6551 ACIA)
        [.X+0] = number of characters that overran the hardware
        [.X+4] = number of characters overflowed from the receive buffer
        [.X+8] = number of characters that have been received from the driver
        [.X+12]= number of characters that have been transmitted by the driver
ALTERS :  .X, .Y

This call returns the statistics for the modem device driver.  The number of
overrun characters is an "at least" value, since more than one character
being overrun cannot be detected.  The number of overflow characters is the
total number of characters that have been dropped becuase the receive buffer
was full at the time that the characters were received.  These two
statistics should normally be zero, unless you're in a bad situation.  These
statistics are kept for the entire ACE session, and all wrap around after 32
bits (at a constant data rate of 38.4 kbps, a wrap-around would happen after
12.9 days).  This function will disappear and be sucked into the
"aceFileIoctl" call in the future.
Future: -format, -validate, -initialize, -block operations?


The ACE system itself is written using the Buddy-128 assembler, so it is
recommended that applications be written in this also, although applications
are slowly being migrated to the ACE assembler.  User programs for ACE have
a very simple structure.  Here is the standard "hello, world" example
program written for the Buddy-128 assembler:

.seq acehead.s
.org aceAppAddress
.obj "@0:hello"

jmp main
.byte aceID1,aceID2,aceID3
.byte 64,0

main = *
   lda #helloMsg
   sta zp+0
   sty zp+1
   lda #helloMsgEnd-helloMsg
   ldx #stdout
   jsr write

helloMsg = *
   .asc "Hello, cruel world."
   .byte chrCR
helloMsgEnd = *

This would normally be put into a file called "hello.s".  The ".s" extension
means that this is an assembler file (a la Unix).  The first thing this
program does is include the "acehead.s" file.  This is the Buddy-assembler
file that contains the header information declarations required to access the
ACE system interface.  The next line gives the start address to start
assembling to; it must be "aceAppAddress", which is the address that ACE will
load the program at.  The next line is a directive to the assembler to write
the executable code to a Commodore-DOS "PRG" file named "hello".  This will be
the command to enter at the ACE shell prompt.

The next eight bytes of object code (which are the first eight bytes of a
loaded program) describe the header required by ACE programs.  The first three
bytes must be a JMP to the main routine of the program.  The next three bytes
must have the values "aceID1", "aceID2", and "aceID3", respectively.  The next
two bytes are the minimum stack requirements and flags, respectively.  The
stack requirement is for the processor stack, and ACE will make sure your
program has at least this much space before your program starts.  The flags
field is currently undefined, but you must give it a value of 0.  And that's
all there is to it.  The rest of the program can be organized however you want
it to be.

In this example, we set up the arguments for the "write" system call to print
the string "Hello, cruel world." plus a carriage return to standard output.
Note that this string does not need a terminating null ($00) character since
the write call takes a buffer length.  The program then returns to its calling
environment via an RTS.  This will cause an implied "exit(0)" to be performed
by the system, returning to the parent program.

Although this program does not take advantage of this, an application program
may use zero-page locations $0002 through $007f for storage without fear of
having the storage trodden upon by the system.

Finally, an application program starts at location "aceAppAddress" (plus six)
and is allowed to use memory all the way up to one byte less than the address
pointed to by "aceMemTop" for its own purposes.  Currently, this amount of
space is on the order of magnitude of about 24K.  This will be increased in
the future.

Application programs are not to access I/O features or even change the current
memory configuration during execution.  All I/O and other unusual contortions
must be performed by ACE system calls; otherwise, we could end up in as bad a
shape as MS-DOS.


Cool, eh?

[Back to the ACE Page]