INT 명령과 인터럽트 벡터 표 문서의 검증 코드다. if __name__ == "__main__": 아래가 자체 테스트다.

# 50.INT 명령과 인터럽트 벡터 표 문서의 계산 확인. 실행: python3 50_interrupt-vector-table_verify.py
# 16비트 실주소 모드를 실제로 돌리지 않는다. 1MB 바이트 배열 위에서 INT/IRET의 동작을 흉내 낸다.
# INT가 스택에 플래그·CS·IP를 넣는 순서는 Intel 설명서의 실주소 모드 동작을 따랐다.

def linear(seg, off):
    return ((seg << 4) + off) & 0xFFFFF


class Machine:
    def __init__(self):
        self.mem = bytearray(1 << 20)
        self.cs, self.ip, self.flags = 0, 0, 0x0202  # IF=1
        self.ss, self.sp = 0x9000, 0x0100

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

    def r16(self, addr):
        return self.mem[addr] | (self.mem[addr + 1] << 8)

    def push(self, v):
        self.sp = (self.sp - 2) & 0xFFFF
        self.w16(linear(self.ss, self.sp), v)

    def pop(self):
        v = self.r16(linear(self.ss, self.sp))
        self.sp = (self.sp + 2) & 0xFFFF
        return v

    def set_vector(self, n, seg, off):
        self.w16(n * 4, off)        # 낮은 주소에 오프셋
        self.w16(n * 4 + 2, seg)    # 높은 주소에 세그먼트

    def get_vector(self, n):
        return self.r16(n * 4 + 2), self.r16(n * 4)

    def int_(self, n):
        self.push(self.flags)
        self.push(self.cs)
        self.push(self.ip)          # INT 다음 명령의 주소
        self.flags &= ~0x0200       # IF = 0
        self.cs, self.ip = self.get_vector(n)

    def iret(self):
        self.ip = self.pop()
        self.cs = self.pop()
        self.flags = self.pop()


if __name__ == "__main__":
    # 오프셋 = 인터럽트 번호 × 4
    assert 0x10 * 4 == 0x0040 and 0x21 * 4 == 0x0084 and 0x16 * 4 == 0x0058
    assert 0x09 * 4 == 0x0024 and 0x00 * 4 == 0 and 0xFF * 4 == 0x03FC
    assert 0xFF * 4 + 4 == 0x0400  # 표의 끝 = BIOS 데이터 영역의 시작
    assert 256 * 4 == 1024
    # 문서 표의 줄: 번호 00-03 / 04-07 / 08-0B / 0C-0F / 10-13 의 오프셋
    for first, off in [(0x00, 0x0000), (0x04, 0x0010), (0x08, 0x0020), (0x0C, 0x0030), (0x10, 0x0040)]:
        assert first * 4 == off
    # 문서 표의 값으로 표를 채운다
    rows = {
        0x00: ["02C1:5186", "0070:0C67", "0DAD:2C1B", "0070:0C67"],
        0x04: ["0070:0C67", "F000:FF54", "F000:837B", "F000:837B"],
        0x08: ["0D70:022C", "0DAD:2BAD", "0070:0325", "0070:039F"],
        0x0C: ["0070:0419", "0070:0493", "0070:050D", "0070:0C67"],
        0x10: ["C000:0CD7", "F000:F84D", "F000:F841", "0070:237D"],
    }
    m = Machine()
    for first, vecs in rows.items():
        for i, v in enumerate(vecs):
            seg, off = (int(x, 16) for x in v.split(":"))
            m.set_vector(first + i, seg, off)
    assert m.get_vector(0x09) == (0x0DAD, 0x2BAD)   # 핸들러 바꿔 끼우기 문서의 BIOS INT 9 주소와 같다
    assert m.get_vector(0x10) == (0xC000, 0x0CD7)
    # 메모리에는 리틀 엔디언으로: INT 9 칸(0024h~0027h) = AD 2B AD 0D
    assert bytes(m.mem[0x24:0x28]) == bytes([0xAD, 0x2B, 0xAD, 0x0D])
    # INT 10h 칸(0040h~0043h) = D7 0C 00 C0
    assert bytes(m.mem[0x40:0x44]) == bytes([0xD7, 0x0C, 0x00, 0xC0])

    # 문서 첫 그림: INT 10h 칸에 F000:F065
    m = Machine()
    m.set_vector(0x10, 0xF000, 0xF065)
    assert linear(0xF000, 0xF065) == 0xFF065
    m.cs, m.ip = 0x1000, 0x0105     # int 10h 다음 명령(add...)의 주소라고 하자
    sp0, flags0 = m.sp, m.flags
    m.int_(0x10)
    assert (m.cs, m.ip) == (0xF000, 0xF065)
    assert m.sp == sp0 - 6          # 플래그, CS, IP 세 워드
    assert m.flags & 0x0200 == 0
    m.iret()
    assert (m.cs, m.ip, m.sp, m.flags) == (0x1000, 0x0105, sp0, flags0)

    # 카드 C2: INT 1Ch의 칸은 0070h, INT 16h는 0058h
    assert 0x1C * 4 == 0x0070
    print("ALL CHECKS PASSED")