Shop OBEX P1 Docs P2 Docs Learn Events
First SX Assembler Program on an SX48 Module — Parallax Forums

First SX Assembler Program on an SX48 Module

Bill ChennaultBill Chennault Posts: 1,198
edited 2010-01-17 21:13 in General Discussion
All--

While I'm waiting other developments, I decided to try to cut a little SX assembler code. The last assembler I used was either·for the·8080 or Z80 instruction set. This was a long time ago.

In any case, I was rather amazed at how the rudiments returned to me! All the code listed below does is blink an LED and call a subroutine for the pause. The environment is a PDB with one of RobotWorkshop's SX48 modules,·a USB SX-Key Rev. B and the SX-Key v3.3.0 IDE.

If you have time to look at the code, I would appreciate comments. I would PARTICULARLY like to learn how to time instructions so I could write a good delay routine, instead of empirically figuring it out. Plus, I am sure the code could be shortened a bunch. I would really like to see how one might do that. (It would be cool if I knew how to make these little code segments look neat in a post like everyone else does, too.)

Thanks!
; Program name: 01-16-10 SX48 First Assembly Code.SRC
;
; Author: Bill Chennault
;
; Date: 01/16/10
;
; Description: This program blinks an LED and uses a subroutine for delay
device··sx48,oschs3
IRC_CAL··IRC_SLOW
reset··start_point
freq··50_000_000
org··0···; place assembled code beginning at address 0
start_point·
Main
··mov ·!rb,#0··; make port B pins all output
··mov·rb,#0··; make all port B pins high (turn LED on)
··call·Delay··; pause
··mov·rb,#1··; make all port B pins low (turn LED off)
··call ·Delay··; delay
··jmp·Main··; do this forever
; subroutine: Delay
Delay··mov·$0f,#$ff·; set fr 0fh to ffh
··mov·$10,#$ff·; set fr 10h to ffh
··mov·$11,#$14·; set fr 11h to 14h
pause··decsz·$0f··; decrement fr 0fh; if 0, skip the next instruction
··jmp·pause··; it wasn't 0, stay in the loop
··decsz·$10··; decrement fr 10h; if 0, skip the next instruction
··jmp·pause··; it wasn't 0, stay in the loop
··decsz·$11··; decrement fr 101; if 0, skip the next instruction
··jmp·pause··; it wasn't 0, stay in the loop

··ret
jmp start_point
--Bill

▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
You are what you write.

Post Edited (Bill Chennault) : 1/16/2010 8:49:26 PM GMT

Comments

  • JonnyMacJonnyMac Posts: 9,298
    edited 2010-01-16 21:14
    Remember, Bill, you can learn a lot from SX/B; write the code in BASIC, then have a look at the output using Ctrl-L. You will ultimately see where you can make this more efficient (i.e., hand-optimize) and build your own library. I did that with the SX and became quite comfortable -- so much so that when we started PropBASIC I was able to contribute assembly code instead of *steal* it!

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Jon McPhalen
    Hollywood, CA
  • Bill ChennaultBill Chennault Posts: 1,198
    edited 2010-01-16 21:53
    Jon--

    When I was writing the code above, I looked at a PAUSE in SX/B using Ctrl-L. I thought I would just copy and paste it into my program. Ha! No such luck! First, copy doesn't appear functional·from within·a Ctrl-L listing. Second, the code must use the compiler (at least in that particular case) in a way that makes it useless as an example.

    Of course, I will continue to use the Ctrl_L function to get hints, but it did not work out for me with the PAUSE statement.

    If each instruction executes in one tick of the clock, then to determine timing·do I just·count the instructions and call that the denominator of frequency/number of instructions?

    --Bill

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    You are what you write.
  • Bill ChennaultBill Chennault Posts: 1,198
    edited 2010-01-16 22:37
    Jon--

    A second look at the Ctrl-L listing shows I was wrong in my assumptions about the code not being valuable in the PAUSE case. I just did not understand all the system variables automatically defined upon SX/B compilation.

    Thanks!

    --Bill

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    You are what you write.
  • JonnyMacJonnyMac Posts: 9,298
    edited 2010-01-17 00:36
    Delays are the biggest pain in the backside in Assembly; if you change processor speed you have to re-code them. This is where SX/B is a real benefit.

    You may find, as I did, that a hybrid program of SX/B and Assembly will work well. At the beginning I started with pure SX/B; over the last 24 months everything has been hybrid. When I started using the Propeller I immediately started writing support objects (e.g., SIRCS stuff in this month's N&V) in Assembly -- which is far easier on the Propeller than on the SX.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Jon McPhalen
    Hollywood, CA
  • Bill ChennaultBill Chennault Posts: 1,198
    edited 2010-01-17 01:22
    Jon--

    Hmmm. Easier on the Propeller, huh?

    For now, I am bedeviled by merely calculating the timing involved in my little Delay subroutine. It can't be hard! One cycle for all but the branching instructions and four cycles for DJNZ if it branches and two cycles if it doesn't makes it seem easy to calculate. Maybe I will have an epiphany soon.

    --Bill

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    You are what you write.
  • JonnyMacJonnyMac Posts: 9,298
    edited 2010-01-17 01:27
    FAR easier. Propeller Assembly, while it has a few things you need to watch for, is a breeze compared to SX Assembly. The waitcnt instruction makes delays child's play, and having the counters for use as timers really makes a lot of applications a cinch. See my column this month for an example.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Jon McPhalen
    Hollywood, CA
  • Bill ChennaultBill Chennault Posts: 1,198
    edited 2010-01-17 03:47
    All--

    Please give me a hand with this timing calculation on a 20MHz SX28! Here's the code and my calculations . . .

    ; subroutine: Delay

    Delay
    ··· mov __param1,#255
    ··· mov __param2,#255
    ··· mov __param3,#80
    d1· djnz __param1,d1
    ··· djnz __param2,d1
    ··· djnz __param3,d1
    ··· ret

    ========================================================
    Given:·mov················ =·2 cycles
    ······ djnz without branch = 2 cycles
    ······ djnz with branch··· = 4 cycles
    ·

    Therefore, the calculations are . . .

    2·+·2 +·2··········· =··········6 mov cycles
    2 * 80 * 255·········=···· 40,800 djnz non branch cycles
    4 * 255 * 255 * 80· ·= 20,808,000 djnz branch cycles

    ······················
    ······················ 20,848,806 cycles

    At 20MHz each cycle takes 50ns and there are 20,848,806 cycles. Therefore . . .

    50 * 20,848,806 = 1,042,440,300ns or 1,042,440.300us or 1,042.440ms or 1.042s.

    Is this ABOUT right?

    Thanks (A LOT)!

    [noparse][[/noparse]EDIT: Changed·mov instruction cycles from·1 to 2 and all involved calculations.]

    --Bill

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    You are what you write.

    Post Edited (Bill Chennault) : 1/17/2010 5:41:35 PM GMT
  • pjvpjv Posts: 1,903
    edited 2010-01-17 06:43
    Bill;

    Give the attached scheduling program kernels·a shot. One is for SX28, and·the other for SX48. They should run right off the bat, flashing LEDs. The scheduler kernels·allow independent co-operative programs to be operated with little interaction between different applications. The kernels permit programs to be executed at various rates, from microseconds to seconds.
    Also take a look at my (2004?) Parallax contest submission that explains the concept of co-operative programs.

     
    id 'Init48' ;Nov 21, 2007
      Device SX48, OscHS3   ;new assembler
    ;  Device watchdog    ;turn the watchdog on
    ;  Device bor42     ;turn brown-out detect on
    ;  Device carryx     ;turn extended carry on
    ;  Device protect     ;turn program read protection on
      Freq 50_000_000    ;run speed
      Reset Initialize    ;start at label on reset
      IRC_Cal IRC_Slow    ;
    

    ;Constants for the Mode register settings for SX48
    ModeComp  equ $18     ;mode for port B comparator swap access
    ModeBPend  equ $19     ;mode for port B change detect sawp access 
    ModeBEdge  equ $1A     ;mode for port B change rising edge selection (0)
    ModeBEnable  equ $1B     ;mode for port B change detection enable (0)
    ModeSchmidt  equ $1C     ;mode for port  BCDE bit Schmidt trigger enable (0)
    ModeLevel  equ $1D     ;mode for port ABCDE bit CMOS level selection (0)
    ModePullup  equ $1E     ;mode for port ABCDE bit pullup selection (0)
    ModeTristate equ $1F     ;mode for port ABCDE bit output direction selection (0)
    

    ;Constants for the option register
    ;     +---------------------------- 0 for RTCC at address 1
    ;     |+--------------------------- 0 for RTCC roll over interrupt enable
    ;     ||+-------------------------- 0 for RTCC increment on instruction clock
    ;     |||+------------------------- 0 for RTCC rising edge increment
    ;     |||| +----------------------- 0 for RTCC prescaler assignment
    ;     |||| |+---------------------- Prescaler 2
    ;     |||| ||+--------------------- Prescaler 1  
    ;     |||| |||+-------------------- Prescaler 0 14kHz watchdog; 000 is 16 mSec
    ;     |||| ||||
    RTCCIntDisable = %1111_1000   ;
    RTCCIntEnable = %1001_1000
    

    IntConst equ -50      ;1 usec base tick at 50 MHz; 12.5 uSec at 4 MHz
    

       org $A
    Flags  ds 1      ;
    

    Timers  org $10
    IntCtr  ds 1      ;interrupt counter
    Time1  ds 1      ;timer to create  10 uSec
    Time2  ds 1      ;timer to create 100 uSec
    Time3  ds 1      ;timer to create   1 mSec
    Time4  ds 1      ;timer to create  10 mSec
    Time5  ds 1      ;timer to create 100 mSec
    Time6  ds 1      ;timer to create   1  Sec
    Time7  ds 1      ;timer to create   1  Sec
    

    ;Interrupt =============================================
       org 0      ;
    Interrupt bank $10     ;
       inc  IntCtr    ;increment interrupt counter
       mov  w,#IntConst   ;load interrupt interval value
       retiw      ;
    

    Initialize ;============================================;Program starts here on power-up and on reset.
    InitLevels mov  w,#ModeLevel  ;Set to 0 for CMOS levels
       mov  m,w     ;
       mov  !ra,#%0000   ;
       mov  !rb,#%0000_0000  ;
       mov  !rc,#%0000_0000  ;
       mov  !rd,#%0000_0000  ;
       mov  !re,#%0000_0000  ;
    

    InitPullup mov  w,#ModePullup  ;Set to 0 for pullups
       mov  m,w     ;
       mov  !ra,#%0000   ;
       mov  !rb,#%0000_0000  ;
       mov  !rc,#%0000_0000  ;
       mov  !rd,#%0000_0000  ;
       mov  !re,#%0000_0000  ;
    

    InitTris mov  w,#ModeTristate  ;Set to 0 for output
       mov  m,w     ;
       clr  ra     ;
       mov  !ra,#%0000   ;
       clr  rb     ;
       mov  !rb,#%0000_0001  ;
       clr  rc     ;
       mov  !rc,#%0000_0000  ;
       clr  rd     ;
       mov  !rd,#%0000_0000  ;
       clr  re     ;
       mov  !re,#%0000_0000  ;
    

    ClrMem  mov  fsr,#$10   ;beginning of ram
    ClrOne  clr  ind     ;clear location
       incsz fsr     ;next location
       jmp  ClrOne    ;continue clearing
       clr  Flags    ;
    

    InitTimers bank Timers    ;
       mov  Time1,#10   ;10 uSec timer
       mov  Time2,w    ;100 uSec timer
       mov  Time3,w    ;  1 mSec timer
       mov  Time4,w    ; 10 mSec timer
       mov  Time5,w    ;100 mSec timer
       mov  Time6,w    ;  1  Sec timer
       mov  Time7,w    ;  1  Sec timer
    InitRTCC clr  rtcc    ;
       mov  !option,#RTCCIntEnable ;enable interrupt
       jmp Main     ;
    

    ;Mainline program loop ================================== 
    Main  bank Timers    ;
    IntTest  test IntCtr    ;see if any interrupts pending
       snz       ;wait for occurence of an interrupt
       jmp  IntTest    ;
    uSec1  dec  IntCtr    ;act on interrupt event
       decsz Time1    ;
       jmp  Main    ;
    uSec10  mov  Time1,#10   ;reload timer for another 10 uSec (4 ticks of 2.5 uSec)
       bank Timers    ;
       decsz Time2    ;
       jmp  Main    ;
    uSec100  mov  Time2,#10   ;reload timer for another 100 uSec
       bank Timers    ;
       decsz Time3    ;
       jmp  Main    ;
    mSec1  mov  Time3,#10   ;reload timer for another 1 mSec
       bank Timers    ;
       decsz Time4    ;
       jmp  Main    ;
    mSec10  mov  Time4,#10   ;reload timer for another 10 mSec
       bank Timers    ;
       decsz Time5    ;
       jmp  Main    ;
    mSec100  mov  Time5,#10   ;reload timer for another 100 mSec
       call FastFlash   ;application program
       bank Timers    ;
       decsz Time6    ;
       jmp  Main    ;
    Sec1  mov  Time6,#10   ;reload timer for another 100 mSec
       call SlowFlash   ;application program
       bank Timers    ;
       decsz Time7    ;
       jmp  Main    ;
    Sec10  mov  Time7,#10   ;reload timer for another 1 Sec
       jmp  Main    ;
    

    ;Application programs =========================================
    FastFlash xor  ra,#1
       retp
    

    SlowFlash xor  ra,#2
       retp
    

    
    
    
    

    Cheers,

    Peter (pjv)
  • Bill ChennaultBill Chennault Posts: 1,198
    edited 2010-01-17 16:03
    pjv and All--

    This morning, I figured out the correct way to use the $ operator and thereby eliminated the label "d1" in my delay routine. A little victory for me, but no help in figuring out the total time the delay routine consumes. I THINK it is correct that the delay routine causes a delay of 1 second (1.042). If anyone can confirm my number or calculations shown above, I would appreciate it. If there is a better was to calculate it, I would like to know.

    pjv, I downloaded your code this morning and will work with it now. I am sure I will learn a lot. (It is clear and concise.)

    --Bill

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    You are what you write.
  • JonnyMacJonnyMac Posts: 9,298
    edited 2010-01-17 16:04
    Bill,

    PJV's scheduler is very helpful, though it will take wrapping your mind around it's structure and adapting to it. I have incorporated some of Peter's style into many of my programs.

    On your calculation, this instruction:

    mov __param1, #255
    



    ...is actually 2 cycles because it breaks down to:

    mov w, #255
    mov __param1, w
    



    This is why I have never tried to do timing loops -- I don't have the patience. With Peter's scheduler an interrupt creates a base "tick" rate that you can build on.

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    Jon McPhalen
    Hollywood, CA
  • Bill ChennaultBill Chennault Posts: 1,198
    edited 2010-01-17 16:22
    Peter and Jon--

    Peter, I printed off your article. Unfortunately, since I do not have Office on this machine, or anything else that might get in the way of my robotics efforts, except Windows itself, of course, I could not get your picture! I'm desolated! (I will print it off again with the machine at my reading desk!) Peter, do you like my hat? [noparse]:)[/noparse]

    Thank you very much for the advice . . . in the 30 seconds I've looked at the code, I can tell it is going to help a lot.

    Jon, I understand--at least a light is glowing dimly at the end of the tunnel--about the difficulty of calculating timing. I may never be its master, but I refuse to be its slave! [noparse]:)[/noparse] Thanks for the feedback on the mov instruction. That will add 3 additional cycles. Not much. But, it is my nature to know things exactly if I am capable of comprehension in the first place. (Of course, nothing I am going to do with a microcontroller will require that precision.)

    --Bill

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    You are what you write.
  • Bill ChennaultBill Chennault Posts: 1,198
    edited 2010-01-17 19:20
    Peter and All--

    Peter, your RTOS code is beautiful.

    Where would one stick an application program's power-up initialization routine?·Between the "Initialize" label and the "InitLevels" label?

    For example, my code has to initialize a parallel 2x16 LCD upon power-up. It seems that the spot I mentioned above is the correct place.

    Thanks for suggesting this approach!

    --Bill

    ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
    You are what you write.
  • pjvpjv Posts: 1,903
    edited 2010-01-17 21:13
    Hi Bill;

    I would do that right after all the RTOS initialization is complete, so, immediately before starting the scheduling kernel running.

    Cheers,

    Peter (pjv)
Sign In or Register to comment.