| Assembly and Cracking From the Ground Up |
| An Introduction by Greythorne the Technomancer |
| Modularity and Procedures |
|
Developing Applications the Cool Way Recently I was asked a few things about how to create large programs. The second part goes back to making modular programs. Recently I have been asked questions from a few people on why program form is important. IMPORTANT BEYOND REASON: COMMENT AS MUCH AS YOU CAN, Saying that mov cx, 9 means 'cx = 9' is not a good comment. If you think you can get by without it, you are better than I. Or you are a fool. Let yourself decide. Assemblers cannot live without comments. ALSO COMMENT the sections of code: * * * This section takes the command line and copies it to a buffer, It expects: CX = number of characters in the command line * * *
Maybe i need to lay off the caffeine... ;) - - - - - - - - - - - - - - - - Modularity: You could write a program straight from beginning to end with no modularity to it. Or you could write in sections that can be easily altered at a moment's notice and called by several parts of the code, thus reducing typing and the possibility of bugs. Sounds better when I put it like that doesn't it? There is also something else involved here. When you modularize your files, compilers are able to compile each section separately, thus causing no memory overloads. - - - - - - - - - - - - - - - - Okay then, now you ask HOW do I do this efficently? Procs. All decent languages have the ability to make subroutines that can be used from anywhere within your program. C calls them functions (though every language handles them slightly differently) - - - - - - - - - - - - - - - - In standard tasm, when you load it with no special mode in mind, a proc can look like this:
PrintLine proc near
mov ah, 9 ; DOS print function ah=09h
int 21h ;
ret ; return from proc
endp PrintLine
and to use it you can do this:
mov dx, offset MyMessage
call PrintLine
This is really handy!!!
It has its drawbacks though, which become apparent when you write several of them
;----------------------
CompareByte proc near
Loop:
inc ah
cmp ah, 092h
jne Loop
ret
endp CompareByte
;----------------------
CompareWord proc near
Loop:
inc ax
cmp ax, 02942h
jne Loop
ret
endp CompareWord
;----------------------
When you compile this, even though CompareWord and CompareByte are 2 separate routines, the label 'Loop' is duplicated.
The compiler has no idea which one you are referring to in either case, so the compiler returns an error.So you would normally have to make differences in the names like so:
;----------------------
CompareByte proc near
CmpByteLoop1:
inc ah
cmp ah, 092h
jne CmpByteLoop1
ret
endp CompareByte
;----------------------
CompareWord proc near
CompareWordLoop1:
inc ax
cmp ax, 02942h
jne CompareWordLoop1
ret
endp CompareWord
;----------------------
If you had a particularly long program, you would get SICK of trying to find ways to differentiate the things.There is an easy solution. Use IDEAL MODE. In the top of the EXE or COM file template header, add the word IDEAL to let TASM know that we are using the ideal mode. Here is an altered version of my com file header you already know:
;----------------------
COM_PROG segment byte public
ideal
assume cs:COM_PROG
org 100h
start:
;----------------------
this makes our basic way to write a proc a little simpler as well...
proc PrintLine
mov ah, 9 ; DOS print function ah=09h
int 21h ;
ret ; return from proc
endp PrintLine
proc and endp line up for ease of reading as a bonus.
The real bonus comes in the form of a pair of 'at' signs (@@)
Take a look at the following code changes to our CompareByte example.
;----------------------
proc CompareByte
@@Loop:
inc ah
cmp ah, 092h
jne @@Loop
ret
endp CompareByte
;----------------------
proc CompareWord
@@Loop:
inc ax
cmp ax, 02942h
jne @@Loop
ret
endp CompareWord
;----------------------
The @@ symbols refer to locality.That means that anything inside the proc with @@ in front of it is local to the proc, and no other code sees it. When something is not local, it is called GLOBAL, which means it is available to all sections of code. Global parts of code are very handy, but only for a few things. In a game program it is very nice to have the running total of your score global, for example. Locality makes it possible to make larger programs without having dangerous effects on other sections of code. Looking back at our program to get 'y' or 'n'... I will rewrite it here as an example of proceduralizing (gret word eh?) the code. I will overdo it a bit on purpose. In computer science classes, the end goal is to make the main section of code have as few lines as possible, and only to make a few calls and then exit. A simplistic vew of 'main' would include: ; your code here call input call process call output ; end program hereI won't require that kind of pressure on you, but the more you have your program built in sub-parts rather than one big block of code, the easier your coding job will be in the long run.
[ ------------------------- CUT HERE -------------------------- ]
;**********************************************
;
; KEYPRESS.ASM
; Our First Interactive Program
; (With a little proceduralizing)
;
; Compile with:
;
; TASM KEYPRESS.ASM
; TLINK /t KEYPRESS.OBJ
;
; +gthorne'97
;
;**********************************************
.model small
.code
.386
ideal
org 100h
start: jmp MAIN_PROGRAM
;----------------------
; Your Data Here
;----------------------
CopyMsg db 'Copyright (c)1997 By Me!',0Dh,0Ah,'$'
PressEnter db 0Dh,0Ah,'$'
;----------------------
; Your Procedures Here
;----------------------
proc PrintString ; Print a string in DX
mov ah, 09h ; DOS command 9 = print string
int 21h ; tell DOS to issue command
ret ; return from call
endp PrintString ;
;----------------------
proc PrintChar ; Print a character in DL
mov ah,6 ; DOS function 06h, print a character
int 21h ; tell DOS to do the command
;(which will make a BEEP)
ret ; return from call
endp PrintChar ;
;----------------------
proc CopyRight ; Display Copyright Message
mov dx, offset CopyMsg ; tell DOS where string is
call PrintString
; Press ENTER for the heck of it
mov dx, offset PressEnter ; tell DOS where string is
call PrintString
ret ; return from call
endp CopyRight ;
;----------------------
proc GetInput ; Check for valid input
@@Loop:
; GET A KEY FROM THE USER INTO AL (DOES NOT ECHO)
mov ah,8 ; DOS function 08h, get a key from user
int 21h ; tell DOS to do the command
mov bl, al ; store key for later in BL register
;so we don't lose it
; because AL gets clobbered
; when a CMP is issued
cmp bl, 'Y' ; see if user pressed a 'Y'
je @@Done
cmp bl, 'N' ; see if user pressed a 'N'
je @@Done
cmp bl, 'y' ; see if user pressed a 'y'
je @@Done
cmp bl, 'n' ; see if user pressed a 'n'
je @@Done
; WE GO BEEP IF ANY OTHER KEY WAS PRESSED
mov dl, 07h ; copy the BEEP code into DL
call PrintChar
jmp @@Loop
@@Done:
; ECHO (SHOW) WHAT KEY THE USER PRESSED
mov dl, bl ; copy the user keypress from BL into DL
call PrintChar
ret ; return from call
endp GetInput ;
;----------------------
MAIN_PROGRAM:
;---------------
; Your Code Here
;---------------
call CopyRight ; Display Copyright Message
call GetInput ; Get User Input
;---------------
mov al, bl ; put keypress into AL to be the
;exit code from this program
mov ah,4ch ; quit to DOS
int 21h
end start
[ ------------------------- CUT HERE -------------------------- ]
Take Care! |
| +gthorne'97 |