What everybody really needs is a good, thorough introduction to assembler which explains the basics before going deeper into technical info. One good online tutorial of this kind I've found is here:http://udgftp.cencar.udg.mx/ingles/tutor/Assembler.html
Also check The x86 Assembly Language Webring, which as you might guess from the name is a webring about assembly language for x86 chips, which refers to the Intel x86-series, the 286, 386, 486, etc. Pentiums are also included.
And check http://www.riverland.net.au/~mdunn for a few examples of neat asm tricks, like how to control the speaker, etc. (This is where I learned how to make a TSR.)
I should, however, include some material of my own. So, in my own style of simply jumping into an example and then explaining how everything works, here are some small programs in assembler to do various things.
;Turns on mouse cursor MOV AX,01 INT 33h MOV AH,4C INT 21h
The first line of this program is simply a comment. In assembler, a semicolon begins a comment, and the comment then continues through to the rest of that line.
The MOV AX,01 line puts a value of 01 (in other words, simply 1) into AX, which is a CPU register. Registers are small temporary storage areas in your CPU used for data processing.
The line INT 33h calls software interrupt number 33, which is used for mouse functions. (Software interrupts are mini-functions built into your computer.) INT 33 gets all its instructions from AX. Whatever we've specified for AX will tell INT 33 what to do. In this case, we've specified 1, which means we're telling INT 33 to turn on the mouse cursor. That's exactly what it will do now.
The final two lines are simply here for the purpose of program termination. INT 21 gets its instructions from AH, and when we feed 4C to it, that tells it to turn off the program. (This is important. If we omitted these last two lines from this program, the mouse cursor would be turned on, but the program would then stop running and lock up the computer.)
By now, you are no doubt wondering: How do I know that INT 33 controls the mouse and that MOV AX,01 will tell it to turn on the cursor? Sure, it's easy enough if you know that, but where might you find that information?
The answer is very simple: External references. Whole books have been written on INTs and registers and the MOVs that run them. Get a book from your local library that explains it all. Better yet, buy one. If you want to become a serious programmer, you will be referring to it constantly.
If you don't want to buy a book, or are simply too lazy to go to a library, you can also easily find complete INT lists on the Internet. In fact, from right here, you can download a program called HelpPC 2.10, which is a reference program chock-full of useful information for programmers and advanced computer users.
Download it here
You can also get HelpPC from Simtel at the following link:
Now that you have a program telling you everything you need to know, you may be wondering exactly how to create assembler programs. The small program listed above looks interesting enough, perhaps, but how do you run it? Just type it in at the computer's command line? Answer: No. You must use a compiler. Just like BASIC and C and Java, assembler is still not pure, raw machine code, and so you must use a program to translate it so the computer can run it.
If you use MS-DOS or Windows 95, you already have a program which can compile assembler instructions: Debug. It comes with MS-DOS and Windows 95. At the command prompt, simply type debug and press ENTER. You'll get a hyphen for a prompt. (Debug is remarkably unintuitive, but makes up for this with power.) Now, if you want to create the program above, you might as well name the program before you start. This can be done by typing simply n filename where "filename" is the name you want to give the file. (Use .COM for the extension, since this is a program.) N is Debug's command for specifying the file name. Most of Debug's commands are one character, actually. (I did say it was unintuitive.) Now you can type A and press ENTER. This is Debug's command for "assemble", and you can now begin typing in the program, pressing ENTER at the end of each line, of course. (Ignore the first line when typing it in, since it's just a comment anyway.) Once you're done, make a note of the number at the end of the prompt. That number shows how many bytes you've entered so far. (Minus 100. Notice that it starts at 100, so the actual number of bytes is 100 less.) For this particular program, you will see that it is 9 bytes long. Now, press ENTER to exit assembling and return to the hyphen prompt. One minor quirk of Debug is, you must specify the byte size of the programs you write before you save them. So, type RCX (short for Register CX). This will have Debug show you the current value of register CX (it will be 0), and the prompt will become a colon, indicating it wants you to type a new value for CX. Type 9 and press ENTER (since we're writing 9 bytes). Now, just type w (for "write") to save the program. All done. You can now type q (for "quit") to exit Debug.
The program is now in the current directory. If you want to see your mouse cursor, just run the program (make sure you have a mouse driver loaded first). You should now see it and be able to move it with the mouse.
By the way, notice the INT numbers above have an H after them. This is because some assembler compilers assume if you just use plain numbers that they should be converted into their hexadecimal values. Debug doesn't but A86, a very popular compiler, does. Basically, it would use INT 21 where it's supposed to use 33 (33 decimal = 21 hex) and 15 where it's suppoed to use 21 (21 decimal = 15 hex). Putting the H after clears up any problems that might come up with this.
Oh, and if you want to turn the cursor back off, here's a program which does just that:
MOV AX,02 INT 33h MOV AH,4C INT 21h
As you can see, this program is almost identical. The only difference is, a value of 2 in AX tells INT 33 to turn the cursor off.
MOV AX,5 ;5 in AX tells INT 33 to get mouse button info INT 33h ;get mouse button info CMP AX,1b ;this compares AX (button status) with 1 JL 0100h ;if no button is pressed, back to start ;AX will be zero while no button is pressed. JL means, if the first ;operand is less than the second, make the jump! mov ah,004C ;terminate program int 21h
The operation of this program should be fairly simple and obvious.
This next program prints out the canonical program test message: "Hello, world." Just about everybody's first program in BASIC or C is a program that prints out this message. Here's one in Assembler that does it
MOV AX,SEG MESSAGE MOV DS,AX MOV DX,OFFSET MESSAGE ;These three lines make DS:DX form a pointer to the segment:offset of ;MESSAGE. Note that AX is used as a go-between for the segment, ;because some assembler compilers don't allow you to MOV immediate ;values into segment registers (like DS) directly. Some do, some don't. ;Just to avoid problems, instead of MOV DS,SEG MESSAGE, we do ;that to AX and then MOV DS,AX. mov ah,0009 ;9 in AH makes INT 21 print the string int 21h ;referenced in DS:DX. mov ah,004C ;terminate program int 21h MESSAGE: DB 'Hello, world.$'
This program should also be fairly obvious. It introduces some new concepts, but just study it and I'm sure you'll figure out what everything does.
And now, yet another mouse program. This whole program is completely self-explanatory. It's bigger than the ones above, but it's well-commented.
;Gives you an on-screen button to click on with the mouse, and ;proceeds only when you do! mov ah,06h ;this calls SCROLL SCREEN UP mov al,00h ;AL specifies how many lines... 0 just clears the screen mov ch,00h mov cl,00h mov dh,24h mov dl,80h int 10h ;The above lines just clear the screen. mov ah,02h ;Set cursor position mov dh,0Bh ;row mov dl,22h ;column int 10h ;Actually does it. :) MOV AX,SEG MESSAGE MOV DS,AX MOV DX,OFFSET MESSAGE mov ah,0009 ;9 in AH makes INT 21 print the string int 21h ;referenced in DS:DX. ;So, we have printed BUTTON right where we want it!!! mov ax,0001 int 33h ;Now, that's got the mouse on. MOUSLOOP: MOV AX,5 ;5 in AX tells INT 33 to get mouse button press into INT 33h ;get mouse button info CMP AX,1b ;this compares AX (button status) with 1 JNE MOUSLOOP ;unless LEFT button ONLY is pressed, go back to MOUSLOOP ;(Only the left button will work for this... Not the right.) MOV AX,3 INT 33h ;Now we're getting the POSITION info through INT 33,3! ;CX now is mouse horizontal position. 0 - 639 ;DX now is mouse vertical position. 0 - 199 CMP CX,270 JL MOUSLOOP ;If CX is less than 270 (left edge of button), go back CMP CX,315 JG MOUSLOOP ;If CX if more than 400 (right edge), go back CMP DX,85 JL MOUSLOOP ;If DX is less than 85 (top), go back CMP DX,95 JG MOUSLOOP ;If DX is more than 110 (bottom), go back ;The numbers above for the button dimensions are approximations, but ;should be good enough. mov ax,0002 int 33h ;This just hides the cursor. Otherwise it would stay on when the program ;ended! :) mov ah,004C ;terminate program int 21h MESSAGE: DB 'BUTTON$'
Often, a program wants to simply check if a mouse driver has been installed or not. (Usually on startup of a program which requires a mouse, so the program can abort with a "No mouse driver" error if needed.) Here's a program which displays an error message if there is NO mouse driver, and exits silently if a driver was detected.
MOV AX,0000 INT 33h CMP AX,0000 JNE Ender ;Code here to perform if mouse driver is not installed. MOV AH,09 MOV DS,SEG Message MOV DX,OFFSET Message INT 21h Ender: mov ah,004C ;terminate program int 21h Message DB 'ERROR: Mouse driver was not detected.$'
A simple addition to the previous program which returns an errorlevel based on the presence of a mouse driver: Returns 0 if the driver was detected, or 1 if no driver was detected. (In DOS, an errorlevel of 0 is the standard code to signify "no error.")
;Checks for mouse driver, returns errorlevel of 0 if no error, returns ;errorlevel of 1 if error (i.e. mouse driver not installed) MOV AX,0000 INT 33h CMP AX,0000 JNE Itsok ;Code here to perform if mouse driver is not installed. MOV AL,01 ;Return errorlevel of 1 (indicating an error) JMP Ender Itsok: MOV AL,00 ;Return errorlevel of 0 (no problem) Ender: MOV AH,4Ch ;terminate program INT 21h
That was fun, but now let's move away from the mouse. Here's a utility to reset dates on files to January 1, 1980 at 12:00:00 AM (which is the earliest date DOS supports on files). Very handy for when you want to forget about when something happened.
;The following 5 lines just clear the carry flag (CF), which is important, ;because clearing the carry flag makes INT 21,3D return the file handle ;instead of an error code. PUSHF ;transfer 16-bit flag register onto stack POP AX ;AX now contains the flag register contents AND AX,0FFFEh ;sets rightmost bit (carry flag) to 0 PUSH AX POPF MOV AH,3Dh ;open file using handle MOV AL,1 ;write only MOV DS,SEG filename MOV DX,OFFSET filename INT 21h ;AX should now contain the file handle. MOV BX,AX ;transfer file handle to BX where it belongs MOV AH,57h ;set file date/time using handle MOV AL,1 ;set (not read) date and time MOV CX,0 ;12:00:00 AM MOV DX,21h ;January 1, 1980 (0000000000100001) INT 21h MOV AH,3Eh ;close file using handle ;BX already contains the appropriate file handle from the previous operation, ;so we don't need to re-set it. INT 21h mov ah,004C ;terminate program int 21h ;Edit the line below as appropriate to reflect the actual filename of the ;file whose date you want to reset. filename DB 'newfile.txt', 00
Here's a TSR shell. It provides the framework for what you need to make a program that stays resident in memory (now you just need to put the function that the memory-resident program should perform at the indicated point):
;HOW TO MAKE A TSR: ;STEP 1. Get the interrupt vector of INT 8. CLI ;turn off hardware interrupts MOV AL,8h ;specifies INT 8 MOV AH,35h ;get interrupt vector INT 21h ;STEP 2. Make INT C8 behave like INT 8. ;(INT C8 isn't used by anything anyway.) MOV DX,BX PUSH ES POP DS ;the above three lines set DS:DX to whatever ES:BX was MOV AL,0C8h MOV AH,25h ;set INT C8's vector (makes INT C8 behave like INT 8) INT 21h ;STEP 3. Set INT 8's vector to your own interrupt function. MOV DX,OFFSET TSRINT PUSH CS ;push the Code Segment... POP DS ;...to make DS equal this routine's segment MOV AL,8 MOV AH,25h ;set INT 8 to behave like your new interrupt function INT 21h ;STEP 4. Do INT 21,31 and you're done. MOV AL,0 ;exit code (for batch files) MOV DX,0FFh ;how much memory is reserved for this TSR MOV AH,31h ;make it into a TSR now INT 21h TSRINT: ;THIS IS THE ACTUAL CODE OF THE INTERRUPT ITSELF: CLI ;disable interrupts PUSHA ;save current registers status for later ;YOUR INTERRUPT ROUTINE HERE! This is where the code of the actual interrupt ;function (the process which the TSR should keep repeating while it's in ;memory) should go. ;A reminder: PUSHA is used above to save the registers so they can be easily ;restored to their former states with POPA later. However, PUSHA and POPA ;only affect (E)AX, (E)CX, (E)DX, (E)BX, (E)SP, (E)BP, (E)SI, and (E)DI. They ;do NOT affect any of the segment registers (CS, DS, SS, or ES), meaning that ;if your routine uses these registers, you will also need to save them by ;PUSHing them before this interrupt routine, and POPing them after it's over. POPA ;restore old register status (so everything's the same as before) INT 0C8h ;do the old INT 8 as well, otherwise what should be INT 8 never gets done PUSH AX MOV AL,20h OUT 20h,AL ;this sends an End-Of-Interrupt signal to the 8259 int controller POP AX STI ;re-enable interrupts (opposite of CLI) IRET ;interrupt return (signifies that the interrupt is over)
A standard PC BIOS stores its release date as an 8-byte string in memory location FFFF:5. This program prints that string.
;Print from ROM, starting at FFFF:5 for 8 characters ;(This shows the date of the ROM BIOS) MOV DS,0FFFFh MOV SI,5 MOV CX,8 mainloop: MOV DL,[SI] ;DL = character to output MOV AH,2 INT 21h ;print the character INC SI ;move SI to the next byte LOOPNZ mainloop ;this loops 8 times, because CX was set to 8 mov ah,004C ;terminate program int 21h
By default, DOS prompts are text-only, meaning you can't draw graphics on the screen with them. However, you can change your graphics mode with INT 10,0. This program starts off by switching to the famous mode 13 (320x200 256-color VGA, a very popular video mode for amateur DOS games) and then draws a single pixel to demonstrate the graphical capabilities of this mode.
;Draws a single yellow pixel in the upper-left. ;Writes directly to video memory which begins at A000:0 mov ah,00h mov al,13h int 10h ;The above three lines just switch to 320x200 256-color VGA. mov ds,40960 ;a000h = 40960 decimal mov ax, 44h ;44h is yellow! ;) mov bx,0000 mov [bx],ax ;This defaults to DS:[whatever], and since whatever here is bx, it moves ;AX into DS:BX!!! mov ah,004C ;terminate program int 21h
;By extension, draws a yellow line in the upper-left. ;A good example of how to efficiently use INC, CMP, ;and a conditional jump for repetitive tasks. mov ah,00h mov al,13h int 10h ;The above three lines just switch to 320x200 256-color VGA. mov ds,40960 ;a000h = 40960 decimal mov ax, 44h ;44h is yellow! ;) mov bx,0000 START: mov [bx],ax inc bx cmp bx,20 JL START ;This waits until BX reaches 20, then exits! mov ah,004C ;terminate program int 21h
;prints all possible 256 colors in a row, in order from 1 to FF, ;along the top of the screen. mov ah,00h mov al,13h int 10h ;The above three lines just switch to 320x200 256-color VGA. mov ds,40960 ;a000h = 40960 decimal mov bx,0000 mov ax, 01h MAINLOOP: mov [bx],ax inc bx inc ax cmp ax,0FFh JNG MAINLOOP mov ah,004C ;terminate program int 21h
This program turns on Caps Lock, Num Lock, and Scroll Lock.
;Move F0h to 40:17 MOV DS,40h MOV BX,17h MOV AX,0F0h MOV [BX], AX mov ah,004C ;terminate program int 21h
;Move 80h to 40:17 MOV DS,40h MOV BX,17h MOV AX,80h MOV [BX], AX mov ah,004C ;terminate program int 21h
Gunship 2000's excellent add-on, Islands And Ice, only lets you use its mission editor after you have attained a rank of at least 2nd Lieutenant (2LT). This is, frankly, outrageous, since you should be able to make your own missions at any time. This program is a simple way of dealing with this problem. It edits Gunship 2000's roster file, ROSTER.DAT, so that the first pilot in the roster is made a 2nd Lieutenant. If you want to change a pilot other than the first one in the roster, you can just edit the number of bytes which the program moves forward in the file. Currently it is set to 54h.
;Set byte 54h of file to 5. This will make you a 2nd Lieutenant (2LT), ;which in turn will let you use the mission builder. MOV AH,3Dh ;open file using handle MOV AL,01 ;write only MOV DS,SEG FILENAME MOV DX,OFFSET FILENAME INT 21h ;AX is now the file handle MOV BX,AX ;moves AX (the file handle) into BX MOV AH,42h ;move file pointer MOV AL,0 MOV CX,0 ;high order word of number of bytes to move MOV DX,54h ;low order word of number of bytes to move ;Move 54h bytes, since that's where the byte we want is INT 21h ;BX is still the file handle MOV AH,40h ;write to file using handle MOV CX,1 ;number of bytes to write MOV DS,SEG BUFFER MOV DX,OFFSET BUFFER INT 21h mov ah,004C ;terminate program int 21h FILENAME db 'ROSTER.DAT', 00 ;The 00 on the end is to terminate the filename with a 00 character, ;making it an ASCIIZ string. BUFFER db 5 ;BUFFER is what will actually get written to the file.
Well, that's all for now. If you have any advice or comments, please e-mail me. Thanks.
Back to the main page