first steps into assembler

Posted on
  • @Gordon, you opened Pandora's box, so I decided to "let's see what inside".
    My background is assembler long time ago, but not ARM.

    1. first step playing around with given example.
      It works, good news. So have a look at minor changes.
      Add 7 instead of 2 is simple by altering first command to movs r2, #7 .
      Double given parameter simply add this adds r0,r0,r0
      BTW, interesting to work with 3 parameters.
    2. switch to external assembler.
      The internal assembler, even if limited is a good starting point.
      By searching for a "full blown" assembler I found http://flatassembler.net/
      At this point I learned something about THUMBS and ARM commands.
      To get same results as in Gordons example this needs to be done.

      thumb;
          ldr    r2, [hugo]
          mov.w    r3, #57344
          str    r3, [r2, #0]
          str    r3, [r2, #4]
          bx lr
      hugo:   dw 0x40010810
      

    As you can see, first line switches to thumb mode.
    One of the main futures of an assembler is last line. You don't have to count the number of word ´s to get the adress of GPIO adress.
    Ok, there is another difference, use dw instead of .word

    1. after compilation we get a bin file.
      Right now I read this with Hex Editor and type it in manually. Don't forget to switch the bytes ;-)
    2. What about using 2 parameters for a function like this adder(2,3)

      var adder = E.asm("long(long,long)",
      "bx  lr");
      adder(2,3);
      

    And I get an error no caller for argument 438

    1. tried with all given datatypes, to see whats given by
      int, bool and Pin return an error.
    2. Where are the parameters, and how could I interpret those ?
      Big question, as far as I can see, r0 is used in the example for input and return value
      If somebody has more examples, please share it
    3. Whats now, hmmm
      During development of Display driver for ILI9341 Gordon mentioned something like "we could speed it up by using SPI.send with repeat option".
      Would it be possible to have something like a list of "interesting entrypoints". There is something like spi_sender_software in jswrap_spi_i2c.c
      This could be an additional file created during compilation of new version, and could be used similiar to boardinfo. A new processor for Web IDE could replace adresses and ....
      Hey Gordon, its you who opened Pandora's box ;-))

    Juergen

  • Awesome, glad you're diving in :)

    Where are the parameters, and how could I interpret those ?

    They're generated by scripts/build_jswrapper.py in Espruino itself - you can use anything that some other function in Espruino uses. At the moment this is:

    ...
    edit: no longer relevant - 1v61 handles all different types
    ...
    

    I'll be trying to change this though, so that Espruino can take a much larger variety of types.

    Big question, as far as I can see, r0 is used in the example for input and return value

    It's the way values are passed to functions on ARM: http://en.wikipedia.org/wiki/Calling_con­vention#ARM

    Would it be possible to have something like a list of "interesting entrypoints". There is something like spi_sender_software in jswrap_spi_i2c.c This could be an additional file created during compilation of new version, and could be used similiar to boardinfo.

    If you compile yourself, there's espruino_1v##_espruino_1r3.lst which actually has this in - I guess this could be included in the binaries folder. IMO it might be more sensible if Espruino itself could return a list of these functions and then the assembler could load them in.

    This would be way more useful if/when someone gets a C compiler working though. For now it would probably be easier (maybe even faster) to do SPI in software.

  • Based on last daily, I did some more testing.
    Your link to Calling_convention gave a first help.
    In my best understanding reg0 to reg3 are used for getting data.
    By testing this, I could get 4 values back, but where is the 5th ?

    var adder = E.asm("int(int,int,int,int,int)",
                      "mov r0,rn", where n is 1 to 3
                      "bx  lr");
    

    Another problem is JsVar, how could this be interpreted ?
    I would like to play around with Uint-Arrays.
    This one works, but now I want to change a value in the array.
    My understanding of C is too poor to get this out of the sources.

    var x,i;
    x = Uint8Array(10);
    for(i = 0; i < 10; i++) x[i] = i;
    var adder = E.asm("JsVar(JsVar)",
                      "bx  lr");
    adder(x)
    
  • After the first 4 values, they get put on the stack and you have to copy them off. But first you have to save the value of any registers above r3 that you might overwrite!

    It's all getting quite complicated by that point.

    Accessing the JsVars from assembler is going to be really difficult too I'm afraid - I wouldn't advise it. For now it's probably best just to call with:

    for(var i in a) adder(a[i]);
    

    Annoyingly map and forEach don't work on Typed Arrays. I'm quite tempted to add support though :/

    EDIT: It's been pointed out to me that you can re-purpose the existing map function:

    [].forEach.call(a, adder);
    

    This won't work in 1v60, but I've just fixed map/forEach so that they will work on the latest build (or when 1v61 comes out). Note that map will create an old-style array from the result, so you'll run out of memory pretty quickly. forEach is fine though.

  • Just to add that in 1v61 you'll be able to use Array.reduce too - which will be extremely useful if you want to do some analysis on an ArrayBuffer with inline assembler.

    For instance maybe you want to sum up all the values in an ArrayBuffer...

    // effectively function (a,b) { return a+b; }
    var adder = E.asm("int(int,int)", 
    "adds    r0, r0, r1",
    "bx  lr");
    
    
    // fill up array buffer
    var a = new Int16Array(100);
    for (var i in a) a[i]=i;
    
    // Call our assembler on every item
    var sum = [].reduce.call(a, adder);
    
    console.log(sum); // 4950 
    
  • During my learningphase I created an ASM call which only returns one out of a row of parameters. Parameter are stored in reg and in stack.
    This is compiled with an external compiler (FASM), it will not run directly with the one in WebIDE.
    I still would like to work with Uint-arrays directly. I'm pretty sure this kind of data is stored in memory directly, at least this is what I would like to have :-) get.
    Would it be an option to have a function, something like adressOfData() which returns startadress in memory for Uint-Arrays ?

    thumb;
            cmp r0,#0       ;1st parameter is pointer to following args
            beq return      ;pointer to first arg which is in r0
            cmp r0,#1
            bgt isgt1
            mov r0,r1       ;2nd arg is in r1
            b return
    isgt1:  cmp r0,#2
            bgt isgt2
            mov r0,r2       ;3rd in r2
            b return
    isgt2:  cmp r0,#3
            bgt isgt3
            mov r0,r3       ;4th in r3
            b return
    isgt3:  sub r0,#4       ;since 5th arg is somewhere else
            lsl r0,r0,#2    ;data uses 4 bytes, so left shift by 2 is like multiplication by 4
            ldr r0,[sp,r0]  ;load from stack
    return: bx lr           ;return value is in r0 
    
  • They're not stored as flat arrays - more of a linked list - See http://www.espruino.com/Performance and http://www.espruino.com/Internals - and the actual JsVar struct declaration

    That's why it's important to iterate over the data rather than accessing individual elements...

  • My knowledge around c++ is poor. Compiling Espruino by myself is out of my knowledge too. Working with assembler is something I will have a fair chance.
    Therefore I would like to ask if there is a "not complicated way" to do some of this in assembler

    • set values in an array (typed or not, it doesn't care)
    • return an array
    • return a string
    • support call of internal function (old basic version had something like adressOf(spi.send) for that)
  • Without recompiling I don't think you can do what you want I'm afraid, as you need the 'lst' file to be able to access the functions you need.

    By the way, If you could find an ARM assembler (or even a machine-readable description of ARM Thumb opcodes) that could be embedded in the Web IDE it'd be extremely useful.

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

first steps into assembler

Posted by Avatar for JumJum @JumJum

Actions