Shop OBEX P1 Docs P2 Docs Learn Events
Where did the Nibs and Bts go? — Parallax Forums

Where did the Nibs and Bts go?

T ChapT Chap Posts: 4,198
edited 2006-07-04 23:39 in Propeller 1
I read through all the available manual sections and have a few remaining issues before I can translate a Pbasic program to Spin:

1. To simulate a bit sized variable, as in: SomeBit VAR BIT what would be the Spin equilavent for BIT and NIB
2. Is "For Next" in Spineeze "Repeat"?
3. If a variable is in a Public method, can the value of that variable be accesed by another cog?



Thanks to all the contributers of examples, they are very helpful!

Comments

  • Mike GreenMike Green Posts: 23,101
    edited 2006-07-01 14:38
    1) There is no equivalent to BIT or NIB. You can do the work yourself using the bit manipulation instructions. Since the Propellor has a huge (relatively) amount of memory that is available for data storage (and program storage) and is much faster than the Stamps, the added complexity in the Spin interpreter to include BIT and NIB was not justified. Use bytes. If you need to pack bit or nibble information because you have very large tables, look at the sample/demo programs for examples of access to parts of words. For access to I/O pins, you can use what are called "range expressions" where you can access a group of pins as a single value like "outa[noparse][[/noparse]7..3]" references pins 7 through 3 of the output register.
  • Mike GreenMike Green Posts: 23,101
    edited 2006-07-01 14:48
    2) Yes
    3) Variables declared in a VAR or DAT section can be accessed by any method (PUB or PRI) in the same object no matter which COG is executing the method. In other words, the variables do not belong to a COG. You can also pass the address of a variable to another object and that object can access the variable regardless of the COG executing the object (Be careful when you do this! You can get really bizarre bugs when two COGs change the same variable in an unplanned way, but it's how COGs communicate).
    If you're writing assembly language routines, the variables in the DAT section are strictly local to the COG that's running the routines and are not accessible from any other COG. Assembly language routines have to communicate through a shared area in the HUB memory.
  • LoopyBytelooseLoopyByteloose Posts: 12,537
    edited 2006-07-01 15:32
    In other words,in a simple 32 bit processor, the Nibbles and Bits actually may slow down processing speed because of added clock cycles required to use them -- even if you think you are conserving your memory space.

    Is that correct?

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    "If you want more fiber, eat the package.· Not enough?· Eat the manual."········
    ···················· Tropical regards,····· G. Herzog [noparse][[/noparse]·黃鶴 ]·in Taiwan
  • Mike GreenMike Green Posts: 23,101
    edited 2006-07-01 16:08
    The nibbles and bits wouldn't slow down the Propellor as much as you think. In most simple processors, shifts are done one bit at a time. In complex processors like the Propellor, there's a special shifter (called a "barrel shifter") that can shift a 32 bit word any number of bit positions in one instruction cycle. To extract any bit field, you only have to do a shift and a bit-wise AND. That takes two instructions. To store in any bit field, you need three instructions (shift, AND, OR). In the case of Spin which is interpreted, the time it takes to get the instruction code, the data for the instruction, and decode it completely swamps the time needed to actually do most operations. I think that the variable types were limited to longs, words, and bytes because those are the natural storage units in the Propellor processor. The HUB read/write instructions work with those sizes directly. Anything smaller would require packing/unpacking routines, so NIBs and BITs were not included as primitive sizes. The range expressions in Spin do help for I/O access since single bits and groups of bits commonly occur in I/O. In assembly language, there are special instructions for storing into specific parts of instructions (bits 8..0 for example) since "self-modifying" code is a feature / requirement for most Propellor assembly language programming.
  • T ChapT Chap Posts: 4,198
    edited 2006-07-01 19:24
    Thanks Mike for the detailed explanation. Regarding the Variables across cogs, I think I was making the mistake of visualising variables across multiple Slots on the P40, which is not a good analogy for var sharing.

    I know there is an object that shows some great examples for BS2> Spin, but, I really wish this forum had a sticky called PBasic>SPIN Translation Tips that could help people translate their projects over more easily. Even with the excellent reply of yours, I am still clueless what is the method to write a variable for a bit and nib to replace code from the Stamp. Granted that with enough experimenting and reading it will come to light. I'll have the Pro stick next week and then will start section by section converting code over, and I am pretty excited about the move and subsequent future use for all new projects on the Propeller.

    If anyone agreed that a post with an accumulation of tips for this purpose would be welcome, here are a few things I'd like to grasp:


    GOSUB
    Return from GOSUB
    Labels compared to Pub Pri
    Variables for Nib/Bit
    Do while Loop

    Even after looking at a lot of examples and the manual segments last night(much too late at night), I don't have a grasp of how you have one program that has a section that can run on one cog only for it's own dedeicated role, whilst another code is running for another cog.

    In a practical sense, I am looking for an example that may already exist of how to have one cog managing all button inputs, LCD and LED feedback, with that cog telling another cog to move a stepper to the desired position reltive to a COUNT that is taking place on another cog thats full time job is to count up and down pulses from an encoder. In othe words, 3 cogs working together sharing variables.

    Thanks again for the great replys, this is going to be fun.
  • Mike GreenMike Green Posts: 23,101
    edited 2006-07-01 21:28
    1) There is no GOSUB. Like most high level languages, Spin allows you to declare functions using PUB and PRI. These functions can have parameters which are always LONG values and return a value which is also always LONG. There's always an implicit return value, but you can give it your own name and you can ignore it if you want. These functions (called methods here) have an implicit return statement at the end, but you can add additional RETURN statements.
    2) See #1. Look at the RETURN statement in the Propellor manual chapter 4. Also look at PRI and PUB in the same chapter.
    3) There are no labels in Spin for points in your program code. Similarly, there is no GOTO. If you're writing assembly language, there are conventional labels, but that's not Spin.
    4) Use BYTEs for variables. If you start getting tight on space, show us what you're doing and I'm sure we'll have suggestions. There's a huge lot of space in the Propellor for variables compared to the Stamps.
    5) Use REPEAT. It does the same things and more. Look in the Propellor manual chapter 4.
    There are no examples yet for what you want to do. The best thing would be to look at one of the text terminal demos and start playing with it. The keyboard driver uses an assembly cog for the actual keyboard I/O and Spin code for talking to the cog. The tv text driver similarly uses a cog for the actual video generation, but uses Spin code to set up the text buffer.
    As I mentioned in another thread, think of the active cogs as either stopped or executing the actual source code you've written. One cog can start another to execute the same source code starting at a specified subroutine. You can have several cogs executing the same subroutine or executing different ones. When one cog executes a function call, it simply continues at the new function (subroutine). The return addresses (from the function calls) and any local variables declared in the function declarations are allocated in the stack that must be unique to that cog. If you have two cogs executing the same code, there must be two different stack variables in HUB memory for them and these are supplied when you start the new cog (using cognew or coginit). Assembly language is a bit different and another discussion.
    If you wanted to handle each button with a different cog, you could do that by writing a universal button routine (method) that is given the pin number to use and the address of a variable in HUB memory to use for the state of the button. Your would do a cognew for each button and provide an array of longs for a stack, the pin number to use, and the address of a variable to hold the button state. You might run out of cogs quickly and it's actually better to devote one cog for all buttons and have that cog do all of them, but you could have one for each.
  • T ChapT Chap Posts: 4,198
    edited 2006-07-02 06:40
    Excellent info Mike.

    Isn't this not the same thing as LABELS for purposes of navigation to certain code to run in the order that you want?

    PUB Start
    BlinkingLED_A16 'This call is executed first.
    BlinkingLED_A17 'This call is executed next

    PRI BlinkingLED_A16 | Pin
    <do something here>
    PRI BlinkingLED_A17 | Pin
    <do something here>


    In the example above the PUB calls the first PRI, executes it, then RETURNS to execute the second PRI.
    In the follwoing example, although the notation says it runs in sync, does it not in fact run 1 clock pulse out of phase due to calling one PRI, then calling the second which can only happen at a subsequent clock right? Unless there is something unseen that would get both PRI's ready, and laung them simultaneously:

    PUB Start
    cognew(BlinkingLED_A16, @stack0) 'Starts a Cog, calls BlinkingLED_A16, the Cog uses @stack0
    cognew(BlinkingLED_A17, @stack1) 'Starts a Cog, calls BlinkingLED_A17, the Cog uses @stack1


    Although Mike said there are no LABELS as such, but for understand how to navigate, is not PUB SomePlace the same thing, and if so, are there methods to go to that PUB even though there are no GOTO?

    Thanks for any clarification. This is a lot harder than I anticipated!
  • Mike GreenMike Green Posts: 23,101
    edited 2006-07-02 15:54
    In your first example, everything is executed by a single COG so things happen in order. Specifically, BlinkingLED_A16 is done first, then BlinkingLED_A17. In the second example, it's difficult to say what the relative timing is between the two routines. There are actually 3 COGs involved, the main one (which will stop if this is the only code for it to execute ... There's an implicit stop when the initial method/function returns), the COG doing BlinkingLED_A16, and the COG doing BlinkingLED_A17. It's not clearly documented how to determine the time for cognew to start a new cog and just when the initiating cog will continue. It probably is deterministic like most of the Propellor stuff, but not obvious and probably not useful given that its an interpreter that's running and there's a lot of overhead that swamps this sort of "little stuff". If you were doing this in assembly language, there is documentation that would allow you to compute how long this would take. If you do want to synchronize two COGs, you can have them wait until the same time (waitcnt with the identical value). In assembly, you can compute the exact amount of time from that point to any specific action. With Spin, it's harder because of the interpreter overhead, but you can always resynchronize when necessary using waitcnt.

    PUB and PRI define functions / procedures / methods. There's a single entry point and there are no GOTOs. You can do the same thing with LABELs and GOSUBs in Stamp Basic, but there are some program structures that you can't readily translate to the GOTOless form (without rewriting them). Keep in mind that a call in Spin causes a return address to be put on the stack. It's not a GOTO.

    Still, you can think of PUB and PRI somewhat like labels and you can think of the use of the method name from the PRI/PUB declaration as a kind of GOSUB and the return statement is still a RETURN. You can pass parameters to a Spin function/method and pass a return value back and you can even pass parameters to a Spin function being launched with cognew or coginit ... Nice for specifying an I/O pin number or some kind of mode information.
  • T ChapT Chap Posts: 4,198
    edited 2006-07-02 19:20
    Thanks again Mike. Slowly it is making some sense, but the Spin is very deep for my level of experience. I am going through all the examples copying and pasteing to a notepad the relevant code that I think will need to apply to my Pbasic conversion. What I am still lacking is the understaning of how to get out of one block under certain conditions and go to another block as needed. I recall seeing an example where a line of code(a PRI USED EARLIER in the Object) was used that indicated to me that at that point, the PRI was to be reused.

    Here is an example of what I need to translate:

    Main:
    If But1 = 1 Then MTRStart
    If But2 = 1 Then SomethingElse
    If But3 = 1 Then SomethingElse2
    GOTO main

    MTRStart:
    <make the motor do something based on other info, position etc>
    goto main

    SomethingElse:
    <whatever needs to happen>
    goto main

    SomethingElse2:
    <whatever needs to happen>
    goto main


    In these cases, MTRStart and SomethingElse(2) never get ran unless the conditions are met. My understanding is that you can't just have these labels converted to PUB ot PRI, as they will run in the order they are seen, whereas in PBasic, they will only be seen when something sends the "tape head" there to play them.

    Is there a good example somewhere of navigating to code that is only used as needed?
  • Mike GreenMike Green Posts: 23,101
    edited 2006-07-02 20:28
    Part of this is that you don't need so many labels as in:

    - PUB Main
    - repeat
    - if ina[noparse][[/noparse]But1] == 1
    - ' make the motor do something
    - if ina[noparse][[/noparse]But2] == 1
    - ' something else
    - if ina[noparse][[/noparse]But3] == 1
    - ' something else 2

    See ... Thing are rearranged. You don't need so many explicit labels, but everything is still there without the GOTOs.
  • T ChapT Chap Posts: 4,198
    edited 2006-07-03 00:01
    In the old basic program, I needed to toggle a BIT in some cases to track the state of a variable. Now that I am told to use a byte instead, is this workable:

    CON
    Byte Pin
    Out = %1
    · In = %0
    Hi := 11111111
    Lo := 00000000
    High := 1
    Low := 0


    VAR
    Byte TrackSomething

    PUB main
    TrackSomething := 00000000
    PIN := 0
    DirA[noparse][[/noparse]PIN] := In
    Repeat
    If PIN := High
    !TrackSomething 'toggle this var
    <Display Tracksomething new state on LCD>
    Elseif PIN:= Low
    Next
    <some other If statement>


    Maybe therre is a better way but write this since only one bit needs changing in the TrackSomething, but if it works who cares, I just need to toggle some states that can be used elsewherein the program.

    I guess from your last reply that there is no way to jump to any place, although I thought I understoodfrom somewhere that you could run any PRI or PUB by calling the correct name of the PRi or PUB block.
  • Mike GreenMike Green Posts: 23,101
    edited 2006-07-03 02:57
    You could use 0 for LOW and 1 for HIGH (High and Low as you've defined them). Note I'm using underlines instead of leading spaces for formatting
    CON
    _pin = 5
    VAR
    _byte TrackSomething
    PUB Main
    _TrackSomething := Low
    _dira[noparse][[/noparse]pin] := In
    _repeat
    __TrackSomething := ina[noparse][[/noparse]pin] 'you can save the state of the pin
    __if TrackSomething == High ' or you can use the pin to control a decision
    ___' Now do something
  • Phil Pilgrim (PhiPi)Phil Pilgrim (PhiPi) Posts: 23,514
    edited 2006-07-03 04:04
    Mike,

    Even with Quick Reply, you can use the &#091code] and &#091/code] tags to enclose program lines and preserve your leading spaces intact.

    -Phil
  • T ChapT Chap Posts: 4,198
    edited 2006-07-03 04:14
    OK great that should get me me going! Thanks Mike
  • T ChapT Chap Posts: 4,198
    edited 2006-07-03 07:24
    If anyone would be kind enough to look at this code (from a text editor) and see if I am on track with the Pbasic>Spin Conversion. I don't need corrections, just let me know if anyting needs repair and I'll chase it down. I think once the initial setup is correct, the coding will be easy, as it is all If Then, GOTO code. Obviously there will have to be some rethinking on the navigation without goto's, but that will work itself out with trial and error when I get the stick.

    I could only get this to upload using a BS2 format, I dont have a pc with me to save it as .spin
  • Mike GreenMike Green Posts: 23,101
    edited 2006-07-03 13:50
    I've made some minor corrections and comments. Spin files are just text files. I used a free text editor for the Mac called TextWrangler to edit it. It knows about different line endings (CR,CRLF,LF) and will maintain whatever it finds so it works great with PC, Mac, Linux.
  • T ChapT Chap Posts: 4,198
    edited 2006-07-03 20:05
    Awesome, thanks so much for taking the time to edit that for me.

    I am looking for clarification on something, I'll quote from chapter 4-150 regarding Return:

    "Assume that DisplayDivByZeroError is a method defined elsewhere"

    PUB Divide(Dividend, Divisor)
    If Divisor == 0
    DisplayDivByZeroError
    Return 0
    Retrun Dividend / Divisor


    This indicates to me that although we don't have GOTO, we can essentially "run" the "PUB or PRI DisplayDivByZeroError" method remotely just by putting its name under the IF statement, assuming the IF were true as in the example.

    I have some code that needs to run under some circumstances, and would like to run it ONLY under those conditions, as the example shows. However, what makes " Pub or Pri DisplayDivByZeroError" NOT RUN? What I mean is, the "error" in the example DOESN'T need to run unless called by PUB Divide IF test, and since the example claims that DisplayDivByZeroError IS a PUB or PRI that exists somewhere else, what stops DisplayDivByZeroError from running where is is located? Certainly in a real case, the error would not be in a timeline that displays the error every cycle throughout the repeating of the code(object?).

    Again the navigation is very confusing in the example. What I think the manual lacks is a clear and consice explanation of how to structure a timeline, with departures to run code(other PUB or PRI) " as needed".

    I am stuck on the Pbasic structure of timelines:

    start here at main:
    do this if x
    do that if y
    go back to main and start over

    If the analogy is correct:


    PUB init
    repeat
    PUB main
    PUB x
    PUB y
    PUB DisplayDivByZeroError 'as in example

    What am I missing to get the timeline straight?

    Thanks
  • T ChapT Chap Posts: 4,198
    edited 2006-07-03 20:34
    Ok I extracted some parts from example 14 that is starting to make sense for timeline structure, these are acting just like GoSubs

    PUB Start··
    · Repeat
    ···· MeasureFrequency
    ···· DisplayFrequency


    PUB MeasureFrequency | Pin

    PUB DisplayFrequency

    If I am correct, the way to NOT see a PUB is to have it not "played" because the REPEAT above it stopped the playhead from ever getting there. PUB DisplayFrequency only plays when called from the infinite repeat loop in back up in PUB Start.

    IF the repeat were not there, the timeline of code execution would have been as follows:

    PUB Start

    PUB MeasureFrequency | Pin

    PUB DisplayFrequency

    END here as there is no way to return to the top. Right?
  • Mike GreenMike Green Posts: 23,101
    edited 2006-07-03 21:25
    Think of it this way .. The first PUB "method" in your program gets called automatically when the Propellor starts up. There is an automatic RETURN at the end of each PUB or PRI "method". Whatever you call your first PUB, that's where things start. Typically, the structure looks like:
    PUB Start
      Initialize_1
      Initialize_2
      ...
      Initialize_n
      repeat
        Do_Something_1
        Do_Something_2
        ...
        Do_Something_n
    
    


    The "Do_Something_x" can be complicated, perhaps an if statement or a "repeat i from 1 to 10" or whatever. You usually want any given "method" to not be longer than a page of text so you can "grasp" it more easily. As things make sense as logical units, you package those up in their own "method"s ... That's a subroutine. Unlike PBasic, execution doesn't "fall through" from one section of a program to another. The structure is strictly hierarchical. If you draw out the structure of a Spin program, you can draw it as a tree without any crossing of branches. That's not true in PBasic where you often have to draw arrows every which way.
  • T ChapT Chap Posts: 4,198
    edited 2006-07-03 22:26
    Ok that makes good sense. Actualy, once you get into Spin, it appears to be pretty simple for the elementary stuff, at least the same results require less code in Spin. I must admit I have no experience other than 4 weeks with Pbasic and the P40 stamp. The Pbasic was a lot easier to get up and running with, but Spin is making sense now. I hope the decision to tackle Spin over the SX as my next learning curve was a good one. The help is greatly appreciated!

    Here is the updated partial code. Notice I am attempting to call other methods from within a method. My understandng is that there is always an implied return address to get back from where you left from, and it resumes from there. In this case:

    PUB Check_A_Pos 'if OM or OS = 1, check A pos, toggle it, then start motor
    Repeat
    If (Lock_Latch_M = 1) AND (OS = 1)
    Exit
    If (LOCK_Latch_S = 1) AND( OM = 1)
    Exit
    If (OM = 0) AND (OS = 0)
    Exit
    If (OM = 1) AND (Lock_Latch_S = 0)
    Mtrstart
    If (OS = 1) AND (Lock_Latch_M = 0)
    Mtrstart ' goes and runs Mtrstart, then returns back to HERE. Maybe the RETURN below isn't req'd
    Return 'exit Check_A_Pos if no conditions met


    The above code is the only way I can think of to check for all the conditions, and then Mtrstart if the right conditions are met.
  • T ChapT Chap Posts: 4,198
    edited 2006-07-04 00:48
    The last one had mistakes, I think this one is good now.
  • Mike GreenMike Green Posts: 23,101
    edited 2006-07-04 13:32
    Here is a more or less corrected source. I've added comments about what I've done in most cases.
    You've used some IF, ELSEIF. That's often used when there are a series of exclusive choices.
    When these choices are in numeric order mostly (like 0,1,2 or 0,2,3), a CASE statement is clearer.
    I like your use of RETURN to handle lockout conditions for the motor operation.
    Check my translation of the logic. The logic operators (AND, OR, NOT) treat all non-zero values as
    TRUE and zero as FALSE. Although it's sometimes clearer to write something like "if ina[noparse][[/noparse]pin] == 1",
    it's also valid to just write "if ina[noparse][[/noparse]pin]" for the same thing. The first format is necessary if you're
    looking at more than one bit as in "if ina[noparse][[/noparse]15..14] == %10"
  • T ChapT Chap Posts: 4,198
    edited 2006-07-04 23:12
    Man what a big help that last was. I applied the majority of your edits to the completed full length version. I may find some typos later when I proof it more carefully, but I think it incorporates most of the nice edits you made.

    This version of the program is simply to replicte the existing Pbasic-BS2P40 version so the Propeller can be a drop in with a few board changes. The next version will incorporate a cognew for controlling the motor driver directly, including accleration values, deceleration, step outputs, direction outs, speed etc. Then, another cog is to read the encoder(translated up/down pulses) full time and store the counts in a global variable for use by the main program. The next version will also feature an LCD with additional buttons for programming setup, since the servo I am currently using has its own controller and PC based software setup for all positions, speeds, etc.

    This was a great exercise, there may be a few things that will surface when I get the chip plugged in, but I predict it is very close to functional. I may build an adapter board to drop in the propstick on the existing board for testing.

    I included the new program with all edits, it is likely hard to follow not knowing how the thing works. It is quite a bit more elegant than the Pbasic version though, which was all over the place. At least it worked.

    Thanks again, it sure is nice to have a fully coded project within 3 days on a brand new language.
  • T ChapT Chap Posts: 4,198
    edited 2006-07-04 23:39
Sign In or Register to comment.