BASIC assembler help
Alan Adams (2486) 1149 posts |
I’m stuck – LDR R2,[R12,#flags] is reporting “Bad address offset”. R12 points to the start of my code and data. I tried moving the data section containing .flags to the start of the code, so the offset would be almost zero, and got the same error. What am I doing wrong, or do I need to tackle this another way? |
Kuemmel (439) 384 posts |
Hm…did you try inserting both the numbers you calculate by “hand” ? As 290 should be okay. But may be if you have “.flag” somewhere, the Basic assembler uses the address at “.flag”, which is may be “0×8000 + 290” …and that is of course invalid as not relative ? I often only hardcode the offsets from a base address in absolute values…if they are static…may be you post your code snippets so it becomes more clear @EDIT: or it would be solved if you calculate it in your instruction like “LDR R2,[R12,#(flag – start_adress)]” |
Julie Stamp (8365) 474 posts |
Put a flags% = 28 underneath countdown% = 24, make sure your .flags equd … is right after .countdown equd … and then do LDR R2,[R12,#flags%] — note the percent sign. |
Alan Adams (2486) 1149 posts |
This is where I get confused.
In this case the offset to flags is zero. I can get this to work by
I’m starting to see the problem – .flags is an address within the code, not an offset. |
Alan Adams (2486) 1149 posts |
This sounds like something I tripped over the other day. I set up a BASIC variable which was going to be the address of an entry point, so i could use CALL init%. I had b init at the start. It assembled as a branch to itself, or was interpreted as b init% instead. I changed the label to .initentry and used b initentry, which worked. It seems the assembler will use a BASIC variable ending % and strip the % before using it, if that makes sense, so labels and BASIC variables should not have names differing only by the %. |
Rick Murray (539) 13839 posts |
What you’re doing with… LDR r2,[R12,#flagoffset%] …is essentially loading a word from the address that is comprised of “R12 + flagsoffset%”.
So… Edit: Just read that you want to load so many words from “flags”. Well, in that case you want to set up the base pointer (the R12, or whatever) using ADR. See below. How about Which would be: R2 = load from (R12 + (Rx << 3)). Edit: Explanation of ADR coming right up! It’s also worth pointing out here that if you want to load a word from an address, you don’’t need to try to construct complicated methods, just use So… ADR R2, word_to_load LDR R2, [R2] ...some code... .word_to_load DCD &0 Of course, if you have multiple words there, you can then use offsets like This old document explains them all and also gives an example in C of what the different sort of memory accesses are doing: https://heyrick.eu/armwiki/LDR |
Alan Adams (2486) 1149 posts |
Thanks Rick, helpful as usual
I’ve been making extensive use of this throughout, but sometimes I get “lost in a maze of twisty little addressing modes, all alike”.
That looks like the version I need, but hadn’t discovered. It’s all a long way from the Z80. |
Rick Murray (539) 13839 posts |
Ah, there’s your problem. Should have grown up with the 6502. 😋 |
Alan Adams (2486) 1149 posts |
Well, I did write a 6502 disassembler in Fortran, and used it to extend the HP-IB bus functions in a Commodore Pet, which involved finding the ROM functions to drive and read the bus, and call then for extra capabilities. I also wrote a terminal emulation ROM for the BBC, which involved hooking into the vectors for the serial buffer to allow a 2K byte buffer. We were working through serial over Ethernet devices, and they would return a complete Ethernet packet of data after “stop” had been asserted. It was interesting though that while all the BBC buffers were 256 bytes, the calls for size, freespace etc all returned 16-bit values. Very handy. I still preferred the Z80 though – the ability to use the register pairs to access 16-bit addresses was far more comfortable than having to use page 0 addresses with a register offset. So back on topic: I’ve now got the callback handler hooked in, and it works, at first. As your code example did, I’m setting a marker to say there’s one pending. All is well, until I clear that marker in the callback handler, at which point I get a series of on-screen errors and a lockup. It does this if ALL that the handler does is to clear the marker and exit. If instead the handler writes a non-zero value, it doesn’t crash, so it’s the fact that the handler can be called a second time that’s causing the crash.
|
Alan Adams (2486) 1149 posts |
So after checking the code, I have got this, which still crashes, with “Undefined instruction at” and an address starting FC. I can’t be more precise because that’s immediately followed by more errors, including “Filer may have gone wrong”.
|
Theo Markettos (89) 919 posts |
How are you calling this code? Are you entering SVC mode yourself (SWI OS_EnterOS) or are you registering the function with an OS callback somewhere? In what memory are you assembling the code? If you’re putting the code in application space and registering a callback beware that, unless you’re singletasking/outside the desktop, application space gets swapped out at Wimp_Poll. So you register &9234 as your callback handler but when it gets called another application is swapped in, and so &9234 in a different application gets called. There’s a high probability that what’s there isn’t valid instructions and so it takes the Undefined Instruction exception. If you put the code in RMA or a dynamic area it should persist across applications being switched in and out. |
Alan Adams (2486) 1149 posts |
It’s registered using OS_AddCallBack, and the address is in RMA. The calling code is from two sources – one is TickerV, the other the common handler for 6 filing system vectors. There’s more detail in the Disc Activity Light thread. https://www.riscosopen.org/forum/forums/11/topics/16274?page=1 The memory is allocated thus: “XOS_Module”,6,,,size% ¸ ,,codespace%;flags% The callback is registered like this
|
Rick Murray (539) 13839 posts |
I’d be inclined to comment out all of the palette setting things and work on trying to get the callback mechanism working correctly. You can always plug more code in later. Start with the basic framework. Additionally, I’m not sure you’ll want to be returning errors. Who or what are you passing the error up to? But that’s for later. We need the callback code working first. BTW… DEF FNcallback When and where is P% being set? Are you sure this is being assembled into the right place? |
Alan Adams (2486) 1149 posts |
I did try that. All the code did was stack the registers, set a value into the pending flag, and unstack the registers (into PC). If I set the flag to 0, meaning allow another callback, it crashed. If instead I set it to 2 for example, the callback was only called once and was fine. I’ve just tested it again. With only one callback it’s fine. Second callback – Internal error, abort on data transfer at &2000…. which is lower than the RMA block Edit: If I allow repeated callbacks but only from TickerV, it still crashes.
There’s only one assembly loop. .callback is at address 20CB8418 E28F00A4 seems to be ADD PC,#164, which with the 8 byte offset on the PC looks right.
I was thinking it might get to the error handler of the BASIC calling program, or at least show up in Reporter. On reflection, I’m getting less certain about that. |
Alan Adams (2486) 1149 posts |
I’ve put the whole thing here, in case any masochist wants a look. |
Rick Murray (539) 13839 posts |
I think you’ll end up throwing an error at whatever happens to be the currently active task. ;-)
Ah. BASIC. My phone’s file viewer showed it to me in random Chinese. When I told it the correct character set, it was still gibberish. So… not tonight. I’m still whacked out from my lack of sleep on early Monday morning. https://heyrick.eu/blog/index.php?diary=20210315 [note: it’s really weird, even for me]
Have you compared the method with the one that I use?
I suppose this might be possible if you’re responding to TickerV. I’d suggest that the setting the flag to allow another callback ought to be the very last thing that you do. |
Rick Murray (539) 13839 posts |
Just a thought… Where are you getting the value of R12 that you are using to set up the callback? |
Alan Adams (2486) 1149 posts |
It’s closely based on your code, and I keep going back to yours to cross-check. I’m missing something, but I don’t know what.
At the moment it’s the ONLY thing that routine does.
I pass in the base address in A5 to R0 when calling init, copy it to R12 and use it throughout. I keep looking at the code to see whether anything could alter it, but it’s only SWIs, and they are all documented as not going near R12. I suppose I could stack it at the start of the section, and unstack it when needed. Not tonight though. The first error message usually (always?) lists an address starting &2000…. The code is assembled and loaded typically into 20CC….
I see what you mean. I generally wake up having a memory of having a strange dream, but I rarely remember any of it, or only tiny bits. I will call that a blessing then. |
Julie Stamp (8365) 474 posts |
I had a look, there are two issues,
|
Alan Adams (2486) 1149 posts |
Thanks Julie, no wonder I didn’t spot that. However I’m struggling to save R14_svc – if I put that as the register to push, Rick’s excelent wiki suggests adding ^ – however that doesn’t seem to work either. |
Rick Murray (539) 13839 posts |
It’s not a register, it’s R14 while in SVC mode. I kind of suspect you might need to switch to SVC mode, stash the register, call the SWI, restore the register, then switch back to IRQ mode (because R13 depends upon mode!). It’s fiddly. This is why I recommend people write stuff in C rather than assembler these days. ;-)
IIRC, that kind of works the other way around, allowing access to USR mode registers from a privileged mode. |
Alan Adams (2486) 1149 posts |
I’ve added the following code, which I used in my other assembler project. I can’t tell whether it’s working here, because the BASIC assembler is now deciding to forget labels between the first and second pass. WTF is that about?
|
Rick Murray (539) 13839 posts |
BANG! YOU’VE JUST BEEN ZAPPED BY ARMV8. Seriously, don’t touch bit four. Actually, nothing happens because you restore bit four before writing the register to CPSR rather than poking it directly. But it’s a good idea to get out of the habit of messing around with bit four (as we all do ;-) ). But all your base still belong to us, so nerr. |
Dave Higton (1515) 3525 posts |
It’s one of numerous reasons why it’s better to write in C these days. I echo Rick’s recommendation. |
Alan Adams (2486) 1149 posts |
and if my first A3000 came bundled with C I might have made the effort. And as it would have cost almost as much to buy as the computer did, that wasn’t reasonable. I don’t know whether it’s changed, but at the time C was regarded as a “write-only language” because it was completely unreadable. Another reason why I stuck with BASIC.
I did check that after reading your previouspost and came to the same conclusion. I think it’s code I copied from somewhere, but I’ll update it. Which still doesn’t explain why the assembler can’t remember labels between the first and second passes. Edit: while holding on my doctor’s reception phone queue for 40 minutes, I’ve found that last problem at least – I was using BASIC functions to try and make the assembler more readable, but one of the functions wasn’t being referenced insode the FOR/NEXT loop. It didn’t error on the first pass as that was merely collecting definitions. On the second pass it hadn’t found the definition. Meanwhile I’ve gone from 39th in the queue to 11th. Whoo-hoo! Last time I did this the call was disconnected by the phone provider after some time interval. We’ll see. |