인터럽트 핸들러 바꿔 끼우기 문서의 검증 코드다. if __name__ == "__main__": 아래가 자체 테스트다.

# 56.인터럽트 핸들러 바꿔 끼우기 문서의 확인. 실행: python3 56_custom-interrupt-handler_verify.py
# TSR을 실제로 올리지 않는다. 바이트 배열로 만든 인터럽트 벡터 표에서 "옛 주소 저장 -> 새 주소 넣기 ->
# 새 처리기가 옛 처리기로 넘기기"를 흉내 내고, No_Reset의 비트 검사를 전수로 확인한다.

mem = bytearray(0x400)       # 인터럽트 벡터 표


def w16(a, v):
    mem[a], mem[a + 1] = v & 0xFF, v >> 8


def r16(a):
    return mem[a] | (mem[a + 1] << 8)


def get_vector(n):           # INT 21h 함수 35h: ES:BX
    return r16(n * 4 + 2), r16(n * 4)


def set_vector(n, seg, off):  # INT 21h 함수 25h: DS:DX
    w16(n * 4, off)
    w16(n * 4 + 2, seg)


# 키보드 상태 바이트의 비트
RT_SHIFT, LEFT_SHIFT, CTRL_KEY, ALT_KEY = 0x01, 0x02, 0x04, 0x08
DEL_KEY = 0x53               # 스캔 코드 표


def int9_handler(flag_byte, scan):
    """문서 코드의 L1~L5. (새 플래그 바이트, 재시작을 막았는가)를 돌려준다."""
    ah = flag_byte
    if ah & CTRL_KEY == 0:           # L2: test ah,ctrl_key / jz L5
        return ah, False
    if ah & ALT_KEY == 0:            # test ah,alt_key / jz L5
        return ah, False
    if scan != DEL_KEY:              # L3: cmp al,del_key / jne L5
        return ah, False
    if ah & RT_SHIFT:                # test ah,rt_shift / jnz L5
        return ah, False
    ah &= (~CTRL_KEY) & 0xFF         # L4: and ah,NOT ctrl_key
    return ah, True                  # mov es:[di],ah


if __name__ == "__main__":
    # 처음: INT 9 칸에 BIOS 처리기 0DAD:2BAD (문서의 흐름 그림)
    set_vector(9, 0x0DAD, 0x2BAD)
    assert 9 * 4 == 0x24
    # 1) 옛 주소 저장: int9Save에 오프셋, int9Save+2에 세그먼트
    es, bx = get_vector(9)
    int9save = bytearray(4)
    int9save[0:2] = bx.to_bytes(2, "little")
    int9save[2:4] = es.to_bytes(2, "little")
    assert bytes(int9save) == bytes([0xAD, 0x2B, 0xAD, 0x0D])
    assert bytes(int9save) == bytes(mem[0x24:0x28])      # 벡터 표의 칸과 같은 배치
    # 2) 새 주소 넣기: 우리 처리기 10B2:0020
    set_vector(9, 0x10B2, 0x0020)
    assert get_vector(9) == (0x10B2, 0x0020)
    assert bytes(mem[0x24:0x28]) == bytes([0x20, 0x00, 0xB2, 0x10])
    # 3) 키가 눌리면: 표 -> 우리 처리기 -> 저장해 둔 옛 주소로 점프
    path = [get_vector(9)]
    old = (int.from_bytes(int9save[2:4], "little"), int.from_bytes(int9save[0:2], "little"))
    path.append(old)
    assert path == [(0x10B2, 0x0020), (0x0DAD, 0x2BAD)]
    # 옛 주소를 저장하지 않고 덮어쓰면 되찾을 수 없다
    assert (0x0DAD, 0x2BAD) != get_vector(9)

    # Ctrl-Break 예제: INT 23h 칸은 008Ch
    assert 0x23 * 4 == 0x008C

    # NOT ctrl_key = 11111011b
    assert (~CTRL_KEY) & 0xFF == 0b11111011
    # No_Reset: 플래그 256가지 × 스캔 코드 256가지 전수
    blocked = 0
    for fb in range(256):
        for sc in range(256):
            new, stop = int9_handler(fb, sc)
            want = bool(fb & CTRL_KEY) and bool(fb & ALT_KEY) and sc == DEL_KEY and not (fb & RT_SHIFT)
            assert stop == want
            if stop:
                blocked += 1
                assert new == fb & 0xFB and new & CTRL_KEY == 0
                assert new | CTRL_KEY == fb          # Ctrl 비트만 바뀐다
            else:
                assert new == fb
    assert blocked == 32                             # 나머지 5비트의 조합
    # 예: Ctrl+Alt+Del (0Ch) -> 08h로 바뀜. 오른쪽 Shift까지(0Dh) -> 그대로
    assert int9_handler(0x0C, DEL_KEY) == (0x08, True)
    assert int9_handler(0x0D, DEL_KEY) == (0x0D, False)
    assert int9_handler(0x0C, 0x1E) == (0x0C, False)
    # 카드 C3: 플래그 2Ch(Num Lock + Alt + Ctrl), Del -> 28h
    assert int9_handler(0x2C, DEL_KEY) == (0x28, True)
    print("ALL CHECKS PASSED")