TSE II: Task Switching Enviroment (second edition)
By Robin Kay & Michael Vincent
All rights reserved worldwide

WEB: http://www.radicalsoft.org/tse
EMAIL: michael@radicalsoft.org
       dekiii@crosswinds.net

DISCLAIMER
I or anyone else that has any connection with this program takes no responsibility for anything this program does or doesn't do. Any damages to you, your property or your TI-83+ programmable graphing calculator that are acquired by using this program are not mine or anyone else's fault or responsibility. This program is to be distributed as freeware, no money may be charged for this program including any form of distribution fee. I cannot guarantee the accuracy of any of the information in this document.

** ABOUT THIS DOCUMENT

        This document is not designed to teach you how to write assembly programs for TSE, it is merely intended to be a reference document containing all the technical information about TSE that a programmer developing for it would need to know. It assumes a good understanding of Z80 assembler and of the TI-83+.

** THE TSE HEADER

.nolist               ;
#include "tse.inc"    ; TSE include file
.list                 ;

.org    userMem-3     ; Program origin
.db     $BB,$6D       ; Program variable header
 ret                  ; RET for TI-OS

.db "TSE",1           ; TSE Header
.db " Hello World", 0 ; Program Title
.dw 20                ; External Data Required

        The first block of code in the above header includes the TSE header file 'tse.inc'. This file is provided along with the TSE kernel distribution. The second block or specifies the origin of the program, note that it is userMem-3 rather than -2 for ION or TI-OS programs. The second line contains the identifier word that TI-OS uses to identify a varaiable as an assembly language program. The header also includes a ret instruction so that the program cannot be accidently run from TI-OS by mistake, note that you cannot choose to omit the ret.
        The third block of code is the actual TSE header. The first line contains the detection string, without this TSE will not recognize it as a valid TSE program. The second line is an ASCIIZ string (a null-terminated string) containing the name of the program. Note that the title MUST begin with a space otherwise the first character of the program name will be corrupted when the program is run. The third line is perhaps the most complicated, it specifies how much external data space is needed by program, this is explained below:-

** ALLOCATING EXTERNAL DATA SPACE

        The TI-83+ has many memory areas that are used by programs. The most important of these are the saferam areas, which have been traditionally used to store a program's variables. Obviously it is important that a paused program's variables are not corrupted by the currently running program, as would happen if they were using the same memory area to store data in. One solution to this would be for the TSE kernel to preserve the contents of all of the saferam areas when a program is paused, however this would require large amounts of free RAM per paused program to store all of this information. It would also be very inefficient as most programs only use a fraction of the available saferam memory. My solution was to have the kernel allocate an amount of RAM at the end of each program's variable as it is started, and to remove it once the task has been ended. This data structure is known as a task-block and is represented diagramatically below:-


| 0000h to 9D95h
-
^
| Program's code and data
-
^                          ^
| Task-block begins here   | Programs variables
| Size specified in header -
| .dw ? ;External data req ^
|                          | Stack grows down from here
-                          -
^
| 2 bytes, copy of stack pointer
-
^
| 60 bytes, copy of flags
-
^
| ????h to FFFFh

        You need to make sure that you have allocated enough external data space for both your program's variables and for its stack, otherwise the stack will overflow into your program's data or even into the program itself. Remember that you have to count not only the pushes onto the stack that your program does but the stack requirements of any TI-OS romcalls that you use, so be generous. Experiment, try and see how low you can set the 'External Data required' parameter without screwing up your program. Note that a stack overflow may not necessary cause your program to crash, but may cause bugs. You should thoroughly test your program to make sure that problems are not being caused by a stack overflow. Note that you don't have to allocate space for the copy of the stack pointer, or the copy of the flags. Below is a piece of sample pseudo-code showing how program variables are allocated:-

; TSE header and program goes here

extdata:
word1    .equ extdata   ; Program variables go here
word2    .equ word1 + 2
byte1    .equ word2 + 2
byte2    .equ byte1 + 1
.end

** IMPLEMENTING TASK SWITCHING

        TSE is co-operatively multi-tasking shell, that means that programs have to co-operate to multi-task properly. Programs are responsible for detecting the user's request for the program to yield (the pressing of the Stat key) and for correctly executing a task switch. This is my one attempt at standardization, programs MUST always have the Stat key as the one that causes a task switch. The one problem with ION programs was that the Quit key in one program, started the game again or cleared the highscore in another. Considering this is a fresh start I would like try to fix this for TSE and I would appreciate it if programmers wouldn't hinder my attempts to do so.
        The problem with task-switching is that the next program that is run will not preserve the state of any of the calculators shared resouces (ie. the screen), so the program must be able to restore it's state from where it left off. Below is a list of things a program can rely on remain the same, and things it can not:-

Reliable Resources
* Program variables in the task block
* The programs stack
* The system flags
* The program counter

Unreliable Resouces
* The screen and graph buffer
* The saferam areas
* The OPx registers
* The floating point stack
* The system registers

        So, the two most important things to remember are: 1) You cannot rely on the state of an unreliable resource after a task switch 2) The program must redraw the screen after a task switch. The best TSE programs will be able to task switch at any point during their execution, all the programs I have written do this. You should look at the soruce code for other TSE programs for an better idea of how to handle task switching. Below is a piece of sample pseudo-code to perform a task switch (You should access the keypad directly if it is to be used in a main game loop):-

 bcall(_GetCSC)
 cp skStat
 jr nz, dontYield

 ; Preserve any data that needs to be preserved
 ; Disable any IM2 handlers if necessary

 call _tseForceYield

 ; Restore any data that was preserved
 ; Redraw the screen 
 ; Re-Enable any IM2 handlers if in use
dontYield:

        One of the most important things to note about TSE programs is that there is no way to end task from within the program, you can't 'ret' back to the shell. When you want to quit, you task switch back to the shell and then end task from there. Trying to ret from a program will probably cause the calculator to crash. Note that you can exit the entire shell by calling _tseExitShell, or by BJUMPing to the system error handler, but this will still not end task.

** TEMPORARY DATA STORAGE

        If you need somewhere to store temporary data that the does not need to be kept between task switches, then there are several locations that you can use. However, it is important that you NEVER write to saferam1 or saferam2, as these areas are used to store the code for the kernel and libraries. Saferam areas 3, 4, 5 and the OPx registers are prefectly safe to use though. You can also you the 62 bytes at the end of the task block (see diagram above) for temporay data storage. Temporary variables are also OK to use, but you SHOULD be very careful about trying to keep them for longer becuase a program may have its task ended before it can mark them dirty.

** PROGRAM LIBRARY CALLS

        TSE provides 8 libraries for programs to use, 7 of these are the first 7 of the ION libraries that we all know and love. The 8th is an alternative to the 8th ION library which was not suitable for use in a multi-tasking enviroment. Below is a list of all of TSE's program libraries and their functions:-

        _tseLibsVer
Inputs    : None
Outputs   : hl = A 16bit value, each bit representing whether a function is
              present.
Destroys  : hl
Interrupts: Mode unaffected, Enable unaffected
Function  : Returns a 16bit value indicating which library functions can beused

        _tseRandom (also defined as ionRandom)
Inputs    : b  = The upper bound of a random number n<b
Outputs   : a  = A psuedo-random number based on the value of r
Destroys  : af b
Interrupts: Mode unaffected, Enable unaffected
Function  : Generates a pseudo random number where 0 <= a < b is true

        _tseSprite (also defined as ionPutSprite)
Inputs    : b  = The height of the sprite in pixels
            a  = The x co-ordinate in pixels the sprite is be placed at
            l  = The y co-ordinate in pixels the sprite is be placed at
            ix = A pointer to the beginning of sprite data in memory
Outputs   : ix = The byte after the end of the sprite data (next sprite?)
Destroys  : af bc de hl ix
Interrupts: Mode unaffected, Enable unaffected
Function  : XORs a sprite 8 pixels (1 byte) wide and b pixels tall onto the graph buffer.

        _tseLargeSprite (also defined as ionLargeSprite)
Inputs    : a  = The y co-ordinate in pixels the sprite is be placed at
            l  = The y co-ordinate in pixels the sprite is be placed at
            b  = The height of the sprite in pixels
            c  = The width of the sprite in bytes (pixels / 8)
            ix = A pointer to the beginning of sprite data in memory
Outputs   : ix = The byte after the end of the sprite data (next sprite?)
Destroys  : af bc de hl ix 'af
Interrupts: Mode unaffected, Interrupts enabled
Function  : XORs a sprite c bytes (c * 8 pixels) wide and b pixels tall onto the graph buffer. This routine is slower than _tseSprite

        _tseGetPixel (also defined as ionGetPixel)
Inputs    : a  = The x co-ordinate in pixels of the pixel to be got
            e  = The y co-ordinate in pixels of the pixel to be got
Outputs   : a  = A bitmask with the specified pixel set
          : hl = The address of the byte in the graph buffer containing the specified pixel
Destroys  : af bc de hl
Interrupts: Mode unaffected, Enable unaffected
Function  : Calculates an offset into the graph buffer pointing to the byte containing the specified pixel, and a bitmask to isolate the individual pixel from the rest of the byte.

        _tseFastCopy (also defined as ionFastCopy)
Inputs    : None
Outputs   : Copies the graph buffer on the the screen
Destroys  : af bc de hl
Interrupts: Mode unaffected, Interrupts enabled
Function  : Copies the graph buffer on the the screen

        _tseDecompress (also defined as ionDecompress)
Inputs    : hl = A pointer to the beginning of the compressed data in memory
            de = A pointer to a place in memory to deposit the data
            b  = The length in bytes of the compessed data
            c  = The compression factor, this can be 1,3 or 15
Outputs   : hl = The byte after the end of the compressed data in memory
Destroys  : af bc de hl
Interruts : Mode unaffected, Enable unaffected
Function  : Decompresses data that was compressed with Jow.W's compression scheme

        _tseFindVar
Inputs    : a  = A number from 1 to 255, each corresponding to a program variable in memory.
Outputs   : OP1= Contains the name of the variable
            zf = This flag is set if the program was found, clear if there aren't that many programs in memory.
Destroys  : af bc de hl
Interrupts: Mode unaffected, Enable unaffected
Function  : Searches the VAT and returns that name of the programs by its index

** KERNEL LIBRARY CALLS

        The TSE kernel has several internal functions that are made available for the benefit of shells. Execpt for _tseForceYield these functions are probably only relevant to shells. There are 7 kernel functions and they are documented below:-

        _tseKrnlVer
Inputs    : None
Outputs   : hl = A 16bit value, each bit representing whether a function is
              present.
            b  = The Major version number of the kernel
            c  = The Minor version number of the kernel
Destroys  : bc hl
Function  : Returns a 16bit value indicating which kernel functions can be used

        _tseChkProg
Inputs    : OP1= The name of a program variable to check
Outputs   : hl = A pointer to the title of the program if successful
          : a  = Kernel error code (see below)
Destroys  : af bc de hl
Function  : Checks to see if the program is a valid TSE program and returns a pointer to the title string of the program on success.
Notes     : If the program is archived then the first 256 bytes of the
            variable are streamed into saferam5. Error code 2 does not
            indicate failure for this kernel call.

        _tseExitShell
Inputs    : None
Outputs   : None
Destroys  : All
Function  : Cleanly shuts down TSE
Notes     : Does not end task, program can still resume! (unless it's a shell as the kernel ends the shell's task on shutdown). Note that the
            interrupt state is unknown after this call.

        _tseSwitchTask
Inputs    : OP1= The name of an active TSE program to switch to
Outputs   : None
Destroys  : All
Function  : Switches to another task
Notes     : Does not check to see if the task has been started, will cause problems (crash?) if it hasn't. Note that the interrupt state is
            unknown after this call.

        _tseStartTask
Inputs    : OP1= The name of a TSE program
Outputs   : a  = Kernel error code (see below)
Destroys  : af bc de hl
Function  : Starts a task

        _tseEndTask
Inputs    : OP1= The name of a TSE program
Outputs   : a  = Kernel error code (see below)
Destroys  : af bc de hl
Function  : Ends a task

        _tseForceYield
Inputs    : None
Outputs   : None
Destroys  : All
Function  : Switches task back to the shell
Notes     : Note that the interrupt state is unknown after this call

** KERNEL ERROR CODES

0 - Success
1 - Variable does not exist (All)
2 - Variable is stored in Flash-ROM (All/non-fatal for _tseChkProg)
3 - Not a valid TSE program (All)
4 - Task block does/doesn't exist (_tseStartTask/_tseEndTask)
5 - Insufficient memory (_tseStartTask)

** WRITING A REPLACMENT SHELL

        ION provides modules to allow you add functionality to it. With TSE you can rewrite the entire user interface. The last nine bytes of 'TSEKRNL' contains the name of the shell to be loaded on startup. The source code for a program called CHSHUT is provided, this programs configures the last nine bytes of TSEKRNL to start Utopia on startup. You may use is as the basis for your shell's setup program.

** THE KERNEL MEMORY STRUCTURE

        While most of your dealing with the kernel will be with function calls, I have made public a few of the addresses for kernel variables. These may be useful for some programs. Here is list of all the variables in the kernel memory structure:-

        pptr_code
The word variable at this address points to the first byte of code in the active program.

        pptr_preserve
The word variable at this address points to the first byte of the task-block of the active program.

        progsize
The word variable at this address contains the size of the active program, note that this is 3 bytes less than the variable size.

        sysstack
The word variable at this address points to the system stack pointer. This is useful if you want to temporarily borrow the system stack for some stack
intensive operation. You also need to borrow the system stack if you want to call system routines that swap page 2 otherwise the calculator will crash.

        sysflags
When TSE starts it makes a copy of the system flags starting at 89F0h, and carrying on for 60 bytes (the last flag being plotFlag3). This copy of the flags is used to restore the system flags when TSE exits, these are also the flags that attributed to each new task. If you want changes to the flags to be permanent then you will have to change this.

        cprogram
This variable contains the nine byte long name of the current program.

        fptr_xxx
After making a call to _tseChkProg (or to _tseStartTask or _tseEndTask, which call _tseChkProg themselves), the fptr's are set. The are pointers to parts of the variable that was checked. They point to the variable size (fptr_varsize), the program title (fptr_prgtitle), the memory required for external data (fptr_memreq), and the end of the variable (fptr_end).