5

I have to do a simple calculator in assembly using EMU8086, but every time I try to launch it EMU8086 gives this error:

INT 21h, AH=09h - 
address: 170B5
byte 24h not found after 2000 bytes.
; correct example of INT 21h/9h:
mov dx, offset msg
mov ah, 9
int 21h
ret
msg db "Hello$"

I checked the other stuff, but there were no mistakes:

data segment
    choice db ?
    snum1 db 4 dup(?)
    snum2 db 4 dup(?)
    sres db 4 dup(?)
    num1 db ?
    num2 db ?
    res db ?
    ;;menu1 db "Chose a function to procced", 10, 13, "Add [+]", 10, 13, "Sub [-]", 10, 13
    ;;menu2 db "Mul [*]", 10, 13, "Div [/]", 10, 13, "Mod [%]", 10, 13, "Pow [^]", 10, 13, "Exit [x]$"
    messStr db "Enter Your Choice:",10,13,"",10,13,"Add --> +",10,13,"Sub --> -",10,13,"Mul --> *",10,13,"Div --> /",10,13,"Mod --> %",10,13,"Pow --> ^",10,13,"Exit --> X",10,13,"$"
    msg1 db "Enter first number$"
    msg2 db "Enter second number$"
    msg3 db "Press any key to procced$"
    msg4 db "The result is $"

ends

stack segment
    dw   128  dup(0)
ends

code segment
assume cs:code, ds:data, ss:stack 

newline proc ;; new line
    push ax
    push dx
    mov ah, 2
    mov DL, 10
    int 21h
    mov ah, 2
    mov DL, 13
    int 21h
    pop dx
    pop ax
    ret
endp

printstr proc ;; print string
    push BP
    mov BP, SP
    push dx
    push ax
    mov dx, [BP+4]
    mov ah, 9
    int 21h
    pop ax
    pop dx
    pop BP
    ret 2
endp

inputstr proc ;; collect input
    push BP
    mov BP, SP
    push bx
    push ax
    mov bx, [BP+4]
k1: 
    mov ah, 1
    int 21h
    cmp al, 13
    je sofk
    mov [bx], al
    inc bx
    jmp k1
sofk:
    mov byte ptr [bx], '$'
    pop ax
    pop bx
    pop BP
    ret 2
endp

getNums proc ;; get the numbers
    call newline
    push offset msg1
    call printstr
    call newline    
    push offset snum1
    call inputstr 

    call newline
    push offset msg2
    call printstr
    call newline
    push offset snum2
    call inputstr
    ret
endp

start:
    mov ax, data
    mov ds, ax
    mov ax, stack
    mov ss, ax

    ;; print the main menu
    call newline
    push offset msg4
    call printstr 
    ;; collect the input
    call newline
    mov bx, offset choice
    mov ah, 1
    int 21h
    mov [bx], al
    ;; check it
    mov al, choice
    cmp al, '+'
    jne cexit
    call getNums

    jmp cont
cexit:    
    cmp al, 'x'
    je cend

cont:
   ;; pause before going to the main menu
   call newline
   push offset msg3
   call printstr

   mov bx, offset choice
   mov ah, 1
   int 21h 

   call newline
   call newline
   call newline

   jmp start

cend:   

mov ax, 4c00h
int 21h  

ends

end start

I cut most of the code segment because it wasn't important here.

After experimenting with the code I found that the problem was related to the lengths of the messages in the data segment. menu1 & menu2 were too long and any message after them can't be printed (msg1 & msg2 are printed, but nothing after them). I checked if I should merge menu1 & menu2, but it didn't help out. Please help me find out what is wrong with it.

Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • The DOS function uses ds:dx. Looks like your string is in the code segment, not the data segment. – Hans Passant Apr 30 '16 at 16:57
  • @HansPassant All the strings are in the code segment. Just checked it again. –  Apr 30 '16 at 16:59
  • Hard to guess what you are talking about, your data segment clearly has strings like "Enter first number$". Move "Hello$" there as well. – Hans Passant Apr 30 '16 at 17:07
  • @HansPassant it's useless, it just adds more strings and I won't be able to work with the last orderd ones. –  Apr 30 '16 at 17:08
  • @HansPassant But you gave me an idia.. placing the long ones in the end of the segment. I'll try it now. –  Apr 30 '16 at 17:10
  • 1
    @HansPassant : None of his strings and data are in the code segment. The pop up **error message** in EMU8086 actually provides an example of proper usage. That example has `msg db "Hello$"` but that isn't something that appears in the OPs code. – Michael Petch Apr 30 '16 at 23:30

3 Answers3

5

The error message means you use int 21h / AH=09h on a string that didn't end with a $ (ASCII 24h). The system-call handler checked 2000 bytes without finding one.

Often, that means your code or data is buggy, e.g. in a fixed string you forgot a $ at the end, or if copying bytes into a buffer then you maybe overwrote or never stored a '$' in the first place.

But in this case, it appears that EMU8086 has a bug assembling push offset msg4. (In a way that truncates the 00B5h 16-bit address to 8-bit, and sign-extends back to 16, creating a wrong pointer that points past where any $ characters are in your data.)


Based on the error message below I know you are using EMU8086 as your development environment.

INT 21h, AH=09h - address: 170B5 byte 24h not found after 2000 bytes. ; correct example of INT 21h/9h: mov dx, offset msg mov ah, 9 int 21h ret msg db "Hello$"

I'm no expert on EMU8086 by any stretch of the imagination. I do know why your offsets don't work. I can't tell you if there is a proper way to resolve this, or if it's an EMU8086 bug. Someone with a better background on this emulator would know.

You have created a data segment with some variables. It seems okay to me (but I may be missing something). I decided to load up EMU8086 to actually try this code. It assembled without error. Using the debugger I single stepped to the push offset msg1 line near the beginning of the program. I knew right away from the instruction encoding what was going on. This is the decoded instruction I saw:

Instruction decoding

It shows the instruction was encoded as push 0b5h where 0b5h is the offset. The trouble is that it is encoded as a push imm8 . The two highlighted bytes on the left hand pane show it was encoded with these bytes:

6A B5 

If you review an instruction set reference you'll find the encodings for PUSH instruction encoded with 6A is listed as:

Opcode*   Instruction Op/En   64-Bit Mode Compat/Leg Mode Description
6A ib     PUSH imm8   I       Valid       Valid           Push imm8.

You may say that B5 fits within a byte (imm8) so what is the problem? The smallest value that can be pushed onto the stack with push in 16-bit mode is a 16-bit word. Since a byte is smaller than a word, the processor takes the byte and sign extends it to make a 16-bit value. The instruction set reference actually says this:

If the source operand is an immediate of size less than the operand size, a sign-extended value is pushed on the stack

B5 is binary 10110101 . The sign bit is the left most bit. Since it is 1 the upper 8 bits placed onto the stack will be 11111111b (FF). If the sign bit is 0 then then 00000000b is placed in the upper 8 bits. The emulator didn't place 00B5 onto the stack, it placed FFB5. That is incorrect! This can be confirmed if I step through the push 0b5h instruction and review the stack. This is what I saw:

Stack

Observe that the value placed on the stack is FFB5. I could not find an appropriate syntax (even using the word modifier) to force EMU8086 to encode this as push imm16. A push imm16 would be able to encode the entire word as push 00b5 which would work.

Two things you can do. You can place 256 bytes of dummy data in your data segment like this:

data segment
db 256 dup(?)
choice db ?
... rest of data

Why does this work? Every variable defined after the dummy data will be an offset that can't be represented in a single byte. Because of this EMU8086 is forced to encode push offset msg1 as a word push.

The cleaner solution is to use the LEA instruction. This is the load effective address instruction. It takes a memory operand and computes the address (in this case the offset relative to the data segment). You can replace all your code that uses offset with something like:

lea ax, [msg1]
push ax

AX can be any of the general purpose 16-bit registers. Once in a register, push the 16-bit register onto the stack.

Someone may have a better solution for this, or know a way to resolve this. If so please feel free to comment.


Given the information above, you may ask why did it seem to work when you moved the data around? The reason is that the way you reorganized all the strings (placing the long one last) caused all the variables to start with offsets that were less than < 128. Because of this the PUSH of an 8-bit immediate offset sign extended a 0 in the top bits when placed on the stack. The offsets would be correct. Once the offsets are >= 128 (and < 256) the sign bit is 1 and the value placed on the stack sign will have an upper 8 bits of 1 rather than 0.


There are other bugs in your program, I'm concentrating on the issue directly related to the error you are receiving.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Michael Petch
  • 46,082
  • 8
  • 107
  • 198
  • So in a nutshell we got just another user that ran into trouble because *emu8086* doesn't stick to the true 8086 instruction set which would not have allowed the pushing of an immediate. – Sep Roland May 01 '16 at 14:18
  • 2
    @SepRoland I believe that EMU**8086** is a bit of a misnomer. It is a quirky hybrid that takes **some** elements from the 80186(aka 8086-2). It supports shifting with an immediate, push of an immediate, `pusha`, `popa`. It doesn't support `imul` with an immediate, and lacks support for other 80186 operations like `enter`, `leave`, `bound`, `ins`, `outs` . I guess that could be interpreted as a bug - yes. – Michael Petch May 01 '16 at 17:41
0

I reviewed your code and concentrated on the following sequence of instructions:

mov bx, offset choice      ; here you set BX to the address of 'choice'
mov ah, 1
int 21h                    ; here you 'READ CHARACTER FROM STANDARD INPUT, WITH ECHO' 
mov [bx], al               ; because INT 21h does preserve BX, you are writing back the result of the interrupt call (AL) back to the memory location at BX, which is named 'choice'
;; check it
mov al, choice             ; HERE you are moving a BYTE variable named 'choice' to AL, overwriting the result of the last INT 21h call
cmp al, '+'                ; ... and compare this variable to the ASCII value of '+'
jne cexit                  ; if this variable is unequal to '+' you jump to 'cexit'
call getNums               ; otherwise you try to get another number from the input/STANDARD CONSOLE

So your sequence

mov bx, offset choice      ; here you set BX to the address of 'choice'
...
mov [bx], al               ; because INT 21h does preserve BX, you ...
...
mov al, choice

essentially means, that you are setting BX to the address of 'choice', then setting 'choice'([BX]) to AL and copying it back to AL.

This is redundant.

After that, you compare that char to '+' and...

  • if that char equals to '+', you get the next char with call getNums and then continue with cont:.
  • if that char does not equal to '+', you compare it to 'x', the exit-char. If it's not 'x', you fall through to cont:

No error here.

So your problem with menu1 and menu2 may stem from some escape characters included in your strings like %,/,\. For example, % is a MACRO character in some assemblers which may create problems.

zx485
  • 28,498
  • 28
  • 50
  • 59
  • Can I use thouse simbols without activating their functions ? –  Apr 30 '16 at 16:21
  • But even if it's the problem.. why msg1 & msg2 are printed perfectly well ? –  Apr 30 '16 at 16:23
  • 1
    Using their hex ASCII values is safe for sure. Their escape sequences may vary depending on the assembler. For example, replacing `%` with 25h is safe. – zx485 Apr 30 '16 at 16:24
  • Maybe because they don't contain any escape characters. – zx485 Apr 30 '16 at 16:25
  • @Sergei: I extended my answer, but this error seems obscure to me. Without further information, I'm not sure what to suggest. – zx485 Apr 30 '16 at 16:31
  • I'll keep experimenting with the code and if I'll find something I'll let you know, Thanks for the help so far. –  Apr 30 '16 at 16:36
  • I kept experementing until I found out that if I make the string shorter.. it works fine. ( but a way shorter.. leaving only a half of it. ) Trying to seperate it to parts has no effect on it.. now I'm almost sure it's about the lenght. ( adding a string with a new name gives the same effect. ) –  Apr 30 '16 at 16:57
-2

simple solution is that your strings should always end in '$' change DUP(?) to DUP('$') and all other strings end with ,'$'