diff --git a/.gitignore b/.gitignore index b03b847..cde4e12 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,9 @@ build/ .vscode .vscode/tasks.json +build-elf.bat +build.bat +build.sh +run.bat +.gdbinit +.gdb_history diff --git a/src/bda.asm b/src/bda.asm index 59fa7be..8eddf60 100644 --- a/src/bda.asm +++ b/src/bda.asm @@ -36,15 +36,13 @@ %define BDA_VIDEO_BUF_SIZE 0x004C %define BDA_VIDEO_OFS_PAGE 0x004E +; keyboard related information +%define BDA_KBD_HEAD 0x0080 ; byte +%define BDA_KBD_TAIL 0x0081 ; byte +%define BDA_KBD_FLAGS 0x0082 ; byte: bit0=shift, bit1=ctrl, bit2=alt, bit3=caps, bit4=ext(E0) +%define BDA_KBD_BUF 0x0090 ; buffer circulaire (par ex 32 entrées *2 = 64 bytes) -; 40:50 8 words Cursor position of pages 1-8, high order byte=row low order byte=column; -; changing this data isn't reflected immediately on the display -; 40:60 byte Cursor ending (bottom) scan line (don't modify) -; 40:61 byte Cursor starting (top) scan line (don't modify) -; 40:62 byte Active display page number -; 40:63 word Base port address for active 6845 CRT controller 3B4h = mono, 3D4h = color -; 40:65 byte 6845 CRT mode control register value (port 3x8h) ; EGA/VGA values emulate those of the MDA/CGA -; 40:66 byte CGA current color palette mask setting (port 3d9h) ; EGA and VGA values emulate the CGA +%define KBD_BUF_MASK 0x1F ; 32 entrées => index 0..31 ; ; information relative à la souris @@ -71,3 +69,19 @@ %define BDA_CURSOR_SAVED 0x001A ; flag if image saved %define BDA_CURSOR_PTR 0x001B ; pointeur vers l'image du curseur %define BDA_CURSOR_BG 0x0020 ; 48 bytes max (3*16) + + + +; ----------------------------------------------------------------------------------- +; PC components I/O ports +; ----------------------------------------------------------------------------------- + +; controleur clavier/souris i8042 (AT-PS/2) +%define PS2_PORT_BUFFER 0x60 +%define PS2_PORT_CTRL 0x64 + +; PIC 8259 ports (Programmable Interrupt Controller) +%define i8259_MASTER_CMD 0x20 +%define i8259_MASTER_DATA 0x21 +%define i8259_SLAVE_CMD 0xA0 +%define i8259_SLAVE_DATA 0xA1 \ No newline at end of file diff --git a/src/boot.asm b/src/boot.asm index e993c23..99058ad 100644 --- a/src/boot.asm +++ b/src/boot.asm @@ -50,6 +50,7 @@ bits 16 %include "./drivers/debug.asm" %include "./drivers/gfx_cgam.asm" %include "./drivers/mouse_ps2.asm" +%include "./drivers/keyboard_ps2.asm" %include "./services/generic.asm" err_vganok db 'VGA Not Initialized',0 @@ -72,6 +73,9 @@ reset: ; installé une table d'interruption "dummy" call ivt_setup + + ; initialisation des PIC 8259A + call pic_init sti ; load other Rom diff --git a/src/drivers/keyboard_ps2.asm b/src/drivers/keyboard_ps2.asm new file mode 100644 index 0000000..81c352f --- /dev/null +++ b/src/drivers/keyboard_ps2.asm @@ -0,0 +1,339 @@ +; ============================================================================= +; Project : Custom BIOS / ROM +; File : keyboard_ps2.asm +; Author : frater +; Created : 06 jan 2026 +; +; License : GNU General Public License v3.0 or later (GPL-3.0+) +; +; This program is free software: you can redistribute it and/or modify +; it under the terms of the GNU General Public License as published by +; the Free Software Foundation, either version 3 of the License, or +; (at your option) any later version. +; +; This program is distributed in the hope that it will be useful, +; but WITHOUT ANY WARRANTY; without even the implied warranty of +; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +; GNU General Public License for more details. +; +; You should have received a copy of the GNU General Public License +; along with this program. If not, see . +; +; ============================================================================= + +; ------------------------------- +; scancode set 1 -> ASCII +; index = scancode (0..0x7F) +; 0 = non imprimable +; ------------------------------- +kbd_map_nomod: + db 0, + db 27 ; 01 ESC + db '1','2','3','4','5','6','7','8','9','0','-','=' + db 8 ; 0E Backspace + db 9 ; 0F Tab + db 'q','w','e','r','t','y','u','i','o','p','[',']' + db 13 ; 1C Enter + db 0 ; 1D Ctrl + db 'a','s','d','f','g','h','j','k','l',';','\'', '`' + db 0 ; 2A LShift + db '\\','z','x','c','v','b','n','m',',','.','/' + db 0 ; 36 RShift + db '*' ; 37 keypad * + db 0 ; 38 Alt + db ' ' ; 39 Space + times (0x80-($-kbd_map_nomod)) db 0 + +kbd_map_shift: + db 0 + db 27 + db '!','@','#','$','%','^','&','*','(',')','_','+' + db 8 + db 9 + db 'Q','W','E','R','T','Y','U','I','O','P','{','}' + db 13 + db 0 + db 'A','S','D','F','G','H','J','K','L',':','"','~' + db 0 + db '|','Z','X','C','V','B','N','M','<','>','?' + db 0 + db '*' + db 0 + db ' ' + times (0x80-($-kbd_map_shift)) db 0 + +; ----------------------------------------------------------- +; Vérifie si une touche est disponible dans le buffer clavier +; Retour: CF=0 si une touche est dispo, CF=1 sinon +; ----------------------------------------------------------- +kbd_kbhit: + push ds + mov ax, BDA_SEGMENT + mov ds, ax + mov al, [BDA_KBD_HEAD] + cmp al, [BDA_KBD_TAIL] + pop ds + je .empty + clc + ret +.empty: + stc + ret + +; ----------------------------------------------------------- +; Lit un caractère du buffer clavier (make+ascii) +; Retour: CF=0 si une touche est dispo, AX=word (scancode<<8 | ascii) +; CF=1 si aucune touche disponible +; ----------------------------------------------------------- +kbd_poll: + push ds + mov ax, BDA_SEGMENT + mov ds, ax + call kbd_buf_pop + pop ds + ret + +; ----------------------------------------------------------- +; Lit un caractère du buffer clavier (make+ascii) +; Bloque jusqu'à ce qu'une touche soit disponible +; Retour: AX=word (scancode<<8 | ascii) +; ----------------------------------------------------------- +kbd_getch: +.wait: + call kbd_poll + jc .wait + ret + +; ----------------------------------------------------------- +; Met à jour les modifiers sur make code +; ----------------------------------------------------------- +kbd_init: + push ds + push ax + + mov ax, BDA_SEGMENT + mov ds, ax + mov byte [BDA_KBD_HEAD], 0 + mov byte [BDA_KBD_TAIL], 0 + mov byte [BDA_KBD_FLAGS], 0 + + ; installer INT 09h = IRQ1 + cli + xor ax, ax + mov ds, ax + mov si, 0x09*4 + mov word [ds:si], kbd_isr + mov ax, cs + mov word [ds:si+2], ax + sti + + ; unmask IRQ1 (master bit1 = 0) + in al, i8259_MASTER_DATA + and al, 0xFD + out i8259_MASTER_DATA, al + + pop ax + pop ds + ret + +; ----------------------------------------------------------- +; Met à jour les modifiers sur make code +; Entrée: AL = scancode (sans bit7) +; ----------------------------------------------------------- +kbd_buf_push: + ; In: AX = word à pousser + ; DS = 0x40 + push bx + push di + + mov bl, [BDA_KBD_HEAD] + mov bh, [BDA_KBD_TAIL] + + ; next_head = (head+1) & MASK + mov dl, bl + inc dl + and dl, KBD_BUF_MASK + + ; si next_head == tail => buffer plein => drop + cmp dl, bh + je .full + + ; écrire à BUF[head] + ; offset = BDA_KBD_BUF + head*2 + xor bh, bh + mov di, bx + shl di, 1 + add di, BDA_KBD_BUF + mov [di], ax + + ; head = next_head + mov [BDA_KBD_HEAD], dl +.full: + pop di + pop bx + ret + +; ----------------------------------------------------------- +; pop un word du buffer clavier (circulaire) +; ----------------------------------------------------------- +kbd_buf_pop: + ; Out: CF=1 si vide, sinon AX=word, CF=0 + ; DS=0x40 + push bx + push di + + mov bl, [BDA_KBD_HEAD] + mov bh, [BDA_KBD_TAIL] + cmp bl, bh + je .empty + + ; lire BUF[tail] + xor bh, bh + mov di, bx + shl di, 1 + add di, BDA_KBD_BUF + mov ax, [di] + + ; tail = (tail+1) & MASK + inc bh + and bh, KBD_BUF_MASK + mov [BDA_KBD_TAIL], bh + + clc + pop di + pop bx + ret +.empty: + stc + pop di + pop bx + ret + +; ----------------------------------------------------------- +; Keyboard ISR (INT 09h) +; ----------------------------------------------------------- +kbd_isr: + push ds + pusha + + mov ax, BDA_SEGMENT + mov ds, ax + + in al, PS2_PORT_BUFFER + cmp al, 0xE0 + jne .sc + or byte [BDA_KBD_FLAGS], 0x10 + jmp .eoi + +.sc: + mov bl, al + + test bl, 0x80 + jz .make + and bl, 0x7F + mov al, bl + call kbd_update_modifiers_break + jmp .clear_eoi + +.make: + mov al, bl + call kbd_update_modifiers_make + + ; filtrer modifiers (ne push pas) + cmp bl, 0x2A + je .clear_eoi + cmp bl, 0x36 + je .clear_eoi + cmp bl, 0x1D + je .clear_eoi + cmp bl, 0x38 + je .clear_eoi + cmp bl, 0x3A + je .clear_eoi + + ; scancode -> ASCII + mov al, bl + xor bh, bh + test byte [BDA_KBD_FLAGS], 0x01 + jz .nomod + mov si, kbd_map_shift + jmp .doxlat +.nomod: + mov si, kbd_map_nomod +.doxlat: + ; XLAT uses DS:BX, so set BX = scancode and DS points to table. + ; On ne veut pas changer DS=0x40. Donc on ne peut pas XLAT directement ici. + ; => on fait un accès indexé. + ; AL = [CS:SI + scancode] + push ds + mov ax, cs + mov ds, ax + xor ah, ah + mov di, si + add di, bx + mov al, [di] + pop ds + + ; AX = (scancode<<8) | ascii + mov ah, bl + call kbd_buf_push + +.clear_eoi: + and byte [BDA_KBD_FLAGS], 0xEF ; clear ext +.eoi: + mov al, i8259_MASTER_CMD + out i8259_MASTER_CMD, al + popa + pop ds + iret + +; ----------------------------------------------------------- +; Met à jour les modifiers sur make code +; Entrée: AL = scancode (sans bit7) +; ----------------------------------------------------------- +kbd_update_modifiers_make: + ; AL = scancode (make) + cmp al, 0x2A + je .shift_on + cmp al, 0x36 + je .shift_on + cmp al, 0x1D + je .ctrl_on + cmp al, 0x38 + je .alt_on + cmp al, 0x3A + je .caps_toggle + ret +.shift_on: + or byte [BDA_KBD_FLAGS], 0x01 + ret +.ctrl_on: + or byte [BDA_KBD_FLAGS], 0x02 + ret +.alt_on: + or byte [BDA_KBD_FLAGS], 0x04 + ret +.caps_toggle: + xor byte [BDA_KBD_FLAGS], 0x08 + ret + +kbd_update_modifiers_break: + ; AL = scancode (break) + cmp al, 0x2A + je .shift_off + cmp al, 0x36 + je .shift_off + cmp al, 0x1D + je .ctrl_off + cmp al, 0x38 + je .alt_off + ret +.shift_off: + and byte [BDA_KBD_FLAGS], 0xFE + ret +.ctrl_off: + and byte [BDA_KBD_FLAGS], 0xFD + ret +.alt_off: + and byte [BDA_KBD_FLAGS], 0xFB + ret diff --git a/src/drivers/mouse_ps2.asm b/src/drivers/mouse_ps2.asm index eef313b..f84ba8b 100644 --- a/src/drivers/mouse_ps2.asm +++ b/src/drivers/mouse_ps2.asm @@ -21,12 +21,6 @@ ; ; ============================================================================= -; -; controleur de souris via le i8042 (PC Classique) -; -%define PS2_PORT_BUFFER 0x60 -%define PS2_PORT_CTRL 0x64 - ; commandes %define MOUSE_CMD_RESET 0xFF %define MOUSE_CMD_DEFAULT 0xF6 @@ -173,10 +167,26 @@ mouse_init: mov word [ds:si], mouse_handler add si, 2 - mov word [ds:si], BDA_SEGMENT + mov ax, cs + mov word [ds:si], cs + +; test: aussi installer sur 0x2C (si PIC remappé base 0x28) +;mov si, 0x2C*4 +;xor ax, ax +;mov ds, ax +;mov word [ds:si], mouse_handler +;add si, 2 +;mov ax, cs +;mov word [ds:si], ax + sti +mov dx, 0x00E9 +mov al, '!' +out dx, al + + pop ax pop ds ret @@ -274,10 +284,10 @@ mouse_sendcmd: out PS2_PORT_CTRL, al call ps2_wait_ready_write - mov al, bl + mov al, bl out PS2_PORT_BUFFER, al - call ps2_wait_ready_read ; AL = réponse + call ps2_read ; AL = réponse cmp al, MOUSE_ACK jne .bad clc @@ -305,51 +315,70 @@ mouse_sendcmd: ; bit 7 = Y overflow ; ------------------------------------------------------------ mouse_handler: + pusha + push ds + + ; vider le byte qui a déclenché IRQ12 + in al, PS2_PORT_BUFFER + + mov dx, 0x00E9 ; debugcon + mov al, '*' + out dx, al ; imprime directement + + ; EOI PIC (slave puis master) + mov al, 0x20 + out 0xA0, al + out 0x20, al + + pop ds + popa + iret + + + + + ; sauvegarder tout les registres pusha + push ds ; use BDA segment mov ax,BDA_MOUSE_SEG mov ds,ax - in al, PS2_PORT_BUFFER ; lire octet souris + ; lire un octet depuis le contrôleur + ; et le stocker dans le buffer + in al, PS2_PORT_BUFFER ; lire octet souris mov byte [BDA_MOUSE_BUFFER + BDA_MOUSE_IDX], al inc byte [BDA_MOUSE_IDX] + ; vérifier si le packet est complet mov al, [DBA_MOUSE_PACKETLEN] - cmp [BDA_MOUSE_IDX], al + cmp byte [BDA_MOUSE_IDX], al jne .done + ; packet complet, on decode les données mov byte [BDA_MOUSE_IDX], 0 + mov al,'*' ; pour debug + call debug_putc - ; decode buffer manuallement - mov al, [BDA_MOUSE_BUFFER] + + mov al, [BDA_MOUSE_BUFFER] ; status mov [BDA_MOUSE_STATUS], al - mov al, [BDA_MOUSE_BUFFER+1] + mov al, [BDA_MOUSE_BUFFER+1] ; delta X cbw add [BDA_MOUSE_X], ax - mov al, [BDA_MOUSE_BUFFER+2] + mov al, [BDA_MOUSE_BUFFER+2] ; delta Y cbw sub [BDA_MOUSE_Y], ax ; si packetlen = 4, gérer la molette; experimental - mov al, [BDA_MOUSE_BUFFER+3] + mov al, [BDA_MOUSE_BUFFER+3] ; delta Wheel cbw add word [BDA_MOUSE_WHEEL], ax - ; debug: afficher les valeurs - mov si, BDA_MOUSE_BUFFER - mov cx, 4 -.debug_loop: - mov al, [si] - call debug_puthex8 - mov al, ' ' - call debug_putc - inc si - loop .debug_loop - ; clamp X cmp word [BDA_MOUSE_X], 0 jge .x_ok_low @@ -383,5 +412,6 @@ mouse_handler: out 0x20, al ; EOI PIC maître ; restaurer tous les registres + pop ds popa iret \ No newline at end of file diff --git a/src/services/generic.asm b/src/services/generic.asm index 0f3d3fd..27e6c01 100644 --- a/src/services/generic.asm +++ b/src/services/generic.asm @@ -20,8 +20,10 @@ ; ; ============================================================================= + + ; --------------------------------------------------------------------------- -; Détection RAM conventionnelle +; Détection RAM conventionnelle ; Teste la RAM de MEM_SEG_DEB -> MEM_SEG_END (0xA000, 640KB), par pas de 1KB. ; Méthode: sauvegarde 1 mot, écrit 2 patterns, relit, restaure. ; @@ -80,36 +82,36 @@ ram_setup: add ax, bx ; AX = taille conventionnelle totale en KB (approx. 0..640) ; DX = segment du premier bloc NON valide (top) ; DX est déjà positionné (segment courant) - + ; stocker l'information dans le BDA mov bx, BDA_SEGMENT mov ds, bx - mov [BDA_INFO_MEM_SIZE], ax + mov [BDA_INFO_MEM_SIZE], ax mov [BDA_INFO_MEM_SEG],dx - + ret - + ; --------------------------------------------------------------------------- ; Détection les ROM supplementaires ; Teste la RAM de 0xC000 jusqu’à 0xE000, par pas de 2KB. ; ; Si une ROM est détectée, le code de la ROM sera appelé. -; --------------------------------------------------------------------------- -setup_load_rom: +; --------------------------------------------------------------------------- +setup_load_rom: mov bx, 0xC000 .scanloop: mov ds, bx cmp word[0x0000],0xAA55 ; ROM signature jne .norom - + ; Debug mov al,'.' call debug_putc - + ; appel le code (en 0x0003) de la ROM push cs push word .norom ; address de retour - + push ds push 0x0003 retf ; call far ds:0x0003 @@ -117,7 +119,7 @@ setup_load_rom: add bx, 0x80 ; add 2kb cmp bx, 0xE000 jbe .scanloop -.end: +.end: ret ; --------------------------------------------------------------------------- @@ -125,7 +127,7 @@ setup_load_rom: ; ; Si le vecteur n'a pas été modifiée, on considère que la ROM video n'a pas ; été chargée. -; --------------------------------------------------------------------------- +; --------------------------------------------------------------------------- setup_check_vga: ; vérification de l'offset 0x0000:0x0040 qui DOIT etre différent de @default_isr xor ax,ax @@ -134,22 +136,22 @@ setup_check_vga: mov bx, [es:0x0040] ; BX = offset INT10 mov cx, [es:0x0042] ; CX = segment INT10 mov ax, cs - + ; Comparer avec default_isr (offset) et CS (segment) mov dx, default_isr ; DX = offset default_isr (dans notre CS) cmp bx, dx jne .init_ok ; vecteur different, bios initialisé cmp cx, ax jne .init_ok - + mov ax,cs ; vecteur identique, sans doute pas d'initialisation mov ds,ax mov si, err_vganok call debug_puts .init_ok: - ret - + ret + ; --------------------------------------------------------------------------- ; IVT install (8088, mode réel) - remplit les 256 vecteurs avec un handler IRET @@ -172,7 +174,7 @@ ivt_setup: loop .fill ; fin de la table d'interrupt ret - + ; --------------------------------------------------------------------------- ; IVT install (8088, mode réel) - installe un handler a une interrupt calculée ; @@ -183,10 +185,10 @@ ivt_setup: ivt_setvector: push es push di - + shl ax,2 ; ax=id *4 mov di,ax - + xor ax,ax mov es, ax ; ES = 0000h -> base IVT @@ -199,7 +201,53 @@ ivt_setvector: pop di pop es - ret + ret + +; ------------------------------------------------------------ +; Initialise les PIC 8259 (PC/AT): +; - Master offset 0x08 +; - Slave offset 0x70 +; - Cascade: IRQ2 +; https://wiki.nox-rhea.org/back2root/ibm-pc-ms-dos/hardware/8259 +; ------------------------------------------------------------ +pic_init: + push ax + + ; ICW1: init + ICW4 + mov al, 0x11 ; 000010001b + out i8259_MASTER_CMD, al + out i8259_SLAVE_CMD, al + + ; ICW2: vector offsets + mov al, 0x08 + out i8259_MASTER_DATA, al + mov al, 0x70 + out i8259_SLAVE_DATA, al + + ; ICW3: wiring + mov al, 0x04 ; master: slave on IRQ2: 0100b + out i8259_MASTER_DATA, al + mov al, 0x02 ; slave: cascade identity 2 + out i8259_SLAVE_DATA, al + + ; ICW4: 8086 mode + mov al, 0x01 + out i8259_MASTER_DATA, al + out i8259_SLAVE_DATA, al + + ; Masques: unmask IRQ2 (master) et IRQ12 (slave) + in al, i8259_MASTER_DATA + and al, 0xFB ; clear bit2 + out i8259_MASTER_DATA, al + + in al, i8259_SLAVE_DATA + and al, 0xEF ; clear bit4 + out i8259_SLAVE_DATA, al + + pop ax + ret + + ; --------------------------------------------------------------------------- ; Handler par défaut: fait juste IRET