Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

INT 10h/ah=13h doesn't print strings when part of second stage bootloader

I used all the memory in the first sector, now I want to store a new variable string in the second sector (second stage) and print it. For instance:

hello db 'Hello World'

The new string should be in another sector (because there is no more memory in the first sector). I did this with INT 13h,ah=2 to read the second disk sector to address 900h:0000. I stored the variable hello in that sector as well as the code to print. It fails to print my string when I use INT 10h/ah=13h in code like this:

mov ax, 7c0h
mov es, ax

mov bp, hello 
mov ah,13h          ; function 13 - write string
mov al,01h          ; attrib in bl, move cursor
mov bl,0bh          ; attribute - magenta
mov cx,30           ; length of string
mov dh,1            ; row to put string
mov dl,4            ; column to put string
int 10h             ; call BIOS service

When the variable is in the first sector it prints well, but when I store it in the second sector it doesn't print, even when I do:

mov ax, 900h
mov es, ax

Example code:

xchg bx, bx
mov ax, 7c0h
mov ds, ax

sector_2:
mov bx, 900h
mov es, bx
mov bx, 0
mov ah, 2
mov al, 1
mov ch, 0
mov cl, 2
mov dh, 0                     
mov dl, 80h                    
int 13h                        
call 900h:0000

jmp $

times 510 - ($-$$) db 0            ; Fill empty bytes to binary file
dw 0aa55h                          ; Define MAGIC number at byte 512
;;;;;;;;;;;;;;;;;;;;;;;;

sector_2:
mov ax, 900h
mov es, ax      
mov bp, hello
mov ah,13h          ; function 13 - write string
mov al,01h          ; attrib in bl, move cursor
mov bl,0bh          ; attribute - magenta
mov cx,5            ; length of string
mov dh,1            ; row to put string
mov dl,4            ; column to put string
int 10h             ; call BIOS service 

retf
jmp $

hello db 'Hello'
times 1024 - ($-$$) db 0
times 2*8*63*512 - ($-$$) db 0
like image 748
JesMot Avatar asked Jun 28 '16 09:06

JesMot


People also ask

What does INT 10h mean in BIOS?

INT 10h, INT 10H or INT 16 is shorthand for BIOS interrupt call 10 hex, the 17th interrupt vector in an x86 -based computer system. The BIOS typically sets up a real mode interrupt handler at this vector that provides video services.

What is the meaning of INT 10h in AutoCAD?

INT 10h / AH = 0Ah -write Character only at cursor position. AL = Character to Display. BH = page number. INT 10h / AH = 0CH – Change color for a Single pixel. CX = column. DX = row. INT 10h; set graphics video mode. INT 10h; set pixel. INT 10h / AH = 0Dh – get color of a Single pixel.

What is the offset of bootloader 0x21a?

But notice that 0x21a is 538 decimal and that would be the offset relative to the beginning of the entire bootloader not relative to an offset of 0 (900h:0000h).

What does -E 512 mean in boot boot?

boot.img is the name of the image file you generated. The -e 512 says to skip disassembling the first 512 bytes of the file. I'm just interested in the first few lines of the output, in particular: 0x21a is the offset of hello.


1 Answers

I think there are some copy and paste errors in your example code. You wrote:

xchg bx, bx
mov ax, 7c0h
mov ds, ax

But I think you meant:

xchg bx, bx
mov ax, 7c0h
mov es, ax      ; Int 10h/ah=13h takes string address in ES:BP

Your code was correct in the very first snippet. Your example has two sector_2 labels so would probably cause NASM some grief. I believe you should just remove the first appearance of the label in your code.

I assume you are assembling your code with something like:

nasm -f bin boot.asm -o boot.img

The file names will be different and you can omit the -f bin since it is default.


Since your code doesn't have an explicit ORG directive in it, NASM assumes org 0h by default. All absolute memory references are relative to an offset of 0. In your case that is what you want for the first sector (512 bytes) of your assembler file. You have coded your bootloader to use a segment of 0x7c0, the segment you choose and an origin point of 0 should point to a physical address of 7c00h. In segment:offset addressing you'd have (7c0h<<4)+0 (where 0 is the origin/org) which yields the proper result of 7c00h.

So all is well and good, you properly read the sectors into memory at 900h:0h. You then do a FAR CALL to it from the first stage of your bootloader via call 900h:0000. This too is correct.

If all this is correct where is the problem? The issue is that NASM has no idea you loaded the code after the first 512 bytes to another location in memory and that the segment:offset used would be relative to 0 again (900h:0000h). It will continue to generate absolute addresses relative to the beginning of your bootloader.

If you were to use NDISASM to display the generated code starting from byte 512 of your disk image you'd discover the problem:

00000000  B80009            mov ax,0x900
00000003  8EC0              mov es,ax
00000005  BD1A02            mov bp,0x21a
00000008  B413              mov ah,0x13

This was generated with the command:

ndisasm -e 512 -b16 boot.img

boot.img is the name of the image file you generated. The -e 512 says to skip disassembling the first 512 bytes of the file. I'm just interested in the first few lines of the output, in particular:

mov bp,0x21a

0x21a is the offset of hello. But notice that 0x21a is 538 decimal and that would be the offset relative to the beginning of the entire bootloader not relative to an offset of 0 (900h:0000h). To fix this, you need to instruct NASM that the code generated in the second stage (second sector) needs to be relative to an origin of 0 and not relative to the beginning of the bootloader. This can easily be done by placing the second stage (second sector) in a new section where the origin point (vstart) is reset to 0. That can be done by placing a section directive like this at the beginning of the second stage:

section stage2, vstart=0h

So in your code it would look like:

dw 0aa55h                          ; Define MAGIC number at byte 512
;;;;;;;;;;;;;;;;;;;;;;;;

section stage2, vstart=0h          ; Section name can be anything of your choosing
sector_2:
mov ax, 900h
mov es, ax
mov bp, hello

Now if you look at the NDISASM output it would look like:

00000000  B80009            mov ax,0x900
00000003  8EC0              mov es,ax
00000005  BD1A00            mov bp,0x1a    ; Notice hello offset is 0x1a not 0x21a

@Jester was on the right track with org 0h being placed before the second stage code (second sector), but there can only be one ORG directive in each assembly file. No matter where you place it in the file, NASM will act as if it actually found it at the top of the file. This behaviour is not well documented! Jester's solution wouldn't have changed anything. NASM's `SECTION directive can be used anywhere in the assembly file to reset the origin point (in this case to 0).

More information on the ORG and SECTION directives can be found in the NASM documentation. The SECTION directive and the VSTART argument are documented in section 7.1.3 Multisection Support for the bin Format

like image 140
Michael Petch Avatar answered Nov 01 '22 17:11

Michael Petch