83Plus:Hooks:Catalog
This page consists of combined documentation for both the Catalog 1 and Catalog 2 hooks. Hook equates can be found on each of those pages.
Contents
Overview
These two hooks are unique on the 83+, as they are, in many cases, the same hook. Although there are a few cases where the differ, in many cases the OS passes the same event to both hooks, one after another, effectivly resulting in the two hooks being chained. Consequently, it's best to document both hooks together, and also commenting on their interactions.
The TI Localization apps use the Catalog 2 hook for their activities (probably because the Catalog 2 hook also provides keypress notification, required to implement the Characters item it provides), so it would be possible to use the Catalog 1 hook to override some of their behaviors.
This document is formatted a little differently from normal, organizing by general event, not by hook specifics, and then discussing how each hook handles it.
Keypress Table
At the heart of the catalog hooks is the keypress table, which provides the ordering for the tokens in the list (obviously it's a good idea to presort the table for speed reasons.) Each entry in the table is two bytes long, the format depending on the whether the key is a one- or two-byte keypress:
- If it's a one byte keypress, then the first byte is zero, and the second byte is the value of the key.
- If it's a two byte keypress, the first byte is the prefix (FCh-FEh, but the OS seems to use FDh and FFh as well - anyone know why?), and the second byte is the other value.
In OS 1.16, the TI-OS stores this table on page 7. Obviously, expect this to change on different calculators and possibly different OS versions.
You can provide your own keypress table in a unique way. It's obvious that, inside the OS's implementation of the catalog, it would have to do something like "ld hl, table_start". However, after doing this, it always calls the Catalog hooks, allowing it to modify the value. It looks something like this internally (this is not the exact code, and is only for showing the theory):
ld hl, table_start ld a, 5 bit Catalog1HookActive, (iy + Catalog1HookFlag) call nz, CallCatalog1Hook bit Catalog2HookActive, (iy + Catalog2HookFlag) call nz, CallCatalog2Hook
where CallCatalog#Hook would return the registers and flags that the hook returned. Consequently, if we change HL in either routine, we can change the pointers the OS is using.
These pointers are pointers to entries within that keypress tables. It would seem that all we can do is just change the range of tokens displayed, but it turns out that the OS only actually reads the table in one situation: when displaying a token, otherwise it's just manipulating the pointers without actually knowing where they are pointing! The cool part is we are able to change how this read occurs. Before drawing an item, the Catalog Hooks are called, with the table pointer being passed in HL. If we want, we can point this somewhere else (say somewhere in RAM, TI uses sFont_record) where we can put our own item that we copied there (realize that the read is performed with page 7 in the 4000h-7FFFh range, so we can't keep anything in flash.) The other interesting side effect is that we can copy anything we want to RAM: we could generate a virtual table on the fly.
Therefore, we can completly create our own table with our own entries. We give our own start and end pointers (which can be on our page.) When the OS does read from this table, it gives us the pointer to the entry it is about to read. We then copy our own entry at this address to RAM, and change the entry pointer to RAM. This will result in our own entry being added.
Events
This is sorted by "events," a very non-technical term for something that the user does, or something the OS needs to do with the hooks. It's grouped this way instead of by inputs to each hook so I can document the interactions between the hooks.
Get Table Start
This event is passed to the Catalog 1 hook first.
Both Catalog Hooks: The OS wants the address of the keypress table. HL contains the address for the OS's table. If you want to provide your own table, change HL to its address.
Catalog 1 Hook Only: This message seems to get triggered in two cases:
- A = 05h. If you don't want the message to be passed to the Catalog 2 hook, return with the zero flag reset. If you modify A, realize that whatever value you change it to will be passed to the Catalog 2 hook (should you permit it.) Although this could be useful, if you don't want to break the Catalog 2 hook, make sure you set A back to 5.
- A = 0Ah. This is called under some unknown circumstances (anyone know why?), but it seems acceptable to treat it as A = 5. In this case, you don't have to worry about trashing A; if you permit the Catalog 2 hook to be called under this condition, the TI-OS sets A = 5 for you.
Catalog 2 Hook Only: A = 05h. HL may contain the address of the table provided by the Catalog 1 hook instead of the OS-provided address. Return flags are ignored.
Event: Get Table End
This event is passed to the Catalog 1 hook first. This event is similar to the previous event.
Both Catalog Hooks: A = 06h. The OS wants the address of the keypress table. DE contains the OS's value. If you want to provide your own address, simply set DE. Catalog 1 Hook Only: Returning with the zero flag reset prevents the Catalog 2 hook from processing the event. If you modify A, restore it as mentioned in the previous event. Catalog 2 Hook Only: DE may contain the address provided by the Catalog 1 hook as above. Return flags ignored.
Event: Get Last Screen
This event is passed to the Catalog 2 hook only, A = 2.
The user has pressed up on the topmost item, so we must scroll back around to the last item. Here, we must provide in HL the pointer to the first item that must be displayed (so when displaying the bottom of the list, the first item shown.) HL will be set to the predefined OS value, but you can overwrite it. Return flags are ignored.
===Event: Get Last Screen Item ----------------------------------------------
This event is passed to the Catalog 2 hook only, A = 3.
In the situation above, you must provide, in HL, the pointer to the item that will be selected. This will probably just be the pointer to the last item in the list, but you could make it different if you want. The return flags are ignored.
===Event: Get Item from Letter ----------------------------------------------
This event is passed to the Catalog 1 hook first.
Both Catalog Hooks: A will be 4. The user has pressed a letter key, and we need to return the address of the item to be selected. DE is set to (key - 41h) * 2, where key is the ASCII value of the key. It's somewhat expected that you store a list of addresses; this would seem to be the most efficient way to do it anyways.
Catalog 1 Hook Only: Returning with zero reset will immediatly use the value in HL as the address of the entry of the keypress table we should display. The catalog 2 hook will not be called. Returning with zero set could result in one of two things happening:
- the Catalog 2 hook is not installed, at which point the OS will call LdHLInd and use that as the pointer to the keypress table. It's assuming that, by returning with zero set, you have essentially ignored the message, and HL is pointing inside it's lookup table. Realize that if you return with zero set you shouldn't trash HL!
- the Catalog 2 hook is installed, and how to handle HL is its decision
Make sure you restore A should you trash it.
Catalog 2 Hook Only: DE or HL could be modified by the Catalog 1 hook. Returning with zero set will immediately use the pointer in HL as a pointer to the item to be displayed next. Returning with zero set will cause LdHLInd to be called, treating the result as a pointer in the keypress table.
===Event: Display Title -----------------------------------------------------
This event is passed to the Catalog 1 hook first.
Both Catalog Hooks: A = 7. The title of the catalog is about to be displayed, but this event can also be used as a generic catalog-is-open event (note: the Get Table Start event is called first!) There aren't any register inputs or outputs.
Catalog 1 Hook Only: If you modify A, make sure you set it back to 7, or the Catalog 2 app (if called) will not recieve the event properly. Return flags ignored.
Catalog 2 Hook Only: Returning with zero set causes the catalog to exit.
===Event: Display Item ------------------------------------------------------
This event is passed to the Catalog 1 hook first.
Both Catalog Hooks: HL is the address of the key in the keypress table. Remember, if you want to provide your own key / token, you must copy the keypress data into RAM, and point HL to it.
Catalog 1 Hook Only: The accumulator is set to 9 for this event. Returning with zero reset will prevent drawing this item, and the Catalog 2 Hook will not be called.
Catalog 2 Hook Only: The accumulator is set to 1 for this event. HL contains whatever the Catalog 1 hook provided, either the OS provided pointer or a custom pointer (if it was called.) Returning with zero reset will prevent drawing the item.
===Event: Key Press ---------------------------------------------------------
This event is passed to Catalog 2 only.
The accumulator is 8, B contains the keypress. You can modify it if you want. If you return with zero set, it will be canceled.
Note: If you want to look for up / down, then also check for kAlphaUp and kAlphaDown, since Alpha Lock is set by default in the catalog.