by Craig Bruce -- for Release #16 -- Rev. February 9, 1997.
1. INTRODUCTION 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 128/64). 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. 2. SYSTEM VARIABLES AND CONSTANTS 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. 2.1. ZERO-PAGE VARIABLES There are four zero-page variables used for passing arguments in most system calls. They are as follows: SYMBOL BYTES DESCRIPTION ------- ----- ----------- 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. 2.2. SYSTEM VARIABLES There are several non-zeropage variables for storing system status and return values: SYMBOL BYTES DESCRIPTION ---------- ----- ----------- 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 aceDirentBufferstorage 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. 2.3. SYSTEM CONSTANTS There are several symbolic constants that are used with the ACE system interface: 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. 3. SYSTEM CALLS 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 locations. 3.1. FILE CALLS ------------------------------------------------------------------------------ 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 program. ------------------------------------------------------------------------------ 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 calls. 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: CODE(hex) CODE(dec) NAME DESCRIPTION --------- --------- ---- ----------- $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 ============================================================================== 3.2. DIRECTORY CALLS ------------------------------------------------------------------------------ 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. ============================================================================== 3.3. SCREEN-CONTROL CALLS 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 thing. 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: CODES (hex) DESCRIPTION ----------- ----------- $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 : RETURNS: 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 zero. ------------------------------------------------------------------------------ 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 ALTERS : 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 RETURNS: 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 RETURNS: 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 faster. ------------------------------------------------------------------------------ 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 RETURNS: 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 RETURNS: 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 RETURNS: 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. ============================================================================== 3.4. CONSOLE CALLS 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 RETURNS: 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 returned. ------------------------------------------------------------------------------ NAME : aceConGetpos PURPOSE: get current cursor location ARGS : RETURNS: .A = row .X = column ALTERS : .Y 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 ALTERS : .Y 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 ------- ------------ ------------ ----------- $00 $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) ALTERS : 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 RETURNS: 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 ALTERS : 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 ============================================================================== 3.5. GRAPHICS CALLS ------------------------------------------------------------------------------ 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 ALTERS : .Y 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 ratio. 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 : RETURNS: 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 exits. ------------------------------------------------------------------------------ NAME : aceGrFill PURPOSE: scroll window ARGS : .A = fill bit value RETURNS: 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 RETURNS: 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. ============================================================================== 3.6. PROCESS-CONTROL CALLS 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: HIGHER ADDRESSES | | | | <--(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 +-----------+ | | LOWER ADDRESSES 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 system. 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 complete). 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 RETURNS: ALTERS : 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. ============================================================================== 3.7. MEMORY CALLS 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 "zpload". ------------------------------------------------------------------------------ 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. ============================================================================== 3.8. TIME CALLS ------------------------------------------------------------------------------ NAME : aceTimeGetDate PURPOSE: get the current date and time ARGS : (.AY) = address of buffer to put BCD-format date into RETURNS: 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 RETURNS: 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. ============================================================================== 3.9. MISCELLANEOUS CALLS ------------------------------------------------------------------------------ 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 ALTERS : 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 ALTERS : 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. ============================================================================== 3.10. IOCTL CALLS ------------------------------------------------------------------------------ #$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. ------------------------------------------------------------------------------ #$42: 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 ALTERS : 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 future. ------------------------------------------------------------------------------ #$43: 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 table: 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. ------------------------------------------------------------------------------ #$44: 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? ============================================================================== 4. USER-PROGRAM ORGANIZATION 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 rts 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. 5. CONCLUSION Cool, eh?