Trapping Errors without Breaking Loops

bbc micro/electron/atom/risc os coding queries and routines
Post Reply
User avatar
BeebMaster
Posts: 3322
Joined: Sun Aug 02, 2009 5:59 pm
Location: Lost in the BeebVault!
Contact:

Trapping Errors without Breaking Loops

Post by BeebMaster » Thu Jan 09, 2020 10:52 am

Is there a way in BBC BASIC to deal with errors using ON ERROR which doesn't upset a FOR...NEXT or REPEAT...UNTIL loop? I usually put my error handler at the beginning with ON ERROR PROC error but if the error occurs within a loop it ends with an untrappable No FOR or No REPEAT error.

In the past I have had to resort to nasty GOTO jumps and manual counter increments so that the program can continue after an error. I'm looking for a better way of doing it.
Image

User avatar
Richard Russell
Posts: 1438
Joined: Sun Feb 27, 2011 10:35 am
Location: Downham Market, Norfolk
Contact:

Re: Trapping Errors without Breaking Loops

Post by Richard Russell » Thu Jan 09, 2020 12:22 pm

BeebMaster wrote:
Thu Jan 09, 2020 10:52 am
Is there a way in BBC BASIC to deal with errors using ON ERROR which doesn't upset a FOR...NEXT or REPEAT...UNTIL loop?
Which version of BBC BASIC? This capability was a significant omission from the early (e.g. 6502) versions, but ON ERROR LOCAL was added in BASIC 5 (and is of course also present in Brandy, BBC BASIC for Windows and BBC BASIC for SDL 2.0). I'm not 100% sure about BASIC 4, somebody here will know but I suspect it doesn't have ON ERROR LOCAL.

So if it's a 6502 version of BBC BASIC that you're interested in I fear you may be out of luck. Later versions allow this kind of thing:

Code: Select all

      FOR I% = -3 TO 3
        ON ERROR LOCAL PRINT REPORT$ : I% += 1
        PRINT 1 / I%
      NEXT I%
which prints:

Code: Select all

-0.333333333
      -0.5
        -1
Division by zero
         1
       0.5
0.333333333

User avatar
BeebMaster
Posts: 3322
Joined: Sun Aug 02, 2009 5:59 pm
Location: Lost in the BeebVault!
Contact:

Re: Trapping Errors without Breaking Loops

Post by BeebMaster » Thu Jan 09, 2020 12:51 pm

Yes, 8-bit BASIC ideally, although what I'm working on just now does use ARM BASIC, so I can use ON ERROR LOCAL for that.
Image

User avatar
Richard Russell
Posts: 1438
Joined: Sun Feb 27, 2011 10:35 am
Location: Downham Market, Norfolk
Contact:

Re: Trapping Errors without Breaking Loops

Post by Richard Russell » Thu Jan 09, 2020 1:40 pm

BeebMaster wrote:
Thu Jan 09, 2020 12:51 pm
Yes, 8-bit BASIC ideally, although what I'm working on just now does use ARM BASIC, so I can use ON ERROR LOCAL for that.
Limitations in early versions of BBC BASIC must be frustrating, and it's one reason why I have no interest at all in 'retro' programming. I suppose for some people overcoming such shortcomings is part of the challenge, but not for me. I'll take all the benefits the modern language offers, thank you!

User avatar
Lardo Boffin
Posts: 2066
Joined: Thu Aug 06, 2015 7:47 am
Contact:

Re: Trapping Errors without Breaking Loops

Post by Lardo Boffin » Thu Jan 09, 2020 7:40 pm

This post from a while ago may be of interest?

viewtopic.php?f=2&t=11973&hilit=On+error
Atom, issue 5
Elk
BBC model B 32k issue 4, 16k sideways RAM, Watford 12 ROM board, Acorn 6502 coproc
BBC model B 32k issue 7, turboMMC, Opus Challenger 3 512k, Pi 3 coproc
USA Model B
BBC Master, Datacentre + HDD, pi co-proc, econet, NULA

User avatar
Richard Russell
Posts: 1438
Joined: Sun Feb 27, 2011 10:35 am
Location: Downham Market, Norfolk
Contact:

Re: Trapping Errors without Breaking Loops

Post by Richard Russell » Thu Jan 09, 2020 8:15 pm

Lardo Boffin wrote:
Thu Jan 09, 2020 7:40 pm
This post from a while ago may be of interest?
viewtopic.php?f=2&t=11973&hilit=On+error
The OP (BeebMaster) posted to that very thread!

Avoiding ON ERROR altogether, which is one solution when you're using a version of BBC BASIC that doesn't have ON ERROR LOCAL, is easier said than done. If you can that's fine, but commonly it's difficult or impossible. Even in a simple case like a floating-point division it may be easy enough to predict (and therefore prevent) a Division by zero error, but not so easy to predict a Number too big error. It's even harder with a star (OSCLI) command which might fail for reasons that only the OS knows about.

Another thing to bear in mind is that determining whether or not an error would occur now doesn't necessarily predict whether it will occur a microsecond later. The classic example is in handling a File not found or File already exists error: the situation may change between the instant when you tested for the existance of the file and when you issue the command that might fail if it does, or doesn't, exist (OK that's not very likely on an 8-bit single-tasking CPU). The preferred approach is to allow the error to occur and trap it, not to prevent it, which probably needs ON ERROR LOCAL.

User avatar
BeebMaster
Posts: 3322
Joined: Sun Aug 02, 2009 5:59 pm
Location: Lost in the BeebVault!
Contact:

Re: Trapping Errors without Breaking Loops

Post by BeebMaster » Thu Jan 09, 2020 8:33 pm

Oh yes! Maybe subconsciously I knew it had been discussed before which prompted me to post.

I'll have to have another look at that other thread in more detail. Trapping errors resulting from OS commands and OS calls without breaking loops is something I definitely need to be able to do.
Image

User avatar
lurkio
Posts: 2686
Joined: Wed Apr 10, 2013 12:30 am
Location: Doomawangara
Contact:

Re: Trapping Errors without Breaking Loops

Post by lurkio » Thu Jan 09, 2020 9:46 pm

Lardo Boffin wrote:
Thu Jan 09, 2020 7:40 pm
This post from a while ago may be of interest? viewtopic.php?f=2&t=11973&hilit=On+error
BeebMaster wrote:
Thu Jan 09, 2020 8:33 pm
I'll have to have another look at that other thread in more detail. Trapping errors resulting from OS commands and OS calls without breaking loops is something I definitely need to be able to do.
As I don't really understand how Jonathan's LocalError handler works, I was sceptical that it could recover from a (local) error within a loop, given that the discussion in that other thread was only about recovering from local errors in PROCs and FNs. But whaddyaknow? -- it can recover from errors in loops inside PROCs too. Lines from 5000 are taken from mdfs.net:

Code: Select all

    5 PRINT"Program begins"
   10 PROCerr_local(1)
   20 PROCa
   30 PROCerr_local(0)
   35 PRINT "Program ends"
   40 END
   50 :
   60 DEFPROCa
   70 LOCAL ERROR:ON ERROR LOCAL REPORT:PRINT:i=i+1:NEXT
   80 FOR i = -3 TO 3
   85  IF i=0 THEN *ACCESS nowt
   90  PRINT 1 / i
  100 NEXT 
  110 ENDPROC
  120 :
 5000 REM > LocalError 0.20 15-Oct-2016 J.G.Harston
 5010 REM Implement LOCAL ERROR and ON ERROR LOCAL for 6502 BASIC
 5020 :
 5030 REM PROCerr_local(1) to enable local error handler
 5040 REM PROCerr_local(0) to restore default error handler
 5050 REM Usage: LOCAL ERROR:ON ERROR LOCAL local error handler
 5060 :
 5070 DEFPROCerr_local(A%):IFHIMEM>&FFFF:ENDPROC
 5080 IF(?&FFF7 AND &DF)<>&4C:ENDPROC
 5090 IFA%=0:?&202=err_0%:?&203=err_0%DIV256:ENDPROC
 5100 A%=!&202:REPEATA%=A%+1:UNTIL(!A%AND&8000FF)=&80004C
 5110 DIMerr_%119:FORB%=0TO1:P%=err_%:[OPT 0
 5120 LDY#0:LDA(&FD),Y:CMP#16:BNEE%
 5130 LDY&0A:DEY:LDA(&0B),Y:CMP#&EA:BNEE%:.C%
 5140 INY:LDA(&0B),Y:CMP#32:BEQC%:CMP#&85:BNEE%
 5150 INY:STY&0A:INC&1FB:LDA4:SBC#8:STA4:LDA5
 5160 SBC#0:STA5:LDY#7:LDA&1F9:STA(&04),Y:DEY:.D%
 5170 LDA&12,Y:STA(&04),Y:DEY:CPY#3:BNED%
 5180 LDA#0:STA(&04),Y:DEY:LDA#4:STA(&04),Y:DEY
 5190 LDA#0:STA(&04),Y:DEY:LDA#&16:STA(&04),Y:BNEG%:.E%
 5200 LDY#0:LDA(&16),Y:CMP#&EA:BEQP%+5:.F%:JMP!&202
 5210 LDA&16:ADC#0:STA&0B:LDA&17:ADC#0:STA&0C
 5220 LDA#0:STA&0A:.G%:LDX#&F5:TXS:JMPA%!1:]:NEXT
 5230 ?&202=err_%:?&203=err_%DIV256:err_0%=F%!1
 5240 ENDPROC
Result:

Code: Select all

>RUN
Program begins
-0.333333333
      -0.5
        -1

Not found
       0.5
0.333333333
Program ends
>_
EDIT: The following works too:

Code: Select all

    5 PRINT"Program begins"
   10 PROCerr_local(1)
   20 PROCa
   30 PROCerr_local(0)
   35 PRINT "Program ends"
   40 END
   50 :
   60 DEFPROCa
   70 LOCAL ERROR
   80 FOR i = -3 TO 3
   81  ON ERROR LOCAL REPORT:PRINT:i=i+1:NEXT
   85  IF i=0 THEN *ACCESS nowt
   90  PRINT 1 / i
  100 NEXT 
  110 ENDPROC
  120 :
 5000 REM > LocalError 0.20 15-Oct-2016 J.G.Harston
 5010 REM Implement LOCAL ERROR and ON ERROR LOCAL for 6502 BASIC
 5020 :
 5030 REM PROCerr_local(1) to enable local error handler
 5040 REM PROCerr_local(0) to restore default error handler
 5050 REM Usage: LOCAL ERROR:ON ERROR LOCAL local error handler
 5060 :
 5070 DEFPROCerr_local(A%):IFHIMEM>&FFFF:ENDPROC
 5080 IF(?&FFF7 AND &DF)<>&4C:ENDPROC
 5090 IFA%=0:?&202=err_0%:?&203=err_0%DIV256:ENDPROC
 5100 A%=!&202:REPEATA%=A%+1:UNTIL(!A%AND&8000FF)=&80004C
 5110 DIMerr_%119:FORB%=0TO1:P%=err_%:[OPT 0
 5120 LDY#0:LDA(&FD),Y:CMP#16:BNEE%
 5130 LDY&0A:DEY:LDA(&0B),Y:CMP#&EA:BNEE%:.C%
 5140 INY:LDA(&0B),Y:CMP#32:BEQC%:CMP#&85:BNEE%
 5150 INY:STY&0A:INC&1FB:LDA4:SBC#8:STA4:LDA5
 5160 SBC#0:STA5:LDY#7:LDA&1F9:STA(&04),Y:DEY:.D%
 5170 LDA&12,Y:STA(&04),Y:DEY:CPY#3:BNED%
 5180 LDA#0:STA(&04),Y:DEY:LDA#4:STA(&04),Y:DEY
 5190 LDA#0:STA(&04),Y:DEY:LDA#&16:STA(&04),Y:BNEG%:.E%
 5200 LDY#0:LDA(&16),Y:CMP#&EA:BEQP%+5:.F%:JMP!&202
 5210 LDA&16:ADC#0:STA&0B:LDA&17:ADC#0:STA&0C
 5220 LDA#0:STA&0A:.G%:LDX#&F5:TXS:JMPA%!1:]:NEXT
 5230 ?&202=err_%:?&203=err_%DIV256:err_0%=F%!1
 5240 ENDPROC
Result:

Code: Select all

>RUN
Program begins
-0.333333333
      -0.5
        -1

Disk fault 1E at 00/00
       0.5
0.333333333
Program ends
>_
Last edited by lurkio on Thu Jan 09, 2020 11:55 pm, edited 1 time in total.

User avatar
Richard Russell
Posts: 1438
Joined: Sun Feb 27, 2011 10:35 am
Location: Downham Market, Norfolk
Contact:

Re: Trapping Errors without Breaking Loops

Post by Richard Russell » Thu Jan 09, 2020 11:20 pm

lurkio wrote:
Thu Jan 09, 2020 9:46 pm
But whaddyaknow? -- it can recover from errors in loops inside PROCs too.
Except that the code you listed (with the PROCerr_locals removed) should not run, and does not when tested in ARM BASIC 5 and my BBC BASICs: it fails with 'Not in a FOR loop at line 70' in both cases. So although JGH's implementation of ON ERROR LOCAL is doing something, it's not doing it right. :(

To make your program run successfully, with 'real' implementations of ON ERROR LOCAL, you would need to move that statement inside the loop:

Code: Select all

   10 PRINT"Program begins"
   20 PROCa
   30 PRINT "Program ends"
   40 END
   50 :
   60 DEFPROCa
   70 LOCAL ERROR
   80 FOR i = -3 TO 3
   90 ON ERROR LOCAL REPORT:PRINT:i=i+1:NEXT
  100 IF i=0 THEN *ACCESS nowt
  110 PRINT 1 / i
  120 NEXT
  130 ENDPROC
Edit: Confirmed that Matrix Brandy also reports 'Not in a FOR loop' with your original code.

User avatar
lurkio
Posts: 2686
Joined: Wed Apr 10, 2013 12:30 am
Location: Doomawangara
Contact:

Re: Trapping Errors without Breaking Loops

Post by lurkio » Thu Jan 09, 2020 11:57 pm

Richard Russell wrote:
Thu Jan 09, 2020 11:20 pm
although JGH's implementation of ON ERROR LOCAL is doing something, it's not doing it right.
:shock:
Richard Russell wrote:
Thu Jan 09, 2020 11:20 pm
To make your program run successfully, with 'real' implementations of ON ERROR LOCAL, you would need to move that statement inside the loop
Updated my previous post.

:idea:

User avatar
jgharston
Posts: 4008
Joined: Thu Sep 24, 2009 12:22 pm
Location: Whitby/Sheffield
Contact:

Re: Trapping Errors without Breaking Loops

Post by jgharston » Sat Jan 11, 2020 1:51 pm

Yes, I'm surprised that:
70 LOCAL ERROR:ON ERROR LOCAL REPORT:PRINT:i=i+1:NEXT
80 FOR i = -3 TO 3
85 IF i=0 THEN *ACCESS nowt
90 PRINT 1 / i
100 NEXT

works, as you're taking the error handler outside the structure that generates the error. You would "normally" be creating another loop when you trap the error instead of continuing the loop.

It could be down to certain idiosyncrasies of the 6502 implementation of BBC BASIC, FOR/NEXTs aren't stacked, they are remembered in workspace, and there are certain things with badly-nested NEXTs that work but strictly speaking shouldn't, and are warned about in documentation for BBC BASIC on other platforms. For instance the 32000 BASIC manual describes this being a badly-nested structure:

FOR i=1 TO n:UNTIL TRUE:NEXT

and:
100 FOR a=1 TO 10
110 GOSUB 2000
...
2000 NEXT a

Code: Select all

$ bbcbasic
PDP11 BBC BASIC IV Version 0.32
(C) Copyright J.G.Harston 1989,2005-2020
>_

User avatar
jgharston
Posts: 4008
Joined: Thu Sep 24, 2009 12:22 pm
Location: Whitby/Sheffield
Contact:

Re: Trapping Errors without Breaking Loops

Post by jgharston » Sat Jan 11, 2020 1:57 pm

Richard Russell wrote:
Thu Jan 09, 2020 11:20 pm
Except that the code you listed (with the PROCerr_locals removed) should not run, and does not when tested in ARM BASIC 5 and my BBC BASICs: it fails with 'Not in a FOR loop at line 70' in both cases. So although JGH's implementation of ON ERROR LOCAL is doing something, it's not doing it right. :(
It's not the 6502 ON ERROR LOCAL that's not working right, it's the 6502 NEXT that's not "working right".

6502 BASIC allows this to work (for a certain value of "work"):
10 FOR a=1 TO 10
20 PRINT a
30 PROCboo
40 NEXT a
50 END
100 DEFPROCboo
110 FOR b=1 TO 2
120 NEXT a
130 ENDPROC

6502 BASIC's NEXT just does an increment-and-jump-not-equal, it doesn't do any structure checking, and from the way 6502 BASIC implements FOR/NEXT it can't.

Code: Select all

$ bbcbasic
PDP11 BBC BASIC IV Version 0.32
(C) Copyright J.G.Harston 1989,2005-2020
>_

User avatar
jgharston
Posts: 4008
Joined: Thu Sep 24, 2009 12:22 pm
Location: Whitby/Sheffield
Contact:

Re: Trapping Errors without Breaking Loops

Post by jgharston » Sat Jan 11, 2020 6:30 pm

An explanation of the 6502 LOCAL ERROR code is at:
http://mdfs.net/System/Library/BLib/Develop/

Code: Select all

$ bbcbasic
PDP11 BBC BASIC IV Version 0.32
(C) Copyright J.G.Harston 1989,2005-2020
>_

User avatar
Richard Russell
Posts: 1438
Joined: Sun Feb 27, 2011 10:35 am
Location: Downham Market, Norfolk
Contact:

Re: Trapping Errors without Breaking Loops

Post by Richard Russell » Sat Jan 11, 2020 7:04 pm

jgharston wrote:
Sat Jan 11, 2020 6:30 pm
An explanation of the 6502 LOCAL ERROR code is at:
http://mdfs.net/System/Library/BLib/Develop/
Maybe it is, but I don't know how to find it! This is what I see at that URL:

mdfs.png

User avatar
lurkio
Posts: 2686
Joined: Wed Apr 10, 2013 12:30 am
Location: Doomawangara
Contact:

Re: Trapping Errors without Breaking Loops

Post by lurkio » Sat Jan 11, 2020 9:09 pm

Richard Russell wrote:
Sat Jan 11, 2020 7:04 pm
jgharston wrote:
Sat Jan 11, 2020 6:30 pm
An explanation of the 6502 LOCAL ERROR code is at:
http://mdfs.net/System/Library/BLib/Develop/
Maybe it is, but I don't know how to find it!
Click on the little "L" next to "LocErrTest".

:idea:

User avatar
Richard Russell
Posts: 1438
Joined: Sun Feb 27, 2011 10:35 am
Location: Downham Market, Norfolk
Contact:

Re: Trapping Errors without Breaking Loops

Post by Richard Russell » Sat Jan 11, 2020 10:03 pm

lurkio wrote:
Sat Jan 11, 2020 9:09 pm
Click on the little "L" next to "LocErrTest". :idea:
I would never have guessed that! A direct link would have been easier. :roll:

Post Reply

Return to “programming”