learn asm in 1 hour flat
Register Use ======== ==== AX Calculation and Main register |__AL Low byte and Int function index (Lower AX) 8-bit register |__AH High byte (Higher AX) 8-bit register BX Calculation and basic Pointer |__BL Lower bit (Lower BX) 8-bit register |__BH High bit (Higher BX) 8-bit register CX Calculation and counter |__CL Lower bit (Lower CX) 8-bit register |__CH High bit (Higher CX) 8-bit register DX Calculation and Data |__DL Lower bit (Lower DX) 8-bit register |__DH High bit (Higher DX) 8-bit register SI Pointer DI Another pointer BP "Byte Pointer" mainly used to access stack and preform data movement SP "Stack Pointer" Points to first byte to the stack IP Code pointer points to current byte of code CS "Code Segment" Points to segment of current code cannot be changed, and even if it can would crash your system. DS "Data Segment" Points to segment of data area ES Extra segment, for use in misc. function FS Same as above GS Same as aboveWe all know what is AX and how AX is used. We all also know it's a 16-bit register (like an integer). But what in a blue moon is AL and AH? Quite simple, really. AH and AL are respectably the higher and lower bytes of AX.
Here's an example:
AX= 0 0 0 0 0 0 1 1 1 1 1 0 1 1 0 0 | | | | | | | | | | | | | | | | \-------------/ \-------------/ Higher Byte Lower ByteWell we have a number in AX (1004), when writen in binary it equals 0000001111101100. But what if we split it into two bytes? One we'll call the Higher Byte AH and we'll call the Lower Bit AL. So, if AX=1004 then,
AX= 0 0 0 0 0 0 1 1 1 1 1 0 1 1 0 0 | | | | | | | | | | | | | | | | \-------------/ \-------------/ Higher Byte Lower Byte = AH = ALAH = 00000011 = 3
It is also interesting to know that thanks to AH and AL, you could manipulate AX in many ways. Here's a formula you really might find handy:
Now that you know how AX, AL and AH behave, I can safely tell you that (as you have probably assumed) this counts to BX, CX and DX as well.
Lesson no.7 Segment Registers and memory
There are techniques around this for example:
mov ax,segment mov ds,axYou can use segments to address parts of the memory, much like POKE and PEEK. But you also need an offset (if you have'nt already,which you should by now, read the segment tutorial). To use an offset is rather simple, you can put your offset in di,si,bx or dx (which ever one picks your fancy) and you put square brackets around it. That is practically an offset pointer. Here's an example:
MOV BX,140h ;Offset MOV AX,3h ;Value to poke MOV [BX],AL ;why AL? because we only want to poke 1 byte (8-bit)This basically goes pokes 3 into the memory location 140h. But what segment you might ask? As QB has DEF SEG to address the default data segment, your processor uses the DS register for that job. So really you are writing 3h to DS:0140. Here's an example using a diffrent segment.
MOV BX,140h ;offset MOV AX,3h ;value to poke MOV CX,0A000h ;new segment MOV DS,CX ;our little trick to change the segment = ) MOV [BX],AL ;writes 3 to DS:BXThis would write 3 to A000:0140. The same code could have been writen like this:
MOV BX,140h ;offset MOV AX,3h ;value to poke MOV CX,0A000h ;new segment MOV DS,CX ;our little trick to change the segment = ) MOV DS:[BX],AL ;writes 3 to DS:BXWhich specifies that we want to use DS as the segment. This just makes things look clearer, but is not needed as DS is the default data segment. But what if you don't want to use DS? maybe you want to use CS or ES... You'd simply do this:
MOV BX,140h MOV AX,3h MOV CX,0A000h MOV ES,CX MOV ES:[BX],AL ;writes 3 to ES:BXPretty simple is'nt it? Well, since you've loyally finished this course you will find the next one more interesting as we get into new and exciting opcodes. These should make your life easier. Also we might be getting on on how to make your own library. All that coming next month!
Before I continue, I'd like to make 2 correction to previous articals.
*****Correction 1: In the segment tutorial I said 2 byte pointers for 32-bit, what I meant to say was 4 byte pointers. Thank god I picked it up while proof-reading it agian!*****
*****Correction 2: In issue#2 I said retf to exit the program. I don't know what I was thinking, but I'm sorry if it crashed your computer. Infact if we meet one day, you can hit me over the head with a stick = P Use retn or better still function 4C like in issue#3*****
Lesson no.8 Basics of The Stack
After coding a while, you'll probably think it gets pretty cramed with the small amount of registers you can use. It gets tacky and some times you might want to "backup" a value a register has. There is a way to do this and obviously it involves the stack or I would'nt be mentioning it (hehehe = P). The two fundimental commands to use the stack are:
PUSH POPPUSH saves a value of a register (or a memory location but lets not get on to that yet) on the stack, and POP basically takes this value which was saved out of the stack and in the register.
MOV AX,3 ;Give AX a value of 3 PUSH AX ;PUSH AX (3) onto the stack MOV AX,5 ;Give AX a value of 5 POP AX ;POP AX off the stackWell, lets take this simple code step by step. First AX is given a value of 3: "MOV AX,3". Then AX is push on the stack: "PUSH AX", after this AX is given a value of 5: "MOV AX,5". To finish it all AX is pulled out of the stack: "POP AX". As you probably (or hopefully as my maths teacher used to say = )) have figured out AX ends up having a value of 3. The reason for this is PUSH AX, pushed the value of AX (which was 3 at the time) onto the stack then at the end, even after AX was given 5, POP AX pops 3 out of the stack and puts it into AX agian.
Now, you might find this handy later on, but you don't have to give AX a register. You can also give it a immediate value! The above code could have been writen like this:
PUSH 3 ;PUSH 3 on the stack MOV AX,5 ;Give AX a value of 5 POP AX ;POP AX off the stackReading that, you'd relise it's not important what register you pop the value of the stack onto, infact you can re-write the code before the one above like this:
MOV AX,3 ;Give AX a value of 3 PUSH AX ;PUSH AX (3) onto the stack MOV AX,5 ;Give AX a value of 5 POP DX ;POP DX off the stack MOV AX,DX ;Put the value of DX into AXSo you pushed the value of AX, being 3, onto the stack and poped it out as DX! And at the end you moved DX into AX (MOV AX,DX). If you run that code, both AX and DX would end up being 3! As you'd realise the stack is very easy to use, but there is one more thing you have to learn. What if you want to push more than 1 register? Would you do something like this
PUSH AX ;PUSH AX onto the stack PUSH BX ;PUSH BX onto the stack POP AX ;POP AX off the stack POP BX ;POP BX off the stackSounds right does'nt it? We push AX, then BX then we POP AX then BX. Sad to say, but it does'nt work like this. Think of the stack, as a stack of papers. You put one paper labeled A on the floor and then put another paper labeled B down on the floor. When you go to pick up the one on the top you don't pick up A but you pick up B! Here's a simple diagram:
First we PUSH AX (put AX on the stack)
The Stack | | | | |===============| <-- AX |_______________|Then we PUSH BX (put BX on the stack)
The Stack | | |===============| <-- BX |===============| <-- AX |_______________|What's at the top of the stack? BX! This is why you can PUSH one register and POP out another one, because the stack does'nt keep track of what you pushed and where you pushed it, it simply pops out the one on the top! If you are ever confused think of "first in last serve", but for now here is another example:
MOV AX,3 ;Give AX a value of 3 MOV DX,5 ;Give DX a value of 5 PUSH AX ;PUSHs AX (3) on the stack PUSH DX ;PUSHs DX (5) on the stack ADD AX,DX ;ADDs DX into AX (3+5=8) PUSH AX ;PUSHs DX (8) on the stack POP CX ;POPs 8 off the stack and into CX POP DX ;POPs 5 off the stack and into DX POP AX ;POPs 3 off the stack and into AXSo AX has a value of 3 and DX has a value of 5. We then push AX then DX onto the stack so it'll look like this:
The Stack | | | | |===============| <-- DX (5) |===============| <-- AX (3) |_______________|When the add DX into AX which gives the value of 8. After this we push AX (which has the value of 8 now) onto the stack agian, so it should look like this:
The Stack | | |===============| <-- AX (8) |===============| <-- DX (5) |===============| <-- AX (3) |_______________|The operations to flow are:
POP CX ;POPs 8 off the stack and into CX POP DX ;POPs 5 off the stack and into DX POP AX ;POPs 3 off the stack and into AXWhen then pop out 8 and put it in CX, then pop 5 out for DX, and finnally 3 out for AX. So it's going to do this:
The Stack | | |===============| <-- AX (8) ==> CX |===============| <-- DX (5) ==> DX |===============| <-- AX (3) ==> AX |_______________|So CX=8, DX=5 and AX=3. Pretty simple if taken step by step, and I promise this will be very very handy to you when you start programming biggies. Now, should I make another chapter? Should I? Ah what the heck, it's 1999 (is it? Don't tell me it's over already = P) so lets go have some fun!
Lesson no.9 Procedures 2
Here's an example from Lesson 4 (edited because of retf ... hit me)
1E7D:0100 CALL 200 RETN ... 1E7D:0200 MOV AX,1 INT 33h RETNWell so we did'nt complicate things, I did'nt explain what the heck 1E7D was. Basically, 1E7D is the current segment the program resides. Now, we already know what CALL does (it's like a SUB) and retn (like END SUB) so in QB it would have looked like this:
CALL asub EXIT SUB asub ... 'Show the mouse END SUB asubYou would probably think that the way above there is quite messy. There is another way however, a diffrent approach. I'll be honest with you though, you'll end up with nearly exactly the same compiled code but the source code will look better. Lets look at the previous example in a diffrent way (and it gives me a chance to further explain the stack):
.STACK 100h ;This gives us 256 (100h) bytes worth ;of stack space, where we can push and ;pop .CODE ;This is the code segment (CS) CALL showmouse ;CALL the procedure "showmouse" mov ax,4C00h ;the prefered way to exit a program int 21h ;Use the function 4C to exit showmouse PROC ;like saying SUB showmouse mov ax,1 ;Use function 1 int 33h ;of int 33h retn ;return showmouse ENDPROCHey, this kinda looks like a .BAS file in a text editor eh? PROC is like SUB and ENDPROC is like END SUB. But remeber, you always have to have the retn at the end it's what gets you back to the main section. Without the retn, your program will most definatly crash, and if you got complex routines that mess with the hard drive to come, you can kiss your computer goodbye. I can't stress enough how important it is to return to the main code section because when you make libraries for QB, you'll probably want control back to your program, not what ever garbage is after your little routine.
Now, on to a part which you've probably been waiting ages for. How to implement such functions in QB. It's all rather simple really, but it does require a linker and a library creator (which ussally come with QB) and a little bit of patience. Compiling and Linking can be a hassel sometimes, but there are make utilities out there to make life just that little bit easier so I suggest you grab one.
First you'd make a assembler file which looks like this:
.model medium,basic .stack 100h ;enough for us and BASIC .data ;Where you would place all your variables ;(explained later,data segment) .code ;the code segment begins here PUBLIC showmouse ;Declare "showmouse" to QB showmouse PROC ;Like SUB showmouse mov ax, 1 ;Function 1 of int 33h ;Int 33 ret ;Return to BASIC showmouse ENDP ;Like END SUB showmouse END ;END the whole thingNotice the PUBLIC up there? Well, that's needed for QB to access the procedure (showmouse) we've made. It's like DECLARE SUB if you think about it. Anyway, from here you'd compile the code as a .obj file then link as a .QLB file like this:
C:\PROJECT\MOUSE>tasm32 mouse.asm mouse.obj Turbo Assembler Version 5.0 Copyright (c) 1988, 1996 Borland International Assembling file: mouse.asm Error messages: None Warning messages: None Passes: 1 C:\PROJECT\MOUSE>link /qu mouse.obj Microsoft (R) Overlay Linker Version 3.69 Copyright (C) Microsoft Corp 1983-1988. All rights reserved. Run File [MOUSE.QLB]: List File [NUL.MAP]: Libraries [.LIB]: bqlb45.libTASM is used here, but any other compiler would do just fine, if it can generate .obj files (But I recommend the Borland TASM compiler). Now, you would load up QB using the library like so:
QB /L mouseWhen inside QB, you'd make a statment like this:
DECLARE SUB showmouseThat's all there is to it! Well, not quite but we'll learn more later on! I hope your busting with ideas for stuff to put into QB! I'll be back next issue with stuff on linking ASM with QB. Hey that's what this whole thing is about is'nt it? = )