Hacking in BASIC
nemo (145) 2546 posts |
I sometimes hack about in BASIC for a bit of fun, and usually forget what I did.
However, this one is so useful one might want to consider it for a future release: STRING$(address,param[,maxlen]) param: b0-7 single terminator chr b8 no single terminator chr (ignore b0-7) b9 any ctrl terminates b10 CR or LF terminates b11 any top-bit-set byte terminates or if negative, maxlen (and no 3rd arg) In other words: STRING$(addr,T) reads a string from memory, terminated by CHR$(T) STRING$(addr,-L) reads a string of LEN L bytes, including CRs STRING$(addr,T,L) reads a string terminated by CHR$(T) or LEN L STRING$(addr,256,L) reads a string of LEN L, no terminator STRING$(addr,512) reads a ctrl-terminated string STRING$(addr,512+127) reads a string terminated by a ctrl or del STRING$(addr,1024+0) reads a string terminated by 0, LF or CR STRING$(addr,1024+13) reads a string terminated by CR or LF only STRING$(addr,2048+512+127) reads a string made up of ASCII chrs only STRING$(addr,32,12) reads a sprite name or drawfile group name STRING$(addr,-4) reads a FourCC STRING$(addr+1,-?addr) reads a Pascal string
And |
Alan Adams (2486) 1149 posts |
That would be a lot tidier than the various tricks using side effects of SWIs. |
Steve Drain (222) 1620 posts |
Fun. ;-) For the most part I now hack around using the constraint of just BASIC. On modern machines, for most purposes, machine code is too much hassle. ;-)
Sometimes useful:
then:
The use of What would be useful is plain
For comparison, Basalt has This returns up to a control character, or if This is not as comprehensive, but it is only intended for RISC OS SWI buffers. In BASIC I can do the equivalent |
nemo (145) 2546 posts |
Yes, arcane magic runes are not to be encouraged, especially considering some of them have moved about (DIMLOCAL I’m looking at you). I didn’t mention
To re-throw the current error without disturbing the line number? That would be very good. i.e. ONERROR:PROCglobal_error_handler ... PROCsomething ... DEFPROCsomething LOCALF% F%=OPENIN"file_I_need_to_close":IFF%=0:ENDPROC LOCALERROR ONERRORLOCAL:RESTOREERROR:CLOSE#F%:ERROR explodehere Yes, the misreporting of error lines due to re-throwing errors from LOCALERROR handlers is a problem. Perhaps though,
I prostrate myself in apology for not having used Basalt or even kept up with developments. Assuming you’re using the last slot of GARBAGE as “and anything bigger than that”, but there’s a lot of places where the ‘fiveness’ of strings is implicit, how are you longifying them? |
Clive Semmens (2335) 3276 posts |
This, exactly. I’ve not even updated any of my old machine code to run on anything post StrongARM. I’ve rewritten some of it in BASIC and it runs quite a bit faster in BASIC on the Pi than it did in machine code on a StrongARM RiscPC, but most of it was specific to things I was doing in the 90s. |
nemo (145) 2546 posts |
I recall the original frothing response to the release of the Archimedes – “it runs BASIC faster than machine code on its competitors” is how I think PC World described it. |
Clive Semmens (2335) 3276 posts |
Something like that. I think I have that issue in a box somewhere. I’ve not kept many PC Worlds, apart from the ones I’ve got things in myself – but I certainly kept that one for a while, perhaps still. One day there’ll be an application I want to write that’s worth bothering with writing at least a part in assembler, or even getting to grips with C. Maybe. |
Steve Drain (222) 1620 posts |
I think only that handful at the top of the arguments have been shuffled, but you are right, any use of them is giving a hostage to fortune. Still, we have had 30 years of stability so far. ;-) The use of variables to hold the magic runes makes it simpler to change should the world shift on its axis. Some other runes are: &857C and &8580 which I use for private variable lists and Basalt implements as LOCAL LIST ` or _
I think so. It is necessary to implement nested error handling.
That is anything bigger than 255 bytes.
It is a while now, but Richard Russell and I hit upon very similar ideas to do this at the same time. He has a library for BB4W for strings longer than 64k and I wrote one using a different implmentation, which I then put into Basalt. In essence, a 5-byte string, not a SIB, can hold an address and a byte indicating that it is to be treated as a long string – for which I coined the term ‘strand’. The address points to a block claimed from a heap, which itself has the length information. Such strands can be concatenated with other strands and strings, within the 255-byte limit. When used only where the they are recognised, you can have a whole system that lives happily, resolving concatenations to single strands where required. In Basalt, strand variables are indicated by being enclosed in () and very nearly all the keywords that use strings recognise them. My BASIC library has to use PROCs and FNs. The implementation detail is omitted for sanity’s sake. ;-) |
Rick Murray (539) 13840 posts |
Now that UTF-8 is a thing and command lines can easily exceed the old 255 character limit, perhaps it’s time that such functionality was baked directly into BASIC? Then we can be free of string length issues. Especially given… Well, refer to the bottom of this post, the OS_GetEnv part. |
GavinWraith (26) 1563 posts |
Nice coinage. It appeals to my hypertrophied verbal lobe. Of course string means a great many different things. But that aside, a coinage that seems to have fairly wide adoption, is that of a rope . A rope is defined recursively as either a string or a list of ropes. Equivalently, a rope is a tree whose leaves are strings, or more informally, a string with holes. This is a very useful datatype for manipulating text without having to move strings around. |
nemo (145) 2546 posts |
OIC. So not a new l-type, v-type or STRACC usage. A contextual extension in effect.
A similar thing had to be done in PostScript to get past implementation limits. It is about time that Basic gained ‘longstrings’, yes. The irony of Rick ‘I lacked the time to write a short one’ Murray asking for longer strings is not lost on me.
Thanks to Rick reminding me how much I love LOLCODE, I should point out that there they’re called ‘yarns’. |
Steve Drain (222) 1620 posts |
I work outside the BASIC code, but if I found myself implementing long strings I have an idea that might work. The 5-byte SIB is a length byte and a pointer. In a zero length string both of these are zero. Let that be so, but if the length byte is zero and the pointer is not, then this is to a long string. This would require an extra check for zero length, but would otherwise be robust, wouldn’t it? This presupposes a flexible memory allocation system, not the BASIC heap. |
Martin Avison (27) 1494 posts |
I suspect that if the code is: |
Steve Drain (222) 1620 posts |
Yup! |
nemo (145) 2546 posts |
I think safer is the assertion that string allocations are aligned, so you have a couple of useful bits even without changing the granularity of allocations. |
Steve Drain (222) 1620 posts |
Hmmm. Haven’t there been enough hiccups in RISC OS from odd bits in addresses being used as flags? And who is to say those addresses will always be word-aligned? Just askin’. ;-) |
nemo (145) 2546 posts |
String allocations are word aligned for many very good reasons, not least of which that they are always a whole number of words! Today’s interesting syntax experiment: FOR a = 1TO3 CASE a OF WHEN1:PRINT"WHEN can ";:ANDCASE99 WHEN2:PRINT"But doesn’t have to ";:ANDCASE99 WHEN3:PRINT"If you don't want it to." WHEN99:PRINT"drop through like C." ENDCASE NEXT Produces
|
Steve Drain (222) 1620 posts |
How about: ERROR LOCAL <try> REM code to execute WHEN 25 <catch> REM code for error 25 WHEN 50 <catch> REM code for error 50 ERROR RESTORE REM pass error to higher nested level <continue> REM code after |
nemo (145) 2546 posts |
With DEFPROCusing_LOCALERROR_and_LOCALERR_to_do_a_catch LOCALERR:REM ERR is now zero LOCALERROR ONERRORLOCAL:REM this space intentionally empty CASE ERR OF WHEN0:REM the thing we are going to "try" blah WHEN25:REM handler for ERR=25 complain WHEN50:REM handler for ERR=50 protest OTHERWISE handle the error nicely ENDCASE REM Yup, I’d like to do a RESTOREERR here, but ENDPROC will have to do. ENDPROC Just thinking aloud here. Perhaps Thinks I’ve often wondered about the dead space in a CASE: CASE something OF These lines are never run you can put anything you like here it doesn't even need to be REMs WHEN1:REM The 1st case ... One could imagine something along the lines of CASE ERROR OF REM This is the code to "try" REM There is an implicit LOCALERROR around it REM and if we reach WHEN or OTHERWISE we REM RESTOREERROR and proceed to ENDCASE REM without collecting £200 WHEN99:REM HOWEVER, if there’s an error, it becomes REM an effective CASE ERR OF OTHERWISE:REM With the general case at the end ENDCASE REM Implicit RESTOREERROR has occurred. It would require But when I want to do a “try” I just stick it in a PROC with an ONERRORLOCAL. What would one ever want to “try” that is likely to error which couldn’t have been done with an |
Clive Semmens (2335) 3276 posts |
I’ve never thunk about using that space, but you could put some code in there and jump to it with a GOTO… (the biggest evil grin smiley you ever saw) Then presumably jump out of it again before hitting the first WHEN…hmmm…what happens if you hit a WHEN without encountering a CASE first? Not the kind of thing I’ve ever tried. |
Steve Drain (222) 1620 posts |
Perhaps I should have put a big smiley after my post. Basalt has had that nested error syntax for years, but it is translated to: ERR=0 REM perhaps !_ERR=0 - without Basalt you need magic LOCAL ERROR:ON ERROR LOCAL:CASE ERR OF WHEN 0:<try> WHEN <err>:<catch err> ... OTHERWISE:RESTORE ERROR:ERROR ENDCASE:RESTORE ERROR <continue> Your In the code above that single Call it as This works in RO5 BASIC, but requires a much more elaborate way to get access to the stack in RO4/6 BASIC. Of course, a tiny bit of assembler does it easily, but there is no fun in that. ;-)
The classic example is negative square roots, but I admit that I have never written a program where it was essential to have nested error handling. Even so, if it existed it might be exploited. Why do other languages have it? |
Steve Drain (222) 1620 posts |
A bug or feature of the interpreter is that if a CASE a OF WHEN a <do something> IF b THEN <do something else> ELSE WHEN <do the last thing> ENDCASE Basalt translates an You can do something similar with an |
nemo (145) 2546 posts |
Clive asked
It’s one of a number of multi-line REMs in BBC BASIC. And as Steve says, as long as it’s not at the start of a line (even a preceding colon will do) it acts as i.e.: ... WHEN 27 IF ignore_this_case THEN WHEN IF go_no_further THEN :WHEN :REM <<< This one needs the colon or it’ll confuse BASIC ENDIF go_further ENDCASE Steve said
I see what you mean about the ‘unhandled error’ case. And anyway, It feels slightly me-too though, IYSWIM. I think if it had been there from day one it would still be little used. Does Basalt fix any of the many bugs?
|
Rick Murray (539) 13840 posts |
What!?! |
GavinWraith (26) 1563 posts |
I am puzzled by the second LOCAL. |