Discussion forum for the BasicX family of microcontroller chips.
A Code Challenge - Tom Becker - Dec 12 16:23:00 2005
A common task is timestamping and logging an event. I propose a
simple challenge: what is _the_ elegant code that produces an 8-byte
ASCII string in the form "HHMMSShh" (hh is _hundredths_ of a second)
from a current BX-24 RTC value?
Any takers?
Tom

(You need to be a member of basicx -- send a blank email to basicx-subscribe@yahoogroups.com )
Re: A Code Challenge - Don Kinzer - Dec 12 19:54:00 2005
--- In basicx@basi..., "Tom Becker" <gtbecker@r...> wrote:
Here is a rendition that generates the characters into a buffer.
'
'' getClock
'
' Write 8 characters representing the current time beginning at
' the given address in the format HHMMSShh.
'
Sub getClock(ByVal addr as Integer)
Dim hr as Byte
Dim min as Byte
Dim sec as Single
Dim v as Byte
Call GetTime(hr, min, sec)
' hours
Call RamPoke(hr Mod 100 \ 10 Or &H30, addr + 0)
Call RamPoke(hr Mod 10 Or &H30, addr + 1)
' minutes
Call RamPoke(min Mod 100 \ 10 Or &H30, addr + 2)
Call RamPoke(min Mod 10 Or &H30, addr + 3)
' integral seconds
v = FixB(sec)
Call RamPoke(v Mod 100 \ 10 Or &H30, addr + 4)
Call RamPoke(v Mod 10 Or &H30, addr + 5)
' hundredths of a second, rounded to the nearest hundredth
v = FixB((sec - CSng(v)) * 100.0 + 0.5)
Call RamPoke(v Mod 100 \ 10 Or &H30, addr + 6)
Call RamPoke(v Mod 10 Or &H30, addr + 7)
End Sub
To print out the result, you can call it thusly:
Dim tstr as String * 8
Public Sub Main()
Do
Call getClock(MemAddress(tstr) + 2)
Debug.Print tstr
Call Delay(1.0)
Loop
End Sub

(You need to be a member of basicx -- send a blank email to basicx-subscribe@yahoogroups.com )
Re: A Code Challenge - Tom Becker - Dec 12 23:10:00 2005
How's this?
function strTimestamp() as string
dim lWork as long, strH as string*3, strM as string*3, _
strS as string*3, strF as string*3
lWork = Register.RTCtick 'read clock
strF = cStr(lWork Mod 512 * 100 \ 512 + 100) 'hundredths
lWork = lWork \ 512 'discard fraction
strS = cStr(lWork Mod 60 + 100) 'seconds
strM = cStr((lWork \ 60) Mod 60 + 100) 'minutes
strH = cStr(lWork \ 3600 + 100) 'hours
strTimestamp = mid(strH, 2, 2) & mid(strM, 2, 2) _
& mid(strS, 2, 2) & mid(strF, 2, 2)
end function
Tom

(You need to be a member of basicx -- send a blank email to basicx-subscribe@yahoogroups.com )
Re: A Code Challenge - Don Kinzer - Dec 13 1:27:00 2005
--- In basicx@basi..., "Tom Becker" <gtbecker@r...> wrote:
I made modifications to the three submitted solutions to make them
have the same invocation signature and used Mike's bxDism to
determine the code and stack size used by each. I set up Main() as
shown below and uncommented just one invocation, compiled, ran
bxDism and recorded the data.
Sub Main()
Debug.Print read_clock() ' Niel's solution
' Debug.Print readClock() ' Don's solution
' Debug.Print strTimeStamp() ' Tom's solution
End Sub
Here are the result:
Niel:
Total code size: 6208 bytes
Actual function size: 532 bytes
Stack space used: 170 bytes
Don:
Total code size: 1587 bytes
Actual function size: 597 bytes
Stack space used: 72 bytes
Tom:
Total code size: 1888
Actual function size: 326 bytes
Stack space used: 73 bytes
The 'Actual function size' data is the number of bytes of the
conversion routine itself, not counting system routines that may be
needed elsewhere in a larger program. If those system routines are
going needed anyway, the "cost" of using them is zero. In all three
cases, the size of Main() was 257 bytes.
Using the information gained from the three tests, I combined the
ideas to produce a new routine:
Function readClock2() as String
Dim tstr as String * 8
Dim lwork as Long
Dim v as Byte
Dim addr as Integer
addr = MemAddress(tstr)
lWork = Register.RTCtick 'read clock
' calculate the hundredths
v = CByte((lWork * 100 Mod 51200 + 256) \ 512)
Call RamPoke(v \ 10 Or &H30, addr + 6)
Call RamPoke(v Mod 10 Or &H30, addr + 7)
lWork = lWork \ 512 'discard fraction
' produce the seconds digits
v = CByte(lWork Mod 60)
Call RamPoke(v \ 10 Or &H30, addr + 4)
Call RamPoke(v Mod 10 Or &H30, addr + 5)
' generate the minutes digits
v = CByte(lWork \ 60 Mod 60)
Call RamPoke(v \ 10 Or &H30, addr + 2)
Call RamPoke(v Mod 10 Or &H30, addr + 3)
' generate the hours digits
v = CByte(lWork \ 3600 Mod 24)
Call RamPoke(v \ 10 Or &H30, addr + 0)
Call RamPoke(v Mod 10 Or &H30, addr + 1)
readClock2 = tstr
End Function
New:
Total code size: 1332
Actual function size: 659 bytes
Stack space used: 25 bytes
The code size could be reduced a little by introducing a helper
function but that would also increase the stack usage (probably by
13 bytes or so).
The same code compiled for the ZX-24 is 257 bytes total including
Main().
Don
http://www.zbasic.net

(You need to be a member of basicx -- send a blank email to basicx-subscribe@yahoogroups.com )
Re: Re: A Code Challenge - Tom Becker - Dec 13 8:34:00 2005
> Total code size: 1332
> The same code compiled for the ZX-24 is 257 bytes total...
!? Total? ZX code is six times more dense?
I like your notion of poking to avoid concatentation but I expected a
greater stack saving. I wonder if mid()= will do the same.
Tom

(You need to be a member of basicx -- send a blank email to basicx-subscribe@yahoogroups.com )
Re: A Code Challenge - Don Kinzer - Dec 13 11:57:00 2005
--- In basicx@basi..., Tom Becker <gtbecker@r...> wrote:
>Total?
Yes, that was the total size. The code is smaller for two reasons.
Firstly, and the major effect in small programs, some of the often
used system library routines are actually implemented in user-space
code in BasicX. Mike calls these "externally implemented system
library functions" in his article series. In ZBasic, all system
library routines are implemented in the firmware except for one,
CStrHex().
The second factor is that the ZX pcode instruction set is richer,
allowing shorter code sequences to implement the same effect. A third
factor is that may affect code size is the optimizer. Depending on
how the code is written, the compiler may be able to simplify it by
combining values that known at compile time, substituting a shorter
equivalent code sequence, eliminating code that cannot possible by
reached, and other well-known optimization strategies.
> ZX code is six times more dense?
No. In small programs that use several externally implemented system
library routines the difference can be quite large as in this case.
If you look at the size of the readClock2() routine itself it is 659
bytes for BasicX and 209 bytes for ZBasic. I think that this is an
unusual case, perhaps somewhat of an anomaly. Normally, I would
expect ZBasic code size to be 60% to 90% of the BasicX size. In some
cases, it is possible that ZBasic code will be about the same size as
the BasicX code.
Don
http://www.zbasic.net

(You need to be a member of basicx -- send a blank email to basicx-subscribe@yahoogroups.com )
Re: A Code Challenge - Don Kinzer - Dec 13 12:12:00 2005
--- In basicx@basi..., "Don Kinzer" <dkinzer@e...> wrote:
> If you look at the size of the readClock2() routine itself it is 659
> bytes for BasicX and 209 bytes for ZBasic.
I erred. When I read the output from Mike's bxDism I took the 293
code size for BasicX to be hexadecimal but it is actually decimal.
The statement should have read:
If you look at the size of the readClock2() routine itself it is 293
bytes for BasicX and 209 bytes for ZBasic.
Don
http://www.zbasic.net

(You need to be a member of basicx -- send a blank email to basicx-subscribe@yahoogroups.com )
Re: A Code Challenge - Jepsen Electronics & Acoustics Ltd - Dec 13 15:22:00 2005
Fun exercise Tom. Whats next?
Tom Becker wrote:
> A common task is timestamping and logging an event. I propose a
> simple challenge: what is _the_ elegant code that produces an 8-byte
> ASCII string in the form "HHMMSShh" (hh is _hundredths_ of a second)
> from a current BX-24 RTC value?
>
> Any takers?
> Tom
>
>
> ------------------------------------------------------------------------
> >.
> ------------------------------------------------------------------------

(You need to be a member of basicx -- send a blank email to basicx-subscribe@yahoogroups.com )
A Code Challenge 2 - Tom Becker - Dec 15 12:07:00 2005
> ... Fun exercise, Tom. Whats next?
OK, using only code, generate accurate 60Hz (or 50Hz) three-phase
(120-degree shift) squarewaves on BX-24 pins 5, 6 and 7.
Tom

(You need to be a member of basicx -- send a blank email to basicx-subscribe@yahoogroups.com )
Re: A Code Challenge 2 - Don Kinzer - Dec 15 15:23:00 2005
--- In basicx@basi..., "Tom Becker" <gtbecker@r...> wrote:
> using only code, generate accurate 60Hz (or 50Hz) three-phase
> (120-degree shift) squarewaves on BX-24 pins 5, 6 and 7.
Measured frequency is accurate to 0.2%.
Sub Main()
Dim idx As Byte
Dim bits(1 to 6) As Byte
' prepare the output bit pattern
bits(1) = &H80
bits(2) = &Hc0
bits(3) = &He0
bits(4) = &H60
bits(5) = &H20
bits(6) = &H00
idx = 1
Register.DDRC = &Hff
' prepare the timer for 360Hz operation
Register.OCR1AH = 0
Register.OCR1AL = 19
Register.TCNT1H = 0
Register.TCNT1L = 0
Register.TIFR = &H10
Register.TCCR1A = &H00
Register.TCCR1B = &H0d
Do
' await the next timer overflow
Do Until CBool(Register.TIFR And &H10)
Loop
' output the next bit pattern
Register.PortC = bits(idx)
Register.TIFR = &H10
' prepare for the next iteration
idx = idx + 1
If (idx > 6) Then
idx = 1
End If
Loop
End Sub

(You need to be a member of basicx -- send a blank email to basicx-subscribe@yahoogroups.com )
Re: A Code Challenge 2 - Tom Becker - Dec 15 19:32:00 2005
Here's another timing method:
' Generate 3-phase nHz on BX-24 pins 5, 6 and 7
sub main()
dim bStates(0 to 5) as byte, _
bT as byte, lNow as long, lThen as long
bStates(0) = bx1010_0000 'phase table
bStates(1) = bx1000_0000
bStates(2) = bx1100_0000
bStates(3) = bx0100_0000
bStates(4) = bx0110_0000
bStates(5) = bx0010_0000
register.DDRC = &hE0 'outputs
do
for bT = 0 to 5 'six states
do
lNow = (register.RTCTick mod 512 * 6 * 60) \ 512 '60=60Hz
loop until lNow <> lThen 'average rate is 360Hz
lThen = lNow
register.PortC = bStates(bT) 'next state
next
loop
end sub
We both step through a table at 360Hz. Yours uses Timer1 to
approximate (closely) 360Hz and yields equal phase step periods. Mine
uses the RTC tick count to yield an average step period that is as
accurate as the processor crystal, but the phase step periods will
jitter as much as one tick.
Anyone else?
Tom

(You need to be a member of basicx -- send a blank email to basicx-subscribe@yahoogroups.com )
Re: A Code Challenge 2 - Don Kinzer - Dec 15 19:47:00 2005
--- In basicx@basi..., "Tom Becker" <gtbecker@r...> wrote:
> We both step through a table...
In a "real" application, I would put the table in a ByteVectorData
element instead of populating a table in RAM. I did it the "easy" way
to avoid introducing another file containing the initialization data.
Don

(You need to be a member of basicx -- send a blank email to basicx-subscribe@yahoogroups.com )
Re: A Code Challenge 2 - Tom Becker - Dec 19 11:34:00 2005
Digging deeper into BX-24 timing, I am puzzled.
Those who have accurate timing instruments, please measure the frequency on pin 6 while
running:
sub main()
dim bT as byte, iX as integer, _
lNow as long, lThen as long
do
do while (lNow = lThen)
lNow = (register.RTCTick) * 1 \ 256
loop
lThen = lNow
if GetPin(6) <> 0 then 'invert pin
call PutPin(6, 0)
else
call PutPin(6, 1)
end if
loop
end sub
You should find 1.000Hz (or within your crystal error, anyway).
Then change
lNow = (register.RTCTick) * 1 \ 256
to
lNow = (register.RTCTick) * 2 \ 512
or any other equivilent ratio.
The resultant pin 6 frequency is repeatably 0.996Hz, lower by ~0.4%. Can this be
confirmed and, if so, what's happening here?
Tom

(You need to be a member of basicx -- send a blank email to basicx-subscribe@yahoogroups.com )
Re: Re: A Code Challenge 2 - Mike Perks - Dec 19 12:22:00 2005
Tom Becker wrote:
> Then change
> lNow = (register.RTCTick) * 1 \ 256
> to
> lNow = (register.RTCTick) * 2 \ 512
> or any other equivilent ratio.
The BasicX compiler does some simple optimization but not complex ones.
I didn't bother verifying the frequency but instead ran bxDism to list
the BasicX instructions and prove my hunch. In this case the
multiplication by 1 is optimized away. Here are the two BasicX
instruction sequences:
pushL register.RTCTick
pushL #256
divL
popL test.main.lnow
and
pushL register.RTCTick
pushL #2
mulL
pushL #512
divL
popL test.main.lnow
Mike

(You need to be a member of basicx -- send a blank email to basicx-subscribe@yahoogroups.com )
Re: A Code Challenge 2 - Don Kinzer - Dec 19 12:50:00 2005
--- In basicx@basi..., "Tom Becker" <gtbecker@r...> wrote:
> Those who have accurate timing instruments, please measure the
> frequency on pin 6 while running:...
I suspect that the problem is that the GetPin() call does not return
the current output state of the pin. Rather, it turns the pin to an
input and then reads the input value (floating pin). If you put a
pullup resistor on that pin the behavior changes.
The program can be fixed by introducing another variable to track the
desired output state or by reading Register.PortC to determine the
current output state. Alternately, you can use a function that I
wrote that returns that state of an output pin given its number. I
posted information about that function, PinState(), in a message in
September:
http://groups.yahoo.com/group/basicx/message/19664
Don

(You need to be a member of basicx -- send a blank email to basicx-subscribe@yahoogroups.com )
Re: A Code Challenge 2 - Tom Becker - Dec 19 12:58:00 2005
> I suspect that the problem is that the GetPin()
No, I added the GetPin/PutPin If-Next to simplify what I actually do
with the change, which is to step through a table as we did earlier in
the thread. The behavior is related to the Long multiplication. I
find that 2\4 is not the same as 1\2, nor 3\6.
Tom

(You need to be a member of basicx -- send a blank email to basicx-subscribe@yahoogroups.com )
Re: Re: A Code Challenge 2 - Tom Becker - Dec 19 13:04:00 2005
> ... multiplication by 1 is optimized away...
Not surprised, but (0+1)\ or (4-3)\ , even (6\3-1)\ behave the same as
(1)\.
Tom

(You need to be a member of basicx -- send a blank email to basicx-subscribe@yahoogroups.com )
Re: A Code Challenge 2 - Tom Becker - Dec 19 15:23:00 2005
The error does not occur using integers! Baffled, I am.
sub main()
dim iNow as integer, iThen as integer
register.DDRC = bx01000000 'pin 6 out
do
do while (iNow = iThen)
iNow = cint(register.RTCTick mod 65536)
iNow = iNow * 2 \ 512 ' 1.000S
loop
iThen = iNow
register.PortC = register.PortC xor bx01000000 'invert pin 6
loop
end sub
Tom

(You need to be a member of basicx -- send a blank email to basicx-subscribe@yahoogroups.com )
Re: A Code Challenge 2 - Tom Becker - Dec 19 20:03:00 2005
Here is as concise a demonstration of the timing error - which occurs
with Long integers and not when doing the same process in integers.
Consider the following code, please; if the iNow*2\512 work is done
in Longs, as in the second line, the result is ~0.4% low. I just
nested the math inside a cLng(), inside a cInt, and the result is
different. Using cUInt() also causes the error. Done in integers,
the result is exactly correct.
Most baffling.
sub main()
dim iNow as integer, iThen as integer
register.DDRC = bx01000000 'pin 6 out
do
do while (iNow = iThen)
iNow = cint(register.RTCTick)
'iNow = iNow * 2 \ 512 ' 1.000S
iNow = cint(clng(iNow) * 2 \ 512) '~0.995S
loop
iThen = iNow
register.PortC = register.PortC xor bx01000000 'invert pin 6
loop
end sub
Tom

(You need to be a member of basicx -- send a blank email to basicx-subscribe@yahoogroups.com )
Re: A Code Challenge 2 - raunig2003 - Dec 20 22:24:00 2005
--- In basicx@basi..., "Tom Becker" <gtbecker@r...> wrote:
>
> The error does not occur using integers! Baffled, I am.
in vb
3/2=1.5
and 3\2= 1
Notice the direction of the division symbols. Does this apply to basicx.
RR

(You need to be a member of basicx -- send a blank email to basicx-subscribe@yahoogroups.com )