;//--------------------------------------------------------------- ;// apictick.s ;// ;// This program activates the Local APIC's timer-interrupt. ;// ;// assemble with: $ as86 apictick.s -b apictick.b ;// install using: $ dd if=apictick.b of=/dev/fd0 seek=1 ;// ;// NOTE: This code begins executing with CS:IP = 1000:0002. ;// ;// programmer: ALLAN CRUSE ;// date begun: 25 APR 2004 ;// completion: 05 MAY 2004 ;//--------------------------------------------------------------- .SECT .TEXT ;----------------------------------------------------------------- .WORD 0xABCD ; programming signature ;----------------------------------------------------------------- main: mov ax, cs ; address this segment mov ds, ax ; with DS register pop dword return_address ; store return-address mov ss, ax ; adjust SS register lea esp, tos0 ; and set new stacktop call initialize_os_tables call clear_display_screen call enter_protected_mode call execute_program_demo call leave_protected_mode push dword return_address ; recover our exit-address retf ; exit to program launcher ;----------------------------------------------------------------- return_address: .LONG 0 ; to store exit-address ;----------------------------------------------------------------- ; EQUATES realCS EQU 0x1000 ; segment-address of code limGDT EQU 0x003F ; allocates 8 descriptors sel_es EQU 0x0008 ; vram-segment selector sel_cs EQU 0x0010 ; code-segment selector sel_ss EQU 0x0018 ; data-segment selector sel_fs EQU 0x0020 ; flat-segment selector ;----------------------------------------------------------------- .ALIGN 8 theGDT: .WORD 0x0000, 0x0000, 0x0000, 0x0000 ; null descriptor .WORD 0x7FFF, 0x8000, 0x920B, 0x0000 ; vram descriptor .WORD 0xFFFF, 0x0000, 0x9A01, 0x0000 ; code descriptor .WORD 0xFFFF, 0x0000, 0x9201, 0x0000 ; data descriptor .WORD 0xFFFF, 0x0000, 0x9200, 0x008F ; flat descriptor .WORD 0x0000, 0x0000, 0x0000, 0x0000 ; null descriptor .WORD 0x0000, 0x0000, 0x0000, 0x0000 ; null descriptor .WORD 0x0000, 0x0000, 0x0000, 0x0000 ; null descriptor ;----------------------------------------------------------------- theIDT: .SPACE 2048 ; enough for 256 gate-descriptors ;----------------------------------------------------------------- ;----------------------------------------------------------------- enter_protected_mode: cli ; no device interrupts mov eax, cr0 ; get machine status bts eax, #0 ; set PE-bit to 1 mov cr0, eax ; enable protection push #0x0001 ; push base-address hiword push #theGDT ; push base-address loword push #limGDT ; push GDT's segment-limit lgdt [esp] ; load GDTR register-image add sp, #6 ; discard three stackwords push #0x0001 ; push base-address hiword push #theIDT ; push base-address loword push #0x07FF ; push IDT's segment-limit lidt [esp] ; load IDTR register-image add sp, #6 ; discard three stackwords jmpf #pm, #sel_cs ; reload register CS pm: mov ax, #sel_ss mov ss, ax ; reload register SS mov ds, ax ; reload register DS xor ax, ax ; use 'null' selector mov es, ax ; to purge invalid ES mov fs, ax ; to purge invalid FS mov gs, ax ; to purge invalid GS ret ; back to main routine ;----------------------------------------------------------------- leave_protected_mode: mov ax, ss ; address 64KB r/w segment mov ds, ax ; using DS register mov es, ax ; and ES register mov eax, cr0 ; get machine status btr eax, #0 ; reset PE-bit to 0 mov cr0, eax ; disable protection push #0x0000 ; push base-address hiword push #0x0000 ; push base-address loword push #0x03FF ; push IDT's segment-limit lidt [esp] ; load IDTR register-image add sp, #6 ; discard three stackwords jmpf #rm, #realCS ; reload register CS rm: mov ax, cs mov ss, ax ; reload register SS mov ds, ax ; reload register DS sti ; interrupts allowed ret ; back to main routine ;----------------------------------------------------------------- ;----------------------------------------------------------------- initialize_os_tables: ; initialize IDT descriptor for gate 0x28 mov edi, #0x28 ; ID-number for the gate lea di, theIDT[edi*8] ; address gate-descriptor mov 0[di], #isrTMR ; entry-point loword mov 2[di], #sel_cs ; selector for code mov 4[di], #0x8E00 ; 386 interrupt-gate mov 6[di], #0x0000 ; entry-point hiword ; initialize IDT descriptor for gate 0x0D mov edi, #0x0D ; ID-number for the gate lea di, theIDT[edi*8] ; address gate-descriptor mov 0[di], #isrGPF ; entry-point loword mov 2[di], #sel_cs ; selector for code mov 4[di], #0x8E00 ; 386 interrupt-gate mov 6[di], #0x0000 ; entry-point hiword ret ;----------------------------------------------------------------- execute_program_demo: call turn_off_drive_motor call modify_the_pic_masks call start_the_apic_timer call initiate_timed_delay call stop_apic_timer_tick call modify_the_pic_masks ret ;----------------------------------------------------------------- clear_display_screen: mov ax, #0x0003 ; set_mode: standard-text int 0x10 ; request VIDEO service ret ;----------------------------------------------------------------- turn_off_drive_motor: mov dx, #0x3F2 ; FDC Digital Output Port mov al, #0xC0 ; command: all motors off out dx, al ; send command to the FDC ret ;----------------------------------------------------------------- mask1: .BYTE 0xFF mask2: .BYTE 0xFF ;----------------------------------------------------------------- modify_the_pic_masks: in al, #0xA1 ; read slave PIC masks xchg al, mask2 ; exchange with storage out #0xA1, al ; install revised masks in al, #0x21 ; read master PIC masks xchg al, mask1 ; exchange with storage out #0x21, al ; install revised masks ret ;----------------------------------------------------------------- ;----------------------------------------------------------------- start_the_apic_timer: push ds mov ax, #sel_fs ; address 4GB memory mov ds, ax ; with FS register xor eax, eax mov [0xFEE003E0], eax ; Timer: Divisor Config mov eax, #10000 ; countdown from 10000 mov [0xFEE00380], eax ; Timer: Initial Count mov eax, #0x28 ; Timer: interrupt-ID bts eax, #17 ; do periodic interrupt mov [0xFEE00320], eax ; set APIC Timer's LVT pop ds ret ;----------------------------------------------------------------- clkhz: .LONG 1193182 outhz: .WORD 100 ;----------------------------------------------------------------- initiate_timed_delay: in al, #0x61 ; get PORT_B settings or al, #0x01 ; enable PIT Channel2 out #0x61, al ; output new settings mov al, #0xB0 ; setup Channel2 Latch out 0x43, al ; for oneshot countdown mov ax, clkhz+0 ; get frequency divisor mov dx, clkhz+2 ; for timed delay of div word [outhz] ; ten milliseconds out #0x42, al ; write divisor's LSB xchg al, ah ; exchange LSB w/MSB out #0x42, al ; write divisor's MSB sti ; interrupts permitted delay: in al, #0x61 ; read PORT_B settings test al, #0x20 ; check: OUT2 active? jz delay ; no, continue polling cli ; interrupts suspended ret ;----------------------------------------------------------------- stop_apic_timer_tick: push ds mov ax, #sel_fs ; address 4GB memory mov ds, ax ; with FS register ; disable the APIC timer's periodic-interrupt mov eax, #0x00010000 ; set the mask bit mov [0xFEE00320], eax ; in APIC timer's LVT pop ds ret ;----------------------------------------------------------------- ;----------------------------------------------------------------- window: .WORD 640 ; next output location ;----------------------------------------------------------------- isrTMR: pushad ; must preserve registers push ds push es mov ax, #sel_ss ; address this segment mov ds, ax ; with DS register mov ax, #sel_es ; address video memory mov es, ax ; with ES register mov di, window ; point ES:DI to window mov ax, #0x4F54 ; setup char and colors stosw ; draw char into window mov window, di ; save window location mov ax, #sel_fs ; address 4GB memory mov ds, ax ; with DS register mov [0xFEE000B0], eax ; write to EOI register pop es ; recover saved registers pop ds popad iretd ; resume suspended task ;----------------------------------------------------------------- ;== WE KEEP THIS GENERAL-PROTECTION FAULT-HANDLER FOR DEBUGGING == ;----------------------------------------------------------------- names: .ASCII " ES DS" .ASCII " EDI ESI EBP ESP EBX EDX ECX EAX" .ASCII " err EIP CS EFL ESP SS" buf: .ASCII " nnn=xxxxxxxx " len: .LONG * - buf att: .BYTE 0x70 ;----------------------------------------------------------------- isrGPF: pushad mov ebp, esp push dword #0 mov [esp], ds push dword #0 mov [esp], es push ds push es mov ax, #sel_ss mov ds, ax mov ax, #sel_es mov es, ax cld xor ebx, ebx .L0: mov eax, [ebp + ebx*4 - 8 ] lea di, buf+5 call eax2hex imul esi, ebx, #4 mov eax, names[esi] mov buf, eax mov edi, #3800 imul eax, ebx, #160 sub edi, eax lea si, buf mov ah, att mov ecx, len .L1: lodsb stosw loop .L1 inc ebx cmp ebx, #14 jb .L0 pop es pop ds mov esp, ebp popad add esp, #4 call stop_apic_timer_tick call modify_the_pic_masks call leave_protected_mode push dword return_address ; recover our exit-address retf ; exit to program launcher ;----------------------------------------------------------------- eax2hex: pushad mov edx, eax mov ecx, #8 .L2: rol edx, #4 mov al, dl and al, #0x0F cmp al, #10 sbb al, #0x69 das mov [di], al inc di loop .L2 popad ret ;----------------------------------------------------------------- .ALIGN 16 ; alignment at paragraph .SPACE 512 ; reserved for stack use tos0: ; label fop top-of-stack ;----------------------------------------------------------------- END