gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

DS development > Coding a game in pure asm using DevKitPro

#165773 - headspin - Tue Jan 06, 2009 3:46 pm

A friend and I decided to try and rewrite an old C64 game he wrote (Warhawk) to the NDS. I wanted to use the very latest DevKitPro and have got a few demos working so far. Since there are very few examples written in pure asm for DS (that we can find) we are learning as we go. We wanted to write it in asm because he has a background in assembly and for the challenge. My background is C so I'm learning asm as I go.

Now we are at the point where we want a level of the game scrolling up both screens. The demo is using a 64x64 tile mode and doing dmaCopy's each 256 lines scrolled for maximum speed. We are using grit's -mLs option so the data is stored in screen blocks ready for dmaCopy'ing. For some reason when I try to scroll BG1 where I'm writing the map data to (as stated in the REG_BG1CNT / REG_BG1CNT_SUB registers) it's actually writing the data to BG0. I can tell this because when I change the scroll registers to REG_BG0VOFS / REG_BG0VOFS_SUB it starts scrolling the map. It just doesn't match up with how the BG's have been assigned. It leads us to believe that the BG's must use certain areas in VRAM but I can't find any documenation to back that theory up.

So I decided to write the demo in C to see if we had messed up in some way. Apart from the screens needing an lcdSwap() the C code seemed to work fine. It's written a bit strangely to keep the code as close to the asm version as possible.

Another thing is we can't seem to get vblank working on hardware. Even running the C version on hardware it does not wait for vblank. That is using the "combined template" example from DevKitPro as the starting point. It scrolls the entire level in under a second. It runs fine in an emulator.

I'm going to attach both the asm and C version so hopefully someone can take a look and help in some way. Thanks for your time :)

[Images not permitted - Click here to view it]
leveltest_asm.zip
leveltest_c.zip
_________________
Warhawk DS | Manic Miner: The Lost Levels | The Detective Game

#165774 - dovoto - Tue Jan 06, 2009 4:23 pm

Well, a quick glance tells me you are treating the background scroll registers as readable...they are write only. This is definitly going to cause you issues although I am not certain if this could cause the issue you are describing.

Background tile and graphics can only be placed in memory set aside for that engines backgrounds....there is no way for a sub background to use or share the tiles or map in a main background for instance.
_________________
www.drunkencoders.com

#165775 - shaymanjw - Tue Jan 06, 2009 4:39 pm

I think you might need an irqEnable(IRQ_VBLANK) at the start of main() somewhere (for the VBLANK problem obviously) (I could be wrong).

#165776 - dovoto - Tue Jan 06, 2009 4:50 pm

shaymanjw wrote:
I think you might need an irqEnable(IRQ_VBLANK) at the start of main() somewhere (for the VBLANK problem obviously) (I could be wrong).


This is no longer required as irqs are initialized and the vblank irq enabled prior to main as of the last release of libnds.
_________________
www.drunkencoders.com

#165781 - headspin - Tue Jan 06, 2009 6:48 pm

Wow I didn't know the scroll registers were read only. Are you sure thats right? The asm code that reads from them is working just not in the background we expect it to. And it's run on hardware before so how can that be?

Also we are not using the swiWaitForVBlank() because we are not linking in libnds for the asm project. So we were trying to use a modified example from GBAGuy's asm tutorial for the GBA.

Code:
    ldr r0, =REG_VCOUNT
waitVBlank:                           
    ldrh r2, [r0]                        @ read REG_VCOUNT into r2
    cmp r2, #(SCREEN_HEIGHT + 1)        @ 193 is, of course, the first scanline of vblank
    bne waitVBlank                        @ loop if r2 is not equal to (NE condition) 193


It doesn't work, and I also tried to modify another version which I translated from C

Code:
// C Code

void waitForVBlank(void)
{
    while(!(REG_DISPSTAT & DISP_IN_VBLANK));
    while((REG_DISPSTAT & DISP_IN_VBLANK));
}

@ My (possibly wrong) translation to asm

    ldr r0, =REG_DISPSTAT
waitVBlankA:
    ldr r1, [r0]
    tst r1, #DISP_IN_VBLANK
    bne waitVBlankA
waitVBlankB:
    ldr r1, [r0]
    tst r1, #DISP_IN_VBLANK
    beq waitVBlankB


Again it didn't work but whats even stranger is the swiWaitForVBlank() in the C version which *does* use libnds still doesn't seem to be waiting for vblank on hardware. So it's like we are screwing something up somewere.

In the demo were not sharing any map or tile data as were using the macro's BG_TILE_RAM(n), BG_TILE_RAM_SUB(n), BG_MAP_RAM(n), BG_MAP_RAM_SUB(n) to make sure we write to the correct location for each screen.

Is there something that libnds does that we should be doing? I copied the initSystem() routine into our app (minus the interrupt code) but that didn't help. If the scroll regs are read only I guess that's a good place to start - it's easy enough to create a variable to keep track of it.
_________________
Warhawk DS | Manic Miner: The Lost Levels | The Detective Game

#165794 - Chase-san - Wed Jan 07, 2009 1:44 am

headspin wrote:
Wow I didn't know the scroll registers were read only. Are you sure thats right? The asm code that reads from them is working just not in the background we expect it to. And it's run on hardware before so how can that be?


He said WRITE only, not read only.

#165801 - headspin - Wed Jan 07, 2009 8:03 am

Chase-san wrote:
He said WRITE only, not read only.


That was obviously a typo ;)
_________________
Warhawk DS | Manic Miner: The Lost Levels | The Detective Game

#165807 - Flash - Wed Jan 07, 2009 9:37 am

The thing about the scroll registers is that our scroll routine relys on the reading of them to update the bit scroll and check for the background block scroll on a page basis, and this works? If the registers are write only then I cannot see how this code would work?

We have 2 main problems at the moment that have been driving both HK and I crazy for the last week,

No matter what we do with checks for VBLANK in the code, the game runs much too fast during emulation and faster still on real hardware. I have successfuly split every raster line to perform as BGxHOFS on a per-line basis to create an old-school raster wobble, and this works fine - Indicating that the VBLANK check works? The problem is in the code posted prior, we are trying to check for the screen base, then do our code, and return to execution. But this is not leaving the update as 1/60th for some reason.

Secondly, access to BG0 and BG2 works as expected, but we cannot appear to make use of BG1 and BG3. Our scroll code works fine using BG0 (and BG0 + BG2 in current version) but what is strange is that swapping the scroll to BG1 causes the display of garbage, where we were expecting it to work the same but on BG1? Checking the MAP and data in Dualis shows that this is still mapped to BG0? Regardless of the setting of the BG1 and BG3 flags, the code carries on working with BG0 and BG2?

Any help in this would be very welcome as this has now become a major stumbling block in the progression of this code!

Thanks in advance!

#165808 - eKid - Wed Jan 07, 2009 10:26 am

Flash wrote:
The thing about the scroll registers is that our scroll routine relys on the reading of them to update the bit scroll and check for the background block scroll on a page basis, and this works? If the registers are write only then I cannot see how this code would work?

GBAtek says that they are read only (gba specific), I just confirmed this for DS. So you must be missing something...

The reason why you can't use BG1/BG3 is because you are writing the wrong sized data to the registers.

Code:
   ldr r0, =REG_BG1CNT            @ Set main screen BG0 format to be 64x64 tiles at base address
   ldr r1, =(BG_COLOR_16 | BG_64x64 | BG_MAP_BASE(4) | BG_TILE_BASE(5) | BG_PRIORITY(1))
   str r1, [r0]


This is a 32-bit write, the registers are 16-bit, REG_BG1CNT/BG3CNT are 'mis-aligned' by 2 bytes. When you write 32 bits to a misaligned address then the write will be force-aligned (i think there may be some other weird behavior sometimes too [with different processor versions])...

Anyway, what's going on is when you write to bg1/bg3cnt it is actually writing the half-word to BG0/BG2CNT and zero filling (the top half-word of your data is zero) the BG1/BG3CNT. To fix, write 16-bits instead with the 'strh' opcode.

#165810 - headspin - Wed Jan 07, 2009 11:02 am

eKid: Thanks alot mate! The funny thing is I swore that I checked those registers and was certain they were 32bit. But you were right looking in background.h we can see.

Code:
#define REG_BG0CNT (*(vu16*)0x4000008)


So using strh fixed that and now the scrolling works :) Can't believe we missed that actually.

Now if the scroll registers are write only that might be causing other issues. I'll look into it, thanks again.
_________________
Warhawk DS | Manic Miner: The Lost Levels | The Detective Game

#165812 - headspin - Wed Jan 07, 2009 12:20 pm

Yep, I changed the code to use a variable instead of attempting to read the scroll registers and vblank now works and it scrolls nice and smoothly on hardware! Thanks to everyone who helped. I'm sure we'll be back with more questions in the near future ;)
_________________
Warhawk DS | Manic Miner: The Lost Levels | The Detective Game