;//--------------------------------------------------------------- ;// tickdemo.s ;// ;// This program handles timer interrupts in protected-mode. ;// It continuously displays the tick-count for ten seconds. ;// ;// assemble with: $ as86 tickdemo.s -b tickdemo.b ;// install using: $ dd if=tickdemo.b of=/dev/fd0 seek=1 ;// ;// NOTE: This code begins executing with CS:IP = 1000:0002. ;// ;// programmer: ALLAN CRUSE ;// written on: 23 FEB 2004 ;// revised on: 23 FEB 2004 -- removed unused 'flat' segment ;//--------------------------------------------------------------- .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 prepare_for_our_demo call enter_protected_mode call exec_timer_tick_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 mswGDT EQU 0x0001 ; base-address upper-word limGDT EQU 0x0027 ; allocates 5 descriptors sel_es EQU 0x0008 ; vram-segment selector sel_cs EQU 0x0010 ; code-segment selector sel_ss EQU 0x0018 ; data-segment selector sel_bs EQU 0x0020 ; bios-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 0x0100, 0x0400, 0x9200, 0x0000 ; bios descriptor ;----------------------------------------------------------------- theIDT: .SPACE 2048 ; enough for 256 gate-descriptors ;----------------------------------------------------------------- ;----------------------------------------------------------------- prepare_for_our_demo: ; initialize interrupt-gate descriptor for INT-0x08 mov edi, #0x08 ; gate ID-number lea di, theIDT[edi*8] ; offset-address mov 0[di], #isrPIT ; entry-loword mov 2[di], #sel_cs ; code-selector mov 4[di], #0x8600 ; gate-type mov 6[di], #0x0000 ; entry-hiword ret ;----------------------------------------------------------------- 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 #mswGDT ; 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 jmpf #pm, #sel_cs ; reload register CS pm: mov ax, #sel_ss mov ss, ax ; reload register SS mov ds, ax ; reload register DS mov es, ax ; reload register ES ret ; back to main routine ;----------------------------------------------------------------- pic_mask_bits: .WORD 0xFFFE ; mask-register settings ;----------------------------------------------------------------- reprogram_interrupts: push ds ; preserve DS contents mov ax, #sel_ss ; address this segment mov ds, ax ; with DS register in al, #0x21 ; read Master-PIC mask xchg al, ah in al, #0xA1 ; read Slave-PIC mask xchg al, ah xchg ax, pic_mask_bits ; swap old w/new masks out #0x21, al ; write Master-PIC mask xchg al, ah out #0xA1, al ; write Slave-PIC mask pop ds ; restore saved DS ret ;----------------------------------------------------------------- ;----------------------------------------------------------------- exec_timer_tick_demo: mov ax, #sel_bs ; address rom-bios data mov ds, ax ; using DS register mov ax, #sel_es ; address video memory mov es, ax ; using ES register push #mswGDT ; push IDT-base hiword push #theIDT ; push IDT-base loword push #0x07FF ; push IDT segment-limit lidt [esp] ; load IDTR with image add sp, #6 ; discard three words call reprogram_interrupts ; mask all but timer-tick sti ; allow device interrupts mov ebp, [N_TICKS] ; get current tick-count add ebp, #180 ; increment by 10-secs again: mov eax, [N_TICKS] ; get current tick-count cmp eax, ebp ; test: timeout yet? jge finis ; yes, exit this loop mov ebx, #10 ; base of decimal system xor cx, cx ; initialize digit-count nxdiv: xor edx, edx ; extend EAX to quadword div ebx ; divide by number-base push dx ; push remainder on stack inc cx ; and count the remainder or eax, eax ; was the quotient zero? jnz nxdiv ; no, do another divide mov di, #156 ; final screen-position sub di, cx ; back up for digits sub di, cx ; back up for colors mov ax, #0x3020 ; initial blank space stosw ; written to screen nxdgt: pop ax ; recover saved remainder or ax, #0x3030 ; convert to numeral/color stosw ; and write it to screen loop nxdgt ; process all the digits mov ax, #0x3020 ; final blank space stosw ; written to screen jmp again ; reenter this loop finis: cli ; discontinue interrupts call reprogram_interrupts ; restore device-masks push #0x0000 ; push IVT-base hiword push #0x0000 ; push IVT-base loword push #0x03FF ; push IVT segment-limit lidt [esp] ; load IDTR with image add sp, #6 ; diacard three words 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 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 ;----------------------------------------------------------------- ;================================================================= ;----------------------------------------------------------------- HOURS24 EQU 0x180000 ; number of ticks-per-day N_TICKS EQU 0x006C ; offset for tick-counter TM_OVFL EQU 0x0070 ; offset of rollover-flag MOTOR_COUNT EQU 0x0040 ; offset of motor-counter MOTOR_STAT EQU 0x003F ; offset for motor-status ;----------------------------------------------------------------- isrPIT: ; Interrupt-Service Routine for the timer-tick interrupt push ax ; save working registers push dx push ds mov ax, #sel_bs ; address rom-bios data mov ds, ax ; using DS register inc dword [N_TICKS] ; increment tick-count cmp dword [N_TICKS], #HOURS24 ; past midnight? jl isok1 ; no, don't rollover yet mov dword [N_TICKS], #0 ; else reset count to 0 mov byte [TM_OVFL], #1 ; and set rollover flag isok1: dec byte [MOTOR_COUNT] ; decrement motor-count jnz isok2 ; nonzero? motor stays on mov dx, #0x03F2 ; else turn off motors mov al, #0x0C ; command: turn off motors out dx, al ; sent to disk-controller and byte [MOTOR_STAT], #0xF0 ; mark motors off isok2: mov al, #0x20 out #0x20, al pop ds ; restore saved registers pop dx pop ax iret ; resume interrupted task ;----------------------------------------------------------------- ;----------------------------------------------------------------- ;================================================================= ;----------------------------------------------------------------- .ALIGN 16 ; alignment at paragraph .SPACE 512 ; reserved for stack use tos0: ; label fop top-of-stack ;----------------------------------------------------------------- END