charvista wrote:If I understand well from your code:
1)-- Sub can be used within procedure as well outside procedure, thus anywhere.
2)-- Sub names can be only used once. Good to know, and that's the same rule as labels. OK, this is not a problem.
3)-- ExitSub works similarly to Break. (Excellent!)
4)-- Sub's do not need to be declared, they can be anywhere, before or after the caller.
Please correct me if I am wrong.
That's all correct.
charvista wrote:5)-- Sub's cannot be nested. This is perhaps the only point which is a pity. It is rare that a sub calls itself (but it occurs), however, it is very frequent that a sub calls another sub. If if was possible to define a different global pointer for each sub that is called, then I think that any number of nesting would be possible?
Obviously, any sub that is entered should be exitted in the same order.
I added a call stack now, so Subs can call Subs that call Subs.
At the start of the include you find: #SUB_STACKSIZE = 100000
It means you can call Subs nested with 100,000 steps deep.
If you need more, change #SUB_STACKSIZE to how many million nested calls you need.
We speak about nested calling here. You can not nest the Subs in code.
This is not allowed:
You have to write the Subs one after another. It is nested calling that is possible:
Code: Select all
Sub(Nested_1)
Call(Nested_2)
EndSub
Sub(Nested_2)
Call(Nested_3)
EndSub
Sub(Nested_3)
EndSub
Nested calling works, but please note: It is not thread safe yet!
The Sub call stack is global now, so different threads would use the same stack and things would get messed up.
But now the code with call stack:
Code: Select all
;--------------------------------------------------------------------------------------------------------
;
; START INCLUDE "Sub.pbi"
;
;--------------------------------------------------------------------------------------------------------
#SUB_STACKSIZE = 100000
;--------------------------------------------------------------------------------------------------------
Global __Sub_ReturnAddress__.s{(#SUB_STACKSIZE+1)*(SizeOf(Integer)/SizeOf(character))}
Global __Sub_ReturnIndex.i
Macro Sub(_name_)
CompilerIf #PB_Compiler_Processor = #PB_Processor_x86 Or #PB_Compiler_Processor = #PB_Processor_x64
!EndSub_Name equ __EndSub_#_name_
! JMP EndSub_Name
!_sub_#_name_:
CompilerElse
CompilerError "Macro Sub(): unsupported processor"
CompilerEndIf
EndMacro
Macro EndSub
CompilerSelect #PB_Compiler_Processor
CompilerCase #PB_Processor_x86
! MOV dword EAX, v___Sub_ReturnAddress__
! MOV dword EDX, [v___Sub_ReturnIndex]
! JMP dword [EAX+EDX*4]
!EndSub_Name:
CompilerCase #PB_Processor_x64
! MOV RAX, v___Sub_ReturnAddress__
! MOV R8, [v___Sub_ReturnIndex]
! JMP qword [RAX+R8*8]
!EndSub_Name:
CompilerDefault
CompilerError "Macro EndSub: unsupported processor"
CompilerEndSelect
EndMacro
Macro ExitSub
CompilerSelect #PB_Compiler_Processor
CompilerCase #PB_Processor_x86
! MOV dword EAX, v___Sub_ReturnAddress__
! MOV dword EDX, [v___Sub_ReturnIndex]
! JMP dword [EAX+EDX*4]
CompilerCase #PB_Processor_x64
! MOV RAX, v___Sub_ReturnAddress__
! MOV R8, [v___Sub_ReturnIndex]
! JMP qword [RAX+R8*8]
CompilerDefault
CompilerError "Macro ExitSub: unsupported processor"
CompilerEndSelect
EndMacro
Macro Call(_name_)
CompilerSelect #PB_Compiler_Processor
CompilerCase #PB_Processor_x86
! INC dword [v___Sub_ReturnIndex]
! MOV dword EAX, v___Sub_ReturnAddress__
! MOV dword EDX, [v___Sub_ReturnIndex]
! MOV dword [EAX+EDX*4], @f
! JMP _sub_#_name_
!@@:
! DEC dword [v___Sub_ReturnIndex]
CompilerCase #PB_Processor_x64
! INC qword [v___Sub_ReturnIndex]
! MOV R9, @f
! MOV RAX, v___Sub_ReturnAddress__
! MOV R8, [v___Sub_ReturnIndex]
! MOV qword [RAX+R8*8],R9
! JMP _sub_#_name_
!@@:
! DEC qword [v___Sub_ReturnIndex]
CompilerDefault
CompilerError "Macro Call(): unsupported processor"
CompilerEndSelect
EndMacro
;--------------------------------------------------------------------------------------------------------
;
; END INCLUDE "Sub.pbi"
;
;--------------------------------------------------------------------------------------------------------
;XIncludeFile "Sub.pbi"
Procedure RandomAverage()
For i=1 To 100000
r=Random(2000-1)
Call(VerifyR)
Next
Debug Str(H)+" times more than half"
Debug Str(L)+" times less than half"
ProcedureReturn; Quit Proc()
Sub(VerifyR)
If r>1000-1
H+1
Else
L+1
EndIf
EndSub
EndProcedure
Procedure testthis()
Call(one)
Call(two)
ProcedureReturn ; no problem if you forget it
Sub(one)
Debug 1
EndSub
Debug "Should never get here, but it is no problem if you forget ProcedureReturn"
Sub(two)
Debug 2
EndSub
EndProcedure
Procedure testthis2(arg1)
Sub(testthis2_one)
Debug "In Sub testthis2_one"
For i = 1 To 2
Debug "Arg is: "+Str(arg1)
If arg1 = -1
Debug "ExitSub"
ExitSub
Debug "after ExitSub"
EndIf
Next
EndSub
;Sub(one)
; not allowed, Sub(one) already defined
;EndSub
Call(testthis2_one)
EndProcedure
RandomAverage()
Debug "----------------------"
testthis()
Debug 3
Debug "----------------------"
testthis2(123)
Debug "----------------------"
testthis2(-1)
Debug "----------------------"
a = 12
Call(globalsub_2)
Debug "----------------------"
Call(Nested_1)
Debug "OK"
Sub(globalsub_1)
Debug "In Sub globalsub_1"
Debug a
EndSub
Sub(globalsub_2)
Debug "In Sub globalsub_2"
Debug a
Call(globalsub_1)
EndSub
Sub(Nested_1)
Debug "In Sub Nested_1"
Call(Nested_2)
Debug "Back in Sub Nested_1"
EndSub
Sub(Nested_2)
Debug "In Sub Nested_2"
Call(Nested_3)
Debug "Back in Sub Nested_2"
EndSub
Sub(Nested_3)
Debug "In Sub Nested_3"
EndSub
BTW: I updated also the first code (single call Subs).