|
Post by scotthuggins on Jul 14, 2024 12:45:32 GMT -5
Hello everyone!
I've been super busy programming my latest effort for the past 14 months. I've got one major bug left that I've been researching and trying to nail down, but I'm a little stuck.
I'm bank switching a 64k rom project (2 x 32k banks). It uses the "standard" bank switching option in Vide. All is fine during gameplay. The bank switching code is located in the same ROM locations for both banks - I had read not following that rule could get bad results.
The game is humming along fine but at some point it will start speeding up. It does it suddenly and it goes about 40% faster than normal. The visuals and the sounds all are fine, just sped up by about 40-50%! In a way it's funny to experience, but I obviously want to get rid of it. It's not one particular gameplay action or anything specific I can nail down that causes it to happen. It's just around 1-2 minutes of play and it always happens. It seems to not be happening in one bank or the other: it happens randomly in bank 0 or in bank 1.
I had thought I wasn't clearing out variables in RAM at first, but I didn't get any improvements troubleshooting that. I also made sure I wasn't overloading the stack by getting rid of all the pushes and pops to the stack and using temp variables instead.
One thing I haven't tried is painstakingly trying to follow every JSR and making sure it has a corresponding RTS. I'm 99% sure it does, but that's the only thing I can think of....but in the past when that was the problem, the game I was working on at the time just plain crashed. In this case it doesn't crash, it just speeds up a lot and stays that way until you reset the Vectrex or emulator....and then it plays normally again for 1-2 minutes before going into "speed up" mode once again.
Before I do another "deep dive", I wanted to post this to see if anyone has experienced this or has any ideas?
What kind of approaches have you tried on troubleshooting things like this? Any forms of stack management? Are there certain hardware registers I need to look at for this?
Thank you so much for reading and I look forward to any ideas you all might have. Scott
PS: the two banks switch bank and forth with this code. I am thinking this is pretty standard technique.
switchToBank1: ldd #$9FFF ; Prepare DDR Registers % 1001 1111 1111 1111 std >VIA_DDR_b ; all ORB/ORA to output except ORB 5, PB6 goes LOW ; RESET to sensible values ldd #$0100 ; A = $01, B = 0 std >VIA_port_b ; ORB = $1 (ramp on, mux off), ORA = 0 (DAC) ldd #$987F ; A= $98, B = $7F sta >VIA_aux_cntl ; Standard AUX Cont = $98, T1 One shot mode with control of ramp stb >VIA_t1_cnt_lo ; Timer = $7f, RAMP On, after timer expires ramp off jmp main ; Basically jump to main loop
switchToBank0: ldd #$DFFF ; Prepare DDR Registers % 1101 1111 1111 1111 std >VIA_DDR_b ; all ORB/ORA to output except ORB 5, PB6 goes LOW ; RESET to sensible values ldd #$0100 ; A = $01, B = 0 std >VIA_port_b ; ORB = $1 (ramp on, mux off), ORA = 0 (DAC) ldd #$987F ; A= $98, B = $7F sta >VIA_aux_cntl ; Standard AUX Cont = $98, T1 One shot mode with control of ramp stb >VIA_t1_cnt_lo ; Timer = $7f, RAMP On, after timer expires ramp off jmp main
|
|
|
Post by Peer on Jul 14, 2024 15:31:42 GMT -5
… The game is humming along fine but at some point it will start speeding up. It does it suddenly and it goes about 40% faster than normal. The visuals and the sounds all are fine, just sped up by about 40-50%! … Hi Scott, since (unfortunately) there is no way to speed up the Vectrex CPU (other than hardware overclocking), could you elaborate a bit more on what exactly happens? Does the framerate increase? This could happen if the value of the refresh counter Vec_Rfrsh (0xc83d) gets corrupted. The BIOS sets it to 30.000 decimal, resulting in a 50Hz framerate. If for some reason a lower value is stored here, and if your game uses less than 30.000 cycles for its computations, then you will get an increased framerate and the game will run faster. This can easily be checked in Vide, using the "Variables" window. Also, you can use "Tracky" to check the cycles counts per frame. Caveat, the value of Vec_Rfrsh is stored in little-ending format, low-byte in 0xc83d and high-byte in 0xc83e. Hope this helps, Peer
|
|
|
Post by scotthuggins on Jul 14, 2024 15:58:23 GMT -5
Hi Peer,
Thank you for your concise response! Excellent. I had no idea the frame rate can be controlled (so to speak). I use Tracky all the time to monitor how many cycles I am using each frame (to try and keep below 30,000). I use the variables window a lot as well. So I can set a "word" watch perhaps as well for Vec_Rfrsh (0xc83d) since it sounds like it takes up two bytes for this value.
I never knew this existed and am quite sure I can now see if that value is being corrupted. Vide also has a nice feature where you can set a RAM breakpoint, ie: set a breakpoint for execution to stop when a RAM location is being "written" to. So I can, perhaps, find out when the program is corrupting this value.
Thanks again for the response, Peer! How are your projects going? Are you mainly busy with teaching and / or are you able to work on any side projects?
Scott
|
|
|
Post by Malban on Jul 15, 2024 1:20:06 GMT -5
Hi Scott,
Stack-corruption can be watched int the latest Vide version also quite nicely. You can in the debugger enter a memory location (in the executable section)... and each "round" Vide then checks if at that memory location the stack position changed. If you indeed have stack problems... you can find it with that option quite easily.
Haha - I am quite certain I implemented that option when debugging your last game :-).
As always - if you are stuck... you can send me the code and I get tracking :-) ...
Regards Chris
|
|
|
Post by scotthuggins on Jul 15, 2024 9:00:03 GMT -5
I guess we all should be proud of something . I'm proud my buggy code inspired a nice feature to Vide. LOL! I wrote this down and will try it out tonight in VIDE. Very useful. Haha - I am quite certain I implemented that option when debugging your last game :-).
|
|
|
Post by scotthuggins on Jul 15, 2024 9:23:27 GMT -5
Peer,
Your suggestion was spot on! Once the "speed up" started happening, I looked at the value in Vec_Rfrsh (0xc83d) and instead of 30,000, it had a value close to 17,000! I set a RAM breakpoint on Vec_Rfrsh in VIDE to find out what was corrupting it. Turns out it was a call to the bios routine, explosion_snd. Replacing that bios routine call with a "ym" sound converted to asm by the Vide YM Converter tool stopped it from happening. Not sure how many programmers are using explosion_snd, but it might not be stable. Thanks again for pointing this out, I never knew it was possible to change the framerate in code. Maybe we're not supposed to change that...
|
|
|
Post by D-Type on Jul 15, 2024 9:29:31 GMT -5
GCE changed the frame rate for various titles, as shown by Peer, so why shouldn't we?
Actually there's a good reason...screen wobble...but only if you're not in America :-)
|
|
|
Post by Peer on Jul 15, 2024 13:12:11 GMT -5
Hey, it's great to see some life in these forums again
Using Explosion_Snd() is perfectly safe. Many of the original games use it, and so do many of my own games. Perfectly safe, if used correctly So it might be interesting to investigate how you (Scott) used it.
Also, modifying Vec_Rfrsh is perfectly safe. It is just the timer value which is used in Wait_Recal() to sync the frames. If your code uses more cycles in one frame than the value stored in Vec_Rfrsh, then you will get an effective framerate, which might vary and which is worse than the designated one. If your code uses less cycles than Vec_Rfrsh, then (and only then) you will get the designed (and stable) framerate.
|
|
|
Post by scotthuggins on Jul 15, 2024 22:19:53 GMT -5
I love this forum. It was a lot of fun when Cavern Rescue was being coded - talking to people here was a blast...and then, of course, the absolute awesomeness that is Vector Wars (we're only 3 and 1/2 months away!).
So... how I was using it was pretty simple (so I thought). I used the explosion editor in Vide to generate the 4 bytes for an explosion sound that sounded reasonably good. And it worked just fine the first 3-4 times that it was called during gameplay. But, inevitably, it was corrupting Vec_Rfrsh.
*** after typing the code in here the last 10 minutes to show what I was doing....it totally jumped out to me: I wasn't using it correctly! Typing it to you guys showed me the err in my ways.
Here's how I was using it:
expl_snd_ply_ship: ; these four bytes were generated from explosion editor in VIDE db $19,$01,$00,$02
main: . . .
jsr wait_recal
. . .
;init the explosion when player is shot lda #-1 sta Vec_Expl_Flag ldu #expl_snd_ply_ship ; point to explosion sound data . . . ; if there is an explosion happening, continue to play the explosion sound jsr DP_to_C8 jsr Explosion_Snd . . .
bra main
See the issue?? I know better - but still didn't see it until I typed here in this thread.
|
|
|
Post by Malban on Jul 16, 2024 4:13:24 GMT -5
Doesn't look to bad... The only thing could be loading the U-Register too early... depending on what lies between that and the call to "jsr Explosion_Snd".
The thing is - the Explosion_Snd() function - is actually TWO functions.
a) init - explosion
If invoked with the hi-bit set in "Vec_Expl_Flag" (like your lda #$ff -> sta Vec_Expl_Flag)
Then it will initiate an NEW explosion - and it will take the data pointer in the U-Register as the source of the new explosion definition. The init function sets the high bit in Vec_Expl_Flag to 0... and each subsequent call of Explosion_Snd() will behave as a "player".
b) play - explosion If invoked with the hi-bit cleared in "Vec_Expl_Flag" The current ongoing explosion is continued till the explosion timer reaches zero. After that nothing happens at all.
---
It follows - when you first call Explosion_Snd() it uses the "U" register, if you just play an explosion the U register is not used.
---
Still - I have not debugged the Explosion_Snd() to depth just now - but I can't see in the two minutes I looked at it - how it may be possible to corrupt the "Vec_Rfrsh"...
|
|
|
Post by Peer on Jul 16, 2024 4:40:20 GMT -5
Doesn't look to bad... The only thing could be loading the U-Register too early... depending on what lies between that and the call to "jsr Explosion_Snd".
The thing is - the Explosion_Snd() function - is actually TWO functions.
a) init - explosion
If invoked with the hi-bit set in "Vec_Expl_Flag" (like your lda #$ff -> sta Vec_Expl_Flag)
Then it will initiate an NEW explosion - and it will take the data pointer in the U-Register as the source of the new explosion definition. The init function sets the high bit in Vec_Expl_Flag to 0... and each subsequent call of Explosion_Snd() will behave as a "player".
b) play - explosion If invoked with the hi-bit cleared in "Vec_Expl_Flag" The current ongoing explosion is continued till the explosion timer reaches zero. After that nothing happens at all.
---
It follows - when you first call Explosion_Snd() it uses the "U" register, if you just play an explosion the U register is not used.
---
Still - I have not debugged the Explosion_Snd() to depth just now - but I can't see in the two minutes I looked at it - how it may be possible to corrupt the "Vec_Rfrsh"...
Explosion_Snd() is actually THREE funtions!
An "init" part, a "play" part, and a "set volumes" part. The last one starts at address 0xf9ca, and is also a BIOS routine of its own. It is listed in the original Vectrex Programmer's Manual Part 2 and called "SETAMP", but has not made it into the "Bruce Tomlin" naming convention and is not shown as entry-point into the non-WT BIOS listings.
The SETAMP part goes backwards through the RAM sound working-buffer, and it should step only through the volume registers, but if the value of Vec_Expl_Chans (0xc854) is corrupted, then it can go further down. And guess what is located directly beneath the sound working-buffer? Vec_Rfrsh!
So it might be worth investigating if Vec_Expl_Chans got corrupted, or any other part of the sound working-buffer. Calling Explosion_Snd() with some garbage value in the u register, should only result in garbled sound, but not in corruption of Vec_Expl_Chans or the working buffer data. The actual culprit might be somewhere else.
|
|
|
Post by Malban on Jul 16, 2024 5:49:24 GMT -5
Oh...
Never seen that... yes in Bruce's Listing it would be that part: LF9CA: LDA <Vec_Expl_Chans LDX #Vec_Music_Wk_7 LF9CF: TSTA ;Exit if all channels done BEQ LF9DB_RTS LEAX -1,X ;Point to next register (8-10) LSRA BCC LF9CF STB ,X ;Store noise value if chan in use BRA LF9CF
The only time that Vec_Rfrsh_hi can be corrupted is if the Vec_Expl_Chans bit 7 is 1. Which of course makes no sense -> hence corruption :-).
|
|
|
Post by Peer on Jul 16, 2024 6:06:53 GMT -5
Oh...
Never seen that... yes in Bruce's Listing it would be that part: LF9CA: LDA <Vec_Expl_Chans LDX #Vec_Music_Wk_7 LF9CF: TSTA ;Exit if all channels done BEQ LF9DB_RTS LEAX -1,X ;Point to next register (8-10) LSRA BCC LF9CF STB ,X ;Store noise value if chan in use BRA LF9CF
The only time that Vec_Rfrsh_hi can be corrupted is if the Vec_Expl_Chans bit 7 is 1. Which of course makes no sense -> hence corruption :-). Yes, corruption is a common evil
Btw, during the past weeks I have been (once more) digging my way through the BIOS and the various BIOS listings. I have put together my own version and started rewriting the comments and correcting several minor flaws. I am also using several new alias names for certain BIOS routines, which I find much more telling. Especially for the trigonometrics routines. Ths is still ongoing work.
|
|
|
Post by scotthuggins on Jul 16, 2024 13:39:55 GMT -5
Wow, great insight from you two! Love it. And good idea about setting U register too early. I need to be careful on that one.
Well, here's what seems to have fixed it for me. It was the location of where I was calling DP_to_c8 and Explosion_Snd(). Both, I believe, need to happen BEFORE wait_recal. The initialization, I think, can happen whenever, but "continuing" the explosion sound should all happen before wait_recal. I've been messing with it for a while now, and Vec_Rfrsh, so far, has been keeping consistent at 30,000.
expl_snd_ply_ship: ; these four bytes were generated from explosion editor in VIDE db $19,$01,$00,$02
main: . . . ; if there is an explosion happening, continue to play the explosion sound jsr DP_to_C8 jsr Explosion_Snd
jsr wait_recal
. . .
;init the explosion when player is shot lda #-1 sta Vec_Expl_Flag ldu #expl_snd_ply_ship ; point to explosion sound data . . . . . .
bra main
|
|
|
Post by Peer on Jul 16, 2024 14:54:45 GMT -5
Wow, great insight from you two! Love it. And good idea about setting U register too early. I need to be careful on that one. Well, here's what seems to have fixed it for me. It was the location of where I was calling DP_to_c8 and Explosion_Snd(). Both, I believe, need to happen BEFORE wait_recal. The initialization, I think, can happen whenever, but "continuing" the explosion sound should all happen before wait_recal. I've been messing with it for a while now, and Vec_Rfrsh, so far, has been keeping consistent at 30,000. expl_snd_ply_ship: ; these four bytes were generated from explosion editor in VIDE db $19,$01,$00,$02 main: . . . ; if there is an explosion happening, continue to play the explosion sound jsr DP_to_C8 jsr Explosion_Snd
jsr wait_recal . . . ;init the explosion when player is shot lda #-1 sta Vec_Expl_Flag ldu #expl_snd_ply_ship ; point to explosion sound data . . . . . . bra main The main loop is a LOOP. It goes round in circles. There is no "before" or "after" Wait_Recal(). Or, in other words, "after Wait_Recal()" is the same as "before Wait_Recal()". This is irrelevant. What is not irrelevant is the huge "space" between the ldu and the jsr Explosion_Snd, i.e. the actual code which you replaced by placeholder dots in the snippet above. If this code somehow alters the contents of the u register, be it either explicitly, or implicitly by calling other routines which clobber u, then things will go awry. The ldu #expl_snd_ply_ship should not be separated from the jsr Explosion_Snd, it should be done right before the subroutine call. But this still does not explain the corruption of Vec_Rfrsh, and likely Vec_Expl_Chans. The rearranging you did in your code apparently (and maybe just accidentally?) fixes the symptom, but I do not see how it could fix a potential cause of the framerate problem.
|
|