[TI ASM] Event handler - Problems

Got questions? Got answers? Go here for both.

Moderator: MaxCoderz Staff

Post Reply
User avatar
driesguldolf
Extreme Poster
Posts: 395
Joined: Thu 17 May, 2007 4:49 pm
Location: $4080
Contact:

[TI ASM] Event handler - Problems

Post by driesguldolf »

All bugs fixed!!! :yay: [I hope this isn't too long]
This program is supposed to have 2 timers wich count at different rates

The main file:

Code: Select all

.NOLIST
#define   EQU   .equ
#define   equ   .equ
#define   END   .end
#define   end   .end
#define	  DB    .db
#define   db    .db
#define   DW    .dw
#define   dw    .dw
#include "ti83plus.inc"
.LIST

#define correctoverflows
queue_size	equ	20

     .org 9D93h
     .db $BB,$6D


	call install

	ld hl,text
	bcall(_puts)	; Disables interrutps
	ei
	call eventloop
	im 1
	ret
text:
	db "      Mode 2!!", 0

evthand1_code:
	ld hl, 0
	inc hl
	ld (evthand1_code + 1), hl
	ld de, 0
	ld (currow), de
	B_CALL(_disphl)
	ret
evthand2_code:
	ld hl, 0
	inc hl
	ld (evthand2_code + 1), hl
	ld de, 1
	ld (currow), de
	B_CALL(_disphl)
	ret

;----[ DATA ]---------------------------------------------------------------

lst_timedevents:
	db 2		; Amount
	db 1, 45	; evthand1_nr, evthand1_timer
	dw 0		; evthand1_data
	db 2, 60	; evthand2_nr, evthand2_timer
	dw 0		; evthand2_data

lut_maxtimers:
	db 0, 45, 60
lut_eventhandlers:
	dw 0
	dw evthand1_code
	dw evthand2_code

eventpointer:
	dw eventqueue
eventqueue:
	db 0, 0, 0
	db 0, 0, 0
	db 0, 0, 0
	db 0, 0, 0
	db 0, 0, 0
	db 0, 0, 0
	db 0, 0, 0
	db 0, 0, 0
	db 0, 0, 0
	db 0, 0, 0
	db 0, 0, 0
	db 0, 0, 0
	db 0, 0, 0
	db 0, 0, 0
	db 0, 0, 0
	db 0, 0, 0
	db 0, 0, 0
	db 0, 0, 0
	db 0, 0, 0
	db 0, 0, 0
	db 0, 0, 0	; Zero-extension
	db $FF		; If (eventpointer) ever points at this, the queue has been flooded

#include "evthand.inc"

.end
and the event handler code:

Code: Select all

.module eventhandler
_start

;----------------------------------------------------------------
;    The main code wich executes events as they are triggered    
;----------------------------------------------------------------
;Author		Dries Guldolf
;Remarks	To start your event based program just call eventloop
;		Make sure that the isr is installed and that interrupts are enabled
shifteventqueue:
	di				; If an interrupt triggers while we're modifying the eventqueue...
	ld hl, eventqueue + 3		; Shift the event queue 3 bytes
	ld de, eventqueue		;
	ld bc, queue_size * 3		;
	ldir				;
	ld hl, (eventpointer)		; Decrement the eventpointer
	dec hl				;
	dec hl				;
	dec hl				;
	ld (eventpointer), hl		;
	ei				; Enable interrupts
eventloop:
	;
	; Do stuff here (like checking if the clear button is pressed and return if it is)
	;
	ld a, (eventqueue)		; Fetch the event type
	;
	; Check for system events here (see $00 as a system event)
	;
	or a				;
	jr nz, runeventcode		; If there was an event, execute it
	halt				; Some power-saving stuff
	jr eventloop			;
runeventcode:
	ld h, 0				; Get the address of the event handler code
	ld l, a				;
	add hl, hl			;
	ld de, lut_eventhandlers	;
	add hl, de			;
	ld a, (hl)			;
	inc hl				;
	ld h, (hl)			;
	ld l, a				;
	ld de, shifteventqueue		; Push the return address
	push de				;
	ld de, (eventqueue + 1)		; Pointer to event data
	jp (hl)				; Run event code

;----------------------------------------------------------------
;                Adds an event to the eventqueue                 
;----------------------------------------------------------------
;Author		Dries Guldolf
;Input		A	The event type
;		DE	pointer to the event data
;Output		HL	points at the next entry in the eventqueue
;Destroys	AF, DE
;Result		Your event is added to the eventqueue
;Remarks	This routine can take care of an overflown eventqueue
;		itself if you define correctoverflows (doesn't add
;		the event) otherwise it will call a costum routine
;		called bufferoverflow, you can quit the program by
;		restoring SP or modify stuff and return (preserve B).
addevent:
	ld hl, (eventpointer)	; Write the data to the eventqueue
	ld (hl), a		;
	inc hl			;
	ld (hl), e		;
	inc hl			;
	ld (hl), d		;
	inc hl			;
	ld a, (hl)		; If the buffer has overflown we can correct it
	inc a			; If (eventpointer) points to $FF there is an overflow
#ifdef floodprotection
	jr nz, _nooverflow	; Clean the zero-extension if there was an overflow
	ld hl, eventqueue + (queue_size * 3)
	xor a			; This can be left out because it will only be executed if a=0
	ld (hl), a		; Unrolled loop
	inc hl			;
	ld (hl), a		;
	inc hl			;
	ld (hl), a		;
	dec hl			;
	dec hl			; HL points again at the zero-ext, if another event triggers it's wiped again
_nooverflow:
#else
	call z, bufferoverflow	; Jump to the overflow handler
#endif
	ld (eventpointer), hl	; Write new pointer
	ret			; Done

;----------------------------------------------------------------
;                 The interrupt service routine                  
;----------------------------------------------------------------
;Author		Dries Guldolf
;Results	Every timer of lst_timedevents is decremented and
;		if a timer hit zero, the event is added to the queue
;Remarks	To use this isr call install first and execute im 1
;		at the end of your program.
interrupt_start
	ex af, af'		;
	exx			;
	ld hl, lst_timedevents	;
	ld b, (hl)		;
	inc hl			; HL points to the beginning of the list
_loop:
	ld a, (hl)		; Get the event type
	inc hl			; HL points to the timer
	dec (hl)		; Decrement the timer
	jr z, _addevent		; If it hit zero then add the event
	inc hl			; Otherwise go to next timed event
	inc hl			;
	inc hl			;
_continue:
	djnz _loop		; Do this for all the timed events
	ld a, %00001000		; Aknowledge thingie
	out (3), a		;
	ld a, %00001010		; Reenable hardware and quit the interrupt
	out (3), a		;
	exx			;
	ex af, af'		;
	ei			;
	ret			;
_addevent:
; Reset the timer
	push hl			; We need the address later
	ld h, 0			; Get the maxtimer for this event
	ld l, a			;
	ld de, lut_maxtimers	;
	add hl, de		;
	ld c, (hl)		;
	pop hl			;
	ld (hl), c		; And writeback
; Add it to the event queue
	inc hl			; Fetch the data pointer
	ld e, (hl)		;
	inc hl			;
	ld d, (hl)		;
	inc hl			;
	push hl			;
	call addevent		; Add the event to the queue
	pop hl			;
	jr _continue		; Repeat for all timed events
interrupt_end

;----------------------------------------------------------------
;              Installs an interrupt in SavesScreen              
;----------------------------------------------------------------
;Author		Dries Guldolf
;Results	A 257 byte table filled with $88 is made
;		The interrupt routine is copied to $8888
;Destroys	All
install:
	di			; Disable interrupts (don't want an interrupt before we're ready)
	ld	hl, $8700	; Start of interrupt table
	ld	de, $8701	; Start of interrupt table +1
	ld	bc, 256		; 257 byte table
	ld	(hl), $88	; Fill it with $88
	ldir			;
; Now we have a 257 byte table starting at $8700 filled with $88
	ld	hl, interrupt_start			; Copy interrupt routine to $8888
	ld	de, $8888				;
	ld	bc, interrupt_end - interrupt_start	;
	ldir						;
	ld	a, $87		; The interrupt relocation table is stored at $87xx
	ld	i, a		;
	im	2		; Activate the costum interrupt
	ei			; Enable interrupts
	ret

#if (interrupt_end - interrupt_start + $8888) > (savesscreen + 768)
.echo "\nFATAL ERROR!\nThe isr is too large ("
.echo interrupt_end - interrupt_start
.echo " bytes).\nThere's a maximum of "
.echo savesscreen + 768 - $8888
.echo " bytes!\nHow the hell did you caused this error???\n\n"
#else
.echo "\nEvent handler package by Dries Guldolf,\nTotal size: "
.echo _end - _start
.echo " bytes.\n\n"
#endif

; 180 bytes :)

_end
.module
now the bugs:
- FIXED This code runs perfectly in TI-flash debugger but not on a real 84+ (shuts down, pull a batery, press on, ram reset :)) - No idea whats happening, probabely some ti84+ unfriendly interrupt thingies...
- FIXED If you press the on key the memory gets corrupted writing +-$88A1 all over the place (starting at the end, slowely gets to my program, corrupts it, tidebugger: undefined instruction), suprisingly this is the address of the EI instruction when the interrupt finishes, must be a wrong mask, cant figure it out? Wikiti says %00011011?
- FIXED In shifteventqueue I tought that the first next 3 instrs were not neccesary, but the emu crashed if these aren't included?

Oh yeah: feel free to optimize :wink:

EDIT: opdated the code
Last edited by driesguldolf on Sun 20 May, 2007 7:03 pm, edited 3 times in total.
User avatar
Jim e
Calc King
Posts: 2457
Joined: Sun 26 Dec, 2004 5:27 am
Location: SXIOPO = Infinite lives for both players
Contact:

Post by Jim e »

now the bugs:
- This code runs perfectly in TI-flash debugger but not on a real 84+ (shuts down, pull a batery, press on, ram reset Smile) - No idea whats happening, probabely some ti84+ unfriendly interrupt thingies...
- If you press the on key the memory gets corrupted writing +-$88A1 all over the place (starting at the end, slowely gets to my program, corrupts it, tidebugger: undefined instruction), suprisingly this is the address of the EI instruction when the interrupt finishes, must be a wrong mask, cant figure it out? Wikiti says %00011011?
- In shifteventqueue I tought that the first next 3 instrs were not neccesary, but the emu crashed if these aren't included?
1: In you ISR, you are not properly using port 3, on the wiki it says you need to acknowledge the interrupt by reseting the corresponding bit for the hardware that generated the interrupt. It's just one line in the intro to that page so I'll edit that page so it's louder. You also seem to only want the timer interrupt. in that case your port code should look like this.

Code: Select all

     ld a,%00001000     ;acknowledge the interrupt request
     out (3),a
     ld a,%00001010     ;enable hardware to interrupt again
     out (3),a
You may also want to set the frequency of the interrupt timer. this is controlled by port 4.

Code: Select all

     ld a,6     ;sets to slowest interrupt frequency roughly 110 ~ 120hz.
     out (4),a

2: That's a stack overflow! Congrats!
On key caused it because you allowed it to generate interrupt via port 3. the prior correction will fix that.

Odd though, after an ei instruction has occurred, interrupts should not happen until after the next instruction. This was on TI-Debugger I assume.



Some notes:

You do not need a di at the beginning of your ISR. The processor does this for you. Ignore what 28days says on that.

Also in your interrupt install code, you should set what hardware can generate an interrupt...basically a copy of the port code in the isr.
Image
User avatar
driesguldolf
Extreme Poster
Posts: 395
Joined: Thu 17 May, 2007 4:49 pm
Location: $4080
Contact:

Post by driesguldolf »

Thanks very much! I always had problems with interrupts...
checks if this version crashes 84+... be back tomorrow... (it's 0:00 here)
This really is the best programming group! (defenately gonna credit this place once my game comes out :worship:)
User avatar
tr1p1ea
Maxcoderz Staff
Posts: 4141
Joined: Thu 16 Dec, 2004 10:06 pm
Location: I cant seem to get out of this cryogenic chamber!
Contact:

Post by tr1p1ea »

Ironically Jim e is from a group called RevSoft. Jim has a link to them in his sig :).

When will we be finding out about this game? :).
"My world is Black & White. But if I blink fast enough, I see it in Grayscale."
Image
Image
User avatar
driesguldolf
Extreme Poster
Posts: 395
Joined: Thu 17 May, 2007 4:49 pm
Location: $4080
Contact:

Post by driesguldolf »

tr1p1ea wrote:Ironically Jim e is from a group called RevSoft. Jim has a link to them in his sig :).
:oops: Oops... Didn't saw that... I'll list him personally under "Debugging" then :wink:
tr1p1ea wrote:When will we be finding out about this game? :).
Well after 1.5 years trying to make games I couldn't make I decided to make som simpler demo's first and then some simple games:
Currently finished are some routines (scankeypad, rect/hline, eventhandler (wich is now :D ), a text displayer and a font :yay:). I made a simple graphical demo I called "Stars", It's basically a screensaver were stars fly away from a point with heavily documented source :P (because I want other people to be able to learn from it :) )

Now i'm trying to make 4 small games (<4kb each) to make an app of them and start competing with puzzpack :twisted:
One game is almost finished (+-95%) and I have ideas about 2 and 3
1 involves bouncing balls in cages. Then a breakout game and an explore game (a mixture between pegs and block dude) ofcourse with full level editors :D
EDIT:better descriptions of the games

okay this is a bit of topic... I'll make new topics in projects section as my games near completion

hmm bug no3 is fixed miracousely... :D
If anyone has ideas, bugfixes, optimizations, ... feel free to say it[/b]
Last edited by driesguldolf on Sat 19 May, 2007 12:48 pm, edited 2 times in total.
User avatar
driesguldolf
Extreme Poster
Posts: 395
Joined: Thu 17 May, 2007 4:49 pm
Location: $4080
Contact:

Post by driesguldolf »

I will try to get everything up at ticalc.org when I write a help file on how to use this code (help is always welcome) so I don't need to make lengthy posts :cough: and I can add examples too
Jim e wrote:2: That's a stack overflow! Congrats!
On key caused it because you allowed it to generate interrupt via port 3. the prior correction will fix that.
Yay that won't happen to my eventqueue!!! it has a neat (...) floodprotection! :joy:

EDIT: spelling and grammar errors
User avatar
driesguldolf
Extreme Poster
Posts: 395
Joined: Thu 17 May, 2007 4:49 pm
Location: $4080
Contact:

Post by driesguldolf »

...Crap... There's a problem...

<points at TI and shouts>WHY THE HELL DID YOU INCLUDE SUCH IDIOTIC LCD SCREEN!!!!

<calms down and explains>because the lcd driver needs so much time to be updated and interrupts may not be activated during this process, everything collapses because this eventhandler requires to be executed quit often BUT it isn't because of the time consuming non interrupt friendly lcd... This event handler is useless...
BUT al hope is not lost because I found a workaround:

say you have a main body wich must be executed on regular time periods (think the example of a mario game sorta thing)

Code: Select all

mainloop:
   call moveplayer    ; move player, apply physics to it
   call enemyai       ; move enemies, apply physics (eg falling)
   call doprojectiles ; move projectiles, check if they hit anything
   call drawall       ; render the scene
   call fastcopyerase ; update lcd
   jr mainloop
This would be executed fast if there are less enemies, projectiles, objects in this game and fast otherwise
To solve such problems have an interrupt like this:

Code: Select all

interrupt:
   ex af, af'
   exx
timer   equ ??? ; relocate this
   ld hl, 0
   inc hl ; I tried an 8bit counter... it gives strange results: it halts the game frequently... :S
   ld (timer), hl
   ; make port 3 happy here
   ex af, af'
   exx
   ret
One should see the possibilities of this simple timer, only change the mainloop a bit

Code: Select all

   ; install the interrupt
   ld hl, 0
   ld (timer), hl
   ei
mainloop:
   call moveplayer    ; move player, apply physics to it
   call enemyai       ; move enemies, apply physics (eg falling)
   call doprojectiles ; move projectiles, check if they hit anything
   call drawall       ; render the scene
   call fastcopyerase ; update lcd
waitloop:
   halt
   ld de, MAXTIMER
   ld hl, (timer)
   sbc hl, de
   jr c, waitloop ; strange... c and nc give not logic results :S
   ld hl, 0
   ld (timer), hl
   jr mainloop
This code should delay everything wether theres much or less on the screen, the higher MAXTIMER the slower everything is

This should do the trick, does anyone has a better idea to do this? Any ideas about the strange things I described?
User avatar
Jim e
Calc King
Posts: 2457
Joined: Sun 26 Dec, 2004 5:27 am
Location: SXIOPO = Infinite lives for both players
Contact:

Post by Jim e »

Well, first off, if you are using an lcd copy routine like fastcopy then it is okay to have interrupts on. The reason it is suggested not to have them on is because some times TI-OS likes to draw the busy indicator, so two pieces of code shouldn't use the lcd at the same time. However any good shell will disable the indicator. Even more so since you have an custom interrupt installed, and I assume it does not execute ti-os interrupt. So interrupts and the lcd are not a problem. Just make sure you lcd routine doesn't do a DI at first.


Your vsync looks fine, using a 16bit counter is okay. But before using SBC you should clear the carry flag. OR A will do the trick. You can also add with a negative value since MAXTIMER is a constant. The difference is that you would use the opposite for the carry test.

Code: Select all

In 16bit math:

hl = 15
de = 30
sbc hl,de
15 - 30 = -15 =  65521  carry

hl = 15
de = -30
add hl,de
15 + (-30) = 15 + 65506 = 65521  no carry

Code: Select all

waitloop:
   halt
   ld de, -MAXTIMER
   ld hl, (timer)
   add hl, de
   jr nc, waitloop      ;adding with negative value
   ld hl, 0
   ld (timer), hl
   jr mainloop
Image
User avatar
driesguldolf
Extreme Poster
Posts: 395
Joined: Thu 17 May, 2007 4:49 pm
Location: $4080
Contact:

Post by driesguldolf »

God bless your knowledge! :worship:

thanks very much, I tought interrupts had something to do with the lcd or something...
The reason I didn't do OR A was that it's insignificant, but the addition is even better
I guess I should combine the event handler with this trick (didn't know it was called vsync, you learn something everyday :D )
Post Reply