z80:Multi-Page Apps

From Learn @ Cemetech
Jump to: navigation, search

The biggest advantage of flash applications over regular assembly programs is the ability to create absolutely massive projects. However, because of the way the hardware is set up, you have to make some changes in your code to accommodate the hardware limitations. This page will explain these changes and how to properly set up your code to make everything and everyone happy.

Paging

On the TI-83+/84+ series of calculators, in order to access the full range of memory in a 16-bit address space, we must map in different pages into different memory banks. The typical way to do this is to write the given page number to port 6 or 7.

A page is a 16384 byte chunk of memory (1/4th of the total 16-bit address space) and these consist of mostly flash pages and some RAM pages.

Every app will be put on its own flash page and no matter how much unused space there is, it will always use a multiple of 16384 bytes. There are three memory banks that are typically accessible that map to the addresses 4000h to 7FFFh, 8000h to $BFFF, and $C000 to $FFFF (the first 16KB is necessary for the OS). When the OS opens an application, the first page is mapped to 4000h. RAM is mapped from 8000h to $FFFF. In order to access off-page data, you can either map it in place of RAM by writing the given page to port 7, or you can write to port 6 to remap the memory at 4000h to 7FFFh. But wait! If your APP is there, you are going to have a hard time, so you will need to make use of the OS bcall() routine or write your own routine in RAM to handle page swapping and restoring.

Page swapping can be a bit of a pain, but once you have the routines or a method you understand, it can be relatively seamless.

For the purpose of this page, we will not discuss the organisation of flash pages beyond the realm of applications.

Changes in the Header

There is an element in the header that denotes the number of flash pages your flash app contains. Depending on your assembler/compiler, you may or may not need to change this.

Page declaration

[! This is true for Brass/Latenite, someone please check to see if this is universal, or what it is using different compilers]

At the start of each page, it is important to tell the compiler you are on a certain page. This is done using the .page # directive. Pages start counting from 0.

   ;ex:
   .page 0
   ; page0 code
   .page 1
   ; page1 code
   ...


Change in 'Memory Counter'

Instead of using .org $9D93, use .org $4000. This is because the Flash address is mapped starting at $4000. Also, the asmprgm token can be omitted (the .db $BB,$6D).

Jump Table

For applications, there are two main types of jump table. The first is a typical jump table that directly jumps to a routine on that page. These are used mostly for shells and other apps that provide a suite of routines for other assembly programs. The other is a lookup table (LUT) that the OS can use for its bcall() routine. This makes it much easier for you to call off-page routines and takes away the brunt of the coding nightmare involved. The format that the OS requires is:

   .dw address    ;relative to 4000h (as if the page was already swapped in there)
   .db offset      ;On which of your pages does the routine reside (0 if it is on the main page).

Then in order to use bcall, provide the address to the three bytes of data and subtract 4000h from that. An example is here:


   ; example of a simple multi-page app
   .org $4000
   .page 0
   ;app header
   .block 128
   
    jp main     ; skip over the jump table to the start of the program
   
   ;bcall LUT
   _func1  equ $-4000h
        .dw func1
        .db 1
   _myPutS  equ $-4000h
        .dw myPutS
        .db 1
   
   main:
        bcall(_func1)      ;call func1
        ld hl,appbackupscreen
        bcall(_myPutS)
        bcall(_JForceCmdNoChar)
   
   .page 1
   
   ; copy txt1 to appbackupscreen
   func1:
    ld de,txt1
    ld hl,appbackupscreen
    ld bc,13
    ldir
    ret
   
   ; PutS, except included into our program
   myPutS:
    push bc
    push af
    ld a,(winBtm)
    ld b,a
   PutS10:
    LD A,(HL) ; get a character of string name
    INC HL
    OR A ; end of string?
    SCF ; indicate entire string was
    ; displayed
    JR Z, PutS20 ; yes --->
    B_CALL PutC ; display one character of string
    ;
    LD A,(curRow) ; check cursor position
    CP B ; off end of window?
    JR C,PutS10 ; no, display rest of string
   PutS20:
    POP BC ; restore A (but not F)
    LD A,B
    POP BC ; restore BC
    RET
   
   txt1:
    .db "Hello world!",0