'=========================================================================== ' Subject: ASSEMBLY IN QBASIC 7: SPEED Date: 02-07-97 (22:15) ' Author: Rick Elbers Code: QB, QBasic, PDS ' Origin: rick@tip.nl Packet: ASMCODE.ABC '=========================================================================== 'ASSEMBLY IN QBASIC 7: ASSEMBLY SPEED ? '--------------------------------------- 'RICK ELBERS FEBRUARI 1997[] '--------------- 'INTRODUCTION '-------------- 'Ray Moon writes in his excellent x86 FAQ that legitimate reasons for using 'asm are: '* Low level control. When you need to change the flags, or the control ' registers of the processor, as when entering protected mode. ' * Speed. Programs written in machine language execute fast! It can ' execute 10-100 times the speed of BASIC, and about twice as fast as a ' program written in C or Pascal. ' * Time Critical Code. Critical sections of programs written in higher ' level languages, can be written in assembly to speed up sections. ' * Small program size. When you write a TSR for example this is very ' useful. Writing interrupt handlers is where assembly language shines. 'While most of my other contributions have been handling low level control, 'this part of ASM in QBASIC will handle speed issues. We will discover 'certain groups of asm functions that can speed up QBASIC. Some timings 'and examples will be given below. And some tentative conclusions reached. '---------------------------------------------------------------- 'A)Peek replacers '----------------- DECLARE FUNCTION peek$ () 'peeks a byte from memory( obsolete) DECLARE FUNCTION peekW$ () 'peeks a word from memory(*) DECLARE FUNCTION peekDW$ () 'peeks a long from memory(!) DECLARE FUNCTION peekmem2arr$ () 'gets an array from memory(!) DECLARE FUNCTION peekmem2string$ () 'gets an ASCIZ string from memory(!) DECLARE FUNCTION peekarr2string$ () 'gets an ASCIZ string from array(!) DECLARE FUNCTION peekbytes2string$ () 'gets a fixed length string from memory(!) 'DECLARE peekarr2arr=pokearr2arr 'gets an subarray from an array(!) 'DECLARE memcopy bytes 'copys nrbytes from mem to mem 'You can find that one all over 'B)Poke replacers '----------------- DECLARE FUNCTION poke$ () 'pokes a byte to memory( obsolete ) DECLARE FUNCTION pokeW$ () 'pokes a word to memory (*) DECLARE FUNCTION pokeDW$ () 'pokes a long to memory (!) DECLARE FUNCTION pokearr2mem$ () 'pokes an array to memory (!) DECLARE FUNCTION pokearr2arr$ () 'pokes an array to another array (!) DECLARE FUNCTION pokestring2arr$ () 'pokes a string into an array (!) DECLARE FUNCTION pokestring2mem$ () 'pokes a string into memory (!) 'C)Math replacers '----------------- DECLARE FUNCTION bytes2int$ () 'gets an integer from 2 bytes (obsolete) DECLARE FUNCTION int2long$ () 'get a long from 2 integers(*) DECLARE FUNCTION getHighLow$ () 'gets high/low bytes from integer(!) DECLARE FUNCTION intdivint$ () 'gets the integerdivision and modulo(!) DECLARE FUNCTION readbit$ () 'reads a bit from a long,int(!) 'D)Misc replacers '---------------- DECLARE FUNCTION cls3$ () 'does a cls for screen 0(!) DECLARE FUNCTION cls13$ () 'does a cls for screen 13(!) DECLARE FUNCTION ascimid$ () DECLARE FUNCTION getusWord$ () 'no function needed: explained! 'E)Demo's '---------- DECLARE SUB peekdemo () DECLARE SUB mathdemo () DECLARE SUB pokedemo () DECLARE SUB miscdemo () CALL pokedemo CALL peekdemo CALL mathdemo CALL miscdemo CLS : COLOR 0, 7: PRINT "Short resume: " PRINT "When speed is not the issue use HLL(QBASIC) commands( much easier)" COLOR 7, 0: PRINT "When speed is important:" PRINT "1) Poke/ Peek one byte using "; : COLOR 0, 7: PRINT " QBASIC POKE/PEEK"; COLOR 7, 0: PRINT " commands ": PRINT "2) Poke/ Peek words using "; : COLOR 0, 7: PRINT " PokeW$/PokeW$": COLOR 7, 0: PRINT "3) Poke/ Peek double words using "; COLOR 0, 7: PRINT " PokeDW$ / PeekDW$": COLOR 7, 0: PRINT "4) Poke/ Peek strings from/to array's with "; : COLOR 0, 7: PRINT " Pokestring2arr$/Peekarr2string$ ": COLOR 7, 0: PRINT "5) Poke/ Peek strings from/to memory with "; : COLOR 0, 7: PRINT " Pokestring2mem$/Peekmem2string$ ": COLOR 7, 0 PRINT "6) Poke/ Peek array's from/to memory with "; : COLOR 0, 7: PRINT " Pokearr2mem$/Peekmem2arr$ ": COLOR 7, 0 PRINT "7) Poke/ Peek array's from array's with "; : COLOR 0, 7: PRINT " Pokearr2arr$ ": COLOR 7, 0: PRINT "8) Get high/low bytes with "; : COLOR 0, 7 PRINT " Highlow$ ": COLOR 7, 0: PRINT " division and modulo with "; : COLOR 0, 7: PRINT " Intdivint$ ": COLOR 7, 0: PRINT " getting an integer from two bytes with "; COLOR 0, 7: PRINT " QBASIC * and + ": COLOR 7, 0: PRINT " bitreading with "; COLOR 0, 7: PRINT " readbit ": COLOR 7, 0: PRINT " getting a long from two integers with "; : COLOR 0, 7: PRINT " Ints2long$ " COLOR 7, 0: PRINT "9) Get asci of char with "; : COLOR 0, 7: PRINT " ascimid$ " COLOR 7, 0: PRINT " Clear the screen with "; : COLOR 0, 7: PRINT " CLS3$,CLS13$ ": COLOR 7, 0: PRINT "10)Get the length of a string with "; : COLOR 0, 7: PRINT " QBASIC LEN ": COLOR 7, 0 COLOR 0, 7: PRINT "When you use asm you are in most cases for more then bytes 6 or" PRINT "more times faster then when you do not ": COLOR 7, 0 PRINT "more..": SLEEP: CLS PRINT "Furthermore for peeking/ poking/ copying large bundles :" PRINT "1)If possible use string or array instead of segm and offset passing," PRINT " since this is faster and uses up less variables" PRINT "2)If possible use stringstorage instead of arraystorage, since this " PRINT " faster and uses up 1 less variable." PRINT "3)If stringspace is tight use arraystorage" PRINT "4)If both stringspace and arrayspace are tight OR you have to use offsets" PRINT " other then zero use segm%,offset% passing" PRINT PRINT "When you need speed in other bit-, string- or sortfunctions" PRINT "you might also turn to assembler functions. ": PRINT : PRINT "Good bye" END DEFSTR A FUNCTION ascimid$ '----------------------------------------------------------- 'Function returns the asci character of the Nth character 'STACKPASSING : String, ascN 'IN : String, ascN 'OUT : ascN has the ascii 'It is just an example of the power of assembly with string 'functions in general.. '----------------------------------------------------------- asm = "" asm = asm + CHR$(&H55) 'push bp 4 asm = asm + CHR$(&H89) + CHR$(&HE5) 'mov bp,sp 1 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H6) 'mov bx,[bp+06] 1+9 asm = asm + CHR$(&H8B) + CHR$(&H17) 'mov dx,[bx] request to dx 1+5 asm = asm + CHR$(&H8B) + CHR$(&H7E) + CHR$(&H8) 'mov di,[bp+08] 1+9 asm = asm + CHR$(&H8B) + CHR$(&HD) 'mov cx,[di] length 1+5 asm = asm + CHR$(&H8B) + CHR$(&H7D) + CHR$(&H2) 'mov di,[di+2]sadd 1+9 asm = asm + CHR$(&HB8) + CHR$(&HFF) + CHR$(&HFF) 'mov ax,ffff set to error 1 asm = asm + CHR$(&H9) + CHR$(&HD2) 'or dx,dx 1 asm = asm + CHR$(&H7E) + CHR$(&HB) 'jle+B skip it:error 1/3 asm = asm + CHR$(&H39) + CHR$(&HCA) 'cmp dx,cx 1 asm = asm + CHR$(&H7F) + CHR$(&H7) 'jg +7 skip it:error 1/3 asm = asm + CHR$(&H1) + CHR$(&HD7) 'add di,dx 1 asm = asm + CHR$(&H4F) 'dec di zero based 1 asm = asm + CHR$(&H8A) + CHR$(&H5) 'mov al,[di] 1+5 asm = asm + CHR$(&H30) + CHR$(&HE4) 'xor ah,ah 1 asm = asm + CHR$(&H89) + CHR$(&H7) 'mov [bx],ax 1+5 asm = asm + CHR$(&H5D) 'pop bp 4 asm = asm + CHR$(&HCA) + CHR$(&H4) + CHR$(&H0) 'retf 4 14 ascimid$ = asm 'clocks 85/91 END FUNCTION FUNCTION bytes2int$ 'This function translates two bytes to an integer 'STACKPASSING : INTEGER, HIGHBYTE, LOWBYTE 'IN : Highbyte, Lowbyte 'OUT : Integer '[obsolete...just for demonstration purposes..] '------------------------------------------------------------------- asm = "" asm = asm + CHR$(&H55) 'push bp 4 asm = asm + CHR$(&H89) + CHR$(&HE5) 'mov bp,sp 1 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H6) 'mov bx,[bp+08] LOWBYTE 1+9 asm = asm + CHR$(&H8A) + CHR$(&H7) 'mov aL,[bx] 1+5 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H8) 'mov bx,[bp+06] HIGHBYTE 1+9 asm = asm + CHR$(&H8A) + CHR$(&H27) 'mov aH,[bx] 1+5 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&HA) 'mov bx,[bp+0A] INTEGER 1+9 asm = asm + CHR$(&H89) + CHR$(&H7) 'mov [bx],aX 1+5 asm = asm + CHR$(&H5D) 'pop bp 4 asm = asm + CHR$(&HCA) + CHR$(&H6) + CHR$(&H0) 'retf 6 14 bytes2int$ = asm 'clocks 71 END FUNCTION FUNCTION cls13$ '---------------------------------------------------------- 'This function does nothing but clear screen for 'SCREENS 13, 2, 1 (320*200) '[.386 instructions are used..] '---------------------------------------------------------- asm = "" asm = asm + CHR$(&HB8) + CHR$(&H0) + CHR$(&HA0) '0:mov ax,a000 1 asm = asm + CHR$(&H8E) + CHR$(&HC0) '3:mov es,ax 3 asm = asm + CHR$(&H31) + CHR$(&HFF) '5:xor di,di 1 asm = asm + CHR$(&H66) + CHR$(&H31) + CHR$(&HC0)'7:xor eax,Eax 1 asm = asm + CHR$(&HB9) + MKI$(&H3E80) '10:mov cx,&h3e80 1 asm = asm + CHR$(&HFC) '11:cld 2 asm = asm + CHR$(&HF2) + CHR$(&H66) + CHR$(&HAB)'14:repnz stosd 2+5*3e80 asm = asm + CHR$(&HCB) '15:retf 13 cls13$ = asm 'clocks 24+5*3e80 END FUNCTION FUNCTION cls3$ '------------------------------------------------------ 'This function replaces CLS for textscreens 'on colormonitor for page zero '[.386 instructions used...] '----------------------------------------------------- asm = "" asm = asm + CHR$(&HB8) + CHR$(&H0) + CHR$(&HB8) '0:mov ax,b800 1 asm = asm + CHR$(&H8E) + CHR$(&HC0) '3:mov es,ax 3 asm = asm + CHR$(&H31) + CHR$(&HFF) '5:xor di,di 1 asm = asm + CHR$(&H66) + CHR$(&HB8) + MKL$(&H7200720) '7:mov Eax,&h720720 1 asm = asm + CHR$(&HB9) + CHR$(&HE8) + CHR$(&H3) '10:mov cx,3e8 1 asm = asm + CHR$(&HFC) '11:cld 2 asm = asm + CHR$(&HF2) + CHR$(&H66) + CHR$(&HAB)'12:repnz stosdw 2+5*3e8 asm = asm + CHR$(&HCB) '15:retf 13 cls3$ = asm 'clocks 22+5*3e8 END FUNCTION FUNCTION getHighLow$ '----------------------------------------------------------------- 'This function returns the high and low byte of an integer 'STACKPASSING: High%,Low%,WORD% 'IN : WORD% 'OUT : High%= High byte of WORD% ' Low %= Low byte of WORD% '----------------------------------------------------------------- asm = "" asm = asm + CHR$(&H55) 'push bp 4 asm = asm + CHR$(&H89) + CHR$(&HE5) 'mov bp,sp 1 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&HA) 'mov bx,[bp+0a] 1+9 asm = asm + CHR$(&H8B) + CHR$(&H7) 'mov ax,[bx] 1+5 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H8) 'mov bx,[bp+08] 1+9 asm = asm + CHR$(&H88) + CHR$(&H27) 'mov [bx],ah 1+5 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H6) 'mov bx,[bp+06] 1+9 asm = asm + CHR$(&H88) + CHR$(&H7) 'mov [bx],al 1+5 asm = asm + CHR$(&H5D) 'pop bp 4 asm = asm + CHR$(&HCA) + CHR$(&H6) + CHR$(&H0) 'retf 6 14 getHighLow$ = asm 'clocks 71 END FUNCTION DEFSNG A FUNCTION getusWord$ 'The situation where you want to read out an 'unsigned integer. Simple: only make sure that you 'pass the unsigned integer as the low word of a 'long with the high word of that long set to zero! 'For the opposite operation( f.i. when filling 'registers with unsigned integers you just get the 'string values by LEFT$(MKL$(60000),2) a$ = LEFT$(MKL$(60000), 2) PRINT a$, HEX$(CVI(a$)) END FUNCTION DEFSTR A FUNCTION int2long$ '------------------------------------------------------------------- 'This function translates two int values to one long ! 'STACKPASSING : LONG&, HIGWORD%, LOWWORD% 'IN : HIGH WORD ' LOW WORD 'OUT : LONG '[routine is not optimized for .386 instructions] '-------------------------------------------------------------------- asm = "" asm = asm + CHR$(&H55) 'PUSH BP 4 asm = asm + CHR$(&H89) + CHR$(&HE5) 'MOV BP,SP 1 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H8) 'MOV BX,[BP+08] 1+9 highint asm = asm + CHR$(&H8B) + CHR$(&HF) 'MOV CX,[BX] 1+5 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H6) 'MOV BX,[BP+06] 1+9 lowint asm = asm + CHR$(&H8B) + CHR$(&H7) 'MOV AX,[BX] 1+5 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&HA) 'MOV BX,[BP+0a] 1+9 asm = asm + CHR$(&H89) + CHR$(&H7) 'MOV [BX],AX 1+5 asm = asm + CHR$(&H89) + CHR$(&H4F) + CHR$(&H2) 'MOV [BX+2],CX 1+9 asm = asm + CHR$(&H5D) 'POP BP 4 asm = asm + CHR$(&HCA) + CHR$(&H6) + CHR$(&H0) 'RETF 6 14 int2long$ = asm 'clocks: 81 END FUNCTION FUNCTION intdivint$ '------------------------------------------------- 'This function is returning both the division 'and the remainder from an SWORD to SWORD division. 'STACKPASSING Word%,Div% 'IN : Word% the word to divide ' Div% the word to divide by 'OUT: Word% the result of integerdivision ' DIv% the remainder of the integerdivision '-------------------------------------------------- asm = "" asm = asm + CHR$(&H55) 'push bp 4 asm = asm + CHR$(&H89) + CHR$(&HE5) 'mov bp,sp 1 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H8) 'mov bx,[bp+08] 1+9 asm = asm + CHR$(&H8B) + CHR$(&H7) 'mov ax,[bx] 1+5 asm = asm + CHR$(&H31) + CHR$(&HD2) 'xor dx,dx 1 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H6) 'mov bx,[bp+06] 1+9 asm = asm + CHR$(&H8B) + CHR$(&HF) 'mov cx,[bx] 1+5 asm = asm + CHR$(&HF7) + CHR$(&HF9) 'idiv cx 27 asm = asm + CHR$(&H89) + CHR$(&H17) 'mov [bx],dx 1+5 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H8) 'mov bx,[bp+08] 1+9 asm = asm + CHR$(&H89) + CHR$(&H7) 'mov [bx],ax 1+5 asm = asm + CHR$(&H5D) 'pop bp 4 asm = asm + CHR$(&HCA) + CHR$(&H4) + CHR$(&H0) 'retf 4 14 intdivint$ = asm 'clocks 105 END FUNCTION DEFINT A-Z SUB mathdemo 'This demonstration explores the use of an asm sub to divide/receive modulo, 'get high and lowbytes, make integers from bytes, make a long from integers, 'read a bit from a long against QBASIC QBASIC /,\,mod,^ etc. commands 'Initiating: '------------ intdiv$ = intdivint$: bits$ = readbit$: bytsint$ = bytes2int$ gethilo$ = getHighLow$: intlong$ = int2long$: CLS : LOCATE 1, 25: COLOR 0, 7: PRINT "TIMING MATH REPLACEMENTS..": COLOR 7, 0 DEF SEG = VARSEG(intdiv$): offcode% = SADD(intdiv$) words% = 456: divs% = &H91 '1)INTDIVINT 'asmdivmod: 'Since it is well known that I hate to use to many variables 'I just refilled them... word2divide% = 456: divider% = &H91 a! = TIMER FOR i% = 0 TO 30000 words% = word2divide%: divs% = divider% CALL absolute(words%, divs%, offcode%) NEXT B! = TIMER DEF SEG 'qbasic divmod: g% = 456: m% = &H91: c! = TIMER FOR i% = 0 TO 30000 k% = g% / m%: n% = g% MOD m% NEXT d! = TIMER COLOR 0, 7: PRINT "Divide Int by Int ": COLOR 7, 0 PRINT "Assembly sub use : "; B! - a!, words%, divs% PRINT "BASIC / and MOD use :"; d! - c!, k%, n% 'Note: can be speeded up a little bit more using 4 variables '2) HIGHLOW 'Initiating: word% = &H1A1B: high% = 0: low% = 0: 'ASM highlow: DEF SEG = VARSEG(gethilo$): offset% = SADD(gethilo$): a! = TIMER FOR i% = -30000 TO 30000 CALL absolute(word%, high%, low%, offset%) NEXT B! = TIMER: 'QBASIC highlow: DEF SEG : c! = TIMER FOR i% = -30000 TO 30000 h% = (&H1A1B / &H100) AND &HFF l% = &H1A1B AND &HFF NEXT d! = TIMER COLOR 0, 7: PRINT "Get high and low bytes ": COLOR 7, 0 PRINT "Assembly use : "; B! - a!, HEX$(high%), HEX$(low%) PRINT "BASIC / and AND use :"; d! - c!, HEX$(h%), HEX$(l%) '3)BITREAD DEF SEG = VARSEG(bits$): offcode% = SADD(bits$) a! = TIMER FOR j% = 0 TO 1000 FOR i% = 0 TO 31 bits% = i% CALL absolute(value&, bits%, offcode%) NEXT NEXT B! = TIMER c! = TIMER FOR i% = 0 TO 1000 FOR B% = 0 TO 30 res% = ((value& AND (2 ^ B%)) > 0) NEXT NEXT d! = TIMER COLOR 0, 7: PRINT "Bitread": COLOR 7, 0 PRINT "Assembly sub use : "; B! - a! PRINT "BASIC ^ and AND use :"; d! - c! '4)Bytes2int 'asm sub: offcode% = SADD(bytsint$): high% = &H1A: low% = &H1C a! = TIMER FOR i% = -30000 TO 30000 CALL absolute(res%, high%, low%, offcode%) NEXT B! = TIMER 'qbasic code c! = TIMER FOR i% = -30000 TO 30000 resQ% = high% * &H100 + low% NEXT d! = TIMER COLOR 0, 7: PRINT "Byts to Integer": COLOR 7, 0 PRINT "Assembly sub use :"; B! - a!, HEX$(res%) PRINT "BASIC ^ and AND use :"; d! - c!, HEX$(resQ%) '5)Ints2long offcode% = SADD(intlong$): high% = &H1C1D: low% = &HFFFF a! = TIMER FOR i% = -30000 TO 30000 CALL absolute(res&, high%, low%, offcode%) NEXT B! = TIMER 'qbasic code c! = TIMER FOR i% = -30000 TO 30000 resQ& = (high% * &H10000) + low% NEXT d! = TIMER COLOR 0, 7: PRINT "Integers to Long": COLOR 7, 0 PRINT "Assembly sub use :"; B! - a!, HEX$(res&) PRINT "BASIC * and OR use :"; d! - c!, HEX$(resQ&) PRINT PRINT "Press a key for next demo": SLEEP END SUB SUB miscdemo 'This sub demonstrates the use of some miscallanous other asm functions 'for QBASIC. 'Initiating: empty3$ = cls3$: empty13$ = cls13$: DEF SEG = VARSEG(empty3$) asci$ = ascimid$ CLS : LOCATE 1, 25: COLOR 0, 7: PRINT "TIMING MISCALLANOUS REPLACEMENTS..": COLOR 7, 0: PRINT : '1) An example of stringfunctions: a$ = "wat is dit dan" a! = TIMER FOR i% = -30000 TO 30000 FOR j% = 1 TO LEN(a$) res% = ASC(MID$(a$, j%, 1)) NEXT NEXT B! = TIMER DEF SEG = VARSEG(asci$): offcode% = SADD(asci$): c! = TIMER FOR i% = -30000 TO 30000 FOR j% = 1 TO LEN(a$) CALL absolute(a$, j%, offcode%) NEXT NEXT d! = TIMER COLOR 0, 7: PRINT "STRINGFUNCTIONS..": COLOR 7, 0 PRINT "BASIC ASC(MID$) use:"; B! - a! PRINT "ASM function use :"; d! - c! PRINT : PRINT PRINT "Next there will be timed CLS functions so do net get scared " PRINT "when you are going to look at: 'long time no see..'": SLEEP PRINT "" VIEW PRINT 2 TO 25 'Screen 3 clearing: 'asm: offcode% = SADD(empty3$): a! = TIMER FOR i% = 0 TO 10000 CALL absolute(offcode%) NEXT B! = TIMER 'basic: PRINT "And know the normal one..." c! = TIMER FOR i% = 0 TO 10000 CLS NEXT d! = TIMER SCREEN 13 'Screen 13 clearing: 'asm: FOR x% = 50 TO 100 CIRCLE (159, 99), 2 * x%, x% + 1 NEXT offcode% = SADD(empty13$): e! = TIMER FOR i% = 0 TO 1000 CALL absolute(offcode%) NEXT f! = TIMER 'basic: PRINT "And know the normal one..." g! = TIMER FOR i% = 0 TO 1000 CLS NEXT h! = TIMER SCREEN 0: WIDTH 80, 25 COLOR 0, 7: PRINT "CLS for SCREEN 0": COLOR 7, 0 PRINT "Assembly use :"; B! - a! PRINT "QBASIC CLS use :"; d! - c! COLOR 0, 7: PRINT "CLS for SCREEN 13": COLOR 7, 0 PRINT "Assembly use :"; f! - e! PRINT "QBASIC CLS use :"; h! - g! PRINT "Press a key for next demo"; : SLEEP END SUB DEFSNG B-Z DEFSTR A FUNCTION peek$ '--------------------------------------------------------------- 'This function does nothing more then peek one byte from a known 'location. For peeking a word, dword, array or string you should 'use more optimized routines. 'STACKPASSING: BAS%, SEGMENT, OFFSET 'IN : SEGMENT, OFFSET 'OUT : BAS% '[Obsolete..just for demonstration purposes.] '---------------------------------------------------------------- asm = "" asm = asm + CHR$(&H55) 'push bp 4 asm = asm + CHR$(&H89) + CHR$(&HE5) 'mov bp,sp 1 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H6) 'mov bx,[bp+06] 1+9 asm = asm + CHR$(&H8B) + CHR$(&H3F) 'mov di,[bx] 1+5 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H8) 'mov bx,[bp+08] 1+9 asm = asm + CHR$(&H8B) + CHR$(&H7) 'mov ax,[bx] 1+5 asm = asm + CHR$(&H8E) + CHR$(&HC0) 'mov es,ax 3 es[di]adress asm = asm + CHR$(&H26) + CHR$(&H8B) + CHR$(&H5) 'es:mov ax,[di] 2+5+1 store it asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&HA) 'mov bx,[bp+0A] 1+9 asm = asm + CHR$(&H88) + CHR$(&H7) 'mov [bx],al 1+9 asm = asm + CHR$(&H5D) 'pop bp 4 asm = asm + CHR$(&HCA) + CHR$(&H6) + CHR$(&H0) 'retf 6 1 peek$ = asm 'clocks : 70 END FUNCTION FUNCTION peekarr2string$ '----------------------------------------------------------- 'This routine does nothing more then store an ASCIIZ string 'from an array. You should look upon this routine as a 'special case of moving bytes in memory 'STACKPASSING B$,A%() 'IN :B$ =string big enough to keep the Bytes ' A%()=the array to get the zero terminated string from. 'OUT:B$ =string contains the array in byte form 'reference: peekstring2loc,pokestring,pokestring2loc '[this routine is not optimized for .386 instructions] '------------------------------------------------------------ asm = "" asm = asm + CHR$(&H55) 'push bp 4 asm = asm + CHR$(&H89) + CHR$(&HE5) 'mov bp,sp 1 asm = asm + CHR$(&H1E) 'PUSH dS 3 asm = asm + CHR$(&H1E) 'PUSH DS 3 asm = asm + CHR$(&H7) 'POP ES 3 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H8) 'mov bx,[bp+08] 1+9 asm = asm + CHR$(&H8B) + CHR$(&HF) 'mov cx,[bx] len 1+5 asm = asm + CHR$(&H8B) + CHR$(&H7F) + CHR$(&H2) 'mov di,[bx+02] es[di]string '1+9 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H6) 'mov bx,[bp+06] 1+9 asm = asm + CHR$(&HC5) + CHR$(&H37) 'lds si,[bx] ds[si]array '6+5 asm = asm + CHR$(&HFC) 'CLD future 2 asm = asm + CHR$(&HF6) + CHR$(&HC1) + CHR$(&H1) 'TEST cl,1 1 asm = asm + CHR$(&H74) + CHR$(&H1) 'jz +1 1/3 asm = asm + CHR$(&HA4) 'movsb odd byte 7 asm = asm + CHR$(&HD1) + CHR$(&HE9) 'shr cx,1 >words 3 asm = asm + CHR$(&HF2) + CHR$(&HA5) 'repnz movsw 2+7*cx asm = asm + CHR$(&H1F) 'Pop dS 3 asm = asm + CHR$(&H5D) 'pop bp 4 asm = asm + CHR$(&HCA) + CHR$(&H4) + CHR$(&H0) 'retf 4 14 peekarr2string$ = asm 'clocks 98/101+7*cx END FUNCTION FUNCTION peekbytes2string$ '----------------------------------------------------------- 'This routine does nothing more then store the number of string 'bytes from the memory location. You should look upon this 'routine as a special case of moving bytes in memory 'STACKPASSING: bas$,seg%,offs% 'IN :B$ = string exactly big enough to keep the Bytes ' Seg%, offset%= Memory location. 'OUT:B$ = string is filled with memory 'reference: peekstring2loc,pokestring,pokestring2loc '[this routine is not optimized for .386 use] '------------------------------------------------------------ asm = "" asm = asm + CHR$(&H55) 'push bp 4 asm = asm + CHR$(&H89) + CHR$(&HE5) 'mov bp,sp 1 asm = asm + CHR$(&H1E) 'PUSH dS 3 'Set up es[di] to string and cx to len string: asm = asm + CHR$(&H1E) 'PUSH DS 3 asm = asm + CHR$(&H7) 'POP ES 3 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&HA) 'mov bx,[bp+0a] 1+9 asm = asm + CHR$(&H8B) + CHR$(&HF) 'mov cx,[bx] len 1+5 asm = asm + CHR$(&H8B) + CHR$(&H7F) + CHR$(&H2) 'mov di,[bx+02]sadd 1+9 'Set up ds[si] to memloc: asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H6) 'mov bx,[bp+06] 1+9 asm = asm + CHR$(&H8B) + CHR$(&H37) 'mov si,[bx] 1+5 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H8) 'mov bx,[bp+08] 1+9 asm = asm + CHR$(&H8B) + CHR$(&H7) 'mov ax,[bx] 1+9 asm = asm + CHR$(&H8E) + CHR$(&HD8) 'mov ds,ax 3 ds[si] to memloc 'Copy it words if possible: asm = asm + CHR$(&HFC) 'CLD 2 asm = asm + CHR$(&HF6) + CHR$(&HC1) + CHR$(&H1) 'test cl,1 odd ? 1 asm = asm + CHR$(&H74) + CHR$(&H1) 'jz +1 1/3 asm = asm + CHR$(&HA4) 'movsb 7 asm = asm + CHR$(&HD1) + CHR$(&HE9) 'shr cx,1 3 asm = asm + CHR$(&HF3) + CHR$(&HA5) 'repz movsw 2+5*cx asm = asm + CHR$(&H1F) 'Pop dS 3 asm = asm + CHR$(&H5D) 'pop bp 4 asm = asm + CHR$(&HCA) + CHR$(&H6) + CHR$(&H0) 'retf 6 14 peekbytes2string$ = asm 'clocks: 116/119+5*cx END FUNCTION DEFINT A-Z SUB peekdemo 'This demonstration explores the way assembly subs match up to 'QBASIC in PEEK commands 'initiating: peekB$ = peek$: peekI$ = peekW$: peekL$ = peekDW$: peekstr$ = peekmem2string$ test% = &H2EF1: segm% = VARSEG(test%): offs% = VARSEG(test%) peekbytstr$ = peekbytes2string$: peekstrarr$ = peekarr2string$: CLS peekarr$ = peekmem2arr$: CLS : LOCATE 1, 25: COLOR 0, 7: PRINT "TIMING PEEK REPLACEMENTS..": COLOR 7, 0 '1) Peek a byte '--------------- 'asmpeek: DEF SEG = VARSEG(peekB$): offcode% = SADD(peekB$) a! = TIMER FOR i% = -30000 TO 30000 CALL absolute(bas%, &H40, &H1A, offcode%) NEXT B! = TIMER: DEF SEG : 'qbasic peek: DEF SEG = &H40: c! = TIMER: FOR i% = -30000 TO 30000 bas1% = PEEK(&H1A) NEXT d! = TIMER: DEF SEG : COLOR 0, 7: PRINT "Peek bytes ": COLOR 7, 0 PRINT "Assembly use :"; B! - a!, HEX$(bas%) PRINT "PEEK use :"; d! - c!, HEX$(bas1%) '2)Peek a word 'asm peekword: DEF SEG = VARSEG(peekI$): offcode% = SADD(peekI$) a! = TIMER FOR i% = -30000 TO 30000 CALL absolute(bas%, &H40, &H63, offcode%) NEXT B! = TIMER: DEF SEG : 'qbasic peek: DEF SEG = &H40: c! = TIMER FOR i% = -30000 TO 30000 bas1% = PEEK(&H63) + PEEK(&H64) * &H100 NEXT d! = TIMER: DEF SEG : COLOR 0, 7: PRINT "Peek words": COLOR 7, 0 PRINT "Assembly use :"; B! - a!, HEX$(bas%) PRINT "2*PEEK use :"; d! - c!, HEX$(bas1%) '3)Peek a Doubleword 'asm peekDword: DEF SEG = VARSEG(peekL$): offcode% = SADD(peekL$) a! = TIMER FOR i% = -30000 TO 30000 CALL absolute(bas&, &H40, &HA4, offcode%) NEXT B! = TIMER: DEF SEG : 'qbasic peekDW:( watch out: do not use +/- here :unsigned values!) DEF SEG = &H40: c! = TIMER FOR i% = -30000 TO 30000 bas1& = ((PEEK(&HA6) OR (PEEK(&HA7) * &H100&)) * &H10000) OR (PEEK(&HA5) * &H100& OR PEEK(&HA4)) NEXT d! = TIMER: DEF SEG : COLOR 0, 7: PRINT "Peek Double words": COLOR 7, 0 PRINT "Assembly use :"; B! - a!, HEX$(bas&) PRINT "4*PEEK use :"; d! - c!, HEX$(bas1&) '4)Peek a bundle( to string and array) 'a)String from a memory location 'F.i read out environmentstrings 'asm peekstring: DIM help%(4): 'allocate some memory help%(0) = &H4142: help%(1) = &H61: segm% = VARSEG(help%(0)): offs% = VARPTR(help%(0)): bas$ = SPACE$(4) DEF SEG = VARSEG(peekstr$): offcode% = SADD(peekstr$): a! = TIMER FOR i% = -30000 TO 30000 CALL absolute(bas$, segm%, offs%, offcode%) NEXT B! = TIMER: DEF SEG : 'qbasic peekstring: DEF SEG = segm%: c! = TIMER FOR i% = -30000 TO 30000 bas1$ = CHR$(PEEK(0)) + CHR$(PEEK(1)) + CHR$(PEEK(2)) + CHR$(PEEK(3)) NEXT d! = TIMER: DEF SEG : 'a)Array from a memory location 'F.i read out screensegment DIM save%(1): DIM sav%(1) DEF SEG = VARSEG(peekarr$): offcode% = SADD(peekarr$): e! = TIMER FOR i% = -30000 TO 30000 CALL absolute(save%(), segm%, offs%, 2, offcode%) NEXT f! = TIMER: DEF SEG : DEF SEG = segm%: g! = TIMER FOR i% = -30000 TO 30000 sav%(0) = PEEK(0) * 256 OR PEEK(1) sav%(1) = PEEK(2) * 256 OR PEEK(3) NEXT h! = TIMER: DEF SEG : COLOR 0, 7: PRINT "Peek a bundle from memory": COLOR 7, 0 PRINT "Asm to string :"; B! - a!, bas$, "length: "; LEN(bas$) PRINT "PEEK use :"; d! - c!, bas1$, "length: "; LEN(bas1$) PRINT "Asm to array :"; f! - e!, HEX$(save%(0)); HEX$(save%(1)), "length: 4" PRINT "PEEK use :"; h! - g!, HEX$(sav%(0)); HEX$(sav%(1)), "length: 4" 'b)From an array f.i. read out stored DTA 'asm peek string from an array : use help%() this time as array! bas$ = SPACE$(4) DEF SEG = VARSEG(peekstrarr$): offcode% = SADD(peekstrarr$): a! = TIMER FOR i% = -30000 TO 30000 CALL absolute(bas$, help%(), offcode%) NEXT B! = TIMER: DEF SEG : 'qbasic peekstring: use help%() this time as array! DEF SEG = segm%: c! = TIMER FOR i% = -30000 TO 30000 bas1$ = MKI$(help%(0)) + MKI$(help%(1)) NEXT d! = TIMER: DEF SEG : COLOR 0, 7: PRINT "Peek a string from an array": COLOR 7, 0 PRINT "Assembly use :"; B! - a!, bas$, "length: "; LEN(bas$) PRINT "PEEK use :"; d! - c!, bas1$, "length: "; LEN(bas1$) 'c)Peek known bytes from memory location f.i. store the screen 'asm peek bytes string: bas$ = SPACE$(4000) DEF SEG = VARSEG(peekbytstr$): offcode% = SADD(peekbytstr$): a! = TIMER FOR i% = 0 TO 2000 CALL absolute(bas$, &HB800, 0, offcode%) NEXT B! = TIMER: DEF SEG : 'qbasic peek bytes string: DEF SEG = &HB800: c! = TIMER FOR i% = 0 TO 3999 bas3$ = bas3$ + CHR$(PEEK(i%)) NEXT d! = TIMER: DEF SEG : COLOR 0, 7: PRINT "Peek a fixed bytenr to string": COLOR 7, 0 PRINT "Assembly use(2000*!) :"; B! - a!, MID$(bas$, 320, 4); " < 2 bytes/2(beep)attributes" PRINT "PEEK use (1*) :"; d! - c!, MID$(bas3$, 320, 4); " < 2 bytes/2(beep)attributes" COLOR 7, 0: PRINT : PRINT "Press a key for next demo": SLEEP: CLS END SUB DEFSNG B-Z DEFSTR A FUNCTION peekDW$ '---------------------------------------------------------------------- 'This function does nothing more then peek one doubleword from a known 'location. For peeking a byte, word, array or string you should 'use more optimized routines. 'STACKPASSING: DWORD, SEGMENT, OFFSET 'IN : SEGMENT, OFFSET 'OUT : DWORD '[.386 instructions used..] '----------------------------------------------------------------------- asm = "" asm = asm + CHR$(&H55) 'push bp 4 asm = asm + CHR$(&H89) + CHR$(&HE5) 'mov bp,sp 1 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H6) 'mov bx,[bp+06] 1+9 asm = asm + CHR$(&H8B) + CHR$(&H3F) 'mov di,[bx] 1+5 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H8) 'mov bx,[bp+08] 1+9 asm = asm + CHR$(&H8B) + CHR$(&H7) 'mov ax,[bx] 1+5 asm = asm + CHR$(&H8E) + CHR$(&HC0) 'mov es,ax 3 es[di]adress asm = asm + CHR$(&H26) + CHR$(&H66) + CHR$(&H8B) + CHR$(&H5)'es:mov EAX,[di] 2+5+1 store it asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&HA) 'mov bx,[bp+0A] 1+9 asm = asm + CHR$(&H66) + CHR$(&H89) + CHR$(&H7) 'mov [bx],EAX 1+5 Dword asm = asm + CHR$(&H5D) 'pop bp 4 asm = asm + CHR$(&HCA) + CHR$(&H6) + CHR$(&H0) 'retf 6 14 peekDW$ = asm 'clocks 83 END FUNCTION FUNCTION peekmem2arr$ '----------------------------------------------------------- 'This routine does nothing more then store an array 'from a memory location. This routine should be looked 'upon as a special case of moving words in memory. 'STACKPASSING: Save%(),seg%,offs%,nrelements% 'IN : Save%() =array big enough to keep the Bytes ' Seg%, offset%=Memory location. ' nrelements% =the number of integers to get 'Reference: peekstring2loc,pokestring,pokestring2loc '[This routine is not optimized for .386 use] '------------------------------------------------------------ asm = "" asm = asm + CHR$(&H55) 'push bp 4 asm = asm + CHR$(&H89) + CHR$(&HE5) 'mov bp,sp 1 asm = asm + CHR$(&H1E) 'push ds 3 asm = asm + CHR$(&H6) 'push es 3 'Set up ES[DI] to save%, cx to length: '---------------------------------------------- asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&HC) 'MOV BX,[BP+0C] 1+9 asm = asm + CHR$(&HC4) + CHR$(&H3F) 'LES di,[bx] 6+5 '---------------- 'GET NRelements% '---------------- asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H6) 'MOV BX,[BP+06] 1+9 asm = asm + CHR$(&H8B) + CHR$(&HF) 'MOV CX,[BX] 1+5 '---------------------- 'GET SOURCE in DS[SI] '---------------------- asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H8) 'MOV BX,[BP+08] 1+9 asm = asm + CHR$(&H8B) + CHR$(&H37) 'MOV SI,[BX] 1+5 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&HA) 'MOV BX,[BP+0A] 1+9 asm = asm + CHR$(&H8B) + CHR$(&H7) 'MOV AX,[BX] 1+5 asm = asm + CHR$(&H8E) + CHR$(&HD8) 'MOV DS,AX 3 '-------------------------------------------------- 'Lets copy... '-------------------------------------------------- asm = asm + CHR$(&HFC) 'CLD 2 asm = asm + CHR$(&HF3) + CHR$(&HA5) 'repnz movsw 2+5*cx asm = asm + CHR$(&H7) 'pop es 3 asm = asm + CHR$(&H1F) 'POP Ds 3 asm = asm + CHR$(&H5D) 'POP BP 4 asm = asm + CHR$(&HCA) + CHR$(&H8) + CHR$(&H0) 'RETF 8 14 peekmem2arr$ = asm 'clocks 111+5*cx END FUNCTION FUNCTION peekmem2string$ '----------------------------------------------------------- 'This routine does nothing more then store an ASCIIZ string 'from a memory location. You should look upon this routine 'as a special case of moving bytes in memory. 'STACKPASSING: bas$,seg%,offs% 'IN :B$ =String big enough to keep the Bytes ' Seg, offset =Memory location. 'reference: peekstring2loc,pokestring,pokestring2loc '[This routine is not optimized for .386 use] '------------------------------------------------------------ asm = "" asm = asm + CHR$(&H55) 'push bp 4 asm = asm + CHR$(&H89) + CHR$(&HE5) 'mov bp,sp 1 asm = asm + CHR$(&H1E) 'PUSH dS 3 'Set up es[di] to string: asm = asm + CHR$(&H1E) 'PUSH DS 3 asm = asm + CHR$(&H7) 'POP ES 3 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&HA) 'mov bx,[bp+0a] 1+9 asm = asm + CHR$(&H8B) + CHR$(&H7F) + CHR$(&H2) 'mov di,[bx+02]sadd 1+9 'Set up ds[si] to memloc: asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H6) 'mov bx,[bp+06] 1+9 asm = asm + CHR$(&H8B) + CHR$(&H37) 'mov si,[bx] 1+5 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H8) 'mov bx,[bp+08] 1+9 asm = asm + CHR$(&H8B) + CHR$(&H7) 'mov ax,[bx] 1+5 asm = asm + CHR$(&H8E) + CHR$(&HD8) 'mov ds,ax 3 ds[si] to memloc asm = asm + CHR$(&HFC) 'CLD future 2 asm = asm + CHR$(&HAC) + CHR$(&HAA) 'lodsb:stosb ds[si]>es[di]10 asm = asm + CHR$(&H84) + CHR$(&HC0) 'test al,al 1 asm = asm + CHR$(&H75) + CHR$(&HFA) 'jnz -7 3/1 asm = asm + CHR$(&H1F) 'Pop dS 3 asm = asm + CHR$(&H5D) 'pop bp 4 asm = asm + CHR$(&HCA) + CHR$(&H6) + CHR$(&H0) 'retf 6 14 peekmem2string$ = asm 'clocks: 96/94+10*len END FUNCTION FUNCTION peekW$ '--------------------------------------------------------------- 'This function does nothing more then peek one word from a known 'location. For peeking a byte, dword, array or string you should 'use more optimized routines. 'STACKPASSING: WORD, SEGMENT, OFFSET 'IN : SEGMENT, OFFSET 'OUT : WORD '--------------------------------------------------------------- asm = "" asm = asm + CHR$(&H55) 'push bp 4 asm = asm + CHR$(&H89) + CHR$(&HE5) 'mov bp,sp 1 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H6) 'mov bx,[bp+06] 1+9 asm = asm + CHR$(&H8B) + CHR$(&H3F) 'mov di,[bx] 1+5 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H8) 'mov bx,[bp+08] 1+9 asm = asm + CHR$(&H8B) + CHR$(&H7) 'mov ax,[bx] 1+5 asm = asm + CHR$(&H8E) + CHR$(&HC0) 'mov es,ax 3 es[di]adress asm = asm + CHR$(&H26) + CHR$(&H8B) + CHR$(&H5) 'es:mov ax,[di] 2+5+1 store it asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&HA) 'mov bx,[bp+0A] 1+9 asm = asm + CHR$(&H89) + CHR$(&H7) 'mov [bx],ax 1+5 asm = asm + CHR$(&H5D) 'pop bp 4 asm = asm + CHR$(&HCA) + CHR$(&H6) + CHR$(&H0) 'retf 6 14 'clocks 82 peekW$ = asm END FUNCTION FUNCTION poke$ '---------------------------------------------------------------- 'This function does nothing more then poke one byte to a know 'location. For poking a byte into a variable/ array you should 'use better optimized routines. 'STACKPASSING: SEGMENT, OFFSET, BYTE 'IN : SEGMENT, OFFSET, BYTE 'OUT : - '----------------------------------------------------------------- asm = "" asm = asm + CHR$(&H55) 'push bp 4 asm = asm + CHR$(&H89) + CHR$(&HE5) 'mov bp,sp 1 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H8) 'mov bx,[bp+08] 1+9 asm = asm + CHR$(&H8B) + CHR$(&H3F) 'mov di,[bx] 1+5 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&HA) 'mov bx,[bp+0A] 1+9 asm = asm + CHR$(&H8B) + CHR$(&H7) 'mov ax,[bx] 1+5 asm = asm + CHR$(&H8E) + CHR$(&HC0) 'mov es,ax 3 es[di]adress asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H6) 'mov bx,[bp+06] 1+9 asm = asm + CHR$(&H8B) + CHR$(&H7) 'mov ax,[bx] 1+5 asm = asm + CHR$(&H26) + CHR$(&H88) + CHR$(&H5) 'es:mov [di],al 2+5+1 store it asm = asm + CHR$(&H5D) 'pop bp 4 asm = asm + CHR$(&HCA) + CHR$(&H6) + CHR$(&H0) 'retf 6 14 poke$ = asm '82 clocks END FUNCTION FUNCTION pokearr2arr$ '------------------------------------------------------------ 'This routine copies a (part of) an array to another array. 'You should look upon this routine as both a special case of 'moving memory blocks and filling parameterblocks. 'STACKPASSING SOURCE%(),DEST%(),nrwords% 'IN : DS[SI] SOURCE%() 'OUT : ES[DI] FILLED DEST%() '[This routine is not optimized for .386 instructions yet] '------------------------------------------------------------- asm = "" asm = asm + CHR$(&H55) 'PUSH BP 4 asm = asm + CHR$(&H89) + CHR$(&HE5) 'MOV BP,SP 1 asm = asm + CHR$(&H1E) 'PUSH DS 3 '---------------------------------------- 'LET US FIRST PUT THE DESTINY IN ES[DI]: '---------------------------------------- asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H8) 'MOV BX,[BP+08] 1+9 asm = asm + CHR$(&HC4) + CHR$(&H3F) 'les di,[bx] 6+5 '---------------- 'GET NRelements% '---------------- asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H6) 'MOV BX,[BP+06] 1+9 asm = asm + CHR$(&H8B) + CHR$(&HF) 'MOV CX,[BX] 1+5 '---------------------- 'GET SOURCE in DS[SI] '---------------------- asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&HA) 'MOV BX,[BP+0A] 1+9 asm = asm + CHR$(&HC5) + CHR$(&H37) 'lds si,[bx] 6+9 '-------------------------------------------------- 'Lets copy... '-------------------------------------------------- asm = asm + CHR$(&HFC) 'CLD 2 asm = asm + CHR$(&HF2) + CHR$(&HA5) 'repnz movsw 2+7*cx asm = asm + CHR$(&H1F) 'POP Ds 3 asm = asm + CHR$(&H5D) 'POP BP 4 asm = asm + CHR$(&HCA) + CHR$(&H6) + CHR$(&H0) 'RETF 6 14 pokearr2arr$ = asm 'clocks 95+7*cx END FUNCTION FUNCTION pokearr2mem$ '----------------------------------------------------------------- 'This routine does nothing more then store an array 'from memory. You should look upon this routine as a 'special case of moving bytes in memory 'STACKPASSING Segm%,offs%,bas%(),nrwords% 'IN :Segm% = string big enough to keep the Bytes ' Offs% = the array to get the zero terminated string from. ' nrwords%= the number of words to copy from memory to array 'OUT:bas%() = array filled with memory contents 'reference: peekstring2loc,pokestring,pokestring2loc '[this routine is not optimized for .386 instructions] '------------------------------------------------------------------ asm = "" asm = asm + CHR$(&H55) 'push bp 4 asm = asm + CHR$(&H89) + CHR$(&HE5) 'mov bp,sp 1 asm = asm + CHR$(&H1E) 'PUSH dS 3 'Set up ES[DI] to memory location, cx to length: '---------------------------------------- 'LET US FIRST PUT THE DESTINY IN ES[DI]: '---------------------------------------- asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&HC) 'MOV BX,[BP+0c]1+9 asm = asm + CHR$(&H8B) + CHR$(&H7) 'mov ax,[bx] 1+5 asm = asm + CHR$(&H8E) + CHR$(&HC0) 'mov es,ax 3 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&HA) 'MOV BX,[BP+0a]1+9 asm = asm + CHR$(&H8B) + CHR$(&H3F) 'mov di,[bx] 1+5 '---------------- 'GET NRelements% '---------------- asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H6) 'MOV BX,[BP+06]1+9 asm = asm + CHR$(&H8B) + CHR$(&HF) 'MOV CX,[BX] 1+5 '---------------------- 'GET SOURCE in DS[SI] '---------------------- asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H8) 'MOV BX,[BP+08]1+9 asm = asm + CHR$(&HC5) + CHR$(&H37) 'lds si,[bx] 6+5 '-------------------------------------------------- 'Lets copy... '-------------------------------------------------- asm = asm + CHR$(&HFC) 'CLD 2 asm = asm + CHR$(&HF2) + CHR$(&HA5) 'repnz movsw 2+7*cx asm = asm + CHR$(&H1F) 'POP Ds 3 asm = asm + CHR$(&H5D) 'POP BP 4 asm = asm + CHR$(&HCA) + CHR$(&H8) + CHR$(&H0) 'RETF 8 14 pokearr2mem$ = asm 'clocks 105+7*cx END FUNCTION DEFINT A-Z SUB pokedemo 'This demonstration is exploring the capacities of our assembly poke 'against the QBASIC POKE command. 'Initiating: asmpoke$ = poke$: asmpokeW$ = pokeW$: asmpokeDW$ = pokeDW$ arr2arr$ = pokearr2arr$: arr2mem$ = pokearr2mem$: string2arr$ = pokestring2arr$: string2mem$ = pokestring2mem$: DIM test%(10): segm% = VARSEG(test%(0)): offs% = VARPTR(test%(0)): CLS peekarr$ = peekmem2arr$: CLS : LOCATE 1, 25: COLOR 0, 7: PRINT "TIMING POKE REPLACEMENTS..": COLOR 7, 0 '1): Poke one byte DEF SEG = VARSEG(asmpoke$): offset% = SADD(asmpoke$): a! = TIMER FOR i% = -30000 TO 30000 CALL absolute(segm%, offs%, &H1A, offset%) NEXT B! = TIMER: DEF SEG = VARSEG(segm%): c! = TIMER FOR i% = -30000 TO 30000 POKE offs%, &H1A NEXT d! = TIMER: DEF SEG COLOR 0, 7: PRINT "Poke 1 byte ": COLOR 7, 0 PRINT "Assembly use :"; B! - a! PRINT "POKE use :"; d! - c! '2) Poke a word DEF SEG = VARSEG(asmpokeW$): offset% = SADD(asmpokeW$): a! = TIMER FOR i% = -30000 TO 30000 CALL absolute(segm%, offs%, &H1A1B, offset%) NEXT B! = TIMER: DEF SEG = segm%: c! = TIMER FOR i% = -30000 TO 30000 POKE offs% + 3, &H1A POKE offs% + 2, &H1B NEXT d! = TIMER COLOR 0, 7: PRINT "Poke a Word ": COLOR 7, 0 PRINT "Assembly use :"; B! - a!, HEX$(test%(0)) PRINT "2*POKE use :"; d! - c!, HEX$(test%(1)) '3)Poke a double word 'asm poke DEF SEG = VARSEG(asmpokeDW$): offset% = SADD(asmpokeDW$): a! = TIMER FOR i% = -30000 TO 30000 CALL absolute(segm%, offs%, &H1C1DF1F2, offset%) NEXT B! = TIMER: 'qbasic poke: DEF SEG = segm%: c! = TIMER FOR i% = -30000 TO 30000 POKE offs% + 4, &HF2 POKE offs% + 5, &HF1 POKE offs% + 6, &H1D POKE offs% + 7, &H1C NEXT d! = TIMER COLOR 0, 7: PRINT "Poke a DWord ": COLOR 7, 0 PRINT "Assembly use : "; B! - a!, HEX$(test%(0)), HEX$(test%(1)) PRINT "4*POKE use :"; d! - c!, HEX$(test%(2)), HEX$(test%(3)) '4)Poke a string 'a)To a memory location f.i. a screenstore to &ha000 bas$ = "Halloz" 'asm: DEF SEG = VARSEG(string2mem$): offcode% = SADD(string2mem$): a! = TIMER FOR i% = -30000 TO 30000 CALL absolute(bas$, segm%, offs% + 6, offcode%) NEXT B! = TIMER: DEF SEG : 'basic: DEF SEG = segm% c! = TIMER FOR i% = -30000 TO 30000 FOR j% = 0 TO 5 POKE j%, ASC(MID$(bas$, j% + 1, 1)) NEXT NEXT d! = TIMER COLOR 0, 7: PRINT "Poke a string 2 memory ": COLOR 7, 0 PRINT "Assembly use : "; B! - a!, HEX$(test%(3)); HEX$(test%(4)); HEX$(test%(5)) PRINT "6*POKE use :"; d! - c!, HEX$(test%(0)); HEX$(test%(1)); HEX$(test%(2)) 'b)To an array f.i an asm to an array bas$ = "daaaag" 'asm: DEF SEG = VARSEG(string2arr$): offcode% = SADD(string2arr$): a! = TIMER FOR i% = -30000 TO 30000 CALL absolute(test%(), bas$, offcode%) NEXT B! = TIMER: DEF SEG : 'basic: DEF SEG = segm% c! = TIMER FOR i% = -30000 TO 30000 FOR j% = 0 TO 5 POKE j% + 6, ASC(MID$(bas$, j% + 1, 1)) NEXT NEXT d! = TIMER COLOR 0, 7: PRINT "Poke a string 2 array ": COLOR 7, 0 PRINT "Assembly use : "; B! - a!, HEX$(test%(0)); HEX$(test%(1)); HEX$(test%(2)) PRINT "6*POKE use :"; d! - c!, HEX$(test%(3)); HEX$(test%(4)); HEX$(test%(5)) '5)Poke an array 'C)To another array. f.i. store a parameterblock into an asm%() DIM param%(2) param%(0) = &H1231: param%(2) = &HFFFF 'asm: DEF SEG = VARSEG(arr2arr$): offcode% = SADD(arr2arr$): a! = TIMER FOR i% = -30000 TO 30000 CALL absolute(param%(), test%(), 2, offcode%) NEXT B! = TIMER: DEF SEG : 'basic: DEF SEG = segm% c! = TIMER FOR i% = -30000 TO 30000 test%(2) = param%(0) test%(3) = param%(1) NEXT d! = TIMER e! = TIMER FOR i% = -30000 TO 30000 POKE 9, (param%(0) / &H100) AND &HFF POKE 10, param%(0) AND &HFF POKE 11, (param%(1) / &H100) AND &HFF POKE 12, param%(1) AND &HFF NEXT f! = TIMER COLOR 0, 7: PRINT "Poke an array 2 array ": COLOR 7, 0 PRINT "Assembly use : "; B! - a!, HEX$(test%(0)); HEX$(test%(1)) PRINT "arr%()=arr2%() : "; d! - c!, HEX$(test%(2)); HEX$(test%(3)) PRINT "Poke use :"; f! - e!, HEX$((test%(4) / &HFF) AND &HFF); HEX$(test%(5)); HEX$(test%(6)) 'd)Finallement: To memory f.i a screen store to &ha000 test%(0) = &H1241: test%(1) = &H943 'asm: DEF SEG = VARSEG(arr2mem$): offcode% = SADD(arr2mem$): a! = TIMER FOR i% = -30000 TO 30000 CALL absolute(&HB800, 3440, test%(), 2, offcode%) NEXT B! = TIMER: DEF SEG : 'basic: DEF SEG = &HB800 c! = TIMER FOR i% = -30000 TO 30000 POKE 3600, (test%(0) / &H100) AND &HFF POKE 3601, test%(0) AND &HFF POKE 3602, (test%(1) / &H100) AND &HFF POKE 3603, test%(1) AND &HFF NEXT d! = TIMER COLOR 0, 7: PRINT "Poke an array 2 memory ": COLOR 7, 0 PRINT "Assembly use : "; B! - a! PRINT "4*POKE use :"; d! - c! COLOR 7, 0: PRINT "Press a key for next demo"; : SLEEP: CLS END SUB DEFSNG B-Z DEFSTR A FUNCTION pokeDW$ '--------------------------------------------------------- 'This function pokes a double word into memory 'STACKPASSING :segm%,off%,bas& 'IN :segm%,off%,bas& 'OUT :- '[makes use of .386 instructions for DWORD] '---------------------------------------------------------- asm = "" asm = asm + CHR$(&H55) 'push bp 4 asm = asm + CHR$(&H89) + CHR$(&HE5) 'mov bp,sp 1 asm = asm + CHR$(&H1E) 'push ds 3 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H6) 'mov bx,[bp+06] 1+9 asm = asm + CHR$(&H66) + CHR$(&H8B) + CHR$(&H7) 'mov EAX,[bx] 1+5 DWord asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H8) 'mov bx,[bp+08] 1+9 asm = asm + CHR$(&H8B) + CHR$(&H37) 'mov si,[bx] 1+5 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&HA) 'mov bx,[bp+0a] 1+9 asm = asm + CHR$(&H8B) + CHR$(&HF) 'mov cx,[bx] 1+5 asm = asm + CHR$(&H8E) + CHR$(&HD9) 'mov ds,cx 3 ds[si]=mem asm = asm + CHR$(&H66) + CHR$(&H89) + CHR$(&H4) 'mov [si],EAX 1+5 store it asm = asm + CHR$(&H1F) 'pop ds 3 asm = asm + CHR$(&H5D) 'pop bp 4 asm = asm + CHR$(&HCA) + CHR$(&H6) + CHR$(&H0) 'retf 6 14 pokeDW$ = asm 'clocks 86 END FUNCTION FUNCTION pokestring2arr$ '-------------------------------------------------------------------------- 'This routine does nothing more then poke an ASCIIZ string 'to an array. You should look upon this routine as a 'special case of moving bytes in memory 'STACKPASSING A%(),B$ 'IN : A%() = array big enough to keep the bytes from the string ' B$ = string to get bytes from terminated with a zero(ASCIZ) 'OUT: A%() = array contains the string in integer form 'reference: peekstring2loc,pokestring,pokestring2loc '[this routine is not optimized for .386 instructions] '-------------------------------------------------------------------------- asm = "" asm = asm + CHR$(&H55) 'push bp 4 asm = asm + CHR$(&H89) + CHR$(&HE5) 'mov bp,sp 1 asm = asm + CHR$(&H6) 'PUSH ES 3 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H6) 'mov bx,[bp+06] 1+9 asm = asm + CHR$(&H8B) + CHR$(&HF) 'mov cx,[bx] len string 1+5 asm = asm + CHR$(&H8B) + CHR$(&H77) + CHR$(&H2) 'mov si,[bx+02] ds[si]string 1+9 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H8) 'mov bx,[bp+08] 1+9 asm = asm + CHR$(&HC4) + CHR$(&H3F) 'les di,[bx] es[di]array 6+5 asm = asm + CHR$(&HFC) 'CLD future 2 asm = asm + CHR$(&HF6) + CHR$(&HC1) + CHR$(&H1) 'TEST cl,1 1 asm = asm + CHR$(&H74) + CHR$(&H1) 'jz +1 1/3 asm = asm + CHR$(&HA4) 'movsb odd byte 7 asm = asm + CHR$(&HD1) + CHR$(&HE9) 'shr cx,1 >words 3 asm = asm + CHR$(&HF2) + CHR$(&HA5) 'repnz movsw 2+7*cx asm = asm + CHR$(&H7) 'Pop ES 3 asm = asm + CHR$(&H5D) 'pop bp 4 asm = asm + CHR$(&HCA) + CHR$(&H4) + CHR$(&H0) 'retf 4 14 pokestring2arr$ = asm 'clocks 92/94+7*cx END FUNCTION FUNCTION pokestring2mem$ '------------------------------------------------------------------ 'This routine does nothing more then poke a string to a 'memory location. You should look upon this routine 'as a special case of moving bytes in memory. 'STACKPASSING: bas$,seg%,offs% 'IN : Seg, offset =Memory location. ' bas$ =The string to poke into memory 'reference: peekstring2loc,pokestring,pokestring2loc '[This routine is not optimized for .386 use] '------------------------------------------------------------------ asm = "" asm = asm + CHR$(&H55) 'push bp 4 asm = asm + CHR$(&H89) + CHR$(&HE5) 'mov bp,sp 1 'Set up ds[si] to string, cx to length: asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&HA) 'mov bx,[bp+0a] 1+9 asm = asm + CHR$(&H8B) + CHR$(&H77) + CHR$(&H2) 'mov si,[bx+02]sadd 1+9 asm = asm + CHR$(&H8B) + CHR$(&HF) 'mov cx,[bx] length 1+5 'Set up es[di] to memloc: asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H6) 'mov bx,[bp+06] 1+9 asm = asm + CHR$(&H8B) + CHR$(&H3F) 'mov di,[bx] 1+5 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H8) 'mov bx,[bp+08] 1+9 asm = asm + CHR$(&H8B) + CHR$(&H7) 'mov ax,[bx] 1+5 asm = asm + CHR$(&H8E) + CHR$(&HC0) 'mov es,ax 3 es[di] to memloc asm = asm + CHR$(&HFC) 'CLD future 2 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H8) 'test cl,1 odd ? 1 asm = asm + CHR$(&H74) + CHR$(&H1) 'jz +1 1/3 asm = asm + CHR$(&HA4) 'movsb 7 asm = asm + CHR$(&HD1) + CHR$(&HE9) 'shr cx,1 3 asm = asm + CHR$(&HF3) + CHR$(&HA5) 'rep movsw 2+7*cx asm = asm + CHR$(&H5D) 'pop bp 4 asm = asm + CHR$(&HCA) + CHR$(&H6) + CHR$(&H0) 'retf 6 14 pokestring2mem$ = asm 'clocks : 100/102+7*len END FUNCTION FUNCTION pokeW$ '-------------------------------------------------------------- 'This function does nothing more then poke one word to a known 'location. For poking a word into a variable, a string or an 'array you should use more optimized routines. 'STACKPASSING: SEGMENT, OFFSET, WORD 'IN : SEGMENT,OFFSET= MEMORY LOCATION ' WORD = WORD TO POKE INTO MEMORY LOCATION '--------------------------------------------------------------- asm = "" asm = asm + CHR$(&H55) 'push bp 4 asm = asm + CHR$(&H89) + CHR$(&HE5) 'mov bp,sp 1 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H8) 'mov bx,[bp+08] 1+9 asm = asm + CHR$(&H8B) + CHR$(&H3F) 'mov di,[bx] 1+5 asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&HA) 'mov bx,[bp+0A] 1+9 asm = asm + CHR$(&H8B) + CHR$(&H7) 'mov ax,[bx] 1+5 asm = asm + CHR$(&H8E) + CHR$(&HC0) 'mov es,ax 3 es[di]adress asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H6) 'mov bx,[bp+06] 1+9 asm = asm + CHR$(&H8B) + CHR$(&H7) 'mov ax,[bx] 1+5 asm = asm + CHR$(&H26) + CHR$(&H89) + CHR$(&H5) 'es:mov [di],ax 2+5+1 store it asm = asm + CHR$(&H5D) 'pop bp 4 asm = asm + CHR$(&HCA) + CHR$(&H6) + CHR$(&H0) 'retf 6 14 pokeW$ = asm 'CLOCKS: 82 END FUNCTION FUNCTION readbit$ '------------------------------------------------------- 'This function reads a bit of an integer or long 'STACKPASSING: LONG/INT, BITNR 'IN : LONG/INT, BITNR 'OUT : BITNR=1 BIT SET ' BITNR=0 BIT NOT SET 'This function is just an example of the powers of 'assembly language for bitfunctions. '[this function is not optimized for .386 instructions] '------------------------------------------------------- asm = asm + CHR$(&H55) 'push bp 4 asm = asm + CHR$(&H89) + CHR$(&HE5) 'mov bp,sp 1 'Get bit to read into CL asm = asm + CHR$(&H8B) + CHR$(&H5E) + CHR$(&H6) 'mov bx,[bp+06]1+9 asm = asm + CHR$(&H8B) + CHR$(&HF) 'mov cx,[bx] 1+5 'Get word to check into ax asm = asm + CHR$(&H8B) + CHR$(&H7E) + CHR$(&H8) 'mov di,[bp+08]1+9 asm = asm + CHR$(&H8B) + CHR$(&H5) 'mov ax,[di] 1+5 lowword asm = asm + CHR$(&H80) + CHR$(&HF9) + CHR$(&H10) 'cmp cx,16 1 asm = asm + CHR$(&H72) + CHR$(&H6) 'jb go to check 1/3 'Check highword asm = asm + CHR$(&H8B) + CHR$(&H45) + CHR$(2) 'mov ax,[di+2]1+9 highword asm = asm + CHR$(&H83) + CHR$(&HE9) + CHR$(&H10) 'sub cx,16 1 asm = asm + CHR$(&HD3) + CHR$(&HE8) 'shR ax,cl 3 asm = asm + CHR$(&H25) + CHR$(&H1) + CHR$(&H0) 'and ax,1 return 1 for set 'Return the bit asm = asm + CHR$(&H89) + CHR$(&H7) 'mov [bx],ax 1+5 asm = asm + CHR$(&H5D) 'pop bp 4 asm = asm + CHR$(&HCA) + CHR$(&H4) + CHR$(&H0) 'retf 4 14 readbit$ = asm 'CLOCKS 78/80 END FUNCTION