diff --git a/src/boot/util/enablea20.s b/src/boot/util/enablea20.s new file mode 100644 index 0000000..709a6cb --- /dev/null +++ b/src/boot/util/enablea20.s @@ -0,0 +1,206 @@ +;; +;; enableA20.s (adapted from Visopsys OS-loader) +;; +;; Copyright (c) 2000, J. Andrew McLaughlin +;; You're free to use this code in any manner you like, as long as this +;; notice is included (and you give credit where it is due), and as long +;; as you understand and accept that it comes with NO WARRANTY OF ANY KIND. +;; Contact me at jamesamc@yahoo.com about any bugs or problems. +;; + +enableA20: + ;; This subroutine will enable the A20 address line in the keyboard + ;; controller. Takes no arguments. Returns 0 in eax on success, + ;; -1 on failure. Written for use in 16-bit code, see lines marked + ;; with 32-BIT for use in 32-bit code. + + pusha + + ;; Make sure interrupts are disabled + cli + + + ;; Keep a counter so that we can make up to 5 attempts to turn + ;; on A20 if necessary + mov cx, 5 + + .startAttempt1: + ;; Wait for the controller to be ready for a command + .commandWait1: + xor ax, ax + in al, 0x64 + bt ax, 1 + jc .commandWait1 + + ;; Tell the controller we want to read the current status. + ;; Send the command 0xD0: read output port. + mov al, 0xD0 + out 0x64, al + + ;; Wait for the controller to be ready with a byte of data + .dataWait1: + xor ax, ax + in al, 0x64 + bt ax, 0 + jnc .dataWait1 + + ;; Read the current port status from port 0x60 + xor ax, ax + in al, 0x60 + + ;; Save the current value of (E)ax + ;; push ax ; 16-BIT + push eax ; 32-BIT + + ;; Wait for the controller to be ready for a command + .commandWait2: + in al, 0x64 + bt ax, 1 + jc .commandWait2 + + ;; Tell the controller we want to write the status byte again + mov al, 0D1h + out 0x64, al + + ;; Wait for the controller to be ready for the data + .commandWait3: + xor ax, ax + in al, 0x64 + bt ax, 1 + jc .commandWait3 + + ;; Write the new value to port 0x60. Remember we saved the old + ;; value on the stack + ;;pop ax ; 16-BIT + pop eax ; 32-BIT + + ;; Turn on the A20 enable bit + or al, 0b00000010 + out 0x60, al + + ;; Finally, we will attempt to read back the A20 status + ;; to ensure it was enabled. + + ;; Wait for the controller to be ready for a command + .commandWait4: + xor ax, ax + in al, 0x64 + bt ax, 1 + jc .commandWait4 + + ;; Send the command 0xD0: read output port. + mov al, 0xD0 + out 0x64, al + + ;; Wait for the controller to be ready with a byte of data + .dataWait2: + xor ax, ax + in al, 0x64 + bt ax, 0 + jnc .dataWait2 + + ;; Read the current port status from port 0x60 + xor ax, ax + in al, 0x60 + + ;; Is A20 enabled? + bt ax, 1 + + ;; Check the result. If carry is on, A20 is on. + jc .success + + ;; Should we retry the operation? If the counter value in Ecx + ;; has not reached zero, we will retry + loop .startAttempt1 + + + ;; Well, our initial attempt to set A20 has failed. Now we will + ;; try a backup method (which is supposedly not supported on many + ;; chipsets, but which seems to be the only method that works on + ;; other chipsets). + + + ;; Keep a counter so that we can make up to 5 attempts to turn + ;; on A20 if necessary + mov cx, 5 + + .startAttempt2: + ;; Wait for the keyboard to be ready for another command + .commandWait6: + xor ax, ax + in al, 0x64 + bt ax, 1 + jc .commandWait6 + + ;; Tell the controller we want to turn on A20 + mov al, 0xDF + out 0x64, al + + ;; Again, we will attempt to read back the A20 status + ;; to ensure it was enabled. + + ;; Wait for the controller to be ready for a command + .commandWait7: + xor ax, ax + in al, 0x64 + bt ax, 1 + jc .commandWait7 + + ;; Send the command 0xD0: read output port. + mov al, 0xD0 + out 0x64, al + + ;; Wait for the controller to be ready with a byte of data + .dataWait3: + xor ax, ax + in al, 0x64 + bt ax, 0 + jnc .dataWait3 + + ;; Read the current port status from port 0x60 + xor ax, ax + in al, 0x60 + + ;; Is A20 enabled? + bt ax, 1 + + ;; Check the result. If carry is on, A20 is on, but we might warn + ;; that we had to use this alternate method + jc .warn + + ;; Should we retry the operation? If the counter value in Ecx + ;; has not reached zero, we will retry + loop .startAttempt2 + + + ;; OK, we weren't able to set the A20 address line. Do you want + ;; to put an error message here? + jmp .fail + + + .warn: + ;; Here you may or may not want to print a warning message about + ;; the fact that we had to use the nonstandard alternate enabling + ;; method + mov esi, a20_warning_msg + call vga_print + .success: + mov esi, a20_enabled_msg + call vga_print + ;sti ; Note: When this is uncommented, shit breaks. I have a few theories as to why, + ; but none of them make sense, so ¯\_(ツ)_/¯ + + popa + xor eax, eax + ret + + .fail: + sti + popa + mov eax, -1 + ret + + +a20_enabled_msg: db "TSBL - INFO: A20 line enabled successfully", 0 +a20_warning_msg: db "TSBL - WARN: A20 line was enabled with nonstandard method", 0 +a20_failed_msg: db "TSBL - ERROR: Failed to enable A20 line" \ No newline at end of file