Seuls les membres ayant 30 points peuvent parler sur le chat.

Forum Casio - Projets de programmation


Index du Forum » Projets de programmation » Complete C standard library
MemalloxHors ligneMembrePoints: 161 Défis: 0 Message

Complete C standard library

Posté le 19/08/2018 19:31

Motivation
Until now there was no complete C standard library (aka libc) available for the Casio calculators. Although some parts of this library have been provided by fxlib and gint, there was no libc implementation complying with the standard and compatible with the sh3eb architecture ready to use.

To change that, I decided to port newlib to the Casio CPU. Newlib is an alternative libc implementation intended for use on embedded systems.


Alpha
Follow this link and click the download button in the top right corner:

>>> v1.1 <<<


Instructions on how to install newlib alongside with gcc (big shout-out to Lephé):

Compiler sous Linux avec GCC


Features for Casio fx9860g calculators:
* C standard library libc
printf implementation to print text to the display
→ Dynamic allocation of memory using malloc and free
→ Memory manipulation using memcpy, memcmp, memset etc.
→ String manipulation using strcpy, strcmp, strstr, strtok
→ ...
* Math library libm
→ Floating point arithmetics
→ ...
* Automatic library and include path recognition after installation
* Basic Casio features:
→ implementation of GetKey, Bdisp_AllClr_DDVRAM, Bdisp_PutDisp_DD, Print and locate without fxlib (but you can use it if you want)


Code
To contribute or get all those bleeding edge features, see the code including all further information:

libc (my GitLab repository)


The project you find in my repository is a fork of the official newlib repository. To make it easier for everyone to follow, I try to keep a clean git history. That means that all my changes are located on a dedicated branch with meaningful commits.

I also try to keep the changes to the upstream library minimal. That increases maintainability a lot.


Contributing
If you have a ideas, feature request, found a bug or simply want to contribute, just send me a message and you're in! You can also create Issues and Merge Requests directly in the repository. As in every OpenSource project: merge requests welcome!


Pages : Précédente1 ... , 5, 6, 7, 8, 9, 10
LephenixnoirHors ligneAdministrateurPoints: 16384 Défis: 140 Message

Citer : Posté le 30/09/2018 20:50 | #


I just finished recompiling the sh3eb-elf toolchain after switching to a new, more powerful laptop. It was pretty fast, but I did not manage to build newlib smoothly.

I still had issues with casio_syscalls.c being mentioned and no Makefile.am file to be found - I'm completely puzzled. make clean did not seem to clean everything, make distclean managed to fail at some point until the target just disappeared. I can say I didn't understand a thing...

So I cloned a fresh copy of the repository ( ) and I hope that building outside the source tree will solve the issues as there will no longer be built or generated files in the original repository. It works for now, I'll have to see for the future (especially when building the sh4eb-nofpu-elf toolchain in 10 minutes).

I'm not saying there is anything to "fix" there, but yeah, building newlib is rather messy compared to binutils/gcc.

Ajouté le 30/09/2018 à 21:10 :
Aha, but there is nothing for "sh4" in there. Gotta use the sh3eb-elf version again I suppose.

Did you ever tried to use your version of newlib with the sh4eb-nofpu-elf toolchain? It would be cool if it worked.
MemalloxHors ligneMembrePoints: 161 Défis: 0 Message

Citer : Posté le 07/10/2018 10:52 | #


Sorry, I was away on vacation and had basically no internet.

If the missing target sh4eb-nofpu-elf is the problem - I know, there is none yet. I planned to support this in the future but cannot anticipate the needed effort at the moment. Do you think that the code can stay the same? I would just map sh4eb-nofpu-elf to sh3eb-elf in this case.

How do you compile binutils and gcc for sh4eb-nofpu-elf targets? If the procedure is the same, I'd try that out some time. You might say a few words about it at the end of your tutorial, just as a note for devs that do not care about compatibility and want to play around
Stop starting~ Start finishing~
LephenixnoirHors ligneAdministrateurPoints: 16384 Défis: 140 Message

Citer : Posté le 07/10/2018 11:00 | #


If you don't hardcode any hardware address, there you can re-use the same code, for sure.

Building for sh4eb-nofpu-elf is exactly the same, in fact there's a section at the beginning of the tutorial that selects that target, then everything is the same.
MemalloxHors ligneMembrePoints: 161 Défis: 0 Message

Citer : Posté le 07/10/2018 19:30 | #


Oh, I didn't see that section in your tutorial.... I just installed it. In fact, I had to install the packages textinfo and flex to be able to build binutils.

I mapped sh4eb to sh3eb now and pushed the changes to the sh3eb branch (which is the default branch). Thanks for the feedback

Ajouté le 07/10/2018 à 19:32 :
Newlib should work for sh4eb-nofpu-elf now as well
Stop starting~ Start finishing~
LephenixnoirHors ligneAdministrateurPoints: 16384 Défis: 140 Message

Citer : Posté le 07/10/2018 20:23 | #


Excellent! Note that texinfo is only for the documentation from info, but you're better off with it. I added the flex dependency in my tutorial.
MemalloxHors ligneMembrePoints: 161 Défis: 0 Message

Citer : Posté le 13/10/2018 13:40 | #


Ok, it seems I did not test enough. Newlib compiles for both sh3eb and sh4eb arches. Also, functions like locate(1, 1) and write(1, "Hello", 5) work just fine for both arches.

However, when I call printf, the program crashes only for the sh4eb arch.


System ERROR
REBOOT    :[EXIT]
INITIALIZE:[EXE]
Illegal Code Err
TARGET=00000000
PC    =00000000


What could this error mean? Do you have a hint for me?

Stop starting~ Start finishing~
LephenixnoirHors ligneAdministrateurPoints: 16384 Défis: 140 Message

Citer : Posté le 13/10/2018 13:58 | #


It means that the program is executing an illegal instruction, typically an SH4 instruction on an SH3 machine. Since the SH-4A is retro-compatible with SH-3, I find odd that you get this error on SH4 but not on SH3.

Usually you would look at PC and check out the incorrect instruction in the binary, but you don't have any PC here. I suggest looking for assembler chunks in the source or doing a big objdump -d and looking for .word. But I think it will yield a lot of immediate data.
MemalloxHors ligneMembrePoints: 161 Défis: 0 Message

Citer : Posté le 13/10/2018 14:14 | #


Hmm, maybe my calculator contains a SH-3 processor? Can I check this somehow?

I tried GlibGetOSVersionInfo():

static const unsigned int GlibGetOSVersionInfo_addr[] = {0xD201D002, 0x422B0009, 0x80010070, 0x015};
//...
char a, b;
short int c, d;
((int (*)(char*, char*, short int*, short int*)) GlibGetOSVersionInfo_addr)(&a, &b, &c, &d);
/*
* a = 2
* b = 9
* c = 1
* d = 0
*/


Ajouté le 13/10/2018 à 14:16 :
When I say "does not work with the sh4 arch", I mean that I compiled the code with the sh4eb-nofpu-elf compiler for the same device.
Stop starting~ Start finishing~
LephenixnoirHors ligneAdministrateurPoints: 16384 Défis: 140 Message

Citer : Posté le 13/10/2018 14:19 | #


You can check for SH3/SH4 differences using the constructor menu, but I don't remember the exact values. Alternatively, you can run this function from Simon Lothar.

Ooh, then you probably have an SH3 machine and ran an SH4 program on it. sh4eb-nofpu-gcc is likely to generate SH4-only code.
MemalloxHors ligneMembrePoints: 161 Défis: 0 Message

Citer : Posté le 13/10/2018 14:43 | #


Home menu -> System -> Version:
02.09.0201


According to this thread, I should have a SH-4. Herrgott, I fear it will be painful to find this bug...
Stop starting~ Start finishing~
LephenixnoirHors ligneAdministrateurPoints: 16384 Défis: 140 Message

Citer : Posté le 13/10/2018 14:48 | #


If you are not using any keyboard I/O or timers, you can switch VBR and map the exception handler to a function of your own. There, you will be able to check the SPC register, which will give you the location of the offending instruction. Make sure to restore VBR once you're done.
ZezombyeHors ligneRédacteurPoints: 1640 Défis: 13 Message

Citer : Posté le 17/10/2018 06:56 | #


Are you using any syscalls in your printf()? If yes, what are they?

I've had crashes on sh4 with some syscalls, while it worked perfectly on sh3.
Divers jeux : Puissance 4 - Chariot Wars - Sokoban
Ecrivez vos programmes basic sur PC avec BIDE
LephenixnoirHors ligneAdministrateurPoints: 16384 Défis: 140 Message

Citer : Posté le 17/10/2018 07:46 | #


Zezombye, which syscalls exactly? I'd like to know, for gint.
MemalloxHors ligneMembrePoints: 161 Défis: 0 Message

Citer : Posté le 17/10/2018 14:54 | #


@Lephe
Thank you, I already found your implementation of set_vbr(). This time I think I need to take some time reading the datasheet :D.

@Zezombye
Actually, I do use system calls like locate() and Print(). However, I did not implement printf() but write() which is called by printf(). Strangely, write() works... only printf() makes the CPU crash.
Stop starting~ Start finishing~
LephenixnoirHors ligneAdministrateurPoints: 16384 Défis: 140 Message

Citer : Posté le 17/10/2018 22:26 | #


Btw it may not be strange that only printf() crashes, because it has something more than write() : variadic arguments. You may have been using it already, but just to check, there's -mrenesas if you're calling Casio code with variadic arguments.
MemalloxHors ligneMembrePoints: 161 Défis: 0 Message

Citer : Posté le 18/10/2018 18:59 | #


I did not even know that there are Casio syscalls that take variadic arguments The printf() function (which I did not change) passes a string of known length to ssize_t write(int fd, const void *buf, size_t size);.

I try to get what you were doing with the VBR in gint. I guess the VBR contains the base address of a table containing the addresses of functions which are called if certain events occur (interrupt routines).

There are many events with certain offsets and event codes. Do you know which kind of event I need to write an interrupt handler for? Is there a kind of "general interrupt handler" which catches all exceptions?

I'm a little bit confused about your code. I think the most important lines are the following:


void *gint_inthandler(int event_code, const void *handler, size_t size)
{
        /*
         * In fx9860g.ld:147 there is
         * _gint_vbr = 0x8800df00;
         * I thought this sets the value (not the address) of a variable?
         * Well, apparently this sets the address of gint_vbr
         */
        extern char gint_vbr;

        /* Normalize the event code */
        event_code -= 0x400;                 // random substraction?

        //...

        void *dest = (void *)&gint_vbr + event_code + 0x620;
        return memcpy(dest, handler, size);
}


You then call this function multiple times for different interrupt routines which you wrote in assembler. Correct me if I'm wrong, but I do not need to restore the VBR nor do I need to call the return-from-exception instruction RTE at the end of the routine if all I want to do is display the SPC, right? After a power down, the processor will be reset anyway.

What I need to do is calling gint_setvbr((uint32_t) &gint_vbr, dummy_fcn);.

Hmm, do you know why do I get "invalid register name" when I try to reference the SPC register? There is probably a compiler flag I'm missing? I saw that you could use vbr in src/core/vbr.s.

register void * spc_addr __asm__("spc");

Stop starting~ Start finishing~
LephenixnoirHors ligneAdministrateurPoints: 16384 Défis: 140 Message

Citer : Posté le 18/10/2018 19:25 | #


Not necessarily syscalls, but mainly functions from fxlib.

I try to get what you were doing with the VBR in gint. I guess the VBR contains the base address of a table containing the addresses of functions which are called if certain events occur (interrupt routines).

Not exactly, the layout of the interrupt handling routines is predetermined in hardware and you do not have the "freedom" of specifying your addresses in a table. You can set VBR at any address you like (in P1 or P2 space), but your functions must be laid out exactly like this :

* Exception handler 0x100 bytes after VBR
* TLB exception handler 0x400 bytes after VBR
* Interrupt handler 0x600 bytes after VBR

I'm a little bit confused about your code. I think the most important lines are the following:

This is actually very old and unoptimized. What's going on here? When an interrupt is accepted, an interrupt event code is placed in an appropriate register (INTEVT2 on SH7705, INTEVT on SH7305). This code happens to be at least 0x400 for any interrupt, so in the interrupt handler, I "normalize" it for logging/debugging/array manipulation purposes. This has no semantic meaning or effect on the interrupt handling system whatsoever.

Correct me if I'm wrong, but I do not need to restore the VBR nor do I need to call the return-from-exception instruction RTE at the end of the routine if all I want to do is display the SPC, right? After a power down, the processor will be reset anyway.

You only restore the VBR when you want to relinquish interrupt management and give it back to the OS. However you do have to call rte at the end of the routine or you cannot leave the routine. Although you can just enter an infinite loop and wait for a reset, this is legit. But if you have __attribute__((interrupt_handler)) on your function, GCC will take care of saving registers and using rte instruction. If you're doing assembler this is different, but to log SPC, C is enough.

What I need to do is calling gint_setvbr((uint32_t) &gint_vbr, dummy_fcn);.

Not any dummy function. Recall that when you own the VBR, you are responsible for handling interrupts. If you do not disable interrupts in your function, you will receive many timer/RTC/keyboard events which will lock your program because your interrupt handler will not know how to handle them.

Hmm, do you know why do I get "invalid register name" when I try to reference the SPC register? There is probably a compiler flag I'm missing? I saw that you could use vbr in src/core/vbr.s.

I don't think you can do this kind of access to spc by associating it to a variable, spc is not a general-purpose register. If you want to get its value you have to use the store control register instruction using inline assembly:

uint32_t spc;
__asm__("stc spc, %0": "r"(spc));
MemalloxHors ligneMembrePoints: 161 Défis: 0 Message

Citer : Posté le 26/10/2018 15:36 | #


Thank you very much for the insight, this helped me quite a bit!

When I read the datasheet, section 5.3.2 and the following table, there are 4 possible vector addresses:

* 0xA000000: not changeable, for resets (abort type ecxeptions)
* VBR + 0x100
* VBR + 0x400
* VBR + 0x600

To distinguish the exception types further, the interrupt routine can read out INTEVT which contains the event code.

However, in your code you write to peculiar addresses:


static void init(void)
{
        /* Install the standard's TMU interrupt handlers. By chance TMU gates
           use the same event codes on SH7705 and SH7305 */
        UNUSED void *h0, *h1, *h2, *hs;
        h0 = gint_inthandler(0x400, inth_tmu_0, 32);
        h1 = gint_inthandler(0x420, inth_tmu_1, 32);
        h2 = gint_inthandler(0x440, inth_tmu_2, 32);
        hs = gint_inthandler(0x460, inth_tmu_storage, 32);

        //...

/* gint_inthandler() - configure interrupt handlers */
void *gint_inthandler(int event_code, const void *handler, size_t size)
{
        extern char gint_vbr;       // set by linker script

        /* Normalize the event code */
        event_code -= 0x400;
        event_code &= ~0x1f;

        /* Prevent overriding the entry gate */
        if(event_code < 0) return NULL;

        void *dest = (void *)&gint_vbr + event_code + 0x620;
        return memcpy(dest, handler, size);
}


Those addresses are 0x620, 0x640, 0x660, and 0x680. I thought the event codes are used for information only, not for VBR address offsets?

I wrote some code (or copied from gint, in fact ) to read out the PC in case of an exception. Unfortunately, as soon as I call gint_setvbr, my calculator crashes. I'm not sure if I copy my exception routine to the right place. I'm also not sure if my lock() function is sufficient (it is also copied ). I see that you disable various interrupts there.


#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>

#include "core/intc.h"    // from gint

uint32_t gint_setvbr(uint32_t vbr, void (*configure)(void));

GDATA sh7305_intc_t INTC4 = {
        .IPRS           = (void *)0xa4080000,
        .MSK            = (void *)0xa4080080,
        .MSKCLR         = (void *)0xa40800c0,
        .USERIMASK      = (void *)0xa4700000,
};

uint32_t pc = 0;
uint32_t *INTEVT = (uint32_t*) 0xFF000028;

void __attribute__((interrupt_handler)) exc_handler(void) {
    __asm__("stc spc, %0": "=r"(pc));
}

void lock(void) {
    /* Unmask the extra timers' interrupts?*/
    // INTC4.MSKCLR->IMR2 = 0x01;
    // INTC4.MSKCLR->IMR5 = 0x06;
    // INTC4.MSKCLR->IMR6 = 0x18;
    // INTC4.MSKCLR->IMR8 = 0x02;

    for(int i = 0; i < 12; i++) INTC4.IPRS[2 * i] = 0x0000;
}

void setup(void) {
    /* gint_inthandler() - configure interrupt handlers */
    void *gint_vbr = (void *) 0x8800df00;    // from linker script
    memset(gint_vbr, 0, 0x1000);

    /* copy own interrupt routine to the vector addresses */
    memcpy(gint_vbr + 0x100, &exc_handler, 32);
    memcpy(gint_vbr + 0x400, &exc_handler, 32);
    memcpy(gint_vbr + 0x600, &exc_handler, 32);

    /* Time to switch VBR and roll! */
    uint32_t system_vbr = gint_setvbr((uint32_t) gint_vbr, lock);
}

void sprint_addr(char* str, uint32_t addr) {
    const char digits[] = "0123456789ABCDEF";
    str[10] = '\0';

    str[9] = digits[addr / 0x1 % 16];
    str[8] = digits[addr / 0x10 % 16];
    str[7] = digits[addr / 0x100 % 16];
    str[6] = digits[addr / 0x1000 % 16];

    str[5] = digits[addr / 0x10000 % 16];
    str[4] = digits[addr / 0x100000 % 16];
    str[3] = digits[addr / 0x1000000 % 16];
    str[2] = digits[addr / 0x10000000 % 16];

    str[1] = 'x';
    str[0] = '0';
}

int main(void) {
    unsigned int key = 0;
    char out[20] = {0};

    setup();

    while (1) {
        if (pc != 0) {
            locate(1, 1);
            Print("SPC:    ");
            sprint_addr(out, pc);
            Print(out);

            locate(1, 2);
            Print("INTEVT: ");
            sprint_addr(out, *INTEVT);
            Print(out);
        }

        GetKey(0);
    }

    return 0;
}



Stop starting~ Start finishing~
LephenixnoirHors ligneAdministrateurPoints: 16384 Défis: 140 Message

Citer : Posté le 26/10/2018 16:37 | #


Those addresses are 0x620, 0x640, 0x660, and 0x680. I thought the event codes are used for information only, not for VBR address offsets?

The truth is, a very efficient way of distinguishing cases for INTEVT in the interrupt handler is to jump at VBR + 0x620 + INTEVT. The first 0x20 bytes are use to retrieve the value of INTEVT and then you just jump. Since INTEVT is always a multiple of 0x20 (for this exact purpose, I reckon), you just need to place each interrupt-source-specific handler in its own 0x20-byte block. Which gint does.

All of this belongs to how you choose to implement the interrupt handler. The only constraint is to put the entry point at VBR + 0x600, for the rest you are free. My previous version was a full-C program with structures and dynamic data, designed to be extendable, but uselessly high-level and heavy. It could only handle up to 180k interrupts per second, while my more recent INTEVT-jumping assembler handler does 320k.

So, to come back to your question, my code here is copying interrupt handler blocks of 32 bytes at appropriate locations in the VBR space. The location is specified in the form of the INTEVT code and gint then computes the proper address. You can extend the interrupt handler by installing your own block at the appropriate offset.

I wrote some code (or copied from gint, in fact ) to read out the PC in case of an exception. Unfortunately, as soon as I call gint_setvbr, my calculator crashes. I'm not sure if I copy my exception routine to the right place. I'm also not sure if my lock() function is sufficient (it is also copied ). I see that you disable various interrupts there.

Let's go through it in order. You first define a C function with the calling conventions of interrupt handlers, that retrieves spc. This is good, but not sufficient. If you use this function, then whenever an exception will occur, you will save the value of spc, then do nothing, then let the user program resume. There are two problems here:

1. You never display the value of spc
2. The program is probably in an inconsistent state because something went wrong, since there was an exception

My advice is, if you're not going to "fix" the exception (if exception) or handle the interrupt (if interrupt), then you should never leave the interrupt handler. This lets you display things and press RESET without risking a crash, because crashes don't leave any information behind. So I'd be inclined to write something like this:

void __attribute__((interrupt_handler)) exc_handler(void) {
    uint32_t spc;
    __asm__("stc spc, %0": "=r"(spc));
    Bdisp_AllClr_VRAM();
    /* print spc in hexadecimal */
    Bdisp_PutDisp_DD();
    while(1);
}

Your lock() function does the right thing. The big idea is that you must disable all interrupts your handler does not support, because an interrupt request can only be cleared by doing a specific action, like clearing a bit in a peripheral register. If you don't do it, the interrupt will stay there and will be re-accepted immediately, so your handler will start going in an infinite loop. This is much like freezing.

So you do it correctly. If you don't handle any interrupt, you can use more severe methods like setting SR.BL or clear the IMASK to 0, but the result is the same (except for the NMI, but it never occurs AFAIK).

void setup(void) {
    /* gint_inthandler() - configure interrupt handlers */
    void *gint_vbr = (void *) 0x8800df00;    // from linker script
    memset(gint_vbr, 0, 0x1000);

    /* copy own interrupt routine to the vector addresses */
    memcpy(gint_vbr + 0x100, &exc_handler, 32);
    memcpy(gint_vbr + 0x400, &exc_handler, 32);
    memcpy(gint_vbr + 0x600, &exc_handler, 32);

    /* Time to switch VBR and roll! */
    uint32_t system_vbr = gint_setvbr((uint32_t) gint_vbr, lock);
}

This function does several things wrong. The first is to hardcode the address of the VBR space instead of using the symbol exported by the linker script. Using the linker script-provided variable will fail to compile if you mess up with the linker script, whereas this will not, so it's one more possible hidden cause of bugs.

Note that you really don't need to wipe the VBR space.

The second error is to copy 32 bytes instead of copying the length of the function. Currently your exc_handler() certainly fits in 32 bytes, but it's not really reliable.

Now, it's unlikely that your code crashes inside gint_setvbr(). It might crash because of an interrupt of the GetKey(0) where you forgot to pass the address of key (yes, it crashes if you do that).

I should add, that when your interrupt handler is not in blocks as mine, the proper way to load it into memory would be to put it in its own section and link it just at VBR + 0x600 with ld in the first place. There are examples of this in the current master branch of the repository, you can check the linker script here.
MemalloxHors ligneMembrePoints: 161 Défis: 0 Message

Citer : Posté le 26/10/2018 19:41 | #


Lephenixnoir a écrit :
1. You never display the value of spc
2. The program is probably in an inconsistent state because something went wrong, since there was an exception

You're right, I moved the code to display the SPC into the interrupt routine.

Lephenixnoir a écrit :
The truth is, a very efficient way of distinguishing cases for INTEVT in the interrupt handler is to jump at VBR + 0x620 + INTEVT

Oh that's actually very clever! I did not see that since I did not take a deeper look into the assembly code.

Lephenixnoir a écrit :
Now, it's unlikely that your code crashes inside gint_setvbr(). It might crash because of an interrupt of the GetKey(0) where you forgot to pass the address of key (yes, it crashes if you do that).

1. You are wrong, GetKey(0) is a valid call which sleeps until the next key stroke.
2. I do not think that the program crashes inside but after gint_setvbr(). I guess there is an interrupt pending which fails as soon as the code in the vector table is to be executed (because I did not set it up properly).

Hmm, now it still crashes when I call gint_setvbr():


OUTPUT_ARCH(sh3)
ENTRY(initialize)
MEMORY
{
        rom  : o = 0x00300200, l = 512k
        ram  : o = 0x08100000, l = 8k  /* pretty safe guess */

        /* RAM section from P1 area, no MMU involved */
    realram    : o = 0x8800d000, l = 12k

}
SECTIONS
{
        .text : {
                *(.pretext)     /* init stuff */
                *(.text)
        } > rom
        .rodata ALIGN(4) : ALIGN(4) {
                . = ALIGN(4);

                *(.rodata)
                *(.rodata.str1.4)

                . = ALIGN(4);
                _romdata = . ;  /* symbol for initialization data */
                /*initialize = .;*/

                . = ALIGN(4);
        } > rom
        
        /*
        .eh_frame : AT(0x00310000) {
                *(.eh_frame)
        } > rom
        */

        .bss : {
                _bbss = . ;
                _bssdatasize = . ;
                LONG(0);        /* bssdatasize */
                *(.bss) *(COMMON);
                _ebss = . ;
        } > ram
        .data ALIGN(4) : ALIGN(4) {
                _bdata = . ;
                *(.data);
                _edata = . ;

        /* Provide a default */
        _end = . ;

                . = ALIGN(4);
        } > ram AT> rom

        /* Real RAM sections: interrupt handlers and some *uninitialized* gint
       data */
    .gint : AT(_end) ALIGN(4) {
        /* The vbr needs to be 0x100-aligned because of an ld issue */
        . = ALIGN(0x100) ;

        _gint_vbr = . ;

        /* Exception handler */
        . = _gint_vbr + 0x100 ;
        *(.gint.exc_handler)

        /* TLB miss handler */
        . = _gint_vbr + 0x400 ;
        *(.gint.exc_handler)

        /* Interrupt handler */
        . = _gint_vbr + 0x600 ;
        *(.gint.exc_handler)
    } > realram


        /*
        PROVIDE(_stack = .);
        PROVIDE(_end = .);
        */
        
        /DISCARD/ : {
               *(.eh_frame)
        }
}



#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>

#include "core/intc.h"    // from gint

uint32_t gint_setvbr(uint32_t vbr, void (*configure)(void));

GDATA sh7305_intc_t INTC4 = {
        .IPRS           = (void *)0xa4080000,
        .MSK            = (void *)0xa4080080,
        .MSKCLR         = (void *)0xa40800c0,
        .USERIMASK      = (void *)0xa4700000,
};

uint32_t pc = 0;
uint32_t *INTEVT = (uint32_t*) 0xFF000028;

void sprint_addr(char* str, uint32_t addr) {
    const char digits[] = "0123456789ABCDEF";
    str[10] = '\0';

    str[9] = digits[addr / 0x1 % 16];
    str[8] = digits[addr / 0x10 % 16];
    str[7] = digits[addr / 0x100 % 16];
    str[6] = digits[addr / 0x1000 % 16];

    str[5] = digits[addr / 0x10000 % 16];
    str[4] = digits[addr / 0x100000 % 16];
    str[3] = digits[addr / 0x1000000 % 16];
    str[2] = digits[addr / 0x10000000 % 16];

    str[1] = 'x';
    str[0] = '0';
}

__attribute__((section(".gint.exc_handler"), interrupt_handler))
void exc_handler(void) {
    // uint32_t spc;
    // char out[20] = {0};

    // __asm__("stc spc, %0": "=r"(spc));
  
    // //Bdisp_AllClr_VRAM();    
    // locate(1, 1);
    // Print("SPC:    ");
    // sprint_addr(out, pc);
    // Print(out);

    // locate(1, 2);
    // Print("INTEVT: ");
    // sprint_addr(out, *INTEVT);
    // Print(out);
    // //Bdisp_PutDisp_DD();

    while(1);
}

void lock(void) {
    for(int i = 0; i < 12; i++) INTC4.IPRS[2 * i] = 0x0000;
}

void setup(void) {
    /* gint_inthandler() - configure interrupt handlers */
    extern void *gint_vbr;    // from linker script

    /* Time to switch VBR and roll! */
    uint32_t system_vbr = gint_setvbr((uint32_t) gint_vbr, lock);
}

int main(void) {
    unsigned int key = 0;

    setup();

    while (1);

    return 0;
}

Stop starting~ Start finishing~
LephenixnoirHors ligneAdministrateurPoints: 16384 Défis: 140 Message

Citer : Posté le 26/10/2018 22:29 | #


1. You are wrong, GetKey(0) is a valid call which sleeps until the next key stroke.

Yes, and then crashes when you press a key. It depends on whether the key you are pressing to start the add-in is enough to end the first call to GetKey() (I reckon not, but humans pressing buttons are not failproof.) I used to use GetKey(NULL), but you gain nothing apart from sparing a variable vs. risking to crash. Anyway, let's not fight over something like this.

2. I do not think that the program crashes inside but after gint_setvbr(). I guess there is an interrupt pending which fails as soon as the code in the vector table is to be executed (because I did not set it up properly).

Yeah, this is a very convincing explanation, and in general the first thing I think of. Except that here you've disabled all interrupts so no request can be accepted until you re-enable them.

     /* gint_inthandler() - configure interrupt handlers */
    extern void *gint_vbr;    // from linker script

    /* Time to switch VBR and roll! */
    uint32_t system_vbr = gint_setvbr((uint32_t) gint_vbr, lock);

This part here is wrong, when you export a symbol from a linker script, the value of the symbol is as you'd expect the calculated address or integer (here the symbol's value is the VBR address); however when you program in C, a variable's symbol represents its address. Here you are declaring an external variable gint_vbr, so the symbol _gint_vbr is the address of the variable. Thus your call should be:

gint_setvbr((uint32_t)&gint_vbr, lock);

Note that, when you extern-declare functions, different things happen depending on whether you use the prototype syntax or the function-pointer syntax. With the prototype syntax, the compiler knows that it's a function and references to the variable will implicitly be turned into references to the function address; but with the function-pointer syntax, the value of the symbol (which is the address of the function code) corresponds to the address of the function-pointer variable, so any access to the variable itself will return the first few bytes of the compiled code of the function. Just to highlight how subtle it can be.
Pages : Précédente1 ... , 5, 6, 7, 8, 9, 10

Planète Casio v42 © créé par Neuronix et Muelsaco 2004 - 2019 | Il y a 61 connectés | Nous contacter | Qui sommes-nous ? | Licences et remerciements

Planète Casio est un site communautaire non affilié à Casio. Toute reproduction de Planète Casio, même partielle, est interdite.
Les programmes et autres publications présentes sur Planète Casio restent la propriété de leurs auteurs et peuvent être soumis à des licences ou copyrights.
CASIO est une marque déposée par CASIO Computer Co., Ltd