The QBNews Page 25 Volume 1, Number 3 May 22, 1990 ---------------------------------------------------------------------- S o m e A s s e m b l y R e q u i r e d ---------------------------------------------------------------------- Assembler Programming for QuickBASIC by Tom Hanlin Perhaps you already know a little bit about programming in assembly language, or perhaps you've never given it much thought. After all, it's supposed to be frighteningly difficult to write assembler programs, and you've already got QuickBASIC anyway. Well, it's true that there's a lot of work involved in writing large programs in assembly language. If you keep to the small stuff, however, there's actually very little to it. One of the easiest and most rewarding uses for assembly language lies in writing routines that can be called from a higher-level language like QuickBASIC. This can give you entirely new capabilities or make your existing programs smaller and faster. By mixing the capabilities of a low-level language like assembler with a high-level language like QuickBASIC, you can gain a lot of flexibility when you need it, without sacrificing the ease of use of good ol' BASIC. Microsoft's documentation on mixed-language programming is rather daunting. It's not exactly clear and the examples never seem to cover quite what you had in mind. Once you understand a few simple rules, though, you'll see that adding assembler routines to your QuickBASIC programs can be readily accomplished. I'm going to assume you have some notion of how to program in both QuickBASIC and assembly language, since explaining an entire language would be a bit more than a single article could cover! With that in mind, let's take a look at the basic rules of writing assembly routines for QuickBASIC and follow that up with the code for a working routine. The first thing you need to know is which registers you can use. The answer is, "all of them." However, certain registers must be preserved for BASIC, so if you use them, you must restore their original values before returning to the main BASIC program. The registers that must be preserved are SI, DI, BP, and DS. You also need to preserve the stack (SS and SP) and direction flag. The direction flag must always be "forward" when you exit, so if you change it using the "STD" instruction, be sure to restore it using the "CLD" instruction. Believe it or not, that's most of what you need to know right there. The other important thing to know is how to pass parameters to or from the assembler routine. I'll keep it simple by assuming you use the standard convention, which is "pass by reference", rather than "pass by value", which has to be explicitly declared. What do I mean by "pass by reference?" I mean that, instead of getting the actual value of a parameter, your routine gets the address of that parameter and has to look up the value. It's useful to have the address of the parameter, since that means you can return a value The QBNews Page 26 Volume 1, Number 3 May 22, 1990 by storing it in the parameter's address. Integral numbers are stored simply. For integers, the address you are given points directly to the integer (a word). If you use long integers, the address points to the long integer (a doubleword). Strings are stored in a slightly more complex fashion. The address you are given points to a "string descriptor". This descriptor is composed of two words, with the first giving the length of the string and the second the address of the string. You are not allowed to change the length or address of the string in your assembler routine, although you may change the string itself. I won't go into single precision or double precision numbers, because they are rather tricky to handle in assembler. I won't go into arrays, TYPEd values, or fixed-length strings here either, to keep it reasonably brief. Perhaps in a later article... Parameters are passed on the stack, starting at offset 6 (six) for the -last- parameter and going up by two as you move towards the first parameter. Finally, you need to end your routine with a special "RET" opcode that will clean the parameters off the stack for QuickBASIC. The RET must have a number after it which is twice the number of parameters passed to the routine. Clear as mud? Well, perhaps the example routine will help show what I'm talking about. The DOSVER.ASM file contains the source code. To assemble it just type: ASM DOSVER; (where "ASM" is the name of your assembler: MASM, TASM, or OPTASM) Convert the resulting DOSVER.OBJ file to a library so you can easily use it both from the command line and QB environment: LIB DOSVER; (this creates DOSVER.LIB) LINK DOSVER/Q,,NUL,BQLB45; (this creates DOSVER.QLB) If you are using QuickBASIC 4.0, change the BQLB45 to BQLB40. If you are using QuickBASIC 4.0a or 4.0b, change it to BQLB41. You can now use the DOSVER routine from the QB environment by specifying the library name when you start QuickBASIC: QB /L DOSVER Use of the DECLARE statement is optional, but it will help catch any errors you might make when calling DOSVER. Use this: DECLARE SUB DOSVER(VERSION$, MAJORV%, MINORV%) Before calling the routine, you must set the VERSION$ string to at least four characters, since the routine is not allowed to change the length of the string. Call the routine like this: VERSION$ = SPACE$(4) CALL DOSVER(VERSION$, MAJORV%, MINORV%) Typical results will be "3.11" for VERSION$, 3 for MAJORV%, and 11 for MINORV%. The QBNews Page 27 Volume 1, Number 3 May 22, 1990 In later articles, if there is any interest in it, I'll explain how to handle arrays, TYPEd variables, and fixed-length strings, and also how to pass values back from functions rather than using subprograms. ********************************************************************** Tom Hanlin is the author of the very popular ADVBAS library for QuickBASIC. His new shareware library is called BASWIZ. He can be reached through the QUIK_BAS echo on Fidonet or in care of this newsletter. ********************************************************************** [EDITOR'S NOTE] The archive ASMREQ.ZIP contains the assembler source code plus an assembled .OBJ file for the routine contained in the article.