'===========================================================================
' Subject: ASSEMBLY IN QBASIC 3: INT86QB      Date: 12-01-96 (05:37)       
'  Author: Rick Elbers                        Code: QB, QBasic, PDS        
'  Origin: t030611@tip.nl                   Packet: ASMCODE.ABC
'===========================================================================
'ASSEMBLY IN QBASIC 3: INT86QB
'QBASIC ABOVE AND BEYOND PART...[ASSEMBLY LIBRARIE]
'---------------------------
'Rick Elbers november 1996
'--------------------------
DECLARE FUNCTION int2str$ (sword%)
DECLARE FUNCTION int86qb$ (intnr%, flag%, ax%, bx%, cx%, dx%, di%, si%, bp%, ds%, es%)

'INTRODUCTION                                     
'-------------
'When i felt the desire to write a routine that make another break in the
'wall of QBASIC opponents who maintain that QBASIC did not have a call
'interrupt, i have choosen for the ASIC approach which distinguishes
'itself from QUICK BASIC(compiler) in that it did not force upon you
'the necessity of defining a REGSTYPE in your main.
'In my humble opinion you should hold things as modulair as possible,meaning
'as much as possible defined in your subs and functions. So therefore, instead
'of INTERRUPT(int%,INREGS as REGStype,OUtregs as REGStype), you will
'be confronted with INT86QB(int%,FLAG%,AX%,BX%,CX%,DX%,DI%,SI%,BP%,DS%,ES%)
'Since it is a function it returns. In fact it returns an array of all the
'registers. For help in parsing this you might take a look at the function
'partstring$ i wrote( available on my homepage in Lstr.zip).

'First notice should be that int86qb returns also flags (unlike int86 of ASIC)
'----------------------------------------------------------------------------
CLS
a$ = int86qb$(&H21, 0, &H200, 0, 0, &H41, 0, 0, 0, 0, 0)
LOCATE 2, 1: PRINT a$

'----------------------------------------------------------------------------
'Although the setup of this function might have his advantages, optimal speed
'or minimum length is not one of them. In an optimized CALL INTERRUPT the
'assembly routine could be speeded up *a lot* AND narrowed down to 80 bytes,
'by the way of using STACK instead of DATAS% and XCHG reg, variable instead
'of PUSH reg /MOV reg,value/ MOV variable, reg/POP reg instructions. The only
'advantage of the present int86qb routine might be that it is not as difficult
'as the routine that uses the optimized method, since for the biggest part
'int86qb is merely repetitive.
'----------------------------------------------------------------------------

FUNCTION int2str$ (sword%)
'This function is translating SWORD Integers into a string. Its only use
'is when you still use asm$ for assembler functions( like i do). In that
'case you can make your integer values usable ..
'THis function simply translates the hexa bytes
'into stringbytes as is.
'----------------------------------------------------
DEF SEG = VARSEG(sword%)
ptr% = VARPTR(sword%)
int2str$ = CHR$(PEEK(ptr%)) + CHR$(PEEK(ptr% + 1))
DEF SEG

END FUNCTION

FUNCTION int86qb$ (intnr%, flag%, ax%, bx%, cx%, dx%, di%, si%, bp%, ds%, es%)
'------------------------------------------------------------------------
'Conversion of all integers in strings
flag$ = LEFT$(int2str$(flag%), 1): ax$ = int2str$(ax%):
bx$ = int2str$(bx%): cx$ = int2str$(cx%): dx$ = int2str$(dx%):
di$ = int2str$(di%): si$ = int2str$(si%): bp$ = int2str$(bp%):
ds$ = int2str$(ds%): es$ = int2str$(es%):
'Now all integersvalues are direct usable in asm$
'------------------------------------------------------------------------
DIM datas%(11)    'that is all we need
dataseg% = VARSEG(datas%(0)): flagoff% = VARPTR(datas%(0))
dataseg$ = int2str$(dataseg%): flagoff$ = int2str$(flagoff%)
axoff$ = int2str$(flagoff% + 2): bxoff$ = int2str$(flagoff% + 4)
cxoff$ = int2str$(flagoff% + 6): dxoff$ = int2str$(flagoff% + 8)
dioff$ = int2str$(flagoff% + 10): sioff$ = int2str$(flagoff% + 12)
bpoff$ = int2str$(flagoff% + 14): dsoff$ = int2str$(flagoff% + 16)
esoff$ = int2str$(flagoff% + 18): intnroff$ = int2str$(flagoff% + 20)
'Now all integers are directly adressable in memory through asm$
'We could have used only this as pointers and leave the immediate values
'but....we don't.
'------------------------------------------------------------------------
asm$ = ""
asm$ = asm$ + CHR$(&H9C)                       'pushf             
asm$ = asm$ + CHR$(&H50)                       'push ax         
asm$ = asm$ + CHR$(&H53)                       'push bx         
asm$ = asm$ + CHR$(&H51)                       'push cx        
asm$ = asm$ + CHR$(&H52)                       'push dx         
asm$ = asm$ + CHR$(&H57)                       'push di         
asm$ = asm$ + CHR$(&H56)                       'push si         
asm$ = asm$ + CHR$(&H55)                       'push bp         
asm$ = asm$ + CHR$(&H1E)                       'push ds         
asm$ = asm$ + CHR$(&H6)                        'push es         

'2)LOAD REGISTERS
'First the flags( questionable if this ever happens):
asm$ = asm$ + CHR$(&HB4) + flag$              'mov ax,flag$
asm$ = asm$ + CHR$(&H9E)                      'sahf stores ah into flags

'Segment registers mbv ax :  only when not (accidentally) 0
IF ds% <> 0 THEN                              'safety
  asm$ = asm$ + CHR$(&HB8) + ds$              'mov ax,ds$
  asm$ = asm$ + CHR$(&H8E) + CHR$(&HD8)       'mov ds,ax
END IF
IF es% <> 0 THEN                              'safety
  asm$ = asm$ + CHR$(&HB8) + es$              'mov ax,es$
  asm$ = asm$ + CHR$(&H8E) + CHR$(&HC0)       'mov es,ax
END IF
'the rest of registers :
asm$ = asm$ + CHR$(&HB8) + ax$                'mov ax,ax$
asm$ = asm$ + CHR$(&HBB) + bx$                'mov bx,bx$ 
asm$ = asm$ + CHR$(&HB9) + cx$                'mov cx,cx$ 
asm$ = asm$ + CHR$(&HBA) + dx$                'mov dx,dx$ 
asm$ = asm$ + CHR$(&HBF) + di$                'mov di,di$ 
asm$ = asm$ + CHR$(&HBE) + si$                'mov si,si$ 
asm$ = asm$ + CHR$(&HBD) + bp$                'mov bp,bp$
'oke now the interrupt himself
asm$ = asm$ + CHR$(&HCD) + CHR$(intnr%)       'interrupt nr

'First make our DATAS% adressable:
asm$ = asm$ + CHR$(&H1E)                         'push ds
asm$ = asm$ + CHR$(&HB8) + dataseg$              'mov ax,DATASseg$
asm$ = asm$ + CHR$(&H8E) + CHR$(&HD8)            'mov ds,ax
                                           
'We first pushed DS segment register since we want to adress our DATA_segment
asm$ = asm$ + CHR$(&H8F) + CHR$(&H6) + dsoff$    'pop dsoff$
asm$ = asm$ + CHR$(&H8C) + CHR$(&H6) + esoff$    'mov esoff$,ES

asm$ = asm$ + CHR$(&H9F)                         'lahf
asm$ = asm$ + CHR$(&H88) + CHR$(&H26) + flagoff$ 'mov flagoff$,ah
                     
'The rest is more simple i think:
asm$ = asm$ + CHR$(&H89) + CHR$(&H2E) + bpoff$   'mov bpoff$,bp
asm$ = asm$ + CHR$(&H89) + CHR$(&H36) + sioff$   'mov sioff$,si
asm$ = asm$ + CHR$(&H89) + CHR$(&H3E) + dioff$   'mov dioff$,di
asm$ = asm$ + CHR$(&H89) + CHR$(&H16) + dxoff$   'mov dxoff$,dx
asm$ = asm$ + CHR$(&H89) + CHR$(&HE) + cxoff$    'mov cxoff$,cx
asm$ = asm$ + CHR$(&H89) + CHR$(&H1E) + bxoff$   'mov bxoff$,bx
asm$ = asm$ + CHR$(&HA3) + axoff$                'mov axoff$,ax
                                
'Now let us neatly finish by just popping everything back in order
asm$ = asm$ + CHR$(&H7)                          'pop es
asm$ = asm$ + CHR$(&H1F)                         'pop ds         
asm$ = asm$ + CHR$(&H5D)                         'pop bp         
asm$ = asm$ + CHR$(&H5E)                         'pop si         
asm$ = asm$ + CHR$(&H5F)                         'pop di         
asm$ = asm$ + CHR$(&H5A)                         'pop dx         
asm$ = asm$ + CHR$(&H59)                         'pop cx         
asm$ = asm$ + CHR$(&H5B)                         'pop bx         
asm$ = asm$ + CHR$(&H58)                         'pop ax        
asm$ = asm$ + CHR$(&H9D)                         'popf            
'We are done?
asm$ = asm$ + CHR$(&HCB)                         'retf 

DEF SEG = VARSEG(asm$)
offcode% = SADD(asm$): CALL absolute(offcode%): DEF SEG

ui$ = HEX$(intnr%)
FOR i% = 0 TO 9
  hx$ = HEX$(datas%(i%))
  hx$ = STRING$(4 - LEN(hx$), "0") + hx$
  ui$ = ui$ + "," + hx$
NEXT

int86qb$ = ui$

END FUNCTION

SUB Pokestring (SEGJE%, OFFJE%, MAIN$)
'------------------------------------------------------
'This function pokes a string (might be ASCIIZ)into
'memory at a given location, making it possible to
'access strings in byte form
'------------------------------------------------------

DEF SEG = SEGJE%
FOR i% = 0 TO LEN(MAIN$) - 1
  POKE OFFJE% + i%, ASC(MID$(MAIN$, i% + 1, 1))
NEXT
DEF SEG

END SUB
