A great vantage of the new computers is the speed of computing, but for the games this is a trouble.
Have you ever played an old game on a new Pentium 133Mhz? Surely the speed of the game was too up
to play...
For resolving the problem of syncronization we can use the timer interrupt. This interrupt (INT 08h)
is issued by the hardware about 18.2 times/sec, but we can program the speed of issuing. For
handling an interrupt we must first save the address of the old handler, install the new handler
and last, before the exit of the program, reinstall the old handler. So we can build two functions
like these:
void init_handler(void)
{
new_handler.pm_offset = (int)new_handler;
new_handler.pm_selector = _go32_my_cs();
_go32_dpmi_get_protected_mode_interrupt_vector(0x08, &old_handler);
_go32_dpmi_allocate_iret_wrapper(&new_handler);
_go32_dpmi_set_protected_mode_interrupt_vector(0x08, &new_handler);
}
void done_handler(void)
{
_go32_dpmi_set_protected_mode_interrupt_vector(0x08, &old_handler);
_go32_dpmi_free_iret_wrapper(&new_handler);
}
The structure of an interrupt handler is very simple: saving the registers, handling and restore
the registers:
void new_handler(void)
{
asm("cli; pusha"); // Saving the register and disabiliting the interrupts
// Place here the code
outportb(0x20, 0x20); // EOI if is a hardware interrupt
asm("popa; sti"); // Restoring the registers and abiliting the interrupts
}
Well, for syncrinozing we must count the number of issuing of the timer in a variable (eg.
timer_tick
) and control in the game if the variable is true before advance in the
elaboration of the game data:
while(!quit)
while(!timer_tick);
timer_tick=0;
.
.
// Code of the game
}
Setting
up
the
speed
of
the
timer
The hardware have a timer chip (8253 or 8254) with tree channels:
- Channel 0: the system timer (or int 08h). We use this channels for programming.
- Channel 1: RAM refresh. Don't touch.....
- Channel 2: controls the frequency generation of the PC speaker. Try it for good sounds!
This chip have a frequency input of 1.19318 Mhz and we can program the channels to divide this
frequency for generating the speed that we want. The 8253 have four ports: from 40h to 43h.
With the ports 40h..42h we program the frequency divisor of the channels, but first we must use the
port 43h for prepare the chip in this mode:
- bits 7 - 6: channel selector:
- 00: channel 0
- 01: channel 1 (don't use!)
- 02: channel 2
- bits 5 - 4: operation selector:
- 00: save the counter (or divisor) for reading
- 01: enable the writing of the high byte of the counter
- 10: enable the writing of the low byte of the counter
- 11: enable the writing of the low byte and next of the high byte
- bits 3 - 1: function mode selector (from 000 to 101)
- bit 0: counter mode:
Well, for programming the timer at a speed tree times up the default we must write the value 05555h
in the counter:
asm("cli");
outportb(0x43, 0x36);
outportb(0x40, 0x55);
outportb(0x40, 0x55);
asm("sti");
And for restore the default speed we must write the value 0000h:
asm("cli");
outportb(0x43, 0x36);
outportb(0x40, 0);
outportb(0x40, 0);
asm("sti");
Handling
the
keyboard
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:
void keyboard_handler(void)
{
BYTE al, ah;
asm("cli; pusha");
// Read the key from the keyboard controller
last_key=inportb(0x60);
// Check if the key is pressed (bit 7 = 0) or released (bit 7 = 1)
if(last_key<128) {
key_status[last_key]=TRUE; // Update the status
// Insert in the key sequence
for(al=SEQUENCE_LENGTH-1;al>0;al--)
key_sequence[al]=key_sequence[al-1];
key_sequence[0]=last_key;
} else key_status[last_key-128]=FALSE; // Update the status
// Tell to the controller that the key code is readed
al=inportb(0x61);
al|=0x82;
outportb(0x61,al);
al&=0x7F;
outportb(0x61,al);
key_delay_count=0; // Recounting
outportb(0x20, 0x20); // EOI
asm("popa; sti");
}
For the right detection of the key sequence we must add some lines of code in the timer handler,
where every n issues insert a NULL key in the sequence:
if(++key_delay_count>key_delay) {
for(i=SEQUENCE_LENGTH-1;i>0;i--)
key_sequence[i]=key_sequence[i-1];
key_sequence[0]=0x00;
key_delay_count=0;
}
Press here to download the source of the demo program.