将 PIT、PIC 和 IVT 绑定在一起以获得 sleep() 函数

Binding PIT, PIC and IVT together to get a sleep() function

我决定编写一个小程序来检查我是否能够在实模式应用程序中实现睡眠功能。那就是我所做的:

  1. 在0x0000:0x80的地址添加了一个IRQ_0处理函数,对应IVT
  2. 中的0x20中断
  3. 设置两个 PIC,告诉 ICW2 中的主 PIC 将其 IRQ0 绑定到 IVT 中的 0x20 中断
  4. 使用除数值 11931 设置 PIT 以获得大约 100hz 的频率
  5. 栈设置在0xffff,因为是小程序,只是为了测试睡眠功能而写的,所以我怀疑它需要超过1kib的内存。
  6. 通过 0x10 BIOS 中断启用具有 320x200 屏幕和 256 色的 VGA 图形模式
  7. 在屏幕中间某处放置一个粉色像素
  8. 睡一小段时间
  9. 在其旁边放置一个不同颜色的像素。

而且我的程序永远无法打印第二个像素。不过,我确定中断 0x20 是我的 IRQ0 处理程序,因为我通过 int 0x20 调用它进行测试。

[BITS 16]
[ORG 0x7C00]

%macro sleep 1              ; one argument, word sized, time to sleep
    mov word [countdown], %1; store the arg in the countdown
    push ax
    %%sleeploop:    
        cli                 ; disable interrupts
        mov ax, word [countdown]
        sti
        test ax, ax
        jz %%endsleep
        nop
        nop
        nop
        nop
        nop
        nop
        jmp %%sleeploop
    %%endsleep:
    sti
    pop ax
%endmacro



;add an IRQ_0 handler at IVT 0x20 entry:
xor ax, ax
mov ds, ax
cli                         ; disable interrupts while modifying
mov word [ds:0x80], IRQ_0   ; IVT entry at 0000:0x80 = 128 / 4 = interrupt number 32, left open fo user to initialize
mov word [ds:0x82], ax      ; our interrupts segment is also 0000, since were touching just the first kib of RAM
sti                         ; enable interrupts, since were finished with modifying IVT

; set up the PIC:
;ICW1:
mov al, 0x11                ; load the ICW1 into al
out 0x20, al                ; send it to the port 0x20(master PIC)
out 0xa0, al                ; send it to the port 0xa0(slave PIC)
;ICW2:
mov al, 0x20                ; the number of the 0 interrupt of the master PIC
out 0x20, al                ; send it to the master PIC. Now IRQ0 is interrupt 0x20 in the IVT table
mov al, 0x28                ; the number of the first interrupt of the slave PIC
out 0xa0, al                ; send it to the slave PIC. Now IRQ9 is interrupt 0x28 in the IVT table
;ICW3:
mov al, 0x4                 ; 0x4 - IRQ2, used to call the slave PIC
out 0x21, al                ; send it to the master PIC
mov al, 0x2                 ; IRQ2 for the slacve PIC
out 0xa1, al                ; send it to the slave PIC
;ICW4:
mov al, 1                   ; only the bit 0 is set. 
out 0x21, al                ; by sending it, tell the master PIC, that we are in a 8086 PC
out 0xa1, al                ; same, for the slave PIC
;Null out the PIC data registers
xor al, al
out 0x21, al
out 0xa1, al


; set up the PIT:
mov al, 0x36                ; a control word fot PIT, tellig that next values will be in binary, not BCD
out 0x43, al                ; send it to PIT
mov ax, 11931               ; our divisor for frequency in order to get 100hz
out 0x40, al                ;}
mov al, ah                  ;}=> send the divisor to PIT
out 0x40, al                ;}

; set up the stack:
mov sp, 0xffff 

cld 
xor ah, ah
mov al, 0x13
int 0x10
mov ax, 0xa000
mov es, ax

mov al, 60
mov byte [es:0x7d96], al
sleep 1
mov al, 50
mov byte [es:0x7d97], al


hlt

IRQ_0:                      ; a function, bound to the IRQ_0 interrupt
    push ax
    mov ax, word [countdown]
    test ax, ax
    jz .IRQ0_end
    dec ax
    mov word [countdown], ax
    .IRQ0_end:
    pop ax
iret

countdown dw 0

times 510 - ($-$$) db 0
dw 0xAA55

我想,我在 PIC 或 PIT 初始化方面做错了。

我想,我想通了,我做错了什么。首先,PIC 已经被 BIOS 初始化,默认 IRQ0 为中断 0x08。我们还需要在自定义中断结束时将 EOI 发送到 PIC。现在可以了! 下面代码中的注释可能无关紧要,不再有意义。

[BITS 16]
[ORG 0x7C00]

%macro sleep 1              ; one argument, word sized, time to sleep
    mov word [countdown], %1; store the arg in the countdown
    push ax
    %%sleeploop:    
        cli                 ; disable interrupts
        mov ax, word [countdown]
        sti
        test ax, ax
        jz %%endsleep
        nop
        nop
        nop
        nop
        nop
        nop
        jmp %%sleeploop
    %%endsleep:
    sti
    pop ax
%endmacro

xor bp, bp

;add an IRQ_0 handler at IVT 0x08 entry:
xor ax, ax
mov ds, ax
cli                         ; disable interrupts while modifying
mov word [ds:0x20], IRQ_0   ; IVT entry at 0000:0x08 
mov word [ds:0x22], ax      ; our interrupts segment is also 0000, since were touching just the first kib of RAM
sti                         ; enable interrupts, since were finished with modifying IVT


; set up the PIT:
mov al, 0x34                ; a control word fot PIT, tellig that next values will be in binary, not BCD
out 0x43, al                ; send it to PIT
mov ax, 11931               ; our divisor for frequency in order to get 100hz
out 0x40, al                ;}
mov al, ah                  ;}=> send the divisor to PIT
out 0x40, al                ;}

; set up the stack:
mov sp, 0xffff 

cld 
xor ah, ah
mov al, 0x13
int 0x10
mov ax, 0xa000
mov es, ax

mov al, 60
mov byte [es:0x7d96], al
sleep 100
mov al, 50
mov byte [es:0x7d97], al


hlt

IRQ_0:                      ; a function, bound to the IRQ_0 interrupt
    push ax
    mov ax, word [countdown]
    test ax, ax
    jz .IRQ0_end
    dec ax
    mov word [countdown], ax
    .IRQ0_end:
    mov al, 0x20
    out 0x20, al
    pop ax
iret

countdown dw 0

times 510 - ($-$$) db 0
dw 0xAA55