Read the code from Protected Mode Tutorial #1 from Alexei Frounze.
It tries to read port 0x70 to toggle the NMI bit (bit 7), but the document generated while running the program apparently shows that it can't be done.
So if that's the case, the program would have an error in the assumptions made to implement it, assuming that the state of the NMI bit could be read:
/*
PMode tutorials in C and Asm
Copyright (C) 2000 Alexei A. Frounze
The programs and sources come under the GPL
(GNU General Public License), for more information
read the file gnu-gpl.txt (originally named COPYING).
*/
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <dos.h>
#include "pm.h"
#define byte unsigned char
#define word unsigned int
unsigned long far *BIOS_timer = MK_FP (0x40, 0x6C);
FILE *messageHandle;
char strbuff[512];
char strbuff2[512];
unsigned int uintbuff=0;
unsigned long ulongbuff=0;
byte port70initialValue=0;
int port70isStuck=1;
void WriteBookFromProgram(FILE *documentHandle, char *message, long messageLength, long messageType);
byte read_CMOS_reg (byte reg) {
outportb (0x70, reg);
return inportb (0x71);
}
void write_CMOS_reg (byte reg, byte value) {
outportb (0x70, reg);
outportb (0x71, value);
}
void delay_RTC (int secs) {
byte x;
while (secs--) {
x = read_CMOS_reg(0); /* read seconds from RTC */
while (read_CMOS_reg(0) == x); /* wait for the next value */
};
}
void delay_RTC_RM (int secs) {
byte x;
unsigned long tickend,tickstart;
#define msg_delay_RTC_0000 "delay_RTC.053 -- We will iterate for %d seconds.\r\n"
sprintf(strbuff,msg_delay_RTC_0000,secs);
WriteBookFromProgram(messageHandle, strbuff, strlen(strbuff)+1, 0);
while (secs--) {
x = read_CMOS_reg(0); /* read seconds from RTC */
tickstart=*BIOS_timer;
#define msg_delay_RTC_0001 "delay_RTC.059 -- STEP 1. Get current seconds count, %d, BCD 0x%s, in x. The BIOS timer at 0x46C is now %ld.\r\n"
itoa(x,strbuff2,16);
sprintf(strbuff,msg_delay_RTC_0001,(int)x, strbuff2,*BIOS_timer);
WriteBookFromProgram(messageHandle, strbuff, strlen(strbuff)+1, 0);
#define msg_delay_RTC_0002 "delay_RTC.064 -- STEP 2. Wait until the new seconds in the CMOS memory index 0 become different than the cached x second.\r\n"
WriteBookFromProgram(messageHandle, msg_delay_RTC_0002, strlen(msg_delay_RTC_0002)+1, 0);
ulongbuff=0;
while (read_CMOS_reg(0) == x)ulongbuff++; /* wait for the next value */
tickend=*BIOS_timer;
#define msg_delay_RTC_0003 "delay_RTC.072 -- STEP 3 (debug only). We iterated %ld times to wait for 1 second change. The BIOS timer at 0x46C is now %ld (%ld ticks total difference)\r\n"
sprintf(strbuff,msg_delay_RTC_0003,ulongbuff,*BIOS_timer,tickend-tickstart);
WriteBookFromProgram(messageHandle, strbuff, strlen(strbuff)+1, 0);
};
WriteBookFromProgram(messageHandle, "\r\n\r\n", 5, 0);
#define msg_delay_RTC_0004 "delay_RTC.080 -- Here we could improve the accuracy of our timer some more\r\n"
#define msg_delay_RTC_0005 " if we made an average of all the counts, except the first one,\r\n"
#define msg_delay_RTC_0006 " then we subtracted the first count from the average, got\r\n"
#define msg_delay_RTC_0007 " the absolute value, or subtracted the smaller from the bigger value\r\n"
#define msg_delay_RTC_0008 " and then we simply iterated one more time in a second loop only for the\r\n"
#define msg_delay_RTC_0009 " amount of the subtraction to complete an obviously partial amount of time\r\n"
#define msg_delay_RTC_0010 " to complete a whole second, as long as that count is smaller than the bigger count so far.\r\n\r\n"
#define msg_delay_RTC_0011 " The only drawback is that it would be very CPU intensive, not suited for every multitasking application\r\n"
#define msg_delay_RTC_0012 " and even less for a multitasking kernel and libraries.\r\n"
WriteBookFromProgram(messageHandle, msg_delay_RTC_0004, strlen(msg_delay_RTC_0004)+1, 0);
WriteBookFromProgram(messageHandle, msg_delay_RTC_0005, strlen(msg_delay_RTC_0005)+1, 0);
WriteBookFromProgram(messageHandle, msg_delay_RTC_0006, strlen(msg_delay_RTC_0006)+1, 0);
WriteBookFromProgram(messageHandle, msg_delay_RTC_0007, strlen(msg_delay_RTC_0007)+1, 0);
WriteBookFromProgram(messageHandle, msg_delay_RTC_0008, strlen(msg_delay_RTC_0008)+1, 0);
WriteBookFromProgram(messageHandle, msg_delay_RTC_0009, strlen(msg_delay_RTC_0009)+1, 0);
WriteBookFromProgram(messageHandle, msg_delay_RTC_0010, strlen(msg_delay_RTC_0010)+1, 0);
WriteBookFromProgram(messageHandle, msg_delay_RTC_0011, strlen(msg_delay_RTC_0011)+1, 0);
WriteBookFromProgram(messageHandle, msg_delay_RTC_0012, strlen(msg_delay_RTC_0012)+1, 0);
WriteBookFromProgram(messageHandle, "\r\n\r\n", 5, 0);
}
void my_exit() {
#define msg_my_exit_0000 "my_exit.047 -- Just print a message right before the program terminates.\r\n"
WriteBookFromProgram(messageHandle, msg_my_exit_0000, sizeof(msg_my_exit_0000), 0);
printf ("\nWe're back...\n");
printf ("\nPMode Tutorial by Alexei A. Frounze (c) 2000\n");
printf ("E-mail :
[email protected]\n");
printf ("Homepage: http://alexfru.chat.ru\n");
printf ("Mirror : http://members.xoom.com/alexfru\n");
printf ("PMode...: http://welcome.to/pmode\n");
}
int main() {
port70initialValue=inportb(0x70);
messageHandle=fopen("SrcDoc00.txt","wb");
/* messageHandle=fopen("SourceDoc0000.txt","wb"); */ /* Use DOSLFN to use this long file name. */
/* Apparently Turbo C truncates long file names, so using DOSLFN will be of no use here
with the default C library from Turbo C. */
if(!messageHandle)
{
printf("ERROR: Couldn't create SourceDoc file, automatically generated book from program.");
exit(-1);
}
#define msg_main_FP0 "main.119: Point in a variable to absolute offset 0x46C, where the BIOS tick timer resides\r\n"
#define msg_main_FP1 " How to properly keep it updated from our OS at all times???\r\n\r\n"
WriteBookFromProgram(messageHandle, msg_main_FP0, sizeof(msg_main_FP0), 0);
WriteBookFromProgram(messageHandle, msg_main_FP1, sizeof(msg_main_FP1), 0);
#define msg_main_0000 "main.065: Clear the screen, print what this program does\r\n"
#define msg_main_0001 " and set the function to be executed by the run time at exit (atexit).\r\n\r\n"
WriteBookFromProgram(messageHandle, msg_main_0000, sizeof(msg_main_0000), 0);
WriteBookFromProgram(messageHandle, msg_main_0001, sizeof(msg_main_0001), 0);
clrscr();
printf ("Welcome to the 1st PMode tutorial!\n\n");
atexit (my_exit);
#define msg_main_0002 "main.076: Use the SMSW, store machine status word instruction,\r\n"
#define msg_main_0003 " and if bit 0 is 1, exit the program\r\n"
#define msg_main_0004 " as the CPU is currently in protected mode.\r\n\r\n"
WriteBookFromProgram(messageHandle, msg_main_0002, sizeof(msg_main_0002), 0);
WriteBookFromProgram(messageHandle, msg_main_0003, sizeof(msg_main_0003), 0);
WriteBookFromProgram(messageHandle, msg_main_0004, sizeof(msg_main_0004), 0);
#define msg_main_0005 " Bit 0 of CR0 is currently "
WriteBookFromProgram(messageHandle, msg_main_0005, sizeof(msg_main_0005), 0);
WriteBookFromProgram(messageHandle, itoa(read_msw() & 1,strbuff,10), 2, 0);
WriteBookFromProgram(messageHandle, "\r\n\r\n", 5, 0);
if (read_msw() & 1) {
printf ("The CPU is already in PMode.\nAborting...");
return 0;
};
printf ("We're going to PMode using CR0 for 5 seconds...\n");
#define msg_main_0006 "main.101: Execute CLI.\r\n\r\n"
WriteBookFromProgram(messageHandle, msg_main_0006, sizeof(msg_main_0006), 0);
/* disable interrupts so that IRQs don't cause exceptions */
disable();
if(inportb(0x70)!=port70initialValue)port70isStuck=0;
uintbuff=(unsigned int)inportb(0x70);
#define msg_main_0007 "main.112: Disable NMIs -- call this log with parameters computed outside the function since x86 code calls it\r\nbackwards on stack and then it will fail to be set properly to display our numbers.\r\n Port 0x70 currently contains 0x"
WriteBookFromProgram(messageHandle, msg_main_0007, sizeof(msg_main_0007), 0);
itoa(uintbuff,strbuff,16);
WriteBookFromProgram(messageHandle, strbuff, strlen(strbuff)+1,0);
WriteBookFromProgram(messageHandle, " -- ", 5, 0);
itoa(uintbuff,strbuff,2);
WriteBookFromProgram(messageHandle, strbuff, strlen(strbuff)+1,0);
WriteBookFromProgram(messageHandle, "b", 2, 0);
WriteBookFromProgram(messageHandle, "\r\n\r\n\r\n", 7, 0);
WriteBookFromProgram(messageHandle, "b", 2, 0);
if(inportb(0x70)!=port70initialValue)port70isStuck=0;
if(uintbuff&0x80)
{
#define msg_main_0008 "main.121: Bit 7 of port 0x70 (CMOS Memory Index) is 1\r\n so NMIs are disabled.\r\n\r\n"
WriteBookFromProgram(messageHandle, msg_main_0008, sizeof(msg_main_0008), 0);
}
else
{
#define msg_main_0009 "main.126: Bit 7 of port 0x70 is 0\r\n so NMIs are enabled.\r\n\r\n"
WriteBookFromProgram(messageHandle, msg_main_0009, sizeof(msg_main_0009), 0);
}
/* disable NMIs as well */
outportb (0x70, inportb(0x70) | 0x80);
#define msg_main_0010 "main.137: We simply enable protected mode by setting\r\nbit 0 of CR0 to 1 (mov eax,cr0 -- inc eax -- mov cr0,eax), but after that we still need to set it up properly.\r\n\r\n"
WriteBookFromProgram(messageHandle, msg_main_0010, sizeof(msg_main_0010), 0);
/* WOW!!! This switches us to PMode just setting up CR0.PM bit to 1 */
write_cr0 (read_cr0() | 1L);
/* a delay for 5 seconds */
/* delay_RTC (0);*/
/* get out of PMode clearing CR0.PM bit to 0 */
write_cr0 (read_cr0() & 0xFFFFFFFEL);
enable();
delay_RTC_RM (5);
disable();
#define msg_main_0011 "main.147: We simply disable protected mode by clearing\r\nbit 0 of CR0 to 0 (mov eax,cr0 -- dec eax, mov cr0,eax). We didn't set up anything further so we don't need to\r\nreconfigure anything to return to Real Mode, but we can't call any Turbo C library functions or Real-Mode specific\r\ncode during that time or we will crash in a badly-enabled protected mode.\r\n\r\n"
WriteBookFromProgram(messageHandle, msg_main_0011, sizeof(msg_main_0011), 0);
/* *BIOS_timer += 91L;*/ /* 5*18.2 ticks total */
if(inportb(0x70)!=port70initialValue)port70isStuck=0;
uintbuff=(unsigned int)inportb(0x70);
#define msg_main_0233 "main.233: Enable NMIs -- call this log with parameters computed outside the function since x86 code calls it\r\nbackwards on stack and then it will fail to be set properly to display our numbers.\r\n Port 0x70 currently contains 0x"
WriteBookFromProgram(messageHandle, msg_main_0233, sizeof(msg_main_0233), 0);
itoa(uintbuff,strbuff,16);
WriteBookFromProgram(messageHandle, strbuff, strlen(strbuff)+1,0);
WriteBookFromProgram(messageHandle, " -- ", 5, 0);
itoa(uintbuff,strbuff,2);
WriteBookFromProgram(messageHandle, strbuff, strlen(strbuff)+1,0);
WriteBookFromProgram(messageHandle, "b", 2, 0);
WriteBookFromProgram(messageHandle, "\r\n\r\n\r\n", 7, 0);
if(inportb(0x70)!=port70initialValue)port70isStuck=0;
if(uintbuff&0x80)
{
#define msg_main_0008 "main.121: Bit 7 of port 0x70 (CMOS Memory Index) is 1\r\n so NMIs are disabled.\r\n\r\n"
WriteBookFromProgram(messageHandle, msg_main_0008, sizeof(msg_main_0008), 0);
}
else
{
#define msg_main_0009 "main.126: Bit 7 of port 0x70 is 0\r\n so NMIs are enabled.\r\n\r\n"
WriteBookFromProgram(messageHandle, msg_main_0009, sizeof(msg_main_0009), 0);
}
/* enable NMIs */
outportb (0x70, inportb(0x70) & 0x7F);
if(inportb(0x70)!=port70initialValue)port70isStuck=0;
uintbuff=(unsigned int)inportb(0x70);
#define msg_main_0259 "main.259: Now port 0x70 contains 0x"
WriteBookFromProgram(messageHandle, msg_main_0259, sizeof(msg_main_0259), 0);
itoa(uintbuff,strbuff,16);
WriteBookFromProgram(messageHandle, strbuff, strlen(strbuff)+1,0);
WriteBookFromProgram(messageHandle, " -- ", 5, 0);
itoa(uintbuff,strbuff,2);
WriteBookFromProgram(messageHandle, strbuff, strlen(strbuff)+1,0);
WriteBookFromProgram(messageHandle, "b", 2, 0);
WriteBookFromProgram(messageHandle, "\r\n\r\n\r\n", 7, 0);
if(inportb(0x70)!=port70initialValue)port70isStuck=0;
#define msg_main_0012 "main.235: Execute STI.\r\n\r\n"
WriteBookFromProgram(messageHandle, msg_main_0012, sizeof(msg_main_0012), 0);
if(port70isStuck)
{
#define msg_main_0291 "main.291: WARNING: Port 0x70 is stuck on reads to %sb, so we can't enable/disable NMIs\r\n It must be a machine with an invalid port 0x70 on reads and write-only NMI enabled/disabled state at write-only byte port 0x70.\r\n\r\n"
sprintf(strbuff,msg_main_0291,itoa((unsigned int)inportb(0x70),strbuff2,2));
WriteBookFromProgram(messageHandle, strbuff, sizeof(strbuff), 0);
}
/* enabling interrupts */
enable();
return 0;
}