10.1 Conspect (ru)
Прерывания в отличие от исключений имеют внешний источник. Происходят потому что-то произошло снаружи. Нужно чтобы не заниматься ”тупым” поллингом. Прерывания асинхронны. Обрабатываются тем же обработчиком что и исключения.
Архитектура вашей машины должна быть так устроена чтобы в какой бы момент не произошло прерывание, переделывать инструкцию которая только что выполнялась было безопасно.
Первая проблема – понять что прерывание произошло.
Вторая проблема - поскольку прерывания могут произойти одновременно, нужно иметь возможность обработать 2,3 и тд. прерываний.
За обработку прерываний отвечает регистр с0 .
Когда происходят прерывания, прерывание имеет номер 0.
Все регистры должны быть сохранены (включая $at), кроме $k0 и $k1.
Прерывания должны быть отключены как можно скорее (для предотвращения параллельной обработки). Не полагайтесь на $sp (он может быть поврежден). Обычно предоставляется отдельный стек ядра. Исключения различны для обработки. При обработке прерывания «код исключения» поле регистра Cause C0 равен 0.
Прерывание должно быть обработано как можно быстрее, чем меньше поток выполнения остается в пространстве ядра, тем лучше.
Чтобы другие устройства работали нормально перед выходом сделать: Чистое поле Cause, Восстановить все регистры, Включить прерывания.
Для этого рассмотрим код:
.data
msg: .asciiz "Tick\n"
.text
mfc0 $a0 $12 # read from the status register
ori $a0 0xff11 # enable all interrupts
mtc0 $a0 $12 # write back to the status register
# Byte value at address 0xFFFF0012 : command row number of hexadecimal keyboard (bit 0 to 3) and enable keyboard interrupt (bit 7)
li $t0 0x80
sb $t0 0xffff0012
loop: li $v0 4
la $a0 msg
syscall
li $v0 32
li $a0 1000
syscall
j loopВ марсе нельзя генерировать прерывания по таймеру, но можно генерировать прерывание каждую 30 инструкцию. Код:
.macro push %reg
addiu $sp $sp -4
sw %reg ($sp)
.end_macro
.macro pop %reg
lw %reg ($sp)
addiu $sp $sp 4
.end_macro
.data
msg: .asciiz " tick\n"
.text
mfc0 $a0 $12 # read from the status register
ori $a0 0xff11 # enable all interrupts
mtc0 $a0 $12 # write back to the status register
# Byte value at address 0xFFFF0012 : command row number of hexadecimal keyboard (bit 0 to 3) and enable keyboard interrupt (bit 7)
li $t0 1
sb $t0 0xffff0013
li $t0 0
loop: li $v0 1
move $a0 $t0
syscall
li $v0 4
la $a0 msg
syscall
li $v0 32
li $a0 1000
syscall
addiu $t0 $t0 1
j loop
.macro keep %reg %addr
move $k0 %reg
sw $k0 %addr
.end_macro
.macro undo %reg %addr
lw $k0 %addr
move %reg $k0
.end_macro
.kdata
_at: .word 0 # keep $at
_sp: .word 0 # keep $sp
imsg: .asciiz "-!-"
.ktext 0x80000180
mfc0 $k0 $12 # !! disable interrupts
andi $k0 $k0 0xfffe # !!
mtc0 $k0 $12 # !!
keep $at _at # why not use "sw $at _at" ? :)
keep $sp _sp # user stack
li $sp 0x90100000 # MARS restriction; we can use stack from now
push $a0
push $v0
mfc0 $k0 $13 # Cause register
srl $a0 $k0 2 # Extract ExcCode Field
andi $a0 $a0 0x1f
bne $a0 $zero kexc # Exception Code is 0 for interrupts
kint: # Just mark the interrupt
li $v0 4
la $a0 imsg
syscall
b intret
kexc: # No exceptions in the program, but just in case of one
b exret
exret: mfc0 $v0 $14
addi $v0 $v0 4 # Return to next instruction
mtc0 $v0 $14
intret:
pop $v0
pop $a0
undo $sp _sp
undo $at _at
mfc0 $k0 $12 # Set Status register
ori $k0 0x01 # Interrupts enabled
mtc0 $k0 $12 # write back to status
mtc0 $zero $13 # clean Cause
eretЕсли засовывать какую-либо логику в ядро, Вы навсегда застрянете в этом ядре.
Keyboard and Display MMIO Simulator:
Консоль-это устройство, которое может вводить байты с клавиатуры и выводить байты на текстовый экран:
Когда устройство готово к trausmit / receive, соответствующий бит ready устанавливается как 1.
Когда RcC ready равен 0, клавиша не была нажата или нажатие клавиши еще не обработано консолью. Когда TxC ready равен 0, консоль не может выполнить вывод . Например, занята передачей предыдущего байта.
Устройство является (намеренно) медленным, поэтому часто неготовым.
Пример консольного пуллинга:
loop: lb $t0 0xffff0000 # check input ready
andi $t0 $t0 1 # is it?
beqz $t0 loop # to check again
lb $a0 0xffff0004 # read character
li $v0 11 # print it
syscall
b loopТакже рассмотрим сonsole interrupts.
li $a0 2
sw $a0 0xffff0000 # enable keyboard interrupt
li $a0 0
loop: beqz $a0 loop # infinite loop
beq $a0 0x1b done # finish when pressing ESC
li $v0 11 # print character stored by handler
syscall
li $a0 0 # make $a0 zer0 again
j loop
done: li $v0 10
syscall
.ktext 0x80000180 # THIS IS DIRTY!
lw $a0 0xffff0004 # store input to $a0Также рассмотрим более чистую реализацию: более или менее справедливый обработчик, программа большую часть времени делает бессмысленные вещи, но периодически проверяет, не было ли чего-то нажато на клавиатуре.
.text
.globl main
main:
mfc0 $a0 $12 # read from the status register
ori $a0 0xff11 # enable all interrupts
mtc0 $a0 $12 # write back to the status register
li $a0 2 # enable keyboard interrupt
sw $a0 0xffff0000
here:
jal sleep
lw $a0 ($gp) # print key stored in ($gp)
beqz $a0 here # no keypress
beq $a0 0x1b done # ESC terminates
li $v0 1
syscall
sw $zero ($gp)
j here
done: li $v0 10
syscall
.eqv ZZZ 100000
sleep: li $t0 ZZZ # Do nothing
tormo0: subi $t0 $t0 1
blez $t0 tormo1
j tormo0
tormo1: jr $ra
.ktext 0x80000180 # kernel code starts here
mfc0 $k0 $12 # !! disable interrupts
andi $k0 $k0 0xfffe # !!
mtc0 $k0 $12 # !!
move $k1 $at # save $at. User programs are not supposed to touch $k0 and $k1
sw $v0 s1 # We need to use these registers
sw $a0 s2 # not using the stack
mfc0 $k0 $13 # Cause register
srl $a0 $k0 2 # Extract ExcCode Field
andi $a0 $a0 0x1f
bne $a0 $zero kexc # Exception Code 0 is I/O. Only processing I/O here
lw $a0 0xffff0004 # get the input key
sw $a0 ($gp) # store key
li $a0 '.' # Show that we handled the interrupt
li $v0 11
syscall
j kdone
kexc: mfc0 $v0 $14 # No exceptions in the program, but just in case of one
addi $v0 $v0 4 # Return to next instruction
mtc0 $v0 $14
kdone:
lw $v0 s1 # Restore other registers
lw $a0 s2
move $at $k1 # Restore $at
mtc0 $zero $13
mfc0 $k0 $12 # Set Status register
ori $k0 0x01 # Interrupts enabled
mtc0 $k0 $12 # write back to status
eret
.kdata
s1: .word 10
s2: .word 11Device control:
Некоторые устройства, например принтеры, консоли, модемы и т. д. не могут полностью контролироваться специальными регистрами MMIO. Вместо этого они интерпретируют полученные данные. Эти данные называются» управляющими символами устройства " (или управляющей последовательностью).
Клавиатура и дисплей - симулятор:
байт выхода №12 - отсутствие контрольных данных - чистый экран
выходной байт 7-позиционирование курсора, управляющие данные: столбец (биты 31-20), строка (биты 19-8).
Показать “@” на экране:
.macro text %reg
wait: lb $a0 0xffff0008 # wait until output is ready
andi $a0 $a0 1
beqz $a0 wait # if not, wait again
move $a0 %reg
sw $a0 0xffff000c
.end_macro
.macro pos %x %y %c
li $a0 %x
sll $a0 $a0 20 # X-coordinate
li $a1 %y
sll $a1 $a1 8 # Y-coordinate
or $a0 $a0 $a1
ori $a1 $a0 7
text $a1 # cursor poitioning
li $a1 %c
text $a1 # character output
.end_macro
.text
li $s1 12
text $s1
pos 1 1 '@'
pos 6 2 '@'
pos 12 4 '@'
pos 15 6 '@'
nop
nop
nop
nop
nop