Omnimaga

Calculator Community => TI Calculators => ASM => Topic started by: Jerros on August 01, 2010, 08:35:24 am

Title: My final border: How to create/work with AppVars?
Post by: Jerros on August 01, 2010, 08:35:24 am
So my game is as good as complete, the last thing I must do is to be able to permanently save data.
I've read the AppVar part in Developpers Guide, but I just lack the knowledge to comprehend it all.
My question remains: How do I store simple data (just numbers, that's all) so that it can be recalled the next time someone runs the App?
All I really want is things like:
Code: [Select]
Hi-Score:
         .db       496
Achievements:
         .db       1
Whatever:
         .db       9999
but then to be saved after the programm is exited, and able to recall later on.
Would be perfect if the AppVar gets archived when the programm is closed, but that part shouldn't be hard and I can figure that out myself.

So... anyone has an easy way to save/recall numbers in Apps?
Title: Re: My final border: How to create/work with AppVars?
Post by: _player1537 on August 01, 2010, 10:10:14 am
Ok, let me try to explain... (didn't look this up, but it should be very close)

Code: [Select]
ld hl, AppVarName     ;ld hl with the string for the name of the appvar
rst 20h ;move 9 to op1, takes the data at hl, and moves it into op1


ld hl, [size of appvar]  ;size of appvar, self explanitory
bcall _createappvar   ;this creates an appvar with the name at HL

ld hl, de  ;de points to the data structure for the newly created appvar

inc hl \ inc hl   ;iirc HL starts at the spot where the size bytes are, so you need to inc hl past them
ld (hl), first byte of data  
inc hl
ld (hl), second byte of data

;etc, I'm sure there is a better routine to do that than lding data at hl, and incrementing though >.<

ret

AppVarName:
  .db 06h,[some other byte, sorry I'll look it up in a second],"AppvarAA",0 ;06h is the appvar data type byte iirc

Like I said, I'm going to look in the SDK guide real quick to confirm this

Edit: glad I went back and checked.  I'm editing the code above to make it correct.  Also, remember when you create the appvar, it will be filled with random data.  I'll edit again to show how to access the appvar after its been created
Title: Re: My final border: How to create/work with AppVars?
Post by: thepenguin77 on August 01, 2010, 12:38:47 pm
_player that is all correct except the type is 15h.

@Jerros, here is a nice routine to get you to the start of the appVar. It will create it if it doesn't exist, and it will unarchive it if it's archived.
Code: [Select]
getAppVar:
ld hl, appVarName
rst 20h ;move9ToOp1
bcall(_chkFindSym)
jr nc, found

ld hl, size
push hl
bcall(_createAppVar)

inc de ;skip size bytes
inc de
ex de, hl
pop bc
bcall(_clearMem) ;fills hl with bc 0's
jr getAppVar
found:
ld a, b
or a
jr z, inRam
bcall(_arc_unarc)
jr getAppVar
inRam:
inc de
inc de
ret

appVarName:
.db appVarObj, "appVar", 0, 0
;  15h

Just be sure to archive it when you program quits.

Edit:
   The appVar is just like regular ram, you can use it however you see fit. Just be sure that you use it the same way consistently. Like, don't save the first byte as highscore, and then read the first byte as achievements.
Title: Re: My final border: How to create/work with AppVars?
Post by: _player1537 on August 01, 2010, 12:43:15 pm
*facepalm* that was on my list of things to change in my code... I forgot haha.  Oh, and you have to make sure (I think) that you end up with the number of 0s to equal 8.  IE for the appvar name AB, you would need to end it with 6 0s.  That was @jerros incase he wanted to use another name.  I think that's correct...
Title: Re: My final border: How to create/work with AppVars?
Post by: thepenguin77 on August 01, 2010, 12:45:42 pm
I've found you actually don't have to fill it with 0's. While it makes your code look pretty and makes it more logical, you only really need one 0. Basically the rst would grab your name and whatever was after it, but it doesn't matter because you already stopped it with 0.
Title: Re: My final border: How to create/work with AppVars?
Post by: Jerros on August 02, 2010, 02:48:35 am
@Jerros, here is a nice routine to get you to the start of the appVar. It will create it if it doesn't exist, and it will unarchive it if it's archived.
Code: [Select]
getAppVar:
ld hl, appVarName
rst 20h ;move9ToOp1
bcall(_chkFindSym)
jr nc, found

ld hl, size
push hl
bcall(_createAppVar)

inc de ;skip size bytes
inc de
ex de, hl
pop bc
bcall(_clearMem) ;fills hl with bc 0's
jr getAppVar
found:
ld a, b
or a
jr z, inRam
bcall(_arc_unarc)
jr getAppVar
inRam:
inc de
inc de
ret

appVarName:
.db appVarObj, "appVar", 0, 0
;  15h
Just be sure to archive it when you program quits.

Yay!
Thanks, I'll mess around with this today, if I stumble across any problems I'll post that here.
I hope I get the hang of this fast, then I can release my game : )

EDIT:
bcall(_createAppVar)
Code: [Select]
bcall(_clearMem) ;fills hl with bc 0's

Erm... you mean b_call _Memclear?
ClearMem doesn't get reconized by my assembler, and using    b_call _MemClear   will just crash the calc.
Also, just removing that line will crash the lot too ;)

Code: [Select]
getAppVar:
ld hl, CHData
rst 20h
bcall(_chkFindSym)
jr nc, found ; AppVar exists
ld hl, 500 ; Size of AppVar, random.
bcall(_createAppVar)

JR RestOfProgramm

found: ; Unarchive if not already
   ld hl, CHData
   rst 20h
   bcall(_chkfindsym)
   ld a, b
   or a
   bcallnz(_arc_unarc)   ;only unrchive if not already in RAM

jr RestOfProgramm
CHData:
.db appVarObj, "CHData  ", 0, 0

RestOfProgramm:
...
...
...
quit:
   ld hl, CHData
   rst 20h        
   bcall(_chkfindsym)
   ld a, b
   or a            
   bcallz(_arc_unarc)
b_call _JforceCmdNoChaR
The above is what I'm doing now and works.
It creates an AppVar if none exists already, and unarchives it if it's archived.
Also Archives it again upon exiting.
Though I'm still not sure on how to store/recall data from it.
Also, I just took an arbitrary size, I don't know how much is needed (just some numbers between 0-9999 won't take up much I suppose...).
Title: Re: My final border: How to create/work with AppVars?
Post by: thepenguin77 on August 02, 2010, 02:55:25 pm
Sorry about that, _memClear is in my mind as _clearMem, I don't know why I always mix them up. As long as you give it an HL that is not in front of your program, and a bc that is >=2 it won't crash.

As for accessing them, I'll draw a picture.

Here is ram before.
prgmA | prgmB | MirageOS appvar | prgmDESTROY | etc....

When you call make appVar, the OS makes a hole in the programs for yours to go, so:
prgmA | prgmB |   500 + 2 byte hole   | MirageOS appvar | prgmDESTROY | etc....
                    ^
Then when you call chkFindSym on it, it will tell you that yes, your appVar exists, and that you can find it's start in DE. DE is now pointing to that line in front of the hole. The first thing you have to do is skip two byte, these two byte are how big of a hole was created. Now you have 500 bytes to do whatever you want with.

To access it, you have to do it through hl or de. ld (appVar), hl won't work because at assembly time, the assembler doesn't know where the appVar is going to be. It's location can even change within your program if you make another appVar. You then have two options:

If you have just a little bit of data to store, you can do this:
Code: [Select]
ld hl, name
rst 20h
bcall(_chkFindSym)

inc de
inc de

ex de, hl

ld (hl), a
inc hl
ld (hl), b
inc hl
ld (hl), c
inc hl
ld de, (score)
ld (hl), e
inc hl
ld (hl), d
You can see how this would get ugly if say you needed to store 500 bytes. The other option is to set up your memory usage to accommodate the final saving. Here is how you could do that:
Code: [Select]
dataStart equ appBackUpScreen

enemyBuf equ dataStart ;256
bulletBuf equ enemyBuf+256 ;256
score equ bulletBuf+256 ;2
lives equ score+2 ;1
reload equ lives+1 ;1

dataEnd equ reload+1 ;1


ld hl, name
rst 20h
bcall(_chkFindSym)

inc de
inc de

ld hl, dataStart
ld bc, dataEnd-dataStart
ldir
This method is good if you need to store buffers and such. Then to access them, just do it in reverse. For the first example, just switch (hl) with the other register and it will pull data out instead. For the last one, ex de, hl and you will be copying to your data area from the appVar.

Title: Re: My final border: How to create/work with AppVars?
Post by: Jerros on August 03, 2010, 03:06:43 am
Ok, I'll try that, though I must admit I still dont understand all of it.
But after messing around for a couple of hours I should get the hang of this.
Thank you!

Code: [Select]
ld (hl), a
inc hl
ld (hl), b
inc hl
ld (hl), c
inc hl
ld de, (score)
ld (hl), e
inc hl
ld (hl), d

So, are a, b, and c just 1 byte numbers I want to save, or the registers? ><
Title: Re: My final border: How to create/work with AppVars?
Post by: Quigibo on August 03, 2010, 04:34:09 am
The way I generally do appvars is like this:

Code: [Select]
;To read form the appvar:

call    Routine_that_Gets_pointer_of_appvar_in_HL
ld      de,AppDataStart
ld      bc,AppDataEnd-AppDataStart   ;Size of data
ldir                                 ;copy the appvar TO your data

;If appvar wasn't found, fill the data area with default values

ld      hl,DefaultDataStart
ld      de,AppDataStart
ld      bc,AppDataEnd-AppDataStart   ;Size of data
ldir                                 ;copy the default TO your data

;To write to your appvar:

call    Routine_that_Gets_pointer_of_appvar_in_HL
ex     de,hl
ld      hl,AppDataStart
ld      bc,AppDataEnd-AppDataStart   ;Size of data
ldir                                 ;copy the data TO the appvar

;This is how I hold my variables using this macro I made:

#define VAR(name,size) name: \ .org name+(size)

.org SomeFreeRamLocation
AppDataStart:

VAR(MyVar1,1)   ;A one byte variable
VAR(MyWord,2)   ;A two byte variable
VAR(MyArray,100)  ;A 100 byte array

AppDataEnd:

Title: Re: My final border: How to create/work with AppVars?
Post by: Jerros on August 03, 2010, 10:43:57 am
I'm now using an modified version of what Thepenguin posted.
It seems to work great, though I deleted some lines of which I didn't knew what they did, so there's probably something wrong, but as long as it works I'll just stick to that:
Code: [Select]

;To create if none exists, and unarchive if it does:

ld hl, "NameOfAppVar"
rst 20h
bcall(_chkFindSym)
jr nc, found
ld hl, "random size"
bcall(_createAppVar)
JR Programm
found:
   ld hl, "NameOfAppVar"
   rst 20h
   bcall(_chkfindsym)
   ld a, b
   or a
   bcallnz(_arc_unarc)

Programm:
....
....
....

;And archiving it again upon exiting

quit:
   ld hl, CHData
   rst 20h
   bcall(_chkfindsym)
   ld a, b
   or a
   bcallz(_arc_unarc)
b_call _JforceCmdNoChaR



;To store numbers:
ld hl, "NameOfAppVar"
rst 20h
bcall(_chkFindSym)
inc de
inc de
ex de, hl
ld a, "Number to be stored"
ld (hl), a
inc HL
inc HL               ;whatever many times needed, depending on the size of the previous number stored
inc HL
ld a, "Another number to be stored"
ld (hl), a
      ; ect ect ect
      ;
      ; To read from the Var, I use the same lines as the "To store numbers:" part, just switch around (hl) and a.
Like I said, it works, but I might have simplified Thepenguin's routine a bit too much, but I'll askif I run into any trouble.

While I'm at it, when doing Hi-Scores, you obviously must compare the currend score with the hi-score, and save the new high-score if it's higher (duh).
Now you guys helped me figure out the hard part, reading the old score and possibly writing the new one, but... what's the best routine for comparing two 16-bit numbers and jump to lbl1 if it's lower or to lbl2 when higher?
Can you just sub two 16-bit registers and check if the outcome is "negative" or not?

Ohh, and another one: Cann you do the CALL command with a condition?
Code: [Select]
   cp 3
   call   z, label
And yes I'm THAT big of a newby...
Title: Re: My final border: How to create/work with AppVars?
Post by: thepenguin77 on August 03, 2010, 12:19:20 pm
Yes, calls can be conditional. They can also use all the conditions not just z nz c nc. Like m for negative or p for positive.

To compare two 16 bit numbers, use:
Code: [Select]
or a
sbc hl, de
add hl, de
This affects the flags the exact same way that cp hl, de would if it were a real command.

Also, you only need one inc hl after you write a variable to memory. That's because A is one byte, ld (hl), a stores one byte, so you only need to increase one byte. If you have a bigger number to store, you have to split it into bytes. So DE is stored E then D.
Title: Re: My final border: How to create/work with AppVars?
Post by: Jerros on August 04, 2010, 02:56:55 am
Yes, calls can be conditional. They can also use all the conditions not just z nz c nc. Like m for negative or p for positive.

Also, you only need one inc hl after you write a variable to memory. That's because A is one byte, ld (hl), a stores one byte, so you only need to increase one byte. If you have a bigger number to store, you have to split it into bytes. So DE is stored E then D.

Calls can be conditional....
Ok, now I got to optimize like 2000 lines >.<
Great to know though.

Also, thanks alot for the "cp hl" routine, I used a routine that splitted the 16-bit numbers and then compared the seperate 8-bit registers, which is much much longer.

And I know now that one inc of HL is enough, thought that if I'd store a large number it would automatically write two bytes, untill I figured that you can't actually do that.

Wel... one again you've all amazed me, and left me without any questions.
Thank you, Thepenguin!
Title: Re: My final border: How to create/work with AppVars?
Post by: Jerros on August 04, 2010, 09:38:00 am
Code: [Select]
or a
sbc hl, de
add hl, de

What should "a" be in the "or    a" line?
Or is that completely irrelevant and are those 3 lines just an equivalent of "cp    HL"?
Title: Re: My final border: How to create/work with AppVars?
Post by: calc84maniac on August 04, 2010, 10:33:06 am
"or a" is a command that resets the carry flag as a side effect (while not modifying the value of A). There is no 16-bit "sub" instruction, so we are forced to use "sbc" with the carry flag reset instead.
Title: Re: My final border: How to create/work with AppVars?
Post by: Jerros on August 05, 2010, 02:26:10 am
Uhm... another stupid question, but how do I use that to check wether one 16-bit number is larger than another?
With the "cp  HL" routine you gave me, it's only possible to check wether the numbers match or not.
How would I do a jump only if, lets say, DE holds a bigger number than HL?

EDIT:
Figured that'd be done with:
Code: [Select]
or a
sbc hl, de
add hl, de
             JR        "condition", "Label-DE-is-Bigger"
.....
code DE-and-HL-were-equal-or-HL-was-Bigger
.....
Not sure which condition to use yet, but I'll try some.
Title: Re: My final border: How to create/work with AppVars?
Post by: Quigibo on August 05, 2010, 03:30:42 am
You just check the c flag.  If during the "sbc" part of the code, there was a carry, it would mean that de was larger than hl.  That means that immediately after when you do the "add" instruction that if there was a carry during the subtraction, there must also be a carry during the addition to get back to the original value.  So the c flag is still in the correct state by the end of that routine.  If you don't need to preserve the value of hl after the comparison, you can omit the "add" part.