Mail Archives: djgpp/1995/04/03/12:58:59
Dear all,
Here is the code that I cut and paste from my code. You may have to
declare the static variables here and there to get it compiled.
/*************************************************************************
Using the TOD 8253/4 timer. Works but had problems under
heavy load of disk access.
*************************************************************************/
#define DOSX_GO32_RAM_OFFSET 0xe0000000
/*---------------------------------------------------------------------------
Definitions for The PC INTERRUPT CONTROLLER
---------------------------------------------------------------------------*/
#define PC_INT_CONTROLLER 0x20 /* Interrupt controller I/O Address */
#define PC_8253_TIMER_CHANNEL 0x40 /* Timer channel (#0) of the timer chip */
#define PC_8253_COMMAND_REG = 0x43 /* Command register of the timer chip */
#define PC_8253_CH_0_SEQ = 0x36 /* 00110110 : ch 0, 2 bytes, mode 3, bin */
#define PC_8253_ONE_MSEC_COUNT = 1193 /* The PC 8253 clock input is 1.193180 MHz */
#define PC_8253_CLOCK_RATE = 1193180
#define TO_KICK_DOS_CLOCK = 0xffff
/*********************************************************************
* PM IRQ 8
*
* DESCRIPTION:
* This function is called when the Time Of Day interrupt occurs
* while the 386 CPU is in Protected mode.
* We need to call the DOS real mode interrupt whenever the
*
* NOTES:
* 1. The Rel_1ms clock is not counted every so often to take into
* account that the real time clock is fast.
* 2. alias_irq_regs & regs are declared sattic as the 'dpmi' code
* assumes they belong to the 'DS' selector.
* 3. Interrupt is explicitly disabled with an "cli" instruction
* as when int86 is called, the processor may get another clock
* interrupt when switched back from the real mode.
*********************************************************************/
static void
pm_irq_08 (void)
{
asm ("cli");
asm ("push %eax");
asm ("push %ecx");
asm ("push %edx");
asm ("push %ds");
asm ("push $0x48");
asm ("pop %ds");
if ((*One_msec_clock_remainder += *Delta_per_tick) >= PC_8253_CLOCK_RATE)
*One_msec_clock_remainder -= PC_8253_CLOCK_RATE;
else
*Rel_1ms += *Msec_per_tick;
if ((*Dos_clock_remainder += *Timer_counter) >= TO_KICK_DOS_CLOCK)
{ asm ("pusha");
asm ("push %gs");
*Dos_clock_remainder -= TO_KICK_DOS_CLOCK;
/*-------------------------------------------------------------------
This is equivalent to _go32_dpmi_simulate_fcall_iret
-------------------------------------------------------------------*/
Alias_irq_regs.x.cs = Rm_old_irq8.rm_segment;
Alias_irq_regs.x.ip = Rm_old_irq8.rm_offset;
Alias_irq_regs.x.ss = 0;
Alias_irq_regs.x.sp = 0;
Alias_irq_regs.x.flags = 0;
Irq_regs.x.ax = 0x302;
Irq_regs.h.bh = 0;
Irq_regs.x.cx = 0;
Irq_regs.x.di = (uint32)(&Alias_irq_regs);
int86 (0x31, &Irq_regs, &Irq_regs);
asm ("add $12, %esp");
asm ("pop %gs");
asm ("popa");
}
else
{ asm ("mov $0x20,%al");
asm ("out %al,$0x20");
}
asm ("pop %ds");
asm ("pop %edx");
asm ("pop %ecx");
asm ("pop %eax");
asm ("leave");
asm ("iret");
}
/*********************************************************************
* INSTALL TOD TIMER INTERRUPT
*
* DESCRIPTION:
* This function will set up the PC time of day (tod) interrupt to
* generate interrupt approximately every one millisecond.
* There are the protected mode and real mode interrupts.
* The protected mode interrupt needs to call the DOS tod interrupt
* roughly every 18msec to keep the DOS clock happy. A set of
* counters are stored in the DOS low memory so the protected mode
* and real mode code can share.
* In real mode, we allocate a block of DOS low memory to create
* the real mode interrupt code. This code basically will update
* the Rel_1ms count and call the DOS tod interrupt every 18 msec.
*
* NOTES:
*
*********************************************************************/
static void
install_tod_timer_interrupt (void)
{
_go32_dpmi_seginfo rm_si;
_go32_dpmi_seginfo pm_si;
uint32 linear_addr;
uint32 go32_addr;
uint16 *p16;
uint32 *p32;
/*---------------------------------------------------------------------------
This data space is the code for the real mode clock interrupt.
The C equivalent form is:
extern void dos_interrupt (void);
static unsigned long Timer_counter;
static unsigned long Dos_clock_remainder;
static unsigned long One_msec_clock_remainder;
static unsigned long Delta_per_tick;
static unsigned long Rel_1ms;
static unsigned long Msec_per_tick;
#define PC_8253_CLOCK_RATE (1193180L)
#define TO_KICK_DOS_CLOCK (0xffffL)
extern void x (void);
extern void outp (unsigned reg, unsigned value);
void x (void)
{
if ((One_msec_clock_remainder += Delta_per_tick) >= PC_8253_CLOCK_RATE)
One_msec_clock_remainder -= PC_8253_CLOCK_RATE;
else
Rel_1ms += Msec_per_tick;
if ((Dos_clock_remainder += Timer_counter) >= TO_KICK_DOS_CLOCK)
{ Dos_clock_remainder -= TO_KICK_DOS_CLOCK;
goto dos_interrupt;
}
else
outp (0x20, 0x20);
}
---------------------------------------------------------------------------*/
static uint8 real_mode_code[] = {
/* offset opcode Assembly Mnemonic */
0x00,0x00,0x00,0x00, /* 0000 00000000 Timer_counter dd 0 */
0x00,0x00,0x00,0x00, /* 0004 00000000 Dos_clock_remainder dd 0 */
0x00,0x00,0x00,0x00, /* 0008 00000000 One_msec_clock_remainder dd 0 */
0x00,0x00,0x00,0x00, /* 000C 00000000 Delta_per_tick dd 0 */
0x00,0x00,0x00,0x00, /* 0010 00000000 Rel_1ms dd 0 */
0x00,0x00,0x00,0x00, /* 0014 00000000 Msec_per_tick dd 0 */
0xfa, /* 0018 FA cli */
0x50, /* 0019 50 push ax */
0x52, /* 001A 52 push dx */
0x2e,0xa1,0x0c,0x00, /* 001B 2E: A1 000C R mov ax,WORD PTR cs:Delta_per_tick */
0x2e,0x8b,0x16,0x0e,0x00, /* 001F 2E: 8B 16 000E R mov dx,WORD PTR cs:Delta_per_tick+2 */
0x2e,0x01,0x06,0x08,0x00, /* 0024 2E: 01 06 0008 R add WORD PTR cs:One_msec_clock_remainder,ax */
0x2e,0x11,0x16,0x0a,0x00, /* 0029 2E: 11 16 000A R adc WORD PTR cs:One_msec_clock_remainder+2,dx */
0x2e,0x83,0x3e,0x0a,0x00,0x12, /* 002E 2E: 83 3E 000A R 12 cmp WORD PTR cs:One_msec_clock_remainder+2,18 */
0x72,0x1b, /* 0034 72 1B jb $I112 */
0x77,0x09, /* 0036 77 09 ja $L20000 */
0x2e,0x81,0x3e,0x08,0x00,0xdc,0x34, /* 0038 2E: 81 3E 0008 R 34DC cmp WORD PTR cs:One_msec_clock_remainder,13532 */
0x72,0x10, /* 003F 72 10 jb $I112 */
/* 0041 $L20000: */
0x2e,0x81,0x2e,0x08,0x00,0xdc,0x34, /* 0041 2E: 81 2E 0008 R 34DC sub WORD PTR cs:One_msec_clock_remainder,13532 */
0x2e,0x83,0x1e,0x0a,0x00,0x12, /* 0048 2E: 83 1E 000A R 12 sbb WORD PTR cs:One_msec_clock_remainder+2,18 */
0xeb,0x14, /* 004E EB 14 jmp SHORT $I113 */
0x90, /* 0050 90 nop */
/* 0051 $I112: */
0x2e,0xa1,0x14,0x00, /* 0051 2E: A1 0014 R mov ax,WORD PTR cs:Msec_per_tick */
0x2e,0x8b,0x16,0x16,0x00, /* 0055 2E: 8B 16 0016 R mov dx,WORD PTR cs:Msec_per_tick+2 */
0x2e,0x01,0x06,0x10,0x00, /* 005A 2E: 01 06 0010 R add WORD PTR cs:Rel_1ms,ax */
0x2e,0x11,0x16,0x12,0x00, /* 005F 2E: 11 16 0012 R adc WORD PTR cs:Rel_1ms+2,dx */
/* 0064 $I113: */
0x2e,0xa1,0x00,0x00, /* 0064 2E: A1 0000 R mov ax, WORD PTR cs:Timer_counter */
0x2e,0x8b,0x16,0x02,0x00, /* 0068 2E: 8B 16 0002 R mov dx, WORD PTR cs:Timer_counter+2 */
0x2e,0x01,0x06,0x04,0x00, /* 006D 2E: 01 06 0004 R add WORD PTR cs:Dos_clock_remainder, ax */
0x2e,0x11,0x16,0x06,0x00, /* 0072 2E: 11 16 0006 R adc WORD PTR cs:Dos_clock_remainder+2, dx */
0x2e,0x83,0x3e,0x06,0x00,0x00, /* 0077 2E: 83 3E 0006 R 00 cmp WORD PTR cs:Dos_clock_remainder+2, 0 */
0x75,0x08, /* 007D 75 08 jne $L20002 */
0x2e,0x83,0x3e,0x04,0x00,0xff, /* 007F 2E: 83 3E 0004 R FF cmp WORD PTR cs:Dos_clock_remainder, TO_KICK_DOS_CLOCK */
0x72,0x13, /* 0085 72 13 jb $I114 */
/* 0087 $L20002: */
0x2e,0x83,0x2e,0x04,0x00,0xff, /* 0087 2E: 83 2E 0004 R FF sub WORD PTR cs:Dos_clock_remainder, TO_KICK_DOS_CLOCK */
0x2e,0x83,0x1e,0x06,0x00,0x00, /* 008D 2E: 83 1E 0006 R 00 sbb WORD PTR cs:Dos_clock_remainder+2, 0 */
0x5a, /* 0093 5A pop dx */
0x58, /* 0094 58 pop ax */
0xea,0x00,0x00,0x00,0x00, /* 0095 EA 0000 ---- E jmp <to be set at run time> */
/* 009A $I114: */
0xb0,0x20, /* 009A B0 20 mov al,20H */
0xe6,0x20, /* 009C E6 20 out 20H,al */
0x5a, /* 009E 5A pop dx */
0x58, /* 009F 58 pop ax */
0xcf /* 00A0 CF iret */
};
/*---------------------------------------------------------------------------
allocate DOS low memory to store the code above.
---------------------------------------------------------------------------*/
Dosx_rm_clock_int_seginfo.size = (sizeof (real_mode_code) + 0x10) / 0x10;
if (_go32_dpmi_allocate_dos_memory (&Dosx_rm_clock_int_seginfo))
exit (1);
/*---------------------------------------------------------------------------
make sure where we put the code is paragraph (16-byte) aligned
fill memory with INT 3 opcode.
---------------------------------------------------------------------------*/
linear_addr = (uint16)Dosx_rm_clock_int_seginfo.rm_segment * 0x10;
if (linear_addr & 0x0000000f)
linear_addr = (linear_addr + 0x10) & 0xfffffff0;
go32_addr = linear_addr + DOSX_GO32_RAM_OFFSET;
memset ((uint8 *)go32_addr, 0xcc, Dosx_rm_clock_int_seginfo.size * 0x10);
/*---------------------------------------------------------------------------
set up pointers to the shared timer variables, and initialize variables
---------------------------------------------------------------------------*/
p32 = (uint32 *)go32_addr;
Timer_counter = p32++;
Dos_clock_remainder = p32++;
One_msec_clock_remainder = p32++;
Delta_per_tick = p32++;
Rel_1ms = p32++;
Msec_per_tick = p32;
/*---------------------------------------------------------------------------
write the code to the DOS low memory
---------------------------------------------------------------------------*/
_go32_dpmi_get_real_mode_interrupt_vector(8, &Rm_old_irq8);
memcpy ((uint8 *)go32_addr, real_mode_code, sizeof (real_mode_code));
*Timer_counter = 1193;
*Delta_per_tick = PC_8253_CLOCK_RATE % Timer_counter_value;
*Msec_per_tick = 1000 / (PC_8253_CLOCK_RATE / Timer_counter_value);
p16 = (uint16 *)(go32_addr + 0x96);
*p16++ = Rm_old_irq8.rm_offset;
*p16 = Rm_old_irq8.rm_segment;
/*---------------------------------------------------------------------------
setup real and protected mode interrupt vectors
---------------------------------------------------------------------------*/
memset (&rm_si, 0, sizeof (rm_si));
rm_si.rm_offset = 0x18;
rm_si.rm_segment = (uint16)(linear_addr >> 4);
memset (&pm_si, 0, sizeof (pm_si));
pm_si.pm_offset = (int) pm_irq_08;
pm_si.pm_selector = _go32_my_cs();
disable();
_go32_dpmi_set_real_mode_interrupt_vector(8, &rm_si);
_go32_dpmi_set_protected_mode_interrupt_vector(8, &pm_si);
outpb (PC_8253_COMMAND_REG, PC_8253_CH_0_SEQ);
outpb (PC_8253_TIMER_CHANNEL, Timer_counter_value & 0xff);
outpb (PC_8253_TIMER_CHANNEL, Timer_counter_value >> 8);
enable();
}
/*************************************************************************
Using the RTC timer. Worked on one PC platform but I
found it did not work on another platform. Still
under investigation.
*************************************************************************/
#define DOSX_GO32_RAM_OFFSET 0xe0000000
/*---------------------------------------------------------------------------
MC146818A Real Time Clock (RTC)
This RTC device can generate IRQ8 interrupt on a periodic basis to
the PC. See:
- Dallas Semiconductor handbook for device DS1287 (MC146818A equiv.)
- ISA System Architecture, by Tom Shanley & Don Anderson - Mind Share Inc
1993. (Chapter 21).
---------------------------------------------------------------------------*/
#define RTC_ADDR_PORT = 0x70; /* RTC chip address port */
RTC_DATA_PORT = 0x71; /* RTC chip data port */
RTC_STATUS_REG_A = 0xa; /* RTC Status Register A RAM offset */
RTC_STATUS_REG_B = 0xb; /* RTC Status Register B RAM offset */
RTC_STATUS_REG_C = 0xc; /* RTC Status Register C RAM offset */
/* status A register definition */
enum uint8 {
RTC_REG_A_RATE_MASK = 0x0f, /* mask to the DS0-DS3 bits */
RTC_REG_A_RATE_1K = 0x06, /* DS0-DS3 value for 1.024 kHz clock rate */
RTC_REG_A_UIP_BIT = 0x80, /* =1 Update in Progress, Read only */
RTC_REG_A_DV_MASK = 0x70, /* oscillator on and off bit */
RTC_REG_A_DV_32K = 0x20 /* DV bits to be set to 32.768khz timebase */
};
/* status B register definition */
RTC_REG_B_PIE_BIT = 0x40; /* 1=> Periodic Interrupt enable */
RTC_REG_B_AIE_BIT = 0x20; /* 1=> Alarm Interrupt enable */
RTC_REG_B_UEIE_BIT = 0x10; /* 1=> Update-Ended Interrupt Enable */
RTC_INT_TYPE = 0x70; /* RTC interrupt type */
RTC_REMAINDER_PER_TICK = 24; /* count remainder per tick */
- Raw text -