Basic internals
Pages: 1 2
Richard Keefe (1495) 81 posts |
Hi I’m not sure if this is the right place to ask? I have a module that used to allow the declaration of basic variables (using * commands) by directly modifying basics internal structures but on my Iyionix 5.18 it no longer works (can’t remember on which version it did but definitly at xmas 2004 it did) it doesn’t crash it just fails to declare the variable as basic cannot use it. Has anything internal changed and is there a simple way/command to see what variables etc basic has currently defined. So I can try to fix it. Many thanks |
Sprow (202) 1158 posts |
Keyword ‘LVAR’ in BASIC might be what you’re thinking of, lists variables. |
Steve Drain (222) 1620 posts |
There should not be any changes to BASIC itself to cause this, but there might be some problem with the *commands. Could you be more explicit about the module and how it is used? LVAR is a command that can only be used at the > prompt, but it does list all existing variables. |
Richard Keefe (1495) 81 posts |
The module/commands are used as follows (this is pseudo code) *setbasref Point1 … DIM code% 64000 for pass=0 to 2 step 2 P%=code% [OPT pass cmp r0,#7 … Where the Addresses of Point1 & Point2 are set by the module when the setbasref command is executed (from an internal table). 3 variables are read by the module start-up there values are: |
Steve Drain (222) 1620 posts |
I still cannot see where the problem might lie. Perhaps you could raise this in c.s.a.programmer or contact me directly at steve[at]kappa[dot]me[dot]uk. From the above it seems that the module contains code that is to be called from a BASIC program. This is something I am very familiar with. ;-)
How does it do this? Does it just assume them (probably safely, but illegaly) or does it have a way to interrogate BASIC?
From CALL, r8 contains ARGP, the arguments pointer, which is &8700 and is unlikely to change; r11 contains STRACC, the string accumulator, which is at &8100. The other value, &9368, would be in the current BASIC program or the heap, but it does not relate to the OS_Module SWI, which possibly returns the address of the BASIC module. &9368 might be the address of a BASIC variable. My initial guess is that the module is being tricky/illegal when creating the BASIC variables, and it has been caught out by some unforeseen change. There is a legal way to use a *command from within BASIC to create a variable holding an address, but it is not at all obvious. It does not involve getting values during initialisation, but does require claiming a vector. An alternative would be for the module to create a system variable containing the address of the code table, for the BASIC progam to read this and then assign table values to variables in the normal way. |
nemo (145) 2556 posts |
The BASIC function table is also available via OSCLI I’m pretty sure, so the module should be able to do this kind of thing legally. That doesn’t mean it does though. |
Richard Keefe (1495) 81 posts |
The table is in fact being correctly updated. With Steve’s help I have been able to diagnose a lot more – the module appears to working for example if setup a variable “This” is gets a value of 67108860 from the module, either by using the module or I can assign it. Both fail to assemble with “Bad address offset” when they get to the line: Has the error generate code been changed? This used to work under Basic 1.35. |
Jeffrey Lee (213) 6048 posts |
The B & BL instructions have a range of +/-32MB. So if you’re trying to branch from an address in application space (e.g. somewhere below 1MB, unless you’ve got a big program) to an address of 67108860 (pretty much bang on 64MB) you’re going to be out of luck. I’m guessing the module must be declaring the variables at the high end of the wimpslot. So you can probably get it to work if you make sure the wimpslot is under 32MB when you run the code. Or for a more reliable option, load the address into the PC instead, e.g. instead of a BL do the following: (... code here ...) MOV r14, pc LDR pc, foo (... more code ...) .foo DCD This Note that because the PC is always 8 bytes ahead of the instruction being executed (well, apart from when it would be 12 bytes ahead when STR’d/STM’d on old machines), the MOV r14, pc sets up the return address to be just after the LDR. |
Richard Keefe (1495) 81 posts |
The code that this generates is not supposed to be executable code – it is just supposed to be valid code. The reason for it is so that multiple basic programs can be assembled seperately and saved as basic object files and then linked together – with the high addresses (67108860 – (n x 4)) – being used as tags to tell the linker what references need to be resolved in the final linking process. IE |
Sprow (202) 1158 posts |
It looks like BASIC 1.39 added a range check on branches, though it might have been earlier as it looks like the sources didn’t make it from Castle back into CVS for a few years. |
Richard Keefe (1495) 81 posts |
Is it possible to make the BL address check(s) feature optional – I don’t really want to have to build a separate version of basic – so that my module still works – what is the best way (env variable, *command, swi …)? |
nemo (145) 2556 posts |
There are a lot of OPT bits left… I made a version that has an OPT bit to avoid the call to OS_SynchroniseCodeAreas – when assembling data or code that won’t be called it’s not needed . Especially in the case of data it’s an unnecessary slow-down, eg: [OPT18:&7:&L%:&0:&0:&0:&0:&&D400F0:&-1:&24:&0:&0:&0:&0 &32:&1:&44:&108: &I%:&60:&152: &C%:&68:&152+I%*60: &0:&48:&L% &-1:&O%!44:&0:&0:&0:&O%!56:&O%!60:&O%!64:&O%!68:&O%!72:&O%!76:&0:] Having said that, can’t you use large negative values that are still in the legal range? What’s the nature of the range check – are the values too large to be encoded correctly, or is it a case of “Nanny knows best” (like the irritating negative-offset check introduced into SpriteExtend in RO4 which I had to work around). |
Richard Keefe (1495) 81 posts |
I’m not really sure as the code worked correctly under BASIC 1.35 and before? The B/BL has 24bits for the offset, How could Basic have encoded it before = 26bits? |
Sprow (202) 1158 posts |
I’m struggling to think how your external reference ‘tag’ was ever going to be unambiguous. Looking in the ARM ARM for B (and BL)
I can imagine Castle changed BASIC to fault them when the wimp slot grew > +/-32MB range could reach, earlier versions of BASIC would trample over the opcode bits and produce duff code. The ARM ARM says “If the byte offset is outside the range -33554432 to +33554428, use an alternative code-generation stategy or produce an error as appropriate”. Perhaps you could output a bitmap of “addresses that need fixing up” for the linker, or just AND your -ve offset with 0xFFFFFF. Alternatively, use one of the many thousands of invalid opcodes in the &E7xxxxxx range. |
Richard Keefe (1495) 81 posts |
The software was using the end of the BASIC 64Mb – so they would be highly unlikely [imposable] to be invalid with a max 1Mb code block size. I’ll try to reduce the module window to 32mb cf 64mb. I think the linker relied on the duff code. Or is it a sign issue as 64MB would have produced a negative number? |
nemo (145) 2556 posts |
I haven’t checked, but perhaps BASIC neither errored nor corrupted the opcode bits but merely truncated the offset and hence wrapped round? And at the risk of repeating myself:
|
Sprow (202) 1158 posts |
So now I’m confused again. You’re refering to the tools in the past tense suggesting you are unable to change them, but wasn’t the origin of this thread to update the tools? Is it the tools or the some program you assemble with the tools which you are attempting to modify?
That doesn’t make sense, because if it spilled into bit 24 it would corrupt a B into a BL instruction – how would the linker know which was required? You can’t corrupt b31-28 because they’ve got the condition codes, and you can’t corrupt b27-25 because then you’d probably mutate the B[L] into some other instruction. |
Richard Keefe (1495) 81 posts |
After more investigation – it is checking for bit 23 to be set, ie a negative number. The old BASIC code just took the (address >> 2) & 0×00FFFFFF and filled in the B / BL. I’ll modify the module to get the basic variable filled with the 0 – (address & 0×007fffff), and see if that works. |
Richard Keefe (1495) 81 posts |
I’ve modified the module to use appropriately compatable -ve numbers unfortunately there are two cases which are mutuely exclusive: BL TheOther In the New Case TheOther = -16777212, in the original case it was 0×1FFFFC the two lines of code produce different outcomes the BL data is correct but the EQUD data is wrong. I therefore need an SWI in BASIC to Enable/Disable the BL Address Checking (Keeping the Masking) – so upon 1st use my module can disable the check and upon complettion of the assemby re-enable the check – I’ve looked at it and the use of OPT bits would mean a lot of edditing. Who would be best to speak to about this? |
Kevin (224) 322 posts |
Richard you want the Allocate application |
Richard Keefe (1495) 81 posts |
Thanks for that, (Sorry it took so long to get back have had a bit of a bug). Have submitted my Allocate request for BASIC & BASIC64 – is there a recomended way of implementing OS SWI tables – or has it been done a number of ways? Or has a particular module done it the most compatable/ideal way? I was intending to add the following SWIs: BASIC<64>_GetVersion – Returns Primary Version, & Variable Format Version |
Sprow (202) 1158 posts |
I can’t help but think you’re making a whole lot of unnecessary pain here. My first suggestion would be to not bother – you mention having an Iyonix so must have at least 128MB of RAM. Since the Wimpslot can now be GB in size why not just put the sources into 1 big file and assemble it as one monolith? I could imagine that the source is split so that it’s a bit easier to maintain. That’s OK too, only the build step needs to chew the file as one monolith. If you look at the sources to !Alarm you’ll see one approach – the sources are split (at arbitrary points) to help readability and use FAppend (which is itself written in BASIC) to join them and subsequently TEXTLOAD them into BASIC. If you splash out a couple of bank notes you could buy the Official DDE which includes ‘objasm’ and ‘link’ which do exactly what you’re trying to do. The assembler, ‘objasm’ includes a powerful macro evaluator which should be able to implement pretty much anything BASIC can for variables (except perhaps sine/cosine lookup tables). For just chewing assembler, ‘asasm’ which is part of the GCCSDK is highly compatible with objasm, but free too. And lastly this all reminds me of a multi pass assembler I wrote to handle making ROM images for the BBC micro (where the amount of source code was larger than the available memory). To paraphrase it, at the end of each sub-source it output the current variable table (to floppy), then the next piece of sub-source read them in (assigning them a dynamically generated ‘LET’ statement or ‘EVAL’ probably). The beauty there was that when BASIC V came along it “just worked” since it was all written in BASIC and didn’t require any knowledge of grubby internals. Plus it was markedly faster than on a 6502! Of course with BASIC V you get access to a lot of environment via CALL, it’s worth looking that up in the user manual. But, in summary, I think you’re backing yourself into a corner when it’s probably not necessary. The ‘intra source variable passing’ problem was solved way back in the 80’s in the BASIC ROM user guide. |
Richard Keefe (1495) 81 posts |
I do already have objasm and all the other RiscOS dev tools – if converting the software had been a viable option then I would have done so. There are several hundred of basic files in the main application [50-400k each] and there are a number of sub-applications with 20-50 files. The total source tree is ~100Mb in size. I wanted to fix the problems for now and forever – I could just produce a personal version of BASIC with the section at issue commented out – but I’m thinking more of the long term and possable release of the module. If you want to contact me directly, as there are parts I do not wish to / cannot discuss in the open, my e-mail is: |
nemo (145) 2556 posts |
Crikey I’ve been doing this a long time. BASIC does indeed expose ARGP, the call table, the line pointer and even its stack via the call to OSCLI, but it has been a very long time indeed since the OSCLI caller’s registers were unmolested by the stuff in between. As it happens though, you can still find BASIC’s useful stuff on the stack, handily marked. On entry to OSCLI, BASIC has set up its registers as: R1 &BA51C005 R2 ARGP (&8700) R3 Line ptr (eg often STRACC+1 for *something) R4 Basic's R13 (so you can even grok nested flow control if you know the format) R5 The CALL block So R1 is magic and as discussed elsewhere R2 is always &8700 (sadly), so it’s pretty easy to find those two on the stack, and the next three words are as above. If you don’t fancy searching the stack, claim CLIV and note the registers down if R1=&BA51C005 and R5 is within the BASIC module. |
Steve Drain (222) 1620 posts |
It is possibly quicker to always copy the registers from CLIV and return, then check the values later in the command code. I owe Martin Avison this method, used in a handful of my modules. I did write a separate module to do this and make the values available, to reduce the number of modules hanging off CLIV, but we decided that this number was never going to be very large. |
Pages: 1 2