Author Topic: NB Question: How to write one byte into al ?  (Read 3763 times)

nobody

  • Guest
NB Question: How to write one byte into al ?
« on: February 08, 2005, 02:37:33 PM »
Hello together

I just wanted to build a small "Hello, world" program which is written directly into the bootsector of a floppy and prints with a BIOS interrupt (0x0e) a string onto the console. You find the program at the end and can be compiled using "nasm -f -o boot boot.asm" and copied to disk with the command "dd if=boot of=/dev/fd0 bs=1 count=512" (on linux). Evertyhing seems to be ok except with line 15. Some garbage is printed. If I replace 15 with stars are printed as expected. So what is wrong with line 15.

Thank's for any help

Pedro


 1 entry:
 2
 3 msg     db      'Hello, World!'
 4 len:    equ $-msg
 5
 6 mov ecx, msg            ; ecx contains startadress of string
 7 mov edx, msg
 8 add edx, $len           ; edx contains endadress of string
 9 mov ah, 0x0e            ; function 0eh: writting in TTY-mode
10 xor bh, bh              ; which screen (0)
11 begin_str:
12         cmp ecx, edx    ; end of string
13         je end_str      ; if ecx and edx contain the same adress jump
14 ; the next line makes problems
15         mov al, [ecx]   ; get one byte of the string
16 ; the next line would work fine
17 ;       mov al, '*'     ; get one byte of the string
18         int 0x10        ; BIOS interrupt
19         inc ecx         ; next byte of string
20         jmp begin_str
21 end_str:
22
23 loop_for_ever: jmp loop_for_ever
24
25 ; now fill the bootsector and write the last two bytes
26 size    equ     $ - entry
27 %if size+2 > 512
28   %error "code is too large for boot sector"
29 %endif
30 times   (512 - size - 2) db 0
31 db      0x55, 0xAA              ;2  byte boot signature

nobody

  • Guest
Re: NB Question: How to write one byte into al ?
« Reply #1 on: February 08, 2005, 04:24:02 PM »
First of all, 32 bit registers don't work in 16-bit real mode.

And you might want to check this out:

http://www.osdever.net/tutorials/brunmar/tutorial_01.php?the_id=8

nasm64developer

  • Guest
Re: NB Question: How to write one byte into al ?
« Reply #2 on: February 08, 2005, 07:08:10 PM »
> First of all, 32 bit registers don't work
> in 16-bit real mode.

Wrong. The use of operand size prefixes is
perfectly legal in 16-bit real mode.

The OP's problem is that the MOV Al,[ECX]
implies DS: -- but he/she forgot to load a
suitable value into DS.

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2379
  • Country: us
Re: NB Question: How to write one byte into al ?
« Reply #3 on: February 08, 2005, 09:26:24 PM »
db "Hello, Pedro :)"

...
> Evertyhing seems to be ok except with
> line 15. Some garbage is printed. If I replace 15 with stars are printed as
> expected. So what is wrong with line 15.

As nasm64developer says, the problem isn't with line 15, but with line 2... or maybe even with "line 0"...

You don't give any "org", so Nasm will default to "org 0". This isn't a problem, although a lot of bootsectors are written to assemble at "org 7C00h". The difference is the value we need to put in ds. We know the bios will load us at 0x7C000 physical, and jump to that address - either ip = 7Cooh, cs = 0, or rarely (perhaps mythically) ip = 0, cs = 7C0h. We know that dl will hold the number of the drive we booted from. And that's *all* we can count on! Specifically, we don't know what's going to be in ds (or any other segreg). Bochs, of course, starts off with known, predictable values, but if you want your bootsector to work on any machine, not just Bochs, assume nothing!

Let's state the origin specifically, just for clarity:

org 0

>  1 entry:
>  2

You're trying to execute data here. A bootsector is expected to start with "jmp short overdata"/"nop", or "jmp near overdata". Some biosen check for this! (most don't, IME)

jmp short overdata
nop

>  3 msg     db      'Hello, World!'
>  4 len:    equ $-msg
>  5
(If you want your bootsector to be compatible with FAT12, you want some specific data here - the Bios Parameter Block - but it isn't strictly necessary.)

overdata:

mov ax, 7C0h
mov ds, ax

; really ought to set up a sane stack here, too!
; since we're not using the stack - except for the int(!!!),
; we can let it slide - the bios "probably" left us with
; a small, but usable stack

>  6 mov ecx, msg            ; ecx contains startadress of string

The 32-bit registers *will* work here, but will emit an instruction size override byte (66h) or an address size override byte (67h) every time you do it. Not something I would use in a bootsector! Using 16-bit registers will generate smaller code. You'll have to select your registers differently - "mov al, [cx]" is *not* valid with 16-bit registers, but "mov al, [bx]" would be (but NG for int 10h/0Eh!), or "mov al, [di]" or "mov al, [si]". The "lodsb" instruction will do "mov al, [si]"/"inc si" in one short (but not especially fast) instruction. (really should clear the direction flag - "cld" - before using "stosb" (or other "string" instructions, but it would be a rude bios indeed that left it "down").

>  7 mov edx, msg
>  8 add edx, $len           ; edx contains endadress of string
>  9 mov ah, 0x0e            ; function 0eh: writting in TTY-mode
> 10 xor bh, bh              ; which screen (0)

My machine doesn't care what's in bl, *or* bh - writes to the "current" page, regardless. Some video biosen write to the page in bh, so it's good to clear it. More rarely, you'll encounter a video bios that uses bl for the color/attribute, so for maximum compatibility, you might do "mov bx, 7"...

> 11 begin_str:
> 12         cmp ecx, edx    ; end of string
> 13         je end_str      ; if ecx and edx contain the same adress jump
> 14 ; the next line makes problems
> 15         mov al, [ecx]   ; get one byte of the string
> 16 ; the next line would work fine
> 17 ;       mov al, '*'     ; get one byte of the string
> 18         int 0x10        ; BIOS interrupt
> 19         inc ecx         ; next byte of string
> 20         jmp begin_str
> 21 end_str:
> 22
> 23 loop_for_ever: jmp loop_for_ever
> 24
> 25 ; now fill the bootsector and write the last two bytes
> 26 size    equ     $ - entry
> 27 %if size+2 > 512
> 28   %error "code is too large for boot sector"
> 29 %endif

You should get an error from "times" anyway, if this condition exists. Doesn't hurt to do it - doesn't emit any code...

> 30 times   (512 - size - 2) db 0
> 31 db      0x55, 0xAA              ;2  byte boot signature

Though there are other things I'd change, just jumping over your data, and loading ds with an appropriate value, should get your message on screen.

Happy Bootin',
Frank