Page 3 of 9

Re: TriggerFx sometimes gets stuck on a note

Posted: Thu Jul 09, 2015 5:43 am
by Artcfox
uze6666 wrote:Hi Matt, welcome to the Uzebox community! Been offline for a couple day and just saw your thread. I also think the lack of cycles is what's causing the issue. Normally the game should gracefully slow down the main program without much other effects. Though, in this case it seems to also affect the kernel that is running at a fixed 60hz speed. I'll try to think why it could happens. If you can record an example with camstudio ans PM it to me it could help understanding the issue (though no garantees it's fixable).
Thanks! I'm starting to think that's the case, because as I'm optimizing more stuff, it's getting harder and harder to reproduce. I actually don't have Windows installed at home, I've been doing all of my development and testing under Linux, but I can PM you the .hex file.
The channel disabling switches are for the vsync mixer to lower the cpu cycles and does nothing for the inline mixer so you can safely remove it.
Cool. I checked the size of the .hex with and without, and when there was no difference, I removed that flag.
Have you tried using Atmel's simulator in AvrStudio and AtmelStudio? It has a cycle counter and I use it heavily when doing assembler stuff, specially video modes. Just put breakpoints in you code and calculate the elapsed cycles.
I've never actually used AVR/Atmel Studio, except once on a borrowed computer when I first got my AVRISP mkII in order to update its firmware. That does sound like a useful tool though! I mostly count cycles by hand looking at the generated assembly, or for the longer stuff, just use a logic analyzer that can automatically count cycles for me. I guess it's because I'm used to doing hardware stuff with the AVR microcontrollers. This is the first time where I actually feel like I'm writing software for one, rather than firmware.
NTSC is 29.97 frames per second, with each frames made of two interlaced fields. At the end of each fields there a vsync retrace and that's what the Uzebox internal interrupts is based on, the field rate that is about 60hz. So when you WaitVsync(1), you are really waiting until the next field retrace (up to 1/60 of a second). In reality the kernel will render (at least actually) each field independently at 60hz with whatever the vram,sprites, etc are setup at that time. So not a full sprites that switch @ 30fps.
Interesting, I think I'll have to do some experiments to solidify it in my head. I've read and re-read the documentation so many times, I think I'm going to need to poke it with a stick before I can fully understand it.
You can run uzem.exe -i yourgame.hex to enable interlacing. By default it just draws the even fields twice. Things are smoother, but there's obvious shearing artifacts between scanlines for moving objects. That's the reason why it's optional.
I'll give that a shot to see if it changes how it looks, though I'm not flickering my sprites, so I'm not sure it would show anything different.
This is indeed a great way to at least test to see if it's a cycle thing. You can use the SetRenderingParameters() function to lower the number of vertical lines to be rendered.
I think I'd rather add

Code: Select all

KERNEL_OPTIONS += -DSCREEN_TILES_V=XX
to the Makefile, because all the code I wrote is keyed off the SCREEN_TILES_V/H defines, so when I change those defines in the Makefile then the entire game automatically compensates, plus all of my bounds checking becomes compares against compile-time constants.
On a related note regarding uzem, MAME will soon ship (if not already) with partial Uzebox emulation. Since they go for perfect emulation at the expense of speed I don't know if it will run full speed on current machines. Could be another way to tests things when uzem seems to act weird.

Looking forward to see your game! :P
Sweet! I'm pretty pumped. I spent quite a few months looking at the documentation and design of the UZEBOX, trying to figure out if I wanted to build my own hardware from scratch, or just purchase a kit, but I have so little free time these days that I just bought the kit, and now it's hard for me to think about anything else other than this game I've been working on. :ugeek:

Re: TriggerFx sometimes gets stuck on a note

Posted: Thu Jul 09, 2015 7:51 pm
by uze6666
've never actually used AVR/Atmel Studio, except once on a borrowed computer when I first got my AVRISP mkII in order to update its firmware. That does sound like a useful tool though! I mostly count cycles by hand looking at the generated assembly, or for the longer stuff, just use a logic analyzer that can automatically count cycles for me. I guess it's because I'm used to doing hardware stuff with the AVR microcontrollers. This is the first time where I actually feel like I'm writing software for one, rather than firmware.
Well, you got to try it, it's a *real* time saver and without it I would have gone nuts and there would be no uzebox at all. The cycle count is very important, but it's the relationship with all the internals of the AVR you see at the same time that saves the day. Things like register states, the current timer counts, flags, etc etc. When doing time sensitive asm stuff and you don't rely on external stimuli or data sources like the sd interface it's really the best tool IMHO. I just bought an ICE lately so I may try that sometime, but not for the uzebox since the JTAG pins are taken by the video dac.

Always wanted to do a quick tutorial on this. Alas, been derailed by some things...enclosure, portable, new video modes, keyboard interface, uzenet, music, uzem fixes, game, etc etc!

Re: TriggerFx sometimes gets stuck on a note

Posted: Thu Jul 09, 2015 9:26 pm
by Artcfox
Wow, that does sound really awesome. For a long time I was working with AVRs completely blind, no debugger, no logic analyzer, and no oscilloscope, but now I'm quick to pull out the logic analyzer when I want to see what's going on.

Re: TriggerFx sometimes gets stuck on a note

Posted: Thu Jul 09, 2015 11:35 pm
by uze6666
You should really get one of those inexpensive AVR ONE in system debugger modules. In addition to programming the chip, you can actually put breakpoints *in* the AVR chip and see all that is going on in the IDE and do step-by-step etc. Most MegaAVR are supported. I bought one when my ISPAVR fried but didn't use it yet for debugging.

Re: TriggerFx sometimes gets stuck on a note

Posted: Fri Jul 10, 2015 3:27 pm
by Artcfox
That sounds wicked powerful. From time to time I check to see the Linux compatibility of the debugging tools, but I haven't actually made the jump yet. Maybe something like that will finally force me to learn more about how to use more than just the basics of gdb from within emacs for source level debugging. I've only messed around with that a bit in the past, but haven't touched it since.

I have to admit that I've gotten very comfortable with my development environment (schematic, pcb, coding) under Linux, so I'm pretty reluctant to switch to a different OS, and a different IDE. Wow, when did I become such a curmudgeon? :?

Re: TriggerFx sometimes gets stuck on a note

Posted: Fri Jul 10, 2015 6:55 pm
by uze6666
Seems some version of AVrStudio runs fine on Wine: https://appdb.winehq.org/objectManager. ... on&iId=402. I use the old v4.19 which is just fine for my needs. 4.18sp2 was good too.

Re: TriggerFx sometimes gets stuck on a note

Posted: Fri Jul 10, 2015 9:04 pm
by Artcfox
Nice!

BTW: I PM'd you a copy of the .hex file, since I figured that might be more helpful than a screen recording, though unfortunately it is a bit harder to trigger the issue now.

Re: TriggerFx sometimes gets stuck on a note

Posted: Fri Jul 10, 2015 10:33 pm
by uze6666
AvrStudio is good for the simulator v2. Alas, if you want ICE, you'll need Atmel Studio which is windows only as far as I know.

Re: TriggerFx sometimes gets stuck on a note

Posted: Thu Jul 30, 2015 5:17 pm
by Artcfox
So I think that I may have finally solved the issue! (And if I haven't solved it 100%, then by making this post, the issue should immediately rear its ugly head again, just to prove me wrong. :P )

I added a chunk of code* (modified to use __bss_end instead of _end) that runs before main() and fills the entire stack with a canary value, and then every frame I call a function that walks the stack backwards and counts the number of canary values still present. That gives me a high water mark of "most stack consumed" and I found that I only had ~4 bytes free. Then I remembered what Lee had said about the inline mixer needing to push all registers to stack if you ever go over on cycles (Lee, does it really need to push all 32 registers?), and then it clicked in my head that if I'm unlucky enough to have the main() code interrupted when it's using a lot of stack, and then the kernel pushes all the registers to RAM, then that's probably blowing the stack, but not enough for it to completely screw up everything—just enough for it to monkey with the sounds. :idea:

Once I realized that I was running so close to the RAM limit, I decreased my RAM_TILE_COUNT from 28 down to 24, and so far I haven't been able to get any notes to stick when calling TriggerFx(). Based on the call to StackCount() it would appear that my high water mark is now 25 bytes free (canary values that haven't ever been overwritten) so I think that I'm home free now. :D

* http://www.avrfreaks.net/forum/soft-c-a ... tack-usage

It might be worth it to conditionally include the stackmon code in the default kernel, so anyone can just call StackCount() to get the amount of free RAM, but it wasn't that hard to modify it and add to the Makefile.

For the benefit of all, here are the modified stackmon files (so it works with the ATmega644). The linker automatically calls the StackPaint() function before main(), so the only function you should actually call is StackCount().

Code: Select all

/***************************************************************************
 * stackmon.h: Stack usage monitoring for ATmega88/168.
 * Michael C McTernan, Michael.McTernan.2001@cs.bris.ac.uk
 *
 * This program is PUBLIC DOMAIN.
 * This means that there is no copyright and anyone is able to take a copy
 * for free and use it as they wish, with or without modifications, and in
 * any context, commercially or otherwise. The only limitation is that I
 * don't guarantee that the software is fit for any purpose or accept any
 * liability for it's use or misuse - this software is without warranty.
 ***************************************************************************/

#ifndef STACKMON_H
#define STACKMON_H

/**************************************************************************
 * Nested Include Files
 **************************************************************************/

#include <stdint.h>

/**************************************************************************
 * Manifest Constants
 **************************************************************************/

/**************************************************************************
 * Type Definitions
 **************************************************************************/

/**************************************************************************
 * Macros
 **************************************************************************/

/**************************************************************************
 * Function Prototypes
 **************************************************************************/

#if !defined(ON_PC)
uint16_t StackCount(void);
#else
#define StackCount()    0
#endif

#endif /* STACKMON_H */

/* END OF FILE */

Code: Select all

/***************************************************************************
 * stackmon.c: Stack usage monitoring for ATmega88/168.
 * Michael C McTernan, Michael.McTernan.2001@cs.bris.ac.uk
 *
 * This program is PUBLIC DOMAIN.
 * This means that there is no copyright and anyone is able to take a copy
 * for free and use it as they wish, with or without modifications, and in
 * any context, commercially or otherwise. The only limitation is that I
 * don't guarantee that the software is fit for any purpose or accept any
 * liability for it's use or misuse - this software is without warranty.
 ***************************************************************************/

/**************************************************************************
 * Include Files
 **************************************************************************/

#include <stdint.h>
#include "stackmon.h"

/**************************************************************************
 * Manifest Constants
 **************************************************************************/

/** Magic value for testing if the stack has overran the program variables.
 */
#define STACK_CANARY 0xc5

/**************************************************************************
 * Type Definitions
 **************************************************************************/

/**************************************************************************
 * Variables
 **************************************************************************/

/** Gain access to linker symbol for end of variable section.
 * This symbol is defined to be the address of the first unused byte of SRAM.
 * The stack is by convention at the top of the SRAM, growing down towards
 * the address at which this variable resides.
 */
extern uint8_t __bss_end;

/** Gain access to linker symbol for base of the stack.
 * This symbol is defined to be the address at the bottom of the stack.
 */
extern uint8_t __stack;

/**************************************************************************
 * Macros
 **************************************************************************/

/**************************************************************************
 * Local Functions
 **************************************************************************/

void StackPaint(void) __attribute__ ((naked)) __attribute__ ((section (".init1")));

/** Fill the stack space with a known pattern.
 * This fills all stack bytes with the 'canary' pattern to allow stack usage
 * to be later estimated.  This runs in the .init1 section, before normal
 * stack initialisation and unfortunately before __zero_reg__ has been
 * setup.  The C code is therefore replaced with inline assembly to ensure
 * the zero reg is not used by compiled code.
 *
 * \note This relies on the linker defining \a __bss_end and \a __stack to define
 *        the bottom of the stack (\a __stack) and the top most address
 *        (\a __bss_end).
 */
void StackPaint(void)
{
#if 0
    uint8_t *p = &__bss_end;

    while(p <= &__stack)
    {
        *p = STACK_CANARY;
        p++;
    }
#else
    __asm volatile ("    ldi r30,lo8(__bss_end)\n"
                    "    ldi r31,hi8(__bss_end)\n"
                    "    ldi r24,lo8(0xc5)\n" /* STACK_CANARY = 0xc5 */
                    "    ldi r25,hi8(__stack)\n"
                    "    rjmp .cmp\n"
                    ".loop:\n"
                    "    st Z+,r24\n"
                    ".cmp:\n"
                    "    cpi r30,lo8(__stack)\n"
                    "    cpc r31,r25\n"
                    "    brlo .loop\n"
                    "    breq .loop"::);
#endif
}


/**************************************************************************
 * Global Functions
 **************************************************************************/

/** Count unused stack space.
 * This examines the stack space, and determines how many bytes have never
 * been overwritten.
 *
 * \returns  The count of bytes likely to have never been used by the stack.
 */
uint16_t StackCount(void)
{
    const uint8_t *p = &__bss_end;
    uint16_t       c = 0;

    while(*p == STACK_CANARY && p <= &__stack)
    {
        p++;
        c++;
    }

    return c;
}

/* END OF FILE */

Re: TriggerFx sometimes gets stuck on a note

Posted: Thu Jul 30, 2015 11:16 pm
by uze6666
Woa, only 4 bytes free?!? You're really running after trouble! As a rule of thumb never go over 97-98% ram usage to leave some stack for the kernel interrupt. I can tell you that sound issues already happened to me a long time ago when I was pushing the ram a bit too tight (that and random crashes and glitches). I never profiled the kernel for its top stack usage but it's probably in the range 60-80 bytes. Usage is spread across:
  • Video rendering interrupt: pushes all registers and flags (33 bytes)
  • Video mode: uses the stack at varying levels, probably 10 bytes in average
  • Inline mixer: pushes 5 registers
  • Music engine: this one is in C and I tried to limit the number of function calls. But it could eat up to 50 bytes.
So yeah, if you triggerFX and the internal call stack is at it deepest (2 sub functions calls) and a rendering interrupt occurred at this precise time, I could well create such issue.