Page 1 of 2

Gosub from inside Procedures...sometimes very useful.

Posted: Wed Nov 26, 2003 10:27 am
by Psychophanta
I think it should be easy to implement to PB the ability to call a routine ( basic command Gosub) from inside a function to inside that function.

Amiga BlitzBasic2 do it, and it is useful.

Please think that a function is a program; or said almost samething in other way, a program is a function.

Moreover, it should be another big and greeeat difference and advantage over C/C++ :wink:

Posted: Wed Nov 26, 2003 12:25 pm
by Fred
The problem than variable are stack relative and using a gosub will break the whole stuff, that's why I disabled it.

Posted: Wed Nov 26, 2003 1:00 pm
by pbdep
PB language is a very "static" based language staight forwards with a lot of procedures linked to eighother you get a "language"..

As in assembler "CALL" and "RET" do it the same way a GOSUB should
be able to do that.. intepret the new variables and jump back, a protect is usefull here...

Or create a global procedure called GOSUB and bring all the variables with it when calling it..

Odd that GOSUB isn't inside indeed ;-)

Posted: Wed Nov 26, 2003 2:05 pm
by Fred
Try to use CALL and see what will happen :twisted:

Posted: Wed Nov 26, 2003 2:58 pm
by Psychophanta

Code: Select all

Procedure Sub(nu)
  !label1:
  !CALL .str
  !jmp @f
  !.str:
  !mov eax,dword[esp+4]
  !inc eax
  !ret
  !@@:
  ProcedureReturn
EndProcedure

Debug Sub(74)
Here works fine.

Posted: Wed Nov 26, 2003 3:08 pm
by pbdep
Yes works..but thats what i already knew ;-)
Thats the only way to use GOSUB...

Posted: Wed Nov 26, 2003 3:41 pm
by Psychophanta
Yeah...
Still more difficult:

Code: Select all

Procedure Sub(nu) 
  !label1:
  !mov eax,dword[esp] 
  !times 400 CALL near .str0 
  ProcedureReturn
  !.str0:times 300 call .str
  !ret
  !.str:
  !times 4 call out1.out1
  !inc eax 
  !ret 
EndProcedure 

Debug Sub(0)
end
!out1:
!.out1:inc eax
!ret
This works, and that means Gosub should work too :)

Posted: Wed Nov 26, 2003 5:26 pm
by Pupil
Psychophanta wrote:Yeah...
Still more difficult:

Code: Select all

Procedure Sub(nu) 
  !label1:
  !mov eax,dword[esp] 
  !times 400 CALL near .str0 
  ProcedureReturn
  !.str0:times 300 call .str
  !ret
  !.str:
  !times 4 call out1.out1
  !inc eax 
  !ret 
EndProcedure 

Debug Sub(0)
end
!out1:
!.out1:inc eax
!ret
This works, and that means Gosub should work too :)
No, that's not more difficult, this is what Fred means:

Code: Select all

Procedure.l go_sub(a, b, c)
  If a = 1
    !call lbl_a1
  EndIf
  
  Debug "a="+Str(a)+", b="+Str(b)+", c="+Str(c)
  ProcedureReturn

!lbl_a1:
  a = b+c
  !ret
EndProcedure

go_sub(1, 1, 1)
When the expression 'a = b + c' is evaluated the stack is off by 4 bytes, as the return address is stored on the stack, which leads to a crash because of the way PB handles local variables inside procedures i.e. by an offset relative to the stack pointer... in this particular case the return address seems to be overwitten by the value of 'a' and thus tries to jump back to the wrong address when 'ret' is encountered.. It doen't always lead to a crash, but you would surely get the wrong result when doing any kind of calculation. As can be seen if you exchange 'a = b + c' with 'b = a'.

Re: Gosub from inside Procedures...sometimes very useful.

Posted: Wed Nov 26, 2003 5:52 pm
by fsw
Psychophanta wrote:I think it should be easy to implement to PB the ability to call a routine ( basic command Gosub) from inside a function to inside that function.

Amiga BlitzBasic2 do it, and it is useful.

Please think that a function is a program; or said almost samething in other way, a program is a function.

Moreover, it should be another big and great difference and advantage over C/C++ :wink:
Well I'm used to different variants of Subroutines:

In wxBasic and BCX you have Procedures (FUNCTION) and Subroutines (SUB).
Both can have parameters (in PureBasic you can't have parameters in SUBs) but only Procedures give a Return, they have to (in PureBasic a Procedure can have a Return but doesn't have to).

In XBasic and XBlite a Subroutine (SUB) is completely inside a Procedure (FUNCTION) and always local. You can call it inside this Procedure and do whatever you need to do and jump back. You can have as many Subroutines as you need to. But you still are inside the same Procedure. You can't use a label from another Procedure. Also there is no code outside a Procedure (except Declarations...) Every code that normally resides between Procedures is inside a Entry() Procedure. This way every Procedure is self consistent and portable and reusable. Procedures can have a Return but don't have to (like in PureBasic).

After messing around with several BASIC's you get used to it and adapt your coding style to what you are using right now...


EDIT


Just popped in my mind:
if it's to critical to jump to a label outside a procedure, maybe it's possible to allow GoSub inside procedures like in XBasic/XBlite.

Posted: Wed Nov 26, 2003 6:45 pm
by pbdep
..basicly the problem is quicly solved..
Dont use the variable handling from PB itself but create
you own dynamic memory buffer and control the io on it by direct addressing..

That how i create a more flexible PB ;-)

Posted: Wed Nov 26, 2003 6:55 pm
by Psychophanta
Thanx, Pupil i expected that, but the only trouble is operations with function input parameters inside the subroutines (changing esp value), or operations with non shared variables.

Mmmh, just should be very ugly to write in PB-help: "it is borbiden operations with function input parameters inside subroutines for functions (Procedures). The variables must be shared with external world to be able to operate inside a procedure subroutine..." :?

Just your example made like this is allowed:

Code: Select all

Procedure.l go_sub(d,f,g)
Shared a,b,c
  If a = 1 
    !call lbl_a1 
  EndIf 
  
  Debug "a="+Str(a)+", b="+Str(b)+", c="+Str(c) 

  ProcedureReturn 

!lbl_a1:

  a = b+c

  !ret
EndProcedure
a=1:b=1:c=1
go_sub(1,1,1)
:(

Re: Gosub from inside Procedures...sometimes very useful.

Posted: Wed Nov 26, 2003 7:59 pm
by fsw
fsw wrote:
Just popped in my mind:
if it's to critical to jump to a label outside a procedure, maybe it's possible to allow GoSub inside procedures like in XBasic/XBlite.
Adaptation by me:

Code: Select all

Procedure.l go_sub(d,f,g)
  Shared a,b,c

  If a = 1
    GoSub lbl_a1
  EndIf
 
  Debug "a="+Str(a)+", b="+Str(b)+", c="+Str(c)

  ProcedureReturn

  lbl_a1:
    a = b+c
  Return
EndProcedure

a=1:b=1:c=1

go_sub(1,1,1) 

FRED is this possible to do without big trouble :?:

Posted: Wed Nov 26, 2003 8:26 pm
by Fred
Yes, the problem is when you deal with local variables. Global variables are not affected by this limitation.

Posted: Wed Nov 26, 2003 8:59 pm
by fsw
Fred wrote:Yes, the problem is when you deal with local variables. Global variables are not affected by this limitation.
I see - this does not work out:

Code: Select all

Procedure.l go_sub2(d,f,g)
  
  a=d:b=f:c=g
  
  If a = 1
    !call go_sub1_lbl_a1
  EndIf
 
  Debug "a="+Str(a)+", b="+Str(b)+", c="+Str(c)

  ProcedureReturn

!go_sub2_lbl_a1:

  a = b+c

!ret

EndProcedure

go_sub2(1,1,1) 
can you do something about it :?:

Posted: Wed Nov 26, 2003 9:11 pm
by Psychophanta
fsw, that doesn't work because you are creating a, b and c inside procedure, so they are local, so the stack is pointer is decremented each tim you define a local variable.

Well, fsw, Pupil and Fred, please, tell me if i am crazy, or on the contrary, it should be possible to implement a Gosub for procedures in this way:

Code: Select all

Procedure.l go_sub(a, b, c) 
  If a = 1
    !jmp near lbl_a1;<-a Gosub here write next address (rett) into  at compilation time and then just jumps.
    !rett:;                                                       |
  EndIf;                                                         |
  ;                                                              /
  Debug "a="+Str(a)+", b="+Str(b)+", c="+Str(c) ;               |
  ProcedureReturn ;                                           /
;                                                              /
!lbl_a1:;                                                     /
;                                                            /
  a = b+c;                                                  /
!jmp near rett;<-------------------------------------------/ 
EndProcedure 

go_sub(1, 1, 1) 
This is just the pupil example, but only changed CALL, and RET by JMP.

NOTE: Of course the PB "Return" instruction should be replaced by a jmp too. As is shown in the example.