Reputation: 535
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.
Upvotes: 2
Views: 558
Reputation: 18503
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!
Upvotes: 1
Reputation: 4455
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)
Upvotes: 2
Reputation: 535
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 = .;
}
Upvotes: 1