0

仍在使用 x86 NASM 程序集操作系统。

我上一个问题的后续问题(CLI 和 STI 不起作用):

在我意识到 CLI 和 STI 指令正确关闭了所有中断之后,不久之后我也意识到系统定时器和 RTC 时钟本身就是中断(参见Wikipedia - IRQ: x86 IRQs: Master PIC)。这就是时钟无法工作的原因——它会一直等待(一直等待),直到系统计时器开始更新。这显然从未发生过,因为更新的中断已关闭!

不幸的是,知道这一点并不能解决我的原子性问题:没有中断就无法读取系统时钟。但是,开启中断并不能确保原子性。

我听说有一些方法可以屏蔽一些中断,但不是全部。我想知道如何屏蔽除 0 和 8 之外的所有中断(参见维基百科链接)。我想在Wait_Clk_Ticks函数中实现这个。


这让我想到了这段代码——这段代码在加载到引导设备(在我的例子中是软盘)上时会显示“下雨滴”的效果。一个 2 像素长的红色部分将在屏幕下方缓慢移动,然后在顶部重新开始。这是它应该做的。

但是,有3个问题,都与时钟有关(我相信):

  • 当一个键被按下时,电脑会发出令人讨厌的哔哔声,雨滴会慢慢流走。我相信这是因为按下一个键会导致中断,这会延迟定时器功能。

  • 计时器没有原子性。

  • 片刻之后,雨滴将完全停止(可能这是导致问题的中断)

我该如何解决这些问题?


编辑:我添加了 Set_IRQ_Mask 函数。但是现在我的操作系统在启动时挂起。

编辑 2:已修复,请参阅答案。

[BITS 16]                   ; 16 bit code
[ORG 0x7C00]                    ; Start to load at 0x7c00

; OS to create 'falling raindrop' effect

jmp Code_Start

Set_IRQ_Mask:       ; see http://wiki.osdev.org/8259_PIC and http://cs.smith.edu/~thiebaut/ArtOfAssembly/CH17/CH17-3.html

    in al, 21h         ; Read existing bits.
    or al, 00010000b   ; Disable IRQ4 (serial port com1)
    out 21h, al

    mov al, 0x20    ; Send end of interrupt signal
    out 0x20, al

    ret


Wait_Clk_Ticks:

    ; MASK CERTAIN INTERRUPTS

    push ax     ; Store current values.
    push bx

    mov ax, 0   ; Reset 'ds' (destination pointer).
    mov ds, ax
    mov bx, [46Ch]  ; Tick status is at 0:46Ch.
            ; Store into 'bx'.
    _WCT_Get_Tick:  ; Gets new tick.
    mov ax, [46Ch]  ; Update current time into 'ax'.

    cmp ax, bx  ; If 'current' == 'older' ticks,
    je _WCT_Get_Tick
            ; Then clock tick isn't over: reset.
    mov bx, ax  ; If the clock tick is over,
            ; put 'current' into 'older'.
    sub cx, 1   ; Decrement number of ticks till exit.
    jnz _WCT_Get_Tick;
            ; If 'cx' (ticks till exit) is zero,

    pop bx      ; Restore current values.
    pop ax

    ; UNMASK CERTAIN INTERRUPTS

    ret     ; Return.

Set_Video_Mode:
    mov ax, 13h                 ; VGA mode (320x200) 256 color
    int 10h                 ; sets vga mode SEE: http://www.wagemakers.be/english/doc/vga
    ret         

Write_Pixel:
    push ax                 ; push variables used here onto the stack
    push bx
    push cx
    mov cx, 320             ; puts 320 (how many rows there are) into a register
    mul cx                  ; multiplies 320 by AX (# of rows) and stores into AX

    add ax, bx              ; adds rows and cols to make the location, puts into ax
    mov si, ax              ; move ax into si
    mov bx, 0a000h              ; this puts 0a000h (starting register) as the beginning
    mov es, bx              ; move bx into es
    pop cx
    mov word [es:si], cx            ; move the color attribute (cx) into the memory location es+si
    pop bx                  ; restore variables from stack
    pop ax
    ret







Code_Start:
    mov bx, 40              ; work on COLUMN 40 solely
    call Set_Video_Mode
    call Set_IRQ_Mask

Reset:
    mov ax, 0               ; Reset row to 0
Loop:
    mov cx, 40              ; Write head in in RED (40)
    call Write_Pixel

    cmp ax, 0               ; Are we at row 1?
    je Next_Zero                ; Go to special conditions

    cmp ax, 1               ; Are we at row 2?
    je Next_One             ; Go to special conditions

    jmp Next_Norm               ; Otherwise, no special conditions

Next_Zero:                  ; Make the cover spot 197 if the current dot is 0
    push ax
    mov ax, 197
    jmp Cover_Black

Next_One:                   ; Make the cover spot 198 if the current dot is 1
    push ax
    mov ax, 198
    jmp Cover_Black

Next_Norm:                  ; Otherwise, make the cover spot 'ax - 2'
    push ax
    sub ax, 2

Cover_Black:                    ; Set color to black
    mov cx, 0
    call Write_Pixel
    pop ax                  ; Restore AX to the RED location.

    mov cx, 1               ; Set to wait for a clock tick
    call Wait_Clk_Ticks

    inc ax                  ; Increment the row
    cmp ax, 199             ; If pixel has reached the bottom of the screen, reset AX
    je Reset
    jmp Loop                ; Otherwise, continue downwards.


End:
    jmp $                   ; Run this line over and over again- stops excecution.

times 510-($-$$) db 0       ; Fill the rest of the 512 byte sector with zeros
dw 0xAA55               ; Boot magic number
4

4 回答 4

1

...好吧,似乎修复是ret在函数之后添加Set_IRQ_Mask...

那一行调试了 5 个小时…… :) 感谢您的帮助!

于 2013-07-04T02:35:14.233 回答
1

假设您还没有尝试处理任何多处理内容,您可以对8259 可编程中断控制器进行编程以屏蔽某些 IRQ。

于 2013-06-30T03:52:20.887 回答
0

您根本不需要为您正在做的事情禁用中断。我不确定你认为什么需要是原子的,但唯一需要以原子方式完成的是内存中滴答计数器的 16 位读取。由于对齐的 16 位读取保证是原子的,并且由于滴答计数器已适当对齐,因此您无需禁用中断。即使不能保证(例如,计数器位于奇数地址),您需要做的就是禁用执行读取的指令的中断。

以下是我将如何实现您的Wait_Clk_Ticks功能:

Wait_Clk_Tics:
    push ds
    push ax
    push bx

    xor  ax, ax
    mov  ds, ax
    ; cli
    mov  ax, [0x46c]
    ; sti
.loop:
    hlt
    ; cli
    mov  bx, [0x46c]
    ; sti
    sub  bx, ax
    cmp  bx, cx
    jb   .loop

    pop  bx
    pop  ax
    pop  ds
    ret

如果定时器计数器没有适当对齐,我已经展示了 CLI/STI 指令的去向。我还简化了代码,因此它不计算计数器的变化,它只是从其原始值中减去计数器的当前值。最后,我插入了 HLT 指令。这将导致 CPU 等待中断。在相对现代的 CPU 上,这也会导致处理器进入较低的功耗状态,并且应该对功耗和 CPU 风扇产生的噪音产生显着影响。

于 2016-11-09T23:02:51.687 回答
-1
or al, 00010000b   ; Disable IRQ4 (serial port com1)

上一行不像你的那样正确,或者不会屏蔽串口 com1!!!

and al, 11101111b   
于 2016-10-14T05:48:05.257 回答