beneath-apple-dos/ch06.txt
2017-07-21 04:20:16 -07:00

850 lines
28 KiB
Plaintext

CHAPTER 6 - USING DOS FROM ASSEMBLY LANGUAGE
CAVEAT
This chapter is aimed at the advanced
assembly language programmer who
wishes to access the disk without
resorting to the PRINT statement
scheme used with BASIC.
Accordingly, the
topics covered here may be beyond the
comprehension (at least for the
present) of a programmer who has
never used assembly language.
DIRECT USE OF DISK DRIVE
It is often desirable or necessary to
access the Apple's disk drives
directly from assembly language,
without the use of DOS. This is
done using a section of 16 addresses
that are latched toggles, interfacing
directly to the hardware. There are
eight two byte toggles that essentially
represent pulling a TTL line high or
low. Applications which could use
direct disk access range from a
user written operating system to DOS-independent
utility programs. The
device address assignments are given
in Figure 6.1.
ADDRESS LABEL DESCRIPTION
---------------------------------------------------------------
$C080 PHASEOFF Stepper motor phase 0 off.
$C081 PHASEON Stepper motor phase 0 on.
$C082 PHASE1OFF Stepper motor phase 1 off.
$C083 PHASE1ON Stepper motor phase 1 on.
$C084 PHASE2OFF Stepper motor phase 2 off.
$C085 PHASE2ON Stepper motor phase 2 on.
$C086 PHASE3OFF Stepper motor phase 3 off.
$C087 PHASE3ON Stepper motor phase 3 on.
$C088 MOTOROFF Turn motor off.
$C089 MOTORON Turn motor on.
$C08A DRV0EN Engage drive 1.
$C08B DRV1EN Engage drive 2.
$C08C Q6L Strobe Data Latch for I/O.
$C08D Q6H Load Data Latch.
$C08E Q7L Prepare latch for input.
$C08F Q7H Prepare latch for output.
Q7L followed by Q6L = Read
Q7L followed by Q6H = Sense Write Protect
Q7H followed by Q6L = Write
Q7H followed by Q6H = Load Write Latch
*** figure 6.1 ***
The addresses are slot dependent and
the offsets are computed by
multiplying the slot number by 16.
In hexadecimal this works out nicely
and we can add the value $s0 (where s
is the slot number) to the base
address. If we wanted to engage disk
drive number 1 in slot number 6, for
example, we would add $60 to $C08A
(device address assignment for
engaging drive 1) for a result of
$C0EA. However, since it is
generally desirable to write code
that is not slot dependent, one would
normally use $C08A,X (where the
X register contains the value $s0).
In general, the above addresses need
only be accessed with any valid 6502
instruction. However, in the case of
reading and writing bytes, care must
be taken to insure that the data will
be in an appropriate register. All
of the following would engage drive
number 1. (Assume slot number 6)
LDA $C0EA
BIT $C08A,X (where X-reg contains $60)
CMP $C08A,X (where X-reg contains $60)
Below are typical examples
demonstrating the use of the device
address assignments. For more
examples, see APPENDIX A. Slot 6 is
assumed and the X-register contains
$60.
STEPPER PHASE OFF/ON:
Basically, each of the four phases
(0-3) must be turned on and then off
again. Done in ascending order, this
moves the arm inward. In descending
order, this moves the arm outward.
The timing between accesses to these
locations is critical, making this a
non-trivial exercise. It is
recommended that the SEEK command in
RWTS be used to move the arm. See
the section on using RWTS immediately
following.
MOTOR OFF/ON:
LDA $C088,X Turn motor off.
LDA $C089,X Turn motor on.
NOTE: A sufficient delay should be
provided to allow the motor time to
come up to speed. Shugart recommends
one second, but DOS is able to reduce
this delay by watching the read latch
until data starts to change.
ENGAGE DRIVE 1/2:
LDA $C08A,X Engage drive 1.
LDA $C08B,X Engage drive 2.
READ A BYTE:
READ LDA $C08C,X
BPL READ
NOTE: $C08E,X must already have been
accessed to assure Read mode. The
loop is necessary to assure that the
accumulator will contain valid data.
If the data latch does not yet
contain valid data the high bit will
be zero.
SENSE WRITE PROTECT:
LDA $C08D,X
LDA $C08E,X Sense write protect.
BMI ERROR If high bit set, protected.
WRITE LOAD AND WRITE A BYTE
LDA DATA
STA $C08D,X Write load.
ORA $C08C,X Write byte.
NOTE: $C08F,X must already have been
accessed to insure Write mode and a
100 microsecond delay should be
invoked before writing.
Due to hardware constraints, data
bytes must be written in 32 cycle
loops. Below is an example for an
immediate load of the accumulator,
followed by a write. Timing is so
critical that different routines may
be necessary, depending on how the
data is to be accessed, and code can
not cross memory page boundaries
without an adjustment.
LDA #$D5 (3 cycles)
JSR WRITE9 (6)
LDA #$AA (3)
JSR WRITE9 (6)
.
.
.
WRITE9 CLC (2)
WRITE7 PHA (3)
PLA (4)
WRITE STA $C08D,X (5)
ORA $C08C,X (4)
RTS (6)
CALLING READ/WRITE TRACK/SECTOR (RWTS)
Read/Write Track/Sector (RWTS) exists
in every version of DOS as a
collection of subroutines, occupying
roughly the top third of the DOS
program. The interface to RWTS is
standardized and thoroughly documented by Apple
and may be called by a
program running outside of DOS.
There are two subroutines which must
be called or whose function must be
performed.
JSR $3E3 - When this subroutine is
called, the Y and A registers are
loaded with the address of the
Input/Output control Block (IOB) used
by DOS when accessing RWTS. The low
order part of the address is in Y and
the high order part in A. This
subroutine should be called to locate
the IOB and the results may be stored
in two zero page locations to allow
storing values in the IOB and
retrieving output values after a call
to RWTS. Of course, you may set up
your own IOB as long as the Y and A
registers point to your IOB upon
calling RWTS.
JSR $3D9 - This is the main entry to
the RWTS routine. Prior to making
this call, the Y and A registers must
be loaded with the address of an IOB
describing the operation to be
performed. This may be done by first
calling $3E3 as described above. The
IOB must contain appropriate
information as defined in the list
on the facing page (offsets are given in hexadecimal):
INPUT/OUTPUT CONTROL BLOCK - GENERAL FORMAT
BYTE DESCRIPTION
00 Table type, must be $01
01 Slot number times 16 (s0: s=slot. Example: $60)
02 Drive number ($01 or $02)
03 Volume number expected ($00 matches any volume)
04 Track number ($00 through $22)
05 Sector number ($00 through $0F)
06-07 Address (LO/HI) of the Device Characteristics Table
08-09 Address (LO/HI) of the 256 byte buffer for
READ/WRITE
0A Not used
0B Byte count for partial sector ($00 for 256 bytes)
0C Command code $00 = SEEK
$01 = READ
$02 = WRITE
$04 = FORMAT
0D Return code - The processor CARRY flag is set upon
return from RWTS if there is a
non-zero return code:
$00 = No errors
$08 = Error during initialization
$10 = Write protect error
$20 = Volume mismatch error
$40 = Drive error
$80 = Read error (obsolete)
0E Volume number of last access (must be initialized)
0F Slot number of last access*16 (must be initialized)
10 Drive number of last access (must be initialized)
DEVICE CHARACTERISTICS TABLE
BYTE DESCRIPTION
00 Device type (should be $00 for DISK II)
01 Phases per track (should be $01 for DISK II)
02-03 Motor on time count (should be $EFD8 for DISK II)
RWTS IOB BY CALL TYPE
SEEK Move disk arm to desired track
Input: Byte 00 - Table type ($01)
01 - Slot number * 16 (s0: s=slot)
02 - Drive number ($01 or $02)
04 - Track number ($00 through $22)
06/07 - Pointer to the DCT
0C - Command code for SEEK ($00)
0F - Slot number of last access * 16
10 - Drive number of last access
Output: Byte 0D - Return code (See previous definition)
0F - Current Slot number * 16
10 - Current Drive number
READ Read a sector into a specified buffer
Input: Byte 00 - Table type ($01)
01 - Slot number * 16 (s0: s=slot)
02 - Drive number ($01 or $02)
03 - Volume number ($00 matches any volume)
04 - Track number ($00 through $22)
05 - Sector number ($00 through $0F)
06/07 - Pointer to the DCT
08/09 - Pointer to 256 byte user data buffer
0B - Byte count per sector ($00)
0C - Command code for READ ($01)
0E - Volume number of last access
0F - Slot number of last access * 16
10 - Drive number of last access
Output: Byte 0D - Return code (See previous definition)
0E - Current Volume number
0F - Current Slot number * 16
10 - Current Drive number
WRITE Write a sector from a specified buffer
Input: Byte 00 - Table type ($01)
01 - Slot number * 16 (s0: s=slot)
02 - Drive number ($01 or $02)
03 - Volume number ($00 matches any volume)
04 - Track number ($00 through $22)
05 - Sector number ($00 through $0F)
06/07 - Pointer to the DCT
08/09 - Pointer to 256 byte user data buffer
0B - Byte count per sector ($00)
0C - Command code for WRITE ($02)
0E - Volume number of last access
0F - Slot number of last access * 16
10 - Drive number of last access
Output: Byte 0D - Return code (See previous definition)
0E - Current Volume number
0F - Current Slot number * 16
10 - Current Drive number
FORMAT Initialize the diskette (does not put DOS on disk,
create a VTOC/CATALOG, or store HELLO program)
Input: Byte 00 - Table type ($01)
01 - Slot number * 16 (s0: s=slot)
02 - Drive number ($01 or $02)
03 - Volume number ($00 will default to 254)
06/07 - Pointer to the DCT
0C - Command code for FORMAT ($04)
0E - Volume number of last access
0F - Slot number of last access * 16
10 - Drive number of last access
Output: Byte 0D - Return code (See previous definition)
0E - Current Volume number
0F - Current Slot number * 16
10 - Current Drive number
CALLING THE DOS FILE MANAGER
The DOS file manager exists in every
version of DOS as a collection of
subroutines occupying approximately
the central third of the DOS program.
The interface to these routines is
generalized in such a way that they
may be called by a program running
outside of DOS. The definition of
this interface has
never been published by APPLE (or
anyone else, for that manner) but
since the calls can be made through
fixed vectors, and, the format of the
parameter lists passed have not
changed in all the versions of DOS,
these routines may be relied upon as
"safe". Indeed, the new FID utility
program
uses these routines to process files
on the diskette.
There are
two subroutines which must be called
in order to access the file manager.
JSR $3DC - When this subroutine is
called, the Y and A registers are
loaded with the address of the file
manager parameter list. The low order
part of the address is in Y and the
high order part in A. This subroutine
must be called at least once to
locate this parameter list and the
results may be stored in two zero page
locations to allow the programmer to
set input values in the parameter
list and to locate output values
there after file manager calls.
JSR $3D6 - This is the main entry to
the file manager. Prior to making
this call the parameter list, located
using the call described
above, must be completed
appropriately, depending upon the
type of call, and the X register must
be set to either zero or non-zero as
follows:
X = 0 - If file is not found, allocate it
X # 0 - If file is not found, do not allocate one
Normally,
X should be zero on an OPEN call for a
new file and non-zero for all other
call types.
Three buffers must be provided to the
file manager by the programmer,
allocated by him in his memory. These
buffers, together, occupy 557 bytes
of RAM, and must be passed to the
file manager each time their
associated file is used. A separate
set of these buffers must be
maintained for each open file.
DOS maintains buffers for this
purpose, as described in earlier
chapters, in high RAM. These buffers
may be "borrowed" from DOS if care is
taken to let DOS know about it. A
method for doing this will be
outlined later.
A chart giving the required inputs
for each call type to the file
manager is given in Figure 6.2.
The general format of the file
manager parameter list is as follows:
FILE MANAGER PARAMETER LIST - GENERAL FORMAT
BYTE DESCRIPTION
00 Call type: 01=OPEN 05=DELETE 09=RENAME
02=CLOSE 06=CATALOG 0A=POSITION
03=READ 07=LOCK 0B=INIT
04=WRITE 08=UNLOCK 0C=VERIFY
01 Sub-call type for READ or WRITE:
00=No operation (ignore call entirely)
01=READ or WRITE one byte
02=READ or WRITE a range of bytes
03=POSITION then READ or WRITE one byte
04=POSITION then READ/WRITE a range
02-09 Parameters specific to the call type used. See
FILE MANAGER PARAMETER LIST BY CALL TYPE below.
0A Return code (note: not all return codes can occur
for any call type). The processor CARRY
flag is set upon return from the file
manager if there is a non-zero return code:
00=No errors
01=Not used ("LANGUAGE NOT AVAILABLE")
02=Bad call type
03=Bad sub-call type (greater than four)
04=WRITE PROTECTED
05=END OF DATA
06=FILE NOT FOUND (was allocated if X=0)
07=VOLUME MISMATCH
08=DISK I/O ERROR
09=DISK FULL
0A=FILE LOCKED
0B Not used
0C-0D Address of a 45 byte buffer which will be used by the
file manager to save its status between calls. This
area is called the file manager workarea and need not
be initialized by the caller but the space must be
provided and this two byte address field initialized.
(addresses are in low/high order format)
0E-0F Address of a 256 byte buffer which will be used by the
file manager to maintain the current Track/Sector List
sector for the open file. Buffer itself need not be
initialized by the caller.
10-11 Address of a 256 byte buffer which will be used by the
file manager to maintain the data sector buffer.
Buffer need not be initialized by the caller.
*** INSERT FIGURE 6.2 ***
FILE MANAGER PARAMETER LIST BY CALL TYPE
OPEN Locates or creates a file. A call to POSITION should
follow every OPEN.
Input: Byte 00 - 01
02/03 - Fixed record length or 0000 if variable
04 - Volume number or 00 for any volume
05 - Drive number to be used (01 or 02)
06 - Slot number to be used (01-07)
07 - File type (used only for new files)
$00 = TEXT
$01 = INTEGER BASIC
$02 = APPLESOFT BASIC
$04 = BINARY
$08 = RELOCATABLE
$10 = S TYPE FILE
$20 = A TYPE FILE
$40 = B TYPE FILE
08/09 - Address of file name (30 characters)
(Low/high format)
0C/0D - Address of file manager workarea buffer
0E/0F - Address of T/S List sector buffer
10/11 - Address of data sector buffer
Output: Byte 07 - File type of file which was OPENed
0A - Return code (see previous definitions)
CLOSE Write out final sectors, update the Catalog.
A CLOSE call is required eventually for every OPEN.
Input: Byte 00 - 02
0C/0D - Address of file manager workarea buffer
0E/0F - Address of T/S List sector buffer
10/11 - Address of data sector buffer
Output: Byte 0A - Return code
READ Read one or a range of bytes from the file to memory.
WRITE Write one or a range of bytes from memory to the file.
Input: Byte 00 - 03 (READ) 04 (WRITE)
01 - Subcode:
00 = No operation
01 = READ or WRITE one byte only
02 = READ or WRITE a range of bytes
03 = POSITION then READ/WRITE one byte
04 = POSITION then READ/WRITE range
02/03 - (Subcodes 03 or 04) Record number
04/05 - (Subcodes 03 or 04) Byte offset
06/07 - (Subcodes 02 or 04) Number of bytes in
range to be read or written. (Note: for
WRITE, this length must be one less
than the actual length to be written)
08/09 - (Subcodes 02 or 04) Address of range of
bytes to be written or address of
buffer to which bytes are to be read.
08 - (WRITE, Subcodes 01 or 03) Single byte
to be written.
0C/0D - Address of file manager workarea buffer
0E/0F - Address of T/S List sector buffer
10/11 - Address of data sector buffer
Output: Byte 02/03 - Record number of current file position
04/05 - Byte offset of current position in file
08 - (READ, Subcodes 01 or 03) Byte read
0A - Return code
DELETE Locate and delete a file, freeing its sectors.
Input: Byte 00 - 05
(remainder are the same as with OPEN call type)
Output: Byte 0A - Return code
CATALOG Produce a catalog listing on the output device.
Input: Byte 00 - 06
05 - Drive
06 - Slot
0C/0D - Address of file manager workarea buffer
0E/0F - Address of T/S List sector buffer
10/11 - Address of data sector buffer
Output: Byte 0A - Return code
LOCK Lock a file.
Input: Byte 00 - 07
(remainder are the same as with OPEN call type)
Output: Byte 0A - Return code
UNLOCK Unlock a file.
Input: Byte 00 - 08
(remainder are the same as with OPEN call type)
Output: Byte 0A - Return code
RENAME Rename a file.
Input: Byte 00 - 09
02/03 - Address of new file name (30 bytes)
(remainder are the same as with OPEN call type)
Output: Byte 0A - Return code
POSITION Calculate the location of a record and/or byte
offset in the file. Position such that next READ or
WRITE will be at that location in the file. A call
to POSITION (either explicitly or implictly using
subcodes of READ or WRITE) is required prior to the
first READ or WRITE. Bytes 02 through 05 should be
set to zeros for a normal position to the beginning
of the file.
Input: Byte 00 - 0A
02/03 - Relative record number for files with a
fixed length record size or zero. First
record of file is record 0000.
04/05 - Relative byte offset into record or of
entire file if record number is zero.
0C/0D - Address of file manager workarea buffer.
0E/0F - Address of T/S List sector buffer.
10/11 - Address of data sector buffer.
Output: Byte 0A - Return code
INIT Initialize a slave diskette. This function formats a
diskette and writes a copy of DOS onto tracks 0-2.
A VTOC and Catalog are also created. A HELLO program
is not stored, however.
Input: Byte 00 - 0B
01 - First page of DOS image to be copied to
the diskette. Normally $9D for a 48K
machine.
04 - Volume number of new diskette.
05 - Drive number (01 or 02)
06 - Slot number (01-07)
0C/0D - Address of file manager workarea buffer.
0E/0F - Address of T/S List sector buffer.
10/11 - Address of data sector buffer.
Output: Byte 0A - Return code
VERIFY Verify that there are no bad sectors in a file by
reading every sector.
Input: Byte 00 - 0C
(remainder are the same as the OPEN call type)
Output: Byte 0A - Return code
DOS BUFFERS
Usually it is desirable to use one of DOS's buffers when
calling the file manager to save memory. DOS buffers consist of
each of the three buffers used by the file manager (file
manager workarea, T/S List sector, and data sector) as well as
a 30 byte file name buffer and some link pointers. All together
a DOS buffer occupies 595 bytes of memory. The address of the
first DOS buffer is stored in the first two bytes of DOS ($9D00
on a 48K APPLE II). The address of the next buffer is stored in
the first and so on in a chain of linked elements. The link
address to the next buffer in the last buffer is zeros. If the
buffer is not being used by DOS, the first byte of the file
name field is a hex 00. Otherwise, it contains the first
character of the name of the open file. The assembly language
programmer should follow these conventions to avoid having DOS
reuse the buffer while he is using it. This means that the
name of the file should be stored in the buffer to reserve it
for exclusive use (or at least a non-zero byte stored on the
first character) and later, when the user is through with the
buffer, a 00 should be stored on the file name to return it
to DOS's use. If the later is not done, DOS will eventually
run out of available buffers and will refuse even to do a
CATALOG command. A diagram of the DOS
buffers for MAXFILES 3 is given in
Figure 6.3 and
the format of a DOS buffer is given below.
*** INSERT FIGURE 6.3 ***
DOS BUFFER FORMAT
BYTE DESCRIPTION
000/0FF Data sector buffer (256 bytes in length)
100/1FF T/S List sector buffer (256 bytes in length)
200/22C File manager workarea buffer (45 bytes in length)
22D/24A File name buffer (30 bytes in length)
First byte indicates whether this DOS buffer is
being used. If hex 00, buffer is free for use.
24B/24C Address (Lo/High) of file manager workarea buffer
24D/24E Address of T/S List sector buffer
24F/250 Address of data sector buffer
251/252 Address of the file name field of the next buffer on
the chain of buffers. If this is the last buffer on
the chain then this field contains zeros.
THE FILE MANAGER WORKAREA
The file manager workarea contains
the variables which, taken together,
constitute all of the information the
file manager needs to deal with an
open file. Each time the file manager
finishes processing a call, it copies
all of its important variables into
the file manager workarea buffer
provided by the caller. Each
subsequent time the file manager is
called, the first thing it does is to
copy the contents of the file manager
workarea buffer back into its
variables so that it may resume
processing for the file where it left
off on the previous call.
Ordinarily, the programmer will have
no need to worry about the contents
of this workarea, since most of the
useful information is present in the
parameter list anyway. Occasionally,
it is handy to know more about the
open file. For these cases, the
format of the file manager workarea
is given below:
FILE MANAGER WORKAREA FORMAT
BYTE DESCRIPTION
00/01 Track/Sector of first T/S List for file
02/03 Track/Sector of current T/S List for file
04 Flags:
80=T/S List buffer changed and needs writing
40=Data buffer has been changed and needs writing
02=Volume freespace map changed and needs writing
05/06 Track/Sector of current data sector
07 Sector offset into catalog to entry for this file
08 Byte offset into catalog sector to entry for file
09/0A Maximum data sectors represented by one T/S List
0B/0C Offset of first sector in current T/S List
0D/0E Offset of last sector in current T/S List
0F/10 Relative sector number last read
11/12 Sector size in bytes (256)
13/14 Current position in sectors (relative)
15 Current byte offset in this sector
16 Not used
17/18 Fixed record length
19/1A Current record number
1B/1C Byte offset into current record
1D/1E Length of file in sectors
1F Next sector to allocate on this track
20 Current track being allocated
21/24 Bit map of available sectors on this track (rotated)
25 File type (80=locked) 0,1,2,4=T,I,A,B
26 Slot number times 16 (example: $60=slot 6)
27 Drive number (01 or 02)
28 Volume number (complemented)
29 Track
2A/2C Not used
COMMON ALGORITHMS
Given below are several pieces of code
which are used when working with DOS:
LOCATE A FREE DOS BUFFER
The following subroutine may be used
to locate an unallocated DOS buffer
for use with the DOS file manager.
FBUFF LDA $3D2 LOCATE DOS LOAD POINT
STA $1
LDY #0
STY $0
*
GBUF0 LDA ($0),Y LOCATE NEXT DOS BUFFER
PHA
INY
LDA ($0),Y
STA $1
PLA
STA $0
BNE GBUF GOT ONE
LDA $1
BEQ NBUF NO BUFFERS FREE
*
GBUF LDY #0 GET FILENAME
LDA ($0),Y
BEQ GOTBUF ITS FREE
LDY #36 ITS NOT FREE
BNE GBUF0 GO GET NEXT BUFFER
*
GOTBUF CLC INDICATE-GOT A FREE BUFFER
RTS RETURN TO CALLER
NBUF SEC INDICATE-NO FREE BUFFERS
RTS RETURN TO CALLER
IS DOS IN THE MACHINE?
The following series of instructions
should be used prior to attempting to
call RWTS or the file manager to
insure that DOS is present on this
machine.
LDA $3D0 GET VECTOR JMP
CMP #$4C IS IT A JUMP?
BNE NODOS NO, DOS NOT LOADED
WHICH VERSION OF DOS IS ACTIVE?
In case the program has version dependent code, a check of
the DOS version may be required:
CLC
LDA #0 ADD $16BE TO DOS LOAD POINT
ADC #$BE
STA $0
LDA $3D2
ADC #$16
STA $1
LDY #0
LDA ($0),Y GET DOS VERSION NUMBER (2 OR 3)
WHICH BASIC IS SELECTED?
Some programs depend upon either the
INTEGER BASIC ROM or the APPLESOFT
ROM. To find out which is active and
select the one desired, the following
subroutine can be called. First the A
register is loaded with a code to
indicate which BASIC is desired. $20
is used for INTEGER BASIC and $4C is
used for APPLESOFT. To set up for
APPLESOFT, for example:
LDA #$4C CODE FOR APPLESOFT
JSR SETBSC CALL SUBROUTINE
BNE ERROR LANGUAGE NOT AVAILABLE
.
.
.
SETBSC CMP $E000 CORRECT BASIC ALREADY THERE?
BEQ RTS YES
STA $C080 NO, SELECT ROM CARD
CMP $E000 NOW DO WE HAVE IT?
BEQ RTS YES
STA $C081 NO, TRY ROM CARD OUT
CMP $EOOO GOT IT NOW?
RTS RTS IN ANY CASE, EXIT TO CALLER
SEE IF A BASIC PROGRAM IS IN EXECUTION
To determine if there is a BASIC program running or
if BASIC is in immediate command mode, use the following
statements:
..IF INTEGER BASIC IS ACTIVE...
LDA $D9
BMI EXEC PROGRAM EXECUTING
BPL NOEXEC PROGRAM NOT EXECUTING
..IF APPLESOFT BASIC IS ACTIVE...
LDX $76 GET LINE NUMBER
INX
BEQ NOEXEC PROGRAM NOT EXECUTING
LDX $33 GET PROMPT CHARACTER
CPX #$DD PROMPT IS A "]"?
BEQ NOEXEC YES, NOT EXECUTING
BNE EXEC ELSE, PROGRAM IS EXECUTING
.nx ch7