FRIHOST FORUMS SEARCH FAQ TOS BLOGS COMPETITIONS
You are invited to Log in or Register a free Frihost Account!


Programming in Assembly Language: x86 - reserved pixel?





NewGuyinTown
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 )
muggle
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.
Indi
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.
NewGuyinTown
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
Kaneda
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 Wink
NewGuyinTown
Ahhh, I see! That explanation was very helpful...

Kaneda, You're my hero. Very Happy Thank you!

And thank you to everyone that helped.
Kaneda
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 Wink). 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 Smile 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".
NewGuyinTown
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?
AftershockVibe
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).
NewGuyinTown
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.
NewGuyinTown
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.
Kaneda
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.
NewGuyinTown
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
NewGuyinTown
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.
muggle
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
Related topics
What computer languages you know?
Complete Tutorial LINKS for every language
Learn Assembly: the LUCID way
Help In ASSEMBLY Language
Decimal, Binary and Hexadecimal
assembly compiler problem
which one programming lanuge result's the best speed
A little help on C++
Artificial Intelligence
Games on the lession table in Denmark
Minueto OS
ARM microprocessor (programming in ARM)
assembly language problem
New to programming, which language do I choose?
Reply to topic    Frihost Forum Index -> Scripting -> Others

FRIHOST HOME | FAQ | TOS | ABOUT US | CONTACT US | SITE MAP
© 2005-2011 Frihost, forums powered by phpBB.