Messing with Variable Data

z80 » Variables

This is my favorite section in the entire Guide. I've referred back to it several times so pay attention!

Getting to the Data

A lot of times you need to access the data in TI86 variables. One good example of this is if your program is larger than 9k. The TI86 will only handle 9k or less asm programs, so you'll have to break up your program into several 9k or less parts...or you can just make an asm program that starts executing a string (which can be any length) and then returns to the asm program.

First you have to use _FindSym to locate the file. _FindSym's output puts an ABS pointer in bde. All the routines we'll be using to access this will use ahl so we need to do two commands to put bde into ahl

	ex de,hl
	ld a,b

This can be further simplified by using _Ex_ahl_bde=$45f3. This is one byte longer since it is a call but will still retain a instead of writing b over it.

I hate trying to assemble someone else's code and getting all these error messages dealing with the include files. That is why I've pasted the equates for many of the ROM Calls used at the top of the routine. Their addresses are also pasted in the comments.

Since there's no step by step process to learn this section which I can lay out for you, I'm just going to go through some examples. You can do anything with TI-OS variables, so adapt these routines to what you want to do.

Stupid Strings

Let's make a routine called StupStr.asm which will create a string and store a sentence to it.
#include "ti86asm.inc"		;normal include file
				; with common ROM calls
_Ex_ahl_bde		=$45f3	;like 'ex bde,ahl'
_ahl_Plus_2_Pg3		=$4c3f	;ahl=ahl+2
_Set_ABS_Dest_Addr	=$5285
_Set_ABS_Src_Addr	=$4647
_Set_MM_Num_Bytes	=$464f
_MM_Ldir		=$52ed
.org _asm_exec_ram
	ld hl,string-1		;copy anything before string
				; for type byte
	rst 20h			;call _Mov10toOP1
	rst 10h			;call _FindSym to see if
				; variable exists
	call nc,_delvar		;delete variable if it exists
				;all necessary info still
				; preserved
	ld hl,end-start		;minus start from end of string
				; so result is length of string
	call _CreateSTRNG	;$472f  create string
	call _ex_ahl_bde	;$45f3  ABS bde &  ahl swapped
	call _AHL_Plus_2_Pg3	;$4c3f  increase ABS ahl by two
	call _Set_ABS_Dest_Addr	;$5285  set that as
				; destination for block copy
	sub a			;AKA ld a,0
	ld hl,start		;hl points to string_start
				;address of string is in
				; relation to 16 bit
				; and ram page
	call _Set_ABS_Src_Addr	;$4647  set that as source
				; for block copy
	sub a			;AKA ld a,0 -it's on already
				; swapped in page (0)
	ld hl,end-start		;length of string
	call _Set_MM_Num_Bytes	;$464f  set # of bytes to copy
				; in block copy
	jp _MM_Ldir		;$52ed  ABS block copy
				;we jump instead of calling
				; and returning
				;this way we just use
				; _MM_Ldir's ret as ours
				;saves 1 byte

string:	.db 6,"Stupid"		;length indexed string name
start:	.db "This is a stupid string."
end:
.end

This routine creates a String named "Stupid" and puts "This is a stupid string" as its data.

  1. We first load the variable name into OP1 and the length into hl and create it.
  2. Then we put the ABS pointer bde (what _CreateSTRNG returned) into ahl.
  3. Increase it by two to get past the Length Word
  4. Set that as where we want to ABS block copy to.
  5. Then we put the address of our string into the ABS block copy source.
  6. Load a with zero because our string is in the 64k of bytes currently available. We can access it with a 16 bit pointer because it is on a page that is swapped in (all asm programs are put into an area on RAM page 0 which is always swapped in).
  7. Then we get the length of the string and put that into the number of bytes to ABS block copy.
  8. Perform the block copy. We jump to the block copy routine because we know we're going to return when we get back so we might as well just use that routine's return for ours. This is something to remember when you're optimizing code because it saves 1 byte and may be a little faster or something.

High Scores

Uh, Oh! TI-OS doesn't save your high scores. You need to copy them to the program as explained in the game saved variables section.

A major thing to remember when you're creating programs is that the first byte of the data (after the two bytes for the length of the program) there is a byte telling whether the program has been tokenized or not. It will be $00 if it is untokenized, or $33 if it has been tokenized.

_asapvar		=$d6fc	;name of the currently
				; running program (this
				; one!)
_set_abs_src_addr	=$4647
_set_abs_dest_addr	=$5285
_set_mm_num_bytes	=$464f
_mm_ldir		=$52ed
	. . .
	. . .
save_game:
	ld hl,_asapvar			;name of current
					; program running
	rst 20h				;copy to OP1
	rst 10h				;_findsym
	ld a,b				;returns bde as prog data
	ld hl,data_start-_asm_exec_ram+2+2
					;offset in this program
					; for start
					; of saved game data
					;2 bytes for prog length
					;2 bytes for asm
					; identifier bytes
	add hl,de			;hl=pointer to data in
					; original prog
	adc a,$00			;in case we overlapped pages
					;if carry was set in previous
					; instruction...this will
					; add 1 to a else...it
					; adds 0 to a
	call _Load_RAM_ahl		;$462f - convert ahl to a
					; RAM page and 16 bit pointer
	ld a,(high_score)		;get high_score from
					; this program
	ld (hl),a			;store it into the
					; actual program's
					; data
	ret
	. . .
	. . .
high_score:		.db 0		;high score in game

What we did was:

  1. Got the address of our program was in the variable memory on the TI86 (not where it is stored at _asm_exec_ram on execution).
  2. Then we added the offset of the high score address to the start of the program. We had to change that into a 16 bit pointer. Then we were able to save the high score data located in our program at _asm_exec_ram into the accumulator (a) and then put that into the high score of the program on the swapped in RAM page.

Suppose you wanted to not just copy one byte, but several. Here's an example of copying several bytes of program data back to the original program in memory. It's the Write Back routine discussed in the Intermediate Variables Section.

_asapvar		=$d6fc
_set_abs_src_addr	=$4647
_set_abs_dest_addr	=$5285
_set_mm_num_bytes	=$464f
_mm_ldir		=$52ed
save_game:
	ld hl,_asapvar			;name of current
					; program running
	rst 20h				;copy to OP1
	rst 10h				;_findsym
	ld a,b				;returns bde as prog data
	ld hl,data_start-_asm_exec_ram+2+2
					;offset in this program
					; for start
					; of saved game data
					;2 bytes for prog length
					;2 bytes for asm
					; identifier bytes
	add hl,de			;hl=pointer to data
					; in original prog
	adc a,$00			;in case we overlapped
					; pages if carry was set
					; in previous
					; instruction...this
					; will add 1 to a
					; else...it adds 0 to a
	call _set_abs_dest_addr		;set that as the absolute
					; destination address
					; to copy to
	sub a				;no absolute addressing now
	ld hl,data_start		;*get data from here
	call _set_abs_src_addr		;set that as the absolute
					; source address to copy
					; from
	ld hl,data_end-data_start	;*number of bytes to save
	call _set_mm_num_bytes		;set that as the number
					; of bytes to copy
	jp _mm_ldir			;perform absolute address
					; data block copy

data_start:				;start of data to copy
high_score:		.db 0		;high score
high_score_initials:	.db "AAA",0	;initials of high score
					; holder
data_end:

Lists

Now, let's make a list named "Stupid" with 2 elements in it. You can download the program StupLst.
#include "ti86asm.inc"		;normal include file with
				; common ROM calls
_Ex_ahl_bde		=$45f3	;like 'ex bde,ahl'
_ahl_Plus_2_Pg3		=$4c3f	;ahl=ahl+2
_Set_ABS_Dest_Addr	=$5285
_Set_ABS_Src_Addr	=$4647
_Set_MM_Num_Bytes	=$464f
_MM_Ldir		=$52ed
.org _asm_exec_ram
	ld hl,list-1		;copy anything before
				; list for type byte
	rst 20h			;call _Mov10toOP1
	rst 10h			;call _FindSym to see if
				; variable exists
	call nc,_delvar		;delete variable if it
				; exists all necessary
				; info still preserved
	ld hl,2			;2 elements
	call _CreateRLIST	;create real list (as
				; opposed to complex)
	call _ex_ahl_bde	;$45f3  ABS bde &  ahl
				; swapped
	call _ahl_Plus_2_Pg3	;get paste element # byte
	call _Set_ABS_Dest_Addr	;$5285  set that as
				; destination for
				; block copy
	sub a			;AKA ld a,0
	ld hl,start		;hl points to list_start
				;address of list is
				; in relation to 16 bit
				; and ram page
	call _Set_ABS_Src_Addr	;$4647  set that as
				; source for block copy
	sub a			;AKA ld a,0 -it's on
				; already swapped in
				; page (0)
	ld hl,end-start		;length of list
	call _Set_MM_Num_Bytes	;$464f  set # of bytes
				; to copy in block copy
	jp _MM_Ldir		;$52ed  ABS block copy
				;we jump instead of
				; calling and returning
				;this way we just use
				; _MM_Ldir's ret as ours
				;saves 1 byte

list:	.db 6,"Stupid"		;length indexed list name
start:
	.db $00,$00, $fc, $10, $00, $00, $00, $00, $00, $00
	.db $00,$00, $fc, $20, $00, $00, $00, $00, $00, $00
end:
.end

Matrices

Check out how to make matrices in StupMat.
#include "ti86asm.inc"		;normal include file
				; with common ROM calls
_Ex_ahl_bde		=$45f3	;like 'ex bde,ahl'
_ahl_Plus_2_Pg3		=$4c3f	;ahl=ahl+2
_Set_ABS_Dest_Addr	=$5285
_Set_ABS_Src_Addr	=$4647
_Set_MM_Num_Bytes	=$464f
_MM_Ldir		=$52ed
.org _asm_exec_ram
	ld hl,matrix-1		;copy anything before
				; matrix for type byte
	rst 20h			;call _Mov10toOP1
	rst 10h			;call _FindSym to see
				; if variable exists
	call nc,_delvar		;delete variable if it
				; exists
				;all necessary info
				; still preserved
	ld hl,1*256+2		;1 row, 2 columns
	call _CreateRMAT	;create real matrix
				; (as opposed to complex)
	call _ex_ahl_bde	;$45f3  ABS bde &  ahl
				; swapped
	call _ahl_Plus_2_Pg3	;get paste row byte
				; and col byte
	call _Set_ABS_Dest_Addr	;$5285  set that as
				; destination for
				; block copy
	sub a			;AKA ld a,0
	ld hl,start		;hl points to matrix_start
				;address of matrix is
				; in relation to 16 bit
				; and ram page
	call _Set_ABS_Src_Addr	;$4647  set that as
				; source for block copy
	sub a			;AKA ld a,0 -it's on
				; already swapped
				; in page (0)
	ld hl,end-start		;length of matrix
	call _Set_MM_Num_Bytes	;$464f  set # of bytes
				; to copy in block copy
	jp _MM_Ldir		;$52ed  ABS block copy
				;we jump instead of
				; calling and returning
				;this way we just use
				; _MM_Ldir's ret as ours
				;saves 1 byte

matrix:	.db 6,"Stupid"		;length indexed matrix name
start:
	.db $00,$00, $fc, $10, $00, $00, $00, $00, $00, $00
	.db $00,$00, $fc, $20, $00, $00, $00, $00, $00, $00
end:
.end

Every Variable

I hope that's all the examples you need. If you didn't catch it above, you can download all the source files for these routines:

Remember that if you want to make a complex type of the above variables, create the complex form of the variable with double the size. You're going to have to put put the real part with the complex part following. Here's the data for a complex list with two elements.

complex_list:
start:
		;element 1 = (1,1)
	.db $01, $00, $fc, $10, $00, $00, $00, $00, $00, $00
	.db $01, $00, $fc, $10, $00, $00, $00, $00, $00, $00
		;element 2 = (2,2)
	.db $01, $00, $fc, $20, $00, $00, $00, $00, $00, $00
	.db $01, $00, $fc, $20, $00, $00, $00, $00, $00, $00
end:

More information on real and complex numbers can be found in the Binary Coded Decimal Section.


More from z80 » Variables
Absolute Addressing // Binary Coded Decimal // Creating Variables // External Levels // _FindSym // Messing with Variable Data // OP Math // TI-OS Variable Manipulation // Variable Name Format // VAT Searches