Compiling your first NDS asm binary

headkaze · 26378

Offline headkaze

  • Administrator
  • Blue Gene Super Computer
  • **********
    • Posts: 7838
on: December 20, 2008, 03:00:06 am
1. If you haven't already followed the Setting up the compiler I suggest you do that first
2. Take a look at helloworld helloworld.zip
3. Create a folder called devkitPro\source\nds\asmtest

The NDS uses both an ARM7 and ARM9 binary that is linked into the one .nds

ARM7 main.s

Code: [Select]
.arm
.text
.global main
main:
    b main

ARM9 main.s

Code: [Select]
.arm
.align
.global initSystem
.global main

initSystem:
    bx lr

main:
    mov r0,#0x04000000      @ I/O space offset
    mov r1,#0x3             @ Both screens on
    mov r2,#0x00020000      @ Framebuffer mode
    mov r3,#0x80            @ VRAM bank A enabled, LCD
 
    str r1,[r0, #0x304]     @ Set POWERCNT
    str r2,[r0]             @     DISPCNT
    str r3,[r0, #0x240]     @     VRAMCNT_A
 
    mov r0,#0x06800000      @ VRAM offset
    mov r1,#31              @ Writing red pixels
    mov r2,#0xC000          @ 96k of them
 
lp: strh r1,[r0],#2         @ Write a pixel
    subs r2,r2,#1           @ Move along one
    bne lp                  @ And loop back if not done

nf: b nf                    @ Sit in an infinite loop to finish

3. Run "make" from the command line to compile

Thanks to Strager, Blasty and LiraNuna for help with this. This is based on Two9A's The Smallest NDS File source.

All the demo source available in this thread updated for devkitARM r27

helloworld.zip
showpic.zip
showpic_combined.zip
dualpics.zip
text.zip
sprites.zip
sprites_rs.zip
sound.zip
fx.zip
« Last Edit: January 06, 2010, 07:40:35 pm by headkaze »



Offline headkaze

  • Administrator
  • Blue Gene Super Computer
  • **********
    • Posts: 7838
Reply #1 on: December 21, 2008, 05:32:16 pm
For our next example we will display a picture

Code: [Select]
   .arm
    .align
    .global initSystem
    .global main

initSystem:
    bx lr

main:
    mov r0,#0x04000000                     @ I/O space offset
    mov r1,#0x3                                 @ Both screens on
    mov r2,#0x00020000                      @ Framebuffer mode
    mov r3,#0x80                               @ VRAM bank A enabled, LCD
 
    str r1,[r0, #0x304]                         @ Set POWERCNT
    str r2,[r0]                                     @     DISPCNT
    str r3,[r0, #0x240]                         @     VRAMCNT_A

    mov r0,#0x06800000                       @ make r0 a pointer to screen memory VRAM is 0x06800000.
    ldr r1,=picBitmap                            @ make r1 a pointer to your bitmap data
    mov r3,#0x6000                             @ Half of 96k (2 pixels at a time)

loop:
    ldr r2,[r1],#4                                 @ Loads r2 with the next two pixels from the bitmap data (pointed to by r1).
    str r2,[r0],#4                                @ Write two pixels
    subs r3,r3,#1                                @ Move along one
    bne loop                                       @ And loop back if not done

nf: b nf                                            @ Sit in an infinite loop to finish

showpic.zip
showpic_combined.zip
« Last Edit: January 06, 2010, 07:19:35 pm by headkaze »



Offline flash

  • Administrator
  • Blue Gene Super Computer
  • **********
    • Posts: 13180
Reply #2 on: December 21, 2008, 07:00:55 pm
The only thing that I find a bit confusing is makefiles and also how it knows where to find the bitmap (png) in the make (ldr r1,=picBitmap)
I can see the png refs in the makefile but have no idea where the ldr picBitmap comes from as a memory reference.

I bet I am being dumb again!! :)

Coding for the love of it!


Offline headkaze

  • Administrator
  • Blue Gene Super Computer
  • **********
    • Posts: 7838
Reply #3 on: December 21, 2008, 07:07:02 pm
The only thing that I find a bit confusing is makefiles and also how it knows where to find the bitmap (png) in the make (ldr r1,=picBitmap)
I can see the png refs in the makefile but have no idea where the ldr picBitmap comes from as a memory reference.

I bet I am being dumb again!! :)

Check in the build folder you will see the makefile generates a file called pic.s. It's an assembly file and in that you will see ".global picBitmap" as the reference to it.

Also I've uploaded the example again to include a combined one that you can open directly in Programmers Notepad.



Offline headkaze

  • Administrator
  • Blue Gene Super Computer
  • **********
    • Posts: 7838
Reply #4 on: December 22, 2008, 11:49:29 am
Time for another example, this one was written by me and includes a whole bunch of #define's for registers I've cut and pasted from the C header files. Should make things alot easier to follow this way.

Too much code to post here so check out the attachment.

dualpics.zip
« Last Edit: January 06, 2010, 07:23:25 pm by headkaze »



Offline flash

  • Administrator
  • Blue Gene Super Computer
  • **********
    • Posts: 13180
Reply #5 on: December 22, 2008, 01:00:59 pm
That is great HK..

I will have a real look hopefully tonight when I get home...

Sweet! (In the words of Cartman)

Coding for the love of it!


Offline headkaze

  • Administrator
  • Blue Gene Super Computer
  • **********
    • Posts: 7838
Reply #6 on: December 27, 2008, 06:23:29 am
Okay next up we have a demo to display text on the screen. It demonstrates using a tile mode.

text.zip
« Last Edit: January 06, 2010, 07:26:10 pm by headkaze »



Offline flash

  • Administrator
  • Blue Gene Super Computer
  • **********
    • Posts: 13180
Reply #7 on: December 27, 2008, 01:10:53 pm
That is a really nice and tidy bit of code for a C coder ( ;) )

Well done HK, That will come in handy for outputing values in bug testing also. Even just outputing an ascII character (rather than a number value) can make things so much clearer when things are going abit awry!

I will have a nice play when I get home from work, and perhaps call on you for some help with initialising the displays for a twin screen vertical tlemap scroller  ;)

Can't wait to get home and have another play!

Ok.. Have had a play and have a couple of questions

you reference fontpal, what is this as I cannot find the pallete? Found it - it is auto generated by grit :)
SCREEN_BASE_BLOCK_SUB(29), what is (29) for?

I do need a bit more by way of descriptions of set-up and manipulation of the DS screen modes (pretty please)
« Last Edit: December 27, 2008, 10:16:12 pm by Flash »

Coding for the love of it!


Offline headkaze

  • Administrator
  • Blue Gene Super Computer
  • **********
    • Posts: 7838
Reply #8 on: December 28, 2008, 02:11:45 am
SCREEN_BASE_BLOCK_SUB(29) is the place where I write the map to and CHAR_BASE_BLOCK_SUB(0) is the place where I write the tile data to. You tell the background which part of memory your going to be using for it like so.

Code: [Select]
ldr r0, =REG_BG0CNT_SUB            @ Set sub screen format to be tiles
ldr r1, =(BG_COLOR_16 | BG_32x32 | BG_MAP_BASE(29) | BG_TILE_BASE(0))
str r1, [r0]

You can change the values for writing maps and tile data to different VRAM locations. You can view the VRAM layout for background memory here

Dualis is a really handy emulator for viewing VRAM. It has a Tile viewer and a Map viewer so you can actually have a look where the data is being written in VRAM. Attached are some screenshots of the data in VRAM for the text example.
« Last Edit: December 28, 2008, 02:26:39 am by headkaze »



Offline flash

  • Administrator
  • Blue Gene Super Computer
  • **********
    • Posts: 13180
Reply #9 on: December 28, 2008, 05:35:22 pm
Okay next up we have a demo to display text on the screen. It demonstrates using a tile mode.
This is just a slight modify of some of the code you used. I only place it as you do not need the 2 counters to do the character data and can simplify it like this

Code: [Select]
ldr r0, =SCREEN_BASE_BLOCK_SUB(29) @ make r0 a pointer to screen memory bg bitmap sub address
mov r3, #14 @ Our character counter (0-14 = 15 chars)
adr r1, [text] @ Load the adress of our text
Maploop:
ldrb r2,[r1],#1 @ Load r2 with our current character code
sub r2,#32
strh r2,[r0], #2 @ Store the result in SCREEN_BASE_BLOCK_SUB
subs r3, #1 @ count down
bpl Maploop

PS. I use 0-14 as a hark back to C64 days (this does cause complications with larger numbers) but using 0-14 is how we used to count. Obviously (and only saying for those reading) setting r3 to 15 requires a bne to check for 0 (still 15 characters)
Ps.. am still madly puzzled by using tile mode to display a simple set of level data? Grrr..
« Last Edit: January 07, 2009, 04:34:24 pm by headkaze »

Coding for the love of it!


Offline headkaze

  • Administrator
  • Blue Gene Super Computer
  • **********
    • Posts: 7838
Reply #10 on: December 29, 2008, 03:28:44 am
Thanks Flash those changes make good sense and I've updated the text example using them :)



Offline headkaze

  • Administrator
  • Blue Gene Super Computer
  • **********
    • Posts: 7838
Reply #11 on: January 07, 2009, 04:43:39 pm
And next we have our first sprite demo. For more info on how to manipulate sprites view the OAM Attributes link at GBATek.

sprites.zip
sprites_rs.zip
« Last Edit: January 06, 2010, 07:32:19 pm by headkaze »



Offline flash

  • Administrator
  • Blue Gene Super Computer
  • **********
    • Posts: 13180
Reply #12 on: January 08, 2009, 07:47:46 am
Once you have initialised your sprites you can use this to move/position them:-

   ldr r0, =OBJ_ATTRIBUTE0(0)
   mov r1, #32
   strb r1, [r0]

Just need a loop to access all OBJ_ATTRIBUTE0(x)

Mind you, looking at sprites.h you also have

#define OBJ_X(m)
#define OBJ_Y(m)

All we need is to pass a sprite number to m?
« Last Edit: January 08, 2009, 07:57:11 am by Flash »

Coding for the love of it!


Offline headkaze

  • Administrator
  • Blue Gene Super Computer
  • **********
    • Posts: 7838
Reply #13 on: January 08, 2009, 11:41:52 am
Those macros can be used yeah but they are just used to & the value to make sure it fits in a certain range. They are used like so which I should have put in the original code for clarity.

Code: [Select]
ldr r0, =OBJ_ATTRIBUTE1(0)
ldr r1, =(OBJ_X(128) | ATTR1_ROTDATA(0) | ATTR1_SIZE_32)
strh r1, [r0]

These registers might be read only as I noticed in the C examples they have a copy of all the sprite data and change their copy then write it all back. So it's probably write only, in that case using strb would be a nice way to write to it. Just be careful though as X is 0-511 and Y is 0-255. So X is larger than a byte in size as shown by the macros.

Code: [Select]
#define OBJ_Y(m) ((m)&0x00ff)
#define OBJ_X(m) ((m)&0x01ff)

If you have to write the full X size then you will just need to write that whole 16 bit value again by or'ing the rest.

Okay so I'm going to attach my rotation/scaling sprites demo which is not working correctly yet so I hope you can help me fix it. I think I badly translated the C code from here. I also don't know how to make a value negative. Is that just a matter of setting the highest significant bit? I also converted the SIN/COS/TAN tables to .s files and hopefully they are the same tables used in that example (I got them from an old libnds release so my guess is they are the same ones).
« Last Edit: January 26, 2009, 11:06:43 am by headkaze »



Offline flash

  • Administrator
  • Blue Gene Super Computer
  • **********
    • Posts: 13180
Reply #14 on: January 08, 2009, 11:59:21 am
I will have a look at the rotation stuff a bit later. The most significant bit in a signed word/byte tell the ARM that this is negative. go %10000011 is -3 and %00000011 is 3. You have to use the S extension to tell ARM that you wish to use the values as signed.

I am sadly a bit busy at work though hope to have a play later!

To get the error I get, open the WarhawkGame source and move the section of Wobble: code to the very end of all code and try to compile. You will get an error that Wobble: is out of range.
BTW, did you have any luck with palettes for the three layers. I wonder if code to add the bits to the map/tile data would do the trick? The palette bits must all be clear in the data so that palette(0) is always used.

Coding for the love of it!


Offline flash

  • Administrator
  • Blue Gene Super Computer
  • **********
    • Posts: 13180
Reply #15 on: January 08, 2009, 12:20:00 pm
I am having a look at your code now. It is a little difficult to concentrate on this a work though, but I can see problems. The wrong values are in r2 and r3 prior to store. This can be shown by altering the ror function and the result is the same, so it is adrift somewhere.
I will have a proper look when I get home from work!

Coding for the love of it!


Offline headkaze

  • Administrator
  • Blue Gene Super Computer
  • **********
    • Posts: 7838
Reply #16 on: January 08, 2009, 12:20:19 pm
According to this this really helpful resource for opcodes

Quote
ADR produces position-independent code, because the address is program-relative or register-relative.

Use the ADRL pseudo-instruction to assemble a wider range of effective addresses

So just use adrl (just tried it and it works) ;)

I am having a look at your code now. It is a little difficult to concentrate on this a work though, but I can see problems. The wrong values are in r2 and r3 prior to store. This can be shown by altering the ror function and the result is the same, so it is adrift somewhere.
I will have a proper look when I get home from work!

It doesn't suprise me it was late when I wrote that :)
« Last Edit: January 08, 2009, 12:22:44 pm by headkaze »



Offline flash

  • Administrator
  • Blue Gene Super Computer
  • **********
    • Posts: 13180
Reply #17 on: January 08, 2009, 12:22:53 pm
Missed that :)

Coding for the love of it!


Offline headkaze

  • Administrator
  • Blue Gene Super Computer
  • **********
    • Posts: 7838
Reply #18 on: January 22, 2009, 02:12:08 pm
Sound demo is up :) Not completely finished but I thought I'd post it now incase you want to have a play with sound.

sound.zip
« Last Edit: January 06, 2010, 07:35:19 pm by headkaze »



Offline flash

  • Administrator
  • Blue Gene Super Computer
  • **********
    • Posts: 13180
Reply #19 on: January 22, 2009, 02:16:15 pm
Looking great HK... :)

So how hard to you think LDMPCMCIABBCSPQRRSVPTITTY sound is gonna be?

Coding for the love of it!


Offline headkaze

  • Administrator
  • Blue Gene Super Computer
  • **********
    • Posts: 7838
Reply #20 on: January 22, 2009, 02:24:58 pm
Looking great HK... :)

So how hard to you think LDMPCMCIABBCSPQRRSVPTITTY sound is gonna be?

As long as we have the samples converted to the right format and set the appropriate bit in the register it should just work IMHO.



Offline flash

  • Administrator
  • Blue Gene Super Computer
  • **********
    • Posts: 13180
Reply #21 on: January 22, 2009, 02:28:27 pm
didn't Custer say something similar about his plan to ambush the Indians?

Will have to get some of tbe music converted. I have nothing here at work to do it sadly, so will have a look when i get home!

Will be really nice to have music in the game!!!!!!! :) :) :)

Coding for the love of it!


Offline headkaze

  • Administrator
  • Blue Gene Super Computer
  • **********
    • Posts: 7838
Reply #22 on: January 26, 2009, 11:11:57 am
Okay I have got the rotating sprite demo working thanks to some guys over on the gbadev forums. Some interesting info in the reply which is worth a read. Especially the notes about negative numbers and compiling C into assembly.

sprites_rs.zip
« Last Edit: January 06, 2010, 07:35:46 pm by headkaze »



Offline flash

  • Administrator
  • Blue Gene Super Computer
  • **********
    • Posts: 13180
Reply #23 on: January 26, 2009, 11:46:07 am
an interesting read at gbadev. There are a couple of things to be learnt there.

Have you managed to understand the code fully :) That is always a tricky bit. I must admit, signed numbers are always a bit of a weak spot with me. Never really got to grips with them. On the C64, none of my games really used anything larger than a unsigned byte...

Coding for the love of it!


Offline headkaze

  • Administrator
  • Blue Gene Super Computer
  • **********
    • Posts: 7838
Reply #24 on: January 26, 2009, 02:08:48 pm
an interesting read at gbadev. There are a couple of things to be learnt there.

Have you managed to understand the code fully :) That is always a tricky bit. I must admit, signed numbers are always a bit of a weak spot with me. Never really got to grips with them. On the C64, none of my games really used anything larger than a unsigned byte...

Everything was written from scratch so I do understand it. The changes I had to make were pretty minor so I actually had most of it right first time. Sorta reminds me of the sound code that didn't work - it was pretty much one line of code that was screwing everything up!

The main problem with this demo was to use ldrsh because the SIN/COS data is actually signed. I didn't know that because I converted the SIN.bin/COS.bin raw binary files to .s files using bin2obj and didn't know exactly what format the data was in. I guessed it as being half word in size, but I didn't realise it was signed. The other key part was to use rsb r2, r2, #0 to make the value negative. That is like saying r2 = #0 - r2. Handy to know ;) And finally a few other minor mistakes of reading/writing in 32 bits when I should have been using 16, but I saw those problems pretty early on. You actually suggested to use the "s" extention but I didn't know exactly how to use it.

What I don't understand though is why fluBBa suggested I use asr instead of lsr. It didn't seem to make a difference. Also why use movpl instead of movgt? Again they didn't make a difference but I made those changes anyway.



Offline flash

  • Administrator
  • Blue Gene Super Computer
  • **********
    • Posts: 13180
Reply #25 on: January 26, 2009, 03:22:14 pm
Everything was written from scratch so I do understand it. The changes I had to make were pretty minor so I actually had most of it right first time. Sorta reminds me of the sound code that didn't work - it was pretty much one line of code that was screwing everything up!
Sorry, I did not mean to sound like I was being condescending. I meant did you understand exactly why it was not working and why it does now - which you do.. (you understand it better than me)

What I don't understand though is why fluBBa suggested I use asr instead of lsr. It didn't seem to make a difference. Also why use movpl instead of movgt? Again they didn't make a difference but I made those changes anyway.

Yes, I do not understand the need for asr? greater and plus (pl, gt) are basically the same. movgt is the more logical to me, though i do not think that works with signed numbers, whereas movpl does. I may be wrong (have not checked this in arm instructions)

I also wonder why Arm is a risc chip.. The 6510 was a cisc chip and contained a maximum of 255 instructions (+ nop), the arm has far in excess of that surely if you consider all the instruction modifyers like movgt.... :)

Coding for the love of it!


Offline flash

  • Administrator
  • Blue Gene Super Computer
  • **********
    • Posts: 13180
Reply #26 on: January 28, 2009, 06:13:17 pm
I got it the wrong way round,

movpl = works with unsigned numbers
movgt = works with signed

This has to be implicitly described else movpl will be confused by the bit signing and give an erronous comparison..

Coding for the love of it!


Offline headkaze

  • Administrator
  • Blue Gene Super Computer
  • **********
    • Posts: 7838
Reply #27 on: March 02, 2009, 07:02:04 am
Okay next demo is how to use interrupts and some screen effects including basic wipes, cross wipe, fades, masaic, sine wobble, scanline and spotlight. Thanks to DarkCloud and Cearn for the original C code these demo's are based from.

fx.zip
« Last Edit: January 06, 2010, 07:38:45 pm by headkaze »