a20.asm (3323B)
1 call a20_test 2 call a20_enable 3 4 ; Test to see if the A20 line is enabled. 5 ; As a reference we will use the magic number 0xaa55 since we know it's always 6 ; going to have the same value and be located at address 0x7dfe (0x7c00 + 510). 7 ; If the return code in `ax` is 1, the A20 is enabled, and if it's 0 it's disabled. 8 a20_test: 9 pusha 10 11 mov ax, [0x7dfe] ; 0x7c00 + 510. The magic number is there. 12 mov dx, ax 13 14 ; We'll try to advance 1MB in memory. If the end result hasn't wrapped up 15 ; around zero, it means that our processor is able to access more than 1MB 16 ; of addresses, so the A20 is enabled. Using 0x7dfe again, the expected 17 ; address after the 1MB advance should be: 18 ; 19 ; 0x100000 + 0x7dfe = 0x107dfe 20 ; 21 ; The formula for memory segmentation is: 22 ; 23 ; address = segment * 16 + offset 24 ; 25 ; To find the offset, the segment will be 0xffff, so `0xffff * 16 = 0xffff0`. 26 ; Applying the formula to calculate our offset, we have: 27 ; 28 ; 0xffff0 + offset = 0x107dfe => 29 ; offset = 0x107dfe - 0xffff0 => 30 ; offset = 0x7e0e 31 32 push bx 33 mov bx, 0xffff 34 mov es, bx ; Assign value to the segment register. 35 pop bx 36 37 mov bx, 0x7e0e 38 mov dx, [es:bx] ; If the A20 line is disabled we get 0xaa55. 39 40 cmp ax, dx 41 je cont ; If they're equal, the A20 line might be disabled. 42 popa 43 mov ax, 0x01 ; Success code 1. 44 ret 45 cont: 46 mov ax, [0x7dff] 47 mov dx, ax 48 49 push bx 50 mov bx, 0xffff ; Make another segment. 51 mov es, bx 52 pop bx 53 54 mov bx, 0x7e0f 55 mov dx, [es:bx] 56 57 cmp ax, dx ; Now it really is disabled if ax == [es:bx]. 58 je exit 59 popa 60 mov ax, 0x01 ; Success code 1. 61 ret 62 exit: 63 popa 64 xor ax, ax ; Error code 0. 65 ret 66 67 ; Enable the A20 line. We'll try enabling it using the following ways: 68 ; - BIOS interrupt. 69 ; - Keyboard controller. 70 ; - FastA20. 71 a20_enable: 72 pusha 73 74 ; BIOS interrupt. 75 mov ax, 0x2401 ; A20-Gate Active. 76 int 0x15 77 78 call a20_test 79 cmp ax, 0x01 80 je a20_done 81 jmp a20_fail 82 83 ; Keyboard controller. 84 sti ; Enable interrupts. 85 86 call a20_waitc 87 mov al, 0xad ; Disable controller. 88 out 0x64, al ; Send data to port 0x64. 89 call a20_waitc 90 mov al, 0xd0 ; We want to read from the controller. 91 out 0x64, al 92 93 call a20_waitd 94 in al, 0x60 ; Read data from port 0x60. 95 push ax ; Save data. 96 97 call a20_waitc 98 mov al, 0xd1 ; We want to send data. 99 out 0x64, al 100 101 call a20_waitc 102 pop ax 103 or al, 0x02 ; Write the second bit back. 104 out 0x60, al ; Send data to the data port. 105 106 call a20_waitc 107 mov al, 0xae ; Enable controller. 108 out 0x64, al 109 110 call a20_waitc 111 sti ; Enable interrupts again. 112 113 call a20_test 114 cmp ax, 0x01 115 je a20_done 116 jmp a20_fail 117 118 ; Wait for the keyboard controller to be available for receiving commands. 119 a20_waitc: 120 in al, 0x64 ; Read from port 0x64. 121 test al, 0x02 ; Test the second bit. 122 jnz a20_waitc ; If it's 1, it's busy. 123 ret 124 ; Wait for the keyboard controller to send data back. 125 a20_waitd: 126 in al, 0x64 ; Read from port 0x64 again. 127 test al, 0x01 ; Test the first bit. 128 jz a20_waitd ; If it's 1, the data is not ready to be sent. 129 ret 130 131 ; FastA20 132 in al, 0x92 ; FastA20 uses port 0x92. 133 or al, 0x02 ; Mask second bit. 134 out 0x92, al ; Send data back to port 0x92. 135 136 call a20_test 137 cmp ax, 0x01 138 je a20_done 139 jmp a20_fail 140 141 ; The A20 is enabled. Go on. 142 a20_done: 143 popa 144 ret 145 146 ; The A20 is disabled. 147 a20_fail: 148 mov si, str_a20_fail 149 call puts 150 jmp $ 151 152 153 str_a20_fail: db "The A20 Line is disabled", 0x0a, 0x0d, 0x00