www.xbdev.net
xbdev - software development
Friday March 29, 2024
home | contact | Support | Assembly Language What every pc speaks..1010...

     
 

Assembly Language

What every pc speaks..1010...

 

Protected Mode - Using Keyboard!

by bkenwright@xbdev.net

 

 

Well without any real code, just enabling the PIC bit 1, to capture interrupts, we can have our computer catch interrupts from the keyboard!  Thats right, when a key is then pressed, this causes an interrupt to be issued.  So then in our interrupt routine, its just a matter of reading from port 60h, which is the keyboard chip, and read which key was pressed, if the key is pressed down or being released...then thats it!

 

 

 

Code Snippet
 

;First we enable our code so we get an interrupt each time a key is pressed!

; Enable Keyboard PIC chips 'ONLY', disable others:
;(Port 0x21 bit 1 is keyboard)
          mov     al, 0xFD
          mov     dx, 0x21
          out     dx, al      ; outb(0x21, ~0x01)

          mov     al, 0xFF
          mov     dx, 0xA1
          out     dx, al      ; outb(0xA1, ~0x00)

;....

;other code etc

;....

 

 

;Our interrupt routine, which is called each time we get an interrupt from the keyboard

intfunc:
 
pushad       ; Push all the standard registers onto the stack
push es      ; Push es register onto stack
;For each key pressed or released an interrupt (INT 09h) is issued by the 
;keyboard controller. When the interrupt is issued we can read the value of 
;the key pressed/released from the port 60h: if the 7th bit is set the key is 
;released, else is pressed. So we can register multiple keys pressions and 
;the sequence of the last n key pressed in this way: 

;Read the key from the keyboard controller
in al,0x60      ; al = inb(0x60)


mov cl, al              ; save the character we read from the keyboard
                        ; in cl
                        
;Tell to the controller that the key code is readed
       in al,0x61      ; al = inb(0x61)
       or al, 0x82     ; al |=0x82
         
       mov dx, 0x61
       out dx, al       ; outb(0x61, al)

       and al,0x7f      ;   al&=0x7F;
       mov dx, 0x61
       out dx, al       ; outb(0x61, al)


nop                     ; no operation (absolutely useless ;)
mov ax,0x10             ; Get our data segment (Global Descriptor Table)
mov es,ax
                           ; Use the character we got from the keyboard
                           ; and saved in cl, and put it on the screen.
                           
mov byte [es: 0xb8002], cl ; poke a character into the graphics output screen
;Whenever we finish an interrupt, we should tell our irq we've dealt with it
;or else it won't issue other interrupts.
;EOI for IRQ 0-7
mov    al, 0x20
        mov dx, 0x20
        out dx, al      ; outb(0x20, 0x20)

pop es
popad
iret

 

;...etc

 

 

 

Well here is the whole code, simple....but not....Its a whole piece of code, which takes us into 32 bit protected mode, and sets up the basic things, like Global Descriptor Table (GDT) and IDT etc and then we turn on the interrupt for the Keyboard.  Of course all the IDT (Interrupt Descriptor Table) Interrupts are all set to a single interrupt, so all the interrupts will of course call the same interrupt...(e.g. divide by zero etc).  But we have turned of all other interrupts and the code is really simple, so we know that the only interrupt will be from key presses....and then when a key is pressed we use the data that is passed to the interrupt to poke a character onto the screen.  Of course later on we'll see what that chacter is and desipher it and use it to display the correct character.  

What a whole piece of code looks like :)

 

Probably the most confusing part is the offsets and protected mode code...which gets us there...the rest of the keyboard code, which I put separately above isn't to bad to digest.

 

 

Keyboard Interrupt Code (Click Here Asm File)
; Run in dos (not under windows) and it will take us to 32 bit protected mode

[ORG 0x100]         ; Reserve 256 bytes for dos

[BITS 16]           ; Dos is 16 bits


; assemble using 'nasm' assemblaer

; C:>nasm asm_1.asm -o test.exe

jmp entry           ; Jump to the start of our code

msg1 db 'Where good to go..$';

jumpOffset:
    dd go_pm
    dw 0x08

entry:
 
; Display a message showing where alive!

mov dx, msg1        ; register dx=msg1
mov ah, 9           ; register ah=9 -- the print string function
int 21h             ; dos service interrupt .. looks at register ah to figure out what to do

; Thanks from Brendan, as we have to make sure our GDTR points to the actual
; memory address, add code location and dos 0x100 onto our loaded offset 

    mov eax,0
    mov ax,cs
    shl eax,4
    add [gdtr+2],eax
    add [idtr+2],eax         ; set idtr and gdtr so it points to the 'real' address in memory
    add [jumpOffset],eax     ; do the same for our 32 pm addr

    add [int_start], eax     ; Set the value of int_start (our simple interrupt function) so
                             ; it has the "physical" address
;We have one interrupt function at the bottom of the code:
;divide by zeros, interrupts, irq's ...all will call this same interrupt.
;Our main one will be intfunc, and its address is saved in memory location int_start
    
    mov eax, [int_start]
    mov word [idt_start], ax
    shr eax,16
    mov word [idt_start+6], ax

;Note that we are using this interrupt for testing...later on we can add different
;routines for the different interrupts.

;---------------------------------------------------------------------------
; Whats all this about?...
;---------------------------------------------------------------------------
; We loop through all 46 of the interrupt function addresses in our IDT and
; set them all to point to the same interupt function, which we need to
; correct because dos loads our code at some unknown address in memory and
; our IDT address values need to be the real memory address, not the offsets.

mov ecx, 46                                 ; Number of IDT Interupt Functions
mov ebx, 0                                  ; Start value for our loop counter
zloop:

    mov eax, [int_start]
    add ebx, 8
    mov word [idt_start + ebx], ax
    shr eax,16
    mov word [idt_start+6 + ebx], ax

      dec ecx     
      jne zloop                             ; if ecx is 0, end of loop
;---------------------------------------------------------------------------


  cli		    ; Clear or disable interrupts
  
  mov     al, 0x70
  mov     dx, 0x80
  out     dx, al      ; outb(0x80, 0x70) - disable NMI (Non Maskable Interupts)
          

  lgdt[gdtr]	    ; Load GDT
  
  lidt[idtr]       ; Load IDT
     
  mov eax,cr0	    ; The lsb of cr0 is the protected mode bit
  or al,0x01	    ; Set protected mode bit
  mov cr0,eax	    ; Mov modified word to the control register

jmp far dword [jumpOffset]  ;can't just use "jmp go_pm" as where in dos!


nop                 ; ignore - no operation opcodes :)
nop

;---------------------------------------------------------------------------
;                                 32 BIT
;---------------------------------------------------------------------------
; Once we reach here where in protected mode!  32 Bit!  Where not in
; the real world (mode) anymore :)
[BITS 32]
go_pm :

mov ax, 0x10        ; use our datasel selector ( alternatively mov ax, datasel )
mov ds, ax,
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax

mov esp, 0afffh    ; we need a simple stack if where calling functions!
                   ; Such as interupt functions etc, as the return address is
                   ; put on the stack remember!

mov word [es: 0xb8000],0x740 ; put a char to the screen!...yeahh!
                             ; '@' in the top left of the screen if we made it
                             ; here okay.

; Force a call to interrupt 4!
; Int 0x4           ; We call our interrupt 4 subroutine

;-------------------------------------------------------------------------
; Divide by Zero Warning
;-------------------------------------------------------------------------
; Just remember, when we do a divide by zero, and the interupt is called,
; the return address passed to the interrupt, is in fact the address of the
; line that caused the interrupt!  So if we just return from the interrupt
; it would just keep causing the interrupt over and over again.
;-------------------------------------------------------------------------
; Do a divide by 0 error, so we force a call to our interrupt 0
;  mov eax, 0
;  mov ebx, 0
;  div ebx            ; eax divided by ebx, and stored back in eax

mov byte [es: 0xb8008], "B" ; poke a character onto our screen buffer

nop
nop

;-------------------------------------------------------------------------
; Where not in basics anymore!
;-------------------------------------------------------------------------
; This is the line where we go from simple settups, to using the computers
; interal hardward, fiddling around with the irqs and pic (Programmable
; Interupt Controller) etc.
;-------------------------------------------------------------------------

; Disable all IRQs.
;-------------------------------------------------------------------------
disable_irqs:
          mov     al, 0xFF
          mov     dx, 0x21
          out     dx, al      ; outb(0x21, 0xFF)

          mov     al, 0xFF
          mov     dx, 0xA1
          out     dx, al      ; outb(0x21, 0xFF)
          
;-------------------------------------------------------------------------          
          mov byte [es: 0xb8020], "Q" ; poke a char onto screen (debug)
          

          mov     cl, 0x20                            ; PIC 1.
          mov     ch, 0x28                            ; PIC 2.
;-------------------------------------------------------------------------
; Remap PICs to:  CL = pic1  CH = pic2
;-------------------------------------------------------------------------
remap_pics:

     ; IWC1
     ;------
          mov     al, 0x11
          mov     dx, 0x20
          out     dx, al      ; outb(0x20, 0x11)

          mov     al, 0x11
          mov     dx, 0xA0
          out     dx, al      ; outb(0xA0, 0x11)
 
     ; IWC2
     ;------
          mov     al, cl
          mov     dx, 0x21
          out     dx, al      ; outb(0x21, pic1)

          mov     al, ch
          mov     dx, 0xA1
          out     dx, al      ; outb(0xA1, pic2)

     ; IWC3
     ;------
          mov     al, 0x04
          mov     dx, 0x21
          out     dx, al      ; outb(0x21, 4)

          mov     al, 0x02
          mov     dx, 0xA1
          out     dx, al      ; outb(0xA1, 2)

     ; IWC4
     ;------
          mov     al, 0x01
          mov     dx, 0x21
          out     dx, al      ; outb(0x21, 0x01)

          mov     al, 0x01
          mov     dx, 0xA1
          out     dx, al      ; outb(0xA1, 0x01)
;-------------------------------------------------------------------------

          mov byte [es: 0xb8022], "R" ; poke a char onto screen (debug)
         
;-------------------------------------------------------------------------

; Enable Keyboard PIC chips 'ONLY', disable others:
;(Port 0x21 bit 1 is keyboard)
          mov     al, 0xFD
          mov     dx, 0x21
          out     dx, al      ; outb(0x21, ~0x01)

          mov     al, 0xFF
          mov     dx, 0xA1
          out     dx, al      ; outb(0xA1, ~0x00)

          mov byte [es: 0xb8024], "S" ; poke a char onto screen (debug)
;-------------------------------------------------------------------------

; Every time an irq interupt occurs, we must clear it before another irq
; is sent.  Else another interupt wont' be sent till its been cleared.
; We usually call this at the end of our interupt routine.

;EOI for IRQ 0-7
mov     al, 0x20
        mov dx, 0x20
        out dx, al      ; outb(0x20, 0x20)
        
;-------------------------------------------------------------------------


;xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

;-------------------------------------------------------------------------

sti              ; Interrupts back..

mov byte [es: 0xb8026], "T" ; poke a char onto screen (debug)

;int 0

lp: jmp lp  ; loops here forever and ever...


; Stays in our loop forever, till something happens, such as an interrupt
; is called by pressing a key or the timer calls an interrupt etc



; We use 16 bits here - as you'll notice we use dw and dd only,
; and out data will be packed together nice and tight.

[BITS 16]
align 4

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Our GDTR register value
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

gdtr :
   dw gdt_end-gdt-1    ; Length of the gdt
   dd gdt	       ; physical address of gdt

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; This is the start of our gdt
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
align 4

gdt:    
gdt0 		       
   dd 0		      
   dd 0                
codesel:          
   dw 0x0ffff	      
   dw 0x0000	       
   db 0x00             	
   db 0x09a	      
   db 0x0cf	       
   db 0x00	       
datasel:        
   dw 0x0ffff	       
   dw 0x0000	      
   db 0x00	       
   db 0x092
   db 0x0cf
   db 0x00
gdt_end:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

align 4
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; Our IDTR register value
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

idtr :
   dw idt_end - idt_start - 1  ; Length of the idt
   dd idt_start                ; physical address of idt

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
; This is the start of our idt - its actual value
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 

; Nothing special, just a very basic interrupt function so we know something
; has happened
;-------------------------------------------------------------------------
[BITS 16]

;-------------------------------------------------------------------------

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

align 4

idt_start:
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0 ;  5
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0 ; 10
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0 ; 15
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0 ; 20
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0 ; 25
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0 ; 29
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0 ; 32
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0
                dw      intfunc, 8, 08e00h, 0 ; 47   
idt_end:

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
align 4
[bits 32]

int_start :
   dd intfunc
   dw 0x08

;-------------------

align 4

;________________________________________________________________________
; 
; Interrupt Routine
;________________________________________________________________________

intfunc:
;________________________________________________________________________
pushad
push es


;For each key pressed or released an interrupt (INT 09h) is issued by the 
;keyboard controller. When the interrupt is issued we can read the value of 
;the key pressed/released from the port 60h: if the 7th bit is set the key is 
;released, else is pressed. So we can register multiple keys pressions and 
;the sequence of the last n key pressed in this way: 

;Read the key from the keyboard controller
;   last_key=inportb(0x60);

in al,0x60      ; al = inb(0x60)


mov cl, al              ; save the character we read from the keyboard
                        ; in cl
                        
;Tell to the controller that the key code is readed
;   al=inportb(0x61);
;   al|=0x82;
;   outportb(0x61,al);
;   al&=0x7F;
;   outportb(0x61,al);

       in al,0x61      ; al = inb(0x61)
       or al, 0x82     ; al |=0x82
         
       mov dx, 0x61
       out dx, al       ; outb(0x61, al)

       and al,0x7f      ;   al&=0x7F;
       mov dx, 0x61
       out dx, al       ; outb(0x61, al)


nop
mov ax,0x10
mov es,ax
                           ; Use the character we got from the keyboard
                           ; and saved in cl, and put it on the screen.
                           
mov byte [es: 0xb8002], cl ; poke a character into the graphics output screen

;EOI for IRQ 0-7
mov    al, 0x20
        mov dx, 0x20
        out dx, al      ; outb(0x20, 0x20)

pop es
popad
iret



;-------------------------------------------------------------------------

TIMES 0x500-($-$$) DB 0x90    ; And of course, this will make our file size
                             ; equal to 0x500 a nice round number -
                             ; 0x500 ... so if you assemble the file
                             ; you should find its that size exactly.


;-------------------------------------------------------------------------

 

 

 

 

 

 

 

 
Advert (Support Website)

 
 Visitor:
Copyright (c) 2002-2024 xbdev.net - All rights reserved.
Designated articles, tutorials and software are the property of their respective owners.