PDA

View Full Version : Better code check before compile?



CuriousOne
- 26th December 2019, 06:28
Hello. Recently accidentially deleted RETURN in one subroutine, and it caused catastrophic failure to attached hardware (almost started a fire). Of course this is my fault, but even on ZX Spectrum we had "gosub without return" error check. So I guess, it'll be nice if this will be implemented in PBP. It does that check for FOR-NEXT, why it can't be done for GOSUB-RETURN?

HenrikOlsson
- 26th December 2019, 11:47
I don't write compilers and/or preprocessors but from my limited point of view I can't really see how a check like that would work in the case of PBP. How would the compiler or preprocessor be able to "match" a RETURN statement with a GOSUB?

You can easily have several RETURN statements and multiple labels within a single subroutine.

Alright, if there's one (or more) GOSUB statements in the program but not a single RETURN statement you know you have a problem but apart from that I'm not sure how it would work.

In your case I suppose the following represents what happened, the RETURN from Label1 got deleted and is missing but that isn't NECCESARILY an error, the code is still correct.

Label1:
...code
...clode

Label2:
...code
...code
RETURN

Out of curiosity, would you share more details about what happened?

/Henrik.

Acetronics2
- 26th December 2019, 12:59
How would the compiler or preprocessor be able to "match" a RETURN statement with a GOSUB?

Hi,

As one RETURN statement can match more than one GOSUB's ...

even math can't do anything for you ... just verify if there's at least one RETURN for one GOSUB ...or number of RETURN is smaller or equal than number of GOSUB

the only way is to run the debugger and verify you jump at the good moment or to the right location ...

for critical applications, only a GOTO - GOTO or multiple local subroutine INCLUDE could secure the code ...

but you also could wipe the critical line ... :D



Alain

pedja089
- 26th December 2019, 13:19
If your code can start fire, you are doing something really wrong.
For first run, always use PSU with limited power(at least current limiting) with no load at output. Move actuators if you have them manually to check sensors.
If you are creating PSU controlled by PIC(or any mcu), simulate output voltage by applying voltage on feedback.
Any way you must have HW protections that would prevent code to do catastrophic damage.

As all software in this world PBP can have unsuspected bugs that doesn't manifest all time. I found one few years ago. Other compilers have many more than one.
Also as this is all clock dependent, it can freeze due falling oscillator eg lot of vibration can cause crystal to breaks.

I saw result of using PLC with untested code on 300ton press, and actuating pressing and pulling actuators, it wasn't pretty... And that could be avoided if hydraulics was done correctly to disallow engaging both actuators in opposite directions. Or using relay that would disable inputs to other solenoids, etc...

Ioannis
- 26th December 2019, 13:39
Added to the above, no one can guarantee you that the software will always run, even with faults in it. MCU's can stop anytime and hardware must protect itself in that case.

Now, regarding Subs, it is not easy or feasible at all, to check for correct pairs of gosub-return. It is not the same as open-close of { and } in C for example.

In the case of Returns, you can have one or 100 for the same sub. So, how will the compiler check?

Ioannis

CuriousOne
- 26th December 2019, 15:22
it is simple. Just count number of Subroutines and RETURNs and make sure they match, that's all, and already done 30 years ago.

Regarding what happened - it was huge (about 30 meters) Christmas tree 8 channel light controller. The code enables lights at max power for 0.1 second, to give "blink" effect. It worked for 2 years without any issues, until now, when I added some new programs, and forgot to add RETURN, so lights remain at high power for about 10 minutes, instead of 0.5second. It ended up with 9 pieces of solid state relays fried, until thermal switch cut off the power :)

CuriousOne
- 26th December 2019, 15:34
Even more simple.

Just add different label for subroutines, say, subroutines should start with SUB statement, after the label. At compile time, do check and that's all.

pedja089
- 26th December 2019, 16:33
Then 80% of my programs wouldn't compile. I often use multiple entry point in same sub. And have only one return at end.

Ioannis
- 26th December 2019, 16:55
Or the reverse. Have one entry and multiple exits:



gosub test

....

test:
if x=1 then
a=1
return
endif

if x=2 then
a=2
return
endif

return


Ioannis

CuriousOne
- 27th December 2019, 08:58
So you're doing it wrong - same for overlapping for-next cycles :)

Ioannis
- 27th December 2019, 14:14
If you mean my example, OK, it is a not efficient one but I was trying to show that you can have many Returns from the same sub.

Sure it can be made more compact or efficient, but still. Compiler has difficult time to check this for errors.

Ioannis

CuriousOne
- 27th December 2019, 20:36
Ok, here is an example (not exact syntax, just to get idea)



MAINCODE: 'do something
high PORTC.0
PAUSE 500
low PORTC.0
Pause 500
GOSUB CHECKER
GOTO MAINCODE
CHECKER:
TOGGLE PORTD.0
RETURN


In above example, if we remove RETURN, it will compile fine in PBP, but will produce unusable code. But even on ZX Spectrum, it would give us "Gosub without return" error, and save our time.

Ioannis
- 28th December 2019, 18:55
Agreed. This is easy to check, but above we stated other cases that are difficult to check.

So a sub-without-return check in whole is not easy to implement. I suppose... Maybe Charles knows better to justify it.

Ioannis

mpgmike
- 29th December 2019, 19:08
There have been situations where if a test meets certain criteria you want to go through 5 sequences of code. However, if one of the criteria is missing, you want to skip the first sequence and do the other 4. On down the line, you may only need to do the last 2. The subroutine may look like;


Main:
SELECT CASE Var
CASE < 50 : GOSUB Sub1
CASE < 100 : GOSUB Sub2
CASE < 150 : GOSUB Sub3
CASE < 200 : GOSUB Sub4
CASE ELSE : GOSUB Sub5
END SELECT
GOTO Main

Sub5:
TOGGLE LED1
Sub4:
TOGGLE LED2
Sub3:
TOGGLE LED3
Sub2:
HIGH LATA.4
Sub1:
LATB = 0
RETURN

Here is a legitimate case of needing only 1 RETURN for multiple subroutines.

pedja089
- 29th December 2019, 23:44
That is exactly what I have on my mind in post #8 (http://www.picbasic.co.uk/forum/showthread.php?t=24018&p=144593#post144593).
Maybe it will be useful if compiler give just warning about missing RETUN.
But That wouldn't prevent bad stuff from happening same as it throwing an error wouldn't prevent that. So no real point of having it.

CuriousOne
- 2nd November 2020, 07:13
Another "bug" found - you can easily refer to out of array bounds and it won't give any error during compile
like

MAS var byte[5]
MAS[10]=20

Won't give any error or warning.

HenrikOlsson
- 2nd November 2020, 08:55
You may have found something you did not know and it IS something to be aware of but it's not a bug. It's like that by design.
It's all covered in details thru out section 7.6 in the manual - read it ;-)

/Henrik.

Ioannis
- 2nd November 2020, 11:10
It is common to other languages too like C for example.

Ioannis

HenrikOlsson
- 2nd November 2020, 11:35
And the compiler is really only able (if so designed) to catch out-of-bounds error when you have a static index like that MAS[10]=20 but in many cases you're using an index variable like MAS[x]=20 and then there is no way for the compiler to know what x is going to be at runtime.

On your typical 80's computer running BASIC, like the ZX Spectrum referenced earlier, the execution was interpreted so the interpreter could catch out of bounds errors during execution. PBP isn't interpreted but say it was or that it in some other way COULD detect that out-of-bounds error - what should happen?

CuriousOne
- 2nd November 2020, 18:29
Yes the example I have is static, not runtime occurrence.

CuriousOne
- 19th November 2020, 17:36
Found another "bug" - if you put more DATA lines in code, than size of built-in eeprom, it will compile without errors, but PicKit 3 programmer will show you "Warning: Hex file loaded is larger than device."

Ioannis
- 19th November 2020, 18:26
Interesting... This is easy to check I guess.

Ioannis

CuriousOne
- 21st February 2021, 15:48
I made a typo, putting = instead of -

hpwm 2,rmd*rmd=1,20000

This statement does not produce any errors, as it should. Also, RMD value remains the same :D

CuriousOne
- 11th April 2021, 10:52
Found another.

Here is sample code, both variants should work same, but they don't



if ticker>150 and ard<140 and atrig=0 then
ticker=1
atrig=1
endif


This one is fine, but this one:


if ticker>150 and ard<140 and atrig=0 then ticker=1: atrig=1


is not - atrig is always set to 1, no matter if ticker and ard values are as defined in if-then thing. It looks like that code after : is handled as separate code, not part of if-then. But, if I add say GOSUB XX after it, it is executed.

pedja089
- 11th April 2021, 11:22
That is not bug. That way should work. This : is same as new row.
So this line

if ticker>150 and ard<140 and atrig=0 then ticker=1: atrig=1

is same as this


if ticker>150 and ard<140 and atrig=0 then ticker=1

atrig=1
I'm using PBP since 2004. And I found only one bug in pbp, and encounter multiple in MPASM.... DT helped me a lot to track bug to MPASM...

CuriousOne
- 11th April 2021, 12:23
no, it is not, because if I add GOSUB statement, it is being triggered on IF-THEN condition only.

pedja089
- 11th April 2021, 15:43
Hehe. Read manual...
This : replaces new row. Same as you press enter on your keyboard. So try to compile code from my code above. You will get same results.
Only thing behind THEN depending on IF. Next statement after : does not.

pedja089
- 11th April 2021, 16:03
From manual

2.18 Line-Concatenation ( : )
Multiple commands may be written on a single line using colon characters to tell PBP where to insert a "virtual" line break.

HenrikOlsson
- 11th April 2021, 17:34
I agree with peja089, the colon acts as a virtual line break.
If more than a signle statement should execute as a result of the evaluation use IF-THEN-ENDIF.

With that said... In section 5.35 of the manual they actually DO show the following as a valid example:

IF B0 <> 10 THEN B0 = B0 + 1: B1 = B1 - 1

And in fact, when compiling the followong code for a 16F628 both the Test1 and Test2 routines results in the same assembly code

A VAR BYTE
B VAR BYTE
C VAR BYTE
D VAR BYTE

Test1:
IF A = 12 THEN B = 2 : C = 3 : D = 4

Test2:
IF A = 12 THEN
B=2
C=3
D=4
ENDIF

Here's the resulting assembly (both versions looks identical to me (wasn't really expecting that to be honest):

_Test1
clrwdt
movf _A, W
sublw 00Ch
btfss STATUS, Z
goto L00001
movlw low (002h)
movwf _B
movlw low (003h)
movwf _C
movlw low (004h)
movwf _D
L00001
_Test2
clrwdt
movf _A, W
sublw 00Ch
btfss STATUS, Z
goto L00003
movlw low (002h)
movwf _B
movlw low (003h)
movwf _C
movlw low (004h)
movwf _D
L00003

That got me curious so I compiled both version of the original code

ticker VAR BYTE
ard VAR BYTE
atrig VAR BYTE

Test1:
if ticker>150 and ard<140 and atrig=0 then ticker=1: atrig=1

Test2:
if ticker>150 and ard<140 and atrig=0 then
ticker=1
atrig=1
ENDIF

And the resulting assebly (less library code) looks like this:


_Test1
movf _ticker, W
movwf R0
movlw low (096h)
call CMPGTB
movwf T1
movf _ard, W
movwf R0
movlw low (08Ch)
call CMPLTB
movwf T2
movf T1, W
movwf FSR
movf T2, W
call LAND
movwf T2
movwf T2 + 1
movf _atrig, W
sublw 000h
btfss STATUS, Z
movlw -1
xorlw 0ffh
movwf T3
movf T2, W
iorwf T2 + 1, W
movwf FSR
movf T3, W
call LAND
movwf T3
movwf T3 + 1
clrwdt
movf T3, W
iorwf (T3) + 1, W
btfsc STATUS, Z
goto L00001
movlw low (001h)
movwf _ticker
movlw low (001h)
movwf _atrig
L00001
_Test2
movf _ticker, W
movwf R0
movlw low (096h)
call CMPGTB
movwf T1
movf _ard, W
movwf R0
movlw low (08Ch)
call CMPLTB
movwf T2
movf T1, W
movwf FSR
movf T2, W
call LAND
movwf T2
movwf T2 + 1
movf _atrig, W
sublw 000h
btfss STATUS, Z
movlw -1
xorlw 0ffh
movwf T3
movf T2, W
iorwf T2 + 1, W
movwf FSR
movf T3, W
call LAND
movwf T3
movwf T3 + 1
clrwdt
movf T3, W
iorwf (T3) + 1, W
btfsc STATUS, Z
goto L00003
movlw low (001h)
movwf _ticker
movlw low (001h)
movwf _atrig
L00003

As far as I can see they are identical, meaning atrig will only get set to 1 if the evaulation is true. So I don't know what might be happening in CuriousOnes case because as far as I can see it IS working as CuriousOne expects - which I did NOT expect.

/Henrik.

pedja089
- 12th April 2021, 00:03
... which I did NOT expect.
/Henrik.

Same here :confused: