Page 1 of 2
MACRO: variable evaluation ?
Posted: Mon Apr 23, 2012 2:00 pm
by charvista
I wonder if there is a function that evaluates the entered value in a macro?
Explaining with an example will clarify my question.
This works:
Code: Select all
Macro Goto(x)
Goto x
EndMacro
Goto(There)
End
There:
Debug "We are there!"
End
Now, imagine that I want to put the label in a variable, and then the macro should evaluate the value A, to get "There" instead of using "A"
So, this does
not work:
Code: Select all
Macro Goto(x)
Goto x
EndMacro
A.s="There"
Goto(A)
End
There:
Debug "We are there!"
End
I was thinking of someting like this: (but I don't know if this exists)
Code: Select all
Macro Goto(x)
Goto Eval(x)
EndMacro
to transform "x" in "There", so the macro should read "Goto There".
Any suggestion?
Thanks
PS: if there is a way, then I have a little surprise for you all !

Re: MACRO: variable evaluation ?
Posted: Mon Apr 23, 2012 2:49 pm
by STARGĂ…TE
a Macro is not a function (procedure).
Macros are create on compile time, not on run time.
So you can't evaluate it!
Code for your axample:
Code: Select all
Global ASMLabelPointer
Global NewMap Label.i()
Macro Goto(x)
ASMLabelPointer = Label(x)
!JMP [v_ASMLabelPointer]
EndMacro
Label("Label_1") = ?Label_1
Label("Label_2") = ?Label_2
Label("Label_3") = ?Label_3
Define A.s = "Label_2"
Goto(A)
Label_1:
Debug "We are at Label_1!"
End
Label_2:
Debug "We are at Label_2!"
End
Label_3:
Debug "We are at Label_3!"
End
Re: MACRO: variable evaluation ?
Posted: Mon Apr 23, 2012 3:24 pm
by Tenaja
Or, if you need to determine the destination on the fly (not in compile time) assign the destination to a pointer variable (i.e. *a) and then gosub or goto the pointer value:
Code: Select all
Goto start
one:
Debug 1
Return
two:
Debug 2
End
start:
; this is like a=one : gosub(a)
*a = ?one
! call [p_a]
; this is like a = two : goto(a)
*a = ?two
! jmp [p_a]
Of course you can turn that into macros...
Re: MACRO: variable evaluation ?
Posted: Mon Apr 23, 2012 3:35 pm
by charvista
VERY VERY interesting, Stargate!
It is an excellent start-idea.
a Macro is not a function (procedure).
Macros are create on compile time, not on run time.
So you can't evaluate it!
I know, hence my question...

I don't think I can do a Goto Label_2 with a Procedure.
The "little surprise" I was trying to make, is the insertion of the Gosub in the procedures. Gosub exists in PB, but works only in the main program, outside the procedures.
I am trying that because I am missing the Gosub. Of course, I can use procedures for repeating operations, but then I need to bring all the variables to that procedure, and that's often a lot of work. The only solution is of course to use Goto.
Stargate, maybe you can help? Because I don't know Assembler at all.
Here is the basic idea:
Code: Select all
Macro Gosub(x,y)
Back.s=Eval(y)
Goto x
EndMacro
Macro Return()
Goto Back.s
EndMacro
Debug "Start!"
Gosub(SubVerify,Cont) : Cont: : Debug "Continue the program after GOSUB"
End
SubVerify:
Debug "Verification routine..."
Return()
You see here that the problem is the Back.s variable. I need to evaluate the value of Back.s, in order to return to the label Cont:, when the subroutine is done.
Sure, we must then think further, because we can have nested Gosub's....
Re: MACRO: variable evaluation ?
Posted: Mon Apr 23, 2012 3:38 pm
by charvista
Tenaja wrote:
; this is like a=one : gosub(a)
*a = ?one
! call [p_a]
Hey Tenaja, maybe that's the solution?! I need to try that!
Re: MACRO: variable evaluation ?
Posted: Mon Apr 23, 2012 3:52 pm
by charvista
Yay !!! Tenaja, I could use your ASM code!
Code: Select all
Macro Gosub(x)
*subroutine=?x
! call [p_subroutine]
EndMacro
Debug "Start!"
Gosub(SubVerify)
Debug "Continue the program after GOSUB"
End
SubVerify:
Debug "Verification routine..."
Return
It seems that the ASM code takes the whole line, so I had to put the "Debug "Continue the program after GOSUB" on a new line, instead of the same line. This is apparently not possible:
Code: Select all
Gosub(SubVerify) : Debug "Continue the program after GOSUB"
This is a minor problem, a pity, but it is a rule that we need to remember. But the Gosub works!!!
Thank you sooo much!!!
Re: MACRO: variable evaluation ?
Posted: Mon Apr 23, 2012 5:32 pm
by Tenaja
charvista wrote:
I am trying that because I am missing the Gosub. Of course, I can use procedures for repeating operations, but then I need to bring all the variables to that procedure, and that's often a lot of work. The only solution is of course to use Goto.
Glad to help... but it will not change the dilemma you mention here. You have to understand scope. If you can't access a var in a proc, then a goto to a different code section will not change that. The way local variables work is by an offset from the stack pointer. The offset is based on when the proc is entered, and any changes to the stack (either with Gosub's, other proc calls, or any other means) will cause the references to change. Consider this "pseudo" code:
Code: Select all
Procedure Proc1()
Protected x ; x is "stackoffset + 0"
LabelAtProc1:
x= 1
EndProcedure
Procedure Proc2:
Protected y ; y is also "stackoffset + 0"
Protected z ; z is "stackoffset + 4" (assuming 32-bit os and y is a 4-byte integer)
Goto LabelAtProc1 ; (using new asm code)
EndProcedure
While incomplete, it serves the purpose of explaining that when you arrive at the goto label, the "x=1" command will actually change the Y variable from the originating Proc! This is because both procs use (stack) as a reference for the first variable, and a goto does not change the stack. For the second local var, it is (stack + (var 1 size)) for the second variable, and keep adding offsets for each new local.
Anyway, if you are just trying to save the work of copying variables, then you have two options: Make all of those vars Global, or put the code section in another Proc. You'll just have problems if you try to execute a "subroutine" in the middle of a proc if that sub references any local vars. There is a reason Fred prohibits Goto and Gosub in a proc, and it is to prevent code screwups.
Re: MACRO: variable evaluation ? >> GOSUB <<
Posted: Mon Apr 23, 2012 10:17 pm
by charvista
charvista (myself) wrote:
[...] I can use procedures for repeating operations, but then I need to bring all the variables to that procedure [...]
Yes Tenaja, what I wrote could be understood in two manners. I believe that you understood that I would like to do a Goto/Gosub from a procedure to another procedure like in your quick example. But I mean this: I need to bring (pass) all the variables to that procedure (through the argument variables)
So, instead of including all the variables the subroutine needs, as arguments to execute the 'subroutine', I preferred a Gosub IN THE SAME PROCEDURE. And I think that Goto's and Gosub's are perfectly legal within the procedure...
Your example to jump from Proc2() to Proc1() is luckily
*not* what I am intending to do. Once, I accidentally made a Goto to a label that existed in another procedure, and the program crashed. This was very obvious!
I only use Goto inside Proc1()
*OR* inside Proc2(). And now I will also use the new Gosub only inside Proc1()
*OR* inside Proc2(). I hope that this way will not cause any problems.
EDIT: I discovered that your ASM does not work within procedures

Re: MACRO: variable evaluation ?
Posted: Mon Apr 23, 2012 10:57 pm
by Tenaja
It works if you make the var a global:
Code: Select all
Global *a
Procedure testthis()
Protected *b
; this is like a=one : gosub(a)
*a = ?one
! call [p_a]
; this is like a = two : goto(a)
*a = ?two
! jmp [p_a]
ProcedureReturn
one:
Debug 1
Return
two:
Debug 2
; End
EndProcedure
testthis()
The reason is because of the stack reference with local vars. See the output differences:
Code: Select all
; *b = ?one
MOV ebp,l_one
MOV dword [esp],ebp ;local has stack (that's esp) offset of 0
; *c = ?two
MOV ebp,l_two
MOV dword [esp+4],ebp ; local as the second var is esp+4, in 32-bit system.
;
; *a = ?one
MOV ebp,l_one
MOV dword [p_a],ebp ;global has fixed reference, p_a.
(edit: added a second local variable demo.)
Re: MACRO: variable evaluation ?
Posted: Mon Apr 23, 2012 11:05 pm
by charvista
The big problem is that the Return does not work in a procedure.... your code stops at the Return line.
Re: MACRO: variable evaluation ?
Posted: Mon Apr 23, 2012 11:20 pm
by charvista
Consider this very easy example...
I am doing a fake Gosub VerifyR, but in fact, I am doing two Goto's... If the Return worked in the procedure, then I could remove the annoying Vok: label....
Code: Select all
Global *LabelGosub
Global *LabelReturn
Macro Gosub(x,y)
*LabelGosub=?x
*LabelReturn=?y
!JMP [p_LabelGosub]
EndMacro
Macro Return()
!JMP [p_LabelReturn]
EndMacro
Procedure RandomAverage()
For i=1 To 100000
r=Random(2000-1)
Gosub(VerifyR,Vok)
Vok:
Next
Debug Str(H)+" times more than half"
Debug Str(L)+" times less than half"
ProcedureReturn; Quit Proc()
VerifyR:
If r>1000-1
H+1
Else
L+1
EndIf
Return()
EndProcedure
RandomAverage()
Re: MACRO: variable evaluation ?
Posted: Mon Apr 23, 2012 11:49 pm
by Tenaja
charvista wrote:The big problem is that the Return does not work in a procedure.... your code stops at the Return line.
It does not fail on my computer, but I am running 32-bit OS. Are you on 64-bit? If so, there may be a change to the asm required.
Here is the same code slightly modified to show it does not have a problem with the return:
Code: Select all
Global *a
Procedure testthis()
Protected *b
Protected *c
*b = ?one
*c = ?two
; this is like a=one : gosub(a)
*a = ?one
! call [p_a]
; this is like a = two : goto(a)
*a = ?two
! jmp [p_a]
ProcedureReturn
one:
Debug 1
Return
Debug "Should never get here."
two:
Debug 2
; End
EndProcedure
testthis()
Debug 3
Re: MACRO: variable evaluation ?
Posted: Mon Apr 23, 2012 11:58 pm
by charvista
Yes, I am on a 64-bit Windows 7 now. (This afternoon I was on a 32-bit, but I moved since then).
In your last example, it stops at line 24, that's the line that contains "Return" after Debug 1, and the error message is: "[ERROR] Invalid memory access. (write error at address 1)"
Glad that we are working together to find a Gosub solution
EDIT:

I confirm that your last example works perfectly on a 32-bit Windows 7. (I tried on my laptop)
Re: MACRO: variable evaluation ?
Posted: Tue Apr 24, 2012 12:32 am
by Tenaja
The easiest way is if someone experienced with 64-bit chimes in and tells us the conversion. If you don't want to wait, this is how to figure out asm from PB...
First--do you know how to view PB's asm output? If not, install this plugin in your IDE:
http://purebasic.fr/english/viewtopic.php?f=12&t=30864
I actually made a Menu button in my IDE to open it in Notepad++.
Then you write some goto's, gosub's and pointer assignments and see what the 64-bit asm codes are, so you can add them with CompilerIf's for 64-bit OS's.
Basically, putting the varname in brackets means it is an indirect call. A direct call such as "call x" is read like a "gosub x" where x is a label, but an indirect call such as "call [y]" is read like "gosub the address pointed to in y", so it requires a pointer, and not a label name. But the 64-bit opcodes are slightly different, and since I don't have a 64-bit os handy, I can't tell you the differences. I do know that you should use integer variables, since they are automatically sized.
It could be that the only difference is how Fred converts the variable name...but I don't know.
Re: MACRO: variable evaluation ?
Posted: Tue Apr 24, 2012 9:35 am
by Danilo
Tested on Windows with 32bit and 64bit PureBasic:
Code: Select all
;--------------------------------------------------------------------------------------------------------
;
; START INCLUDE "Sub_SingleCall.pbi"
;
;--------------------------------------------------------------------------------------------------------
Threaded *__Sub_ReturnAddress__
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
CompilerIf #PB_Compiler_Processor = #PB_Processor_x86 Or #PB_Compiler_Processor = #PB_Processor_x64
! JMP [p___Sub_ReturnAddress__]
!EndSub_Name:
CompilerElse
CompilerError "Macro EndSub: unsupported processor"
CompilerEndIf
EndMacro
Macro ExitSub
CompilerIf #PB_Compiler_Processor = #PB_Processor_x86 Or #PB_Compiler_Processor = #PB_Processor_x64
! JMP [p___Sub_ReturnAddress__]
CompilerElse
CompilerError "Macro ExitSub: unsupported processor"
CompilerEndIf
EndMacro
Macro Call(_name_)
CompilerSelect #PB_Compiler_Processor
CompilerCase #PB_Processor_x86
! MOV dword [p___Sub_ReturnAddress__],@f
! JMP _sub_#_name_
!@@:
CompilerCase #PB_Processor_x64
! MOV RAX, @f
! MOV qword [p___Sub_ReturnAddress__],RAX
! JMP _sub_#_name_
!@@:
CompilerDefault
CompilerError "Macro Call(): unsupported processor"
CompilerEndSelect
EndMacro
;--------------------------------------------------------------------------------------------------------
;
; END INCLUDE "Sub_SingleCall.pbi"
;
;--------------------------------------------------------------------------------------------------------
;XIncludeFile "Sub_SingleCall.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(arg)
Sub(testthis2_one)
Debug "In Sub testthis2_one"
For i = 1 To 2
Debug "Arg is: "+Str(arg)
If arg = -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 "----------------------"
Sub(globalsub_1)
Debug "In Sub globalsub_1"
EndSub
Sub(globalsub_2)
Debug "In Sub globalsub_2"
Debug a
EndSub
Sub names can only be used once or you get an "PureBasic - Assembler error" -> "sub_NAME: error: symbol already defined." (NAME is you sub name),
so use unique names for each sub in procedures or global subs.
There is a jump over the sub, so if you forget ProcedureReturn the code inside the sub is not called - it always jumps over the sub.
You are not allowed to call a sub from within a sub or other nesting. That's the limitation of using a global return pointer,
so only 1 sub at a time can be active.
EDIT1: Added ExitSub for jumping back immediately.
EDIT2: Changed Macro Call() for 32bit mode (removed use of EAX register).
EDIT3: renamed include to 'Sub_SingleCall.pbi'