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 > MTCLib Port (Renamed. Was: Cross-compiling hell...)

#103371 - OOPMan - Thu Sep 21, 2006 12:05 pm

Or something...

Right, if anyone recalls I discovered a thread library called MTClib a while back that has support for ARM7.

The default dev environment for compiling the ARM7 version is not devkitPro, though. Rather, a file for the official ARM SDT is included, which isn't much time. Included are Makefiles for Cygwin and DJGPP, so I grabbed one of those an modified it with some devkitPro specific stuff...

Of course, things aren't compiling nicely and I was wondering if anyone could give me an insight into why the following error is cropping up:

Quote:
$ make
arm-eabi-gcc -c -Wall -W -Wunused -Wpointer-arith -Wwrite-strings -Wcast-qual -W
cast-align -Wshadow -Wno-nested-externs -Wstrict-prototypes -Wmissing-prototypes
-Wmissing-declarations -mthumb -mthumb-interwork -mcpu=arm7tdmi -mtune=arm7tdmi
-O3 -Wall -Wwrite-strings -Wpointer-arith -Wconversion -Wmultichar -Wunknown-pr
agmas -Wno-sign-compare -O3 -fomit-frame-pointer -DMTC_SCHEDULE=0 -I../../source
-I../../include -DNDEBUG -o rel/armsdt.obj ../../source/armsdt/armsdt.c
In file included from ../../source/armsdt/armsdt.c:26:
../../source/platform.h:166: warning: '__regparm__' attribute directive ignored
../../source/platform.h:167: warning: '__regparm__' attribute directive ignored
../../source/platform.h:168: warning: '__regparm__' attribute directive ignored
../../source/platform.h:169: warning: '__regparm__' attribute directive ignored
../../source/platform.h:170: warning: '__regparm__' attribute directive ignored
../../source/platform.h:171: warning: '__regparm__' attribute directive ignored
../../source/platform.h:172: warning: '__regparm__' attribute directive ignored
../../source/armsdt/armsdt.c: In function 'TimeoutDPC':
../../source/armsdt/armsdt.c:225: warning: unused parameter 'pv'
../../source/armsdt/armsdt.c: In function 'MTC_PlatformThreadFree':
../../source/armsdt/armsdt.c:365: warning: cast increases required alignment of
target type
../../source/armsdt/armsdt.c: In function 'MTC_PlatformThreadSwitch':
../../source/armsdt/armsdt.c:385: warning: cast increases required alignment of
target type
../../source/armsdt/armsdt.c:388: warning: cast increases required alignment of
target type
../../source/armsdt/armsdt.c: In function 'PlatformSwitchStack':
../../source/armsdt/armsdt.c:411: error: expected '(' before '{' token
../../source/armsdt/armsdt.c:412: error: 'mov' undeclared (first use in this fun
ction)
../../source/armsdt/armsdt.c:412: error: (Each undeclared identifier is reported
only once
../../source/armsdt/armsdt.c:412: error: for each function it appears in.)
../../source/armsdt/armsdt.c: At top level:
../../source/armsdt/armsdt.c:510: error: expected '=', ',', ';', 'asm' or '__att
ribute__' before 'void'
../../source/armsdt/armsdt.c:515: error: stray '#' in program
../../source/armsdt/armsdt.c:520: error: expected '=', ',', ';', 'asm' or '__att
ribute__' before 'int'
../../source/armsdt/armsdt.c:526: error: stray '#' in program
../../source/armsdt/armsdt.c:536: warning: '__regparm__' attribute directive ign
ored
../../source/armsdt/armsdt.c: In function 'MTC_AtomicCmpXchg':
../../source/armsdt/armsdt.c:562: warning: implicit declaration of function 'dis
able'
../../source/armsdt/armsdt.c:569: warning: implicit declaration of function 'ena
ble'
make: *** [rel/armsdt.obj] Error 1


The code in the file that seems to be causing the problem is this:

Quote:
/*
* Switch stacks
*/
void* PlatformSwitchStack( register void* pvStack)
{
__asm {
mov r1, r13
mov r13, pvStack
mov pvStack, r1
}
return pvStack;
}


From what I can tell it seems like arm-eabi-gcc doesn't like the __asm directive thing being used. Can I assume that this is a directive that is specific the the official ARM SDT and doesn't work?

Or is there some deeper problem? Maybe something along the lines of "Foolish n00b, that will never work!"...

Any insight would be nice :-)
_________________
"My boot, your face..." - Attributed to OOPMan, Emperor of Eroticon VI

You can find my NDS homebrew projects here...


Last edited by OOPMan on Sat Sep 23, 2006 5:55 pm; edited 1 time in total

#103374 - Cearn - Thu Sep 21, 2006 1:29 pm

OOPMan wrote:
From what I can tell it seems like arm-eabi-gcc doesn't like the __asm directive thing being used. Can I assume that this is a directive that is specific the the official ARM SDT and doesn't work?
Yeah, GCC uses a different inline assembly format than ARM SDT, something like 'asm ("foo");', where foo is put directly into asm. The following should work as a replacement.
Code:
void *PlatformSwitchStack(register void *pvStack)
{
   asm volatile(
   "mov r1, r13\n"
   "mov r13, r0\n"
   "mov r0, r1\n"
   );
   return pvStack;
}

It compiles the way it should but this particular procedure isn't exactly foolproof; I'm not fully familiar with GCC's inline capabilities. You could also consider putting the full function into assembly:

Code:
@ whatever.s : assembly version of PlatFormSwitchStack (and maybe others)

@ --- void *PlatformSwitchStack(register void *pvStack) ---
    .text
    .align
    .thumb_func
    .global PlatformSwitchStack
PlatformSwitchStack:
    mov     r1, r13
    mov     r13, r0
    mov     r0, r13
    bx      lr

#103385 - gmiller - Thu Sep 21, 2006 2:24 pm

Also remember with a file of the format "*.c" the compiler will guess the language to be C not C++ and depending on the standard the compiler is built against it may or may not support inline assembly. You can force the language with command line switches or rename the file to one the compiler recognizes as C++. The syntaxt differences may be the issue here but I have also found that the inline assembly error checking and error messages leave a lot to be desired.

#103386 - OOPMan - Thu Sep 21, 2006 2:30 pm

Thanks for that. Using the inline assembly for the segment as you recommended got the compiler past that part of the file...

Unsurprisingly enough it has a problem with the next function (And will probably have the same problem with the functions following it).

The new problem follows. The compiler reaches thew latter stages of the file now and only has a few more functions to compile (All assembler heavy ones as well). Now, it chokes with the following:

Quote:
$ make
arm-eabi-gcc -c -Wall -W -Wunused -Wpointer-arith -Wwrite-strings -Wcast-qual -W
cast-align -Wshadow -Wno-nested-externs -Wstrict-prototypes -Wmissing-prototypes
-Wmissing-declarations -mthumb -mthumb-interwork -mcpu=arm7tdmi -mtune=arm7tdmi
-O3 -Wall -Wwrite-strings -Wpointer-arith -Wconversion -Wmultichar -Wunknown-pr
agmas -Wno-sign-compare -O3 -fomit-frame-pointer -DMTC_SCHEDULE=0 -I../../source
-I../../include -DNDEBUG -o rel/nds.obj ../../source/nds/nds.c
In file included from ../../source/nds/nds.c:26:
../../source/platform.h:166: warning: '__regparm__' attribute directive ignored
../../source/platform.h:167: warning: '__regparm__' attribute directive ignored
../../source/platform.h:168: warning: '__regparm__' attribute directive ignored
../../source/platform.h:169: warning: '__regparm__' attribute directive ignored
../../source/platform.h:170: warning: '__regparm__' attribute directive ignored
../../source/platform.h:171: warning: '__regparm__' attribute directive ignored
../../source/platform.h:172: warning: '__regparm__' attribute directive ignored
../../source/nds/nds.c: In function 'TimeoutDPC':
../../source/nds/nds.c:225: warning: unused parameter 'pv'
../../source/nds/nds.c: In function 'MTC_PlatformThreadFree':
../../source/nds/nds.c:365: warning: cast increases required alignment of target
type
../../source/nds/nds.c: In function 'MTC_PlatformThreadSwitch':
../../source/nds/nds.c:385: warning: cast increases required alignment of target
type
../../source/nds/nds.c:388: warning: cast increases required alignment of target
type
../../source/nds/nds.c: At top level:
../../source/nds/nds.c:517: error: expected '=', ',', ';', 'asm' or '__attribute
__' before 'void'
../../source/nds/nds.c:534: error: expected '=', ',', ';', 'asm' or '__attribute
__' before 'int'
../../source/nds/nds.c:540: error: stray '#' in program
../../source/nds/nds.c:550: warning: '__regparm__' attribute directive ignored
../../source/nds/nds.c: In function 'MTC_AtomicCmpXchg':
../../source/nds/nds.c:576: warning: implicit declaration of function 'disable'
../../source/nds/nds.c:583: warning: implicit declaration of function 'enable'
make: *** [rel/nds.obj] Error 1


The code from the file is as follows...

Quote:
static __inline __pure void enable( void)
{
register int tmp;
__asm {
MRS tmp, CPSR
BIC tmp, tmp, #0x80
MSR CPSR_c, tmp
}
}


Now, I grabbed a nice little PDF about using inline assembly with GCC from the net and read up on how to produce the correct inline assembly to handle the tmp register variable but the compiler seems to be having issues with the __inline and __pure keywords...

I fuddled about and tried removing __inline and __pure and the compiler seemed happy. It also compiled past the problem area using inline as oppossed to __inline. However, I'd like a second opinion on whether it's actually a good idea to remove these attributes, since they're probably there for a good reason in the first place...

Also, considering that there are a good few more assembler methods in the file, would you recommend converting them all into pure assembly?

Overall, it's probably only another 40 or so lines of asm, if that...

BTW, do you think that this assembly code will compile using the -mcpu arm9tdmi option? I seem to recall that the ARM line of CPUs is pretty uniform in terms of design and so forth, but I'm not sure whether the code in question would work on an ARM9 (I don't have much asm experience and it's pretty rusty at that...)
_________________
"My boot, your face..." - Attributed to OOPMan, Emperor of Eroticon VI

You can find my NDS homebrew projects here...

#103390 - Lick - Thu Sep 21, 2006 3:26 pm

The C language doesn't have the keyword 'inline'. C++ however, does.
The '__inline' possibly forced the function to become inlined, I think that's the difference.
_________________
http://licklick.wordpress.com

#103393 - tepples - Thu Sep 21, 2006 3:30 pm

Lick wrote:
The C language doesn't have the keyword 'inline'.

Or it didn't until ISO added 'inline' to the C language in 1999.
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#103394 - Lick - Thu Sep 21, 2006 3:46 pm

Yuck, first page Google links aren't trustworthy! I stand corrected.

Well that makes removing '__' from '__inline' a better option than removing it all the way.

BTW: this MSDN page hints that the original lib was probably developed with a microsoft product.
_________________
http://licklick.wordpress.com

#103396 - tepples - Thu Sep 21, 2006 4:00 pm

The informal name for the new version of the C language standard is "C99". Does this page of Google results look better?
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#103412 - OOPMan - Thu Sep 21, 2006 5:45 pm

Well, the library is written in ANSI C90, with the exception of the platform specific part that I'm working on right now...

Given that it's C90 switching to inline may not be a good idea...

However, I'm going to give it a go anyway and just completely drop the __pure line. If it compiles then we can see if it actually works properly :-)

As for notes on the library, it was quite possibly developed on an M$ product initially...

Take a look at the manual page and you'll see it supports a lot of Intel/M$ platform stuff, although it's basically a cross-platform library with explicit room for being ported :-)
_________________
"My boot, your face..." - Attributed to OOPMan, Emperor of Eroticon VI

You can find my NDS homebrew projects here...

#103427 - OOPMan - Thu Sep 21, 2006 10:14 pm

Hey all...

Well, I've managed to migrate the assembly code into a GCC friendly form and the compiler now gets as far as passing the stuff over to the assembler...

Alas, the assembler is giving me the following output:

Quote:
C:/DOCUME~1/ALLENJ~1.MAI/LOCALS~1/Temp/cc0NFAqb.s: Assembler messages:
C:/DOCUME~1/ALLENJ~1.MAI/LOCALS~1/Temp/cc0NFAqb.s:81: Error: selected processor
does not support `mrs r3,CPSR'
C:/DOCUME~1/ALLENJ~1.MAI/LOCALS~1/Temp/cc0NFAqb.s:83: Error: unshifted register
required -- `orr r3,r3,#0x80'
C:/DOCUME~1/ALLENJ~1.MAI/LOCALS~1/Temp/cc0NFAqb.s:84: Error: selected processor
does not support `msr CPSR_c,r3'
C:/DOCUME~1/ALLENJ~1.MAI/LOCALS~1/Temp/cc0NFAqb.s:93: Error: selected processor
does not support `mrs r3,CPSR'
C:/DOCUME~1/ALLENJ~1.MAI/LOCALS~1/Temp/cc0NFAqb.s:94: Error: unshifted register
required -- `bic r3,r3,#0x80'
C:/DOCUME~1/ALLENJ~1.MAI/LOCALS~1/Temp/cc0NFAqb.s:95: Error: selected processor
does not support `msr CPSR_c,r3'
make: *** [rel/nds.obj] Error 1



Now I'm guessing there's something funny going on because according to this website the instructions it's complaining about are part of the core ARM instruction set...

I've trading chaning the -mcpu argument in my makefile but that doesn't give me any joy. The options I've tried are:

arm7tdmi
arm9tdmi
arm9e
arm9e-s
arm946e-s

None of those have given me any luck...

Has anyone got any ideas what the problem might be?

EDIT:

I tried testing some of the assembler code in a separate file and it assembled fine so I'm guess that gcc isn't calling the correct assembler or something...

Time to delve into that GCC manual. Anyone got any immediate pointers (Other than move all the assembler into separate files. I'm not that hot with assembly code :-) )
_________________
"My boot, your face..." - Attributed to OOPMan, Emperor of Eroticon VI

You can find my NDS homebrew projects here...

#103429 - Cearn - Thu Sep 21, 2006 10:47 pm

The '-mcpu' option specifies the CPU, but not the instruction set. To compile as ARM code, use '-marm', instead of as thumb code ('-mthumb'). Or, in assembly, '.arm' vs '.thumb_func'.

Full asm replacements:
Code:
// NOTE! It assembles, but haven't double-checked for correctness.

/*! \fn void *PlatformSwitchStack(void *pvStack);
*   \brief  Switch the stack with \a pvStack.
*   \return Previous stack pointer position.
*/
    .text
    .align
    .thumb_func
    .global PlatformSwitchStack
PlatformSwitchStack:
    mov     r1, sp
    mov     sp, r0
    mov     r0, r1
    bx      lr

// --- NOTE! ARM code ---

/*! \fn void enable();
*   \brief  Enable IRQ.
*/
    .text
    .align
    .arm
    .global enable
enable:
    mrs     r0, cpsr
    bic     r0, #0x80   // Enable IRQ
    msr     cpsr, r0
    bx      lr

/*! \fn int disable();
*   \brief  Disable IRQ.
    \return Previous IRQ-mode status
*/
    .text
    .align
    .arm
    .global disable
disable:
    mrs     r1, cpsr
    mvn     r0, r1, lsl #23 // if( IRQbit ? 0 : 1)
    mov     r0, r0, lsr #31
    orr     r1, #0x80       // Enable IRQ
    msr     cpsr, r1
    bx      lr

/*! \fn int MTC_AtomicCmpXchg(MTC_atomic_t *p, int compare, int update);
*   \brief Uhm ...
*   \return Current p->v value
*/
    .text
    .align
    .arm
    .global MTC_AtomicCmpXchg
MTC_AtomicCmpXchg:
    // Disable IRQ
    mrs     r3, cpsr
    mov     r12, r3
    orr     r3, #0x80
    msr     cpsr, r3

    // Update atom if allowed
    ldr     r3, [r0]
    cmp     r3, r1
    streq   r2, [r0]

    // Restore IRQ
    msr     cpsr, r12
    mov     r0, r3
    bx      lr

If you want, you can put the ARM functions in ITCM too, but with the NDS instruction cache I'm not sure that's really worth it.

New declarations:
Code:
void *PlatformSwitchStack(void *pvStack);
int disable();
void enable();
int MTC_AtomicCmpXchg(MTC_atomic_t *p, int compare, int update);

With this you should be able to remove their C equivalents from the file, but comment them out just to be sure. You'd have to replace the declarations yourself somewhere.
I think you don't actually need the enable() and disable() function; they're only required because of the half-C/half-asm code. Also, the assembly for the last 3 functions concern interrupts (disabling them so that they don't interfere with critical sections I guess), which you can probably do with REG_IME as well.

Looking through the rest of the library code, I get the feeling this predates the AAPCS, which is why you see 'register' everywhere, and things like __pure (GCC equivalent : __attribute__((pure)) ), __inline (GCC: inline or static inline) and __fastcall and regparm(3), which are the default ARM procedure call standard nowadays. It's probably quite safe to remove all those extra function specifiers, or replace them with something GCC will recognize.

#103435 - OOPMan - Thu Sep 21, 2006 11:09 pm

Thanks for that. I knew it was some compiler switch or other which I need. Gah, I should probably go to bed now :-)

Anyway, using -marm the library compiles as is with the inline versions I've written. A nice little 48kb libmtc.a file :-) Now I'll have to do some testing as see whether it actually works as advertised. If so, the it looks like a usable general use thread library is now available.

Which is bloody good news for my Ur-Quan Master DS project, since de-threading that was looking to be a real hassle...

I'm not sure whether they're all correct myself, seeing as I put them together with the aid of some very useful websites (Maybe I should post the links in the Tuts thread, if they're not already there?)...

If anyone would care to verify my inline code. I'm quoting it below...

Quote:
/*
* Switch stacks
*/
void* PlatformSwitchStack( register void* pvStack)
{
asm volatile(
"mov r1, r13\n"
"mov r13, r0\n"
"mov r0, r1\n"
: /* No output operands */
: /* No input operands */
: "r1", "r13", "r0"
);
return pvStack;
}

static inline void enable( void)
{
register int tmp;
asm volatile(
"MRS %0, CPSR\n"
"BIC %0, %0, #0x80\n"
"MSR CPSR_c, %0\n"
: /* No output operands */
: "r" (tmp)
: "cc"
);
}

static inline int disable( void)
{
register int tmp, prev;
asm volatile(
"MRS %1, CPSR\n"
"MOV %0, %1\n"
"ORR %1, %1, #0x80\n"
"MSR CPSR_c, %1\n"
: "=r" (prev)
: "r" (tmp)
: "cc"
);
/* Return !0 if interrupts were enabled */
return (prev & 0x80) ? 0 : 1;
}

MTC_FASTCALL( int, MTC_AtomicCmpXchg(
register MTC_atomic_t* p,
register int compare,
register int update
)){
register int i;

#if MTC_ASSEMBLER
register temp;

asm volatile(
// Disable IRQs
"mrs %0, CPSR\n"
"mov %4, %0\n"
"orr %0, %0, #0x80\n"
"msr CPSR_c, %0\n"
"ldr %0, [%1]\n"
"cmp %0, %2\n"
"streq %3, [%1]\n"
// Restore IRQs
"msr CPSR_c, %4\n"
: "=&r" (i)
: "r" (&p->v) "r" (compare) "r" (update) "r" (temp) "0" (i)
: "cc" "memory"
);

#else
int const enabled = disable();

i = MTC_AtomicRead( p);
if ( i == compare)
MTC_AtomicWrite( p, update);

if ( enabled)
enable();

#endif

return i;
}


Anyway, as I said, I'm not hot on assembler and quite sleepy at the moment as well so I'm sure there are one or two bits that I've managed to mooch up.

Any corrections and advice would be great. Once I've got any corrections in place and/or integrated the pure assembly code I'll put together a proper source release and find somewhere to put it up...

EDIT: Okay, well, I've got the lib compiling fine and it links into the a slightly modified form of the basic demo app without any problems. Unsurprisingly enough it seems to lock up shortly after creating the test thread (On DeSmuME it crashes when attempting to create the thread, on actual hardware it locks...). I'm guessing there's possibly something wrong with my inline assembly. I'm gonna setup a test version using cearns external asm and see how that goes...
_________________
"My boot, your face..." - Attributed to OOPMan, Emperor of Eroticon VI

You can find my NDS homebrew projects here...

#103596 - OOPMan - Fri Sep 22, 2006 11:00 pm

Yay, some real progress...

I set-up a version that uses Cearn's external assembler code and made a few changes here and there in the source to suppor the asm, as Cearn advised.

The library compiles and links into the demo app fine :-)

The demo app (Which creates two threads that seem to sleep for a bit and then die) works, kind of. It creates both threads fine and each thread get's a chance to print it's details but I'm not sure it's finishing properly...

At present it's printing out the main thread details, then the thread created messages and finally the messages from within the threads themselves.

However, the thread done messages are not being printed.

Is it possible that the threads are terminating correctly and that the final lines of code are just running too fast for the thread done messages to get printed, or is it more likely that something is causing things to die when it attempts to kill a thread?

On a side note, this thread library is strange in a number of ways. Most of the code seems to have been written in 2002, but the library was only released in March this year, according to the website. Also, for a supposedly OSS library it doesn't contain much in the way of an OSS license. Oh, and the creators are rathe quiet. I've emailed them a few times. Some of the addresses bounced, although my last email has yet to bounce or be replied to...
_________________
"My boot, your face..." - Attributed to OOPMan, Emperor of Eroticon VI

You can find my NDS homebrew projects here...

#103662 - OOPMan - Sat Sep 23, 2006 6:00 pm

Another progress update. I've been doing some debuggin of the demo application and have gotten to the general root of the lock-up problem that was occurring...

The problem was/is related to the thread sleep function. In the demo
this was being called as follows:

Quote:
MTC_ThreadSleep( (MTC_ui32)(0.01 * MTC_TICKS_PER_SEC));


Something with this was causing the app to stall. I'm still checking into the precise details.

Replacing that call with the following one works:

Quote:
MTC_ThreadSleep( (MTC_ui32)(0));


Calling the threadsleep function with a value of 0 just causes the thread to yield the current time-slice. Doing so allowed the demo application to terminate normally.

Print-outs inserted into the app indicated that it was indeed multi-threading.

This means I have to get some idea as to why the basic call was failing and work out whether it's a bug with MTClib or something that relates to the app being run on an ARM cpu...
_________________
"My boot, your face..." - Attributed to OOPMan, Emperor of Eroticon VI

You can find my NDS homebrew projects here...

#103664 - OOPMan - Sat Sep 23, 2006 6:11 pm

Also, a note...

I've placed an NDS specific copy of the library and it's source up on RapidShare...

The link is:

http://rapidshare.de/files/34174427/ndsmtc.rar.html

Please note that the file may end up being taken down. I'm mainly providing it so that people who are interested can take a look at things in their current form...
_________________
"My boot, your face..." - Attributed to OOPMan, Emperor of Eroticon VI

You can find my NDS homebrew projects here...

#103701 - Lick - Sat Sep 23, 2006 11:27 pm

Just so you know, I have been following this topic since the start. I can't really help out as I don't know much about this subject.. Great job so far, it would be really cool if threading would work with stuff like networking (dswifi).
_________________
http://licklick.wordpress.com

#103734 - OOPMan - Sun Sep 24, 2006 8:40 am

I hope it can be used for all kinds of things. If it can it'll save me and my project buddies the work of de-threading the Ur-Quan Masters source code, which'll mean the DS version will be out sooner :-)

With any luck I can get to the bottom of this sleep function issue with some help and it can be sorted out. At the moment the function doesn't seem to want to accept millisecond sleep values >= 1, although decimal values are fine, which is a little odd overall...

If things end up working nicely then it might also be useful for the SDL DS port...
_________________
"My boot, your face..." - Attributed to OOPMan, Emperor of Eroticon VI

You can find my NDS homebrew projects here...

#103760 - masscat - Sun Sep 24, 2006 12:29 pm

For interest:
One reason the inline assembler function PlatformSwitchStack does not work is that you have included the input register (r0) in the clobber list. This causes GCC to generate code to save the value of r0 before entering the asm statement and then restore it at the end. Therefore the value passed into the function will actually be the value returned (not what is wanted).
Including r13 in the clobber list could also cause problems with saving and restoring. Although it does not appear to as GCC does not think it has anything important in it. A working function could be:
Code:
void *
PlatformSwitchStack_other( void* pvStack) {
  void *old_stack;
  asm volatile(
          "mov %0, r13\n\t"
          "mov r13, %1\n\t"
          : "=&r"(old_stack)
          : "r"(pvStack)
          );
  return old_stack;
}

The "=&r"(old_stack) bit tells the compiler that old_stack is written before the value of pvStack is used and therefore stops it from using the same register. Here is a useful reference for inline assembler.
As with Cearn's code, it is often easier to work in pure assembler as you do not have to worry about the compiler doing strange things behind your back.

As for the MTClib code and license, I would have thought that it something that the company (Software Systems?) have written for internal use in their projects and then decided to release for people to use (as is). Unless you are paying money for a license I would not expect any support (even an email reply) unless somebody at the company is feeling kind and/or interested in the port. The "Limitations of use" section of the MTCLib website does not clearly state that you are allowed change and redistribute the source.

#103778 - OOPMan - Sun Sep 24, 2006 3:43 pm

Thanks for the info masscat, that was a nice detailed post there :-)

Yeah, I figured my inline asm was borked. Not surprising, seeing as how I've not done any actual asm for a long long time :-)

The howto you linked to was one that I referred to and it is a very useful little piece. Actuall, that reminds me, I think I'll post links to all the useful GCC and ARM related asm pages I located while digging around for help on this.

At the moment I'm using Cearn's external asm code and it seems to be working fine. Much easier to use and a whole lot cleaner than the inline stuff :-)

With regards to re-distributing the library, yeah, I know. However, I thought it would be better if other people could get their hands on the source (Easy to do anyway) and the NDS specific changes (Minor mostly) in one easy package so they can follow this thread a bit more easily...

The whole "open source" nature of the library is a bit odd. It's described as "open source, shareware", but doesn't seem to have any formal license (Not even GPL) attached. Weird and a little irritating...

EDIT: I've put the links to the various sites and so forth that I found proved useful in the stickied tutorials thread...
_________________
"My boot, your face..." - Attributed to OOPMan, Emperor of Eroticon VI

You can find my NDS homebrew projects here...

#104100 - masscat - Tue Sep 26, 2006 4:24 pm

I forgot to mention, I am having trouble downloading the source from the rapidshare link you posted. It keeps saying "Download-session invalid.".

#104109 - OOPMan - Tue Sep 26, 2006 5:05 pm

Gah, damn RapidShare. I hate those silly file-sharing sites...

Just took a look and the link *seems* to be working and indicates that 2 people have already downloaded...

Hmmmm, I'm gonna email you a copy (Via your website's email address).

If anyone else has issues I'll put the file up again on a proper site...

Also, some good news. I've managed to get in touch with the creator of the library, so he might be able to help me get to the bottom of this ThreadSleep problem...
_________________
"My boot, your face..." - Attributed to OOPMan, Emperor of Eroticon VI

You can find my NDS homebrew projects here...

#104188 - masscat - Wed Sep 27, 2006 12:21 am

Thanks for the source code.

I have been playing around with it.
One of the reasons the MTC_ThreadSleep does not work with a non-zero argument is that MTC_TimerTicks from ndscearn.c does not return updating timer values (the clock() function used does not do anything in libnds).
As a quick fix I added a counter using the DS timer 0.
Add the following to ndscearn.c:

Code:
#include <nds.h>

Code:
/*
 * The tick system
 */
static MTC_ui32 millisec_count = 0;
void
timingSystem( void) {
  millisec_count += 1;
}
static MTC_ui32
getTiming( void) {
  return millisec_count;
}


And change the follow functions to the code given:
Code:
int MTC_PlatformInit( void)
  {
  if ( 1 == ++s_iInited)
    {
    char* psz = getenv( "MTC_STACK");
    if ( NULL != psz)
      s_uStackSize = (MTC_size_t)atoi( psz);
    else
      s_uStackSize = MTC_STACKSIZE;
    if ( s_uStackSize < STACK_MIN)
      s_uStackSize = STACK_MIN;

    /* install the timer 0 handler */
    irqSet( IRQ_TIMER0, timingSystem);
    irqEnable( IRQ_TIMER0);

    /*
     * Start up a 1000Hz timer.
     */
    TIMER_CR(0) = 0;
    TIMER_DATA(0) = TIMER_FREQ_1024( 1000);
    TIMER_CR(0) = TIMER_ENABLE | TIMER_IRQ_REQ;

    MTC_PlatformEnterCritical();
    }

  return MTC_kErrorNone;
  }

Code:
MTC_EXPORT( MTC_ui32, MTC_TimerTicks( void))
  {
  /* NB clock returns elapsed process time, not real time */
    return getTiming();
    /*return (MTC_ui32)(((unsigned long long)clock() * MTC_TICKS_PER_SEC) / CLOCKS_PER_SEC);*/
  }


The demo still does not complete but it does get a bit further.

EDIT: you also need to add a call to irqInit() in main of main.c of the demo app.


Last edited by masscat on Wed Sep 27, 2006 9:37 am; edited 1 time in total

#104210 - HyperHacker - Wed Sep 27, 2006 5:28 am

masscat wrote:
I forgot to mention, I am having trouble downloading the source from the rapidshare link you posted. It keeps saying "Download-session invalid.".

You mean "Download-ticket [some German text about Javascript]"? When that happens I just keep refreshing many, many times until the dumb thing finally works.
_________________
I'm a PSP hacker now, but I still <3 DS.

#104267 - OOPMan - Wed Sep 27, 2006 4:08 pm

Thanks for that fix masscat, I'll test it out and add it to the library. The creator of the library emailed me again today. He gave me some useful info on a variety of aspects of the library.

With regards to the ThreadSleep issue he wrote:

Quote:
This maybe is a problem in MTC_PlatformTimerStart. If you have enabled
pre-emptive scheduling in config.h (MTC_SCHEDULE > 0) then you need to
arrange an asynchronous callback on timer expiration - see the example
skeleton.c for using signals. In the absence of pre-emptive scheduling then
the likely problem is that your MTC_TimerTicks routine not returning sensible
values.


Now, at moment pre-emptive scheduling hasn't been enabled in makefile (At present MTC_SCHEDULE is set to 0) so it looks like masscat hit the nail on the head :-) Thanks for that. Hopefully once we can get pre-emptive scheduling working once the current problem is properly wrapped up and verified to be fixed.

With regards to the history of MTCLib he wrote the following:

Quote:
I wrote the precursor to MTClib many years ago for use in small embedded
systems, which I've used many times over. In 2002 I started to pull together
some of the best concepts with a view to making it available side by side
with my SwsSock sockets library (which uses a subset). I ran out of time in
2002 but hoped to complete it later. In 2006 I had the time that it deserved
so I finished it off and released it.


and with regards to the library's support for ARM CPUs he said:

Quote:
I've written a lot of stuff for ARM's over the years - including a stint at
Acorn where I wrote parts of RISCOS 3. So I've always had a soft spot for
them. When I started the current version of MTClib I just happened to be
using the ARM SDT for a project, so I naturally used it to test the platform
level interfaces of MTClib.


Anyway, it's nice to see a proper multi-threading library that has been targetted at the kind of hardware and architecture we're developing on.

On the personal front, I should be able to spend some time integrating masscats changes tonight. Hopefully testing will prove interesting. I think I'll also put some work into the makefile...

EDIT: I've tested out masscat's fix, although I haven't updated the download archive with it yet. Hopefully an updated archive will be uploaded once the current new issue is resolve. I'm not certain myself whether or not it might be a side effect of masscat's fix code, but this is something we'll just have to look at...
_________________
"My boot, your face..." - Attributed to OOPMan, Emperor of Eroticon VI

You can find my NDS homebrew projects here...

#104504 - OOPMan - Fri Sep 29, 2006 9:30 pm

Well, I've done some tinkering with masscat's code and I've got some idea as to what's causing the problem...

Reducing the n value for TIMER_FREQ_1024 to 1 allowed the demo to complete successfully. Setting it to 500 allowed ot to get about halfway before throwing an assert error.

The basic problem is, I think, two-fold.

The first issue is that the MTC_uint32 type used for millisec_count is probably not large enough. I think the timingSystem() function is being called often enough to overflow the variable's upper bound thus causing a rollover, something that I'd imagine the thread system does not like at all (Going back in time is a neat trick, but not one we need in thise case...)

The second issue, I think, is that possibly the timer masscat has set up is being called too often and is thus causing an immediate overflow. Could someone verify whether or not his timer is correctly set up to mirror the effect of the original TimerTicks function, which is a monotonic millisecond timer.

If someone could suggest a piece of code that does not cause this overflow problem I'd greatly appreciate it. I have a suspicion that it's mainly a matter of tinkering, but I'd like some input on this...
_________________
"My boot, your face..." - Attributed to OOPMan, Emperor of Eroticon VI

You can find my NDS homebrew projects here...

#104553 - masscat - Sat Sep 30, 2006 11:50 am

I appear to have setup the timer value using a divider but then not included it when setting up the control, so it is running 1024 times too quickly. Change the timer setup to:
Code:
/*
 * Start up a 1000Hz timer.
 */
TIMER_CR(0) = 0;
TIMER_DATA(0) = TIMER_FREQ(1000);
TIMER_CR(0) = TIMER_ENABLE | TIMER_DIV_1 | TIMER_IRQ_REQ;

Demo completes now.

#104583 - OOPMan - Sat Sep 30, 2006 5:36 pm

Erm, masscat, which version of devkitPro are you running? Are you on r19b?

Compiling with that altered line has no effect. An examination of timers.h reveals the following that TIMER_DIV_1 is defined as follows:

Quote:
#define TIMER_DIV_1 TIMER_ENABLE


Hence, TIMER_DIV_1 is basically synonymous with TIMER_ENABLE...

Changing your line to

Quote:
TIMER_CR(0) = TIMER_DIV_1024 | TIMER_IRQ_REQ


or

Quote:
TIMER_CR(0) = TIMER_ENABLE | TIMER_DIV_1024 | TIMER_IRQ_REQ


allows the demo to complete. However, from the feel of it, using TIMER_DIV_1024 slows things down a little too much. I'm not really sure whether or not this is the case though.

For interests sake, TIMER_DIV_1024 is defined as:

Quote:
#define TIMER_DIV_1024 (TIMER_ENABLE|3)


Anyone else have any thoughts on this?
_________________
"My boot, your face..." - Attributed to OOPMan, Emperor of Eroticon VI

You can find my NDS homebrew projects here...

#104595 - OOPMan - Sat Sep 30, 2006 7:04 pm

Hey all, another update...

I've spend some time this afternoon cleaning the code, playing with the makefile and just doing some general re-organisation and so forth.

A new trial version of the library is available for download and testing here.

There is an attached readme file that contains all the details.

Enjoy :-)
_________________
"My boot, your face..." - Attributed to OOPMan, Emperor of Eroticon VI

You can find my NDS homebrew projects here...

#104601 - masscat - Sat Sep 30, 2006 8:22 pm

I am using devkitpro r19b under linux.

Regarding the timer thing, I described the error in terms of setting up the control value but then in the code I changed the data value to get the correct result. Sorry for the confusion.
Setup the data using the 1 divisor macro (TIMER_FREQ):
Code:
TIMER_DATA(0) = TIMER_FREQ(1000);

Then setup and enable the timer to use the same 1 divisor:
Code:
TIMER_CR(0) = TIMER_ENABLE | TIMER_DIV_1 | TIMER_IRQ_REQ;

This also works using the TIMER_FREQ_1024 and TIMER_DIV_1024 macros. The important thing is using the same divisor macro in both cases. The reason for using the 1 divisor macro is to get a more accurate timer (not that important).

As for the timings (very rough estimates):
Using MTC_ThreadSleep( (MTC_ui32)(0.01 * MTC_TICKS_PER_SEC));, the demo completes in about 1/4 of a second.
Using MTC_ThreadSleep( (MTC_ui32)(0.1 * MTC_TICKS_PER_SEC));, it takes about 2.5 seconds. So in both cases this is about right (26 loops at intervals of 0.01 and 0.1 seconds respective).

If I use MTC_ThreadSleep( (MTC_ui32)(1 * MTC_TICKS_PER_SEC)); then the output gets to "c2 C2" and then there is an assertion error. I have not looked into why this is happening.

#104625 - OOPMan - Sat Sep 30, 2006 10:32 pm

Woops, my bad. Gah, I really need to pay more attention to more than one line of code at a time :-)

The fix has been added to the source and the archive available at NDS Storage has been amended (At least, I'm 99% sure I just uploaded an amended version...)

I'll look into the assert error you mentioned.

Also, do you have any idea on how to go about implementing pre-emptive scheduling?

EDIT: I've looked into the assert error that masscat mentioned. Don't worry, this is actually meant to happen :-) Why?

Well, it boils down to this:

On lines 96 though 9 we have:
Quote:
err = MTC_ThreadWait( h1, 3 * MTC_TICKS_PER_SEC, &v1);
assert( !err);
err = MTC_ThreadWait( h2, 3 * MTC_TICKS_PER_SEC, &v2);
assert( !err);


On line 139 we have:
Quote:
MTC_ThreadSleep( (MTC_ui32)(0.01 * MTC_TICKS_PER_SEC));


Now, if we change the value on line 139 from 0.01 to 1 we're instructing the thread to wait for a longer period.

However, if we don't change the values on lines 96 and 98 from 3 to a higher value the program will fail with an assert error.

This happens because the ThreadWait function has been told to wait for a set period of time, in total, for the thread to finish. If the thread doesn't finish in the given time period it is assumed to have hung and the program terminates.

So, nothing to worry about in this respect :-)
_________________
"My boot, your face..." - Attributed to OOPMan, Emperor of Eroticon VI

You can find my NDS homebrew projects here...

#104820 - masscat - Mon Oct 02, 2006 10:33 pm

Good to see that threading appears to be working.

As for pre-emption, the best place to do this would be inside the timer handler.
One way to do it:
Include a field in the platform thread structure to hold the thread kick time. When you set up the running thread set this kick time to the time, in milliseconds, when the thread is to be kicked off (something like kick_time = getTiming() + timeslot_length).
In the timer handler, compare the kick time of the current thread with the current time and if it is less than or equal then switch threads.

Now that sounds nice and easy, but the problem is that if the thread switch happens in the timer handler you actually want to save the register set of the running thread as it was when it was interrupted and not that in the handler (you want to return to the point of execution in the thread and not to the timer handler when you switch back to the thread). This means that you cannot use the C functions setjmp and longjmp for saving context and jumping between the context. Back to doing a bit of assembler.

It could be better and easier to write your own interrupt dispatcher code and code for threads to install interrupt handlers, i.e. replacing the libnds irqInit(), irqSet and other irq functions. This would insulate the library from changes to the libnds interrupt handler (which may happen as Hermes appears to have found a bug in the libnds despatcher) and make it easier forming the correct register set when switching threads.

#104929 - OOPMan - Wed Oct 04, 2006 11:23 am

Okay, well, I've done a preliminary implementation of pre-emption. I'm doing some checking with the creator of the library to ensure whether or not it is functioning correctly and I'll probably put up a testr version for download.

At the moment I'm running the pre-emption on a separate timer for testing purposes (Although I can integrate it into the millisecond timer...) and it seems to be working (Well, demo4, which requires pre-emption, runs to completion anyway...)

Anyway, expect an upload sometime soon...

I'll also look into that interrupt dispatcher code, as you mentioned, since it would be unpleasant for a bug to disrupt things...
_________________
"My boot, your face..." - Attributed to OOPMan, Emperor of Eroticon VI

You can find my NDS homebrew projects here...

#104986 - OOPMan - Wed Oct 04, 2006 10:04 pm

I've now uploaded a copy of the updated library to NDS Storage, having done some more bug-fixing and so forth.

Pre-emption appears to be working fine at the moment. The test version that ran it on a separate timer had sychronisation problems (This was revealed by running the debug version of demo4) and hence ended up pre-empting too soon.

I've now intergrated the pre-emption into the timer routine, although the current intergration is not necessarily final. At present demo4 runs perfectly in both debug and release builds.

The makefiles have also been tuned and tweaked, allowing for more build options and brining the source tree more in line with the basic library.

Comments would be appreciated and optimisations would be welcome :-)
_________________
"My boot, your face..." - Attributed to OOPMan, Emperor of Eroticon VI

You can find my NDS homebrew projects here...

#106900 - OOPMan - Tue Oct 24, 2006 4:23 pm

Okay, some more news. The devkitPro port of MTCLib has been integrated into the main source tree and is now available as a download from the library's homepage.

I'm going to take the version available at NDS Storage down, since it was primarily intended for testing.

Once again, many thanks to cearn, masscat and sasq for their help in porting the library.
_________________
"My boot, your face..." - Attributed to OOPMan, Emperor of Eroticon VI

You can find my NDS homebrew projects here...