allocazione XMS a 16 bit DOS
Domanda
Si prega di perdonare i miei tentativi di necromanzia, ma in realtà ho bisogno di scrivere del codice per DOS a 16 bit (!). Devo verificare che un pezzo di software esegue correttamente quando costruito per una piattaforma a 16 bit, e ho scoperto che le nostre stazioni di lavoro di XP possono effettivamente funzionare a 16 bit applicazioni DOS, che permette di utilizzare il sistema di test batch esistente.
In ogni caso, il software è composto da una biblioteca e un database. database più piccoli (fino a ~ 150 kB) possono sia essere definiti come un array globale statico o lette da file in un buffer allocato con halloc()
, quindi sono abbastanza fiducioso che lo strumento biblioteca e di test è costruito in modo corretto.
Tuttavia, abbiamo anche alcune banche dati più grandi per prova, fino a ~ 1.8Mb. Questo è troppo grande per allocare normalmente, così ho scritto una piccola libreria di supporto per allocare la memoria XMS. Con questo, posso allocare con successo, l'uso (vale a dire di scrittura e lettura dei dati) e gratis fino a 16 Mb di dati in un piccolo programma giocattolo. Tuttavia, quando si utilizzano i servizi XMS nell'applicazione "reale", ottengo il seguente errore:
The NTVDM CPU has encountered an illegal instruction.
CS:0000 IP:00ba OP:0f 04 10 0e 51
Googling su questo errore ha dato risultati poco rilevanti, questo tipo di errore sembra essere di solito la colpa su di malware vari.
Il codice di base è severo C90, il compilatore attualmente utilizzata per DOS costruisce è OpenWatcom 1.9 utilizzando il "grande" modello di memoria. Non ci sono avvisi o errori durante la costruzione.
La libreria di supporto XMS segue, l'errore sembra si verifica dopo la chiamata a xmsmalloc ():
/* This file implements rudimentary XMS memory handling.
* Documentation on the XMS API was found on http://www.qzx.com/pc-gpe/xms30.txt
*/
#include <stddef.h> /* Definition of NULL */
#include <limits.h> /* Definition of UINT_MAX */
#include <stdio.h> /* fprintf and (FILE *) */
/* Allow external configuration of maximum concurrent XMS allocations */
#ifndef MAX_XMS_ALLOCATIONS
#define MAX_XMS_ALLOCATIONS 4
#endif
/* Address of the XMS driver */
static long XMSControl;
/* Mapping of XMS handle <-> normal pointer */
typedef struct {
unsigned int XMSHandle;
void huge * XMSPointer;
} XMSHandleMap;
static XMSHandleMap allocMap[MAX_XMS_ALLOCATIONS];
/* Set up the XMS driver, returns 0 on success and non-zero on failure */
static int initxms(void)
{
char XMSStatus = 0;
if ( XMSControl == 0 )
{
__asm {
; Is an XMS driver installed?
mov ax,4300h
int 2Fh
mov [XMSStatus], al
}
if ( XMSStatus == 0x80 )
{
__asm {
; Get the address of the driver control function
mov ax,4310h
int 2Fh
mov word ptr [XMSControl] ,bx
mov word ptr [XMSControl+2],es
}
}
}
return ( XMSControl == 0 );
}
/* Allocate a slab of memory from XMS */
void huge * xmsmalloc(long unsigned int size)
{
unsigned int kB;
unsigned int XMSStatus = 0;
unsigned int XMSHandle = 0;
void huge * XMSPointer = NULL;
int n;
/* If we can not initialize XMS, the allocation fails */
if ( initxms() )
return NULL;
/* It is not possible to allocate more kilobytes than a 16-bit register can hold :-) */
if ( size / 1024 > UINT_MAX )
return NULL;
/* Get the next free entry in the handle <-> pointer mapping */
for ( n = 0; n < MAX_XMS_ALLOCATIONS; n++ )
{
if ( allocMap[n].XMSPointer == NULL )
break;
}
if ( n == MAX_XMS_ALLOCATIONS )
return NULL;
kB = size / 1024 + (size % 1024 > 0);
__asm {
; Allocate [kB] kilobytes of XMS memory
mov ah, 09h
mov dx, [kB]
call [XMSControl]
mov [XMSStatus], ax
mov [XMSHandle], dx
}
/* Check if XMS allocation failed */
if ( XMSStatus == 0)
return NULL;
__asm {
; Convert XMS handle to normal pointer
mov ah, 0Ch
mov dx, [XMSHandle]
call [XMSControl]
mov [XMSStatus], ax
mov word ptr [XMSPointer], bx
mov word ptr [XMSPointer+2],dx
}
if ( XMSStatus == 0 )
{
/* Lock failed, deallocate the handle */
__asm {
; Free XMS handle
mov ah, 0Ah
mov dx, [XMSHandle]
call [XMSControl]
; Return value is not really interesting
; mov [XMSStatus], ax
}
return NULL;
}
/* Create an entry in the handle <-> pointer mapping */
allocMap[n].XMSHandle = XMSHandle;
allocMap[n].XMSPointer = XMSPointer;
return XMSPointer;
}
/* Free a pointer allocated with xmsalloc */
void xmsfree(void huge * XMSPointer)
{
int n;
if ( XMSPointer == NULL )
return;
if ( initxms() )
return;
for ( n = 0; n < MAX_XMS_ALLOCATIONS; n++ )
{
if ( allocMap[n].XMSPointer == XMSPointer )
{
int XMSHandle = allocMap[n].XMSHandle;
__asm {
; Unlock handle so we can free the memory block
mov ah, 0Dh
mov dx, [XMSHandle]
call [XMSControl]
; Free XMS memory
mov ah, 0Ah
mov dx, [XMSHandle]
call [XMSControl]
; Return value ignored
}
/* Clear handle <-> pointer map entry so it can be reused */
allocMap[n].XMSHandle = 0;
allocMap[n].XMSPointer = NULL;
return;
}
}
}
/* Write a memory report for debugging purposes */
void xmsreport(FILE * stream)
{
int XMSVersionNumber = 0;
int XMSLargestBlock = 0;
int XMSTotal = 0;
if ( initxms() )
{
puts("Could not initialize XMS Driver!");
return;
}
__asm {
; Get the driver version number
mov ah,00h
call [XMSControl] ; Get XMS Version Number
mov [XMSVersionNumber], ax
; Get the amount of free XMS memory
mov ah, 08h
call [XMSControl]
mov [XMSLargestBlock], ax
mov [XMSTotal], dx
}
fprintf(stream, "XMS Version number: %d\n", XMSVersionNumber);
fprintf(stream, "Largest available block: %d kB (%d kB total)\n", XMSLargestBlock, XMSTotal);
}
Alcune domande concrete:
- Dove posso trovare ulteriori informazioni sul messaggio di errore (immagino mezzi OP codice operativo, ma per quanto riguarda gli altri campi?)
- Il riferimento API XMS ho trovato ancora si applica quando in esecuzione su Windows XP, o c'è qualche nuova versione dovrei fare riferimento?
- Potrei avvitata dello stato del sistema con l'assemblatore inline? Come devo risolvere questo?
- Avete un'idea migliore su come risolvere questo problema? :-) estensori DOS sembrano richiedere modalità a 32 bit, l'intero punto di questo esercizio utilizza la modalità a 16 bit.
Soluzione 2
Ack, devo rispondere alla mia domanda.
Mentre è possibile per allocare relativamente grandi quantità di memoria che utilizzano le routine XMS, non è possibile esprimere il indirizzo con i 16 bit. Il valore di ritorno è un 32-bit lineari indirizzo, e se ho capito correttamente semplicemente non è possibile utilizzare tale da un ambiente a 16 bit.
dovrebbe essere possibile utilizzare tale blocco di memoria mappando una parte a qualche buffer di memoria locale di sotto del limite indirizzabile 1Mb e spostare la finestra, molto simile a come funziona SME, ma che è semplicemente non ne vale la pena.
Altri suggerimenti
vedo due problemi.
1 °: Assicurarsi che il compilatore utilizza un gran lunga chiamata invece di una chiamata nei pressi altrimenti si passa al segmento sbagliato ed eseguire codice sconosciuto e la generazione di un codice operativo non valido possably ... che è quello che sembra accadere. Prova "chiamata lontano [XMSControl]" nel codice, se il compilatore default è vicino chiamate.
2 °: NTVDM.EXE esegue codice in modalità virtuale 86 e non è vero modalità reale. Se è vero che Windows XP supporta 16 applicazioni bit, ha un supporto limitato. Si può incorrere in altri problemi con il database lungo la linea a causa di questo. Per esempio non sarà in grado di accedere USB, quindi non è possibile stampare su una stampante USB e sarà necessario utilizzare una vecchia stampante stile porta parallela. Buona fortuna findign uno di quelli usati in un garage vendita ...
'0f 04 10 0e 51' è un'istruzione non valida in sé si sta tentando di eseguire.
Ho controllato, e non vedo di istruzioni x86 con questo codice 0F 04
http://ref.x86asm.net/geek.html#x0F04
Quindi è necessario trovare quale codice di generare tali un'istruzione non valida e risolverlo.
Per quanto riguarda la memoria, ho sempre pensato che lo SME è più facile da usare rispetto XMS, ma potrei sbagliarmi.