Blue Ice
Blue Ice

Reputation: 7930

How to mask/block certain IRQs

Still working on an x86 NASM assembly OS.

Followup question to my last one (CLI and STI are not working):

After I realized that the CLI and STI instructions were turning all of interrupts off correctly, I also realized shortly afterward that the System Timer & RTC clock are interrupts themselves (see Wikipedia - IRQ : x86 IRQs : Master PIC). This was why the clock would not function- it would wait and wait (and wait) forever, until the System timer got around to updating. This obviously never happened, because the Interrupt for the update was off!

Unfortunately, knowing this does not solve my atomicity problem: the system clock cannot be read without interrupts on. However, having interrupts on will not ensure atomicity.

I have heard that there are ways to mask some interrupts, but not all of them. I would like to know how to mask all of the interrupts besides 0 and 8 (see wikipedia link). I want to implement this in the Wait_Clk_Ticks function.


Which brings me to this code- this code, when loaded onto the boot device (floppy disk in my case) will display a "falling raindrop" effect. A red, 2 pixel long section will move slowly down the screen, then restart at the top. Which is what it is supposed to do.

However, there are 3 problems, all of which are related to the clock (I believe):

How can I fix these issues?


EDIT: I have added the Set_IRQ_Mask function. However now my os hangs on startup.

EDIT 2: Fixed, see answer.

[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

Upvotes: 0

Views: 1687

Answers (4)

Ross Ridge
Ross Ridge

Reputation: 39651

You don't need to disable interrupts at all for what you're doing. I'm not sure what you think needs to be atomic, but the only thing that does need to be done atomically is the 16-bit read of the tick counter in memory. Since aligned 16-bit reads are guaranteed to be atomic, and since the tick counter is suitably aligned you don't need to disable interrupts. Even if it wasn't guaranteed (eg. the counter was located at an odd address) all you would need to do is disable interrupts for the instructions that perform the reads.

Here's how I would implement your Wait_Clk_Ticks function:

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

I've shown where the CLI/STI instructions would go if timer counter wasn't suitably aligned. I've also simplified the code so it doesn't count counter changes, it just subtracts the current value of the counter from its original value. Finally I've inserted a HLT instruction. This will cause the CPU to wait for an interrupt. On a relatively modern CPU this will also cause the processor to enter a lower power state and should have noticeable effect power usage and the noise generated by the CPU fan.

Upvotes: 0

user7017402
user7017402

Reputation: 1

or al, 00010000b   ; Disable IRQ4 (serial port com1)

The previous line is not as correct as yours or will NOT mask off the serial port com1!!!

and al, 11101111b   

Upvotes: -1

Blue Ice
Blue Ice

Reputation: 7930

...Well, it seems that the fix was to add ret after the Set_IRQ_Mask function...

That was 5 hours of debugging for that one line... :) Thanks for the help though!

Upvotes: 1

Drew McGowen
Drew McGowen

Reputation: 11716

Assuming you're not trying to deal with any multiprocessing stuff yet, you can program the 8259 Programmable Interrupt Controller to mask certain IRQs.

Upvotes: 1

Related Questions