0

I am currently playing around with x86 assembler since I wanted to refresh my skills for low level programming :-). For testing purposes I tried to write a function that just prints out a given string. The printing function itself works fine. In a further step I wanted to load a second assembler program from disk jump to it and just print out a text. Loading from disk at jump to the address works fine.

Here is the given scenario:

[... loading from disk etc ... program is loaded to 0x7e0:0001]

jmp 0x7e0:0001

[... context of other asm ...]

jmp Start
;data fields
msg db "Hello World!",0

Start:
     xor si, si     ; clear SI register
     mov si, msg    ; load message to SI register

     call Print

     cli
     hlt            ; halt the system

Print:
     .PrintLoop:
         lodsb                ; load byte from SI register
         or al, al            ; check if 0 byte
         jz short .PrintDone  ; if so - stop
         mov ah, 0Ah          ; function - print text to cursor
         int 0x10             ; BIOS interrupt

         jmp .PrintLoop       ; continue with next char
     .PrintDone:
         ret

All of this program is working fine. The only problem that I face is, that no text is printed. During debugging I saw that the print function immediately jumps to the .PrintDone label since there seems to be no data in SI and therefore lodsb loads nothing to al (besides null byte).

I was thinking about the fact, that there might be something wrong with the data segment.

Thus, I added the following line at the beginning of the Start-Routine:

xor ax, ax    ; clear ax register
mov ax, cs    
mov ds, ax    ; set data segment pointer

But this changed nothing regarding the programs behaviour. Nothing is printed.

Inspecting the CPU registers when execution reaches halt instruction, gives the following:

EAX=00000a00 EBX=00000000 ECX=00000002 EDX=00000000
ESI=00000026 EDI=00000000 EBP=00000000 ESP=0000ffff
EIP=00000036 EFL=00000046 [---Z-P-] CPL=0 II=0 A20=1 SMM=0 HLT=1
ES =07e0 00007e00 0000ffff 00009300
CS =07e0 00007e00 0000ffff 00009b00
SS =9000 00090000 0000ffff 00009300
DS =07e0 00007e00 0000ffff 00009300

Do you have any clue what's going on here?

[EDIT - PROBLEM RESOLVED]

Replacing:

mov ah, 0Ah -> mov ah, 0xE

fixes the problem!

Best Sebastian

rkhb
  • 14,159
  • 7
  • 32
  • 60
Sebastian B.
  • 287
  • 4
  • 19
  • Does it still go straight to the `.PrintDone` with your addition to set `ds`, or does it just not print anything? (I think you want `ah` just before the interrupt, not `ax`.) – ughoavgfhw Jan 29 '12 at 04:32
  • Hey, classic typo on ax - fixed to ah again. Now the program is printing something but just one weird char. When inspecting the registers when the execution reached system halt they have the following entries: ES =07e0 00007e00 0000ffff 00009300 CS =07e0 00007e00 0000ffff 00009b00 SS =9000 00090000 0000ffff 00009300 DS =07e0 00007e00 0000ffff 00009300 – Sebastian B. Jan 29 '12 at 04:48
  • I don't think that interrupt moves the cursor, which means if more than one character is printed, you will only see the last. Also, check to make sure `si` is pointing to the right spot at the end. If I counted right, there must be 24 bytes of data at the "data fields" comment for it to be right. The code looks fine. – ughoavgfhw Jan 29 '12 at 05:43
  • Hi, issue is fixed by replacing the interrupt vector function from 0Ah to 0xE... Everything works as expected now! Thanks for your help! – Sebastian B. Jan 29 '12 at 05:52

1 Answers1

2

There are a few problems.

First, you don't properly set the registers for function 0Ah. You have to set bh to the page number (0), and cx to the repeat count (1).

Second, this BIOS function doesn't advance the cursor position and all characters get printed into the same location on the screen, overwriting each other, which should lead to only '!' visible since it's the last character.

I advise to use function 0Eh instead.

Third, you don't initialize the direction flag (flags.df) which lodsb relies upon. You should use cld to reset df.

Fourth, I don't see all the code, but you should use the proper org directive to generate correct offsets for instructions and data.

Also, NMIs and SMIs will cause hlt completion and the code that follows will execute (and what follows is your Print). You want to execute hlt in a loop.

With these fixes we arrive at:

bits 16
org 1

jmp Start
;data fields
msg db "Hello World!",0

Start:
     mov ax, cs    
     mov ds, ax    ; set data segment pointer
     cld

     mov si, msg    ; load message to SI register

     call Print

     cli
.halt:
     hlt            ; halt the system
     jmp .halt

Print:
     .PrintLoop:
         lodsb                ; load byte from SI register
         or al, al            ; check if 0 byte
         jz short .PrintDone  ; if so - stop
;         mov ah, 0Ah          ; function - print text to cursor
;         xor bh, bh
;         mov cx, 1
         mov ah, 0Eh          ; function - print text tty

         int 0x10             ; BIOS interrupt

         jmp .PrintLoop       ; continue with next char
     .PrintDone:
         ret

The binary which you get by compiling the above with nasm blah.asm -f bin -o blah.bin should then be loaded at 0x7e0:0001 and jumped to with jmp 0x7e0:0001.

Alexey Frunze
  • 61,140
  • 12
  • 83
  • 180
  • Thanks for the post! But shouldn't it be 'org 0'? Since the program is loaded to the base address 0x7e0 with offset 0001 org 1 would furthermore cut of one byte right? The program may than print out 'ello World!' – Sebastian B. Jan 29 '12 at 06:31
  • `org n` sets the offset of the very first instruction or data item to `n`. If you `org 0`, load at 0, but jump to 0x7e0:1, you jump into the middle of `jmp Start` instead of the beginning. If you `org 0`, load at 0, and jump to 0x7e0:0, everything's OK. Likewise everything's OK if you `org 1`, load at 1, and jump to 0x7e0:1. – Alexey Frunze Jan 29 '12 at 06:37
  • `test al,al` is [more efficient](https://stackoverflow.com/questions/33721204/test-whether-a-register-is-zero-with-cmp-reg-0-vs-or-reg-reg/33724806#33724806) than `or al,al` - that's a leftover idiom from 8080. – Peter Cordes Nov 30 '22 at 21:29