PICAXE program Size, Optimisations and Speed

westaust55

Moderator
The attached document is a first pass on collating the available information on the PICAXE chips with respect the program size, possibly optimisations for size and the speed of various BASIC commands.

Still work to do to record the speed related data for various other PICAXE chips.
This version will give others an opportunity to comment or correct information prior to a further release.

Much of the data on program sizing has been gleaned from past posts on this forum particularly by hippy.
Some test work has been undertaken by myself and more by myself to come as time permits.

Hopefully others find this as interesting and useful as I have.

EDIT: See post 9 for new Rev D version which includes more timing tests and other data
 

Attachments

Last edited:

hippy

Senior Member
Seems to be a good summary and nothing I could see which jumps out as a complete misunderstanding.

For the last page, the two FOR-command-NEXT speed tests -

"b3=b4", f=464.0Hz

"Readadc10 0,w2", f=478.3Hz (curious, does readadc10 wait for completion?)

Yes, READADC and READADC10 wait for completion of ADC read though this is quite quick. Not sure what's "curious"; seems too fast or too slow ? Both "LET var = var" and "READADC pin, var" are three token commands, though differently sized. The small difference in execution is likely the way NEXT falls with bit-alignment after the commands and size and alignment of each token will vary; "READADC10 var, var" would be a more comparable test, though not exactly comparable, and different firmware handlers for the commands will take their toll. What's gained in alignment may be lost in firmware code and vice-versa. Hence, all-in, it's near impossible to get an accurate measure of anything or even predict it in any meaningful absolute way; it's a bit like trying to guess at the 'length of string' from that set of string which we can measure. That's not to say it's completely without merit though!
 

BillyGreen1973

Senior Member
Thanks Westaust,

Very informative. I have learned a little more about how PICAXE firmware does it's magic :)
I would have to agree with Hippy with regard to speed prediction, it's near impossible, but your work does show how you can optimise for better performance by just using a little forethought and using lower numbers and variables etc.

Well worth the read.
Thanks
 

westaust55

Moderator
Thanks hippy and BillyGreen1973 for the feedback.

with respect to the comment:
"(curious, does readadc10 wait for completion?)"
that was verbatim from the data Beaniebots had posted.
AT this moment I had collected the data from the 2003 thread but not done anything further with it.

This morning I have done timing tests with a PICAXE 18X (more at a later time).
The revised table with the 18X results is attached. When I have done tests for more PICAXE chips I will update the main document (including a few typos I have already spotted :eek: ).
 

Attachments

westaust55

Moderator
Just had a chance to undertake the timing test for the 18M2 for comparative purposes.

Most commands are slightly slower which may be expected considering the multitasking/time slice capability of the BASIC interpreter and greater versatility.

A significant increase in the time required for GOTO and GOSUB...RETURN commands compared with 08M and 18X chips.
Of interest is that the time to write to EEPROM was the fastest (shortest time) by around 25% of those tested thus far. Reading from EEPROM was on par with other PICAXE chips.
 

Attachments

BeanieBots

Moderator
Readadc10 0,w2", f=478.3Hz (curious, does readadc10 wait for completion?)

The curiosity was that a ReadADC10 took LESS time than that required to make a single byte data transfer. The implication being that the ADC part took no time at all. Just a curiosity but I guess the conversion time is very small compared to the code unpacking overhead.
 

westaust55

Moderator
Managed to perform the tests for some further and newer PICAXE chips today and accordingly attached is a tentatively final version of my document.

For completeness, while for consistency most tests were performed at 4 MHz clock speed, I did perform a test as well at 8 MHz for the 28X2 for comparative purposes.
 

Attachments

Last edited:

westaust55

Moderator
PICAXE program Size, Optimisations and Speed - Rev C

Attached is an update to my PICAXE program space requirements, optimisations and timing information.

I have corrected a couple of typographic errors in this revision and being one who likes to delve into all manner of equipment to better understand how it works, had a look yesterday at how the PICAXE programs are “assembled” in token format using nothing more than un-altered Rev Ed hardware and software. This enabled me to provide some edits in relation to program space requiremtns and relating information with respect to the number of bits used by the X2 series PICAXE chips, which due to the greater number of variables and commands, need more bits than for X1 and early PICAXE chips.
 

Attachments

I found the paper on the characteristics of the Picaxe Variants Optimization most interesting however I do have a question.
Since the Basic Language is an easily learned and very transportable language, Is the Picaxe designed to teach Basic or to
teach the organizational requirements necessary to teach logical problem solving skills?.
Certainly while an Excellent reference to effectively using the Picaxe, it would seem to me that many of the most effective
methods pointed out directly do so by utilizing 'quirks' in the Picaxe OS rather than more commonly 'portable' methods of
writing Basic code.
Somehow the thought of a middle to high school pupil needing to write a program that needed 4095 of 4096 bytes of code
is kind of mind boggling, there are multiple program slots and data passing can bee done 'off' chip too.
Enough I know why the paper was written and I applaud all and the effort (read dedication) required for such a worthy
task, well Done!.
You 'might' pardon the apparent flame but I needed to set up that compliment first, I was so impressed with the paper I
thought I might have some fun and send a heartfelt compliment about a very welcome document.

Robert K. Johnson Sr.
 

westaust55

Moderator
Since the Basic Language is an easily learned and very transportable language, Is the Picaxe designed to teach Basic or to
teach the organizational requirements necessary to teach logical problem solving skills?.
I am happy to hear that you found BASIC to be easy to learn. While many do, others new to programming can struggle with some concepts and rules - such as ending a subroutine with RETURN and not with GOTO.

With microcontrollers in particular, the BASIC needs to be very specific to the make of chip and this is equally the case for the PICAXE.
Features one PIC, PICAXE or other microcontroller may have, another does not and the addressing for various IO, timers and other chip features differs form chip to chip. As a result portability becomes, at least very difficult and to the other extreme, near impossible or needing great effort.

Certainly while an Excellent reference to effectively using the Picaxe, it would seem to me that many of the most effective
methods pointed out directly do so by utilizing 'quirks' in the Picaxe OS rather than more commonly 'portable' methods of
writing Basic code.
There are many variants of BASIC. The early versions had no access to the computers IO and thus were far more portable.
But even then, the tokens differed even for computers with similar capabilities but different processors.
I recall in times long gone writing a converter program to read the tokenised BASIC file from one computer and on the fly converting to a format to suit another computer so tokens and real processor memory addresses (hi-lo versus lo-hi bytes) were fixed.

...I know why the paper was written and I applaud all and the effort (read dedication) required for such a worthy
task, well Done!.
You 'might' pardon the apparent flame but I needed to set up that compliment first, I was so impressed with the paper I
thought I might have some fun and send a heartfelt compliment about a very welcome document.
Thanks for the compliment.
Such work does take time but being of an investigative mind I wanted to understand better how the PICAXE worked.
There is more that I learnt but will not publish to protect Rev Ed's copyright (and me!) which folks should well understand.
 

AllyCat

Senior Member
Hi,

Here is an alternative method for measuring the exectution times of individual (or multiple) instructions using the Timer1 hardware in recent PICaxe chips. I've only tried M2 devices, but it also should be usable at least for the X2 family. Advantages are that instruction cycles are read directly on a single device which can thus report the test associated with each measurement, and results are independent of the clock frequency. However, the "Special Functon Register" (SFR) addresses for Timer1 vary between PICaxe familiies (M2, X2, etc.) so the test code must be personalised for each family. The results appear to be "consistent" (i.e. a re-run gives identical values) but, as explained earlier in this thread, even the slightest change to the program code can alter the token/byte boundaries, and thus the execution times by at least some (maybe many) tens of cycles.

Initially, I attempted a "non-invasive" method, of reading Timer1 before and after each instruction in the test. However, the maths becomes very complicated because the timer can overflow at many points during the measurement. The timer does not simply wrap around to 0 but appears to be reset to 45536 (-20,000) to give a 20ms sub-tick for the (one second) "time" variable. This seems to occur even if DISABLETIME is commanded (and then Timer1 restarted by writing directly to the SFR control register). Therefore, a rather more "brute force" method of resetting Timer1 is used, which of course upsets the value of the PICaxe "time" variable. However, another advantage of resetting Timer1 is that the maximum measurement time is about 60,000 instruction cycles (allowing for an initial "null" execution time), compared with a maximum of about 15,000 cycles if the timer were not reset.

Timer1 uses two one-byte registers which AFAIK cannot be read or written concurrently (in PICaxe Basic), so the program reads the High byte first, then the Low byte and then the High byte again. The two High byte values are then averaged, taking into account their (odd/even) separation and the value of the Low byte. Beware that some bytes in the program are used as part of a Word variable, including High bytes in a "Low" position (to correctly handle overflow/carry). The first reported measurement is of a Null delay (i.e. the subroutine return/call and the SFR poke/peeks) which is then subtracted from subsequent reported values. Here is the basic core program:

Code:
#picaxe 20m2				; SFRs only tested for M2 series
#no_data				; No EEPROM data

symbol TMR1L = $16			; Timer 1 Low byte
symbol TMR1H = $17			; Timer 1 High byte
symbol T1CON = $18			; Timer 1 Control register

symbol Nul = w5					; Null time delay
symbol WVHL = w6				; Word variable
symbol WVLo = b12				; Word variable Low byte
symbol WVHi = b13				; Word variable High byte
symbol TMBV = b14				; Byte variable for reading timer

main:	
	Nul  = 0					; Initialise time delay
	sertxd("Null=")
	gosub start				; Start Timer 1
	gosub measure				; Read and report timer delay
	Nul = WVHL				; Set NUL time delay			

	sertxd("b0=b0:")			; Report the test instruction
	gosub start
	b0 = b0					; Test instruction
	gosub measure
 	stop
start:
	pokesfr TMR1L,0				; Set Timer Lo byte
	pokesfr TMR1H,0				; Zero Timer Hi byte 
	return
measure:
	peeksfr TMR1H,WVLo 			; Read Timer Hi byte
	peeksfr TMR1L,TMBV			; Read Timer Low byte
	peeksfr TMR1H,WVHi			; Read Timer Hi byte again
	if TMBV < 128 then inc WVLo  endif
	WVHL = WVLo  + WVHi  / 2		; Average Timer High byte
	WVHi  = WVLo 
	WVLo  = TMBV				; Timer Low byte
	WVHL = WVHL - Nul			; Subtract Nul delay	
	sertxd (#WVHL," ")			; Report the time delay
	return
Below is a more complete test harness. It repeats, using different starting values of the timer Low byte, as a "sanity check" that the carry from low to high bytes is handled correctly. At the end, a Null is measured again, which should be within a few tens of instruction cycles of zero (or 65536). With an M2 at nominal 4MHz clock frequency, the (PIC Assembler) instruction cycles are exactly 1us so I use these units as a "shorthand" for Instruction Cycles. However, for an X2 the nominal time is halved, and the minimum execution times for M2 and X2 devices respectively (at maximum 32 and 64MHz clock frequencies) would be divided by 8 and 16.

Code:
#picaxe 20m2				; SFRs only tested for M2 series
#no_data				; No EEPROM data
#define M2				; PICaxe family

#ifdef M2
symbol TMR1L = $16			; Timer 1 Low byte
symbol TMR1H = $17			; Timer 1 High byte
symbol T1CON = $18			; Timer 1 Control register
#endif
#ifdef X2
symbol TMR1L = $CE 			;)
symbol TMR1H = $CF			;)-Not tested
symbol T1CON = $CD 			;)
#endif

symbol Nul = w5				; Null time delay
symbol WVHL = w6				; Word variable
symbol WVLo = b12				; Word variable Low byte
symbol WVHi = b13				; Word variable High byte
symbol TMBV = b14				; Byte variable for reading timer
symbol Seed = b15				; Low seed for timer low byte
symbol Pass = b16				; Loop counter

main:	
	for Seed = 0 to 256 step 32			; Sanity check for data handling
	Nul = 0						; Initialise time delay
	sertxd(cr,lf,"Seed=",#Seed)			; Report testing seed value
	sertxd(cr,lf,"Null=")			; Report Null instruction period
	gosub start						; Start Timer 1
	gosub measure					; Read and report timer delay
	Nul = WVHL						; Set NUL time delay
		
	sertxd("Swap=")				; Report the test instruction
	gosub start
	swap b0,b0					; Test instruction
	gosub measure	

	sertxd(cr,lf,"Gosub+Ret=") 		
	gosub start
	gosub ret	 			
	gosub measure
	
	sertxd("Gosub(only)=")			
	gosub start
	gosub dummy					
	gosub measure
	
	sertxd(cr,lf,"IFtrue=")	 
	gosub start
	if b0 = b0 then true	 
true:
	gosub measure

	sertxd("IFfalse=")		 
	gosub start
	if b0 <> b0 then false 			; Fall through
false:	
	gosub measure

	sertxd("Readtable=")	
	gosub start
	readtable b0,b0 	
	gosub measure
	
	for Pass = 0 to 3					; Now Test some multiple values

	sertxd(cr,lf,"b0*",#Pass,"=")	; Report the test instruction
	gosub start
	b0 = b0 * Pass	 
	gosub measure

	sertxd("b0/",#Pass,"=")	 
	gosub start
	b0 = b0 / Pass	 
	gosub measure
	
	sertxd("OnGoTo",#Pass,"=")  
	gosub start	
	on Pass goto nx,nx,nx			; Test instruction (or fall thro') 
nx:	gosub measure

	sertxd("OnGoSub",#Pass,"=") 	 
	gosub start	
	on Pass gosub dummy,dummy,dummy		 ; Test instruction (or fall thro') 
	gosub measure
	next Pass

	sertxd(cr,lf,"Null Check=")			; Check consistency
	gosub start
	gosub measure	
	pause 9000
	next Seed	
dummy:
	gosub measure
	sertxd("Ret=")	 
	gosub start
ret:	
	return 				
start:
	b0 = Seed						; Seed value for maths calculations
	pokesfr TMR1L,Seed				; Set Timer Lo byte
	pokesfr TMR1H,0					; Zero Timer Hi byte 
	return
measure:
	peeksfr TMR1H,WVLo 				; Read Timer Hi byte
	peeksfr TMR1L,TMBV				; Read Timer Low byte
	peeksfr TMR1H,WVHi 				; Read Timer Hi byte again
report:
	if TMBV < 128 then inc WVLo  endif
	WVHL = WVLo  + WVHi  / 2			; Average Timer High byte
	WVHi  = WVLo 
	WVLo  = TMBV					; Timer Low byte
	WVHL = WVHL - Nul - Seed			; Subtract Nul and sanity check bytes
	if WVHL > 65000 then				; Report slightly negative value
		WVHL = 65535 - WVHL
		sertxd("-")
	endif	
	sertxd (#WVHL," ")				; Report the time delay
	return
Here are some typical output results:

Code:
Seed=32
Null=4531 Swap=2833 b0=b0:581 b0=b0+b0:715 b0=b0+b0+b0:1006 
Gosub+Ret=3247 Gosub(only)=958 Ret=2223 
IFtrue=1273 IFfalse=832 Readtable=665 
b0*0=862 b0/0=996 OnGoTo0=1085 OnGoSub0=2878 Ret=2252 
b0*1=862 b0/1=1086 OnGoTo1=1307 OnGoSub1=3100 Ret=2252 
b0*2=862 b0/2=1086 OnGoTo2=1562 OnGoSub2=3347 Ret=2252 
b0*3=862 b0/3=1080 OnGoTo3=1151 OnGoSub3=1965 
Null Check=3
A feature of this method is that it need take only a few minutes for anyone to measure any particular instuction(s), requiring only the connection of a target PICaxe to the PE. So I have not (yet) prepared a full table of typical exectution times, Also, I am not sure what are now the most useful "typical" instruction times to record. The M2 PICaxes seem to have suffered a significant increase in the execution time of their "jump" instructions, typically two or even three times longer than previous versions. So differentiating between the execution times with different values of a constant, seems to be rather insignificant compared for example with the "true" (~1250us) and "false" (~825us) paths for an IF. Also, the exact structure of formulae seems rather significant, for example "b0 = 0 + b0" executes far slower than "b0 = b0 + 0".

Here is a summary of a few of the "newer" instructions and some of my more "unexpected" results:

The new (not x8M2s) READTABLE command seems usefully fast at ~660us. The time executing an ON..GOTO depends on the position of the label in the list (~220us for each additional element), so the Basic interpreter appears to be searching through a list rather than using a vector into an "address offset" table. Thus it can be more efficient to omit the last label in an ON..GO.. command and allow the code to fall through for the final value. A GOSUB (alone) requires ~1000us with the corresponding RETURN a surprising ~2200us.

SWAP is obviously a pseudo-instruction since it is rather slow (~2800us) and uses around 15 bytes of code. Similarly ON..GOSUB appears to be the PE combining ON..GOTO, GOSUBs and GOTOs. SELECT..CASE is another rather slow preudo command which may be better replaced by an ON..GOTO where applicable. LOOKUP and LOOKDOWN appear to take ~2200us each for 8 elements and ~4200us for 16 elements (e.g. an ASCII-Hex lookup), regardless of the position of the searched element in the list.

Cheers, Alan.
 

graynomad

Senior Member
LOOKUP and LOOKDOWN appear to take ~2200us each for 8 elements and ~4200us for 16 elements (e.g. an ASCII-Hex lookup), regardless of the position of the searched element in the list.
That's weird, LOOKUP should be a simple index into an array, and LOOKDOWN should terminate when the match is made I would think.
 

hippy

Senior Member
LOOKUP and LOOKDOWN appear to take ~2200us each for 8 elements and ~4200us for 16 elements (e.g. an ASCII-Hex lookup), regardless of the position of the searched element in the list.
That's weird, LOOKUP should be a simple index into an array, and LOOKDOWN should terminate when the match is made I would think.
On a PICAXE that's not as easy as one may think it should be. As the PICAXE uses variable length tokens, each entry in the LOOKUP or LOOKDOWN can be a different size, so there's no easy way to tell where each starts or create an index that points to each.

For LOOKUP you can imagine the problem as being similar to a blind person told to take the Nth book from a shelf of books of varying thickness. You can't say book N is N x 1 inch along the shelf.

You could use some lookup or jump table to know where each item starts but that requires more memory than the actual LOOKUP or LOOKDOWN data does, and storing data as fixed size to allow indexing would likely double the memory used in most cases.

Also LOOKUP and LOOKDOWN data can be numeric or a variable so any fixed size entities would need to be 17 bits long to allow for all potential options. Optimising that uses more memory, increases and complicates firmware.

The PICAXE has always put efficient memory usage over speed for its primary commands so the memory optimised solution is to step through the sequences to obtain the result required, and then continue stepping to find the end of the command itself when done. It's not a bug or flaw but a design choice; the usual trade-off of memory size versus speed.

Knowing how it works one can come to the conclusion that it's not a good choice, rather slow, but it has to borne in mind that these perceptions are often simply a result of such a revelation as to how it works. One has to ask was it slow, or too slow to be usable, when you did not know how it worked ?

It's easy to become obsessed with absolute speeds and demanding fastest execution time but for most cases that's not required. Where it is there's the simple SETFREQ trick for instant speed gains, plus READ and READTABLE for faster lookup tables. It can be somewhat like worrying over the top speed of a car when the more important thing is does it get you to the supermarket and back and can it carry your shopping. The PICAXE has quite elegant solutions when execution speed isn't considered the number one priority but I'll admit it took me a little time to fully appreciate that coming from a background where speed was always seen as desirable even where it was often an irrelevancy. It's the standard case of being told something and coming to believe it, hard to put aside or question that belief and see things in a different light.
 

hippy

Senior Member
I forgot to add that the ability to mix numeric and variable data in a LOOKUP and LOOKDOWN rather dictates how they are implemented. Take ...

LookUp b0, ( 100, b1, 1000, w1 ), w2

And consider how the same functionality could be achieved in some other way, or even in another programming language.

It's not easy without using RAM or conditionals or jumping through some hoops. Any language which allows the functionality to be expressed in an elegant way is probably creating some larger overhead under the hood such as pointer tables or hiding processing code which makes it work. There's a penalty for how it's done no matter what is used.

Of course we could define LOOKUP and LOOKDOWN not to use variables to simplify things and allow other options but reducing functionality and increasing memory usage for a potential gain in speed may help some but will create problems for others. As ever it's always a compromise.
 

AllyCat

Senior Member
and then continue stepping to find the end of the command itself when done.
Thanks Hippy, that was the "missing link" as far as I was concerned.

I had only included the LOOK... instructions above rather as an afterthought, but have now confirmed (as does your explanation) that their execution time depends "only" on the length of the string, not on the position of the searched item. Incidentally, I've now compared two methods of ASCII Hex to Decimal conversion and Lookup b3,("0123456789ABCDEF"),b0 takes about twice as long, but is three times larger (24 v 8 bytes) compared with my preferred b0 = b3 / "A" * 249 + b3 - "0". However, explaining how that works to a novice may take some time. ;)

But the instruction which puzzles me most is RETURN. Of course PICaxe basic must be emulating a hardware stack (using a pointer into a section of RAM?) but it seems surprising that it needs around 2,200 PIC instruction cycles to do that.

Unfortunately, for my current project(s), winding up the clock speed is not a satisfactory solution. Firstly, the power consumption rises and it's a battery-powered 24/7 application. Secondly, I'm trying to sandwhich a small amount of code (counting each pulse on a pin) in betweeen receiving serial bytes (from existing commercial hardware). The rate is 1200 baud so SERIN only permits doubling the clock to 8MHz. But to be fair, the real culprit is that Microchip "forgot" to put an XOR gate (to optionally invert the serial data) on the hserin pin (20M2), so I can't employ the serial hardware.

Cheers, Alan.
 

mrburnette

Senior Member
<...>
The PICAXE has always put efficient memory usage over speed for its primary commands so the memory optimised solution is to step through the sequences to obtain the result required, and then continue stepping to find the end of the command itself when done. It's not a bug or flaw but a design choice; the usual trade-off of memory size versus speed.
<...>
For a product that developed and evolved from the limited resources of the early PIC, I think that decision was paramount in the PICAXE success and acceptance.
However, with so much more flash memory available in the PICs of today, perhaps it is time to evolve to a non-compressed variable/value storage; implementing a more standardized "C-ish" definition of variables and constants.

- Ray
 

graynomad

Senior Member
I get all that, it's pretty simple stuff, I was only commenting on the "fact" that the LOOKDOWN function takes the same time regardless of the position of the found item which does not make sense.

If the line

LOOKDOWN b3,("0123456789ABCDEF"),b0

is executed with b3 = 0 it should be much faster than if b3 = 15.

Obviously I'm not privy to the code but if you scan a list looking for a match you exit when that match is found. The earlier you find it the earlier you exit.

I don't have any hardware to test this, but is that the case?
 

AllyCat

Senior Member
I get all that, it's pretty simple stuff, ....... but is that the case?
Hi Rob,

With respect, no you don't, as it isn't ...... and no it isn't the case. The code has to determine where to exit TO.

As Hippy explained, having found a "match", the interpreter still has to continue decoding the rest of the string to "find" where the next instruction starts, because of the tokenised (variable length) format of the data.

Certainly I doubt if variable-width tokens make much sense with modern PICs, so I tend to agree with Ray that RevEd "should" adopt a simplified structure. But we are in the realm of Legacy Components, Educational Authority requirements and perhaps the protection of Intellectual Property.

Cheers, Alan.
 

hippy

Senior Member
If

LOOKDOWN b3,("0123456789ABCDEF"),b0

is executed with b3 = 0 it should be much faster than if b3 = 15 ... but is that the case?
No, it's not the case.

Not sure if you meant LOOKDOWN or LOOKUP but the principle is still the same for both so it doesn't really matter -

LOOKUP b0, ( "0123456789ABCDEF" ), b1

under the hood is effectively ...

If b0 = 0 Then : b1 = "0" : End If
If b0 = 1 Then : b1 = "1" : End If
If b0 = 2 Then : b1 = "2" : End If
:
If b0 = 15 Then : b1 = "F" : End If

and -

LOOKDOWN b0, ( "0123456789ABCDEF" ), b1

is ...

If b0 = "0" Then : b1 = 0 : End If
If b0 = "1" Then : b1 = 1 : End If
If b0 = "2" Then : b1 = 2 : End If
:
If b0 = "F" Then : b1 = 15 : End If

Both very similar and both encoded to do most of the work in the firmware. Thus the time taken is the same no matter which is matched.

There could be a GOTO executed whenever a match is found which skipped to the end of the command, but there isn't. It could exit sooner but doesn't. That's another design / implementation decision.

Should one be added ? For 'bale out quicker is best' views the answer tends to yes, but others would argue that having timing altered depending on the data values used is a negative, that more determinism in timing than less is better. A particular case is if code is debugged using the first match value you don't find it slows down until more realistic data is used which matches later, and then you might have to rewrite the code and/or adjust timing to match every case if you do want deterministic timing.

One could add additional FAST-EXITING-LOOKUP/LOOKDOWN commands but that uses more firmware, alters a number of things, and brings in 'which should I use' questions, potentially makes things more confusing than clearer for some programmers. There's also the question of does it actually help anyone, is it worth it and are there other or better ways to do that?
 

hippy

Senior Member
Certainly I doubt if variable-width tokens make much sense with modern PICs, so I tend to agree with Ray that RevEd "should" adopt a simplified structure. But we are in the realm of Legacy Components, Educational Authority requirements and perhaps the protection of Intellectual Property.
There's also the old adage of, "if it ain't broke; don't fix it", and good reason for it. I'm sure everyone has made some change or other that should be simple but has brought their world crashing down on them.

Perhaps variable length tokens might not appear to make much sense but for PICAXE they've historically proved their use and it is 'does it work' not 'how it does it' which is most important.

That's not to say change can't happen, won't happen, or won't be considered, but any change involves a lot more than just doing it. There is, as noted, a legacy issue and any change can cause problems for some PICAXE users, some which may not have been predicted, and sometimes more than could be expected. Not everyone sees change said to be for the better as an improvement, particularly if what they had worked, and that stops working exactly the same.
 

graynomad

Senior Member
With respect, no you don't, as it isn't ...... and no it isn't the case. The code has to determine where to exit TO.
the interpreter still has to continue decoding the rest of the string to "find" where the next instruction starts,
That's a design decision, it is not ordained, a simple indicator to the next instruction would save the traversing of a list for no good reason.

And this is simple stuff, the author of a certain well-known single board computer wrote the entire BASIC interpreter in 7 days. The implementation of LOOKUP/DOWN should be half an hour's work at most, maybe 40 minutes if it's written in assembler.

As hippy said

There could be a GOTO executed whenever a match is found which skipped to the end of the command, but there isn't. It could exit sooner but doesn't.
@hippy
As you say the current design allows the instruction to have deterministic timing and that's valid, it's a design decision with pros and cons but it works and that's all that matters.

I'm sure everyone has made some change or other that should be simple but has brought their world crashing down on them.
Anyone that's done more that a few days programming :) The old programmer's cry "But I didn't change anything" springs to mind.

Yes changing the timing of the LOOKUP/DOWN commands may break existing code, that of course means that said code was not written well as relying on the timing of an interpreted command is bad form. But I agree that it's not broke and doesn't need to be fixed and you certainly don't need the agro of introducing bugs into 1000s of existing programs.

I was simply trying to determine how things worked, and now I know.
 

westaust55

Moderator
Incidentally, I've now compared two methods of ASCII Hex to Decimal conversion and Lookup b3,("0123456789ABCDEF"),b0 takes about twice as long, but is three times larger (24 v 8 bytes) compared with my preferred b0 = b3 / "A" * 249 + b3 - "0". However, explaining how that works to a novice may take some time. ;)
Another alternative to the above lookup statement which may be considered is:
Code:
b0 = b3 + $30
IF b0 > $39 THEN
   b0 = b0 + 7
ENDIF
Untested on speed, but using 12 bytes less than the LOOKUP version
 

AllyCat

Senior Member
Hi,

For ASCII to Decimal conversion I believe I should have quoted LOOKDOWN not Lookup. A "one-liner" to replace LOOKUP for Hexa-Decimal to ASCII conversion could be b0 = b3 / 10 * 7 + b3 + "0" . These one-liners sometimes may be marginally slower (a few tens of us) than the conditional (IF...) version (probably because of the division and multiplication operations) but appear to be significantly more compact (a half to 2/3 the number of bytes). However, I'm impressed to hear that for example LOOKUP b0,(b1,b2,b3),b4 is permitted, now to try to think where I might use it. ;)

Fortunately the LOOKUP/DOWN instructions seem to execute about twice as fast as hippy's "under the hood" version. If PICaxe Basic really does encode them just as a string of conditional statements then all the additional "Fast Exits" could make the code much larger, but if it uses a "Loop" structure, then only one additional Exit would be required. I do agree that "constant" (predictable) execution times can be beneficial, so it's a pity that this doesn't apply to (any of?) the "branching" (conditional jump) instructions. Here are some formulae that I estimated recently:

ON b0 GOTO... = b0 * 240 + 1000 us (b0 >= 0)
ON b0 GOSUB... = b0 * 220 + 5100 us Including RETURN (b0 >=0)
SELECT : CASE ... = b0 * 1600 + 400 us (b0 >= 1)

It could be argued that the M2s have been slightly "broke" because of the significant increase in the execution times of GOSUB and RETURN instructions compared with previous devices. I can imagine that some well-structured (i.e. using subroutine calls) but "time critical" code (e.g. processing data inbetween received SERIN characters) written for pre-M2 devices might "fail" if ported directly to an M2. However, to be honest, I doubt if I could/would have tolerated the limitations (memory size, etc.) of pre-M2 devices.

Cheers, Alan.
 
Top