'===========================================================================
' 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..<key>": 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 "<key..>"
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<g>"
PRINT "PEEK use    (1*)     :"; d! - c!, MID$(bas3$, 320, 4); " < 2 bytes/2(beep)attributes<g>"


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

