[NTVDM]
authorHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Tue, 7 Jul 2015 00:33:53 +0000 (00:33 +0000)
committerHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Tue, 7 Jul 2015 00:33:53 +0000 (00:33 +0000)
Implement basic DOS country info (some stuff is not implemented yet, but the basic functionality is there). This is needed by Microsoft Diagnostics 2.0+ (amongst others)...
See also r66039.
CORE-9903 #resolve

svn path=/trunk/; revision=68363

reactos/subsystems/mvdm/ntvdm/CMakeLists.txt
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/country.c [new file with mode: 0644]
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/country.h [new file with mode: 0644]
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dos.c
reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/dos.h

index fd91955..d1de397 100644 (file)
@@ -30,6 +30,7 @@ list(APPEND SOURCE
     hardware/video/svga.c
     dos/dos32krnl/bios.c
     dos/dos32krnl/condrv.c
+    dos/dos32krnl/country.c
     dos/dos32krnl/device.c
     dos/dos32krnl/dos.c
     dos/dos32krnl/dosfiles.c
diff --git a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/country.c b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/country.c
new file mode 100644 (file)
index 0000000..921bac0
--- /dev/null
@@ -0,0 +1,289 @@
+/*
+ * COPYRIGHT:       GPL - See COPYING in the top level directory
+ * PROJECT:         ReactOS Virtual DOS Machine
+ * FILE:            dos/dos32krnl/country.c
+ * PURPOSE:         DOS32 Country support
+ * PROGRAMMERS:     Hermes Belusca-Maito (hermes.belusca@sfr.fr)
+ *
+ * NOTE:            Support for default (english) language only.
+ *                  For other languages, please use COUNTRY.SYS
+ */
+
+/* INCLUDES *******************************************************************/
+
+#define NDEBUG
+
+#include "ntvdm.h"
+#include "emulator.h"
+
+#include "country.h"
+#include "memory.h"
+
+/* PRIVATE VARIABLES **********************************************************/
+
+/* CaseMap routine: should call INT 65h, AL=20h */
+// ATM, just do nothing.
+static const BYTE CaseMapRoutine[] =
+{
+    0xCB // retf
+};
+
+#pragma pack(push, 1)
+
+#define DATATABLE(name, type, len)   \
+    typedef struct _##name      \
+    {                           \
+        WORD Size;              \
+        type Data[(len)];       \
+    } name
+
+DATATABLE(UPPERCASE, CHAR, 0xFF-0x80+1);
+DATATABLE(LOWERCASE, CHAR, 0xFF-0x00+1);
+DATATABLE(FNAMETERM, BYTE,          22);
+DATATABLE(COLLATE  , BYTE, 0xFF-0x00+1);
+DATATABLE(DBCSLEAD , WORD,      0x00+1);
+
+typedef struct _COUNTRY_DATA
+{
+    BYTE      CaseMapRoutine[sizeof(CaseMapRoutine)];
+    UPPERCASE UpCaseTbl;    // Used also for filename uppercase
+    LOWERCASE LoCaseTbl;
+    FNAMETERM FNameTermTbl;
+    COLLATE   CollateTbl;
+    DBCSLEAD  DBCSLeadTbl;
+} COUNTRY_DATA, *PCOUNTRY_DATA;
+
+#pragma pack(pop)
+
+/* Global data contained in guest memory */
+static WORD CountryDataSegment;
+static PCOUNTRY_DATA CountryData;
+
+WORD YesNoTable[2] = { MAKEWORD('Y', 0), MAKEWORD('N', 0) };
+
+/*
+ * See: http://www.ctyme.com/intr/rb-3163.htm#Table1754
+ *      http://www.ctyme.com/intr/rb-3164.htm
+ *      http://www.ctyme.com/intr/rb-3166.htm
+ */
+
+/* PRIVATE FUNCTIONS **********************************************************/
+
+/* PUBLIC FUNCTIONS ***********************************************************/
+
+WORD
+DosGetCountryInfo(IN OUT PWORD CountryId,
+                  OUT PDOS_COUNTRY_INFO CountryInfo)
+{
+    INT Return;
+    DWORD NumVal;
+
+    Return = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_IDATE | LOCALE_RETURN_NUMBER, // LOCALE_ILDATE | LOCALE_RETURN_NUMBER
+                            (LPSTR)&NumVal,
+                            sizeof(NumVal));
+    if (Return == 0) return LOWORD(GetLastError());
+    CountryInfo->DateTimeFormat = (WORD)NumVal;
+
+    Return = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SCURRENCY,
+                            (LPSTR)&CountryInfo->CurrencySymbol,
+                            sizeof(CountryInfo->CurrencySymbol));
+    if (Return == 0) return LOWORD(GetLastError());
+
+    Return = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND,
+                            (LPSTR)&CountryInfo->ThousandSep,
+                            sizeof(CountryInfo->ThousandSep));
+    if (Return == 0) return LOWORD(GetLastError());
+
+    Return = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL,
+                            (LPSTR)&CountryInfo->DecimalSep,
+                            sizeof(CountryInfo->DecimalSep));
+    if (Return == 0) return LOWORD(GetLastError());
+    
+    Return = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SDATE,
+                            (LPSTR)&CountryInfo->DateSep,
+                            sizeof(CountryInfo->DateSep));
+    if (Return == 0) return LOWORD(GetLastError());
+    
+    Return = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_STIME,
+                            (LPSTR)&CountryInfo->TimeSep,
+                            sizeof(CountryInfo->TimeSep));
+    if (Return == 0) return LOWORD(GetLastError());
+    
+    // NOTE: '4: Symbol replace decimal separator' is unsupported.
+    Return = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_ICURRENCY | LOCALE_RETURN_NUMBER,
+                            (LPSTR)&NumVal,
+                            sizeof(NumVal));
+    if (Return == 0) return LOWORD(GetLastError());
+    CountryInfo->CurrencyFormat = (BYTE)NumVal;
+    
+    Return = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_ICURRDIGITS | LOCALE_RETURN_NUMBER, // LOCALE_IDIGITS | LOCALE_RETURN_NUMBER
+                            (LPSTR)&NumVal,
+                            sizeof(NumVal));
+    if (Return == 0) return LOWORD(GetLastError());
+    CountryInfo->CurrencyDigitsNum = (BYTE)NumVal;
+    
+    Return = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_ITIME | LOCALE_RETURN_NUMBER,
+                            (LPSTR)&NumVal,
+                            sizeof(NumVal));
+    if (Return == 0) return LOWORD(GetLastError());
+    CountryInfo->TimeFormat = (BYTE)NumVal;
+
+    CountryInfo->CaseMapPtr = MAKELONG(FIELD_OFFSET(COUNTRY_DATA, CaseMapRoutine), CountryDataSegment);
+    
+    // CountryInfo->DataListSep;
+
+    return ERROR_SUCCESS;
+}
+
+WORD
+DosGetCountryInfoEx(IN BYTE InfoId,
+                    IN WORD CodePage,
+                    IN WORD CountryId,
+                    OUT PDOS_COUNTRY_INFO_2 CountryInfo,
+                    IN OUT PWORD BufferSize)
+{
+    // FIXME: use: CodePage; CountryId
+    // FIXME: Check BufferSize
+    // FIXME: Use NLSFUNC resident?
+
+    switch (InfoId)
+    {
+        /* Get General Internationalization Info (similar to AX=3800h) */
+        case 0x01:
+        {
+            WORD ErrorCode;
+            ErrorCode = DosGetCountryInfo(&CountryId,
+                                          &CountryInfo->CountryInfoEx.CountryInfo);
+            if (ErrorCode != ERROR_SUCCESS) return ErrorCode;
+            CountryInfo->CountryInfoEx.Size = sizeof(CountryInfo->CountryInfoEx);
+            CountryInfo->CountryInfoEx.CountryId = CountryId;
+            // CountryInfo->CodePage;
+            break;
+        }
+
+        /* Get Pointer to Uppercase Table */
+        case 0x02:
+            CountryInfo->UpCaseTblPtr = MAKELONG(FIELD_OFFSET(COUNTRY_DATA, UpCaseTbl), CountryDataSegment);
+            break;
+
+        /* Get Pointer to Lowercase Table */
+        case 0x03:
+            CountryInfo->LoCaseTblPtr = MAKELONG(FIELD_OFFSET(COUNTRY_DATA, LoCaseTbl), CountryDataSegment);
+            break;
+
+        /* Get Pointer to Filename Uppercase Table */
+        case 0x04:
+            CountryInfo->FNameUpCaseTblPtr = MAKELONG(FIELD_OFFSET(COUNTRY_DATA, UpCaseTbl), CountryDataSegment);
+            break;
+
+        /* Get Pointer to Filename Terminator Table */
+        case 0x05:
+            CountryInfo->FNameTermTblPtr = MAKELONG(FIELD_OFFSET(COUNTRY_DATA, FNameTermTbl), CountryDataSegment);
+            break;
+
+        /* Get Pointer to Collating Sequence Table */
+        case 0x06:
+            CountryInfo->CollateTblPtr = MAKELONG(FIELD_OFFSET(COUNTRY_DATA, CollateTbl), CountryDataSegment);
+            break;
+
+        /* Get Pointer to Double-Byte Character Set Table */
+        case 0x07:
+            CountryInfo->DBCSLeadTblPtr = MAKELONG(FIELD_OFFSET(COUNTRY_DATA, DBCSLeadTbl), CountryDataSegment);
+            break;
+
+        default:
+            return ERROR_CALL_NOT_IMPLEMENTED;
+    }
+    CountryInfo->InfoId = InfoId;
+
+    return ERROR_SUCCESS;
+}
+
+WORD DosIfCharYesNo(WORD Char)
+{
+    Char = toupper(Char);
+
+    /* NO-type */
+    if (Char == YesNoTable[1])
+        return 0x0000;
+    /* YES-type */
+    if (Char == YesNoTable[0])
+        return 0x0001;
+    /* Unknown type */
+        return 0x0002;
+}
+
+CHAR DosToUpper(CHAR Char)
+{
+    // FIXME: Use the current locale
+    return toupper(Char);
+}
+
+VOID DosToUpperStrN(PCHAR DestStr, PCHAR SrcStr, WORD Length)
+{
+    while (Length-- > 0)
+        *DestStr++ = toupper(*SrcStr++);
+}
+
+VOID DosToUpperStrZ(PSTR DestStr, PSTR SrcStr)
+{
+    while (*SrcStr)
+        *DestStr++ = toupper(*SrcStr++);
+}
+
+BOOLEAN DosCountryInitialize(VOID)
+{
+    UINT i;
+
+    /* Initialize some memory to store country information */
+    // FIXME: Can we use instead some static area from the DOS data structure??
+    CountryDataSegment = DosAllocateMemory(sizeof(COUNTRY_DATA), NULL);
+    if (CountryDataSegment == 0) return FALSE;
+    CountryData = (PCOUNTRY_DATA)SEG_OFF_TO_PTR(CountryDataSegment, 0x0000);
+    
+    RtlMoveMemory(CountryData->CaseMapRoutine,
+                  CaseMapRoutine,
+                  sizeof(CaseMapRoutine));
+
+    CountryData->UpCaseTbl.Size = ARRAYSIZE(CountryData->UpCaseTbl.Data);
+    for (i = 0; i < CountryData->UpCaseTbl.Size; ++i)
+        CountryData->UpCaseTbl.Data[i] = 0x80 + i;
+
+    CountryData->LoCaseTbl.Size = ARRAYSIZE(CountryData->LoCaseTbl.Data);
+    for (i = 0; i < CountryData->LoCaseTbl.Size; ++i)
+        CountryData->LoCaseTbl.Data[i] = i;
+
+    CountryData->FNameTermTbl.Size = ARRAYSIZE(CountryData->FNameTermTbl.Data);
+    CountryData->FNameTermTbl.Data[ 0] = 0x01; // Dummy Byte
+    CountryData->FNameTermTbl.Data[ 1] = 0x00; //  Lowest permissible Character Value for Filename
+    CountryData->FNameTermTbl.Data[ 2] = 0xFF; // Highest permissible Character Value for Filename
+    CountryData->FNameTermTbl.Data[ 3] = 0x00; // Dummy Byte
+    CountryData->FNameTermTbl.Data[ 4] = 0x00; // First excluded Character in Range \ all characters in this
+    CountryData->FNameTermTbl.Data[ 5] = 0x20; //  Last excluded Character in Range / range are illegal
+    CountryData->FNameTermTbl.Data[ 6] = 0x02; // Dummy Byte
+    CountryData->FNameTermTbl.Data[ 7] = 14;   // Number of illegal (terminator) Characters
+//  CountryData->FNameTermTbl.Data[ 8] = ".\"/\\[]:|<>+=;,"; // Characters which terminate a Filename
+    CountryData->FNameTermTbl.Data[ 8] = '.';
+    CountryData->FNameTermTbl.Data[ 9] = '\"';
+    CountryData->FNameTermTbl.Data[10] = '/';
+    CountryData->FNameTermTbl.Data[11] = '\\';
+    CountryData->FNameTermTbl.Data[12] = '[';
+    CountryData->FNameTermTbl.Data[13] = ']';
+    CountryData->FNameTermTbl.Data[14] = ':';
+    CountryData->FNameTermTbl.Data[15] = '|';
+    CountryData->FNameTermTbl.Data[16] = '<';
+    CountryData->FNameTermTbl.Data[17] = '>';
+    CountryData->FNameTermTbl.Data[18] = '+';
+    CountryData->FNameTermTbl.Data[19] = '=';
+    CountryData->FNameTermTbl.Data[20] = ';';
+    CountryData->FNameTermTbl.Data[21] = ',';
+
+    CountryData->CollateTbl.Size = ARRAYSIZE(CountryData->CollateTbl.Data);
+    for (i = 0; i < CountryData->LoCaseTbl.Size; ++i)
+        CountryData->LoCaseTbl.Data[i] = i;
+
+    CountryData->DBCSLeadTbl.Size = 0; // Empty DBCS table
+    CountryData->DBCSLeadTbl.Data[0] = 0x0000;
+
+    return TRUE;
+}
diff --git a/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/country.h b/reactos/subsystems/mvdm/ntvdm/dos/dos32krnl/country.h
new file mode 100644 (file)
index 0000000..ecabd00
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * COPYRIGHT:       GPL - See COPYING in the top level directory
+ * PROJECT:         ReactOS Virtual DOS Machine
+ * FILE:            dos/dos32krnl/country.h
+ * PURPOSE:         DOS32 Country support
+ * PROGRAMMERS:     Hermes Belusca-Maito (hermes.belusca@sfr.fr)
+ *
+ * NOTE:            Support for default (english) language only.
+ *                  For other languages, please use COUNTRY.SYS
+ */
+
+#ifndef _COUNTRY_H_
+#define _COUNTRY_H_
+
+/* DEFINITIONS ****************************************************************/
+
+#pragma pack(push, 1)
+
+#if 0 // Keep here for information purposes only
+// DOS 2.00-2.10 country info structure
+typedef struct _DOS_COUNTRY_INFO_OLD
+{
+    WORD DateTimeFormat;
+    CHAR CurrencySymbol[2];
+    CHAR ThousandSep[2];
+    CHAR DecimalSep[2];
+    BYTE Reserved[24];
+} DOS_COUNTRY_INFO_OLD, *PDOS_COUNTRY_INFO_OLD;
+C_ASSERT(sizeof(DOS_COUNTRY_INFO_OLD) == 0x20);
+#endif
+
+// DOS 2.11+ compatible country info structure
+typedef struct _DOS_COUNTRY_INFO
+{
+    WORD DateTimeFormat;
+    CHAR CurrencySymbol[5];
+    CHAR ThousandSep[2];
+    CHAR DecimalSep[2];
+    CHAR DateSep[2];
+    CHAR TimeSep[2];
+    BYTE CurrencyFormat;
+    BYTE CurrencyDigitsNum;
+    BYTE TimeFormat;
+    DWORD CaseMapPtr;
+    CHAR DataListSep[2];
+    BYTE Reserved[10];
+} DOS_COUNTRY_INFO, *PDOS_COUNTRY_INFO;
+C_ASSERT(sizeof(DOS_COUNTRY_INFO) == 0x22);
+
+typedef struct _DOS_COUNTRY_INFO_EX
+{
+    WORD Size;
+    WORD CountryId;
+    WORD CodePage;
+    DOS_COUNTRY_INFO CountryInfo;
+} DOS_COUNTRY_INFO_EX, *PDOS_COUNTRY_INFO_EX;
+C_ASSERT(sizeof(DOS_COUNTRY_INFO_EX) == 0x28);
+
+typedef struct _DOS_COUNTRY_INFO_2
+{
+    BYTE InfoId;
+    union
+    {
+        DOS_COUNTRY_INFO_EX CountryInfoEx;
+
+        DWORD UpCaseTblPtr;
+        DWORD LoCaseTblPtr;
+
+        DWORD FNameUpCaseTblPtr;
+        DWORD FNameTermTblPtr;
+
+        DWORD CollateTblPtr;
+        DWORD DBCSLeadTblPtr;
+    };
+} DOS_COUNTRY_INFO_2, *PDOS_COUNTRY_INFO_2;
+
+#pragma pack(pop)
+
+/* FUNCTIONS ******************************************************************/
+
+WORD
+DosGetCountryInfo(IN OUT PWORD CountryId,
+                  OUT PDOS_COUNTRY_INFO CountryInfo);
+
+WORD
+DosGetCountryInfoEx(IN BYTE InfoId,
+                    IN WORD CodePage,
+                    IN WORD CountryId,
+                    OUT PDOS_COUNTRY_INFO_2 CountryInfo,
+                    IN OUT PWORD BufferSize);
+
+WORD DosIfCharYesNo(WORD Char);
+CHAR DosToUpper(CHAR Char);
+VOID DosToUpperStrN(PCHAR DestStr, PCHAR SrcStr, WORD Length);
+VOID DosToUpperStrZ(PSTR DestStr, PSTR SrcStr);
+
+BOOLEAN DosCountryInitialize(VOID);
+
+#endif
index efed9ea..15bf0b9 100644 (file)
@@ -18,6 +18,7 @@
 
 #include "dos.h"
 #include "dos/dem.h"
+#include "country.h"
 #include "device.h"
 #include "handle.h"
 #include "dosfiles.h"
@@ -895,59 +896,21 @@ VOID WINAPI DosInt21h(LPWORD Stack)
         /* Get/Set Country-dependent Information */
         case 0x38:
         {
-            INT Return;
-            PDOS_COUNTRY_CODE_BUFFER CountryCodeBuffer =
-                (PDOS_COUNTRY_CODE_BUFFER)SEG_OFF_TO_PTR(getDS(), getDX());
+            WORD CountryId = getAL() < 0xFF ? getAL() : getBX();
+            WORD ErrorCode;
+            
+            ErrorCode = DosGetCountryInfo(&CountryId,
+                                          (PDOS_COUNTRY_INFO)SEG_OFF_TO_PTR(getDS(), getDX()));
 
-            if (getAL() == 0x00)
+            if (ErrorCode == ERROR_SUCCESS)
             {
-                /* Get */
-                Return = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_IDATE,
-                                        (LPSTR)&CountryCodeBuffer->TimeFormat,
-                                        sizeof(CountryCodeBuffer->TimeFormat));
-                if (Return == 0)
-                {
-                    Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-                    setAX(LOWORD(GetLastError()));
-                    break;
-                }
-
-                Return = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SCURRENCY,
-                                        (LPSTR)&CountryCodeBuffer->CurrencySymbol,
-                                        sizeof(CountryCodeBuffer->CurrencySymbol));
-                if (Return == 0)
-                {
-                    Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-                    setAX(LOWORD(GetLastError()));
-                    break;
-                }
-
-                Return = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND,
-                                        (LPSTR)&CountryCodeBuffer->ThousandSep,
-                                        sizeof(CountryCodeBuffer->ThousandSep));
-                if (Return == 0)
-                {
-                    Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-                    setAX(LOWORD(GetLastError()));
-                    break;
-                }
-
-                Return = GetLocaleInfoA(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL,
-                                        (LPSTR)&CountryCodeBuffer->DecimalSep,
-                                        sizeof(CountryCodeBuffer->DecimalSep));
-                if (Return == 0)
-                {
-                    Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
-                    setAX(LOWORD(GetLastError()));
-                    break;
-                }
-
                 Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+                setBX(CountryId);
             }
             else
             {
-                // TODO: NOT IMPLEMENTED
-                UNIMPLEMENTED;
+                Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                setAX(ErrorCode);
             }
 
             break;
@@ -1616,7 +1579,7 @@ VOID WINAPI DosInt21h(LPWORD Stack)
              */
 
             // FIXME: Check for buffer validity?
-            // It should be a ASCIZ path ending with a '\' + 13 zero bytes
+            // It should be a ASCIIZ path ending with a '\' + 13 zero bytes
             // to receive the generated filename.
 
             /* First create the temporary file */
@@ -1769,6 +1732,91 @@ VOID WINAPI DosInt21h(LPWORD Stack)
             break;
         }
 
+        /* Extended Country Information */
+        case 0x65:
+        {
+            switch (getAL())
+            {
+                case 0x01: case 0x02: case 0x03:
+                case 0x04: case 0x05: case 0x06:
+                case 0x07:
+                {
+                    WORD BufferSize = getCX();
+                    WORD ErrorCode;
+                    ErrorCode = DosGetCountryInfoEx(getAL(),
+                                                    getBX(),
+                                                    getDX(),
+                                                    (PDOS_COUNTRY_INFO_2)SEG_OFF_TO_PTR(getES(), getDI()),
+                                                    &BufferSize);
+                    if (ErrorCode == ERROR_SUCCESS)
+                    {
+                        Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+                        setCX(BufferSize);
+                    }
+                    else
+                    {
+                        Stack[STACK_FLAGS] |= EMULATOR_FLAG_CF;
+                        setAX(ErrorCode);
+                    }
+
+                    break;
+                }
+
+                /* Country-dependent Character Capitalization -- Character */
+                case 0x20:
+                /* Country-dependent Filename Capitalization -- Character */
+                case 0xA0:
+                {
+                    setDL(DosToUpper(getDL()));
+                    Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+                    // setAX(ERROR_SUCCESS);
+                    break;
+                }
+
+                /* Country-dependent Character Capitalization -- Counted ASCII String */
+                case 0x21:
+                /* Country-dependent Filename Capitalization -- Counted ASCII String */
+                case 0xA1:
+                {
+                    PCHAR Str = (PCHAR)SEG_OFF_TO_PTR(getDS(), getDX());
+                    // FIXME: Check for NULL ptr!!
+                    DosToUpperStrN(Str, Str, getCX());
+                    Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+                    // setAX(ERROR_SUCCESS);
+                    break;
+                }
+
+                /* Country-dependent Character Capitalization -- ASCIIZ String */
+                case 0x22:
+                /* Country-dependent Filename Capitalization -- ASCIIZ String */
+                case 0xA2:
+                {
+                    PSTR Str = (PSTR)SEG_OFF_TO_PTR(getDS(), getDX());
+                    // FIXME: Check for NULL ptr!!
+                    DosToUpperStrZ(Str, Str);
+                    Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+                    // setAX(ERROR_SUCCESS);
+                    break;
+                }
+
+                /* Determine if Character represents YES/NO Response */
+                case 0x23:
+                {
+                    setAX(DosIfCharYesNo(MAKEWORD(getDL(), getDH())));
+                    Stack[STACK_FLAGS] &= ~EMULATOR_FLAG_CF;
+                    break;
+                }
+
+                default:
+                {
+                    DPRINT1("INT 21h, AH = 65h, subfunction AL = %Xh NOT IMPLEMENTED\n",
+                            getAL());
+                }
+            }
+
+            break;
+        }
+
         /* Set Handle Count */
         case 0x67:
         {
@@ -2139,6 +2187,9 @@ BOOLEAN DosKRNLInitialize(VOID)
     /* Unimplemented DOS interrupts */
     RegisterDosInt32(0x2A, NULL); // Network - Installation Check
 
+    /* Initialize country data */
+    DosCountryInitialize();
+
     /* Load the CON driver */
     ConDrvInitialize();
 
index 8b869bd..0c0baca 100644 (file)
@@ -125,14 +125,6 @@ typedef struct _DOS_FIND_FILE_BLOCK
     CHAR FileName[13];
 } DOS_FIND_FILE_BLOCK, *PDOS_FIND_FILE_BLOCK;
 
-typedef struct _DOS_COUNTRY_CODE_BUFFER
-{
-    WORD TimeFormat;
-    WORD CurrencySymbol;
-    WORD ThousandSep;
-    WORD DecimalSep;
-} DOS_COUNTRY_CODE_BUFFER, *PDOS_COUNTRY_CODE_BUFFER;
-
 typedef struct _DOS_SDA
 {
     BYTE PrinterEchoFlag;