Calling PASM functions with spin?
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 :
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
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 .....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).
How would that be written in PASM?
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 modeNow 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 value1 = 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
declare a constant near the end of the pasm code and name it something that makes sense
and then in the code
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 496It 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 retAnd 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 retI 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.
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 retBut 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
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_11111111And 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!