'=========================================================================== ' 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