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.

ASM > foolish question from an asm knobbit

#163703 - moonlightcheese - Thu Oct 09, 2008 1:35 pm

i'm 'porting' a program from a fake assembly language to arm assembly for a project and i just want to make sure i'm not screwing up. the .align at the top aligns everything to words right? so if i type some code like:
Code:
mov r3, #0x5C
mov r2, #10
str r2, [r3]
mov r2, #6
str r2, [r3, #1]


this should place the value 10 in address 0x5C and the value 6 in address 0x5D as 32 bit words right? sorry for the stupid question... just making sure i'm doing things right.

#163704 - eKid - Thu Oct 09, 2008 2:28 pm

The .align directive aligns the code to the word boundary.

What that code will do is write 10 to the word at 0x5C and write 6 to 0x5C too!

ARM can't write 32-bit values to misaligned addresses so the lower two bits of the address are ignored when using str.

The final result will be '6' in the byte at 0x5C and 0 in the next 3 bytes, or 6 in the word at 0x5C.

#163705 - moonlightcheese - Thu Oct 09, 2008 2:54 pm

eKid wrote:
The .align directive aligns the code to the word boundary.

What that code will do is write 10 to the word at 0x5C and write 6 to 0x5C too!

ARM can't write 32-bit values to misaligned addresses so the lower two bits of the address are ignored when using str.

The final result will be '6' in the byte at 0x5C and 0 in the next 3 bytes, or 6 in the word at 0x5C.

ok.

i thought that the instr (str r2, [r3, #1]) would increment the address stored in r3 and store r2 into that address, no? should i add the number of bytes instead? essentially you're telling me to do this, right?
Code:
mov r3, #0x5C
mov r2, #10
str r2, [r3]
mov r2, #6
str r2, [r3, #4]

so that the value 10 stores to 0x5C, and the value 6 stores to (0x60?) right?

#163706 - Cearn - Thu Oct 09, 2008 3:32 pm

moonlightcheese wrote:
i thought that the instr (str r2, [r3, #1]) would increment the address stored in r3 and store r2 into that address, no? should i add the number of bytes instead?

Unlike in C, the offset is always measured in bytes, so `str, [r3, #1]' would try to store r2 in 0x5D (and do it incorrectly for the reason eKid mentioned). So yes, use #4 in this case.

moonlightcheese wrote:
Code:
mov r3, #0x5C
mov r2, #10
str r2, [r3]
mov r2, #6
str r2, [r3, #4]

so that the value 10 stores to 0x5C, and the value 6 stores to (0x60?) right?

Yes.

You can also make use of ARM block-transfers LDM and STM if you want to load/store multiple words of data. These tend to be a little faster.
Code:
mov     r0, #0x5C
mov     r2, #10
mov     r3, #6
stmia   r0, {r2-r3}     @ Store r2 in 0x5C and r3 in 0x60.


Note that you shouldn't be able to access addresses as low as 0x5C. on GBA or NDS, but I'm assuming this was just an example.

#163708 - moonlightcheese - Thu Oct 09, 2008 4:08 pm

Cearn wrote:
moonlightcheese wrote:
i thought that the instr (str r2, [r3, #1]) would increment the address stored in r3 and store r2 into that address, no? should i add the number of bytes instead?

Unlike in C, the offset is always measured in bytes, so `str, [r3, #1]' would try to store r2 in 0x5D (and do it incorrectly for the reason eKid mentioned). So yes, use #4 in this case.

moonlightcheese wrote:
Code:
mov r3, #0x5C
mov r2, #10
str r2, [r3]
mov r2, #6
str r2, [r3, #4]

so that the value 10 stores to 0x5C, and the value 6 stores to (0x60?) right?

Yes.

You can also make use of ARM block-transfers LDM and STM if you want to load/store multiple words of data. These tend to be a little faster.
Code:
mov     r0, #0x5C
mov     r2, #10
mov     r3, #6
stmia   r0, {r2-r3}     @ Store r2 in 0x5C and r3 in 0x60.


Note that you shouldn't be able to access addresses as low as 0x5C. on GBA or NDS, but I'm assuming this was just an example.


cool thanks!

so instead of storing to memory i'm storing to the stack, which should be perfectly legal, afaik. so the code i'll be using goes something like this to start (this is just to store the data for the program to be used later).

Code:
mov r2, #10
str r2, [r13]  @store to top of stack
mov r2, #6
str r2, [r13, #-4] @store next word onto stack, moving down SP
@9 more data moves in this fashion

also, is there a better way to store the data in the program, like using a .data section or something? i don't know how to do that and couldn't find any specifics in my ARM resources.

also also, how can i accept user or file input? is there an easy way?

my idea was to write this app as inline assembly, accepting file input into an array and then somehow (this is the part that eludes me) storing the array to the top of the stack (or passing a pointer to the array maybe?) and then running the inline assembly instructions to execute the program using the values placed on the stack by the C code. can i do this?

sorry for being such a noob :(

#163710 - eKid - Thu Oct 09, 2008 5:04 pm

When the 'push' instruction is used, it decrements before the store, so writing to [r13] will overwrite critical data, only underneath r13 is safe.

Also, if you have some interrupts enabled, then storing underneath r13 is risky. If the interrupt handler switches back to the user/system stack (this is common to achieve more stack space during interrupts), then that data will get corrupted. To be safe you can decrement r13 beforehand to reserve the space.

#163714 - Cearn - Thu Oct 09, 2008 5:48 pm

moonlightcheese wrote:
So instead of storing to memory i'm storing to the stack, which should be perfectly legal, afaik. so the code i'll be using goes something like this to start (this is just to store the data for the program to be used later).
Code:
mov r2, #10
str r2, [r13]  @store to top of stack
mov r2, #6
str r2, [r13, #-4] @store next word onto stack, moving down SP
@9 more data moves in this fashion


Yeah keeping things on the stack should work fine. Stackwork is usually done with stmfd and ldmfd, though (or push and pop in Thumb mode).
Code:
mov     r2, #10
mov     r3, #6
stmfd   sp!, {r2-r3}    @ Push r2 and r3 on stack. The `!' means sp
                        @ is updated to sp-8.


moonlightcheese wrote:
also, is there a better way to store the data in the program, like using a .data section or something? i don't know how to do that and couldn't find any specifics in my ARM resources.

You can put things in .data (initialized data), .bss (uninitialized data) and .section .rodata (const data). You can find a few things relating to the GNU assembler in tonc:asm, and of course the manual.

moonlightcheese wrote:
also also, how can i accept user or file input? is there an easy way?

my idea was to write this app as inline assembly, accepting file input into an array and then somehow (this is the part that eludes me) storing the array to the top of the stack (or passing a pointer to the array maybe?) and then running the inline assembly instructions to execute the program using the values placed on the stack by the C code. can i do this?

sorry for being such a noob :(

Eeew, inline asssembly :P.
IMHO, it's best to use a proper assembly file for your asm code. Inline assembly has a fairly obscure syntax and may not play well with the surrounding C code (not to mention the instruction set the C file is compiled under; inline ARM asm in Thumb compilation unit is probably a bad idea). You don't have to do anything extra to deal with assembly files if you're working with the standard devkitPro template makefiles: just give it an .s extension and the makefile will take care of the rest.

Instead of keeping the data on the top of the stack, you could simply malloc an area and use the pointer it gives you.

As for file input, it's probably best to use libfat for something like that. You can still call functions from assembly. The thing is, though, if you're calling file operations, why would use assembly for it? Assembly is most properly used for speeding up time-critical code (well, that and for the lulz of course), but any gains you think you might get from hand-coded assembly will be nullified by the file access.

#163717 - moonlightcheese - Thu Oct 09, 2008 6:12 pm

Cearn wrote:
Yeah keeping things on the stack should work fine. Stackwork is usually done with stmfd and ldmfd, though (or push and pop in Thumb mode).
Code:
mov     r2, #10
mov     r3, #6
stmfd   sp!, {r2-r3}    @ Push r2 and r3 on stack. The `!' means sp
                        @ is updated to sp-8.


moonlightcheese wrote:
also, is there a better way to store the data in the program, like using a .data section or something? i don't know how to do that and couldn't find any specifics in my ARM resources.

You can put things in .data (initialized data), .bss (uninitialized data) and .section .rodata (const data). You can find a few things relating to the GNU assembler in tonc:asm, and of course the manual.

ok cool, i'll have to look up how to use the .data section and how to use that data in the .text

Cearn wrote:
moonlightcheese wrote:
also also, how can i accept user or file input? is there an easy way?

my idea was to write this app as inline assembly, accepting file input into an array and then somehow (this is the part that eludes me) storing the array to the top of the stack (or passing a pointer to the array maybe?) and then running the inline assembly instructions to execute the program using the values placed on the stack by the C code. can i do this?

sorry for being such a noob :(

Eeew, inline asssembly :P.
IMHO, it's best to use a proper assembly file for your asm code. Inline assembly has a fairly obscure syntax and may not play well with the surrounding C code (not to mention the instruction set the C file is compiled under; inline ARM asm in Thumb compilation unit is probably a bad idea). You don't have to do anything extra to deal with assembly files if you're working with the standard devkitPro template makefiles: just give it an .s extension and the makefile will take care of the rest.

Instead of keeping the data on the top of the stack, you could simply malloc an area and use the pointer it gives you.

As for file input, it's probably best to use libfat for something like that. You can still call functions from assembly. The thing is, though, if you're calling file operations, why would use assembly for it? Assembly is most properly used for speeding up time-critical code (well, that and for the lulz of course), but any gains you think you might get from hand-coded assembly will be nullified by the file access.

well there are two reasons i'd like to use inline assembly. one, this is a project and the first part is due monday so i'd like to have as much done as possible and i have a better idea (i think) of how to get things working in this manner and second, this project is basically an operating system. we were supposed to kind of 'emulate' a machine and OS in java but i asked if i could do this instead. the learning curve has proved most difficult but i'm getting more out of coding for the gba in terms of real world experience on the low level.

the idea behind this project is to build an OS to run his program (the code snippets in this thread are from his program) and run a number of instances of that program with his input files (which is why i need input and output).

so... it would be even better if i could do things properly and implement true context switching (saving the registers to the stack, saving the next instr to LR, calling the program as a subroutine, then switching back the PC from LR and returning the registers to their states, re-initiating the OS). i think this would be too difficult, no? i have the weekend to finish this first part...

i definitely appreciate the responses! this brings me much closer to my end goal, which would be an OS for the gba complete with CLI. :D

i have a lot of work to do...

#164043 - strat - Sun Oct 19, 2008 8:08 am

Quote:
The thing is, though, if you're calling file operations, why would use assembly for it? Assembly is most properly used for speeding up time-critical code (well, that and for the lulz of course)


Or when it just feels natural. I'd like to store the level data for my game as a 3d array, but since that's a lot of trouble in C, it makes more sense to wrap all the pointer arithmetic in a neat little asm function.

#164092 - Miked0801 - Mon Oct 20, 2008 4:44 pm

How are 3D arrays a pain in c?

foo[1][2][3];

What's the issue?

#164096 - Cearn - Mon Oct 20, 2008 6:09 pm

Miked0801 wrote:
How are 3D arrays a pain in c?

foo[1][2][3];

What's the issue?
I kinda agree with this. However, if you're passing it onto functions and the sizes aren't known in advance you can't really do that. Still, you could just use single pointers like bar[z*zPitch + y*yPitch+x], which is what it would look like in assembly anyway.

That said, there are a few times when doing it in C really is ugly. In a fast non-DMA copy routine you may have t do it-masks on pointer and switch between byte, halfword and word types for the pointers. That just looks awful in C.

#164102 - Miked0801 - Mon Oct 20, 2008 8:01 pm

If you are doing as you suggest, you are already abusing the arrays and deserve what you get :)

Anytime you get to the point where you are trying to pass around entire multi-dim array access, as pointer or otherwise, you need to ask yourself if there's a better way to represent your data. Personally, the only time my arrays get to 3 dimensions is when I'm creating 2-permutation lookup tables. At that point, the going from 2 dim to 3 is just a convience for not having to create a structure and defining data that way. And these tables tend to get factored into smaller, more precise tables over time.

And I'm missing why you'd have to play the byte masking game. If something is a 1/2/4 bytes in base size, pointer arithmetic will take care of the various multiplies you may need to do to get around in the structure. Oh, and if you are talking about passing a byte array to a u32 expecting function such as the DMA copy, again you are asking for it. :)

#164235 - strat - Fri Oct 24, 2008 2:12 am

Cearn pretty much nailed it. Also, it had more to do with the nature of C syntax than anything. There'd be all those brackets and comments telling what level it is. And trying to do the same thing in C that worked in assembly (storing the levels in their own arrays and having an array of pointers to each level) got to be a mess.

This way I get to store each level in a neat .ascii array with its own variable name, a pointer table of the level names, and a look-up function that almost looks like a 3D array: MetaTileEntry(row,col,num)

Plus it was a good excuse to use assembly in a game that will probably never need it.