
                         CrASH programmer's Guide!


-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- IMPORTANT -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

 This deals with Metacommands of TASM:
  CrASH programs must start with ".INCLUDE CRASH82.INC"
  There is no need to address a .ORG at the beginning of a program.
  There is no need to place a .END at the end of a program.

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-



 Contents

Introduction
Differences from ASH 3.0
A Guide to the ROM_CALLs
A Guide to the CrASH calls
Some Notes of Caution
Installing an Interrupt Handler



 Introduction

  Hello, programmers!  Programming in assembly for the TI-82 is just like
programming for any other TI calculator.  Because there are so many of you
coding for TI-85's ZShell, we have tried to make adapting to CrASH very
simple, by using all of the same ROM_CALL names.  We hope that this will help
current or former ZShell programmers in this new environment.  Since CrASH
relocates its programs when running, some parts may even be more simple to
program than ZShell.

  CrASH is VERY simple to learn if you have programmed for the TI-82 before,
in either OS-82/OShell-82 or ASH.  CrASH is most similar in programming to
ASH.

  The primary difference between 85/86 programming and the 82 is how the
display is accessed.  As you already may know, the screen is smaller
(96x64 instead of 128x64), but this is NOT the only difference.  The most
important difference is that the display is accessed through a port, instead
of through a memory map like the 85 and 86 use.  It is not necessary for you
to know how to access this port.  The easiest way to display stuff is to
use the GRAPH_MEM area as if it were the memory-mapped area of the 85/86
(except being smaller of course), and then using CR_GRBCopy (a CrASH-specific
call) to do the port manipulation for you to display GRAPH_MEM.

  In order to make programming easier, CrASH supports usage of commonly used
rom procedures, using the ROM_CALL macro.  The commonly used functions are
listed inside CRASH82.INC.

  There are some functions built into CrASH which are also listed inside
CRASH82.INC.  This includes a very fast display routine.

  If you cannot understand the example listed below, it would probably be to
the best of your interests to begin by learning assembly language.  There are
many sources from which to learn assembly language, and we may even release
a tutorial ourselves in the future.

  Because the calculator needs to always move variables around in memory,
assembly programs were truly difficult to write.  To ease this problem, after
the user selects a program, CrASH performs a simple relocation routine and
moves the program to a fixed position in user memory.  This means that you
may address variables and labels INSIDE your own program.  Note that this
differs from OS-82 and ZShell - you do NOT add (PROGRAM_ADDR) every time you
want to address something in your program.  For example:

.INCLUDE CRASH82.INC            ; The Only file CrASH program needs to start
.DB "Hello World", 0            ; ALL CrASH programs must have a long name

  LD  HL, $0000                 ; Set cursor to top-left
  LD  (CURSOR_POS), HL
  LD  HL, HelloString           ; Point to HelloString
  ROM_CALL(D_ZT_STR)
  JP  AfterText                 ; Jump to after the string
HelloString:
  .DB  "Hello world!", 0
AfterText:
  CALL CR_KHAND                 ; Call uncrashable keyboard handler

  RET

  This loads the pointer to point to HelloString, so D_ZT_STR can display it.
The JP after the D_ZT_STR allows you to jump over the String data.  Some of
you experienced coders out there may be laughing at how terribly this program
has been coded, and you're right, so don't use it!

  There are several addresses in memory where you can store temporary data.
Inside the include file, they are defined as FREE_A1, FREE_A2, FREE_A3,
FREE_B, and FREE_C.  FREE_A1 is actually the GRAPH_MEM, which can be used
as free memory.  FREE_A2, which is TEXT_MEM, can also be used as free memory.
FREE_A3 should be safe to use unless the OPX variables are being used by a
ROM_CALL function that is unknown.  FREE_B is actually TEXT_MEM2, which CrASH
will clear if you use CR_KHAND.  FREE_C is actually APD_BUF, which may be
being used by an interrupt.  If you know the amount of memory needed, check
to see if it fits before running the program, by reading APD_FREE (integer).
Information on this is in the section titled "Some Notes of Caution".

  At startup, CrASH will always initialize the GRAPH_MEM, TEXT_MEM, CURSOR_X,
CURSOR_Y, CURSOR_ROW, CURSOR_POS, and all general registers (A, B, C, D, E,
H, L, IX) to 0.  CrASH will also enable interrupts, and initialize the LCD
Controller Port (for use with CR_GRBCopy).

  To convert the OBJ files produced by the assembler to the 82P files which
the link program handles, use the DOS program CrPRGM82.EXE (which is included
in the CrASH.ZIP archive).  Included also is CrAsm.BAT, a small batch file
that will process an assembly language file and automate the process to give
you an 82P file.



 Differences from ASH 3.0

  The main differences between CrASH and ASH are as follows:

 Using the APD_BUF
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  In an effort to make interrupt handlers easier to write, CrASH includes a
simple method of interrupt handling programs that run even when the program
that started it is no longer loaded (IE Game Wizard) telling the other
programs that APD_BUF is in use.  It is VERY IMPORTANT that your programs pay
attention to this information.  If you absolutely MUST use APD_BUF, simply
have your program refuse to run if not enough space is available in APD_BUF.


 The KEY_HAND replacement
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  In ASH, as many users AND programmers already know, you will crash your
calculator if you turn off the calc during what is known as a KEY_HAND - the
"normal" keyboard input routine used by TI-OS.  CrASH has a method to program
around this.  The problem is that when any shell relocates a program to run
it, memory is in an unstable (TI-OS doesn't know how to handle it) state.
Exiting to TI-OS in this state will not crash immediately, but it will soon.
ASH didn't have a way to counteract this.  CrASH does, and this is in fact
the original reason why we wrote CrASH.

  The way around is fairly simple.  Relocate everything back, call KEY_HAND,
and then rerelocate and return to the program.  Fortunately, you don't have
to code this.  A built-in CrASH routine does this for you.  Simply call
CR_KHAND instead of KEY_HAND.  There IS a downside - TEXT_MEM2 (shadow) is
deleted as part of CR_KHAND.  If you find this unacceptable, then just write
your own key handler that doesn't use CR_KHAND - GET_KEY isn't very hard.

  See CrASH-specific calls for more information.  Unlike most calls you do
in TI-82 assembly, you do NOT use ROM_CALL to access CrASH calls.  This is
simply because CrASH call addresses don't vary depending on ROM version like
ROM calls do.


 The Easy Random Call
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  To help keep program sizes down and make them faster, a simple random
number routine has been added to CrASH.  It's very simple to use.  Call RAND,
and it will return a 7-bit number in A (0-127).  See CrASH-specific calls for
more information.


 Compare HL with BC
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  To help keep program size down (again), and for programmer convenience, we
made a routine that compares HL with BC, in a matter identical to CP_HL_DE.
Fortunately, CP_HL_DE is not a ROM_CALL, so you don't have to remember that
CP_HL_DE is part of ROM and CP_HL_BC is part of CrASH.


 "Boss Key"
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- 
  "Boss key" features are simply a method of exiting your program immediately
to TI-OS so that you look like you're doing something important to your
"boss".  Of course, the person who you'll use it with is usually a teacher,
not a boss.  Anyways, returning to TI-OS directly has the exact same problems
as KEY_HAND, so a workaround has been made.  If you want your program to have
a "boss key", simply return to TI-OS with the EXIT_2_TIOS CrASH routine.  You
may either CALL or JP to this routine - it does not matter.  Of course,
EXIT_2_TIOS does not return, so you don't need a RET after the JP :)

 Large Programs
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  Theoretically, CrASH programs can relocate as long as there is enough
memory for that program to store itself on the calculator.  The calculator
does not crash after running like it did in ASH for programs that are even
about 27 Kbytes.
  Yes, this means that FFX4 can be ported to CrASH if the author wanted to
(provided that he wants to and removes the turboing).


 K_RIGTH
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  The name has been spelled correctly in CrASH.  K_RIGTH included also for
compatibility.


 Program names
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  The last, and most insignificant, change is in the naming of programs.  ASH
programs are usually named with "TI-OS valid" names so you can run them
directly if you want.  However, since CrASH does not have this capability,
we changed that.  All CrASH programs normally have a lowercase inverse A (as
in the 85's lowercase alpha lock) at the beginning, to signify that they are
assembly programs and to move them to the bottom of the program list.




 A Guide to the ROM_CALLs

  The names were mostly taken from the ZShell function names.

- LD_HL_MHL
- CP_HL_DE
- UNPACK_HL
- STORE_KEY
- GET_KEY

- CLEARLCD
- CLEARTEXT
- SCROLL_UP
- D_HL_DECI
- D_ZT_STR
- D_LT_STR
- D_ZM_STR
- D_LM_STR
- TR_CHARPUT
- TX_CHARPUT
- M_CHARPUT

 Memory Addresses and Bits for Text Operations
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 (CURSOR_ROW)    = Row to put character in (Y-coordinate)
 (CURSOR_COL)    = Column to put character in (X-coordinate)
 SET 3, (IY+$05) = Display inverted (White on Black)
 RES 3, (IY+$05) = Display normally (Black on White)
 SET 1, (IY+$0D) = Alter text memory with writes
 RES 1, (IY+$0D) = Don't alter text memory with writes

 Memory Addresses and Bits for Mini Font Operations
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
 (CURSOR_X)      = x coordinate
 (CURSOR_Y)      = y coordinate
 RES 1, (IY+$05) = Display only 6 rows of the character
 SET 1, (IY+$05) = Display the entire 7 rows of the character
 RES 3, (IY+$05) = Display character over the current screen
 SET 3, (IY+$05) = XOR character with the current screen


 CALL LD_HL_MHL

  Inputs: HL = Pointer to memory offset
 Returns: HL = (HL)    (the word pointed by HL)
           A = L

  LD_HL_MHL places the word pointed to by HL into HL.  This is useful when
you have a table of word and you want to retrieve a word from it.

Example:
  LD  HL, Table    ; HL points to a table defined by user
  LD  DE, Offset   ; DE is offset
  ADD HL, DE       ; Add DE to HL twice -- This will get
  ADD HL, DE       ;  us the word offset (words are 2 bytes)
  CALL LD_HL_MHL   ; HL now contains the word we looked up in the table


 CALL CP_HL_DE

  Inputs: HL and DE = The words to compare
 Returns: Updated flags

  CP_HL_DE compares HL and DE, just as if you had used the CP instruction.

Example:
  LD  HL, (Score)  ; HL contains value of (Score)
  LD  DE, $2FFF    ; DE contains a value
  CALL CP_HL_DE
  JR  Z, Same      ; Go to Same if HL and DE were equal.


 CALL UNPACK_HL

  Inputs: HL = value to "unpack" from
 Returns:  A = value "unpacked" from HL
          HL = modified, to allow "unpacking" of next number

  UNPACK_HL "unpacks" one digit from HL.  Unpacking means taking the number
and dividing it by 10, and putting the remainder into A.  This function is
also used by D_HL_DECI.

Example:
  LD  DE, Digits+4 ; Location to store the string of 5 characters
  LD  B, 5         ; A word can be described by 5 digits.
ConvertLoop:
  CALL UNPACK_HL   ; Unpack a number
  ADD A, '0'       ; Now A is the CHARACTER for this value
  LD  (DE), A      ; Put that character into the string
  DEC DE           ; Point DE to the previous byte in the string
  DJNZ ConvertLoop ; Convert all the 5 characters


 CALL STORE_KEY

  Inputs:  A = scancode
 Returns: ($8000) = scancode
          ($8006) = scancode (if scancode <> 0)
          SET 3, (IY+$00)

  STORE_KEY stores a keystroke into the key buffer.  This keystroke can then
be read by the GET_KEY function.

Example:
  LD  A, $09       ; $09 is scancode for ENTER
  CALL STORE_KEY   ; Store ENTER into the key buffer


 CALL GET_KEY

  Inputs: None
 Returns:  A = scancode of last key pressed
          HL = $8000
          RES 3, (IY+$00)

  GET_KEY is one of the most used ROM functions.  It returns the last key
that was pressed.  The value returned in A is the scancode of the key.  If
a key is not pressed, instead of waiting for a key, it returns 0.

Example:

WaitKey:
  CALL GET_KEY
  OR  A
  JR  Z, WaitKey   ; If A is not 0 (a key is pressed), then continue


 ROM_CALL(CLEARLCD)

  Inputs: None
 Returns: The LCD is cleared.
          Text/graphics memory are not modified.
          Many registers are modified.

  CLEARLCD clears the LCD screen.  Since it does not change the text memory,
your variables are left alone.

Example:
  ROM_CALL(CLEARLCD) ; Clear the text and start
  JR  Start


 ROM_CALL(CLEARTEXT)

  Inputs: None
 Returns: None

  SET 1, (IY+$0D) = fill text memory (8BDF) with spaces
  RES 1, (IY+$0D) = clear the LCD without altering the text memory

  CLEARTEXT clears the text memory.  If you want to be sure you just clear
the LCD and not the memory, use the CLEARLCD function instead.

Example:
  ROM_CALL(CLEARTEXT) ; Where did the variables go?
  RET              ; Let's go back to CrASH.


 ROM_CALL(SCROLL_UP)

  Inputs: ($8C8F) = first row to scroll (0-7)
          ($8C90) = last row to scroll (1-8)
 Returns: None

  SCROLL_UP scrolls the screen or part of it one line up, and inserts a blank
line at the end of it.

  SET 1, (IY+$0D) = scroll the text memory
  RES 1, (IY+$0D) = don't scroll the text memory

Example:
  XOR A            ; Set A=0
  LD  ($8C8F), A   ; Starting row is 0
  LD  A, 5
  LD  ($8C90), A   ; Last row to scroll is 5
  ROM_CALL(SCROLL_UP)


 ROM_CALL(D_HL_DECI)

  Inputs: HL = decimal 16-bit number to display
          (CURSOR_POS) with cursor positions.
 Returns: (CURSOR_POS) updated with new cursor positions.

  D_HL_DECI displays HL as a 5-digit, right justified, blank-padded decimal
number.

Example:
  LD  HL, (Score)  ; We want to display the value of Score
  ROM_CALL(D_HL_DECI)


 ROM_CALL(D_ZT_STR)

  Inputs: HL = pointer to zero-terminated string
          (CURSOR_POS) with cursor positions.
 Returns: (CURSOR_POS) updated with new cursor positions.
          HL points to byte after the string.

  D_ZT_STR displays a text zero-terminated string on the screen.

Example:
  LD  HL, Hello    ; Offset of the string
  ROM_CALL(D_ZT_STR) ; Display the string
  RET              ; Return from sub-routine (or to CrASH)
Hello:
.DB "Hello", 0     ; String to use with a 0 behind


 ROM_CALL(D_LT_STR)

  Inputs: HL = pointer to length indexed string
          (CURSOR_POS) with cursor positions.
 Returns: (CURSOR_POS) updated with new cursor positions.
          HL points to byte after the string.

  D_LT_STR displays a normal text string of characters to the screen.  The
length of the string is determined by the value of the first byte in the
string.  The cursor coordinades are updated, and HL will point to the next
byte after the string.

Example:
  LD  HL, Hello    ; Offset of the string
  ROM_CALL(D_LT_STR) ; Display the string
  RET              ; Return from sub-routine (or to CrASH)
Hello:
.DB 5, "Hello"     ; String to use


 ROM_CALL(D_ZM_STR)

  Inputs: HL = pointer to zero-terminated string
          (GRAF_CURS) with cursor positions
 Returns: (GRAF_CURS) updated with new cursor positions.
          HL points to next byte after string.

  D_ZM_STR displays a zero-terminated string on the screen in mini font.

Example:
  LD  HL, Hello    ; Offset of the string
  ROM_CALL(D_ZM_STR) ; Display the string
  RET              ; Return from sub-routine (or to CrASH)
Hello:
.DB "Hello", 0     ; String to use, with a 0 at the end


 ROM_CALL(D_LM_STR)

  Inputs: HL = pointer to string
           B = length of string
          (GRAF_CURS) with cursor positions
 Returns: (GRAF_CURS) updated with new cursor positions.
          HL points to next byte after string.

  D_LM_STR displays a length indexed string on the screen in mini font.

Example:
  LD  HL, Hello    ; Offset of the string
  LD  B, 5         ; We want to display 5 characters
  ROM_CALL(D_LT_STR) ; Display the string
  RET              ; Return from sub-routine (or to CrASH)
Hello:
.DB "Hello"        ; String to use


 ROM_CALL(M_CHARPUT)

  Inputs:  A = character to display
          (GRAF_CURS) with cursor positions
 Returns: (GRAF_CURS) updated with new cursor positions.
          DE is destroyed.

  M_CHARPUT displays the character in A on the screen in mini font.  This is
the style that is usually used by the TI-82 when in graphics mode.

Example:
  LD  HL, $1010    ; HL contains the coordinates
  LD  A, 'C'       ; We want to display a 'C'.
  LD  (GRAF_CURS), HL ; Store the coordinates
  ROM_CALL(M_CHARPUT) ; Display the character


 ROM_CALL(TR_CHARPUT)

  Inputs:  A = Character to display
 Returns: (CURSOR_POS) updated with new cursor positions.

  TR_CHARPUT displays a raw character to the screen.

Example:
  LD  HL, $0207    ; HL contains X and Y position
  LD  (CURSOR_POS), HL ; Store the XY coordinates
  LD  A, 'a'       ; Char to put
  ROM_CALL(TR_CHARPUT) ; Put a at (7,2)


 ROM_CALL(TX_CHARPUT)

  Inputs:  A = Character to display
          (CURSOR_POS) with cursor positions.
 Returns: (CURSOR_POS) updated with new cursor positions.

  TX_CHARPUT function displays a single character to the screen.  It also
converts the character if it is a control character.

Example:
  LD  HL, $0000    ; HL contains X and Y position (top left)
  LD  (CURSOR_POS), HL ; Store the XY coordinates
  LD  A, 'A'       ; Char to put
  ROM_CALL(TX_CHARPUT) ; Put A in the top-left corner



 A Guide to the CrASH CALLs

- EXIT_2_TIOS
- CR_KHAND
- CR_GRBCopy
- CP_HL_BC
- RAND


 CALL EXIT_2_TIOS

  Inputs: None
 Returns: You are back in the normal TI-OS.

  EXIT_2_TIOS is THE boss key feature of CrASH.  You may also use a JP
instead of CALL.

Example:
  CALL GET_KEY     ; Check for keys pressed
  CP  G_ZOOM       ; Check for the status of ZOOM
  JP  Z, EXIT_2_TIOS ; If ZOOM was pressed, exit to TI-OS.


 CALL CR_KHAND

  Inputs: None
 Returns: A with keycodes from the standard key handler.

  CR_KHAND is THE uncrashable key feature of CrASH.  Using this function will
wipe out TEXT_MEM2, however (the FREE_B memory location).  CrASH will
automatically reset (IY+$09), allowing the key handler to definitely register
a key.

Example:
  CALL CR_KHAND    ; Wait for a key to be pressed
  CP  K_ENTER      ; Check if ENTER was pressed.
  JR  Z, Option    ; If ENTER was pressed, go to option


 CALL CR_GRBCopy

  Inputs: None
 Returns: Screen updated.

  CR_GRBCopy is THE fast display feature of CrASH.  Using this function will
provide a faster display (faster than say, DISP_GRAPH) to the screen allowing
for faster game play.
  None of the registers are modified.

CAUTION:
  Be sure not to change the turbo port of the calculator; it will cause many
problems with other programs using the CR_GRBCopy function, and CrASH itself.


Example:
  LD  HL, GRAPH_MEM   ; \
  LD  DE, GRAPH_MEM+1 ; |
  LD  BC, $2FF        ;  > Fill the GRAPH_MEM with $FF
  LD  (HL), $FF       ; |
  LDIR                ; /

  CALL CR_GRBCopy     ; Display the filled screen


 CALL CP_HL_BC

  Inputs: HL and BC = The words to compare
 Returns: Updated flags

  CP_HL_BC compares HL and BC, just as if you had used the CP instruction.

Example:
  LD  HL, (Score)  ; HL contains value of (Score)
  LD  BC, $2FFF    ; BC contains a value
  CALL CP_HL_BC
  JR  Z, Same      ; Go to Same if HL and BC were equal.


 CALL RAND

  Inputs: None
 Returns: A is a 7-bit pseudorandom number (0-127)

  RAND yields a random number in A.

Example:
  CALL RAND        ; A contains 0-127
  AND %00100000    ; Check to see if bit 6 is set
  JR  Z, NoBit     ; If bit is not found, go to NoBit



 Some Notes of Caution

* CR_GRBCopy and other functions are included for your convenience.  Please
follow any directions given in the notes above.

* Be sure to test that your program runs correctly on all calculators before
distributing it.  Check for any chances for a crash and please fix them.

* Be sure not to change the turbo port of the calculator; it will cause many
problems with other programs using the CR_GRBCopy function, and CrASH itself.

* When using FREE_B, DO NOT use CR_KHAND unless it is acceptable for
CR_KHAND to wipe out any data there.  We feel not having another 128 bytes
of free memory far outweighs the calculator crashing during KEY_HAND.

* Don't use ROM_CALL(KEY_HAND).  Make sure you change ALL of these calls to
CALL CR_KHAND when porting a program to CrASH.
  (We do not want crashing programs)

* When you use CR_KHAND, the calculator CAN turn off, either by APD or by
the user hitting [2nd]+[OFF].  For this reason, PLEASE make sure that your
program does not leave the calculator in a state that TI-OS can't handle.

* Please notify the authors about any bugs you find while programming for
CrASH.

* Don't use VAT ROM_CALLs in CrASH because relocation will make the VAT's
pointer wrong.  We have an idea to fix this, but this will not be done until
version 2.0

* When using the APD_BUF memory area (FREE_C), be sure to check INT_STATE and
APD_FREE.  INT_STATE (byte) will contain 0 if APD_BUF has no interrupts
running.  APD_FREE (word) will contain the amount of free APD_BUF at the
beginning of APD_BUF if any interrupts are being used.  THIS MAY BE ZERO.

Example:
  LD  A, (INT_STATE)   ; Find the Interrupt State
  OR  A \ JR  Z, Ok    ; If clear, then go ahead and use the APD_BUF

  LD  HL, (APD_FREE)   ; Find the amount of free APD_BUF
  LD  DE, $100         ; Compare to amount of memory needed (here, $100 bytes)
  CALL CP_HL_DE
  JR  NC, Ok           ; Go ahead and use the APD_BUF if enough.

  LD  HL, $0002        ; Display starting second row
  LD  (CURSOR_POS), HL
  LD  HL, UnloadStr    ; Display the UnloadStr
  ROM_CALL(D_ZT_STR)

  CALL CR_KHAND        ; Wait for a key
  RET                  ; Quit
Ok:
  .
  . Program
  .

UnloadStr   .DB "Please disable  "
            .DB "the interrupt   "
            .DB "that is running.", 0



 Installing an Interrupt Handler

  The following instructions will help you in writing your own interrupt
handler to run under CrASH.  Note that these things are ONLY necessary if you
intend to use APD_BUF to store your interrupt handler AND you want your
program to work even outside of your own program, including outside CrASH
totally.

  APD_BUF is the only location you can use acceptably for an interrupt
handler because of relocation.  Once your program exits, it will no longer be
in the same place, and *boom*.  Using APD_BUF has strings attached, because
other programs may feel like using APD_BUF.  For this reason, CrASH programs
MUST read the APD in use flag before touching APD memory.  Use this to your
advantage when writing interrupt handlers.

  To get CrASH programs to work correctly, you need to do the following:

- Copy your code to APD_BUF.  It simply will not work any other way, no
matter how hard you try.
- Check the INT_STATE variable in CrASH RAM (it's part of CrASH).  If this is
not 0, then you can't install another interrupt because one is in use already
in APD_BUF.  If your "ID" byte is in INT_STATE, your interrupt is already
loaded, though you may want to confirm another way.  INT_STATE is an 8 bit
variable.
- Write your ID value to INT_STATE.  Contact us to receive an official ID
number that does not conflict with any other.  Programs will now know that
an interrupt is in use.
- Copy your interrupt handler and table AS HIGH IN APD_BUF AS POSSIBLE.  The
more APD space you use, the less programs will work with your interrupt
handler.
- Write the number of bytes (16-bits long) still available to programs at
APD_FREE.  Any memory between and including APD_BUF and APD_BUF+APD_FREE-1 is
free game for ANY program.
- It's your job to set up the Z80 interrupt stuff.  Both the I table and your
handler must be in APD_BUF.
- Every time your interrupt is called, you must set INT_STATE to your ID byte
again.  This is because CrASH zeros it on startup.  It is not necessary to
set APD_FREE again.
- Write $74 to $800A.  This will prevent APD from occuring, removing the
TI-OS threat of overwriting.
 (except for mem-resets... but THEN again, you're trying to clear mem...)
- Restore all registers.  This is pretty obvious.  The "prime" registers -
the second set of registers with ' on them - are reserved for interrupt use.
Therefore, EXX is the simplest way to do this, though you can use other ways.
- Jump to the original interrupt handler at the end of yours.  This is $0038.
- When you remove your interrupt handler, load $00 into I, and set INT_STATE
to $00.  Though not required, please set APD_FREE to $300.

Try to test as many CrASH and ASH programs as possible when you write your
program.

You may also look in CrCrASH.ASM for some information and an example.

Currently registered ID bytes:
 $80 = Crash to CrASH
