Gosub from inside Procedures...sometimes very useful.

Got an idea for enhancing PureBasic? New command(s) you'd like to see?
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Gosub from inside Procedures...sometimes very useful.

Post 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:
Fred
Administrator
Administrator
Posts: 18162
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Post by Fred »

The problem than variable are stack relative and using a gosub will break the whole stuff, that's why I disabled it.
pbdep
User
User
Posts: 68
Joined: Fri Apr 25, 2003 5:18 pm
Location: Netherlands

Post 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 ;-)
Fred
Administrator
Administrator
Posts: 18162
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Post by Fred »

Try to use CALL and see what will happen :twisted:
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post 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.
pbdep
User
User
Posts: 68
Joined: Fri Apr 25, 2003 5:18 pm
Location: Netherlands

Post by pbdep »

Yes works..but thats what i already knew ;-)
Thats the only way to use GOSUB...
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post 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 :)
Pupil
Enthusiast
Enthusiast
Posts: 715
Joined: Fri Apr 25, 2003 3:56 pm

Post 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'.
User avatar
fsw
Addict
Addict
Posts: 1603
Joined: Tue Apr 29, 2003 9:18 pm
Location: North by Northwest

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

Post 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.
Last edited by fsw on Wed Nov 26, 2003 6:47 pm, edited 1 time in total.
pbdep
User
User
Posts: 68
Joined: Fri Apr 25, 2003 5:18 pm
Location: Netherlands

Post 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 ;-)
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post 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)
:(
User avatar
fsw
Addict
Addict
Posts: 1603
Joined: Tue Apr 29, 2003 9:18 pm
Location: North by Northwest

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

Post 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 :?:

I am to provide the public with beneficial shocks.
Alfred Hitshock
Fred
Administrator
Administrator
Posts: 18162
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Post by Fred »

Yes, the problem is when you deal with local variables. Global variables are not affected by this limitation.
User avatar
fsw
Addict
Addict
Posts: 1603
Joined: Tue Apr 29, 2003 9:18 pm
Location: North by Northwest

Post 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 :?:
User avatar
Psychophanta
Always Here
Always Here
Posts: 5153
Joined: Wed Jun 11, 2003 9:33 pm
Location: Anare
Contact:

Post 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.
Post Reply