Is it my computers (a desktop and a laptop) not working correctly or is it that I can't increment past 32767 to 32769? I tried incrementing from 0 to 32766 and decrementing from 63999 to 32769 and it works, but i can't do a increment from 0 to 63999. Are there something special for 32767 to 32769?
I was trying to create a simple program that color the screen that is similar to the BSOD.
Thank you.
EDIT: Can someone also tell if the variable names can be anything other than a [Letter]X ( like using LOC instead AX? The sample code my professor uses only AX, BX as variable names )
A piece of your code, where you're experiencing described problems will be very helpful.
And of course a basic reading of a x86 assembly language, covering binary arithmetic, processor registers (e.g. AX, BX) and their size as long as different address modes which processor supports.
I can't really make any guesses at answering your first question because you didn't give enough information. I assume you're using one of the 16-bit registers (AX, BX, CX, etc.). There should be no problem incrementing right from 0 to 65535. What do you mean you "can't do an increment from 0 to 63999"? Why can't you? Your program crashes? Your computer explodes? It's against your religious beliefs? You'll have to be a little more clear than just saying you "can't".
As for the second question, that's complicated, because it depends on your assembler. As far as I recall, generally in x86 assembly, you have the full range of 8-bit registers (AL, AH, BL, BH, CL, CH, etc.), the full range of 16-bit registers (AX, BX, CX, etc.), the full range of 32-bit registers (EAX, EBX, ECX, etc.), plus loads of special purpose registers including floating point registers, plus you can access memory using addresses (if you're using 32-bit flat mode, those will be virtual addresses, otherwise they will be real, physical segmented addresses). Most assemblers will allow you to define macros for any combination of those (for example, you can make a macro for [EBP+4]). Read your assembler docs for more info.
I am not on my personal computer where all my files are saved at the moment, but here is an example of my working code:
| Code: |
MOV BX, 0
MOV AL, 1
FillTopScrn: MOV [ES:BX], AL
INC BX
CMP BX 32767
JLE FillTopScrn
MOV BX, 63999
FillBotScrn: MOV [ES:BX], AL
DEC BX
CMP BX 32769
JGE FillBotScrn
|
where as the following code does not work:
| Code: |
MOV BX, 0
MOV AL, 1
FillScrn: MOV [ES:BX], AL
INC BX
CMP BX 63999
JLE FillScrn
|
| NewGuyinTown wrote: |
where as the following code does not work:
| Code: |
MOV BX, 0
MOV AL, 1
FillScrn: MOV [ES:BX], AL
INC BX
CMP BX 63999
JLE FillScrn
|
|
It's been 10 years since I did any assembly, but if I recall, JLE is based on signed arithmetic. The long explanation is, it will do this check (in pseudocode):
IF (SignFlag != OverflowFlag || ZeroFlag == 1) JMP
So:
For BX = 63999 (0xF9FF), you'll jump (ZeroFlag == 1).
For BX = 32768 (0x8000), you'll get:
0xF9FF - 0x8000 = 0x79FF
That sets the ZeroFlag to 0 (not good enough...)
... and the SignFlag to 0 (0x79FF is a positive number, when the word is treated as signed)
... and the OverflowFlag to 0 (because 0xF9FF - 0x8000 doesn't cause an overflow). So, SignFlag equals OverflowFlag, meaning you'll not jump (again, the condition is (SignFlag != OverflowFlag || ZeroFlag == 1)).
In non-assembly terms, you're doing IF (-31231 <= -32768) JMP, hence no jump will happen.
The short explanation is, JLE will treat your numbers as if they're signed, so the highest number you'll be able to work with is 32767, any number "higher" than that will be treated as a negative number (starting with 0x8000 = -32768).
The shortest explanation: The unsigned equivalent of JLE is JBE (Jump if Below or Equal). Use that 
Ahhh, I see! That explanation was very helpful...
Kaneda, You're my hero.
Thank you!
And thank you to everyone that helped.
Totally overlooked the second question. Indi is right. (E)AX, (E)BX, (E)CX, (E)DX are not really "variables", but registers supplied by the CPU. There are several more, most of them have a specific use in relation to specific instructions.
They aren't (as it would seem) simply named alphabetically (their rather forced names are an indication that the alphabet was an inspiration, though
). AX is known as the "Accumulator" register, BX as the "Base" register, CX as the "Counter" register, and DX as the "Data" register. The LOOP instruction, for example, specifically uses (E)CX as a Counter for how many times to loop:
| Code: |
MOV cx, 8
mylabel:
; Do something
LOOP mylabel ; loops 8 times, decrementing cx each time
|
In 16-bit programming, BX was the only (general purpose) register that could be used for pointing to a memory address (being the "Base" part of the pointer, while other registers - CS, DS, ES and SS - held the "Segment" part), like:
| Code: |
MOV ax, [bx] ; moves the word at the address pointed to by bx into ax - DS holds the memory segment by default.
|
In 32-bit programming, you can use any register for that, so BX has no real specific purpose anymore.
But when such instructions aren't used, many of the registers can be used in the same way as variables - for storing anything. Doing calculations etc. directly on registers is faster than doing it on values stored in memory - but there aren't many of them, so most often you'll need memory too.
Most assemblers, as Indi indicated, will allow you to define variables (being areas in memory storing data) in much the same way as a high level language, through symbolic names for memory locations, so you'll be able to write:
| Code: |
MOV ax, i
MUL j
MOV i, ax
|
... which would amount to i = i * j. The assembler would then "convert" this into something like:
| Code: |
MOV ax,ds:[00h]
MUL ds:[02h]
MOV ds:[00h], ax
|
Might want to add to the conditional jump bit, that the Below vs. Less naming scheme goes for all the conditional jumps
Heard a lot of people saying how they're arbitrarily named, but it's really very simple - and systematic.
Signed comparisons use the terms "Less" and "Greater", unsigned use "Below" and "Above".
Is it safe to assume the stack is empty when I run my assembled program?
What happen if I pop from an empty stack, will I get some garbage data? If not, what do I compare this null pointer or empty stack value to?
How do I determine if the stack is empty without pushing anything onto the stack?
And how do I truncate the first byte (keeping the last 4 byte) of AX register so I can store it in AL?
The stack is used in ways you may not be aware of as well. Most notably it is often used for storing the program counter when you call a procedure.
I'm not sure if the assembler you use has CALL in the language but it is never safe to assume anthing about the stack, since it isn't an object like you may expect in higher level languages. If you pop from the stack, it just returns the data from wherever the stack pointer is pointing to and decrements the stack pointer.
If you pop and the stack is empty you will return garbage and may end up writing up to places in memory you don't want to. You might overwrite your program, you might just make your OS throw a hissy fit over access violations.
Why would you ever need to know if the stack is empty? However, the answer is to store whatever the stack pointers value is when you start your program, store it, and compare. The stack pointer is normally adressed as SP.
The larger registers should be individually addressable, the higher and lower orders of AX are likely to be AH and AL (or similar). Failing that you can always AND with 00001111 then MOV it (if your assembler allows it).
OK... my assignment is to convert the unsigned number the user inputs to hexidecimal (4 bits), binary (16 bits), the decimal when the unsigned binary format is used, and the decimal when the signed binary format is used (so 2^15 is actually 0 when it's in signed format).
So, I do not have write my own input/output procedure. The output procedure will print whatever character is in AL. The input give me the character in AL. So, it's not hard to print what they have inputted.
Say if I input "34252." First, I will multiple whatever is in BX by 10, but that will automatically store in a EAX, a register that stores extended words which is a pain for me. So I got to get the value in EAX back into BX. I can't use MOV BX,EAX... So I'm like "UGHHH!" After I converted EAX to BX (don't know if what I did works, but I stopped for the day), I have to convert AL, a register that store bytes, to AX, a register that store words, and then add AX to BX. And Taddah: I get 34. Then 342. Then 3425. Then 34252. I stopped at the conversion part...
The instructions I'm using to convert bytes into words and words into bytes are multiplication and division respectively, which sounds lame. If someone knows a better way to do with the x86, please advise me what to do.
Sorry... just wanted to bump this thread back up.
I still need help with the solution that transfer a byte to word and vice versa without using MUL and DIV.
MOV won't work for the combination of a word and a byte.
First of all, to be sure you already have that down, EAX is AX with 16 additional bits "on the left". AX consists of AH and AL, so, if you have the value 1234567 (hex 0x12D687) stored in EAX, the register representations will contain:
| Code: |
EAX: 00 12 D6 87
AX: D6 87
AH: D6
AL: 87 |
If you move a value into EAX, AX will contain part of that value, same for AH and AL. They're simply 32, 16 and 8 bit representations of the same register.
If you need to get the contents of AH (D6) into AL (and the 12 into AH), the simplest way is to shift the register towards the right by 8 bits:
| Code: |
; EAX = 00 12 D6 87 (AL = 87)
SHR eax, 8
; Now EAX = 00 00 12 D6 (AL = D6)
SHR eax, 8
; Now EAX = 00 00 00 12 (AL = 12)
SHR eax, 8
; Now EAX = 00 00 00 00 (AL = 00) |
In 16-bit programming, this obviously also works on AX.
You can keep the bits, rather than shifting them out, by using ROR (Rotate Right):
| Code: |
; EAX = 00 12 D6 87 (AL = 87)
ROR eax, 8
; Now EAX = 87 00 12 D6 (AL = D6)
ROR eax, 8
; Now EAX = D6 87 00 12 (AL = 12)
ROR eax, 8
; Now EAX = 12 D6 87 00 (AL = 00)
ROR eax, 8
; Now EAX = 00 12 D6 87 (AL = 87) |
There are equivalent ROL and SHL operations for rotating/shifting towards the left.
Thanks again. Your advice was very helpful.
Another question... sorry for asking so many questions... it's just that it's so different from high level programming.
I have a question regarding values stored in DX:AX...
[EDIT]: Disregard it, I have figured it out
For some reason I can't move values directly into DX:AX, but I was wondering if this will work:
| Code: |
PUSH 0
PUSH 0
...
...
POP AX
POP DX
... Some operation that changes the value of DX and AX...
PUSH DX
PUSH AX
...
...
POP AX
POP DX
MOV BX, 2
DIV BX
|
According to the instructions about x86 I downloaded from the internet, it say:
| Quote: |
Unsigned divide DX:AX by r/m16; AX <- Quotient,
DX <- Remainder
|
Would this code shift everything to the right by one bit in AX (and the rightmost bit of DX stored in the leftmost bit of AX)? Is it possible that everything in DX will not carry over and get lost?
[EDIT]: Disregard it, I have figured it out
Last edited by NewGuyinTown on Fri Oct 13, 2006 2:16 pm; edited 1 time in total
| Code: |
;********************************************************************************
;
; Program to allow users to enter unsigned numbers from the
; keyboard, and print them out in hexadecimal, binary, signed decimal,
; and unsigned decimal.
;
; Simon Tang
;
;********************************************************************************
DOSSEG
.MODEL SMALL
.STACK 100H
.CODE
RETURN EQU 13
ESCAPE EQU 27
BACKSPACE EQU 8
; EXTERNALS GO HERE
.CODE
EXTRN Print_Blank:NEAR
EXTRN Print_Greater:NEAR
EXTRN Print_Tab:NEAR
EXTRN Print_CRLF:NEAR
EXTRN Print_Char:NEAR
EXTRN Read_Char:NEAR
EXTRN Print_CR:NEAR
; MAKE NO CHANGES TO ANY CODE BETWEEN THE FOLLOWING TWO LINES OF STARS:
;********************************************************************************
MOV AX,@DATA ; Set DS to point to the
MOV DS,AX ; DATA segment
MOV AX,0003H ; Reset screen to Text, 80x25
INT 10H ; Call Function
Continue: CALL Print_Greater
CALL Print_Blank
CALL Read_Number
CALL Print_Hex
;CALL Print_Tab
;CALL Print_Binary
;CALL Print_Tab
;CALL Print_Unsigned
;CALL Print_Tab
CALL Print_Signed
CALL Print_CRLF
CMP AX,0
JNE Continue
JMP COntinue
MOV AH,4CH ; DOS "Terminate Program" Function
INT 21H ; Call Function
;********************************************************************************
Read_Number:
; REPLACE THESE LINES WITH A SUBROUTINE
; TO READ IN AN UNSIGNED INTEGER INTO AX,
; ONE CHARACTER AT A TIME, USING NO
; EXTERNAL MEMORY, ALLOWING FOR BACKSPACE,
; ENDING ON RECEIPT OF A "RETURN" (13)
; KEY (EXCEPT THAT USERS MUST ENTER AT
; LEAST ON DIGIT). TRANSPARENCY ON ALL
; USED REGISTERS EXCEPT AX MUST BE
; MAINTAINED.
PUSH 0
MOV BX,10
Contin_Read:
CALL Read_Char
CMP AL,57
JA Not_a_Return_Key
CMP AL,48
JB Not_a_Number_Key
CALL Print_Char
SUB AX,48
MOV CX, AX
POP AX
MUL BX
ADD AX, CX
PUSH AX
JMP Contin_Read
Not_a_Number_Key:
CMP AL,RETURN
JNE Not_a_Return_Key
;CALL Print_CRLF
;CALL Print_Char
;CALL Print_Hex
;CALL Print_CRLF
;CALL Print_Greater
;CALL Print_Blank
RET
JMP Contin_Read
Not_a_Return_Key:
CMP AL,BACKSPACE
JNE Not_a_BackSpace
RET
Not_a_BackSpace:
JMP Contin_Read
;-----------------------------------------------;
Print_Nybble:
; REPLACE THESE LINES WITH A SUBROUTINE
; THAT PRINTS OUT THE APPROPRIATE
; CHARACTER CORRESPONDING TO THE VALUE
; IN THE LOWER FOUR BITS OF AX. ANY
; VALUES BETWEEN 0 AND 9 MUST PRINT THE
; CORRECT CHARACTER BETWEEN '0' AND '9',
; AND ANY VALUES BETWEEN 10 AND 15 MUST
; PRINT OUR THE CORRECT CHARACTER BETWEEN
; 'A' AND 'F'. IGNORE ANY BITS OTHER THAN
; THE RIGHTMOST FOUR BITS.
;
; TRANSPARENCY ON ALL REGISTERS MUST BE
; MAINTAINED.
PUSH AX
AND AX,000FH
CMP AL,10
JAE Bit_Above_Nine
ADD AL,48
CALL Print_Char
POP AX
RET
Bit_Above_Nine:
ADD AL,65
CALL Print_Char
POP AX
RET
;-----------------------------------------------;
Print_Hex:
; REPLACE THESE LINES WITH A SUBROUTINE
; THAT PRINTS THE VALUE IN AX AS FOUR
; HEXADECIMAL (BASE 16) DIGITS. THIS
; ROUTINE MUST CALL Print_Nybble FOUR
; TIMES.
;
; TRANSPARENCY ON ALL REGISTERS MUST BE
; MAINTAINED.
;MOV AL,83
;CALL Print_Char
;MOV AL,105
;CALL Print_Char
;MOV AL,109
;CALL Print_Char
;MOV AL,111
;CALL Print_Char
;MOV AL,110
;CALL Print_Char
;CALL Print_CRLF
POP AX
ADD AX,48
MOV DL,AL
CALL Print_Char
SUB AX,48
CALL Print_CRLF
PUSH AX
MOV BL,0
MOV CL, 4
Hex_AX: CALL Print_Nybble
SHR AX,CL
INC BL
CMP BL,4
JB Hex_AX
RET
;JMP Hex_DX
;-----------------------------------------------;
Print_Binary:
; REPLACE THESE LINES WITH A SUBROUTINE
; THAT PRINTS THE VALUE IN AX AS 16 BINARY
; (BASE 2) DIGITS.
; TRANSPARENCY ON ALL REGISTERS MUST BE
; MAINTAINED.
RET
;-----------------------------------------------;
Print_Signed:
; REPLACE THESE LINES WITH A SUBROUTINE
; THAT PRINTS THE VALUE IN AX AS A SIGNED
; INTEGER. ANY NEGATIVE NUMBERS MUST
; BE PRINTED WITH A LEADING MINUS SIGN,
; ANY POSITIVE NUMBERS MUST BE PRINTED
; WITH A LEADING PLUS SIGN, AND ZERO
; MUST BE PRINTED WITH NO PREFIX.
; IF APPROPRIATE, YOU MAY CALL THE
; Print_Unsigned ROUTINE FROM THIS ONE.
;
; TRANSPARENCY ON ALL REGISTERS MUST BE
; MAINTAINED.
POP DX
POP DX
RET
;-----------------------------------------------;
Print_Unsigned:
; REPLACE THESE LINES WITH A SUBROUTINE
; THAT PRINTS THE VALUE IN AX AS AN
; UNSIGNED INTEGER OF BETWEEN ONE AND
; FIVE DIGITS.
;
; TRANSPARENCY ON ALL REGISTERS MUST BE
; MAINTAINED.
MOV CX,5
POP AX
PUSH AX
MOV DL,10
Find_Unsigned: DIV DL
ADD AL,48
CALL Print_Char
MOV DL,1
MUL DL
MOV AL,AH
ADD AL,48
CALL Print_Char
MOV AL, 0
MUL DL
MOV DL,2
DIV DL
MOV AL,AH
ADD AL,48
CALL Print_Char
MOV DL,10
CALL Print_CRLF
LOOP Find_Unsigned
RET
;-----------------------------------------------;
JMP Continue
END
|
I wasn't suppose to edit the code at Continue... but I'll haven't implemented code for the parts I commented out.
I'm having so much trouble with this... My head is spinning.
Read_Char reads input from keyboard and stores it in AX and AL
Print_Char prints the value of AL in ASCII
The Print_Hex keep printing the same Hex number no matter what inputs.
here you are one possible solution:
| Code: |
; compile: nasm -f bin -o ex.com ex.asm
;
org 0x100
section .text
start:
.L1:
call print_greater
call print_blank
call read_number
test ax, ax
jz .E1
call print_crlf
call print_hex
call print_tab
call print_binary
call print_tab
call print_unsigned
call print_tab
call print_signed
call print_crlf
jmp .L1
.E1:
mov ax, 0x4c00
int 0x21
; out: al := character_read
; altered: ah
read_char:
mov ah, 8
int 0x21
ret
; in: dl := character_to_print
print_char:
push ax
mov ah, 2
int 0x21
pop ax
ret
print_blank:
push dx
mov dl, ' '
P1:
call print_char
pop dx
ret
print_tab:
push dx
mov dl, 9 ; tab
jmp P1
print_cr:
push dx
mov dl, 13 ; cr
jmp P1
print_crlf:
call print_cr
print_lf:
push dx
mov dl, 10 ; lf
jmp P1
print_greater:
push dx
mov dl, '>'
jmp P1
; reads unsigned decimal number from 0 to 65535
; ends on first non digit character
; no overflow check i.e. numbers are taken mod 65536
; out: ax := number read
read_number:
push bx ; preserve bx and dx
push dx
sub bx,bx ; read_number will be acumulated here
.L1:
call read_char
cmp al, '0'
jb .E1
cmp al, '9'
ja .E1
mov dl, al
call print_char
sub al, '0'
xchg ax, bx
mov dx, 10
mul dx
sub bh, bh
add bx, ax
jmp .L1
.E1:
mov ax, bx
pop dx ; restore dx and bx
pop bx
ret
PH1:
and dl, 0x0f
cmp dl, 9
jbe PD1
add dl, 'a' - 10 - '0'
PD1:
add dl, '0'
call print_char
ret
PB1:
and dl, 1
jmp PD1
print_hex:
push ax
push cx
push dx
mov cx, 4
.L1
rol ax, 4
mov dl, al
call PH1
loop .L1
pop dx
pop cx
pop ax
ret
print_binary:
push ax
push cx
push dx
mov cx, 16
.L1:
rol ax, 1
mov dl, al
call PB1
loop .L1
pop dx
pop cx
pop ax
ret
print_unsigned:
push ax
push bx
push cx
push dx
mov bx, 10000
mov cx, 5
.L1:
sub dx, dx
div bx
xchg ax, dx
call PD1
push ax
mov ax, 10
xchg ax, bx
sub dx, dx
div bx
mov bx, ax
pop ax
loop .L1
pop dx
pop cx
pop bx
pop ax
ret
print_signed:
push ax
push dx
test ax, ax
jg .L1
mov dl, '-'
call print_char
neg ax
.L1:
call print_unsigned
pop dx
pop ax
ret
|