Shop OBEX P1 Docs P2 Docs Learn Events
Calling PASM functions with spin? — Parallax Forums

Calling PASM functions with spin?

eagletalontimeagletalontim Posts: 1,399
edited 2012-03-03 22:37 in Propeller 1
I am still tinkering with my touchscreen code and was wondering if it was possible to call a PASM function from spin? Since PASM runs faster and I would like to have the screen run faster, I hope to convert as much as I can to PASM. The only way I can think of doing this is to have a cog running the PASM that listens for a change in a global variable. Once spin updates that global variable, the PASM will match the variable with one of the functions to run. I am in the beginning stages of learning PASM so hopefully I can get this working. I have been reading others code to get an idea of what I am getting into....yep..this will take awhile!

I am trying to make my first PASM function to clear the screen and set it to a specific color. I am not sure of the best method to do this, but this is what I have written :
    'init stuff before
    PUB Main
    ILI9325.Draw(0,0,240,320)
    repeat 76800 ' draws a box 10x10 pixels in white.
       ILI9325.Pixel($00,$FF)  'Clears the screen and makes it blue

' and the some of the screen functions
PUB Pixel(VH,VL)               ' send out a pixel, high byte then low byte                                
    Lcd_Write_Data(VH,VL) 
                                         
PRI LCD_Writ_Bus(VH,VL) 
    OUTA[7..0]  := VL
    OUTA[15..8] := VH
    WriteLow                                            ' write pin low
    WriteHigh                                           ' toggle write pin

PRI Lcd_Write_Com(VH,VL)
    RSLow
    LCD_Writ_Bus(VH,VL)

PRI Lcd_Write_Data(VH,VL)
    RSHigh
    LCD_Writ_Bus(VH,VL)

PRI RSLow                                    ' RS pin
 outa[RS] := 0                  

PRI RSHigh
 outa[RS] := 1                 '

PRI WriteLow                                    
    outa[LCD_WR] := 0                             ' 

PRI WriteHigh
    outa[LCD_WR] := 1

Comments

  • JonnyMacJonnyMac Posts: 9,197
    edited 2012-02-29 18:30
    Have a look at the attached PCD8544 object (LCD driver -- type used in Nokia phones) as you may find its architecture similar to what you're after (calling one of many functions in the PASM code).
  • eagletalontimeagletalontim Posts: 1,399
    edited 2012-02-29 18:59
    That is quite a bit of code! I am hoping to start out with the basics, but am having problems from the get go with PASM :( How do I pass information to PASM from spin?

    I see something similar to this in every PASM example and have read up on each of the PASM commands, but still don't understand what exactly it does.
    dat
                            org     0
    
    pcd8544                 mov     outa, #0                        ' clear pins
                            mov     t1, par                         ' start of structure  ----- Is this what gets the passed variable?  If so, what variable is this reading?
    
                            add     t1, #4
                            rdlong  t2, t1
    .....
    
  • JonnyMacJonnyMac Posts: 9,197
    edited 2012-02-29 19:42
    That is quite a bit of code!

    It's like a box of macaroni: a lot of the same little things.

    These articles may be helpful:
    -- http://www.parallax.com/Portals/0/Downloads/docs/cols/nv/prop/col/nvp0.pdf
    -- http://www.parallax.com/Portals/0/Downloads/docs/cols/nv/prop/col/nvp10.pdf

    Note, though, that I am now in the camp that says "Do not modify PASM code from Spin -- do everything through PAR." My May 2012 column will be a reboot of Spin-to-PASM basics that follow this guideline so that the PASM code may be used with Spin, as well as Prop-GCC (and probably other languages, too).
  • eagletalontimeagletalontim Posts: 1,399
    edited 2012-02-29 19:51
    I think I may have something going, but I am stuck at one line of code that is Spin which needs to be converted to PASM :
    OUTA[7..0]  := VL  ' VL is something like $FF or $00 which is passed from another area.
    

    How would that be written in PASM?
  • JonnyMacJonnyMac Posts: 9,197
    edited 2012-02-29 20:06
    First things first: you would pass the LSB of that pin group to the PASM code; remember you want to pass this pin so that you can change your mind about connections later. Here's what the code would look like assuming that you had the address of the hub variable holding the LSB pin for the group:
    rdlong  lsbpin, t1                      ' read (and save) LSB pin of group
                            mov     pinsmask, #%11111111            ' group has eight pins
                            shl     pinsmask, lsbpin                ' align LSBs
                            mov     dira, pinsmask                  ' set pins to output mode
    


    Now that those pins are outputs and you have a value to write to them, here's how you can do it:
    and     outbyte, #$FF                   ' trim to 8 bits
                            shl     outbyte, lsbpin                 ' align with group output pins
                            andn    outa, pinsmask                  ' clear old outputs
                            or      outa, outbyte                   ' update pins with new value
    
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-03-02 20:01
    This is Cluso's ram driver, stripped down and bare bones ready for code. You can pass it 4 variables
    1 = command to run in pasm
    2 = variable
    3 = variable
    4 = number of bytes to move.

    Generally you would use this to move data from one place to another.

    I tend to start by getting things working in spin first. Then take the block of spin and copy it into the pasm part and comment it all out. Then work on the lines one at a time and put a star or something in the line when that line has been translated to pasm.

    Learning pasm can be tricky. One of the hardest concepts I found was the idea that this a 32 bit processor but it isn't really. In some ways it is more a 9 bit processor, so any constant that happens to be more than 511 (2^9-1) needs to be declared in a constant section at the end of the pasm code.

    As an example (and yes there are other ways to do this with bitshifting), let's say you want to convert this line of code to pasm
    OUTA |= %00000000_10000000_00000000_00000000 ' set P23 high    
    

    declare a constant near the end of the pasm code and name it something that makes sense
    Pin23high               long    %00000000_10000000_00000000_00000000     
    

    and then in the code
                            or      outa,pin23high           ' set P23 high   
    

    if the pin you wanted to change happened to be P0 to P8 you could have used a constant. Oh yes, constants start with #. That gets me all the time too. So you could use #%00010000 to change P4.

    generally pasm follows the machine code convention : instruction destination, source

    I say generally because the other thing that gets very confusing is rdlong and wrlong.

    I'll post other hints as I come across them.

    PUB ILI_pasm_start : err_
    ' Initialise the cog driver. No actual changes to ram as the read/write routines handle this
      command := "I"
      cog := 1 + cognew(@tbp2_start, @command)
      if cog == 0
        err_ := $FF                 ' error = no cog
      else
        repeat while command        ' driver cog sets =0 when done
        err_ := errx                ' driver cog sets =0 if no error, else xx = error code
    
    PUB ILI_pams_stop
       if cog
          cogstop(cog~ - 1)      
    
    PUB ILI_pasm_command(command_, hub_address, ram_address, block_length) : err_
    ' Do the command: A B C D etc. Note I is reserved for initialise
      hubaddrs := hub_address       ' hub address start
      ramaddrs := ram_address       ' ram address start
      blocklen := block_length      ' block length
      command  := command_          ' must be last !!
    ' Wait for command to complete and get status
      repeat while command          ' driver cog sets =0 when done
      err_ := errx                  ' driver cog sets =0 if no error, else xx = error code
    
    VAR
    
    ' communication params(5) between cog driver code - only "command" and "errx" are modified by the driver
       long  command, hubaddrs, ramaddrs, blocklen, errx, cog ' rendezvous between spin and assembly (can be used cog to cog)
    '        command  = A,B,C,D etc and  = 0 when operation completed by cog
    '        hubaddrs     = memory or variable 1
    '        ramaddrs     = memory or variable 2
    '        blocklen = number of bytes to transfer
    '        errx     = returns =0 (false=good), else <>0 (true & error code)
    '        cog      = cog no of driver (set by spin start routine)
    
    DAT
    '' +--------------------------------------------------------------------------+
    '' | ILI pasm Touchscreen 2 driver           |
    '' +--------------------------------------------------------------------------+
                            org     0
    tbp2_start    ' setup the pointers to the hub command interface (saves execution time later)
                                          '  +-- These instructions are overwritten as variables after start
    comptr                  mov     comptr, par     ' -|  hub pointer to command                
    hubptr                  mov     hubptr, par     '  |  hub pointer to hub address            
    ramptr                  add     hubptr, #4      '  |  hub pointer to ram address            
    lenptr                  mov     ramptr, par     '  |  hub pointer to length                 
    errptr                  add     ramptr, #8      '  |  hub pointer to error status           
    cmd                     mov     lenptr, par     '  |  command  I/R/W/G/P/Q                  
    hubaddr                 add     lenptr, #12     '  |  hub address                           
    ramaddr                 mov     errptr, par     '  |  ram address                           
    len                     add     errptr, #16     '  |  length                                
    err                     nop                     ' -+  error status returned (=0=false=good) 
    
    ' Initialise
    init                    mov     err, #0                 ' reset err=false=good
                                                            ' add any other initialise code here eg tristate pins
    done                    wrlong  err, errptr             ' status  =0=false=good, else error x
                            wrlong  zero, comptr            ' command =0 (done)
    ' wait for a command 
    pause
                            rdlong  cmd, comptr     wz      ' command ?
                  if_z      jmp     #pause                  ' not yet
    ' decode command
                            cmp     cmd, #"A"       wz      ' command A
                  if_z      jmp     #commandA
                            cmp     cmd, #"B"       wz      ' command B
                  if_z      jmp     #commandB
                            cmp     cmd, #"I"       wz      ' init - search fastest if this is last
                  if_z      jmp     #init     
                            mov     err, cmd                ' error = cmd (unknown command)
                            jmp     #done
    
    ' -------------- Commands ----------------
    
    commandA                ' add pasm here
                            jmp     #init                   ' reinitialise 
    
    commandB                ' add pasm here
                            jmp     #init                   ' reinitialise  
    
    ' -------------- Constants ---------------
    
    Zero                    long    %00000000_00000000_00000000_00000000 ' used in several places
    
    ' -------------- Variables ---------------   
    
                            fit     496   
    
  • JonnyMacJonnyMac Posts: 9,197
    edited 2012-03-02 22:06
    if the pin you wanted to change happened to be P0 to P8 you could have used a constant. Oh yes, constants start with #. That gets me all the time too. So you could use #%00010000 to change P4.

    It is important to remember the # symbol for constants, and you _must_ define a constant for values > 511 (nine-bit limit of source and destination fields).

    Also remember that you can create generic IO subroutines in PASM as easily as they're created in Spin. In these examples, the variable called "pin" holds the pin # to affect before making the call. In the case of the input routine the c flag returns the result.
    high                    mov     t1, #1                          ' make pin mask
                            shl     t1, pin
                            or      outa, t1                        ' pin high
                            or      dira, t1                        ' output mode
    high_ret                ret
    
    
    low                     mov     t1, #1
                            shl     t1, pin
                            andn    outa, t1                        ' pin low
                            or      dira, t1
    low_ret                 ret
    
    
    toggle                  mov     t1, #1
                            shl     t1, pin
                            xor     outa, t1                        ' toggle pin
                            or      dira, t1
    toggle_ret              ret
    
    
    input                   mov     t1, #1
                            shl     t1, pin
                            andn    dira, t1                        ' input mode
                            test    t1, ina                 wc      ' return pin state in c
    input_ret               ret
    


    And if you wanted to move the input value to a variable, it's an easy update:
    input                   mov     t1, #1
                            shl     t1, pin
                            andn    dira, t1                        ' input mode
                            test    t1, ina                 wc      ' capture pin state in c
            if_c            mov     pinstate, #1                    ' move c to pinstate
            if_nc           mov     pinstate, #0
    input_ret               ret
    


    I think the key to learning PASM is studying code by others and constantly reviewing the manual. Some things will click very quickly, some things will not. There are some really clever coders here (I do not consider myself among them) and I am constantly gleaning new tricks by studying their code.
  • Dr_AculaDr_Acula Posts: 5,484
    edited 2012-03-03 22:37
    Good advice there from Jon.

    I forgot to add this little bit of code to retrieve the variables you passed to the pasm routine.
    get_values              rdlong  hubaddr, hubptr         ' get hub address
                            rdlong  ramaddr, ramptr         ' get ram address
                            rdlong  len, lenptr             ' get length
                            mov     err, #5                 ' err=5
    get_values_ret            ret
    

    But there are some times where I doubt my ability to write anything that works!.

    Pin won't change. Ok, stick in a call to an endless loop to stop the code at a known place
    stop                   jmp #stop 
    

    Try various variables. Pin still high. Try this code
                            mov     dira,ILIDirPins
                            mov     outa,zero
                            jmp     #stop
    
    Zero                    long    %00000000_00000000_00000000_00000000 ' used in several places
    ILIDirPins              long    %00000000_11111111_11111111_11111111
    

    And pins P0 to P15 are low and P16 to P23 are high.

    So maybe I'm not the best person to ask about how to program in pasm!
Sign In or Register to comment.