BBC BASIC for Windows
Programming >> Assembly Language Programming >> CALLing BASIC from ASSEMBLY
http://bb4w.conforums.com/index.cgi?board=assembler&action=display&num=1225371003

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:
  xchg esp,[saved_esp] 

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. smiley
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