First of all, if you manage to type something in wrong it can hard lock the calculator, and you may have to pop the batteries, and lose everything on the calculator. So don't try typing assembly language programs unless losing everything on the calculator is acceptable.
Some basic facts about the Ti-86. It uses the Z-80 processor. Because it is difficult to write relocatable assembly language programs, the Ti-86 always relocates them to 0D748h. Two useful routines are _clrScrn at 4A82h and _puts at 4A37h
Google Gemini wrote the following assembly code and compiled it (note that Google Gemini did make a mistake once, which hard locked the calculator, so again, don't do this if there is anything on the calculator you care about losing).
CD 82 4A ; CALL $4A82 21 52 D7 ; LD HL, $D752 CD 37 4A ; CALL $4A37 C9 ; RET 48 45 4C 4C 4F 00 ; DB "HELLO\0"
Next, we need to get it into the calculator, edit a program:
PROGRAM:ASMHELLO AsmPrgm CD824A 2152D7 CD374A C9 48454C4C4F00
Then run this with: Asm(ASMHELLO)
You should get HELLO Done printed on the screen. Note that this was tested on a calculator with a rom version of 1.6
The start address and the calling routine numbers were found at https://github.com/abbrev/ti-86-asm/blob/db010670f8aad1509633b01d39c0c563e4ee7fb0/stranded/ti86asm.inc
Some notes Gemini wrote for readers of this post:
🧠 The Z80 Architecture: Why We Use HL
The Zilog Z80 processor, a 1970s workhorse, forms the brain of the TI-86. To understand the program, we need to look at its core components, especially its registers.
Our program uses the HL register pair. The 21 opcode for LD HL, nnnn is specifically designed to load a 16-bit memory address. This is critical because the _puts system routine (4A37h) is hardwired by the TI-86 operating system to look only at the HL register to find the starting address of the string it needs to print.
⚙️ Deconstructing the Hex: What the CPU Sees
Typing in hex means we are speaking the Z80's native machine language. Here is a breakdown of the "magic numbers" in our code:
- CD 82 4A (CALL $4A82): CD is the opcode for CALL. The address 4A82h is written in the Z80’s little-endian format (low byte first) as 82 4A. This is the absolute jump to the OS’s _clrScrn routine.
- 21 52 D7 (LD HL, $D752): 21 is the opcode for "Load 16-bit value into HL." The address D752h is where our string data starts, calculated from the program's load address 0D748h.
- CD 37 4A (CALL $4A37): Another CALL, this one jumps to the _puts routine at 4A37h to draw characters to the display buffer, starting from the address currently stored in HL.
- C9 (RET): This is the most crucial byte for stability. C9 is the opcode for Return, which safely hands execution control back to the Asm( shell, preventing a calculator lockup.
- 48 45...00: These are the ASCII codes for 'H', 'E', 'L', 'L', 'O', and finally, the null terminator (00), which tells _puts when to stop printing.
🛑 The Hex Trap: Why Relocatable Code Fails Here
Our program relies on absolute addressing, which is not ideal but necessary. We tried to write relocatable code using the Z80's "Pop the PC" technique (involving CALL followed by POP HL), but that method has a hidden requirement:
It needs a Relocating Loader (a feature of advanced assemblers) to fix the address inside the CALL instruction at runtime. Since the TI-86's hex editor is primitive, it loads the CALL operand as an unfixed absolute address, which would always jump to the wrong place ($0009 in our test) and crash the calculator.
Therefore, we are forced to rely on the fact that the shell always loads programs at 0D748h to calculate the string pointer. This makes the code fast and functional, but brittle—if the TI OS ever changes that load address, the program will break.