Why Is This Int $0X10 Bios Int Not Working on Linux

C Inline Asm Int 0x10

Okay after some helpful comments, I may just go with full assembly. Something like

Print:
push %ax
movb $0x0E, %ah # Set interrupt code
movb $0x00, %bh # Set page #
.loop:
lodsb # Load next char
test %al, %al # Check for \0
je .done
int $0x10 # Call interrupt
jmp .loop

.done:
pop %ax
ret

That should be 16-bit real mode compatible and can be assembled with GAS, which, as I understand it, works better than GCC for compiling 16-bit programs.

two int 0x10 command,one in bios, one in processor

In X86 architecture, interrupt handlers are defined in the Interrupt descriptor table (IDT).

In read mode, The IDT consists of 256 four-byte real mode pointers. When system startup, BIOS will initialize this IDT to provide some basic service for real mode programs (for example DOS code / OS boot code). The handlers are all defined in BIOS.

Later, the OS boot code may switch the processor into protected mode and load its new IDT. This time the IDT is an array of 8-byte descriptors stored in memory. From this point, OS start to handle interrupts instead of BIOS.

So there is no contradictory since the first int 0x10 is used to invoke BIOS service in real mode and then its handling is passed to OS to handle exceptions in protected mode.

Interrupt 10h not working

BIOS interrupts are 16-bit code. Your OS has put the CPU in 32-bit protected mode. The hardware will allow a switch back to 16-bit real mode (there are hoops to jump through) but the OS won't allow it. Wouldn't be very "protected" if it did. It is "protected" from US, my friend!

I think what you probably want to look into is "vt100" terminal emulation. By rights, a "robust" program would consult the "termcaps" file to make sure vt100 emulation is available before attempting to use it. My experience is that it's "usually" available on a "desktop Linux" box, so I just ASSume it's there. Worst that can happen (I think) is garbage on the screen if we ASSume wrong.

This example doesn't do exactly what you want. It saves the current cursor position (lord knows where), moves the cursor to a new position, prints a message, and goes back to the original cursor position. You'll need to look up the "home cursor" command ("ESC [h"? lookitup). Just write it to stdout, same as "hello world". You can get colors and stuff, too.

; nasm -f elf32 mygem.asm
; ld -o mygem mygem.o -melf_i386

global _start

section .data
savecursor db 1Bh, '[s'
.len equ $ - savecursor

unsavecursor db 1Bh, '[u'
.len equ $ - unsavecursor

getcursor db 1Bh, '[6n'
.len equ $ - getcursor

setcursor db 1Bh, '[10;20H'
.len equ $ - setcursor

msg db "Hello, new cursor position!"
.len equ $ - msg

section .text
_start:

mov ecx, savecursor
mov edx, savecursor.len
call write_stdout

mov ecx, setcursor
mov edx, setcursor.len
call write_stdout

mov ecx, msg
mov edx, msg.len
call write_stdout

mov ecx, unsavecursor
mov edx, unsavecursor.len
call write_stdout

exit:
mov eax, 1
xor ebx, ebx
int 80h

;------------------------
write_stdout:
push eax
push ebx
mov eax, 4
mov ebx, 1
int 80h
pop ebx
pop eax
ret
;---------------------

Int 10H not working in QEMU

It appears that your linker script was written for an environment where a protected mode kernel would be loaded at 0x00100000. This is common for bootloaders conforming to the multiboot specification. Real mode bootloader like yours will be read from the first 512 bytes of the disk and placed at 0x7c00 in memory. You'll need a linker script that uses an origin point of 0x7c00. You can also use the linker script to output the boot disk signature 0xAA55.

A basic bootloader linker script could look something like this link.ld:

OUTPUT_FORMAT("binary");
ENTRY(start);
SECTIONS
{
. = 0x7C00;
.text : AT(0x7C00) {
*(.text);
}
.data : SUBALIGN(0) {
*(.data);
*(.rodata);
}
.bss : SUBALIGN(4) {
__bss_start = .;
*(COMMON);
*(.bss)
. = ALIGN(4);
__bss_end = .;
}
__bss_sizel = SIZEOF(.bss)>>2;
__bss_sizeb = SIZEOF(.bss);

/* Boot signature */
.sig : AT(0x7DFE) {
SHORT(0xaa55);
}
}

Your boot.s is not being told to generate 16-bit code. You can place the directive .code16 at the top of your assembly file to force it to generate 16-bit instructions. You will also have to amend your GCC command to compile as 16-bit code, and the linker will need the -melf_i386 option.

Your bootloader code should really set the DS register as well as the stack. Since the linker script assumes code is located at memory location 0x07c00 we'll need to set DS to zero. In segmented model a physical address = (segment<<4)+offset . DS set to 0, and an origin (base offset) of 0x7c00 would map to (0<<4)+0x7c00 = physical address 0x07c00 which is where our bootloader is loaded by the BIOS.

I wrote an answer with some general bootloader tip that may be of some value.

Cleaned up boot.s could look like:

#
# boot.s
#

.code16
.section .text
.globl start
start:
//setup stack
mov $0x7c0, %ax
mov %ax, %ss
mov $512, %sp

xor %ax, %ax # AX = 0
mov %ax, %ds # Set DS = 0 since origin point is 0x7c00

//setup video
xor %ax, %ax # Zero 16-bit AX register (includes AL and AH)
//mov $0x0, %ax # Works but is not preferred for zeroing a reg
int $0x10

//print a character say 'm'
mov $'m', %al
mov $0x0E, %ah
int $0x10
1:
jmp 1b

To assemble, link and generate a disk image you could use:

gcc -c boot.s -m16
ld -melf_i386 -T link.ld boot.o -o b.bin -nostdlib --nmagic
dd if=b.bin of=HD.img conv=notrunc

You could then run it using the image as a floppy:

qemu-system-i386 -fda HD.img

Or as a hard drive image using:

qemu-system-i386 -hda HD.img


Related Topics



Leave a reply



Submit