Well, I've officially made my most complicated program in z80 asm ever...and pretty proud of it B) All it does is move a dot around the screen in response to arrow presses, but that in itself involves a fair bit of syntax knowledge, as I'm sure you all know. Unfortunately, while the compiled .8xp does work on my WabbitEmu emulator, it does not work on my actual TI-84 plus, which I find very confusing. Instead, the dot makes erratic jumps to the right and left whenever I press a different vertical (up/down) arrow key, and the horizontal keys won't even respond. Moreover, the screen doesn't seem to erase the point every time it moves, and even turns on several points at once sometimes. In short, the basic functionality of a moving dot is there, and the calculator seems to be responding to my key presses (quitting when I press clear/enter, etc) but the program has several bugs that never showed up when I was running it on an emulator. The code for this program is below...any idea what's wrong? Also, if you have any tips for optimizing this code, I'm always up for streamlining suggestions. Thanks in advance!

Code:

#include    "ti83plus.inc"
.org    $9D93
.db    $BB, $6D
   
   bcall(_clrLCDfull)
   
   set fullScrnDraw, (IY + apiFlg4)   ;So we can use last column/row
   ld bc,$2F1E            ;Initialize pixel coordinates at (47, 30)
   ld a, kUp
   jp prog
   
Loop:
   bcall(_getKey)
prog:
   cp kEnter   ;End if Enter or Clear is pressed
   jp z,End
   cp kClear
   jp z,End

   cp kLeft
   jp z,Left

   cp kRight
   jp z,Right

   cp kUp
   jp z,Up

   cp kDown
   jp z,Down

   jp nz,loop


Left:            ;Move dot left by one pixel if not at the left screen edge
   ld a,b
   cp 1
   jp c,loop   

   ld d, 0
   bcall(_Ipoint)
   dec b
   ld d,1
   bcall(_Ipoint)
   jp loop
Right:            ;Move dot right by one pixel if not at the right screen edge
   ld a,b
   cp 95
   jp nc,loop

   ld d, 0
   bcall(_Ipoint)
   inc b
   ld d,1
   bcall(_Ipoint)
   jp loop
Up:            ;Move dot up by one pixel if not at the top screen edge
   ld a,c
   cp 63
   jp nc,loop

   ld d, 0
   bcall(_Ipoint)
   inc c
   ld d,1
   bcall(_Ipoint)
   jp loop
Down:            ;Move dot down by one pixel if not at the bottom screen edge
   ld a,c
   cp 1
   jp c,loop

   ld d, 0
   bcall(_Ipoint)
   dec c
   ld d,1
   bcall(_Ipoint)
   jp loop
End:
   bcall(_clrLCDfull)
   ret   

.end
I'd be willing to bet that you're running into register destruction again, for example with b being destroyed in the bcall in:
Code:
   ld d, 0
   bcall(_Ipoint)
   dec b
You should either push and pop your important variables around bcalls, or (the better solution) use some SafeRAM for X and Y coordinates.
But again the program worked on my emulator... and either way _IPoint doesn't destroy any registers when you're just turning pixels on and off, and _getKey only destroys DE and HL
Usual questions:
  • What OS version is in your emulator?
  • What OS is on your calc?
  • Are you in classic mode or MP mode?
  • Assume _Ipoint doesn't preserve any registers. Does this help?
Saying something works on an emulator and not on a calc means either there is a version miss-match (your ROM in the emu. is not the one from your calc) or the emu itself has a bug. For the latter, your code has a bug.[/list]
I just sent it to my calc (84+SE OS 2.43 and 83+ OS 1.19) and it works just fine.

As for optimizations and tips, like Kerm said it really would be better to save the coordinates in saferam or put some empty space in your program (.db 0) to store them to, or even better use SMC to load them directly into the location you plan to use them, as using a register to keep track of them, especially when you already are limited to 7 (and ix/iy/sp, but those are kinda "special"), will just complicate things as your program grows. You could maybe get away with using the shadow registers to store them if you disable interrupts and don't plan on using them yourself.

Another thing is you have every bcall(_IPoint) in each individual label, you might be able to fit it into the main loop.

EDIT: Here's some code you can play around with that uses a safer way of keeping track of your X/Y positions and also removes all the extra BCALLs. What it does is store the offset in de, d = X offset (1, 0, or -1) and e = Y offset (1, 0, -1). Note however that if you have -1 in e, de will equal $00FF, or 255 (NOT -1) so you need to change d to $FF also so that de = -1, $FFFF.

Also note the ld hl,updateCoords \ push hl. When you ret, you really take the last value on the stack and load that into the pc. So instead of all of those jr/jp's you can now use rets, but be careful because if you jump back to loop without first popping that value you will eventually fill up the entire stack and start running into outside memory or end up jumping places you don't want to Wink


Code:
   bcall(_ClrLCDFull)
   
   set fullScrnDraw, (IY + apiFlg4)   ;So we can use last column/row
   ld a, kUp
   jp prog
   
loop:
   bcall(_GetKey)
prog:
   cp kEnter   ;End if Enter or Clear is pressed
   jp z,End
   cp kClear
   jp z,End

   ld hl,updateCoords
   push hl

   ld de,$0000   ;initiate de to 0. d will be added to the Y value, e will be added to
   ld hl,X
   cp kLeft
   jp z,Left

   cp kRight
   jp z,Right

   dec hl      ;now points to "Y"
   cp kUp
   jp z,Up

   cp kDown
   jp z,Down
   ret

Left:         ;Move dot left by one pixel if not at the left screen edge
   ld a,(hl)
   or a
    ret z      ;quit if X = zero
   dec d      ;d = -1
   ret
Right:         ;Move dot right by one pixel if not at the right screen edge
   ld a,(hl)
   cp 95
    ret z      ;if (X) = 95, we'll jump to updateCoords leaving d at 0 (which won't change the coordinate)
   inc d      ;d = 1
   ret
Up:            ;Move dot up by one pixel if not at the top screen edge
   ld a,(hl)
   cp 63
    ret z
   inc e      ;e = 1
   ret
Down:            ;Move dot down by one pixel if not at the bottom screen edge
   ld a,(hl)
   or a
    ret z      ;quit if Y = 0
   dec e      ;
   dec d      ;de = -1 ($FFFF)
   ret

updateCoords:
Y = $+1
X = $+2
coords = $+1
   ld bc,$2F1E      ;Initialize pixel coordinates at (47, 30)
   ld l,c
   ld h,b
   add hl,de      ;update the coordinates (offset in de)
   ld (coords),hl
   ld d,0
   bcall(_IPoint)
   ld c,l
   ld b,h         ;the new coordinates
   inc d         ;d = 1
   bcall(_IPoint)
   jr loop
Hm well WabbitEmu is supposed to be pretty good...my version emulates a TI-83 plus, OS v1.16. My TI-84 Plus calculator has OS v2.55MP and is currently running in Classic mode. I have a PDF document that lists all the possible TI ROM calls, and it says _GetKey destroys registers DE and HL, while _IPoint destroys "None, except for option 3 (test) then all." Obviously my program uses only options 1 (pixel on) and 0 (pixel off), so there should be no issue with registers destroyed.
Now, as far as using SafeRAM for variables goes, I've heard that using RAM memory is always a lot slower than using registers directly. So how does one determine exactly when to use RAM locations and when to use registers (other than if you have a bazillion values to keep track of, obviously)?
On non-z80s, yes. On z80 processors, accessing main memory takes only a few more cycles than accessing registers, not the order-of-magnitudes longer in GHz processors connected to DDR3 over the Northbridge. To use SafeRAM, you simply add equates in your program that let you give a name to a particular address in SafeRAM. Registers should always be used for short-term, temporary storage, inside a particular routine or function. They shouldn't be used for global, program-wide values (unless you have a particularly good reason and few/no calls that could potentially destroy variables).
I know the docs say that GetKey only destroys HL and DE, but the docs are wrong. If a key hook is enabled it will certainly destroy B, and you should probably assume the hook can destroy C and IX as well.

It's generally not a good idea to keep variables in registers for a long time; store them in RAM instead. As your program gets more complicated, you'll find that registers and the stack aren't enough, and keeping your variables well-organized from the start will save you headaches later.

(Of course registers and the stack are faster. But your program is nowhere near complicated enough for optimization of that sort to make any sense at all. And as a general matter - I think a lot of assembly newbies run into problems because they try to learn optimization techniques before they really understand the language. Don't do that - figure out how to write the program before you try to optimize it.)

Also, GetKey is risky for a number of reasons and generally should only be used by Flash apps, not RAM programs.
I'm curious, what risks are there with _GetKey apart from 1) the user getting angry at how slow it is and throwing their calculator against the wall and 2) being able to turn off the calculator mid-program?
If you turn off the calc mid-program from an ASM program, some of your RAM will be eaten and you will eventually need to RAM clear to get it back (that, or you will need to manually adjust the pointers to get your RAM back).
Not to mention that link activity might cause your program to get confused, if I recall correctly from my tests. Overall it's just a bad idea, because it gives the OS way too much control in the middle of your program.
Indeed; silent linking can do all sorts of unexpected things (as well as obvious RAM areas like header and ioData, the silent linking routines can also use OP1, iMathPtr5, ramCode, and probably others.)

Also, the system APD can cause problems (it will run OFFSCRPT and ONSCRPT; it will forcibly exit your program if the batteries are removed during APD), and note that APD can remain enabled outside of the GetKey routine itself (if an arrow or DEL key is held down.)
Still though, does anyone know why my above program works on WabbitEmu but not on my calculator? Is it just because my calculators OS is significantly different from the emulator's? If so, is there perhaps a better emulator for me to use?
Ah, that's a much more specific response. Thanks for sharing your expertise, Floppus.
As others have mentioned it's probably due to the registers getting messed with. I sent it to both my calculators and it worked fine. Did you try my updated version? Try pushing/popping your registers around the bcalls, in particular _GetKey, and see if that makes a difference.
Shundra, it's likely the difference between the two operating systems. Presumably 1.1x has a GetKey that is behaving (or doesn't have various hooks and side effects messing up the registers) and 2.55MP does. As noted, it's not recommended to use _Getkey anyway.
Yeah it must be the OS. Unfortunately chickendude I did not try your program, mainly because I havn't learned about the stack fully in my tutorial yet, and I don't want to use any code that I don't fully understand (some of the "ret" usage in your labels also confused me). However, I have since made another program that uses RAM to store the row and col, and it works just fine on both machines. Only two issues remain:
1) Is there an emulator I could use to more accurately reflect the inner-workings of a TI-84+ plus calculator running a v2.55MP OS (preferrably one for 64-bit Windows)?
2) While the program works fine on my calculator, it sort of "uncovers" other pixels while the dot is moving. In fact if I move the dot around the entire screen, I sort of reveal a coordinate axes, along with any graphs that are still active in the Y= menu. Is this my first encounter with the graph buffer? More importantly, how can I prevent these ghost pixels from forming?
Also my new program uses _getCSC instead of _getKey...thank you all for that tip!
In the future please edit your post rather than double posting if it's within 24 hours.
Shundra:
1) WabbitEmu is very accurate for the TI-84+. Another option is jsTIfied; a third option is TILEm.
2) Clear the graph buffer with the _grbufclr call.
  
Register to Join the Conversation
Have your own thoughts to add to this or any other topic? Want to ask a question, offer a suggestion, share your own programs and projects, upload a file to the file archives, get help with calculator and computer programming, or simply chat with like-minded coders and tech and calculator enthusiasts via the site-wide AJAX SAX widget? Registration for a free Cemetech account only takes a minute.

» Go to Registration page
Page 1 of 2
» All times are UTC - 5 Hours
 
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

 

Advertisement