CALLing BASIC from ASSEMBLY
Post by Michael Hutton on Oct 30th, 2008, 12:50pm
If, hypothetically, I wanted to exit a machine code routine to call a BASIC procedure and then return to the machine code (which could be inside another subroutine itself) could this be done?
Why? PRINTing to the screen and manipulation of dialogue boxes is sooo much easier in BASIC!
I suppose you would have to 'invoke' the BB4W interpreter to take over for the procedure and then return to the machine code again.
Would it involve suspending threads?
I know it sounds a bit too complicated for what I need to do, well, have 90% done anyway but the idea crossed my mind and I was wondering if it can be done, rather than is it worth it.
Michael
Re: CALLing BASIC from ASSEMBLY
Post by admin on Oct 30th, 2008, 3:26pm
Quote:If, hypothetically, I wanted to exit a machine code routine to call a BASIC procedure and then return to the machine code (which could be inside another subroutine itself) could this be done? |
|
Well, the rub is in your qualifier "which could be inside another subroutine itself" which complicates things somewhat.
What you'd have to do is determine 'ahead of time' all the possible BASIC procedures that you might want to execute 'from machine code', then code the BASIC part something like the following:
Code:
P% = code_start
REPEAT
B% = USR(P%)
CASE B% OF
WHEN 1: PROCbasic1
WHEN 2: PROCbasic2
REM. etc.
ENDCASE
UNTIL B% = 0
and the assembler part something like the following:
Code:
.code_start
; Some assembler code here
; When you want to 'call' BASIC, load P% with
; the 'return address' and then ret:
mov eax,which_BASIC_procedure
mov dword [^P%],next
ret
.next
; Assembler code now continues until final exit:
mov eax,0
ret
To make it possible to do this even if your machine code is within a subroutine (or if items are pushed on the stack) you'd need to implement a 'dual stack' arrangement in which the stack being used when BASIC calls the machine code is different from the stack used for PUSHes and CALLs within the machine code itself.
BB4W uses this 'dual stack' technique quite a lot itself, and it uses the following instruction to 'swap' the stacks:
Code:
So, not particularly difficult, but fiddly.
Richard.
Re: CALLing BASIC from ASSEMBLY
Post by David Williams on Oct 30th, 2008, 5:05pm
I, for one, would be interested to see what Michael does with this, if anything.
Re: CALLing BASIC from ASSEMBLY
Post by admin on Oct 30th, 2008, 6:40pm
Quote:I, for one, would be interested to see what Michael does with this, if anything. |
|
Here is a complete program illustrating the technique, including 'calling' BASIC from machine code both while the stack is in use and from within a subroutine:
Code:
REM Example of 'calling' BASIC from assembler code
REM Richard Russell, 30-Oct-2008
PROCassemble
P% = code_start
REPEAT
B% = USR(P%)
CASE B% OF
WHEN 1: PROC1
WHEN 2: PROC2
ENDCASE
UNTIL B% = 0
PRINT "Finished"
END
DEF PROC1
PRINT "BASIC procedure 1 called"
ENDPROC
DEF PROC2
PRINT "BASIC procedure 2 called"
ENDPROC
DEF PROCassemble
DIM mystack% 255, gap% 1023, code% 255, L% -1
FOR pass% = 8 TO 10 STEP 2
P% = code%
[OPT pass%
.saved_esp dd mystack%+256
.help db "help":db 13
;
.code_start
xchg esp,[saved_esp]
;
pushad
mov dword [^P%],next1
mov eax,1
xchg esp,[saved_esp]
ret ; 'call' BASIC while stack is in use
.next1
xchg esp,[saved_esp]
popad
;
mov edx,help
xchg esp,[saved_esp]
call "oscli" ; output something from assembler code
xchg esp,[saved_esp]
;
call subroutine
;
xchg esp,[saved_esp]
mov eax,0
ret
;
.subroutine
mov dword [^P%],next2
mov eax,2
xchg esp,[saved_esp]
ret ; 'call' BASIC from inside a subroutine
.next2
xchg esp,[saved_esp]
ret
]
NEXT pass%
ENDPROC
Re: CALLing BASIC from ASSEMBLY
Post by David Williams on Oct 31st, 2008, 07:36am
Quote:Here is a complete program illustrating the technique, including 'calling' BASIC from machine code both while the stack is in use and from within a subroutine: |
|
Thanks Richard, excellent -- saved to my harddrive for closer scrutiny and education later.
Re: CALLing BASIC from ASSEMBLY
Post by admin on Oct 31st, 2008, 09:33am
Quote:excellent -- saved to my harddrive for closer scrutiny and education later. |
|
A couple of further thoughts, for what they may be worth.
1. The entire 'call BASIC' code could be hived off into a subroutine for neatness (you'd call it with eax indicating the procedure to be 'called'):
Code:
.callbasic
xchg esp,[saved_esp]
mov dword [^P%],next
ret
.next
xchg esp,[saved_esp]
ret
2. You could use indirect procedure calls instead of a CASE statement:
Code:
P% = code_start
REPEAT
PROC(USR(P%))
UNTIL FALSE
(you'd have to exit via a PROC as well).
In this case the 'call' from assembler to BASIC would want to be as follows:
Code:
mov eax,^PROC1
call callbasic
This provides a more general way of 'calling' any (parameterless) procedure from assembler code.
Re: CALLing BASIC from ASSEMBLY
Post by admin on Nov 2nd, 2008, 4:33pm
Now written up on the Wiki (with slight modifications):
http://bb4w.wikispaces.com/Calling+BASIC+from+assembler+code
Richard.
Re: CALLing BASIC from ASSEMBLY
Post by Michael Hutton on Nov 2nd, 2008, 11:08pm
Thank you very much Richard. I'm sorry to seem impolite for not answering sooner.
I have been thinking about this one for a few days but had come nowhere near a 'dual-stack"! Thank you for the pointers. Yet again, great stuff.
The reason I wanted it was to simply manipulate dialogue boxes, which I find a lot easier in BASIC than machine code, but the machine code itself is called from yet another bit of code.
Michael
Re: CALLing BASIC from ASSEMBLY
Post by admin on Nov 3rd, 2008, 08:26am
Quote:I have been thinking about this one for a few days but had come nowhere near a 'dual-stack"! |
|
Although I've never been a great fan of forums (fora?) you will hopefully feel more comfortable asking here when you get stuck, rather than spending too much time struggling with a problem, or eventually succeeding but in so doing re-inventing the wheel.
Richard.
P.S. Are you still hoping to report the instruction timings you measured?
Re: CALLing BASIC from ASSEMBLY
Post by Michael Hutton on Nov 3rd, 2008, 12:39pm
Well, this is what I sort of want to do. This is a shell of a library to dump the registers (only four of the general and efalgs at the moment) within a machine code program which can be used to help with debugging.
Obviously the code is a bit 'loose' at the moment. After the first code segment there is another test program to get an output.
They should be in the same directory.
Code:
REM REGDUMPLIB
REM Michael Hutton
DEFPROCDebug(A%,U%)
LOCAL B%,P%
B% = USR(A%)
WHILE B%
PROC(B%)
B% = USR(P%)
ENDWHILE
ENDPROC
DEFPROCshutdown(M%)
SYS "GlobalFree", M%
ENDPROC
DEFFN_initdump(RETURN A%, RETURN M%)
LOCAL mystack, gap, code, L%
LOCAL pass, saved_esp, start, BASIC, next
SYS "GlobalAlloc", &40, 300 TO M%
INSTALL @lib$ + "WINLIB5"
DIM mystack 255, gap 2048, code 5000, L% -1
FOR pass=8 TO 10 STEP 2
P%=code
[
OPT pass
.saved_esp
dd mystack+256
]
P%+=2048
[
OPT pass
;REM ENTRY POINT FROM BASIC
.start
xchg esp,[saved_esp]
;REM here I want to transfer control to the userscode however
;REM I might be "osf*****" if he calls "os****" ??
call [^U%]
;REM exit back to BASIC
mov eax,0
xchg esp,[saved_esp]
ret
.A%
pushad
pushfd
pushfd
mov dword [M%],eax
mov dword [M%+4],ebx
mov dword [M%+8],ecx
mov dword [M%+12],edx
pop eax
mov dword [M%+16],eax
mov eax,^PROCdosomething
call BASIC
popfd
popad
ret
.BASIC
xchg esp,[saved_esp]
mov dword [^P%],next
ret
.next
xchg esp,[saved_esp]
ret
]
NEXT
REM H% = FN_editbox("",100,100,400,200,100,&FF0804)
=start
REM This will be a dialogue box
DEFPROCdosomething
PRINT '"The Registers in the code we are debugging are:"
PRINT "EAX = ";!(somememory%)
PRINT "EBX = ";!(somememory%+4)
PRINT "ECX = ";!(somememory%+8)
PRINT "EDX = ";!(somememory%+12)
PRINT "EFLAGS = ";!(somememory%+16)
ENDPROC
save this as REGDUMPLIB in the same directory as this...
Code:
INSTALL @dir$+"REGDUMPLIB"
whateveriwant%=FN_initdump(whatever%,somememory%)
DIM code 100
FOR pass=0 TO 2 STEP 2
P%=code
[
OPT pass
.userscode
mov eax,65
call whatever%
call "oswrch"
push eax
mov ebx,eax
shr ebx,5
call whatever%
pop eax
mov ecx,ebx
mov edx,ebx
.loop
shr eax,1
shl edx,1
call whatever%
loop loop
ret
]
NEXT
PROCDebug(whateveriwant%,userscode)
PROCshutdown(somememory%)
END
Michael