I am learning about i8086 real mode programming.

In the first .S assembly cdoe file:

.intel_syntax noprefix

/* _a20en defined in another .S file */
.section .text
.code16
.extern _a20en

/* code entry is 0x7c00, defined in BIOS */
entry:
    xor ax, ax
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov gs, ax
    mov sp, 0x7c00
    mov ax, 0xb800
    mov fs, ax
    cld

    call _a20en
    call _a20en

    /* send string '8086' to video ram */
    mov byte ptr fs : [0], '8'
    mov byte ptr fs : [1], 0x01
    mov byte ptr fs : [2], '0'
    mov byte ptr fs : [3], 0x01
    mov byte ptr fs : [4], '8'
    mov byte ptr fs : [5], 0x01
    mov byte ptr fs : [6], '6'
    mov byte ptr fs : [7], 0x01

    jmp . /* stop here */

The second .S assembly code file:

.intel_syntax noprefix

.section .text
.code16
.global _a20en

_a20en:
    enter 2, 0
    push ax

    in al, 0x92
    or al, 0x02
    out 0x92, al

    pop ax
    leave
    ret

After assembly, linking and run. I can't see the string '8086' in screen. After debug in bochs, I found the problem is in the 2 'call _a20en' instructions. If I change the 2 lines to:

lea bx, _a20en
call bx
call bx

The code works fine. If using only a single 'call _a20en' instruction, the code works fine too.

The real problem is that in the 'call _a20en' instruction exection. After push ip, then jump to the 'jmp .' instruction, the dead loop. The real destination jump addr is _a20en, 2 bytes far from 'jmp .' instruction.

I don't know how the 2-byte-offset generated. There's no warning when assembly & linking.

有帮助吗?

解决方案 2

There is a problem using the GNU toolchain (ld.exe) I also observed in my own programs:

The GNU linker (MinGW variant "ld.exe") does not always (or even never) resolve PC-relative 16-bit relocations correctly.

Therefore you should not use (near) "call" and (near) "jmp" instructions to other object files but you'll have to do the work-around using "lea" and "call cx" (or "call bx").

Why does the program work with only one "call _a20en" instruction?

The answer is simple: The linker inserts "NOP" instructions between the object files (depending on the file length). When you used only one "call _a20en" instruction the file length was short enough that two NOPs have been inserted. Of course you cannot rely on this!

其他提示

For the record here is the disassembled version of the sector image. I figured I might as well spam this thread with another post.

00007C00  31C0              xor ax,ax
00007C02  8ED8              mov ds,ax
00007C04  8EC0              mov es,ax
00007C06  8ED0              mov ss,ax
00007C08  8EE8              mov gs,ax
00007C0A  BC007C            mov sp,0x7c00
00007C0D  B800B8            mov ax,0xb800
00007C10  8EE0              mov fs,ax
00007C12  FC                cld
00007C13  E83400            call word 0x7c4a
00007C16  E83100            call word 0x7c4a
00007C19  64C606000038      mov byte [fs:0x0],0x38
00007C1F  64C606010001      mov byte [fs:0x1],0x1
00007C25  64C606020030      mov byte [fs:0x2],0x30
00007C2B  64C606030001      mov byte [fs:0x3],0x1
00007C31  64C606040038      mov byte [fs:0x4],0x38
00007C37  64C606050001      mov byte [fs:0x5],0x1
00007C3D  64C606060036      mov byte [fs:0x6],0x36
00007C43  64C606070001      mov byte [fs:0x7],0x1
00007C49  EBFE              jmp short 0x7c49
00007C4B  90                nop
00007C4C  C8020000          enter 0x2,0x0
00007C50  50                push ax
00007C51  E492              in al,0x92
00007C53  0C02              or al,0x2
00007C55  E692              out 0x92,al
00007C57  58                pop ax
00007C58  C9                leave
00007C59  C3                ret
00007C5A  90                nop
00007C5B  90                nop

Notice that the (relative!) calls fall two bytes short. I can only assume that this is an issue with the linker script, though to be honest I'm not sufficiently knowledgeable about that particular area of black magic to tell you what.

As for workarounds I suspect that this will work just fine if you link the image as a single object file. That should also get rid of the 32-bit alignment for you.

(Complete binary and disassembly at https://sites.google.com/site/doynax/gary_sector.zip)

g++ -c -o xx.o xx.S
ld -T boot.lds
The file boot.lds :

OUTPUT_ARCH(i8086)
ENTRY(_start)

MEMORY
{
    ROM (rx) : ORIGIN = 0x7c00, LENGTH = 0x1f0
    RAM (rwx) : ORIGIN = 0x7df0, LENGTH = 0x10
}
/* the 0x16 bytes RAM seg is for pading 0xaa55  */

SECTIONS
{
    /* may overide by cmd 'ld -Ttext 0x...' */
    . = ORIGIN(ROM);
    _start = .;

    .text :
    {
        boot.o (.text)
        lib.o (.text)
        *(.rodata)
    } > ROM

    .data :
    {
        *(.data)
    } > RAM

    _end = .;
}

Boot sector

Following is th final boot.bin data snapshot.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top