我想在 FFFF0H 位置写一条jmp
指令,目的地 F8000H 。
之后,我想将下面的代码放在 F8000H 上,这样在从 FFFF0H 执行时,控件将通过上面的跳转传输到它。
.MODEL SMALL
.8086
.STACK
.DATA
.CODE
.STARTUP
LL:
MOV AL, 1H
IO_LOOP:
OUT 0EH, AL
SHL AL, 1
;Delay
MOV CX, 0FFFFH
DLOOP:
NOP
LOOP DLOOP
;Start all over again
CMP AL, 00H
JE LL
;Get back to IO cycle
JMP IO_LOOP
END
答案 0 :(得分:3)
注意强>
地址范围 0F0000h - 0FFFFFh 是只读的。
真实的故事是,在BIOS的卷影复制完成后,它在启动时被固件设置为只读,因此访问不必一直向下到PCH,默认情况下在标准地址范围 FFFF_0000H - FFFF_FFFFH 重新路由它。
因此,这些地址范围要么映射到只读DRAM区域,要么映射到忽略写入请求直到编程的闪存ROM。
如果您正在编写固件,那么构建链肯定支持创建可放置在ROM中任何位置的部分。
解释如何使用此类工具超出了本答案和本网站的范围。
看来你正在写一个普通的DOS可执行文件,请注意下面的解决方案不工作。
因此,我将简单地假设您要将一段代码从 A 移动到 B 。
我们需要:
一些代码用于在两个内存区域之间移动数据
这可以通过多种方式完成,最快的代码,但最慢的(对于小块)是使用rep movsb
将cx
字节从ds:si
复制到es:di
。
一种判断一段代码长度的方法。
我们可以在代码片段的开头和结尾分别放两个标签,分别是 S 和 E 。这样,表达式 E - S 给出了两个标签之间的差异,以字节为单位,即代码的长度。
这是一种可能的实施方式:
.MODEL SMALL
.8086
.STACK
.DATA
.CODE
.STARTUP
;Set up ES
mov ax, 0f000h
mov es, ax
;Move the first routine
mov si, OFFSET __ROUTINE_1__START__ ;DS:SI = Start of the routine to copy
mov di, 8000h ;ES:DI = 0f000h:8000h = 0f8000h
mov cx, __ROUTINE_1__END__ - __ROUTINE_1__START__ ;CX = Length of the routine to copy
rep movsb
;Copy the jump
mov si, OFFSET __ROUTINE_2__START__
mov di, 0fff0h ;ES:DI = 0f000h:0fff0h = 0ffff0h
mov cx, 5 ;Absolute far jump is 5 bytes
rep movsb
;DO SOMETHING HERE
;
; ROUTINE TO MOVE
;
__ROUTINE_1__START__:
LL:
MOV AL, 1H
IO_LOOP:
OUT 0EH, AL
SHL AL, 1
;Delay
MOV CX, 0FFFFH
DLOOP:
NOP
LOOP DLOOP
;Start all over again
CMP AL, 00H
JE LL
;Get back to IO cycle
JMP IO_LOOP
__ROUTINE_1__END__:
__ROUTINE_2__START__:
jmp FAR 0f000h:8000h ;TODO: Adjust the syntax for the assembler dialect
END
正如我所说,除非您在特殊情况下工作,否则此解决方案不会适用于您选择的地址范围。 我还假设你知道细分是如何运作的。
我没有直接创建远jmp
,而是将机器代码存储为mov
- 直接存储;相反,我让汇编程序创建机器代码,然后我复制它
它的效率较低,但可以让您轻松更改代码。