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 11
Device 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