String vars?

RogerTango

Senior Member
With the ability to accept serial in, I see a wide range of possibilities. Some of which would need the support of string vars, such as LEN LEFT RIGHT and MID.

But, I do not see any such support. Am I missing anything?

Thanks!
Andrew
 

inglewoodpete

Senior Member
You will have to write all your string handling routines yourself. Due to the memory limitations of the 08M, 14M and 20M they are generally unsuitable for all but the most rudimentary string handling.
 

RogerTango

Senior Member
Ive figured (an untested as of yet) way to do LEFT RIGHT and MID, but not sure how I can accomplish LEN.

Any suggestions would be welcomed.


Andrew
 

hippy

Technical Support
Staff member
There are three ways to do string handling ...

1) Terminate the string with a specific value, $00 being the commonest value.

2) Prefix every string with a count which indicates how many characters are in the string

3) Treat a string as a data item which contains a pointer to the bytes of the string and a count of how many characters there are ( 2 is a subset of this, the pointer always points to immediately after itself so isn't needed ).

Each has its advantages and disadvantages. Many languages which implicitly support strings use (2) or (3), eg Visual Basic, those which use arrays of characters will usually use (1), eg C. Some languages support both. To implement LEN for (1) means having to count how many characters until the terminator is found, for (2) and (3) it's a simple case of reading whatever the count of characters is.

Fixed strings are a special variant of (1), where the terminator pads out the string to the size the string would be. LEN for that means going to the end and determining how much shorter the string is than expected.

The problem for PICAXE with strings is that memory to hold and manipulate them is quite small. It also needs quite a lot of overhead for the code to create generic string handling routines and, because of the nature of the PICAXE Basic language, it's not easy to do this in an elegant way.
 

hippy

Technical Support
Staff member
Here's one (untested) example of determining LEN for strings stored with a $00 terminator ...

Code:
MainProgram:
  Gosub SetB0ToHello
  Gosub LenB0ToB1
  SerTxd( "Len(b0) = ",#b1 )
  End

SetB0ToHello:
  Poke $C0,"H"
  Poke $C1,"e"
  Poke $C2,"l"
  Poke $C3,"l"
  Poke $C4,"o"
  Poke $C5,$00
  b0 = $C0
  Return

LenB0ToB1:
  b1 = -1
  b13 = b0
  Do
    Peek b13,b12
    b1 = b1+1
    b13 = b13+1
  Loop Until b12 = $00
  Return
 

RogerTango

Senior Member
Thank you Hippy, you have been very helpful.

I hope to have some proto boards made as soon as I finialize the process, and Ill be doing some serious development.

I hope to have some projects to share with the community soon!!!

Im so excited... the AXE is FUUUUN!

Andrew
 

Jeremy Leach

Senior Member
In terms of a simple system ....String handling functions are all about counting and comparing and indexes, so if I was writing functions in basic I'd probably use an approach like this:

String Definition Table
Have a RAM or EEPROM lookup table containing string definitions. Call it 'RAM_StringDefinitionTable'.

Assuming all strings are stored in the same place (you could make this more complex by allowing different places), then the definition for a string would simply be it's start and end address. For instance, suppose all strings are stored in the Scratchpad of a 28X1. So a string is defined by the start and end address in the scratchpad.

The StringDetailsTable holds all the string definitions as a series of 2-byte Definition records (1st byte is the start address, second byte end address of the string).

Lets say you refer to strings by their 'StringID', which is really just their record number.

Defining strings
You need to write a little routine ...lets call it 'String_Define' to define a string. So if you have a block of bytes that you want to define as a string, you call this routine and give the start and end address, and also the StringID. The routine then loads the start and end into the relevant record of the table.

String Functions
The routines you write all need to refer to this StringDefinitionTable. For instance, LEFT (let's call it 'String_Left') ...

You need to pass the String_Left routine the StringID, and the ByteCount. The routine returns a new string, which is ByteCount leftmost bytes of the string. By 'return a new string' I mean a String definition record is defined in the StringDefinitionTable. It might be an idea to reserve a record in the table purely for the result of any String Function. For instance StringID 10, could always be used to hold the result.

String_Mid would be similar, and you pass the routine the StringID, StartByte and ByteCount. And then return the result string details to StringID = 10. In fact the String_Left and String_Right routines are just special cases of String_Mid, so really you just need to focus on the code for String_Mid.

You could even have functions to search for one string in another string, and find/replace etc etc !



I don't think the code for these routines would be too long.

PS: Don't get thinking I'm using any standard names when I talk about StringDefinitionTable ....you just make up names and a system that makes sense to you ...that's why it's all good creative fun ;)

EDIT: here's some code based on this approach. Untested. Note this approach doesn't actually move any bytes around at all. If you want to do that you'd need to modify it as required.

Code:
'DEFINITIONS:
'============

'String Variables:
Symbol	StartAddress		= b0
Symbol	EndAddress			= b1
Symbol	StringID			= b3
Symbol	ByteCount			= b4
Symbol	Index				= b5

'General variables:
Symbol 	Address			= b6


'Constants
Symbol	RAM_StringDefinitionTable = 80
Symbol 	ResultID			= 10


'SUBROUTINES:
'============

String_Define:
	'Populates a record in the String Definition Table
	
	'ON ENTRY: 	String_StartAddress = the start address
	'		String_EndAddress   = the end address
	'		StringID		  = the ID of the string to define. 0 to 9.
	
	'ON EXIT:   The record in the String Defrinition Table relating to StringID is defined.

	Address = StringID * 2 + RAM_StringDefinitionTable
	Poke Address,StartAddress,EndAddress
Return

String_MID:
	'Returns a string containing ByteCount middle bytes of the specified string.
	
	'ON ENTRY:	StringID		  = the ID of the source string.
	'		Index		  	  = the position within the string to start from.
	'		ByteCount		  = the number of bytes to extract.

	'ON EXIT:	The ResultID string holds the middle bytes.
	
	Address = StringID * 2 + RAM_StringDefinitionTable
	Peek Address,StartAddress
	StartAddress = Index + StartAddress
	EndAddress = StartAddress + ByteCount - 1
	
	Gosub String_Define
Return

String_LEFT:
	'Returns a string containing ByteCount leftmost bytes of the specified string.
	
	'ON ENTRY:	StringID		  = the ID of the source string.
	'		ByteCount		  = the number of bytes to extract.

	'ON EXIT:	The ResultID string holds the leftmost bytes.


	Index = 0
	Gosub String_MID
Return	

String_RIGHT:
	'Returns a string containing ByteCount Rightmost bytes of the specified string.
	
	'ON ENTRY:	StringID		  = the ID of the source string.
	'		ByteCount		  = the number of bytes to extract.

	'ON EXIT:	The ResultID string holds the rightmost bytes.
	Address = StringID * 2 + RAM_StringDefinitionTable + 1
	Peek Address,EndAddress
	StartAddress = EndAddress - ByteCount + 1
	Gosub String_Define
Return
 
Last edited:

boriz

Senior Member
I recommend you have a look at the I2C docs for the 18X (Help > PICAXE – 18 datasheets > I2C tutorial). With an additional 8 pin device (24LC65 or 24LC64) you can have a massive 8K of EEPROM to play with. I have just bought five of these on ebay for £2.50 + £1.00 postage.

Anyone know where I can get some cheap I2C RAM?
 

hippy

Technical Support
Staff member
Tech-supplies used to sell a 256x8 I2C RAM. Not sure of the stock code.

A big issue with strings is when they change. This usually dictates some form of dynamic memory handling plus 'garbage recovery' to reclaim unused space, plus being able to determine which strings are no longer used. To move, copy or change a string involves quite a bit of work and data structure which a compiler and run-time library will usually hide. It all looks deceptively simple in a high level language, Today$ = "Year "+Right$(Date$,4), but under the hood there's a lot going on.
 

moxhamj

New Member
I've just written all the string handlers in OshonSoft basic for the Z80 compiler, as all that program had was numerical arrays. I used 'picaxe' variables like b0-b13 and w0 etc because I've started to think in picaxe basic (sad, eh?). I'll maybe post it when I get home.

Hippy's thoughts are most helpful. I went for straight 32 bytes reserved for each string (when a DIM instruction is encountered), and the last byte is the length. So LEN is easy. Dynamic allocation of string space is how microsoft's original MBASIC works, and is great till your game suddenly pauses for 30 seconds while all the strings are sorted out.

So if you want speed and no weird pauses it kind of implies using lots of ram space. Maybe there is a smarter algorithm out there.

Anyway, where that leads is to consideration of I2C ram chips. EEPROM is not the best for strings as EEPROM eventually wears out. I've been playing recently with CP/M with 64k of program ram space and 512k of ram disk space. So a 256 byte ram seems a bit wimpy!

I wonder what it would take to interface a big ram chip to a picaxe? Eg a 512k ram needs 19 address lines and 8 data lines and one line for read and one for write. It would need lots of output lines, eg HC595s or latched HC373s. And maybe some 241s or 245s to steer the data into the ram or out of the ram into the picaxe inputs. And this will gobble some program space, so it would need one of the picaxes with lots of program space. 18X might be a bit short.

If you use a DS1210 you can make the ram permanent with battery backup and these chips are very easy to use.

So on the upside, you could have a small book stored away if you want.
 

krypton_john

Senior Member
It would be kinda cool if there was some way to extend PICAXE basic with your own C and ASM functions. That way you could write low level functions to do stuff like string manipulation, parsing gps sentences etc rather more easily and efficiently.
 

boriz

Senior Member
Like an inline assembler?

I’ve suggested this before, but I’m told it’s not possible with PICAXE. Not sure why though. It would definitely be on the top of my wish list.
 

westaust55

Moderator
I think you will that there not find many (maybe read “any”) i2c RAM chips available.

I have recently purchased some 8 kbyte F-RAM chips through Farnell at reasonable prices. F-RAM are capable of far more write cycles than EEPROM (around 100 Million from memory).

You will need to buy say the FM24C64 which is the 8 kbyte chip.

DO NOT buy the FM24C16 2 kbyte chips.

The reason is that the F-RAM and 24LCxxx series EEPROM have the same slave addresses. And if you use a 24C16 EEPROM or a FM24C16 F-RAM chip they do not use the 3 address pins and you can only have one memory device on your i2c bus.
 

moxhamj

New Member
Strings are possible on pic chips. Just need the right chip eg the 18F series. http://www.picbasic.org/proton_lite.php#download but I'm only listing that link as an example of where picaxe needs to be heading, and where it could easily go given the hardware already exists. Left$, Right$, Mid$ etc are all there. But you have to fork out some big $ to get that program. I much prefer the picaxe model where the program is free and there is a little cost added to each chip.
 

Jeremy Leach

Senior Member
Coming back to strings, it's a big topic, and Andrew hasn't really stated what he wants to use strings for;) With limited picaxe resources it might be a matter of crafting the code for the specific application in mind, rather than try to implement a complex generic string handling solution. Also there might not be any need for external RAM or EEPROM - I mean, I've done string stuff on picaxes for menu displays just using internal EEPROM.
 
Top