Multitasking im x86 Real Mode



  • Hallo,

    um ein bisschen in die Materie der Betriebssysteme einzusteigen, habe ich versucht ein extrem primitives Multitasking im x86 Real Mode zu realisieren.

    Konkret wechsle ich im Timer Interrupt zwischen dem Task1 (=main) und Task2 (=thread). Der Stack, der vom Interrupt aufgebaut wird, enthält Infos zu IP und SP. Genau diese Werte passe ich dem entsprechenden Task an.
    Den iret Befehl nutze ich, um dann wirklich den Task zu wechseln, indem von nun an ein anderer IP und SP gesetzt ist.

    Das ganze habe ich maximal einfach gehalten, ich speichere mir keine Register mit ab, kann nur zwischen 2 Tasks hin und her wechseln und mir ist klar, dass gewisse Dinge im PM einfacher gingen. Allerdings hat es mir geholfen, Multitasking mal praktisch umzusetzen, um die Dinge möglichst im Detail zu verstehen.

    Der Code funktioniert wie gewünscht (getestet mit bochs), Task1 schreibt M auf die Konsole, Task2 T, und der Interrupt Handler schließlich H, so kann man das Multitasking schön verfolgen.
    Ich stell den Code hier mal rein, denn ich hab ich Gegenzug von Euren tollen Tutorials auch schon einiges lernen dürfen, so hilft vielleicht mein code auch dem einen oder anderen Einsteiger wie mir.
    Falls euch Fehler auffallen, sagt Bescheid. Auch sonstiges Feedback ist willkommen!

    ; Simples Multitasking im x86 Real Mode
    ;======================================
    ;
    ; 1. Installieren eines Interrupt Handlers
    ;    für den PIT
    ; 2. Der Task "main" führt eine Endlosschleife aus
    ;    und gibt am Bildschirm "M" aus
    ; 3. Der Task "thread" führt eine Endlosschleife aus
    ;    und gibt am Bildschirm "T" aus
    ; 4. Der Interrupt Handler hat einen Zähler, und wechselt 
    ;    beim Zählerstand 0 und 128 den Task
    ;    Bei jedem Aufruf des Handlers wir "H" ausgegeben
    ; 5. switch_taskx speichert tasky und fügt taskx ein,
    ;    schliesslich erhält durch iret der andere 
    ;    Task die CPU
    ; 6. Wichtige Adressen des 0. Segments
    ;    0x0000: Segmentbeginn
    ;    0x7C00: main, Kernel wird von BIOS hier gestartet
    ;    0x9c00: main stack
    ;    0x8c00: thread1 stack
    ; 7. Stack beim Eintritt in Interrupt Handler
    ;    IP: SP+0
    ;    CS: SP+2
    ;    FLAGS: SP+4
    ;    SP: SP+6
    ;    SS: SP+8
    
    %define SEGMENT_NR 	0
    %define START_OFFSET 	0x7C00
    %define TASK1_STACK 	0x9c00
    %define TASK2_STACK 	0x8c00
    %define TASK2_FLAGS	0x200
    %define PIT_IRV_NR	0x8
    %define BIOS_VIDEO	0x10
    %define BIOS_OUTPUT	0x0E
    %define BOOT_SIG	0xAA55
    %define EOI_BYTE	0x20
    %define EOI_PORT	0x20
    
    org START_OFFSET
    
    ;-------------------------------------
    main:
    	; ax enthält vorerst 0 Wert 
    	mov ax,SEGMENT_NR
    
    	; stack vorbereiten
    	mov ss,ax
    	mov sp,TASK1_STACK
    
    	; IVT, Eintrag 8 (PIT) setzen auf den Handler pit_handler
    	cli
    	mov gs,ax
    	mov bx,PIT_IRV_NR
    	shl bx,2 ;bx=bx*4
    	mov word [gs:bx], pit_handler
    	add bx,2
    	mov word [gs:bx], ax
    	sti
    
    	; Endlosschleife in main
    	; gibt sich am Bildschirm mit "M" aus
    	loop_inf:
    		mov bl,'M'
    		call showAction
    		jmp loop_inf
    	end_loop_inf:
    end_main:
    
    ;-------------------------------------
    ; Endlosschleife in thread
    ; gibt sich am Bildschirm mit "T" aus
    thread:
    	mov bl,'T'
    	call showAction
    	jmp thread
    end_thread:
    
    ;-------------------------------------
    ; Funktion zum Ausgeben eines Zeichens
    ; das Zeichen wird im Register bl erwartet
    showAction:
    	mov ah, BIOS_OUTPUT
    	mov al, bl
    	int BIOS_VIDEO
    	ret
    end_showAction:
    
    ;-------------------------------------
    ; Interrupt Handler für IRQ=0 (PIT)
    ; wird verwendet um einen Context Switch zwischen
    ; den Tasks main und thread durchzuführen
    pit_handler:
    	; am Bildschrim ausgeben, dass Handler 
    	; aufgerufen wurde
    	mov bl,'H'
    	call showAction
    
    	; end of interrupt dem PIC mitteilen
    	mov al, EOI_BYTE
    	out EOI_PORT, al 
    
    	; ctrB++, läuft nach 256 Werten über
    	add byte [ctrB],1
    	mov byte al,[ctrB]
    	; ctr==128, Task2 (thread) starten
    	cmp al,128
    	je switch_task2
    	; ctr==0, für Task1 (loop_inf) starten
    	cmp al,0
    	je switch_task1
    	; derzeit kein Task Switch nötig, raus hier!
    	iret
    
    	;Aufbau des Stacks siehe oben, Punkt 6.	
    	; Task2 speichern, Task1 einfügen
    	switch_task1:
    		;task2 speichern
    		;ip
    		mov bx,sp
    		mov ax,[bx+0]
    		mov bx,task2+0
    		mov [bx],ax
    		;flags
    		mov bx,sp
    		mov ax,[bx+4]
    		mov bx,task2+2
    		mov [bx],ax
    		;sp
    		mov bx,sp
    		mov ax,[bx+6]
    		mov bx,task2+4
    		mov [bx],ax
    
    		;task1 einfügen
    		;ip
    		mov ax,[task1+0]
    		mov bx,sp
    		mov [bx],ax
    
    		;flags
    		mov ax,[task1+2]
    		mov bx,sp
    		mov [bx+4],ax
    
    		;sp
    		mov ax,[task1+4]
    		mov bx,sp
    		mov [bx+6],ax
    
    		;interrupt verlassen
    		iret
    	end_switch_task1:
    
    	; Task1 speichern, Task2 einfügen
    	switch_task2:
    		;task1 speichern
    		;ip
    		mov bx,sp
    		mov ax,[bx+0]
    		mov bx,task1+0
    		mov [bx],ax
    		;flags
    		mov bx,sp
    		mov ax,[bx+4]
    		mov bx,task1+2
    		mov [bx],ax
    		;sp
    		mov bx,sp
    		mov ax,[bx+6]
    		mov bx,task1+4
    		mov [bx],ax
    
    		;task2 einfügen
    		;ip
    		mov ax,[task2+0]
    		mov bx,sp
    		mov [bx],ax
    
    		;flags
    		mov ax,[task2+2]
    		mov bx,sp
    		mov [bx+4],ax
    
    		;sp
    		mov ax,[task2+4]
    		mov bx,sp
    		mov [bx+6],ax
    
    		;interrupt verlassen
    		iret
    	end_switch_task2:
    
    end_pit_handler:
    
    ;-------------------------------------
    ; zähler für aktuellen task
    ctrB	db 0
    
    ; speichern des task zustandes: IP, Flags, SP
    task1	dw loop_inf,0,0 
    task2	dw thread,TASK2_FLAGS,TASK2_STACK
    
    ;-------------------------------------
    ; erster Sektor bis auf 512 byte auffüllen
    ; Boot Signatur 55AA ganz am Ende
    times 510-($-$$) hlt
    dw BOOT_SIG
    

  • Mod

    MT in RM, das ist mal was anderes. Danke für deinen Beitrag! 🙂


Log in to reply