Now for the first time we are going to use memory to store data, there is a db directive which tells assembly compiler to place the operand right as it is to the memory. Notice the difference, it is a directive not an instruction. Directive is a thing for your compiler while the instructions are to be executed by the CPU. Normally when you write ADD your compiler translates this into the corresponding bytecode. An example will make it clearer for you to understand.
In this example I am going to use the db concept as well as direct addressing mode which is indicated by [] syntax, and procedure calls.
First of all have a look at this procedure:
printstr PROC
myloop:
MOV AH, 02h ; we are setting it to print mode
MOV DL,[BX] ; we are moving the data located at the memory address BX to DL
CMP DL, 0h ; we are checking if that data is 0
JE finish ; if 0 we jump to finish and return
INT 21h ; if not we output that data
INC BX ; we are incrementing the memory location.
JMP myloop
finish:
RET
printstr ENDP
Notice that MOV DL, BX and MOV DL, [BX] do completely different things. First copies the contents of the BX to DL, second copies the data located at the memory address of BX to DL.
So if we know we have a string starting at the address 0100h, you know that each character takes one byte. And one memory cell is one byte too, so the next character will be at the next address. We can access it by incrementing our current address. Notice that H e l l and o are placeholders for their ascii values while 0 is the exact 0 not the printable digit whichs ascii value is 48 if I am not mistaken. Because we are comparing with 0h not '0'.
0100h H
0101h e
0102h l
0103h l
0104h o
0105h 0h
If our memory looks like this we can do the following:
MOV BX, 0100h
CALL printstr
and voila! This will print Hello to screen. We need 0h because otherwise we wouldn't know when to stop. C does the same thing at the background when you create a string "Hello", it allocates 6 bytes, put the characters to first 5 bytes and adds a null to the end.
Now you may ask how are we going to allocate a memory like this, it's simple, with db directive!
message:
db 'Hello',0
message2:
db ' World!',0
message and message2 are labels. When we write 'Hello' to memory we need to know the address of it so we place a label before it. Remember labels give us the exact addresses of the next instruction. (This is not an instruction but uh.. you got it)
All together our code will look like this:
MOV BX, message
CALL printstr
MOV BX, message2
CALL printstr
INT 20h
message:
db 'Hello',0
message2:
db ' World!',0
printstr PROC
myloop:
MOV AH, 02h
MOV DL,[BX]
CMP DL, 0h
JE finish
INT 21h
INC BX
JMP myloop
finish:
RET
printstr ENDP
We are actually sending a parameter to printstr but not in a way you used to. Instead we place it into BX register and procedure uses that. This is how you transfer data between procedures in Assembly.
Now we know how to get data from the memory using brackets( [] ), next I will show you how to edit the memory, for example let's design a program where you read input from the user and write it to the memory till user presses enter, and than echo it back to the user. For this we need an empty location where we can store our characters. Actually it doesnt have to be empty since we will overrite but empty in a manner that no one is going to use it. We wouldn't want to overwrite some meaningful bytecode.
Here is how we get a memory location in which we can write,
myspace:
db ? ; ? means we don't care what is written there now, because we will overwrite it.
Actually saying something like db 'Hello this is a string' is completely OK too, because we won't read data from there which we didn't write.
MOV BX, message
CALL readstr
MOV BX, message ; we need to reset BX to the starting address since readstr procedure changes BX.
CALL printstr
INT 20h
readstr PROC
MOV AH, 01h ; set it to read mode
readloop:
INT 21h
CMP AL, 13d ;if user pressed enter we finish reading.
JE readfinish
MOV [BX], AL
INC BX
JMP readloop
readfinish:
MOV B[BX],0h ; null-terminate our string, we need B[BX] because we want to store a 8-bit 0. not 16-bit zero.
RET
readstr ENDP
printstr PROC
printloop:
MOV AH, 02h
MOV DL,[BX]
CMP DL, 0h
JE printfinish
SUB DL, 32d ; we are making the given lower case text to uppercase. 32 is 'a' - 'A'. if user enters upper characters this will print meaningless characters. But we don't check it here.
INT 21h
INC BX
JMP printloop
printfinish:
RET
printstr ENDP
message:
db ?
Now run the program and input something like "hello" and it will reply with "HELLO". After db ? don't put any more code or data. Because it only reserves one byte. So if you do something like this
db ?
db 'Hello'
the memory would look like this
0100h RANDOMDATA
0101h H
0102h e
..
So if you try to write something to 0100h more than one byte, it will overlap with Hello. But this is perfectly OK:
db 'Hello'
db ?
Because first directive knows how much bytes to reserve, you can write anything starting from the second one. Of course I omitted the labels here for convenience. But if you have the label for the first db you can find out the address of the location of the second directive, how? adding 5 to the first one. Because we know exactly how many bytes the first directive takes.
Now let's write a very simple calculator which can take 1 digit numbers and add, subtract, multiply and divide them. Later we will expand this to accept more than 1 digit numbers, but as a starting point let's consider this minimal case. Every query will be 3 characters long, in the form of
<integer><operator><integer>.
3+5, 4*2,2-1 are valid queries.
CALL readdata ;read three characters and store them in first, operator and second.
CALL convertdata ;convert the ascii values to actual values to be able to compute
CALL calculate ;calculate the result and put it in BL
MOV AH, 02h
MOV DL, '='
INT 21h
;then print =result
ADD BL, '0'
MOV DL, BL
INT 21h
INT 20h
myadd PROC
MOV BL, first B
ADD BL, second B
RET
myadd ENDP
mysub PROC
MOV BL, first B
SUB BL, second B
RET
mysub ENDP
mymul PROC
MOV AL, first B
MUL second B
MOV BL, AL
RET
mymul ENDP
mydiv PROC
MOV AH, 0h
MOV AL, first B
DIV second B
MOV BL, AL
RET
mydiv ENDP
calculate PROC
CMP operator B, '+'
JNE notAdd
CALL myadd
notAdd:
CMP operator B, '-'
JNE notSub
CALL mysub
notSub:
CMP operator B, '/'
JNE notDiv
CALL mydiv
notDiv:
CMP operator B, '*'
JNE notMul
CALL mymul
notMul:
RET
calculate ENDP
readdata PROC
MOV AH, 01h
INT 21h
MOV first B, AL
INT 21h
MOV operator B, AL
INT 21h
MOV second B, AL
RET
readdata ENDP
convertdata PROC
SUB first B, '0' ;get the real numerical value instead of ascii value to do calculations
SUB second B, '0'
RET
convertdata ENDP
first:
db ? ;we were able to add more code after ? because we know that
operator: ;we won't be putting more than one byte into these locations.
db ?
second:
db ?
If you examine the individual procedures one-by-one you should be able to understand the code.
No comments:
Post a Comment