This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Porting MSP430P325 to MSP430F417

Other Parts Discussed in Thread: MSP430P325, MSP430F417

Hello all! I'm new and I didn' find topics about my question, that's why I'm here ;)

I have to do porting in assembly code from the obsolete MSP430P325 and the MSP430F417.

Could you explain to me what kind of changes must be done? Are the instructions the same? I have particulary a question if I should put a "&" before the destination of some instructions like "move" or "clr".


I hope I was clear, I you need some details, do not hesitate to ask me.


Thanks

  • Pierre Augustin said:
    I have particulary a question if I should put a "&" before the destination of some instructions like "move" or "clr".

    Depends :)

    Teh & means the destination address is an absolute address. If you move teh binary code, teh destination won't change.

    If you omit the &, the destiantion address is stored relative to PC. So if you move the code (without re-linking), the destination changes. This is useful if you write relocatable functions that e.g. use constants stored right after or before the function. Like the format parameter for printf.

    Example:

    MOV #12, &EDE
    actually generates
    MOV #12, EDE(R2)
    where (R2) acts as constant generator for the value 0.

    MOV #12, EDE
    generates
    MOVE #2, (EDE-$)(PC)

    The instructions are still the same (even though on some MSPs of 2x/4x and all of 5x/6x family, some new have been added for 20 bit addressing)

    But the hardware modules may have changed. Not only the address of the hardware module registers but also some of the functionality. The clock module is completely different. The UART may have changed (I don't have a 3x family users guide or datasheets for comparison).

    Also, check the device errata sheet. Things that worked on the 325 may have a bug on the 417. Or workarounds required for the 325 are no longer necessary.

    Also, the header files may have undergone a restructuring since, so you can't just import the headers for the 417 and have everything work. Maybe you'll need to do some non-obvious changes to make the linker happy.

  • Thank you for your answer.

    I understood that instructions with and without '&' are slightly differents. Tell me if I'm right : 

    -with the operand '&' it's the adress of the second term whereas

    -without the operand '&' it's the value's adress of the second term

    I'm right?

    So I don't understand why when I try to compile the old code I have errors that tell me 'illegal un-relocatable operand' and if I put an '&' on the second term the error disapears?

    For example I have the instruction MOV.B #08h,IE1 which have and error and IE1 is define above : IE1 .equ 0h

    I saw '.equ' and '.set' are equivalent, is it right?

    I also check the errata sheet but I did'nt see anything interesting.

    One more thing : is it necessary to specify precize memory adress like this line?

    .sect "MAIN", 0C000H

    I thank people who will answer me ;)

  • Pierre Augustin said:
    -with the operand '&' it's the adress of the second term whereas
    -without the operand '&' it's the value's adress of the second term

    Not exactly. In both cases, the value stored in teh instruction is an offset value.
    But with the '&' parameter, it is an offset relative to 0 (effectively an absolute memory position) while without it it is an offset relative to the current program counter (a relative memory position).
    Usually, the linker calculates the values at link time, so the result is the same. But if you would move the code after linking, the first case would stilla ccess the same absolute memory position (e.g. a register, a global variable), while in the second case, the accessed location would move with the code (e.g. a constant that is stored right before or after the function).

    So when linking the code to a fixed address, there is no difference at all, as both addressing mdoes take the same number of clock cycles.
    If you write relocatable code that e.g. is meant for dynamically loading at runtime, then you'd use absolute addressing for destinations that are independent of the code position while using relative addressing for data that moves with the code.

  • Thank you Jens-Michael for your answer.

    I think I'm close to understand, I read some tutorials in french for adressing modes because even in native language it's quite hard to understand so you can imagine in english it's not so easy ^^

    I just need some precisions about what you mean in "move the code". Is it for example to change adresses of some variables or constants?

    In the code I have to port, some instructions were without the '&' and I had "illegal un-relocatable operand" errors (for example with the instruction MOV.B #08h,IE1). Instead, if I put MOV.B #08h,&IE1 I don't have the error anymore but nore sure the values are well stored in IE1. The same for MOV.B   AIN,R15 or MOV.B   CSBIP,conf_max

    And to complete there were instructions to put some instructions at some particular adresses but I'm not sure that it's necessary :

    .sect "MAIN", 0C000H

    SP_orig .set 003DCh   ; stackpointer
    RAM_orig .set 00240h
    USER_END .set 0FFFFh   ; RAM endadress

    Thank you.

  • Pierre Augustin said:
    I had "illegal un-relocatable operand" errors (for example with the instruction MOV.B #08h,IE1)

    Yes,that's to be expected.

    IE1 is a fixed address. It cannot be expressed relative to the current PC, as the PC is unknown before linking, and IE1 is fixed.

    Pierre Augustin said:
    if I put MOV.B #08h,&IE1 I don't have the error anymore but nore sure the values are well stored in IE1.

    Yes, they are.
    The addressing and how it is done is covered in teh CPU part of the family users guide. However, the explanations are ratehr short.

    Pierre Augustin said:
    what you mean in "move the code"

    Example:

    .org 0x1000
    MOV &MyConst, R4
    NOP
    MyConst: .dw 0x0010

    This will result in

    0x1000  MOV 0x1006(R2), R4;
    0x1004 NOP
    0x1006  0x0010

    Note, that R2, when addressed in indexed mode, results in 0x00 (see 'constant generator'), so basically, the address (0x0000+0x1400) is read.

    Now

    .org 0x1000
    MOV MyConst, R4
    MyConst: .dw 0x0010

    will result in

    0x1000 MOV 0x0004(R0), R4
    0x1004 NOP
    0x1006 0x0010

    Since when reading the 0x0004 and adding it to the PC (R0), the PC is 0x1002, the 0x0004 is added to get the target address 0x1006 to read from.

    The main difference is, that when you move these 8 bytes as a block form the intended location of 0x1000, the second code will still read 0x0010 while the first version will read something form 0x1006, no matter where the code ends up.

    So with the relative addressing (no '&'), you can create code that can be e.g. loaded from external storage and placed at any free location and it will still be able to access any constants that are part of the same code block. Like format strings for printf. The absolute addressing ('&'), however, will always access a fixed location (like the hardware registers), no matter where you place the code.

    The typical applicaiton is to have the code linked to a fixed location anyway, so the absolute addressing is the preferred addressing method.

    Pierre Augustin said:
    .sect "MAIN", 0C000H

    This defines 0xc000 to be the beginning of the main memory section, likely because this is the beginning of the flash area in the address space.

    Pierre Augustin said:
    SP_orig .set 003DCh   ; stackpointer

    I think (I'm not sure, as I didn't know htis assembler) this is similar to a 'define' in C. It defines the label SP_orig to have the value 0x3DC.
    Most likely there is a "MOV #SP_orig, R1" (or SP) somewhere to se tthe stack pointer initially to 0x3DC. Usually, it is set to the location behind the end of ram (perhaps 0x0440, assuming 512 bytes of ram, see next paragraph), but apparently, some bytes 'above the stack' have been spared. Maybe as storage location for some data that is meant to be kept across a reset.

    Pierre Augustin said:
    RAM_orig .set 00240h

    Another label, likely defining the beginning of RAM

    Pierre Augustin said:
    USER_END .set 0FFFFh   ; RAM endadress

    This apparently denotes the end of usable memory. However, on MSPs, the area just below 0xFFFF contains the interrup tvectors, so it shouldn't be used for code or data storage.

    All MSPs have flash up to 0xFFFF, so you can take this for granted. However, the size of the interrupt vector table varies (32/64/128 bytes, depending on the MSP). And the beginning of flash area (in your case, apparently 16k form 0xC000 to 0xFFFF), as well as beginning and end of ram varies too.

    Usually, the linker knows the exact limits by the target devcice seleciton you make in the IDE (or in the makefile), and should provide these values, so normally, there shouldn't be any need to define them yourself, unless oyu do something that's 'special'.

  • Thank you a lot Jens-Michael, it seems you are the only one interested by the subject...

    Jens-Michael Gross said:
    I had "illegal un-relocatable operand" errors (for example with the instruction MOV.B #08h,IE1)

    Yes,that's to be expected.

    IE1 is a fixed address. It cannot be expressed relative to the current PC, as the PC is unknown before linking, and IE1 is fixed. [/quote]

    I understood why it was written many times something like "IE1" instead of "&IE1" : in fact every constant or variable was defined in the code by their adresses because they were not using an IDE.

    Jens-Michael Gross said:
    if I put MOV.B #08h,&IE1 I don't have the error anymore but nore sure the values are well stored in IE1.

    Yes, they are.
    The addressing and how it is done is covered in teh CPU part of the family users guide. However, the explanations are ratehr short.[/quote]

    Yes I checked it : the values are well-stored, I saw it in CCS. Just I don't know how to see user-declared variables values in CCS, I hope I will find it in the help.

    Jens-Michael Gross said:
    what you mean in "move the code"

    Example:

    .org 0x1000
    MOV &MyConst, R4
    NOP
    MyConst: .dw 0x0010

    This will result in

    0x1000  MOV 0x1006(R2), R4;
    0x1004 NOP
    0x1006  0x0010

    Note, that R2, when addressed in indexed mode, results in 0x00 (see 'constant generator'), so basically, the address (0x0000+0x1400) is read.

    Now

    .org 0x1000
    MOV MyConst, R4
    MyConst: .dw 0x0010

    will result in

    0x1000 MOV 0x0004(R0), R4
    0x1004 NOP
    0x1006 0x0010

    Since when reading the 0x0004 and adding it to the PC (R0), the PC is 0x1002, the 0x0004 is added to get the target address 0x1006 to read from.

    The main difference is, that when you move these 8 bytes as a block form the intended location of 0x1000, the second code will still read 0x0010 while the first version will read something form 0x1006, no matter where the code ends up.

    So with the relative addressing (no '&'), you can create code that can be e.g. loaded from external storage and placed at any free location and it will still be able to access any constants that are part of the same code block. Like format strings for printf. The absolute addressing ('&'), however, will always access a fixed location (like the hardware registers), no matter where you place the code.

    The typical applicaiton is to have the code linked to a fixed location anyway, so the absolute addressing is the preferred addressing method.[/quote]

    I didn't understand your exemple in details but on the whole I think.

    Jens-Michael Gross said:
    .sect "MAIN", 0C000H

    This defines 0xc000 to be the beginning of the main memory section, likely because this is the beginning of the flash area in the address space.

    Pierre Augustin said:
    SP_orig .set 003DCh   ; stackpointer

    I think (I'm not sure, as I didn't know htis assembler) this is similar to a 'define' in C. It defines the label SP_orig to have the value 0x3DC.
    Most likely there is a "MOV #SP_orig, R1" (or SP) somewhere to se tthe stack pointer initially to 0x3DC. Usually, it is set to the location behind the end of ram (perhaps 0x0440, assuming 512 bytes of ram, see next paragraph), but apparently, some bytes 'above the stack' have been spared. Maybe as storage location for some data that is meant to be kept across a reset.

    Pierre Augustin said:
    RAM_orig .set 00240h

    Another label, likely defining the beginning of RAM

    Pierre Augustin said:
    USER_END .set 0FFFFh   ; RAM endadress

    This apparently denotes the end of usable memory. However, on MSPs, the area just below 0xFFFF contains the interrup tvectors, so it shouldn't be used for code or data storage.

    All MSPs have flash up to 0xFFFF, so you can take this for granted. However, the size of the interrupt vector table varies (32/64/128 bytes, depending on the MSP). And the beginning of flash area (in your case, apparently 16k form 0xC000 to 0xFFFF), as well as beginning and end of ram varies too.

    Usually, the linker knows the exact limits by the target devcice seleciton you make in the IDE (or in the makefile), and should provide these values, so normally, there shouldn't be any need to define them yourself, unless oyu do something that's 'special'. [/quote]

    Yes, I would like to know if I have to defined the size of the stack pointer (SP) or the size of the RAM or something like that? The compiler and the linker know the adresses and the sizes so it's not necessary? For the moment we are using the 16k code-size-limited CCS whereas the MSP has 32kb so maybe in the future we will have to use mspgcc.

    Moreover, I have a question about the LCD driver feature : can you confirm that if we want to select S0 to S15 to work as LCD driver, S12, S13, S14 and S15 are not available for working as I/O? I saw that on some MSP : we can select LCD outputs 4 by 4 but it seems that the MSP430F417 we can't select less LCD outputs than S0 to S15...

  • Jens-Michael Gross said:

    I had "illegal un-relocatable operand" errors (for example with the instruction MOV.B #08h,IE1)

    Yes,that's to be expected.

    IE1 is a fixed address. It cannot be expressed relative to the current PC, as the PC is unknown before linking, and IE1 is fixed.

    [/quote]

    It depends on the assembler used and the header file. IAR Assembler for MSP430 can use the directive "SFRB IE1 = 0" that declares IE1 to be a Special Function Register Byte at the address 0. In this case, both "MOV.B  #08h,IE1" and "MOV.B  #08h,&IE1" can be used.

  • old_cow_yellow said:
    IAR Assembler for MSP430 can use the directive "SFRB IE1 = 0"

    this defines IE1 as a constant with value '0', so teh assembler just sees a numerical value '0', and
    MOV.B #08h,0 and MOV.B #08h,&0 can both be directly turned into an opcode by the assembler. Different opcodes but same result.
    However, if IE1 is an external symbol of unknown value, the assembler can't know how to generate the proper opcode so that the linker can put the IE1 value into it later.

  • Yes it's a constant, in fact in the old code, there was no header file with all registers defined so they had to declare all registers as constants like IE1  .equ  0h 

    But now I'm using the header file for the MSP430 so that's why I have to put the symbol "&".

    I had to change the layer board because it's not written at all in datasheets that when we choose S0-S15 to work as LCD functions, we can't use pins 12 to 15 for working as I/O functions

    I prefer Microchip's datasheets, especially for PIC, they are easier to understand...

    I have still some problem with PC (Program Counter), the program, when is running, is lost when it's calling functions, just after the problem the PC's adress is really strange, something like 0xFFFF or 0x0010.

    I'm checking clock parameters in order to see if something is wrong. Do you have any idea?

    thanks

  • Pierre Augustin said:
    I have still some problem with PC (Program Counter), the program, when is running, is lost when it's calling functions, just after the problem the PC's adress is really strange, something like 0xFFFF or 0x0010.

    I'm checking clock parameters in order to see if something is wrong. Do you have any idea?

    Sounds liek a stack problem.

    When calling a function with CALL, the CPU will execute sort of

    MOV PC, @SP
    ADD #-2,SP
    MOV #function, PC

    (of course in one instruction, this is pseudo-microcode)

    At end of the function, it does

    MOV @SP+,PC (actually RET is an alias for this! There is no RET instruction in the instruction set. THis could be found in the 1x users guide but the emulated instruciton info ha sbeen removed in the 5x users guide)

    Now SP (== R1) needs to contain an address of an unused RAM area, or else the return address is stored into the void and hten garbage is fetched from the void. This area is called 'stack'.

    Typically, at beginning of your program, youi'll need to set R1 to the address right after the end of RAM (which likely is oat different locatiosn on the two MSPs). This is called 'stack initialization' and is mandatory for any program that uses CALL/RET, PUSH/POP isntructions or interrupts.

  • Thank you one more time for your answers.

    Yes it sounds strange...

    So in fact you think it's a problem of RAM and SP register?

    We have to specify the beginning and the end of RAM? It doesn't allocate automatically?

    In examples I found, it's written   mov.w   #300h,SP   whereas when I create an new project in CCS it's written   mov.w   #__STACK_END,SP   and when I run the program, SP is 0x0600

    When the program begins, PC has the address 0x8000 and is incremented. Is it OK?

    The program was not well working, I tested to compile it in release mode (it was OK) and after that to come back in debug mode and now it seems to work better : functions look OK but the execution during the main program looks still lost.

    I hope that fixing this execution problem will fix everything ;-)

    Thanks in advance.

  • Pierre Augustin said:
    We have to specify the beginning and the end of RAM? It doesn't allocate automatically?

    No. For the C startup code, the device-specific linker script contains a hardcoded label that is used by the startup code (and resolved by the linekr when linking the startup code).
    If you write in assembly, you'll have to either hardocde the address in your code, or use the same symbols (which are compiler-specific).
    __STACK_END is the end of stack, usually the end of ram area, bur somethimes there might be data in ram after the stack. E.g on MSPGCC, if you put variables in uninitialized variable sspace (so they will survive a reset, yent not  apower-off, of course).

    Since each MSP has its ram at a different location, and usually (for backwards compatibility), teh ram of similar devices begins at the same location but ends later on those with mroe ram), the location of the stack pointer may vary. It does no harm if you use a binary on an MSP which has more ram. YOu jsut do not use this additional ram then. As long as the ram starts at the same place. In some cases, MSPs with more ram than their smaller brethren, have part of their ram mirrored to a different location where those did have their ram and which was not large enough to hold the new amount due to other things like BSL or INFO memory limiting it. The 16xx series is such a family.

    If ram goes from 0x100 to 0x600, it doesn't hurt to set SP to 0x300 (then from 0x300 to 0x600 the ram can be used for the heap or for direct data access, or just be ignored).

    Pierre Augustin said:
    When the program begins, PC has the address 0x8000 and is incremented. Is it OK?

    Apparently, 0xFFFE holds 0x8000 as "reset vector", or rather as program entry point vector address. This is where the MSP jumps to after performing the power-up initialization.

  • Jens-Michael Gross said:
    is there a way to declare the entry-point outside the project properties?

    I have problem with interrupts, I tried to fin examples with interrupts but  many exemples in slac017i are the same and no one on P1.

    The problem is with the basic timer interrupt, is it necessary to have it? In the code delays are not made with timers but with loops and execution is struck in the Basic Timer Interrupt whereas Port1 Interrupt that why I wanted to delete it but if I delete it my code is not working anymore (execution lost with PC when running).

    One other thing : on examples I saw in vector declaration there is a line with a strange number but I can't find where the number on P1 interrupt is written : 

    .sect ".int00" ; Basic Timer Vector
    .short Int_BT ; Basic Timerr


    thanks

  • Pierre Augustin said:
    , I tried to fin examples with interrupts but  many exemples in slac017i are the same and no one on P1.

    Indeed, most examples are using interrupts for demonstrating the use of a hardwar emodule. There are no exmaples I know of abotu interrupts as such.

    There's a chapter in the users guide about interrupts, but it assumes that the general use of interrutp sis already known.
    Well, back in C64 times, everyone who coded past Basic did know about interrupts. And the concept hasn't changed much since. Even in DOS times, people knew about them.
    But these days, with an OS between the hardware and the typical application coder, interrupts are no longer commonly known, and are often (ab)used like threads or even cmpletely misunderstood.

    Pierre Augustin said:
    The problem is with the basic timer interrupt, is it necessary to have it?

    Depends on your code and how you use it.

    Pierre Augustin said:
    In the code delays are not made with timers but with loops

    When usign a high-level language, loops are not recommended, because you don't know how the compiler will compile them, so the outcome is unpredictable. In assembly language, loops are under the coders control, so they are more or less safe. The 'less' refers to the fact that if someone changes the CPU speed, the delay time of the loop changes too - unexpectedly. The one who changes the CPU speed, e.g. because some unrelated code needs more power, would have to scan all code for delay loops and adjust them - difficult, if they are not properly documented.

    Pierre Augustin said:
    execution is struck in the Basic Timer Interrupt

    An ISR (interrupt service routine) should never be written in a way that it could lock somehow. Best is a straight linear code, no loops, of course no delay loops. fast in and fast out.

    However, one thing people often forget is to clear the IFG bit that 'caused' the interrupt.
    On each CPU clock cycle, the CPU performs the following:
    if(GIE && xxIFG && xxIE) ISR();

    GIE and xxIE are set by software, xxIFG by hardware event (but can be also set by software).
    If you leave the ISR and the IFG bit is still set, the ISR will be entered again instantly. This effectively stopms main from excuting any instruction, and also all interrupts with lower priority.

    Pierre Augustin said:
    if I delete it my code is not working anymore (execution lost with PC when running).

    Your code enables teh IE bit for the basic tiem rinterrupt. So when the basic timer overflows, the CPU will call the ISR. When there is no IST, wit will jump into the void (reading the default 0xffff as ISR address from the vector table). This will lead to erratic code execution and/or crash and reset.

    You should share your code to see where it hangs.

  • Jens-Michael Gross said:
    Indeed, most examples are using interrupts for demonstrating the use of a hardware module. There are no examples I know of about interrupts as such.

    It could be nice if TI could add it because it's quite usefull...

    Jens-Michael Gross said:
    IWhen usign a high-level language, loops are not recommended, because you don't know how the compiler will compile them, so the outcome is unpredictable. In assembly language, loops are under the coders control, so they are more or less safe. The 'less' refers to the fact that if someone changes the CPU speed, the delay time of the loop changes too - unexpectedly. The one who changes the CPU speed, e.g. because some unrelated code needs more power, would have to scan all code for delay loops and adjust them - difficult, if they are not properly documented.

    yes I know that's why I quite stressed with this code because it's not very well documented or comments don't match with the code or values.

    That why I have random timing but I will modify the code with timer interrupt only if I have enough time.

    By chance it's possible to have same clock speed with the new MSP!

    Jens-Michael Gross said:
    IAn ISR (interrupt service routine) should never be written in a way that it could lock somehow. Best is a straight linear code, no loops, of course no delay loops. fast in and fast out.

    However, one thing people often forget is to clear the IFG bit that 'caused' the interrupt.

    GIE and xxIE are set by software, xxIFG by hardware event (but can be also set by software).
    If you leave the ISR and the IFG bit is still set, the ISR will be entered again instantly. This effectively stopms main from excuting any instruction, and also all interrupts with lower priority.

    I agree with you, that's why I'm not totally happy to work on this code because I think a lot of things should be re-written.

    I add a line to clear the flag P1IFG because previously it was only disabling the interrupt (P1IE).

    Jens-Michael Gross said:
    Your code enables teh IE bit for the basic tiem rinterrupt. So when the basic timer overflows, the CPU will call the ISR. When there is no IST, wit will jump into the void (reading the default 0xffff as ISR address from the vector table). This will lead to erratic code execution and/or crash and reset.

    Yes that was that, it uses timer because after a certain time the system sleeps

    It could be nice that TI explains how use interrupt vectors, I couldn't guess the number of the interrupt vector of P1 and there are no examples about it. (ie add .sect ".int04" and .short Int_P1)

  • Pierre Augustin said:
    it's not very well documented or comments don't match with the code or values.

    So it's not worse than much of the code posted here in the forum in order to get help :)

    Yes, documenting and commenting code is usually orphaned - it doesn't add or improve functionality and takes quite some time to do.

    Pierre Augustin said:
    I couldn't guess the number of the interrupt vector of P1 and there are no examples about it. (ie add .sect ".int04" and .short Int_P1)

    While not in the older ones, the recent datasheets contain an interrupt vector table.
    However, the implementation of the interrupt vector segment is compiler/linker specific. And I agree that it is confusing at first in assembly. The 5x family users guide contains a chapter about interrupts, and it covers many of the details. But it doesn't cover the 'great whole' and the basic concept of how to implement ISRs.

  • Jens-Michael Gross said:
    While not in the older ones, the recent datasheets contain an interrupt vector table.

    However, the implementation of the interrupt vector segment is compiler/linker specific. And I agree that it is confusing at first in assembly. The 5x family users guide contains a chapter about interrupts, and it covers many of the details. But it doesn't cover the 'great whole' and the basic concept of how to implement ISRs.

    I didn't find interrupt vector in the datasheet... and I couldn't guess how to implement it. When we see examples it looks easy but in fact when we want to write our interruption it never work...

    Even if it's specific to the compiler it should be written no?

    In the old code the part of interrupts was :

    .sect "Int_Vect",USER_END-31

    .word Int_P_27 ; Port0, bit 2 to bit 7
    .word Int_BT ; Basic Timer
    .word RESET ; no source
    .word RESET ; no source
    .word RESET ; no source
    .word Int_ADC ; EOC from ADC
    .word RESET ; no source
    .word RESET ; no source
    .word RESET ; no source
    .word RESET ; no source
    .word Int_WDT_T ; Watchdog/Timer, Timer mode
    .word RESET ; no source
    .word Int_P0_1 ; Address of UART handler
    .word Int_P0_0 ; P0.0
    .word RESET ; NMI, Osc. fault
    .word RESET ; POR, ext. Reset, Watchdog

    .sect "IDENT",0E9DEH
    .word 0AA55H
    .end

  • Pierre Augustin said:
    I didn't find interrupt vector in the datasheet...

    In SLAS340J (MSP430x41x datasheet) page 11: Interrupt vector addresses

    Pierre Augustin said:
    Even if it's specific to the compiler it should be written no?

    Well, the syntax should be in the compiler documentation (fo compiled C code) or in the assembly and/or linker documentation for assembler code.

    Pierre Augustin said:
    In the old code the part of interrupts was :

    Well, this table definitely looks different from the one on the 417. Specifically, I see entries for ADC and UART where teh 417 does have neither ADC nor UART. (Well, could have been a software UART)

**Attention** This is a public forum