83Plus:OS:Variable Storage in the User Archive
The SDK doesn't have much information on this. The SDK mostly claims that you can't access in data in the archive without unarchiving it; this is untrue, for you can in fact access the data using FlashToRAM. But before that can work, you need to understand how data is stored in flash.
The user archive differs between the 83+, 83+SE, 84+, and 84+SE. The "archive" for programs, appvars, groups, lists, REALs, matrices, and every other data type grow upwards from page 08h. On the TI-83+, the last page of the archive is page 15h; on the TI-84+, the last page is page 29h; and on the TI-83+SE and TI-84, the last page is page 69h. You can read data from the archive using FlashToRAM, or though paging with port 6.
The flash memory is divided into sectors of four pages. The flash chip can program any single bit from a 1 to a 0 at any time, but erasing from a 0 to a 1 can only be done on a per-sector basis. Each sector has a byte at the very beginning (4000h, pages 08h, 0Ch, etc.) which holds the status of that sector. The following values mean it is valid:
The swap sector is also stored in this area, and it can be any full 64K sector. It is identified with the byte 0FEh.
After the status byte for that sector, the archived data begins. The 83+ SDK has some information on this, but it's repeated here. Each variable entry begins with three bytes:
Flag | sizeLow | sizeHigh
The flag byte will be 0FCh (valid) or 0F0h (deleted, ignore it). (It may also 0FEh, meaning that the OS was still in the process of writing to it when you got control.) The size word can be used to skip to the next variable. After this will be the variable's VAT entry. Note that the VAT entries stored in RAM are stored backwards, but the VAT entries stored in flash are stored forwards.
T | T2 | Version | addressLow | addressHigh | Page | Name Length | (Name)
The address and page of this entry points to the flag byte at the start of the entry, not the start of the variable's data. Don't forget, most data types start with a size word, which will appear directly after the name.
Note that data can span multiple pages, but cannot cross sector boundries.
This will search for a program, appvar, or group in the archive, and, if in the archive, return the amount of data in the item and location of the data. This code should work.
; Inputs: ; - OP1: name of variable ; Outputs: ; - A: Page data is on ; - HL: Pointer to data ; - DE: Size of data ; - T1 and T2 can be found at 8003h and 8004h. B_CALL ChkFindSym jr c, errorNotFound ld a, b or a jr z, itemIsInRAM push de ex de, hl ld de, 8000h ld bc, 30 B_CALL FlashToRAM ld a, (8009h) ld hl, 800Ah add l, a ; note that you must change this to a 16-bit add if you move the above address ld e, (hl) inc hl ld d, (hl) ld a, (8008h) pop bc res 7, h add hl, bc bit 7, h ret z res 7, h inc a ret errorNotFound: ; Oh no! itemIsInRAM: ; Getting the data is now much easier.
Adding to Archive
Start at page 08h and access the status byte. If it is 0FEh, skip to the next sector (4 pages). If you find any other status byte, abort.
Once you've found a valid sector, start reading each entry by checking the status byte (0FCh for exists, 0F0h for deleted). Then read the size and add it to your current location to find the next VAT entry (it can and will span to the next page).
Whenever you find 0FFh, stop and add an appropriate VAT entry and data. Make sure there's enough space left on the sector to write your data.
Don't write directly to flash unless you know what you're doing.
Applications are stored at the top of the archive and grow downward. On the 83+, the top page is 15h; on the 84+, it is 29h, and on the 83+SE/84+SE it is at 69h. The following status bytes are used on each base page:
0FFh = empty. 080h = valid Flash application (this is part of the app header). 000h = deleted Flash application (this is part of the app header).
If there is one multipage application on the 84+SE, for example, then the first byte of page 69h is 080h, and page 68h will contain the second page of the application. THE FIRST BYTE OF THIS PAGE HAS NO MEANING. To traverse this, you must access the Flash application's number of pages on the base page by parsing the application's header with the field search routines and use this to go backwards a certain number of pages.
Determining the "last" app page is rather difficult. If a page begins with FFh (or any other value other than 80h or 00h), it cannot be the start of an application. If a page begins with 80h or 00h, followed by 0Fh, it might be the start of an application. Of course, since archived variables can contain any sort of data you like, there is no way to tell for sure whether you're looking at an app or an archived variable, but for most purposes, it's probably safe to assume that if it looks like an app, it is one.
(The definitive answer to "is this an app or not?" can only be found on the certificate page, which is unreadable by user programs. At the same time, many OS routines do make the assumption that "if it looks like an app, it is one".)
Note that applications and archive data are never allowed to occupy the same sector.
The following code will walk through the list of installed applications.
; Determine first application page ld b,15h in a,(2) ; is calculator a TI-83+ BE? add a,a jr nc,traverse_apps_loop ld b,29h in a,(21h) ; is calculator a TI-84+ BE? and 3 jr z,traverse_apps_loop ld b,69h traverse_apps_loop: ; Check if page B is a possible starting app page ld hl,4000h B_CALL LoadDEIndPaged ld a,e and 7Fh jr nz,traverse_apps_done ld a,d cp 0Fh jr nz,traverse_apps_done ; Check for page count field in app header ld a,b B_CALL FindAppNumPages sub c cp b jr nc,traverse_apps_done ; Page B is the start of a (possibly deleted) app ; C is the number of pages ; A (= B - C) is the next possible app page ld b,a jr traverse_apps_loop traverse_apps_done:
Credits and Contributions
- Brandon Wilson, for originally writing most of this up.