All the cool kids are doing it! Actually, I just think I'm going to have a lot of questions, and I thought "all the cool kids" had a good idea. So, let's start with the questions!

How do I make a timer that runs a subroutine, say, every two seconds?

The only way I can think of is to have a huge multi-byte integer and increase it at the start of each cycle of my program loop, and then check for the highest byte to be 255 or something and, if so, execute the routine...

Is there a better way? If not, how many bytes would I need in order to have a long enough delay? (I'm thinking it'd be a lot...)

Thanks in advance for all the help!
-Zippy (Taw)
If your main loop runs in a fairly consistent amount of time, the best thing to do is have a variable that you decrement once per cycle, then reset to a starting value and run your periodic code if it reaches zero, more or less what you said. If your main loop is doing a decent amount of stuff, a two-byte counter will probably give you a second or two (or more). You more or less have it.
Another way is to use an interrupt handler to increment your counter, and then check the value of the counter in the main loop. Or, depending on what the subroutine does, you might be able to run the subroutine directly from the interrupt handler so you don't have to check the counter in the main loop. Using an interrupt is a more accurate, or at least more consistent, method than counting cycles through the main loop, especially if the main loop cycle varies considerably.

I'm pretty sure there's at least a ~120Hz interrupt on all models of 83(+)/84+ calcs, and some models have other clock sources as well (but I don't know if those other ones even trigger interrupts).
That's probably WAAAAAY over my head at this point. I think I'll try using just a 2 byte counter and see how that does Smile
Sure, I understand. Interrupts can be tricky when you're still trying to learn everything else about ASM. A counter in the main loop will probably work just fine.

Actually you might be able to exploit the 120Hz interrupt without having to write an interrupt handler at all:

Code:
mainloop:
  halt
  ; do main loop stuff here
 
  ; test counter
  ld hl,counter
  dec (hl)
  jr nz,mainloop
  ; counter is zero. call subroutine and reset counter
  ld (hl),240  ; 120 Hz * 2 seconds
  call subroutine
  jr mainloop

The "halt" instruction waits until the next interrupt occurs, so if your "main loop stuff" runs in less than 1/120th of a second, the loop will run 120 times per second. As a result, the counter decrements 120 times per second, and the subroutine is called every 2 seconds (every 240 times through the loop). Just make sure that interrupts are enabled before calling "halt", otherwise the calc will completely freeze. If you don't do anything with interrupts, they will be enabled by default.

As you can see, there are quite a few ways to do things in a low-level language like ASM (and ASM is as low as you can practically get (ignoring micro-code inside some CPU's), since it's a human-readable form of machine code).
To continue making a long story longer, if you're operating on the TI-84+ and TI-84+SE line of calculators, there are three crystal timers accessible via ports $30 through $38 that can be set to run and occasionally checked to see if they have expired yet; one or two of them is used by the OS for things like direct USB, but I believe at least one remains free for userland use.
Okay, so right now I'm trying to write a routine to write a custom 5x7 font character to the buffer. The character sprites are stored using 3 bytes where each nibble is one row of the sprite.

The routine I have come up with doesn't seem to be working, though I don't see why not. Maybe I did something wrong with the masking?

Code:

zPutC:
   ;; Draws the character in A using custom 5x7 font sprites starting at _FONT
   ;;   NOTE: The sprites are stored using 3 bytes, so this routine accounts for that, but it doesn''t display the last row of whitespace
   ;; IN:
   ;;   A contains the character to write
   ;;   D contains Y
   ;;   E contains X

   ld l, a   ;load the char address into IX
   ld h, 0
   ld c, l
   ld b, h
   add hl, hl
   add hl, bc
   ld bc, _FONT
   add hl, bc
   push hl
   pop ix
   
   ld h, 0   ;Get address etc for sprite
   ld l, d
   ld b, h
   ld c, l
   add hl,hl
   add hl,bc
   add hl,hl
   add hl,hl
   ld a,e
   srl e
   srl e
   srl e
   ld d,b
   add hl,de
   
   ld bc,plotsscreen
   add hl,bc
   
   ld a,e
   and 7
   ld b,6
   jr z,_aligned
   ld c,a
   
_rowloop:
   push bc
   ld a,1
   cp b
   ld a,(ix)
   jr z,_in_left_half   ;this should only jump for even rows...
   
   inc ix      ;this is using the low order bits, so rotate them into the high order bits
   sla a
   sla a
   sla a
   sla a
   
_in_left_half:
   ld b,c
   and $F0
   ld d,a
   ld a,$F8
   ld c,0
   ld e,c

_shiftloop:
   srl a
   rr c
   srl d
   rr c
   djnz _shiftloop
   
   cpl
   and (hl)
   xor d
   ld (hl),a
   inc hl
   
   ld a,c
   cpl
   and (hl)
   xor e
   ld (hl),a
   
   ld de,12
   add hl,de
   pop bc
   djnz _rowloop
   ret

_aligned:
   ld de,12
_putloop:
   ld a,1
   cp b
   ld a,(ix)
   jr z,_lefthalf   ;this should only jump for even rows...
   
   inc ix      ;this is using the low order bits, so rotate them into the high order bits
   sla a
   sla a
   sla a
   sla a
   
_lefthalf:
   and $F0
   ld c,a
   ld a,$07
   and (hl)
   xor c
   add hl,de
   djnz _putloop
   ret

I based my code off of the masked xor sprite routine found in ASM in 28 days.
Before I start critiquing code, you do a lot of very cool and very smart things there, so many kudos! Anyway, I spotted what I think is a glitch in checking if the coordinates are aligned:


Code:
   ld a,e
   srl e
   srl e
   srl e
   ld d,b
   add hl,de
   
   ld bc,plotsscreen
   add hl,bc
   
   ld a,e
   and 7
   ld b,6
   jr z,_aligned
   ld c,a
Shouldn't that ld a,e in the fifth-to-last line not be there, since you saved the unshifted e to a previously?
Ooh, good catch! That was definitely an issue, but that still didn't fix it Sad It's displaying nothing...

Well...technically it's not nothing. It's just displaying the graph screen with the axes and "DONE" that's it.
I assume that you iFastCopy'd after running that routine, just to rule out silly omissions? Smile
Indeed I wrote a program that just does basically

Code:
ld a,$30
ld de,0
call zPutC
call iFastCopy
Okay, so I'm obviously failing hard here, but I'm trying to write to the homescreen from inside an app. Why won't it work?


Code:
#include "ti83plus.inc"
editBuf .equ textShadow
.org $4000

 db 080h, 00Fh
 db 000h, 000h, 000h, 000h
 db 080h, 012h
 db 001h, 004h
 db 080h, 021h
 db 001h
 db 080h, 031h
 db 001h
 db 080h, 048h
 db "TEST", 000h, 000h, 000h, 000h
 db 080h, 081h
 db 001h
 db 080h, 090h
 db 003h, 026h, 009h, 004h
 db 01Bh, 00Bh, 0AEh, 0F0h
 db 002h, 00Dh, 040h, 0A1h, 06Bh, 099h, 0F6h, 059h, 0BCh, 067h
 db 0F5h, 085h, 09Ch, 009h, 06Ch, 00Fh, 0B4h, 003h, 09Bh, 0C9h
 db 003h, 032h, 02Ch, 0E0h, 003h, 020h, 0E3h, 02Ch, 0F4h, 02Dh
 db 073h, 0B4h, 027h, 0C4h, 0A0h, 072h, 054h, 0B9h, 0EAh, 07Ch
 db 03Bh, 0AAh, 016h, 0F6h, 077h, 083h, 07Ah, 0EEh, 01Ah, 0D4h
 db 042h, 04Ch, 06Bh, 08Bh, 013h, 01Fh, 0BBh, 093h, 08Bh, 0FCh
 db 019h, 01Ch, 03Ch, 0ECh, 04Dh, 0E5h, 075h
 db 080h, 07Fh
 db 000h, 000h, 000h, 000h
 db 000h, 000h, 000h, 000h
 db 000h, 000h, 000h, 000h
 db 000h, 000h, 000h, 000h
 db 000h, 000h, 000h, 000h


   ;jp PROGRAM_START
   ;I was gonna have stuff here, but now I don't so that jump is not necessary
PROGRAM_START:
   ld a, $30
   bcall(_putc)
   ;ld hl, textShadow   ;I've tried both using _putc and loading something to the text shadow. Nothing is working. Whyyy??
   ;ld (hl), $30
   bcall(_jForceCMDNoChar)

.end


Really I'm trying to get that 4x6 sprite routine to work, but I'm now realizing that part of the problem may have been that NOTHING seems to be working.

EDIT: Also, is there any place that documents the actual source for the different bcalls? That would probably be pretty useful...
_putc should work fine. Did you make sure that textWrite, (iy+sgrFlags) is reset so that text will go to the LCD rather than the graph buffer?
Ah, that did it...So now I can write to the LCD, but how about writing to the actual homescreen buffer? How would I do that?
ZippyDee wrote:
Ah, that did it...So now I can write to the LCD, but how about writing to the actual homescreen buffer? How would I do that?
You can just store the bytes representing the characters into the 128-byte buffer; it's stored in 8 rows of 16 columns each, of course.
Well, writing to the text shadow doesn't seem to be working...or am I writing to the wrong place?

But now that I've gotten it to at least display some stuff with _PutC, I was able to partially fix my font routine Very Happy It still has a few bugs, but at least I can do some forms of output to trace the issues.

Also, why does this code freeze my calc?

Code:
ClearPlotSScreen:
   di
   ld (save_sp), sp
   ld sp, plotSScreen+768
   ld hl, $0000
   ld b, 48
_clearplotloop:
   push hl
   push hl
   push hl
   push hl
   push hl
   push hl
   push hl
   push hl
   djnz _clearplotloop
   
   ld sp, (save_sp)
   ei
   ret
save_sp:
   .dw 0


Edit: I think I figured it out.....It's because I'm in an app, so I can't write to that address, I can only read from it, correct? Does that mean I have to write ALL data that I ever want to modify to some free area of RAM? How do I go about doing that (and still keeping track of where everything is)?

Edit again: I have concluded that that is indeed the reason...Right now I'm just looking at the RAM areas and trying to find ones that are large enough and also presumably safe....Is there a better way to get usable RAM?
No, you are allowed to rampage all over RAM addresses if you so choose. The reason that PutS and VPutS are special is that those routines reside in ROM, so your App's page gets swapped out while those routines are running, hence any constants (like strings) inside your code aren't physically available while those two routines are running. The solution is to either copy your strings to RAM and call them, or make your own PutS and VPutS inside your app that just call PutC and VPutMap respectively in a loop.
I know that...but as far as I can tell it's impossible to modify values within the app itself. That's my understanding of why that ClearPlotSScreen routine wasn't working, because as soon as I used iMathPtr5 for my save_sp, it worked perfectly. If there is in fact a way to modify values within the app itself, then PLEASE let me know how to do that.

Also, I am using entirely customized text-display routines along with a custom font, so I won't be using PutS, vPutS, PutC, or vPutMap very much if at all. I almost have my PutC routine entirely working with masked sprites and clipping, but I'm still getting a few bugs...The mask is always $F8 though, unless it's clipped.
Nope, your app is in Flash ROM (read-only memory), so you can't use the non-App program trick of using parts of yourself as scratch space.
Okay, so my question still is: Is there any good way to get scratch space, or do I just have to use safe areas of RAM?


Also, completely unrelated, I randomly thought of the idea that it would be possible to change the name of programs/appvars/lists by modifying their VAT entries. Would that work? Honestly I don't know why I thought of this at all...
  
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 3
» 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