OSLib bug on ARM2 CPUs
Developer peer review of proposed code alterations.
Mar 13, 2025 - 14:34
Cameron Cawley
(173 posts)
|
I’ve been trying to investigate a bug in OSLib where using the MRS instruction causes a crash on ARM2 CPUs rather than being a no-op like other pre-ARM6 CPUs. However, I haven’t been able to replicate it using emulators, and I don’t have actual hardware to test with.
https://gist.github.com/ccawley2011/3f89d2763fe4800af95be57c64dfecf8 This GitHub issue contains some more context: https://github.com/dpt/The-Great-Escape-in-C/issues/5 |
Mar 13, 2025 - 16:30
Stuart Swales
(1405 posts)
|
Does the MRS still stiff if (not) executed conditionally? e.g. protect with TEQ R0,R0; TEQ PC,PC; RSB …; MRSEQ … Edit: looks like emulators have had a rough time with CMP-without-S in the past (which is what MRS will be on ARM2 I believe) – see https://www.stardot.org.uk/forums/viewtopic.php?p=94567#p94567 |
Mar 13, 2025 - 17:56
Clive Semmens
(3318 posts)
|
gah. Brian on, Clive! (It’s over 17 years since I was responsible for documenting this stuff…) |
Mar 13, 2025 - 19:20
Cameron Cawley
(173 posts)
|
This was my initial thought on how to fix it, however it was pointed out to me that because TEQ itself sets flags, it would affect the value returned by MRS. Is this important for any of the functions provided by OSLib that return the flags, or are all of them unaffected by this? |
Mar 13, 2025 - 20:47
Stuart Swales
(1405 posts)
|
Easy enough in general – two copies of the routine tail (from SWI onwards) selected for 26 or 32 bit execution. But all you want in this case is the C flag output from OS_ReadModeVariable? Which such TEQ would not affect, BUT if an ARM2 emulation got CMP-without-S wrong, it might be the calling routine that got confused. And likely that the ARM2 emulation is now fixed everywhere? But some folk are still running old ones…? |
Mar 13, 2025 - 21:44
Cameron Cawley
(173 posts)
|
The SWI veneers are all generated using defmod, so I wouldn’t be able to change the behaviour for OS_ReadModeVariable without it affecting other SWIs as well.
I’ve taken a look at both Arculator and ArcEm, and neither of them seem to distinguish between ARM2 and ARM250 besides the lack of the SWP instruction, which suggests that the issue is exclusive to real hardware, which I’m not able to test with. If the issue is because of the behaviour of CMP-without-S on machines without MRS, would clearing the flags after retrieving them solve the problem, or does the APCS-32 API require them to be set when returning? |
Mar 14, 2025 - 10:01
Jon Abbott
(2675 posts)
|
If you post a binary of it, I can test for you. That code looks odd, what is this meant to do? rsb r0, pc, pc mrs r0, CPSR In fact the whole sequence looks odd: bl os_read_mode_variable tst r0, #(1<<29) adrne r0, carry_set adreq r0, carry_clear svc 0x00000002 svc 0x00000011 os_read_mode_variable: stmfd sp!, {r2} svc 0x00000035 ldr ip, [sp] teq ip, #0 strne r2, [ip] rsb r0, pc, pc mrs r0, CPSR add sp, sp, #4 mov pc, lr Could be better coded as: bl os_read_mode_variable adrcs r0, carry_set adrcc r0, carry_clear svc 0x00000002 svc 0x00000011 os_read_mode_variable: movs ip, r2 mov r2, #0 <-- handle failures if required? bne %FT02 svc 0x00000035 mov pc, lr 02 svc 0x00000035 str r2, [ip] mov pc, lr Admittedly that’s not your question, but it does seem an odd way to handle C on return from an SWI. |
Mar 14, 2025 - 10:19
Stuart Swales
(1405 posts)
|
Clearly when executing in 32-bit mode that RSB will yield zero. If the MRS instruction (which is a NOP on ARM2) is available, flags from that are used.
|
Mar 14, 2025 - 10:41
Jon Abbott
(2675 posts)
|
Ah. That explains why the code looks so odd, ignore my recode then. |
Mar 15, 2025 - 08:16
Colin Ferris
(1874 posts)
|
How does this work in 26bit ARM – ie getting the flags in R0? rsb r0, pc, pc |
Mar 15, 2025 - 08:50
David J. Ruck
(1711 posts)
|
PC as the 3rd operand is program counter plus flags, PC as the 2nd operand is just the program counter. |
Mar 15, 2025 - 13:46
Colin Ferris
(1874 posts)
|
What does ‘Reverse’ mean in RSB ? |
Mar 15, 2025 - 14:11
Clive Semmens
(3318 posts)
|
Subtracts first operand from second operand. This permits subtraction from constants and shifted registers. SUB subtracts the second operand from the first. |
Mar 15, 2025 - 14:26
Stuart Swales
(1405 posts)
|
RSB can also be useful for comparing against ranges. e.g. CMP r4, #2 ; Is base valid [2..36] ? RSBGES r0, r4, #36 ; LT -> invalid (r0 scratch register) |
Mar 15, 2025 - 22:20
Colin Ferris
(1874 posts)
|
Why are the two PCs different values – ie one PC with flags and the other without? |
Mar 15, 2025 - 22:46
Cameron Cawley
(173 posts)
|
Thanks, I’ve uploaded a binary here: https://drive.google.com/file/d/1dJhUgwTt0fqWkfJFXdwuz0Ng-JEkJXZy/view?usp=sharing
I’ve looked at the code again, and it already uses TEQ when returning the value of R2, which suggests that it shouldn’t be too much of a problem to use TEQ again to determine whether to use MRS or not (although there may still be some edge cases for other SWIs that I’m not aware of). I’ll try and update DefMod with this fix and report back. |
Mar 16, 2025 - 05:15
Clive Semmens
(3318 posts)
|
It’s the same register. It’s just that you either want the flags alone, or the program counter alone, when you’re using them – whereas saving and reloading the two together is often very convenient. And since in 26-bit days the two fitted together nicely in 32-bits, that was a reasonable thing to do. |
Mar 16, 2025 - 07:41
Colin Ferris
(1874 posts)
|
Yes the 26bit ARM is quite clever :-) But how does one PC gets the flags removed? |
Mar 16, 2025 - 08:11
Stuart Swales
(1405 posts)
|
Please do see the section ‘R15 as an Operand’ in the ARM manual above, p2-35. |
Mar 16, 2025 - 08:26
Colin Ferris
(1874 posts)
|
Tks for that info – didn’t see that the page numbers differed – to me small numbers of p2-35 :-( |