Any ARM Debuggers Ported to RO5 ?
Rick Murray (539) 13839 posts |
why it didn’t fail on the older machines. Just to clarify ;) when I say older I’m usually talking about machines such as the Pi1 (ARMv6) and Beagle/Pi1r1 (ARMv7). What interests me was that the bogus data sent to the PSR didn’t seem to cause any ill effects despite trying to set an unknown processor mode, thumb state, and ignoring both types of interrupts. Honestly, I’m surprised the machine didn’t simply lock solid, so it’s as if the processor looked at the instruction, thought “bollocks”, and simply ignored it. This, of course, will be the part where Jeffrey/Stuart/Clive point out the words “Implementation defined” in that a different processor (perhaps an OMAP or something non-Pi) will indeed lock itself up. ;) |
Stuart Swales (8827) 1357 posts |
From ARMv6 ARM: “If any other value is programmed into the mode bits M[4:0], the result is UNPREDICTABLE.” I think that the last ARM ARM that covered 26-bit mode was RevE, see Table 8.1. That pattern (0b01100) ain’t valid there. |
Ralph Barrett (1603) 154 posts |
Indeed. Still some more work to do before any ‘release’, and certainly more testing required. I’ve just emailed Theo van der Boogaert to ask whether I can distribute the RO5 version of the singlestepper module, and an updated app. Just a reminder that Theo’s current license conditions for !ARM_Debug are: ‘This program is freely distributable as long as you don`t make any money out of it, and as long as you include all the files unaltered. This program is supplied “as is”.’ Ralph |
Colin Ferris (399) 1813 posts |
Note – Rick has a handy prog that looks though your code for 26bit instructions. Hard to believe that Gerfs made RO6 with no debugger for Modules. |
Rick Murray (539) 13839 posts |
Time to go put some popcorn into the microwave… |
tymaja (278) 174 posts |
Regarding the CPSR_cf – the issue is that it was trying to change the CPU into 64-bit mode from bit 5 (but also, even if bit 5 was modified to become a ‘1’, it would be setting mode %11100 – which isn’t (at least as of ARMv8A) a functional mode! Looking at the ARM ARM, many instructions that have illegal values default to ‘undefined instruction’ – this even includes MRC/MCR coprocessor commands specifying registers other than coprocessor 14/15 (I believe, need to check exactly what the outcome is with those instructions). Instructions issued without enough privilege can also become ‘undefined instructions’, so – that MSR CPSR instruction – would fail due to illegal values, and would probably also cause an undefined instruction if executed in USR mode! Regarding the debugging app – the ‘single stepping’ mode – is that using an ‘emulator’ for running the individual instructions in single step mode? And is it written in ARM code? |
tymaja (278) 174 posts |
Edit – I missed the but about sending bogus data to CPSR – in addition to ‘undefined instruction’ errors, some bits are read-only in (For example) USR mode, which may explain why some CPSR-write instructions didn’t cause a lockup; also, depending on the MSR instruction used, it may write only part of the data (using the 4 bit mask to represent the 4 bytes of the CPSR) – as an example, the f flag refers to the leftmost 8 bits of the CPSR, hence how MSR CPSRf,Rx can generally be used in USR mode without too many worries) |
Ralph Barrett (1603) 154 posts |
The debugger runs in a ‘singlestepper’ module running as a RISC OS module, in the normal manner. Theo originally wrote 4 different versions of the singlestepper module: For the ARM2 computers, non-SA RPCs, SA RPCs running RO3.7 and SA RPCs running RO4.0. I’ve been 32-bitting the RPC SA module, so that it now runs on the Raspberry Pi under RISC OS 5. A branch instruction is insterted into the target ARM code redirecting into the ‘singlestepper’ module. When this branch instruction is executed, then the target module’s code continues to run instruction by instruction in a ‘sandbox’ in the singlestepper module using a local copy of the target module’s registers etc. The singlestepper module also has some basic graphics and user input which allows the target code to be single-stepped through, multi-stepped or whatever. There are lots of useful ‘bells-and-whistles’ too. e.g. You can even save an output file containing all the instructions executed and all the register values for each of those instructions. The target ARM instructions continue to run on the same ARM processor and in the same processor mode at all times – just at a different address. There is no emulation involved. The singlestepper module contains lots of clever code to make this all seem transparent to the user. Note that all of the clever ARM code is Theo’s and not mine – I’d not be clever enough to have ever got this working ! Ralph |
tymaja (278) 174 posts |
If Theo is OK with modifications to the code etc, I do wonder whether we need to take a new approach for some instructions (as per the discussions above around instructions that ‘break the rules’ (R15 and STMIA, MSR MRS etc?) I would need to look at the code to figure out how it deals with issues such as access to illegal memory addresses etc; Whatever happens, I think that a partial emulation / instruction validation step will be needed; this could be done by having some filter code to detect problematic instructions, and would allow the module to continue with the sandboxed method of executing almost all instructions, but also allow emulation of instructions known to be problematic. In a way, it shouldn’t be an issue if we were to emulate some instructions – as long as the emulation itself is correct, then the updated emulated registers and updated CPSR will be correct, and can be used for running further sandboxed instructions, before returning control to the ARM when leaving single stepping mode; - another benefit of emulating a small subset of instructions is that they can be carefully emulated, as opposed to trying to emulate the entire ARM instruction setIf you can get the go-ahead re: modifying the code, I would be happy to assist if that was of any help. i currently have a single-stepper ARMv8 Aarch32 emulator running on desktop, with registers, CPSR, CPU modes emulated |
Ralph Barrett (1603) 154 posts |
My intention with !ARM_Debug is to get the same level of functionality on the RPi, as with the RISC PC (from 2001!). With the minimum amount of changes to Theo’s original code. I’m afraid that all of this other stuff is not really ‘on my radar’… Ralph |
Ralph Barrett (1603) 154 posts |
Theo responded positively to my email, although he did express surprise that his code was still useful more than 20 years later ! Theo said that it was OK to distribute a 32-bit version of his !ARM_Debug debugger programme. I’ve not discussed this with Rick yet, but putting a copy on Rick’s ‘HeyRick’ web site on his RISC OS debugger web page might be a good place to put a copy :-) Quick update. I am continuing to slowly fix (mostly) self-introduced bugs in the !ARM_Debug ‘BreakRPi’ module, and this is now mostly working the same as Theo’s (2001) RISC PC’s ‘BreakSA’ module. I can now multi-step (or single-step) through ARMBasicEdit from the initial 00000094:STMIA R13,{R0,R1,R14} through to the BASIC programme appearing on the screen and the ARMBE module waiting for user input at 00007134: SWI OS_ReadC. The initial (hard) break is done by inserting a branch instruction at 00000094:STMIA R13,{R0,R1,R14} that jumps from the ARMBE module into the ‘BreakRPi’ module. Once running in the ‘BreakRPi’ module each ARMBE module instruction is run in a sandbox in the ‘BreakRPi’ module and displayed on the screen with all the register values etc. Once running in the ‘BreakRPi’ module ‘soft’ breaks can be inserted to stop the multi-step execution, as required. Previously the ‘BreakRPi’ module was geting as far as the 000173C: ADD PC,R5,#&4C ARMBE instruction. Where R5 = FC1B2E7C, which is a jump info the BASIC module. Theo’s code substitutes the destination register PC (R15) in this instruction with R0 to run the instruction in the sandbox, then writes to new R0 value to back into to the stored sandbox PC (R15) 32bit-word. This means that instruction with a source or destination register of R15 can still be successfully ‘run’ in the sandbox, even though they’re actually being run at a different address and in a different module. Sandbox uses the same ARM processor mode (USR and SVC are supported – IRQ/FIQ interrupt code is not supported). Clever stuff. I’ve also created a test incarnation of ‘!ARM_debug’ named ‘!ARM_Debug1’ which uses an incarnation of the module called ‘BreakRP1’. This has allowed me to single step through the ‘BreakRPi’ module when it is itself debugging the ARMBE module. This works OK, and has allowed me to find a couple of bugs in my code. However, sometimes your brain explodes trying to work out which module you are in ! I’ve still got some bugs to wheedle out. The very useful ‘Record’ instructions/mnemonics/registers to file option is running mostly but goes awry after a bit. Also need to get !ARM_Debug working in the natively supported Mode 15, which appears to be supported on RISC OS 5 as its a 256 colour mode. Ralph |
Ralph Barrett (1603) 154 posts |
!ARM_Debug verifies that the current ‘target’ instruction address is running at a valid RISC OS 5 memory address. So there are quite a few bits of code using ‘CMP’ instructions on 32bit unsigned integers. The BASIC ROM base address is (say) ‘FC1B2E7C’, which has the top bit set. The following code fails on the RPi:
The BGT should be BHI, and BLE should be BLS for unsigned comparisons to work correctly for RO5 memory addresses. This has been discussed previously on this forum. Took me a long time to find why this code wasn’t working ! I eventually found the root cause by running !ARM_Debug1 on ARM_Debug (i.e. debugging the debugger). Until now I’ve never used either the ‘HI’ or ‘LS’ conditionals on hand-written ARM assembler, although most of my assembler programming was on my A310 back in the days of yore. When this wouldn’t have been a problem. Ralph |
Rick Murray (539) 13839 posts |
I think in the old days it was quite common to use GT and LE because it was easy to think of it as “Greater Than” and “Lesser or Equal”, and since the address would be handed to you with the PSR stripped out it simply wasn’t an issue if it was signed or unsigned tests. Fast forward a few decades and now the signed stuff risks blowing up. And, I’d imagine, is why the RMA is stuck where it is in the memory map (at the half gigabyte point).
😂 Using it to fix itself, pretty good test right there. |
Ralph Barrett (1603) 154 posts |
I’ve finally fixed a really obvious bug. Most things are obvious in retrospect :-) When debugging the !ARM_Debug module using another incarnation (!ARM_Debug2) the second debugger incarnation was running along fine but eventually I got an abort on an LDM instruction with an invalid PC. An example:
R13 was correct and pointed to the local module, but the value of the PC read from the stack was not correct. I eventually realised that R13 was pointing to the wrong stack ! It needed to be pointing to the target module stack and not the local stack. R13_usr and R14_usr had not been correctly restored when the preceeding MSR instruction was run. In the RISC-PC Strongarm version of the !ARM_Debug code written by Theo this MSR instruction has been a TEQP instruction. Theo was trapping the TEQP instruction when it ran in the !ARM_Debug sandbox and had extra code to reload R13_usr and R14_usr. I’ve now re-purposed this code to do the same functionality for the various incarnations of the MSR instruction. So I can successfully debug the ARMBE module now from entry to exit, and save out each ARMBE instruction and all the register values to a file. Or I can run a second copy of !ARM_Debug (2) and save out all the instructions and register values of the first !ARM_Debug into one enormous file :-) So I’m getting to the point that !ARM_Debug32 is a very useful tool, but I’ve still got quite a few cosmetic bugs to fix e.g. when saving instructions to a text file the top (8th) digit of the instruction memory address is not there (i.e. originally 26-bit addressing). etc… Virtually all the changes that I’ve had to make to ‘32-bit’ !ARM_Debug are described on Rick’s excellent ‘Heyrick’ pages. A very useful resource if you’re porting a 26-bit RISC OS programme to run on a Raspberry Pi. I just tried running !ARM_Debug32 on an RPi4 and it seems to work OK. However, loading a BASIC file to the ARMBE module crashes with an illegal instruction error. Might be a TEQNEP instruction, even though it is never run ?? Need to check if there’s a newer version of the ARMBE module for the RPi4 ;-) Ralph |
Ralph Barrett (1603) 154 posts |
A brief example of the output of !ARM_Debug as saved (‘recorded’) to a text file:
This example shows the start and end of the ARMBE module (i.e. ‘end’ just waits for the ARMBE user to hit a keyboard key etc.). Note the first digit of the address should be a ‘2’ (e.g. 20215B2C) but !ARM_Debug is still showing the address in 26-bit format. I’m expecting this ‘cosmetic’ bug to be an easy fix ;-) !ARM_Debug can also save the register values for each instruction, but can only save the output to an !ARM_Debug formatted file (i.e. not text). I might have a go at adding a new option to also save out all the register values and instructions in text format. Also need to add the CPSR (and SPSR?), even though I think that these registers already existed for the RISC PC on RISC OS 3.71 ?? Note: ARMBE = ARM Basic Editor, which was an early Archimedes program made available by Acorn. I’m just using ARMBE as an easy way to fire-up some relatively simple module code (i.e. drag and drop a BASIC file to the ARMBE (‘BED’) icon. I also tried decoding !ZAP but soon got lost down a huge rabbit hole… Ralph |
Rick Murray (539) 13839 posts |
They existed on the processor, but remember that 3.7 would have been running in a 26 bit mode where the PSR would have been merged with PC rather than accessed via a separate status register. So faffing with R14 and TEQP etc would still have been the way to fiddle flags back then.
It’s still around. |
Graeme (8815) 106 posts |
ARM made a change with their processors at some point. Older CPUs look at the condition field and ignore the instruction if the condition doesn’t succeed. The instruction can be undefined and it will not cause a problem. This is how RPCEmu works. Newer ones parse the instruction and if the instruction is undefined it will take the undefined instruction exception, normally giving you the error. It is irrelevant if the condition matches or not, the instruction still needs to be valid. Perhaps this is the issue you are getting that causes the error? |
Ralph Barrett (1603) 154 posts |
Thanks this explains why the TEQNEP was generating an illegal instruction error on the RPi4. But worked OK on the RPi1. I also looked at the link provided by Rick, and after some searching I found that the latest version of the Basic Editor module (1.07) is supplied in the Risc OS 5.30 module folder:
My old copy of !Bedit was using an earlier 32-bitted version of the ARMBE module which still showed version 1.00 (21 Aug 1987). I’ve updated my !Bedit application to use the new (1.07) module, and this works fine on my RPi4. Added here just for anybody finding this in the future :-) Ralph |
Ralph Barrett (1603) 154 posts |
One of the very useful features of !ARM_Debug is that it can record instructions to a file. This allows you to subsequently check through code off-line, and also allows you to automatically compare (e.g. using the very useful !Sidediff application) two instances of running the same code, where the outcome was different. Theo’s original 26bit !ARM_Debug supported outputting files in the following formats: 1. Just instruction addresses (text). Double-clicking on one of the save ‘data’ files opens up a scrollable window where the data is displayed. Mostly done in assembler for speed on an ARM2! However, you cannot copy this window data. Whilst I work on how to implement the copying of the window data, I’ve added the ‘missing’ text option to the 32-bit !ARM_Debug module: 6. Instruction addresses, mnemonics and register values (text). Which allows register and flag output to be easily copied to elsewhere :-) Here is an example showing an ARM mode change. Note that the ‘i’ and ‘f’ flags are shown, but interrupts is not supported on !ARM_Debug.
edit – correcting typos as ever |
Rick Murray (539) 13839 posts |
Yup, it’s a shame DDT isn’t able to record state so when something but, one can unwind to determine how it got to that state.
That would be useful, and certainly a lot nicer than “single step, dump registers, step, dump, step, dump…”. |
Piers (3264) 43 posts |
That’s very complex to do efficiently. Recording every instruction to a file sounds incredibly slow. Dr Smith (as in the toolkit), went on to write undodb for Linux. I tried to persuade him to port it to macOS, but I failed. I guess RISC OS might be a lower priority. |
Ralph Barrett (1603) 154 posts |
I am now mostly doing !ARM_Debug work on RO5.28 on my Pi2. A useful function of !ARM_Debug is that crtl-shft-keypad minus key breaks into the debugger and shows the current code being run e.g. in the ‘unlikely’ event of Risc OS becoming ‘unresponsive’. This function is used much like the (extremely useful) recent addition of ALT-BREAK, but you also get to step through the instructions that caused the ‘lock-up’. The crtl-shft-keypad minus key combination no longer works correctly on RO5.30, and generates an abort:
Seems that RO5.30 has been been changed so that a programme running in USR mode can no longer READ the system workspace. Same code works fine on RO5.28. I can understand not being able to WRITE from USR mode to the system workspace, but why are reads not allowed any more ? Is this by design or is this a bug ? Ralph |
Colin Ferris (399) 1813 posts |
Have you tried surrounding arm code with swi OS_EnterOS |