Something weird going on with USR
David Williams (2619) 103 posts |
Of course it’s more likely to be a case of something weird (and obvious!) going on with my code rather than with BASIC’s USR function. Still, I’m a bit stumped here. The following code includes an assembly language routine to perform 64-bit integer division (i.e. 64-bit numerator, 32-bit denominator, 32-bit result). It’s Conrad Hughes’s effort from 1991, which I nicked from… well, just Google ‘chiark arm division’. :-) Upon calling the code via BASIC’s USR function, I get an “Internal error: abort on data transfer at &FC19F168 [on my RPi 3B running RISC OS 5.23, BASIC V version 1.63, but similar happens under RISC OS 5.24 via RPCEmu]” In practice, I will probably (if I ever use it) call this division routine from another assembly language routine, rather than from BASIC, but I can’t figure out why USR seems to be choking on it? Here’s the code:
Thanks in advance. David. |
Rick Murray (539) 13851 posts |
[this was rubbish, ignore it] |
Rick Murray (539) 13851 posts |
This gets stranger. It looks like some sort of error is happening, which is just failing in an epic sense. *MulTest [huge snippage] 00009420 E1A0F00E MOV pc, R14 Internal error: abort on data transfer at &FC1A8F18 *Where Address &FC1A8F18 is at offset &00001218 in module 'BASIC' Doesn’t matter if USR or CALL is used, error in same location. This translates to the following chunk of code: ;r1 = highest program section start (of ALL sections) below LINE ptr ;Now search from that pointer to end of that program section... MSG0START MOV AELINE,R1 ;keep pointer to start of program section MSG0 CMP R1,LINE ;is this line > error line? BHI MSG1 ;yes, so go use last ERL LDRB R2,[R1,#1] CMP R2,#&FF ;at end of program? MVNEQ R0,#1 ;yes, so ERL:=-2 BEQ MSG1 LDRB R0,[R1,#2] ADD R0,R0,R2,LSL #8 ;r0 = line number LDRB R2,[R1,#3] ;line length ADD R1,R1,R2 ;move ptr to next line B MSG0 MSG1 STR R0,[ARGP,#ERRLIN] ;store ERL ADD R1,ARGP,#ERRORS ADD R4,R1,#255 ;end of error buffer ADR LINE,ERRHAN ; -> default error handler *** THIS IS THE LINE THAT FAILS *** LDR R0,[R7],#4 STR R0,[ARGP,#ERRNUM] ;store ERR CMP R0,#0 ;is ERR = 0 ? LDRNE LINE,[ARGP,#ERRORH] ;no, so use last error handler STREQ LINE,[ARGP,#ERRORH] ;yes, save default error handler MSGA LDRB R0,[R7],#1 ;copy error message ... TEQ R0,#0 STRNEB R0,[R1],#1 ;... to error buffer BNE MSGA ;save error message LDR R2,[ARGP,#PAGE] CMP R2,AELINE BEQ MSGLIBRARYDONE ;not in a strange bit within …RiscOS.Sources.Programmer.BASIC.s.Basic. According to Debugger module, at the point of crashy-crashy, R0 is &294, R1 is &8000, R2 is &18, and R7 is &FFFFFFFF. If it’s not weird enough wondering how we got here, consider that replacing any of those three instructions with a NOP makes the program complete. The hell is going on…? |
Rick Murray (539) 13851 posts |
Think I might have an idea… |
Rick Murray (539) 13851 posts |
Yup, got it. Had to take BASIC apart to work it out though. ;-) |
Rick Murray (539) 13851 posts |
Okay, this is how BASIC calls some assembler: CALLARMROUT ;call routines for CALL and USR MOV TYPE,SP ;pointer to list of lvs STMFD SP!,{R4,R5,ARGP,AELINE,LINE,R10,R14} STR LINE,[ARGP,#R12STORE] ;store r12 MOV R10,#2 ;set MEMM bit for r12 STRB R10,[ARGP,#MEMM] ;and store it MOV R10,R5 ;number ADD R11,ARGP,#STRACC ADDS R0,ARGP,#INTVAR+4 LDMIA R0,{R0-R7} ;A%-H% ADR R14,CALL2 LDR PC,[SP],#4 ;go there! Note that just before the jump, R14 is set to point to CALL2. This function tidies up on return. It’s this: CALL2REAL ; Return after CALL and USR LDMIA SP!,{R5,ARGP,AELINE,LINE,R10} ; omit r14 for now MOV R14,#0 ;unset all MEMM flags STRB R14,[ARGP,#MEMM] ;and store flags LDR R14,[SP],#4 ;now restore r14 ADD SP,SP,R5,LSL #3 ;pop stack by two words per parameter MOVVC PC,R14 MOV R14,R0 B MSGERR DCD &BA51C005 CALL2 B CALL2REAL ;0th entry in table is return address Suddenly a lightbulb flickers in my head. Stuck in there is MSGERR which is in the ballpark of where BASIC goes horribly wrong; however we shouldn’t be ending up there as nothing went wrong (we should drop out after the “pop stack by two words” line); BUT… if your code (accidentally) sets the V flag (to indicate an error) but R0 doesn’t actually point to a valid error block, well this could explain what is going on. To test this, I have ended your code with: MSR CPSR_f, #0 ; clears all flags MOV pc, lr And… it completes. Tells me the quotient is -1. I suck at maths, so no idea if that’s right or not. Therefore, two bits of Rick Wisdom:
and:
Phew. |
Clive Semmens (2335) 3276 posts |
Interesting – actually, I’m slightly surprised that I’ve never collided with that myself! |
Steve Drain (222) 1620 posts |
As it says in the BASIC manual: MOV PC,R14 returns to the BASIC calling program. If V is set on return an error will be signalled, with R0 being treated as a pointer to a standard RISC OS error block.
I do not think it is BASIC’s job to check the validity of the block. Nothing else checks error blocks that way, does it? |
Rick Murray (539) 13851 posts |
Ah, you mean the BASIC manual that those of us who are long time BASIC users have never actually read? ;-) The last BASIC instruction that I read was in the BBC Micro User Guide. Back when people wrote decent user guides.
Given that BASIC is supposed to be a sanitised environment, it might be useful. Especially, as in this example, it was setting V as a side effect to something else, not intentionally to flag an error.
Um… About that…
Probably would not have happened in the 26 bit world if using MOVS/LDM^ to return, as you’d be handing back the flags as they were on entry. |
David Williams (2619) 103 posts |
@ Rick: Thank you so much for looking into this issue in such depth; it had been f*****g with my head for a few hours before I threw the towel in despair and reached out here for help. The thing is, I’m pretty certain I’ve been snagged by this little feature of ARM BASIC in the past, but most likely just put it down to my dodgy assembly language. All those years of writing programs with BASIC V – reams of assembler code, and yet I was completely unaware of the V-flag-and-R0-error-block-pointer thing w.r.t. USR and CALL! I think, from now on, because this has made me a bit paranoid, I’ll issue a MSR CPSR_f, #0 instruction as a matter of course before returning to BASIC from (most) machine code routines. Peace of mind! @ Steve: I bet it came as a surprise to many — perhaps most? |
Steve Pampling (1551) 8172 posts |
Nothing else checks error blocks that way, does it? +1. I’m pretty sure there’s been a conversation or two about various aspects of the OS and lack of error/bounds/parameter(existence) checking. ZeroPain anyone? |
Jeff Doggett (257) 234 posts |
To clear the V flag I usually use
as it’s easier to remember and will work on all ARM architectures. |
Clive Semmens (2335) 3276 posts |
To clear the V flag I usually use As long as you’re happy to have all the other flags cleared as well! 8~)
Good point. Or any of the other normal ways to return (which to be honest I’d have to look up to remind myself – there may only be one other…I’ve not written assembler for years, just BASIC…) |