如何在 nasm 中启用 a20?
How do you enable a20 in nasm?
我正在尝试在我的引导加载程序中进入长模式,现在我在启用 a20 行的第一部分,但我遇到了一个问题,那就是我制作的代码刚刚制作了屏幕完全黑了,如果有效的话应该说的数字甚至没有显示
我在互联网上尝试了很多不同的 a20 功能,但对我来说还没有任何效果。
函数的代码是:
check_a20:
pushf
push ds
push es
push di
push si
cli
xor ax, ax ; ax = 0
mov es, ax
not ax ; ax = 0xFFFF
mov ds, ax
mov di, 0x0500
mov si, 0x0510
mov al, byte [es:di]
push ax
mov al, byte [ds:si]
push ax
mov byte [es:di], 0x00
mov byte [ds:si], 0xFF
cmp byte [es:di], 0xFF
pop ax
mov byte [ds:si], al
pop ax
mov byte [es:di], al
mov ax, 0
je check_a20_exit
mov ax, 1
check_a20_exit:
pop si
pop di
pop es
pop ds
popf
ret
seta20: ;Enable the a20 line if it worked then ax = 1 else 0
pusha
call check_a20 ;Check a20
cmp ax, 1
je .end ;If it worked then end function else:
.keyboard: ;Test the 8042 keyboard controller
call .empty_8042
mov al, 0xd1 ;command write
out 0x64, al
call .empty_8042
mov al, 0xdf ; A20 on
out 0x60, al
call .empty_8042 ;wait
.empty_8042: ;For the 8042 function over this
in al, 0x64
test al, 2
jnz .empty_8042
ret
call check_a20 ;Check a20
cmp ax, 1
je .end ;If it worked then end function else:
.fasta20:
in al, 0x92
or al, 2
out 0x92, al
.end:
popa
call check_a20
ret
在这些函数之后,我有一个将 ax 打印为十六进制的函数:
main:
;Stack, video and other setups(not important)
call seta20 ;Set a20
mov dl, 00h ;Set cursor for a print a20 check
mov dh, 01h
mov bh, 00h
mov ah, 02h
int 10h
call check_a20 ;Check a20
mov dl, al
mov bl, 02h
call printhex ;Print dl
jmp $ ;Infinite loop
printhex: ;print hex input(dl=value, bl=color) 8 bit
pusha
mov dh, 0x00
mov ch, dl ;unshifted (for next hex)
shr dl, 4 ; get high 4 bits(HEX)
cmp dl, 9
jna .1to9
.atof: ;if the number is a to f
add dl, 55
jmp .next
.1to9:
add dl, 48 ;add 48 to make it into a number
.next:
mov ah, 0Eh ;print char mode
mov bh, 0
mov al, dl
int 10h ;Print 1st number of the two
shl ch, 4
mov dl, ch
shr dl, 4 ; get high 4 bits(HEX)
cmp dl, 9
jna .1to92
.atof2: ;if the number is a to f
add dl, 55
jmp .print2
.1to92:
add dl, 48 ;add 48 to make it into a number
.print2:
mov ah, 0Eh ;print char mode
mov bh, 0
mov al, dl
int 10h ;Print 1st number of the two
popa
ret
我已经知道我打印结果的函数有效,因为我已经测试了很多次,但应该发生的是它应该用我的 printhex16
打印一个十六进制数
我拥有的功能
您的 A20 代码背后的方法看起来不错,但您的实现方式似乎存在错误。您有 seta20
:
的代码
seta20: ;Enable the a20 line if it worked then ax = 1 else 0
pusha
call check_a20 ;Check a20
cmp ax, 1
je .end ;If it worked then end function else:
.keyboard: ;Test the 8042 keyboard controller
call .empty_8042
mov al, 0xd1 ;command write
out 0x64, al
call .empty_8042
mov al, 0xdf ; A20 on
out 0x60, al
call .empty_8042 ;wait
.empty_8042: ;For the 8042 function over this
in al, 0x64
test al, 2
jnz .empty_8042
ret
call check_a20 ;Check a20
cmp ax, 1
je .end ;If it worked then end function else:
.fasta20:
in al, 0x92
or al, 2
out 0x92, al
.end:
popa
call check_a20
ret
问题是您将一个函数放在另一个函数中,并且无意中让您的代码落入该函数中。特别是这些代码行是问题所在:
out 0x60, al
call .empty_8042 ;wait
.empty_8042: ;For the 8042 function over this
in al, 0x64
test al, 2
jnz .empty_8042
ret
call check_a20 ;Check a20
call .empty_8042
会调用函数.empty_8042
,8042会被刷新; ret
将return 到call .empty_8042
之后的指令,然后开始执行.empty_8042
中的代码。问题是它第二次没有作为函数被调用,所以没有正确的 return 地址。当它到达 ret
时,它将尝试 return 到堆栈顶部的任何值。这可能会导致您的代码挂起、重新启动系统或执行其他意外操作。
一个快速修复方法是放置一个 JMP 指令来跳过 .empty_8042
中的代码。这样的事情会做:
out 0x60, al
call .empty_8042 ;wait
jmp .skip_function
.empty_8042: ;For the 8042 function over this
in al, 0x64
test al, 2
jnz .empty_8042
ret
.skip_function:
call check_a20 ;Check a20
最好将 .empty_8042
函数与 seta20
函数分开,这样您就不必不必要地跳过 .empty_8042
。您的代码可能如下所示:
empty_8042:
in al, 0x64
test al, 2
jnz empty_8042
ret
seta20: ;Enable the a20 line if it worked then ax = 1 else 0
pusha
call check_a20 ;Check a20
cmp ax, 1
je .end ;If it worked then end function else:
.keyboard: ;Test the 8042 keyboard controller
call empty_8042
mov al, 0xd1 ;command write
out 0x64, al
call empty_8042
mov al, 0xdf ; A20 on
out 0x60, al
call empty_8042 ;wait
call check_a20 ;Check a20
cmp ax, 1
je .end ;If it worked then end function else:
.fasta20:
in al, 0x92
or al, 2
out 0x92, al
.end:
popa
call check_a20
ret
我正在尝试在我的引导加载程序中进入长模式,现在我在启用 a20 行的第一部分,但我遇到了一个问题,那就是我制作的代码刚刚制作了屏幕完全黑了,如果有效的话应该说的数字甚至没有显示
我在互联网上尝试了很多不同的 a20 功能,但对我来说还没有任何效果。
函数的代码是:
check_a20:
pushf
push ds
push es
push di
push si
cli
xor ax, ax ; ax = 0
mov es, ax
not ax ; ax = 0xFFFF
mov ds, ax
mov di, 0x0500
mov si, 0x0510
mov al, byte [es:di]
push ax
mov al, byte [ds:si]
push ax
mov byte [es:di], 0x00
mov byte [ds:si], 0xFF
cmp byte [es:di], 0xFF
pop ax
mov byte [ds:si], al
pop ax
mov byte [es:di], al
mov ax, 0
je check_a20_exit
mov ax, 1
check_a20_exit:
pop si
pop di
pop es
pop ds
popf
ret
seta20: ;Enable the a20 line if it worked then ax = 1 else 0
pusha
call check_a20 ;Check a20
cmp ax, 1
je .end ;If it worked then end function else:
.keyboard: ;Test the 8042 keyboard controller
call .empty_8042
mov al, 0xd1 ;command write
out 0x64, al
call .empty_8042
mov al, 0xdf ; A20 on
out 0x60, al
call .empty_8042 ;wait
.empty_8042: ;For the 8042 function over this
in al, 0x64
test al, 2
jnz .empty_8042
ret
call check_a20 ;Check a20
cmp ax, 1
je .end ;If it worked then end function else:
.fasta20:
in al, 0x92
or al, 2
out 0x92, al
.end:
popa
call check_a20
ret
在这些函数之后,我有一个将 ax 打印为十六进制的函数:
main:
;Stack, video and other setups(not important)
call seta20 ;Set a20
mov dl, 00h ;Set cursor for a print a20 check
mov dh, 01h
mov bh, 00h
mov ah, 02h
int 10h
call check_a20 ;Check a20
mov dl, al
mov bl, 02h
call printhex ;Print dl
jmp $ ;Infinite loop
printhex: ;print hex input(dl=value, bl=color) 8 bit
pusha
mov dh, 0x00
mov ch, dl ;unshifted (for next hex)
shr dl, 4 ; get high 4 bits(HEX)
cmp dl, 9
jna .1to9
.atof: ;if the number is a to f
add dl, 55
jmp .next
.1to9:
add dl, 48 ;add 48 to make it into a number
.next:
mov ah, 0Eh ;print char mode
mov bh, 0
mov al, dl
int 10h ;Print 1st number of the two
shl ch, 4
mov dl, ch
shr dl, 4 ; get high 4 bits(HEX)
cmp dl, 9
jna .1to92
.atof2: ;if the number is a to f
add dl, 55
jmp .print2
.1to92:
add dl, 48 ;add 48 to make it into a number
.print2:
mov ah, 0Eh ;print char mode
mov bh, 0
mov al, dl
int 10h ;Print 1st number of the two
popa
ret
我已经知道我打印结果的函数有效,因为我已经测试了很多次,但应该发生的是它应该用我的 printhex16
打印一个十六进制数
我拥有的功能
您的 A20 代码背后的方法看起来不错,但您的实现方式似乎存在错误。您有 seta20
:
seta20: ;Enable the a20 line if it worked then ax = 1 else 0
pusha
call check_a20 ;Check a20
cmp ax, 1
je .end ;If it worked then end function else:
.keyboard: ;Test the 8042 keyboard controller
call .empty_8042
mov al, 0xd1 ;command write
out 0x64, al
call .empty_8042
mov al, 0xdf ; A20 on
out 0x60, al
call .empty_8042 ;wait
.empty_8042: ;For the 8042 function over this
in al, 0x64
test al, 2
jnz .empty_8042
ret
call check_a20 ;Check a20
cmp ax, 1
je .end ;If it worked then end function else:
.fasta20:
in al, 0x92
or al, 2
out 0x92, al
.end:
popa
call check_a20
ret
问题是您将一个函数放在另一个函数中,并且无意中让您的代码落入该函数中。特别是这些代码行是问题所在:
out 0x60, al
call .empty_8042 ;wait
.empty_8042: ;For the 8042 function over this
in al, 0x64
test al, 2
jnz .empty_8042
ret
call check_a20 ;Check a20
call .empty_8042
会调用函数.empty_8042
,8042会被刷新; ret
将return 到call .empty_8042
之后的指令,然后开始执行.empty_8042
中的代码。问题是它第二次没有作为函数被调用,所以没有正确的 return 地址。当它到达 ret
时,它将尝试 return 到堆栈顶部的任何值。这可能会导致您的代码挂起、重新启动系统或执行其他意外操作。
一个快速修复方法是放置一个 JMP 指令来跳过 .empty_8042
中的代码。这样的事情会做:
out 0x60, al
call .empty_8042 ;wait
jmp .skip_function
.empty_8042: ;For the 8042 function over this
in al, 0x64
test al, 2
jnz .empty_8042
ret
.skip_function:
call check_a20 ;Check a20
最好将 .empty_8042
函数与 seta20
函数分开,这样您就不必不必要地跳过 .empty_8042
。您的代码可能如下所示:
empty_8042:
in al, 0x64
test al, 2
jnz empty_8042
ret
seta20: ;Enable the a20 line if it worked then ax = 1 else 0
pusha
call check_a20 ;Check a20
cmp ax, 1
je .end ;If it worked then end function else:
.keyboard: ;Test the 8042 keyboard controller
call empty_8042
mov al, 0xd1 ;command write
out 0x64, al
call empty_8042
mov al, 0xdf ; A20 on
out 0x60, al
call empty_8042 ;wait
call check_a20 ;Check a20
cmp ax, 1
je .end ;If it worked then end function else:
.fasta20:
in al, 0x92
or al, 2
out 0x92, al
.end:
popa
call check_a20
ret