Search code examples
assemblyx86bit-manipulationportosdev

How to unset a bit, thereby closing the A20 line


I read this tutorial. In the tutorial the author writes about the A20 and different methods of enabling it. When he writes about the "fast A20 method", he says that this is done through setting the second bit in port 0x92. Here is the example he gives, of a piece of code that does this:

mov al, 2   ; set bit 2 (enable a20)
out 0x92, al

Now, suppose I want to unset that second bit and disable the A20, how would I do that?


Solution

  • The instruction you are looking for is AND. If you use the AND instruction with all bits set to 1 EXCEPT the 2nd bit, you will effectively set the bit to 0.

    For instance,

    AL = 0101'1010b    0101'1000b
    BL = 1111'1101b    1111'1101b
    
    AND AL, BL
    
    AL = 0101'1000b    0101'1000b
    

    Alternatively, if you want to flip a bit regardless of its current value, simply do the XOR with all bits set to 0 except the one bit you want to flip.

    AL = 0101'1010b    0101'1000b
    BL = 0000'0010b    0000'0010b
    
    XOR AL, BL
    
    AL = 0101'1000b    0101'1010b
    

    Also, for future reference, if you want to force a bit to 1, use the OR instruction.

    AL = 0101'1000b    0101'1010b
    BL = 0000'0010b    0000'0010b
    
    OR AL, BL
    
    AL = 0101'1010b    0101'1010b
    

    As a side note, I noticed you are following the Brokenthorn Operating System Development Tutorials. I would recommend sticking with the keyboard controller method explained in the tutorials as it works on more hardware, and is in many cases safer to use. I've posted it below for you to look at.

    EnableA20:
        PUSH AX
        CLI
    
        CALL .WaitA
        MOV AL, 0xAD             ; CMD: Disable PS/2 Port 1
        OUT 0x64, AL
    
        CALL .WaitA
        MOV AL, 0xD0             ; CMD: Read Controller Output Port
        OUT 0x64, AL
    
        CALL .WaitB
        IN AL, 0x60              ; READ: Controller Output Port
        PUSH AX
    
        CALL .WaitA
        MOV AL, 0xD1             ; CMD: Write Controller Output Port
        OUT 0x64, AL
    
        CALL .WaitA
        POP AX
        OR AL, 0x02              ; WRITE: Controller Output Port
        OUT 0x60, AL
    
        CALL .WaitA
        MOV AL, 0xAE             ; CMD: Enable PS/2 Port 1
        OUT 0x64, AL
    
        CALL .WaitA
        STI
        POP AX
        RET
    
        .WaitA:
            IN AL, 0x64
            TEST AL, 0x02        ; Check Input Buffer Status
            JNZ .WaitA
            RET
    
        .WaitB:
            In AL, 0x64
            TEST AL, 0x02        ; Check Output Buffer Status
            JZ .WaitB
            RET
    

    To instead disable the A20 line, replace the OR instruction on the line with the comment saying WRITE with AND, and invert the bits to read 0xFD. (To toggle the bit, use XOR).