Reputation: 794
I am trying to implement a function which tries to print string in 16-bit mode seen on QEmu:
kernel.c file:
void main()
{
char* str = "Hello World!";
printString(str);
}
The printString function is defined in another file printString.c:
int printString(char* string)
{
int i = 0;
while (*(string + i) != '\0')
{
char al = *(string + i);
char ah = 0xe;
int ax = ah * 256 + al;
interrupt(0x10,ax,0,0,0);
i++;
}
return i;
}
The interrupt function calls BIOS interrupt, as given in the first argument of the function with other arguments specifying respectively the contents of ax, bx, cx and dx registers. Here is the code
.global _interrupt
_interrupt:
push bp
mov bp, sp
push si
push ds
mov ax, #0x100
mov ds, ax
mov ax, [bp + 0x4]
mov si, #intr
mov [si + 1], al
pop ds
mov ax, [bp + 0x6]
mov bx, [bp + 0x8]
mov cx, [bp + 0xa]
mov dx, [bp + 0xc]
intr: int #0x0
pop si
pop bp
ret
I compile the .c files using the command:
bcc -ansi -c -o <name.o> <name.c>
And link them using:
ld86 -o kernel -d kernel.o interrupt.o printString.o
Instead of printing "Hello World!", the program prints "S" on the screen. I loaded the kernel.c file at the address 0x1000. I saw the dis-assembly of the code:
0x1000: push %bp
0x1001: mov %sp,%bp
0x1003: push %di
0x1004: push %si
0x1005: dec %sp
0x1006: dec %sp
0x1007: mov $0xc8,%bx
0x100a: mov %bx,-0x6(%bp)
0x100d: pushw -0x6(%bp)
0x1010: call 0x1058
For the pointer to be passed to the printString function (in kernel.c file) the argument passed is 0xc8 which contains 0xf000ff53. Thus 53, which is the ASCII code for S gets printed on the screen.
How should I pass the string to the printString function and why doesn't the above code works?
Please tell me if I need to give more explanation.
Upvotes: 2
Views: 2043
Reputation: 7228
kernel.c
with inline assembly.0x1000
and building your code so that it thinks it starts at address 0x0
. Thus for data access to work, you need to setup the DS data segment register to add 0x1000
to memory addresses in data access instructions.
0xc8
of the kernel image, you need to access physical memory address 0x10c8
. By setting DS to 0x100
, an access to data memory at address 0xc8
is translated into an access to physical address $ds*0x10 + 0xc8 == 0x10c8
, i.e. the address we want.*(string + i)
expression in printString()
thus stepping over the assembler level instructions of the printString()
loop in GDB should also help. You were focusing on the wrong code by disassembling main()
because you didn't understand x86 segmentation.0x1000
with Qemu. In my setup below, I'm loading the code via GDB and have a small boot sector that just jumps to that address.void main()
{
#asm
mov ax, #0x100
mov ds, ax
#endasm
char* str = "Hello World!";
printString(str);
}
int printString(char* string)
{
int i = 0;
while (*(string + i) != '\0')
{
char al = *(string + i);
char ah = 0xe;
int ax = ah * 256 + al;
interrupt(0x10,ax,0,0,0);
i++;
}
return i;
}
.global _interrupt
_interrupt:
push bp
mov bp, sp
push si
mov ax, [bp + 0x4]
mov si, #intr
mov [si + 1], al
mov ax, [bp + 0x6]
mov bx, [bp + 0x8]
mov cx, [bp + 0xa]
mov dx, [bp + 0xc]
intr: int #0x0
pop si
pop bp
ret
.global _main
_main:
mov ax, #0x1000
jmp ax
#!/usr/bin/env python3
# Create an x86 boot sector
# Pad file to 512 bytes, insert 0x55, 0xaa at end of file
import sys
import os
def program_name():
return os.path.basename(sys.argv[0])
def print_usage_exit():
sys.stderr.write('usage: %s IN_FILENAME OUT_FILENAME\n' % (program_name(),))
sys.exit(2)
def main(args):
try:
(in_filename, out_filename) = args
except ValueError:
print_usage_exit()
buf = bytearray(512)
f = open(in_filename, 'rb')
f.readinto(buf)
buf[510] = 0x55
buf[511] = 0xaa
fout = open(out_filename, 'wb')
fout.write(buf)
fout.close()
if __name__ == '__main__':
main(sys.argv[1:])
set confirm 0
set pagination 0
set architecture i8086
target remote localhost:1234
set disassemble-next-line 1
monitor system_reset
delete
restore kernel binary 0x1000
continue
DERVED_FILES := kernel kernel.o interrupt.o printString.o boot boot.o bootsect
.PHONY: all
all: boot kernel
bootsect: boot
./bootsector-create $< $@
boot: boot.o
ld86 -o $@ -s -d $+
kernel: kernel.o interrupt.o printString.o
ld86 -o $@ -s -d $+
%.o: %.c
bcc -ansi -c -o $@ $<
%.o: %.asm
as86 -o $@ $<
.PHONY: clean
clean:
rm -f $(DERVED_FILES)
.PHONY: emulator
emulator: bootsect
qemu-system-x86_64 -s -S bootsect
.PHONY: gdb
gdb:
gdb -q -x kernel.gdb
$ make emulator
(In a separate terminal)
$ make gdb
Upvotes: 4