QB ON ACID ISSUE #4 December 21st, 1999 _________________________________________________________________ QB parameter passing (technical edition) Hey again to all our favourite readers. My friend Nodtveidt asked me to do another one of my special articles. This time im going to explain a bit about QB parameter passing, yes i know alot of you know this already, but for the newbies out there (who happen to know asm by heart :) read on. Assumptions: QuickBASIC 4.5 (no other compiler or version) MASM 6.11d (or newer, tasm is no good in my humble opinion) Lets start off with common variables. Normally, they are passed by reference, this is the same way QB does it internally between subs and functions. By reference Example program: BASIC: DECLARE FUNCTION MyInteger& (Integ%, Lon&) t& = MyInteger(123, 456) PRINT t& MASM: .model medium, basic .386 .code MyInteger proc public uses bx cx si di ds es Integ:word, Lon:word mov bx, Integ mov ax, [bx] ; ax now contains the value of Integ% (123) mov bx, Lon mov ax, [bx] ; ax now contains the lower 16 bit of Lon mov dx, [bx+2] ; dx now contains the upper 16 bit of Lon inc ax ret MyInteger endp end SCREEN OUTPUT: 457 Explanation: In the basic module, i pass the value 123 by reference. What happens is the value is moved to a temporary location in the common data segment, and i retrieve MyInteger's return value and print it. In the assembly module, i use a VERY handy way of referencing parameters. ".model medium, basic" tells the assembler i want a medium sized memory configuration which is identical to QB's (dont use any other, it wont do anything to make your modules smaller or faster, but only to make it crash). The basic parameter tells the assembler we are using BASIC parameter passing technique (its pretty much identical to PASCAL but they where nice enough to give it its own name). This way we dont have to count where in the asm routine's stack we keep the variables, they are automatically handled by masm, and BP is set up. Don't worry, the code is NOT slower or bigger than the hand coded technique, it is exactly the same. Down the line we find "uses", this tells the assembler that we use these registers, its only there to make the code look alot better, but you can use the regular way, if you like. I typed in all the registers QB wants you to save, of course if you dont use them all you could just remove those from the list. By reference means a 16bit OFFSET pointing to the actual variable. And you must NOT tamper with DS if you want to be able to reference them. It is slower to use this technique, but if speed is not an issue, it does make things look better for the programmer, and makes it more userfriendly, in my humble opinion. One advantage with by reference passing is your ability to alter the parameter variables. That way you can return more values. LONG variables are 32bit signed integers. These can be a bore to handle, since you need two 16bit registers. I usually use a 32bit register for them, makes things easier to load and store. If you use 32bit registers, you still have to save the lower 16bit of those registers. ** RETURNING VALUES are simple. If you define MyInteger to return an Integer then AX must contain the returning value. If you define MyInteger to return a Long then DX:AX must contain the returning value. Returning values are passed by value, not by reference. By value Example program: DECLARE FUNCTION MyInteger& (BYVAL Integ%, BYVAL Lon&) t& = MyInteger(123, 456) PRINT t& MASM: .model medium, basic .386 .code MyInteger proc public uses bx cx si di ds es Integ:word, Lon:dword mov ax, Integ ; ax now contains the value of Integ% (123) mov eax, Lon ; eax now contains the value of Lon& (456) inc ax ret MyInteger endp end SCREEN OUTPUT: 457 STRINGS are a bit tricky, a single little screw up and your program will crash. do not try to pass these byvalue, its not a good thing. A 16bit offset is passed instead, this points to a header that looks roughly like this: string_length word SIZEOF string_string string_offset word OFFSET string_string <....> string_string byte 'my string is here' A simple example here: in BASIC: declare function mystr$ (a$) a$="lala" b%=mystr(a$) print b% in MASM: .model medium, basic .386 .code mystr proc public uses bx cx si di ds es astr:word cld mov bx, astr mov cx, [bx] ; cx contains LENGTH of astr (a$) lds si, [bx+2] xor ax, ax lodsb ; ax contains ASCII value of first character in astr. ret mystr endp end Simple as that, there you got the string position and length, no sweat. Problems arise when you want to get a string and return a string, you also have to use complex segment declaration. And you have to DGROUP both _BSS and _DATA. You who know what im talking about, you can easily do it. You who doesn't, well, not my problem. Microsoft wont officially support ARRAY passing for QB4.5, and I wont either, but if you want to know how its done, just tinker around abit. Its really easy. Hint: myarray:word, myarray will point to header. By Sten Daniel Sørsdal