Page 1 of 1
Goto/Gosub a variable and psuedo On Goto/Gosub
Posted: Wed Jan 23, 2008 6:08 pm
by DoubleDutch
These routines let you create easy to use jump tables and jump sequences. With them you can jump to a variable, an address held within an array, or a psuedo On Goto/Gosub system with the list af addresses being in the data section.
Code: Select all
Macro On(address,pos=0)
(PeekL(address+(pos*4)))
EndMacro
Macro GotoAddress(var)
JmpCallVarAddress=(var)
!MOV eax,dword [v_JmpCallVarAddress]
!JMP eax
EndMacro
Macro GosubAddress(var)
JmpCallVarAddress=(var)
!MOV eax,dword [v_JmpCallVarAddress]
!CALL eax
EndMacro
GotoAddress(On(?routines,1))
test1: Debug("Between")
test2: Debug("If you don't see between, it worked!")
Dim MyArray(4)
MyArray(0)=?routine0
MyArray(1)=?routine1
MyArray(2)=?routine2
MyArray(3)=?routine3
MyArray(4)=?routine4
For loop=0 To 4
GosubAddress(MyArray(loop))
Next
For loop=0 To 4
GosubAddress(On(?sequencetable,loop))
Next
End
routine0:
Debug("at 0")
Return
routine1:
Debug("at 1")
Return
routine2:
Debug("at 2")
Return
routine3:
Debug("at 3")
Return
routine4:
Debug("at 4")
Return
DataSection
routines:
Data.l ?test1,?test2
sequencetable:
Data.l ?routine0,?routine1,?routine2,?routine3,?routine4
EndDataSection
Posted: Thu Jan 24, 2008 12:40 am
by jack
the Macro GosubAddress is not procedure safe, because of the CALL eax messes with the stack.
Posted: Thu Jan 24, 2008 12:45 am
by DoubleDutch
The return address is pushed on the stack by the call, it get pop'ed off by the return. Why do you think that this messes up the stack?
Posted: Thu Jan 24, 2008 1:30 am
by jack
as long as you use the code outside of procedures you ar OK, but used inde a procedure is asking for trouble, let's say your procedure initializes some variables and then use you macros to go to subroutine that uses the variables, because the CALL pushed the EIP onto the stack, your variables may not point to valid memory.
for the same reason you can't use gosub in a procedure.
Posted: Thu Jan 24, 2008 2:15 am
by DoubleDutch
I think your wrong because the routine will be assembled outside the procedure - when it is assembled it's variable access will not be the procedures stack based variables, but regular program variables. It shouldn't effect the procedure variables at all. I don't think that you could actually access the procedure variables anyhow unless you accessed them directly on the stack?
Here is some code to demo what I mean:
Code: Select all
Global JmpCallVarAddress
Macro On(address,pos=0)
(PeekL(address+(pos*4)))
EndMacro
Macro GotoAddress(var)
JmpCallVarAddress=(var)
!MOV eax,dword [v_JmpCallVarAddress]
!JMP eax
EndMacro
Macro GosubAddress(var)
JmpCallVarAddress=(var)
!MOV eax,dword [v_JmpCallVarAddress]
!CALL eax
EndMacro
Procedure TestProc()
blah=20
GosubAddress(?routine)
Debug("Blah is: "+Str(blah))
EndProcedure
blah=30
TestProc()
TestProc()
End
routine:
Debug("Main blah: "+Str(blah))
Debug("at 1")
Return
I had to add the global at the top, because the JmpCallVarAddress would otherwise be local to the procedure and the actual jmp would get the variable that "belonged" to the main program - so it would go the the last jumped location that the main program used. It was either that or have a local version of the macro that jmp'ed to p.v_JmpCallVarAddress instead of v_JmpCallVarAddress.
I haven't looked at the real gosub - but it will probably work in much the same way?
Posted: Thu Jan 24, 2008 2:25 am
by DoubleDutch
Here is another test to show you it works fine...
Code: Select all
Global JmpCallVarAddress
Macro On(address,pos=0)
(PeekL(address+(pos*4)))
EndMacro
Macro GotoAddress(var)
JmpCallVarAddress=(var)
!MOV eax,dword [v_JmpCallVarAddress]
!JMP eax
EndMacro
Macro GosubAddress(var)
JmpCallVarAddress=(var)
!MOV eax,dword [v_JmpCallVarAddress]
!CALL eax
EndMacro
Procedure TestProc()
blah=20
GosubAddress(?routine)
Debug("Blah is: "+Str(blah))
EndProcedure
blah=30
TestProc()
TestProc()
End
routine:
Debug("Main blah: "+Str(blah))
Gosub routine2
Return
routine2:
Gosub routine3
Gosub routine3
Return
routine3:
Debug("At routine3!")
Return
Posted: Thu Jan 24, 2008 2:40 am
by DoubleDutch
Here is a test that gosubs a routine that calls a procedure that gosubs another routine (it also tests strings)... No problems:
Code: Select all
Procedure TestProc()
string$="test of string"
blah=20
GosubAddress(?routine)
Debug("Blah is: "+Str(blah))
Debug("string: "+string$)
EndProcedure
Procedure TestProc2()
blah=40
GosubAddress(?routine3)
Debug("Proc2 blah: "+Str(blah))
EndProcedure
blah=30
TestProc()
TestProc()
End
routine:
Debug("Main blah: "+Str(blah))
Gosub routine2
Return
routine2:
TestProc2()
TestProc2()
Return
routine3:
Debug("At routine3!")
Return
You need the Macros from the previous post.
Posted: Thu Jan 24, 2008 2:51 am
by jack
DoubleDutch try this then
Code: Select all
Global JmpCallVarAddress
Macro GosubAddress(var)
JmpCallVarAddress=(var)
!MOV eax,dword [v_JmpCallVarAddress]
!CALL eax
EndMacro
Procedure test1()
NewList gosublist.l()
retaddress.l
i.l
For i=1 To 10
GosubAddress(?factorial)
PrintN(Str(fac))
Next
ProcedureReturn
factorial:
If fac.l=0
fac=1
n.l=1
EndIf
fac=fac*n
n=n+1
Return
EndProcedure
OpenConsole()
test1()
Input()
CloseConsole()
End
and you see why I used an extra return label and did not mess with the stack
Code: Select all
Macro _gosub(label,retrn)
AddElement(gosublist())
gosublist()=?retrn
GoToEIP(?label)
EndMacro
Macro _return
retaddress=gosublist()
DeleteElement(gosublist())
GoToEIP(retaddress)
EndMacro
Procedure test1()
NewList gosublist.l()
retaddress.l
i.l
For i=1 To 14
_gosub(factorial,l1) ;you need to supply a return label, in this case l1
PrintN("This never gets executed")
l1: PrintN(Str(fac))
Next
ProcedureReturn
factorial:
If fac.l=0
fac=1
n.l=1
EndIf
fac=fac*n
n=n+1
_return
EndProcedure
OpenConsole()
test1()
Input()
CloseConsole()
End
Posted: Thu Jan 24, 2008 3:03 am
by DoubleDutch
That is because your routine is still in the procedure and your stack will be messed up. My routine gosubs out of the procedure and back. If you want to gosub to an address within the procedure then you need to make sure that the variables in the gosub are either global or static:
Code: Select all
Global fac,n
Procedure test1()
i.l
For i=1 To 10
GosubAddress(?factorial)
PrintN(Str(fac))
Next
ProcedureReturn
factorial:
If fac.l=0
fac=1
n.l=1
EndIf
fac=fac*n
n=n+1
Return
EndProcedure
or
Code: Select all
Procedure test1()
Static fac,n
i.l
For i=1 To 10
GosubAddress(?factorial)
PrintN(Str(fac))
Next
ProcedureReturn
factorial:
If fac.l=0
fac=1
n.l=1
EndIf
fac=fac*n
n=n+1
Return
EndProcedure
Posted: Thu Jan 24, 2008 3:08 am
by jack
DoubleDutch why do you think gosubs are not allowed inside a procedure?
using your macros inside procedures is not safe, and jumping outside of procedures makes me cringe, but you don't want to believe me go ahead.
Posted: Thu Jan 24, 2008 3:24 am
by DoubleDutch
using your macros inside procedures is not safe
But I've demonstrated (by adding 1 line to your test) that they are.
jumping outside of procedures makes me cringe

Why does it make you cringe?
I think that Gosubs are not allowed within a procedure to an address within a procedure because if you access a local variable then you will accessing the wrong location and will get either corrupt data (a read) or you will corrupt the stack (a write).
In my tests Gosub'ing out of a procedure and back should not have these side effects at all. Also Gosub'ing to an address within a procedure and not accessing local variables (other types are fine) will have no side effects either.
but you don't want to believe me go ahead.
Do you mean go ahead and jump or go ahead and gosub?

Posted: Tue Mar 24, 2009 3:32 pm
by mesozorn
In VB, you can easily do:
Code: Select all
Public Sub Test()
a = 20
GoSub Whatever
Exit Sub
Whatever:
MsgBox "Value is " + Str(a)
Return
End Sub
... gosubing and accessing local variables without messing up the stack or causing any problems whatsoever. It should be just as simple to do this in PB.