First SX Assembler Program on an SX48 Module

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!
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
You are what you write.
Post Edited (Bill Chennault) : 1/16/2010 8:49:26 PM GMT
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!
--Bill; 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
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
You are what you write.
Post Edited (Bill Chennault) : 1/16/2010 8:49:26 PM GMT
Comments
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Jon McPhalen
Hollywood, CA
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.
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.
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
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.
▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔
Jon McPhalen
Hollywood, CA
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
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)
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.
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
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.
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.
I would do that right after all the RTOS initialization is complete, so, immediately before starting the scheduling kernel running.
Cheers,
Peter (pjv)