The New Line Characted in the String Constant Isn't Being Recognized by Nasm

The new line characted in the string constant isn't being recognized by nasm

You need to use 'backquotes' around the string to support escape sequences:

str1: db `abcd\n`
str2: db `efgh\n`

Reference: http://www.nasm.us/doc/nasmdoc3.html

3.4.2 Character Strings:

"Strings enclosed in backquotes support C-style -escapes for special
characters."

Why the following assembly code doesn't print out the new line (0xa) character?

write_string expects the address of a string, but new_line is just the character value 0xa. Trying to interpret this as the address of a string will give unpredictable results. If you want a string that just contains a single 0xa that you can print then you'd need to do something like this:

    section .data

message: db 'Displaying 9 stars', new_line ; a message
.length: equ current_address - message ; length of the message
asterisks: times 9 db '*' ; a string of 9 asterisks
newline: db new_line ; <<< a single LF

global start

section .text

start: ; linker entry point

write_string message, message.length ; print a message to standard output
write_string asterisks, 9 ; print 9 asterisks
write_string newline, 1 ; <<< print LF

Iterate through string in memory in Assembly

Many, many years ago, someone put it this way:

Do you want the box, or what is in the [box]?

I've also tried the following:

mov al, [bx] ; Move the dereferenced address to `al` (so `al` has `H`) 
add al, 1 ; Increment `al`, but of course I'm getting the ASCII value of `H` + 1

The CPU is doing exactly what you told it to do!

mov al, [bx]

Moves the value pointed to by bx into al (in your case, H)

add al, 1

adds 1 to H.

add bx, 1
mov al, [bx]

Now, al will contain E

Or you could do:

mov al, [bx + 1]

to get E

In your other code, bx is a word sized register (16 bits) and cl is a byte sized register (8 bits) you truncated the address hence the invalid address (what do you expect to happed when you try to put 16 bits into an 8 bit register?)

Here is an example:

 HELLO_MSG db 'Hello, World!', 0
HELLO_LEN equ $ - HELLO_MSG
...
...
...
mov si, HELLO_MSG
xor bx, bx
Next:
mov al, byte [si + bx]
mov ah, 0x0e
int 0x10

mov al, 10
mov ah, 0x0e
int 0x10

inc bx
cmp bx, HELLO_LEN
jne Next

Output:

Sample Image

How do i write a function that prints a null terminated string in NASM 16 bit real mode?

print_string:
mov ah, 0x0e
int 0x10
ret

Your print_string routine uses the BIOS.Teletype function 0Eh. This function will display the single character held in the AL register. Since this BIOS function additionally expects you to supply the desired DisplayPage in BH and the desired GraphicsColor in BL (only for when the display is in a graphics video mode), it's perhaps not the best idea to use the BX register as an argument to this print_string routine.

Your new routine will have to loop over the string and use the single character output function for every character contained in the string. Because your strings are zero-terminated, you stop looping as soon as you encounter that zero byte.

[org 7C00h]

cld ; This makes sure that below LODSB works fine
mov si, HELLO_MSG
call print_string
mov si, GOODBYE_MSG
call print_string

jmp $

print_string:
push bx ; Preserve BX if you need to!
mov bx, 0007h ; DisplayPage BH=0, GraphicsColor BL=7 (White)
jmp .fetch
.print:
mov ah, 0Eh ; BIOS.Teletype
int 10h
.fetch:
lodsb ; Reads 1 character and also advances the pointer
test al, al ; Test if this is the terminating zero
jnz .print ; It's not, so go print the character
pop bx ; Restore BX
ret

What does this asm expression `64-$+buffer` means?

Yes, you are right, the $ symbol is basically the current target address while assembling. Let's look at some example values:

buffer: db 'hello, world' 
times 64-$+buffer db ' '

We'll start by setting buffer to some arbitrary address like 27. The 12 characters for the message run then from 27 thru 38 inclusive so $ will be 39 following that.

The times count will then be (64 - 39 + 27) or 52, and that plus the 12 characters total 64.

So yes, assuming your string is less than 64 characters, it will be padded out with enough spaces to make 64 in total (if it's longer than 64, you'll probably get an assembler error because you're supplying a negative count).

Hello World bootloader not working

You say "boot straight into windows" so I assume you are using a physical PC. Future note to make: Always use an emulator for development! It's just easier. I like Bochs for OSDeving cause it has nice debugging features. Now, onto the possible solution.

There are a lot of buggy BIOSes that break the informal specifications of the IBM PC for the 0x7C00 load address.

This can give a lot of problems with memory addresses and such whenever you are assembling. So make the beginning look like this:

[BITS 16] ;tell the assembler that its a 16 bit code
[ORG 0x7C00] ;this tells the assembler where the code will be loaded at when it runs on your machine. It uses this to compute the absolute addresses of labels and such.

jmp word 0:flush ;#FAR jump so that you set CS to 0. (the first argument is what segment to jump to. The argument(after the `:`) is what offset to jump to)
;# Without the far jmp, CS could be `0x7C0` or something similar, which will means that where the assembler thinks the code is loaded and where your computer loaded the code is different. Which in turn messes up the absolute addresses of labels.
flush: ;#We go to here, but we do it ABSOLUTE. So with this, we can reset the segment and offset of where our code is loaded.
mov BP,0 ;#use BP as a temp register
mov DS,BP ;#can not assign segment registers a literal number. You have to assign to a register first.
mov ES,BP ;#do the same here too
;#without setting DS and ES, they could have been loaded with the old 0x7C0, which would mess up absolute address calculations for data.

See, some load at 0x07C0:0000 and most load(and its considered proper to) at 0x0000:7C00. It is the same flat address, but the different segment settings can really screw up absolute memory addresses. So let's remove the "magic" of the assembler and see what it looks like (note I don't guarantee addresses to be completely correct with this. I don't know the size of all opcodes)

jmp word 0:0x7C04 ;# 0x7C04 is the address of the `flush` label 
...

So, we jump to an absolute address.

Now then. What happens when we don't do this?

take this program for example:

mov ax,[mydata]
hlt

mydata: dw 500 ;#just some data

This disassembles to something like

mov ax,[0x7C06] 

Oh, well it uses absolute addressing, so how could that go wrong? Well, what if DS is actually 0x7C0 ? then instead of getting the assembler expected 0:0x7C06 it will get 0x7C0:0x7C06 which are not the same flat address.

I hope this helps you to understand. It's really a complicated topic though and takes a while of low level programming to fully understand.



Related Topics



Leave a reply



Submit