Assembly and Cracking From the Ground Up
An Introduction by Greythorne the Technomancer

Some User Interactivity



Our First Real Program


For this next section we are going to put together a small program,
Complete with user interactivity.

First of all, note the semicolons marking the start of a comment

Inline commentation is the technical term, but what it means is simple:
anything that is written on a line after the semicolon is ignored by the compiler.

You can therefore use that space to make your own comments in the program. This works in TASM and MASM and most other assembly compilers, but don't try it in Borland's C/C++ compiler when doing inline assembly, it just doesn't know what a semicolon is for inside the assembly sections.

* * *

Here is a sample that shows text string output like described in the last lesson.


[ ------------------------- CUT HERE -------------------------- ]

;**********************************************
;
; .COM File Program Template  (COM.ASM)
;
; Compile with:
;
;      TASM COM.ASM
;      TLINK /t COM.OBJ
;
; +gthorne'97
;
;**********************************************

            .model small
            .code
            .386

            org     100h

start:  jmp     MAIN_PROGRAM

;----------------------
; Your Data Here
;----------------------

            CopyMsg     db      'Copyright (c)1997 By Me!',0Dh,0Ah,'$'

            ;----------------------

MAIN_PROGRAM:

            ;---------------
            ; Your Code Here
            ;---------------

            mov dx, offset CopyMsg      ; tell DOS where string is
            mov ah, 09h                 ; DOS command 9 = print string
            int 21h                     ; tell DOS to issue command

            ;---------------

            mov al, 0h             ; return code (0 = no error)

EXIT_PROGRAM:

            mov ah,4ch           ; quit to DOS
            int 21h

            end     start

[ ------------------------- CUT HERE -------------------------- ]

Notice that I called the message CopyMsg, and used the name as a label by which I can reference my string at any time. Strings are referenced by their offsets in memory so as to differentiate them from a normal tasm label or some kind of variable. If you look at the usage of the phrase MAIN_PROGRAM, it is also a label - but it is not a string, so you don't use the offset directive to point at it.

For now, think of DOS strings as offsets in memory that end in either $ or a zero.

The 0Dh,0Ah is the code for the computer to hit the enter key at the end of the string.

(0D is the hex code for a carriage return which means 'move all the way to the left', and 0A is the code for a linefeed which means 'drop down one line on the screen')

Just for fun, another interesting code is 07h that is known as the BELL character, which used to be ctrl-G on older computer keyboards. When a 07h is printed to the screen, it issues a computer beep (the same one you hear when you reboot your machine or get some lame windows error) which is quite handy on occasion.

* * *

Now that we know how to print a string, here is a simple one:
how to print a character.

The DOS version is like this (the non-dos version is on my page in sample code, so you can experiment with that if you like. For this lesson you won't need it - but remember that people use all of these methods (and then some) to print text to a screen, so reverse engineering an application requires knowledge of all the possibilities.

Here are two in a row that are very useful.

This one expects the user tyo hit a key on the keyboard:

mov ah,08h ; DOS function 08h, get a key from user
int 21h ; tell DOS to do the command

After this command, the code for the character that the user pressed is sitting in the AL register.

This next command, shows what character was pressed on the screen.
It expects the code of the character that we want to put on the screen is in the DL register, so we move it there (mov means COPY really, so when we type MOV DL, AL it makes a copy of AL into DL)

mov dl, al ; copy the user keypress from AL into DL
mov ah,06h ; DOS function 06h, print one character to the screen
int 21h ; tell DOS to do the command

Now we will use it, in the program we have already been using as an example:

[ ------------------------- CUT HERE -------------------------- ]

;**********************************************
;
; KEYPRESS.ASM
; Our First Interactive Program
;
; Compile with:
;
;      TASM KEYPRESS.ASM
;      TLINK /t KEYPRESS.OBJ
;
; +gthorne'97
;
;**********************************************

           .model small
           .code
           .386

           org     100h

start:  jmp     MAIN_PROGRAM

;----------------------
; Your Data Here
;----------------------

CopyMsg     db      'Copyright (c)1997 By Me!',0Dh,0Ah,'$'

PressEnter  db      0Dh,0Ah,'$'

;----------------------

MAIN_PROGRAM:

;---------------
; Your Code Here
;---------------

; DISPLAY OUR COPYRIGHT MESSAGE

mov dx, offset CopyMsg      ; tell DOS where string is
mov ah, 09h                 ; DOS command 9 = print string
int 21h                     ; tell DOS to issue command

; Press ENTER for the heck of it

mov dx, offset PressEnter   ; tell DOS where string is
mov ah, 09h                 ; DOS command 9 = print string
int 21h                     ; tell DOS to issue command

; GET A KEY FROM THE USER (DOES NOT ECHO)
; RESULT IS LEFT IN AL REGISTER

mov     ah,08h       ; DOS function 08h, get a key from user 
int     21h          ; tell DOS to do the command

; ECHO (SHOW) WHAT KEY THE USER PRESSED

mov      dl, al    ; copy the user keypress from AL into DL
mov     ah,06h      ; DOS function 06h, print character in DL to screen  
int     21h         ; tell DOS to do the command      

; JUST FOR FUN WE GO BEEP

mov      dl, 07h     ; copy the BEEP code into DL
mov     ah,06h    ; DOS function 06h, print one character to the screen  
int     21h        ; tell DOS to do the command (which will make a BEEP)    

;---------------

mov al, 0h                  ; return code (0 = no error)
mov ah,4ch                  ; quit to DOS
int 21h

end     start

[ ------------------------- CUT HERE -------------------------- ]

This last section is the most fun, because now we have most of what we need to make a real program that actually does something.

Whenever you are sitting in front of a screen with a blinking cursor in some program waiting for a keypress, it isn't passive. It is actively running through a loop that checks constantly whether you are pressing a key. The computer itself is not required to do this, but programs are.

In actuality, the INT 21 (interrupt 21) really means this:
The processor in the computer is doing it's own thing. All of a sudden, some program or person does something that interrupts the normal flow of things. So it doesnt have to stay wasting processor time by constantly scanning the keyboard to see if some user pressed a key or not. But in our programs, we are required to do the constant checking. This isn't a problem really, and quite the normal way that input is done in any game or data entry program, even if you don't see it.

For this purpose, it is usually the norm to set up and endless loop that only gets broken out of in a special case (say for instance a special code is entered, or just a simple key is hit. In our case, we will expect that the key has to be a Y or N (we will accept lowercase as well)

Our code will not be optimized, it is important to be clear in this case. Showing you how to make the program compile so that the executable is smaller will be for later. Right now we just want to make it work.

Making an endless loop is simple.


START_OF_LOOP: ; the label (notice the colon)

; your code goes here

JMP START_OF_LOOP ; jump back to the label

GO_ON_WITH_PROGRAM: ; a label to mark after the loop


Now we need to know how to get out of it (some way to get to the part that says GO_ON_WITH_PROGRAM)

Remember how a key is entered by the user with this command:


; 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


We must be able to tell if it is a key we want it to be. For this we have CMP (compare).
CMP compares 2 things and returns a result in AL. If the result of the compare is TRUE, then AL=0. If it is false, it is something other than zero.

Since our key is stored in AL, a CMP would clobber it if we did not store it somewhere for safety, so we choose to store it in BL. (Why BL? because we havent used it yet. No other reason.)

the way you use CMP is like this:

CMP SOMETHING , SOMETHING

in our case, we are comparing BL to a letter Y from the user so we type:

CMP BL, 'Y'

notice that the letter Y is in quotes. Text characters should be in quotes or the assembler gets confused.

numbers in decimal are written normally:

CMP BL, 89

numbers in base 16 (hexadecimal are written like this:

CMP BL, 059h

All 3 of these reveal the same result, as the ASCII code for the letter 'Y' is 89 in decimal, which is 59 in base 16.

* * *

then we have many nice commands that work based on the results of a compare.
there is JZ (jump if zero)

JZ is also called JE (jump if EQUAL)

Basically it is also a simple concept. If the result of a compare is zero then your program jumps to somewhere you want it to, otherwise it does not make a jump to anywhere.

and JNZ (jump if not zero) which is also known as JNE
which is the exact opposite of JZ

Here is a sample section of code that does what we have just been describing:

* * *

START_OF_LOOP:	; the label (notice the colon)


; 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           GO_ON_WITH_PROGRAM

     cmp       bl, 'N'	; see if user pressed a 'N'
     je           GO_ON_WITH_PROGRAM



JMP START_OF_LOOP	; jump back to the label

GO_ON_WITH_PROGRAM:	; a label to mark after the loop

* * *


You should be able to follow this program rather easily now.

[ ------------------------- CUT HERE -------------------------- ]

;**********************************************
;
; KEYPRESS.ASM
; Our First Interactive Program
;
; Compile with:
;
;      TASM KEYPRESS.ASM
;      TLINK /t KEYPRESS.OBJ
;
; +gthorne'97
;
;**********************************************

.model small
.code
.386

org     100h

start:  jmp     MAIN_PROGRAM

;----------------------
; Your Data Here
;----------------------

CopyMsg     db      'Copyright (c)1997 By Me!',0Dh,0Ah,'$'

PressEnter  db      0Dh,0Ah,'$'

;----------------------

MAIN_PROGRAM:

;---------------
; Your Code Here
;---------------

; DISPLAY OUR COPYRIGHT MESSAGE

            mov dx, offset CopyMsg      ; tell DOS where string is
            mov ah, 09h                 ; DOS command 9 = print string
            int 21h                     ; tell DOS to issue command

            ; Press ENTER for the heck of it

            mov dx, offset PressEnter   ; tell DOS where string is
            mov ah, 09h                 ; DOS command 9 = print string
            int 21h                     ; tell DOS to issue command

START_OF_ENDLESS_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           GO_ON_WITH_PROGRAM

      cmp       bl, 'N'		; see if user pressed a 'N'
      je           GO_ON_WITH_PROGRAM

      cmp       bl, 'y'		; see if user pressed a 'y'
      je           GO_ON_WITH_PROGRAM

      cmp       bl, 'n'		; see if user pressed a 'n'
      je           GO_ON_WITH_PROGRAM   

      ; WE GO BEEP IF ANY OTHER KEY WAS PRESSED

       mov      dl, 07h           ; copy the BEEP code into DL
       mov     ah,6               ; DOS function 06h, print a character 
       int     21h                ; tell DOS to do the command (make a BEEP)    

       JMP START_OF_ENDLESS_LOOP

GO_ON_WITH_PROGRAM:

      ; ECHO (SHOW) WHAT KEY THE USER PRESSED

       mov      dl, bl            ; copy the user keypress from BL into DL
       mov     ah,6               ; DOS function 06h, print a character
       int     21h                ; tell DOS to do the command  

      ;---------------
         
      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 -------------------------- ]

Now for experimentation, see if you can modify the above program so that it compares whether you typed 'N' or 'n' and tells you a text string saying 'you said no!' and on the other possibility it will say 'that was affirmative!' if you hit a 'y' or a 'Y'

It would also be nice if you made a text message at the start of the program that tells the user to hit keys (Y/N). There is nothing more frustrating to a user than not knowing what the machine is supposed to be waiting for.

Well take care and enjoy!

also - if you get edgy to try something, the exit code of the program is known to batch files as the ERRORLEVEL
so you can actually use this program to make batch files that do different results based on whether you hit yes or no in this program.

There are many uses of the errorlevel being set at the end of the program, since another program can tell exactly what the results of your app were.



+gthorne'97