Three different LED blinking programs in FlexBASIC

FlexBASIC (the dialect of BASIC supported by fastspin) is a pretty broad and forgiving language, and supports lots of different styles of programming. To illustrate that I thought I'd post 3 very different takes on the traditional LED blinking program.

First, a version in "oldschool" BASIC with line numbers and such:
10 let LED=16
20 direction(LED) = output
30 output(LED) = output(LED) xor 1
40 pausems 500
50 goto 30
This will look pretty familiar to anyone who programmed in BASIC on 8 bit microcomputers. It's not really the way I'd choose to do it today, but flexBASIC supports this kind of old style BASIC programming so it is possible.

A more "modern" take in BASIC would use structured programming and look like:
' declare which pin we will use
const LED=16
' set it as an output
direction(LED) = output

' declare a subroutine to toggle a pin
sub toggle(pin)
  output(pin) = output(pin) xor 1
end sub

' loop forever
do
  toggle(LED)
  pausems(500)
loop
I've commented that better and broken the toggle operation out into a subroutine, but it's essentially the same program as the old style one (the compiler will generate pretty much the same code, too).

Finally a functional programming take on it:
const LED = 16

sub forever( f as sub() )
  f()
  forever(f)
end sub

direction(LED) = output

forever( [: output(LED) = output(LED) xor 1 : pausems 500 :] )

Note that the recursive definition of "forever" works fine; fastspin does tail call elimination so it gets turned into a loop internally. The "[: ... :]" notation is a simple way to define anonymous functions and subroutines.

Comments

  • Very nice!
  • jmgjmg Posts: 14,224
    ersmith wrote: »
    FlexBASIC (the dialect of BASIC supported by fastspin) is a pretty broad and forgiving language, and supports lots of different styles of programming.
    Good name, but I see some banking product called FlexBASIC - can these be named like FlexBASICpx for where P1 and P2 work, and FlexBASICp2 for where it is a p2 only version ?
    ersmith wrote: »
    To illustrate that I thought I'd post 3 very different takes on the traditional LED blinking program.

    First, a version in "oldschool" BASIC with line numbers and such:
    This will look pretty familiar to anyone who programmed in BASIC on 8 bit microcomputers. It's not really the way I'd choose to do it today, but flexBASIC supports this kind of old style BASIC programming so it is possible.
    Hehe, yes, that would be heavily deprecated today, but impressive to see it can swallow it, without barfing :)

    ersmith wrote: »
    A more "modern" take in BASIC would use structured programming and look like:
    ' declare a subroutine to toggle a pin
    sub toggle(pin)
      output(pin) = output(pin) xor 1
    end sub
    
    Easy to read, but raises questions about what output() actually reads back, eg if the pin is open collector ? is it the pin itself, or the pin-latch ?

    I presume this also works, and this reads the pin ?
    sub toggle(pin)
      output(pin) = input(pin) xor 1
    end sub
    


    If you take the pauses out, how fast can each of these toggle a pin ? Is there a generated source/pasm listing you can include in #1 ?
  • jmg wrote: »
    Good name, but I see some banking product called FlexBASIC
    That looks like a banking account name. I think the programming language is sufficiently different that there shouldn't be confusion :).
    Easy to read, but raises questions about what output() actually reads back, eg if the pin is open collector ? is it the pin itself, or the pin-latch ?

    "output(x)" is bit "x" of OUTA (and/or OUTB if x >= 32 and on P2). "input(x)" is, similarly, a bit of INA (or INB).
    If you take the pauses out, how fast can each of these toggle a pin ? Is there a generated source/pasm listing you can include in #1 ?

    You can always enable listing with fastspin's -l option. The second example (with the "const" declaration) compiles to;
    001a4 061             | _program
    001a4 061 5C EC BF 68 | 	or	dira, imm_65536_
    001a8 062 43 AC FC 5C | 	call	#LMM_FCACHE_LOAD
    001ac 063 08 00 00 00 | 	long	(@@@LR__0002-@@@LR__0001)
    001b0 064             | ' 
    001b0 064             | ' ' loop forever
    001b0 064             | ' do
    001b0 064 
    001b0 064             | LR__0001
    001b0 064 5C E8 BF 6C | 	xor	outa, imm_65536_
    001b4 065 66 00 7C 5C | 	jmp	#LMM_FCACHE_START + (LR__0001 - LR__0001)
    001b8 066 
    001b8 066             | LR__0002
    001b8 066             | _program_ret
    001b8 066 3B 84 FC 5C | 	call	#LMM_RET
    001bc 067 
    

    The first, old-style program is a bit slower since it stores the pin number in a variable:
    001a4 061             | _program
    001a4 061 10 C0 FC A0 | 	mov	_var01, #16
    001a8 062 5D C0 3C 08 | 	wrlong	_var01, objptr
    001ac 063 5C EC BF 68 | 	or	dira, imm_65536_
    001b0 064 43 AC FC 5C | 	call	#LMM_FCACHE_LOAD
    001b4 065 14 00 00 00 | 	long	(@@@LR__0002-@@@LR__0001)
    001b8 066 
    001b8 066             | LR__0001
    001b8 066 01 C2 FC A0 | 	mov	_var02, #1
    001bc 067 5D C4 BC 08 | 	rdlong	_var03, objptr
    001c0 068 62 C2 BC 2C | 	shl	_var02, _var03
    001c4 069 61 E8 BF 6C | 	xor	outa, _var02
    001c8 06a 69 00 7C 5C | 	jmp	#LMM_FCACHE_START + (LR__0001 - LR__0001)
    001cc 06b 
    001cc 06b             | LR__0002
    001cc 06b 
    001cc 06b             | _program_ret
    001cc 06b 3B 84 FC 5C | 	call	#LMM_RET
    

    The last example is considerably slower since it's got a function indirection in it.
  • First example is very clear
    I don't like output as both keyword and function..
  • Rayman wrote: »
    First example is very clear
    I don't like output as both keyword and function..

    "output" is always a keyword. It does have different meanings depending on context though. "output" on the right hand side of an assignment to the direction() pseudo-array is basically equivalent to "1", whereas "input" in that context is equivalent to "0". So you can use those values (or constants like that) instead if you prefer.

    "output(n)" and "input(n)" act like special bit arrays, basically OUTA and INA, but they can go up to 64 (so they can work on P2s and on P1v where the OUTB and INB registers are hooked up).
  • What?

    All this Spin2 stuff like output and multiple return values does work on the P1 too?

    We should refer to you as "The Great ersmith", I stole that from @Heater. he was titling @Cluso99 to be called "The Great clusso99" for his decoding trick with ZiCog.

    Question to @ersmith.

    I can not figure out how to fill the left space between org 0 and org $200 with 0 in the created binary so that pos $200 in the binary is the code of $200.
    If you look at a main-file listing it seems ok.

    But when some main containing another object, then it is somehow 52 bytes displaced.

    So without using a orgh, what I like to avoid since it is supposed to be a driver object

    How in PASM would I write

    ORG 0

    some code


    ORG $200

    some LUT code or tables

    and have in the created binary ORG $200 - hm - $200 away from ORG 0 and the space in between filled with 0.

    Currently it compiles for $200 but sticks right after the COG code in the binary, not at $200

    Can I use ORG $200 - $ or ORGF $200 - $ or something like that?

    Mike

    Help, please
  • msrobots wrote: »
    All this Spin2 stuff like output and multiple return values does work on the P1 too?
    Pretty much everything except PASM is common between P1 and P2. There are a few builtin functions for P2 instructions that aren't emulated on P1, and vice-versa, but those are rare.
    I can not figure out how to fill the left space between org 0 and org $200 with 0 in the created binary so that pos $200 in the binary is the code of $200.

    You can use ORGF $200. ORGF is like ORGH in that it fills memory with 0 until it reaches the address specified, but it's for COG addresses instead of HUB addresses.

    However, are you sure you really need to do this? If you just want to load some code into LUT, you should probably use its hub address instead, something like:
       ' load the LUT
       mov  pa, ##@my_lut_start
       setq2  ##(@my_lut_start - @my_lut_end)/4 ' number of longs
       rdlong 0, pa
       ...
       ORG $200
    my_lut_start
       ' LUT code goes here
    my_lut_end
    
    That way you don't need any extra 0 padding in your code.

  • I am hunting down some mysterious error and grasping for straws right now.

    It just occurs if I include the code as sub object and somehow the memory is shifted by 52 longs.

    Since t is a sub-object, to be included by fastspin I think I can not use ORGH since fastspin will place it somewhere.

    so I will try orgf .

    thanks

    Mike
  • msrobots wrote: »
    I am hunting down some mysterious error and grasping for straws right now.

    It just occurs if I include the code as sub object and somehow the memory is shifted by 52 longs.

    Since t is a sub-object, to be included by fastspin I think I can not use ORGH since fastspin will place it somewhere.

    so I will try orgf .

    Are you able to share the code that's causing trouble? You're right that ORGH with a number isn't allowed inside an object, but just "ORGH" to say "this code is to go in HUB somewhere" is legal. And hub addresses with @ should work inside sub-objects now (they didn't in older versions of fastspin, but that's been fixed for a few months now I think).

  • still at work, it is my current broken version of @TonyB_ 's Z80 bytecode interpreter.

    can post in a couple of hours

    Mike
Sign In or Register to comment.