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:
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
Is There a Subversion Web Client That I Can Use
How to Append to an Indirect Parameter Expansion of an Array in Bash
Access Denied to Android.Git.Kernel.Org
Bash: How to Pass in Arguments to an Alias: Cannot Use a Function - Syntax of Bash Conditionals
Shell Script to Set Environment Variables Permanently
How to Autocomplete a Bash Commandline with File Paths
Escaping Quotes in Bash (Embedded Awk)
Permission Issues, Not Able to Run Script as Root
How to Concatenate Files with the Same Prefix (And Many Prefixes)
Bluez Blotoothctl Scan VS Hcitool Scan
Objdump and Resolving Linkage of Local Function Calls
How to Cross Compile R Packages for MACos from a Linux Environment
Automatic Syntax/Headers in Vim for C++ Files
This Script Wont Sort Correctly by Age
Why Does Unitywebrequest Return Unkown Error When I Do a Get Request on Linux