mirror of
git://source.winehq.org/git/wine.git
synced 2024-09-05 20:59:42 +00:00
Moved most global data out of the LPDOSTASK structure.
Allocate DPMI real-mode segments globally at startup. Try to allocate DOS memory at address 0.
This commit is contained in:
parent
0ff083bab8
commit
770eb51ed5
|
@ -11,38 +11,13 @@
|
|||
#include "winbase.h" /* for LPSTARTUPINFO32A */
|
||||
#include "winnt.h" /* for PCONTEXT */
|
||||
|
||||
typedef struct _DOSSYSTEM {
|
||||
int id;
|
||||
void *data;
|
||||
struct _DOSSYSTEM *next;
|
||||
} DOSSYSTEM;
|
||||
|
||||
struct _DOSEVENT;
|
||||
|
||||
typedef struct _DOSTASK {
|
||||
LPVOID img;
|
||||
unsigned img_ofs;
|
||||
WORD psp_seg,load_seg;
|
||||
WORD init_cs,init_ip,init_ss,init_sp;
|
||||
WORD xms_seg;
|
||||
WORD dpmi_seg,dpmi_sel,dpmi_flag;
|
||||
char mm_name[128];
|
||||
int mm_fd;
|
||||
HANDLE hReadPipe,hXPipe,hConInput,hConOutput;
|
||||
int read_pipe,write_pipe;
|
||||
pid_t task;
|
||||
int sig_sent,entered,idling;
|
||||
struct _DOSEVENT *pending,*current;
|
||||
DOSSYSTEM *sys;
|
||||
WORD psp_seg;
|
||||
WORD dpmi_flag;
|
||||
} DOSTASK, *LPDOSTASK;
|
||||
|
||||
typedef struct _DOSEVENT {
|
||||
int irq,priority;
|
||||
void (*relay)(LPDOSTASK,CONTEXT86*,void*);
|
||||
void *data;
|
||||
struct _DOSEVENT *next;
|
||||
} DOSEVENT, *LPDOSEVENT;
|
||||
|
||||
#define DOS_PRIORITY_REALTIME 0 /* IRQ0 */
|
||||
#define DOS_PRIORITY_KEYBOARD 1 /* IRQ1 */
|
||||
#define DOS_PRIORITY_VGA 2 /* IRQ9 */
|
||||
|
@ -50,26 +25,19 @@ typedef struct _DOSEVENT {
|
|||
#define DOS_PRIORITY_SERIAL 10 /* IRQ4 */
|
||||
|
||||
#if defined(linux) && defined(__i386__)
|
||||
|
||||
#define MZ_SUPPORTED
|
||||
|
||||
extern BOOL MZ_InitTask( LPDOSTASK lpDosTask );
|
||||
extern void MZ_KillTask( LPDOSTASK lpDosTask );
|
||||
extern LPDOSTASK MZ_AllocDPMITask( void );
|
||||
|
||||
#endif /* linux-i386 */
|
||||
|
||||
#define V86_FLAG 0x00020000
|
||||
|
||||
extern BOOL MZ_LoadImage( HMODULE module, HANDLE hFile, LPCSTR filename );
|
||||
extern LPDOSTASK MZ_Current( void );
|
||||
extern LPDOSTASK MZ_AllocDPMITask( void );
|
||||
extern int DOSVM_Enter( CONTEXT86 *context );
|
||||
extern void DOSVM_Wait( int read_pipe, HANDLE hObject );
|
||||
extern void DOSVM_QueueEvent( int irq, int priority, void (*relay)(LPDOSTASK,CONTEXT86*,void*), void *data );
|
||||
extern void DOSVM_QueueEvent( int irq, int priority, void (*relay)(CONTEXT86*,void*), void *data );
|
||||
extern void DOSVM_PIC_ioport_out( WORD port, BYTE val );
|
||||
extern void DOSVM_SetTimer( unsigned ticks );
|
||||
extern unsigned DOSVM_GetTimer( void );
|
||||
extern void DOSVM_SetSystemData( int id, void *data );
|
||||
extern void* DOSVM_GetSystemData( int id );
|
||||
|
||||
#endif /* __WINE_DOSEXE_H */
|
||||
|
|
|
@ -60,6 +60,8 @@ extern ldt_copy_entry ldt_copy[LDT_SIZE];
|
|||
|
||||
#define PTR_SEG_OFF_TO_LIN(seg,off) \
|
||||
((void*)(GET_SEL_BASE(seg) + (unsigned int)(off)))
|
||||
#define PTR_REAL_TO_LIN(seg,off) \
|
||||
((void*)(((unsigned int)(seg) << 4) + LOWORD(off)))
|
||||
#define PTR_SEG_TO_LIN(ptr) \
|
||||
PTR_SEG_OFF_TO_LIN(SELECTOROF(ptr),OFFSETOF(ptr))
|
||||
#define PTR_SEG_OFF_TO_SEGPTR(seg,off) \
|
||||
|
|
|
@ -128,8 +128,11 @@ extern BYTE * DOSMEM_BiosSys();
|
|||
|
||||
extern DWORD DOSMEM_CollateTable;
|
||||
|
||||
extern DWORD DOSMEM_ErrorCall;
|
||||
extern DWORD DOSMEM_ErrorBuffer;
|
||||
/* various real-mode code stubs */
|
||||
extern WORD DOSMEM_wrap_seg;
|
||||
extern WORD DOSMEM_xms_seg;
|
||||
extern WORD DOSMEM_dpmi_seg;
|
||||
extern WORD DOSMEM_dpmi_sel;
|
||||
|
||||
extern DWORD DOS_LOLSeg;
|
||||
extern struct _DOS_LISTOFLISTS * DOSMEM_LOL();
|
||||
|
@ -249,7 +252,7 @@ extern void ASPI_DOS_HandleInt(CONTEXT86 *context);
|
|||
* a *32-bit* general register as third parameter, e.g.
|
||||
* CTX_SEG_OFF_TO_LIN( context, DS_reg(context), EDX_reg(context) )
|
||||
* This will generate a linear pointer in all three cases:
|
||||
* Real-Mode: Seg*16 + LOWORD(Offset) + V86BASE
|
||||
* Real-Mode: Seg*16 + LOWORD(Offset)
|
||||
* 16-bit: convert (Seg, LOWORD(Offset)) to linear
|
||||
* 32-bit: use Offset as linear address (DeviceIoControl!)
|
||||
*
|
||||
|
@ -258,8 +261,8 @@ extern void ASPI_DOS_HandleInt(CONTEXT86 *context);
|
|||
* (0 counts also as 32-bit segment).
|
||||
*/
|
||||
#define CTX_SEG_OFF_TO_LIN(context,seg,off) \
|
||||
(ISV86(context) ? (void*)(V86BASE(context)+((seg)<<4)+(off&0xffff)) : \
|
||||
(!seg || IS_SELECTOR_SYSTEM(seg))? (void *)off : PTR_SEG_OFF_TO_LIN(seg,off&0xffff))
|
||||
(ISV86(context) ? PTR_REAL_TO_LIN((seg),(off)) : \
|
||||
(!seg || IS_SELECTOR_SYSTEM(seg))? (void *)(off) : PTR_SEG_OFF_TO_LIN((seg),LOWORD(off)))
|
||||
|
||||
#define INT_BARF(context,num) \
|
||||
ERR( "int%x: unknown/not implemented parameters:\n" \
|
||||
|
|
|
@ -730,7 +730,6 @@ typedef HANDLE *PHANDLE;
|
|||
#define RESET_ZFLAG(context) (EFL_reg(context) &= ~0x0040)
|
||||
|
||||
#define ISV86(context) (EFL_reg(context) & 0x00020000)
|
||||
#define V86BASE(context) DOSMEM_MemoryBase()
|
||||
|
||||
|
||||
/* Macros to retrieve the current context */
|
||||
|
|
|
@ -58,6 +58,21 @@ DECLARE_DEBUG_CHANNEL(relay)
|
|||
|
||||
#undef TRY_PICRETURN
|
||||
|
||||
typedef struct _DOSEVENT {
|
||||
int irq,priority;
|
||||
void (*relay)(CONTEXT86*,void*);
|
||||
void *data;
|
||||
struct _DOSEVENT *next;
|
||||
} DOSEVENT, *LPDOSEVENT;
|
||||
|
||||
static struct _DOSEVENT *pending_event, *current_event;
|
||||
static int sig_sent, entered;
|
||||
|
||||
/* from module.c */
|
||||
extern int read_pipe, write_pipe;
|
||||
extern HANDLE hReadPipe;
|
||||
extern pid_t dosmod_pid;
|
||||
|
||||
static void do_exception( int signal, CONTEXT86 *context )
|
||||
{
|
||||
EXCEPTION_RECORD rec;
|
||||
|
@ -77,10 +92,8 @@ static void do_exception( int signal, CONTEXT86 *context )
|
|||
EXC_RtlRaiseException( &rec, context );
|
||||
}
|
||||
|
||||
static void DOSVM_Dump( LPDOSTASK lpDosTask, int fn, int sig,
|
||||
struct vm86plus_struct*VM86 )
|
||||
static void DOSVM_Dump( int fn, int sig, struct vm86plus_struct*VM86 )
|
||||
{
|
||||
unsigned iofs;
|
||||
BYTE*inst;
|
||||
int x;
|
||||
|
||||
|
@ -106,20 +119,17 @@ static void DOSVM_Dump( LPDOSTASK lpDosTask, int fn, int sig,
|
|||
fprintf(stderr,"CS=%04X DS=%04X ES=%04X SS=%04X\n",REGS.cs,REGS.ds,REGS.es,REGS.ss);
|
||||
fprintf(stderr,"IP=%04lX EFLAGS=%08lX\n",REGS.eip,REGS.eflags);
|
||||
|
||||
iofs=((DWORD)REGS.cs<<4)+REGS.eip;
|
||||
inst = PTR_REAL_TO_LIN( REGS.cs, REGS.eip );
|
||||
#undef REGS
|
||||
inst=(BYTE*)lpDosTask->img+iofs;
|
||||
printf("Opcodes:");
|
||||
for (x=0; x<8; x++) printf(" %02x",inst[x]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
static int DOSVM_Int( int vect, CONTEXT86 *context, LPDOSTASK lpDosTask )
|
||||
static int DOSVM_Int( int vect, CONTEXT86 *context )
|
||||
{
|
||||
extern UINT16 DPMI_wrap_seg;
|
||||
|
||||
if (vect==0x31) {
|
||||
if (CS_reg(context)==DPMI_wrap_seg) {
|
||||
if (CS_reg(context)==DOSMEM_wrap_seg) {
|
||||
/* exit from real-mode wrapper */
|
||||
return -1;
|
||||
}
|
||||
|
@ -129,7 +139,7 @@ static int DOSVM_Int( int vect, CONTEXT86 *context, LPDOSTASK lpDosTask )
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void DOSVM_SimulateInt( int vect, CONTEXT86 *context, LPDOSTASK lpDosTask )
|
||||
static void DOSVM_SimulateInt( int vect, CONTEXT86 *context )
|
||||
{
|
||||
FARPROC16 handler=INT_GetRMHandler(vect);
|
||||
|
||||
|
@ -137,7 +147,7 @@ static void DOSVM_SimulateInt( int vect, CONTEXT86 *context, LPDOSTASK lpDosTask
|
|||
/* if internal interrupt, call it directly */
|
||||
INT_RealModeInterrupt(vect,context);
|
||||
} else {
|
||||
WORD*stack=(WORD*)(V86BASE(context)+(((DWORD)SS_reg(context))<<4)+LOWORD(ESP_reg(context)));
|
||||
WORD*stack= PTR_REAL_TO_LIN( context->SegSs, context->Esp );
|
||||
WORD flag=LOWORD(EFL_reg(context));
|
||||
|
||||
if (IF_ENABLED(context)) flag|=IF_MASK;
|
||||
|
@ -154,51 +164,50 @@ static void DOSVM_SimulateInt( int vect, CONTEXT86 *context, LPDOSTASK lpDosTask
|
|||
}
|
||||
|
||||
#define SHOULD_PEND(x) \
|
||||
(x && ((!lpDosTask->current) || (x->priority < lpDosTask->current->priority)))
|
||||
(x && ((!current_event) || (x->priority < current_event->priority)))
|
||||
|
||||
static void DOSVM_SendQueuedEvent(CONTEXT86 *context, LPDOSTASK lpDosTask)
|
||||
static void DOSVM_SendQueuedEvent(CONTEXT86 *context)
|
||||
{
|
||||
LPDOSEVENT event = lpDosTask->pending;
|
||||
LPDOSEVENT event = pending_event;
|
||||
|
||||
if (SHOULD_PEND(event)) {
|
||||
/* remove from "pending" list */
|
||||
lpDosTask->pending = event->next;
|
||||
pending_event = event->next;
|
||||
/* process event */
|
||||
if (event->irq>=0) {
|
||||
/* it's an IRQ, move it to "current" list */
|
||||
event->next = lpDosTask->current;
|
||||
lpDosTask->current = event;
|
||||
event->next = current_event;
|
||||
current_event = event;
|
||||
TRACE_(int)("dispatching IRQ %d\n",event->irq);
|
||||
/* note that if DOSVM_SimulateInt calls an internal interrupt directly,
|
||||
* lpDosTask->current might be cleared (and event freed) in this very call! */
|
||||
DOSVM_SimulateInt((event->irq<8)?(event->irq+8):(event->irq-8+0x70),context,lpDosTask);
|
||||
* current_event might be cleared (and event freed) in this very call! */
|
||||
DOSVM_SimulateInt((event->irq<8)?(event->irq+8):(event->irq-8+0x70),context);
|
||||
} else {
|
||||
/* callback event */
|
||||
TRACE_(int)("dispatching callback event\n");
|
||||
(*event->relay)(lpDosTask,context,event->data);
|
||||
(*event->relay)(context,event->data);
|
||||
free(event);
|
||||
}
|
||||
}
|
||||
if (!SHOULD_PEND(lpDosTask->pending)) {
|
||||
if (!SHOULD_PEND(pending_event)) {
|
||||
TRACE_(int)("clearing Pending flag\n");
|
||||
CLR_PEND(context);
|
||||
}
|
||||
}
|
||||
|
||||
static void DOSVM_SendQueuedEvents(CONTEXT86 *context, LPDOSTASK lpDosTask)
|
||||
static void DOSVM_SendQueuedEvents(CONTEXT86 *context)
|
||||
{
|
||||
/* we will send all queued events as long as interrupts are enabled,
|
||||
* but IRQ events will disable interrupts again */
|
||||
while (IS_PEND(context) && IF_ENABLED(context))
|
||||
DOSVM_SendQueuedEvent(context,lpDosTask);
|
||||
DOSVM_SendQueuedEvent(context);
|
||||
}
|
||||
|
||||
void DOSVM_QueueEvent( int irq, int priority, void (*relay)(LPDOSTASK,CONTEXT86*,void*), void *data)
|
||||
void DOSVM_QueueEvent( int irq, int priority, void (*relay)(CONTEXT86*,void*), void *data)
|
||||
{
|
||||
LPDOSTASK lpDosTask = MZ_Current();
|
||||
LPDOSEVENT event, cur, prev;
|
||||
|
||||
if (lpDosTask && lpDosTask->entered) {
|
||||
if (entered) {
|
||||
event = malloc(sizeof(DOSEVENT));
|
||||
if (!event) {
|
||||
ERR_(int)("out of memory allocating event entry\n");
|
||||
|
@ -209,20 +218,20 @@ void DOSVM_QueueEvent( int irq, int priority, void (*relay)(LPDOSTASK,CONTEXT86*
|
|||
|
||||
/* insert event into linked list, in order *after*
|
||||
* all earlier events of higher or equal priority */
|
||||
cur = lpDosTask->pending; prev = NULL;
|
||||
cur = pending_event; prev = NULL;
|
||||
while (cur && cur->priority<=priority) {
|
||||
prev = cur;
|
||||
cur = cur->next;
|
||||
}
|
||||
event->next = cur;
|
||||
if (prev) prev->next = event;
|
||||
else lpDosTask->pending = event;
|
||||
else pending_event = event;
|
||||
|
||||
/* get dosmod's attention to the new event, if necessary */
|
||||
if (!lpDosTask->sig_sent) {
|
||||
if (!sig_sent) {
|
||||
TRACE_(int)("new event queued, signalling dosmod\n");
|
||||
kill(lpDosTask->task,SIGUSR2);
|
||||
lpDosTask->sig_sent++;
|
||||
kill(dosmod_pid,SIGUSR2);
|
||||
sig_sent++;
|
||||
} else {
|
||||
TRACE_(int)("new event queued\n");
|
||||
}
|
||||
|
@ -234,7 +243,7 @@ void DOSVM_QueueEvent( int irq, int priority, void (*relay)(LPDOSTASK,CONTEXT86*
|
|||
/* callback event, perform it with dummy context */
|
||||
CONTEXT86 context;
|
||||
memset(&context,0,sizeof(context));
|
||||
(*relay)(lpDosTask,&context,data);
|
||||
(*relay)(&context,data);
|
||||
} else {
|
||||
ERR_(int)("IRQ without DOS task: should not happen");
|
||||
}
|
||||
|
@ -247,8 +256,7 @@ void DOSVM_QueueEvent( int irq, int priority, void (*relay)(LPDOSTASK,CONTEXT86*
|
|||
CP(ss,SS); CP(fs,FS); CP(gs,GS); \
|
||||
CP(eip,EIP); CP(eflags,EFL)
|
||||
|
||||
static int DOSVM_Process( LPDOSTASK lpDosTask, int fn, int sig,
|
||||
struct vm86plus_struct*VM86 )
|
||||
static int DOSVM_Process( int fn, int sig, struct vm86plus_struct*VM86 )
|
||||
{
|
||||
CONTEXT86 context;
|
||||
int ret=0;
|
||||
|
@ -270,7 +278,7 @@ static int DOSVM_Process( LPDOSTASK lpDosTask, int fn, int sig,
|
|||
}
|
||||
#else
|
||||
/* linux doesn't preserve pending flag on return */
|
||||
if (SHOULD_PEND(lpDosTask->pending)) {
|
||||
if (SHOULD_PEND(pending_event)) {
|
||||
SET_PEND(&context);
|
||||
}
|
||||
#endif
|
||||
|
@ -280,48 +288,48 @@ static int DOSVM_Process( LPDOSTASK lpDosTask, int fn, int sig,
|
|||
TRACE_(int)("DOS module caught signal %d\n",sig);
|
||||
if ((sig==SIGALRM) || (sig==SIGUSR2)) {
|
||||
if (sig==SIGALRM) {
|
||||
lpDosTask->sig_sent++;
|
||||
sig_sent++;
|
||||
DOSVM_QueueEvent(0,DOS_PRIORITY_REALTIME,NULL,NULL);
|
||||
}
|
||||
if (lpDosTask->pending) {
|
||||
if (pending_event) {
|
||||
TRACE_(int)("setting Pending flag, interrupts are currently %s\n",
|
||||
IF_ENABLED(&context) ? "enabled" : "disabled");
|
||||
SET_PEND(&context);
|
||||
DOSVM_SendQueuedEvents(&context,lpDosTask);
|
||||
DOSVM_SendQueuedEvents(&context);
|
||||
} else {
|
||||
TRACE_(int)("no events are pending, clearing Pending flag\n");
|
||||
CLR_PEND(&context);
|
||||
}
|
||||
lpDosTask->sig_sent--;
|
||||
sig_sent--;
|
||||
}
|
||||
else if ((sig==SIGHUP) || (sig==SIGILL) || (sig==SIGSEGV)) {
|
||||
do_exception( sig, &context );
|
||||
} else {
|
||||
DOSVM_Dump(lpDosTask,fn,sig,VM86);
|
||||
DOSVM_Dump(fn,sig,VM86);
|
||||
ret=-1;
|
||||
}
|
||||
break;
|
||||
case VM86_UNKNOWN: /* unhandled GPF */
|
||||
DOSVM_Dump(lpDosTask,fn,sig,VM86);
|
||||
DOSVM_Dump(fn,sig,VM86);
|
||||
do_exception( SIGSEGV, &context );
|
||||
break;
|
||||
case VM86_INTx:
|
||||
if (TRACE_ON(relay))
|
||||
DPRINTF("Call DOS int 0x%02x (EAX=%08lx) ret=%04lx:%04lx\n",VM86_ARG(fn),context.Eax,context.SegCs,context.Eip);
|
||||
ret=DOSVM_Int(VM86_ARG(fn),&context,lpDosTask);
|
||||
ret=DOSVM_Int(VM86_ARG(fn),&context);
|
||||
if (TRACE_ON(relay))
|
||||
DPRINTF("Ret DOS int 0x%02x (EAX=%08lx) ret=%04lx:%04lx\n",VM86_ARG(fn),context.Eax,context.SegCs,context.Eip);
|
||||
break;
|
||||
case VM86_STI:
|
||||
case VM86_PICRETURN:
|
||||
TRACE_(int)("DOS task enabled interrupts with events pending, sending events\n");
|
||||
DOSVM_SendQueuedEvents(&context,lpDosTask);
|
||||
DOSVM_SendQueuedEvents(&context);
|
||||
break;
|
||||
case VM86_TRAP:
|
||||
do_exception( SIGTRAP, &context );
|
||||
break;
|
||||
default:
|
||||
DOSVM_Dump(lpDosTask,fn,sig,VM86);
|
||||
DOSVM_Dump(fn,sig,VM86);
|
||||
ret=-1;
|
||||
}
|
||||
|
||||
|
@ -335,7 +343,7 @@ static int DOSVM_Process( LPDOSTASK lpDosTask, int fn, int sig,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void DOSVM_ProcessConsole(LPDOSTASK lpDosTask)
|
||||
static void DOSVM_ProcessConsole(void)
|
||||
{
|
||||
INPUT_RECORD msg;
|
||||
DWORD res;
|
||||
|
@ -360,7 +368,7 @@ static void DOSVM_ProcessConsole(LPDOSTASK lpDosTask)
|
|||
}
|
||||
}
|
||||
|
||||
static void DOSVM_ProcessMessage(LPDOSTASK lpDosTask,MSG *msg)
|
||||
static void DOSVM_ProcessMessage(MSG *msg)
|
||||
{
|
||||
BYTE scan = 0;
|
||||
|
||||
|
@ -391,21 +399,20 @@ static void DOSVM_ProcessMessage(LPDOSTASK lpDosTask,MSG *msg)
|
|||
|
||||
void DOSVM_Wait( int read_pipe, HANDLE hObject )
|
||||
{
|
||||
LPDOSTASK lpDosTask = MZ_Current();
|
||||
MSG msg;
|
||||
DWORD waitret;
|
||||
HANDLE objs[2];
|
||||
int objc;
|
||||
BOOL got_msg = FALSE;
|
||||
|
||||
objs[0]=lpDosTask->hConInput;
|
||||
objs[0]=GetStdHandle(STD_INPUT_HANDLE);
|
||||
objs[1]=hObject;
|
||||
objc=hObject?2:1;
|
||||
do {
|
||||
/* check for messages (waste time before the response check below) */
|
||||
while (Callout.PeekMessageA(&msg,0,0,0,PM_REMOVE|PM_NOYIELD)) {
|
||||
/* got a message */
|
||||
DOSVM_ProcessMessage(lpDosTask,&msg);
|
||||
DOSVM_ProcessMessage(&msg);
|
||||
/* we don't need a TranslateMessage here */
|
||||
Callout.DispatchMessageA(&msg);
|
||||
got_msg = TRUE;
|
||||
|
@ -415,7 +422,7 @@ void DOSVM_Wait( int read_pipe, HANDLE hObject )
|
|||
INPUT_RECORD msg;
|
||||
DWORD num;
|
||||
if (PeekConsoleInputA(objs[0],&msg,1,&num) && num) {
|
||||
DOSVM_ProcessConsole(lpDosTask);
|
||||
DOSVM_ProcessConsole();
|
||||
got_msg = TRUE;
|
||||
}
|
||||
}
|
||||
|
@ -439,63 +446,42 @@ void DOSVM_Wait( int read_pipe, HANDLE hObject )
|
|||
if (waitret==(WAIT_OBJECT_0+1)) break;
|
||||
}
|
||||
if (waitret==WAIT_OBJECT_0) {
|
||||
DOSVM_ProcessConsole(lpDosTask);
|
||||
DOSVM_ProcessConsole();
|
||||
}
|
||||
} while (TRUE);
|
||||
}
|
||||
|
||||
int DOSVM_Enter( CONTEXT86 *context )
|
||||
{
|
||||
LPDOSTASK lpDosTask = MZ_Current();
|
||||
struct vm86plus_struct VM86;
|
||||
int stat,len,sig;
|
||||
|
||||
if (!lpDosTask) {
|
||||
/* MZ_CreateProcess or MZ_AllocDPMITask should have been called first */
|
||||
ERR_(module)("dosmod has not been initialized!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (context) {
|
||||
#define CP(x,y) VM86.regs.x = y##_reg(context)
|
||||
CV;
|
||||
#undef CP
|
||||
if (VM86.regs.eflags & IF_MASK)
|
||||
VM86.regs.eflags |= VIF_MASK;
|
||||
} else {
|
||||
/* initial setup */
|
||||
/* registers */
|
||||
memset(&VM86,0,sizeof(VM86));
|
||||
VM86.regs.cs=lpDosTask->init_cs;
|
||||
VM86.regs.eip=lpDosTask->init_ip;
|
||||
VM86.regs.ss=lpDosTask->init_ss;
|
||||
VM86.regs.esp=lpDosTask->init_sp;
|
||||
VM86.regs.ds=lpDosTask->psp_seg;
|
||||
VM86.regs.es=lpDosTask->psp_seg;
|
||||
VM86.regs.eflags=VIF_MASK;
|
||||
/* hmm, what else do we need? */
|
||||
}
|
||||
|
||||
/* main exchange loop */
|
||||
lpDosTask->entered++;
|
||||
entered++;
|
||||
do {
|
||||
TRACE_(module)("thread is: %lx\n",GetCurrentThreadId());
|
||||
stat = VM86_ENTER;
|
||||
errno = 0;
|
||||
/* transmit VM86 structure to dosmod task */
|
||||
if (write(lpDosTask->write_pipe,&stat,sizeof(stat))!=sizeof(stat)) {
|
||||
ERR_(module)("dosmod sync lost, errno=%d, fd=%d, pid=%d\n",errno,lpDosTask->write_pipe,getpid());
|
||||
if (write(write_pipe,&stat,sizeof(stat))!=sizeof(stat)) {
|
||||
ERR_(module)("dosmod sync lost, errno=%d, fd=%d, pid=%d\n",errno,write_pipe,getpid());
|
||||
return -1;
|
||||
}
|
||||
if (write(lpDosTask->write_pipe,&VM86,sizeof(VM86))!=sizeof(VM86)) {
|
||||
if (write(write_pipe,&VM86,sizeof(VM86))!=sizeof(VM86)) {
|
||||
ERR_(module)("dosmod sync lost, errno=%d\n",errno);
|
||||
return -1;
|
||||
}
|
||||
/* wait for response, doing other things in the meantime */
|
||||
DOSVM_Wait(lpDosTask->read_pipe, lpDosTask->hReadPipe);
|
||||
DOSVM_Wait(read_pipe, hReadPipe);
|
||||
/* read response */
|
||||
while (1) {
|
||||
if ((len=read(lpDosTask->read_pipe,&stat,sizeof(stat)))==sizeof(stat)) break;
|
||||
if ((len=read(read_pipe,&stat,sizeof(stat)))==sizeof(stat)) break;
|
||||
if (((errno==EINTR)||(errno==EAGAIN))&&(len<=0)) {
|
||||
WARN_(module)("rereading dosmod return code due to errno=%d, result=%d\n",errno,len);
|
||||
continue;
|
||||
|
@ -505,11 +491,10 @@ int DOSVM_Enter( CONTEXT86 *context )
|
|||
}
|
||||
TRACE_(module)("dosmod return code=%d\n",stat);
|
||||
if (stat==DOSMOD_LEFTIDLE) {
|
||||
lpDosTask->idling--;
|
||||
continue;
|
||||
}
|
||||
while (1) {
|
||||
if ((len=read(lpDosTask->read_pipe,&VM86,sizeof(VM86)))==sizeof(VM86)) break;
|
||||
if ((len=read(read_pipe,&VM86,sizeof(VM86)))==sizeof(VM86)) break;
|
||||
if (((errno==EINTR)||(errno==EAGAIN))&&(len<=0)) {
|
||||
WARN_(module)("rereading dosmod VM86 structure due to errno=%d, result=%d\n",errno,len);
|
||||
continue;
|
||||
|
@ -519,7 +504,7 @@ int DOSVM_Enter( CONTEXT86 *context )
|
|||
}
|
||||
if ((stat&0xff)==DOSMOD_SIGNAL) {
|
||||
while (1) {
|
||||
if ((len=read(lpDosTask->read_pipe,&sig,sizeof(sig)))==sizeof(sig)) break;
|
||||
if ((len=read(read_pipe,&sig,sizeof(sig)))==sizeof(sig)) break;
|
||||
if (((errno==EINTR)||(errno==EAGAIN))&&(len<=0)) {
|
||||
WARN_(module)("rereading dosmod signal due to errno=%d, result=%d\n",errno,len);
|
||||
continue;
|
||||
|
@ -529,8 +514,8 @@ int DOSVM_Enter( CONTEXT86 *context )
|
|||
} while (0);
|
||||
} else sig=0;
|
||||
/* got response */
|
||||
} while (DOSVM_Process(lpDosTask,stat,sig,&VM86)>=0);
|
||||
lpDosTask->entered--;
|
||||
} while (DOSVM_Process(stat,sig,&VM86)>=0);
|
||||
entered--;
|
||||
|
||||
if (context) {
|
||||
#define CP(x,y) y##_reg(context) = VM86.regs.x
|
||||
|
@ -542,27 +527,25 @@ int DOSVM_Enter( CONTEXT86 *context )
|
|||
|
||||
void DOSVM_PIC_ioport_out( WORD port, BYTE val)
|
||||
{
|
||||
LPDOSTASK lpDosTask = MZ_Current();
|
||||
LPDOSEVENT event;
|
||||
LPDOSEVENT event;
|
||||
|
||||
if (lpDosTask) {
|
||||
if ((port==0x20) && (val==0x20)) {
|
||||
if (lpDosTask->current) {
|
||||
if (current_event) {
|
||||
/* EOI (End Of Interrupt) */
|
||||
TRACE_(int)("received EOI for current IRQ, clearing\n");
|
||||
event = lpDosTask->current;
|
||||
lpDosTask->current = event->next;
|
||||
event = current_event;
|
||||
current_event = event->next;
|
||||
if (event->relay)
|
||||
(*event->relay)(lpDosTask,NULL,event->data);
|
||||
(*event->relay)(NULL,event->data);
|
||||
free(event);
|
||||
|
||||
if (lpDosTask->pending &&
|
||||
!lpDosTask->sig_sent) {
|
||||
if (pending_event &&
|
||||
!sig_sent) {
|
||||
/* another event is pending, which we should probably
|
||||
* be able to process now, so tell dosmod about it */
|
||||
TRACE_(int)("another event pending, signalling dosmod\n");
|
||||
kill(lpDosTask->task,SIGUSR2);
|
||||
lpDosTask->sig_sent++;
|
||||
kill(dosmod_pid,SIGUSR2);
|
||||
sig_sent++;
|
||||
}
|
||||
} else {
|
||||
WARN_(int)("EOI without active IRQ\n");
|
||||
|
@ -570,27 +553,25 @@ void DOSVM_PIC_ioport_out( WORD port, BYTE val)
|
|||
} else {
|
||||
FIXME_(int)("unrecognized PIC command %02x\n",val);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DOSVM_SetTimer( unsigned ticks )
|
||||
{
|
||||
LPDOSTASK lpDosTask = MZ_Current();
|
||||
int stat=DOSMOD_SET_TIMER;
|
||||
struct timeval tim;
|
||||
|
||||
if (lpDosTask) {
|
||||
if (MZ_Current()) {
|
||||
/* the PC clocks ticks at 1193180 Hz */
|
||||
tim.tv_sec=0;
|
||||
tim.tv_usec=((unsigned long long)ticks*1000000)/1193180;
|
||||
/* sanity check */
|
||||
if (!tim.tv_usec) tim.tv_usec=1;
|
||||
|
||||
if (write(lpDosTask->write_pipe,&stat,sizeof(stat))!=sizeof(stat)) {
|
||||
if (write(write_pipe,&stat,sizeof(stat))!=sizeof(stat)) {
|
||||
ERR_(module)("dosmod sync lost, errno=%d\n",errno);
|
||||
return;
|
||||
}
|
||||
if (write(lpDosTask->write_pipe,&tim,sizeof(tim))!=sizeof(tim)) {
|
||||
if (write(write_pipe,&tim,sizeof(tim))!=sizeof(tim)) {
|
||||
ERR_(module)("dosmod sync lost, errno=%d\n",errno);
|
||||
return;
|
||||
}
|
||||
|
@ -600,18 +581,17 @@ void DOSVM_SetTimer( unsigned ticks )
|
|||
|
||||
unsigned DOSVM_GetTimer( void )
|
||||
{
|
||||
LPDOSTASK lpDosTask = MZ_Current();
|
||||
int stat=DOSMOD_GET_TIMER;
|
||||
struct timeval tim;
|
||||
|
||||
if (lpDosTask) {
|
||||
if (write(lpDosTask->write_pipe,&stat,sizeof(stat))!=sizeof(stat)) {
|
||||
if (MZ_Current()) {
|
||||
if (write(write_pipe,&stat,sizeof(stat))!=sizeof(stat)) {
|
||||
ERR_(module)("dosmod sync lost, errno=%d\n",errno);
|
||||
return 0;
|
||||
}
|
||||
/* read response */
|
||||
while (1) {
|
||||
if (read(lpDosTask->read_pipe,&tim,sizeof(tim))==sizeof(tim)) break;
|
||||
if (read(read_pipe,&tim,sizeof(tim))==sizeof(tim)) break;
|
||||
if ((errno==EINTR)||(errno==EAGAIN)) continue;
|
||||
ERR_(module)("dosmod sync lost, errno=%d\n",errno);
|
||||
return 0;
|
||||
|
@ -621,47 +601,6 @@ unsigned DOSVM_GetTimer( void )
|
|||
return 0;
|
||||
}
|
||||
|
||||
void DOSVM_SetSystemData( int id, void *data )
|
||||
{
|
||||
LPDOSTASK lpDosTask = MZ_Current();
|
||||
DOSSYSTEM *sys, *prev;
|
||||
|
||||
if (lpDosTask) {
|
||||
sys = lpDosTask->sys;
|
||||
prev = NULL;
|
||||
while (sys && (sys->id != id)) {
|
||||
prev = sys;
|
||||
sys = sys->next;
|
||||
}
|
||||
if (sys) {
|
||||
free(sys->data);
|
||||
sys->data = data;
|
||||
} else {
|
||||
sys = malloc(sizeof(DOSSYSTEM));
|
||||
sys->id = id;
|
||||
sys->data = data;
|
||||
sys->next = NULL;
|
||||
if (prev) prev->next = sys;
|
||||
else lpDosTask->sys = sys;
|
||||
}
|
||||
} else free(data);
|
||||
}
|
||||
|
||||
void* DOSVM_GetSystemData( int id )
|
||||
{
|
||||
LPDOSTASK lpDosTask = MZ_Current();
|
||||
DOSSYSTEM *sys;
|
||||
|
||||
if (lpDosTask) {
|
||||
sys = lpDosTask->sys;
|
||||
while (sys && (sys->id != id))
|
||||
sys = sys->next;
|
||||
if (sys)
|
||||
return sys->data;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#else /* !MZ_SUPPORTED */
|
||||
|
||||
int DOSVM_Enter( CONTEXT86 *context )
|
||||
|
@ -674,15 +613,13 @@ void DOSVM_Wait( int read_pipe, HANDLE hObject) {}
|
|||
void DOSVM_PIC_ioport_out( WORD port, BYTE val) {}
|
||||
void DOSVM_SetTimer( unsigned ticks ) {}
|
||||
unsigned DOSVM_GetTimer( void ) { return 0; }
|
||||
void DOSVM_SetSystemData( int id, void *data ) { free(data); }
|
||||
void* DOSVM_GetSystemData( int id ) { return NULL; }
|
||||
void DOSVM_QueueEvent( int irq, int priority, void (*relay)(LPDOSTASK,CONTEXT86*,void*), void *data)
|
||||
void DOSVM_QueueEvent( int irq, int priority, void (*relay)(CONTEXT86*,void*), void *data)
|
||||
{
|
||||
if (irq<0) {
|
||||
/* callback event, perform it with dummy context */
|
||||
CONTEXT86 context;
|
||||
memset(&context,0,sizeof(context));
|
||||
(*relay)(NULL,&context,data);
|
||||
(*relay)(&context,data);
|
||||
} else {
|
||||
ERR_(int)("IRQ without DOS task: should not happen");
|
||||
}
|
||||
|
|
|
@ -37,6 +37,8 @@
|
|||
|
||||
DEFAULT_DEBUG_CHANNEL(module);
|
||||
|
||||
static LPDOSTASK dos_current;
|
||||
|
||||
#ifdef MZ_SUPPORTED
|
||||
|
||||
#ifdef HAVE_SYS_MMAN_H
|
||||
|
@ -48,15 +50,21 @@ DEFAULT_DEBUG_CHANNEL(module);
|
|||
#undef MZ_MAPSELF
|
||||
|
||||
#define BIOS_DATA_SEGMENT 0x40
|
||||
#define START_OFFSET 0
|
||||
#define PSP_SIZE 0x10
|
||||
|
||||
#define SEG16(ptr,seg) ((LPVOID)((BYTE*)ptr+((DWORD)(seg)<<4)))
|
||||
#define SEGPTR16(ptr,segptr) ((LPVOID)((BYTE*)ptr+((DWORD)SELECTOROF(segptr)<<4)+OFFSETOF(segptr)))
|
||||
|
||||
static LPDOSTASK dos_current;
|
||||
static WORD init_cs,init_ip,init_ss,init_sp;
|
||||
static char mm_name[128];
|
||||
|
||||
int read_pipe, write_pipe;
|
||||
HANDLE hReadPipe, hWritePipe;
|
||||
pid_t dosmod_pid;
|
||||
|
||||
static void MZ_Launch(void);
|
||||
static BOOL MZ_InitTask(void);
|
||||
static void MZ_KillTask(void);
|
||||
|
||||
static void MZ_CreatePSP( LPVOID lpPSP, WORD env )
|
||||
{
|
||||
|
@ -107,68 +115,18 @@ static char int08[]={
|
|||
0xCF /* iret */
|
||||
};
|
||||
|
||||
static void MZ_InitHandlers( LPDOSTASK lpDosTask )
|
||||
static void MZ_InitHandlers(void)
|
||||
{
|
||||
WORD seg;
|
||||
LPBYTE start=DOSMEM_GetBlock(sizeof(int08),&seg);
|
||||
memcpy(start,int08,sizeof(int08));
|
||||
/* INT 08: point it at our tick-incrementing handler */
|
||||
((SEGPTR*)(lpDosTask->img))[0x08]=PTR_SEG_OFF_TO_SEGPTR(seg,0);
|
||||
((SEGPTR*)0)[0x08]=PTR_SEG_OFF_TO_SEGPTR(seg,0);
|
||||
/* INT 1C: just point it to IRET, we don't want to handle it ourselves */
|
||||
((SEGPTR*)(lpDosTask->img))[0x1C]=PTR_SEG_OFF_TO_SEGPTR(seg,sizeof(int08)-1);
|
||||
((SEGPTR*)0)[0x1C]=PTR_SEG_OFF_TO_SEGPTR(seg,sizeof(int08)-1);
|
||||
}
|
||||
|
||||
static char enter_xms[]={
|
||||
/* XMS hookable entry point */
|
||||
0xEB,0x03, /* jmp entry */
|
||||
0x90,0x90,0x90, /* nop;nop;nop */
|
||||
/* entry: */
|
||||
/* real entry point */
|
||||
/* for simplicity, we'll just use the same hook as DPMI below */
|
||||
0xCD,0x31, /* int $0x31 */
|
||||
0xCB /* lret */
|
||||
};
|
||||
|
||||
static void MZ_InitXMS( LPDOSTASK lpDosTask )
|
||||
{
|
||||
LPBYTE start=DOSMEM_GetBlock(sizeof(enter_xms),&(lpDosTask->xms_seg));
|
||||
memcpy(start,enter_xms,sizeof(enter_xms));
|
||||
}
|
||||
|
||||
static char enter_pm[]={
|
||||
0x50, /* pushw %ax */
|
||||
0x52, /* pushw %dx */
|
||||
0x55, /* pushw %bp */
|
||||
0x89,0xE5, /* movw %sp,%bp */
|
||||
/* get return CS */
|
||||
0x8B,0x56,0x08, /* movw 8(%bp),%dx */
|
||||
/* just call int 31 here to get into protected mode... */
|
||||
/* it'll check whether it was called from dpmi_seg... */
|
||||
0xCD,0x31, /* int $0x31 */
|
||||
/* we are now in the context of a 16-bit relay call */
|
||||
/* need to fixup our stack;
|
||||
* 16-bit relay return address will be lost, but we won't worry quite yet */
|
||||
0x8E,0xD0, /* movw %ax,%ss */
|
||||
0x66,0x0F,0xB7,0xE5, /* movzwl %bp,%esp */
|
||||
/* set return CS */
|
||||
0x89,0x56,0x08, /* movw %dx,8(%bp) */
|
||||
0x5D, /* popw %bp */
|
||||
0x5A, /* popw %dx */
|
||||
0x58, /* popw %ax */
|
||||
0xCB /* lret */
|
||||
};
|
||||
|
||||
static void MZ_InitDPMI( LPDOSTASK lpDosTask )
|
||||
{
|
||||
unsigned size=sizeof(enter_pm);
|
||||
LPBYTE start=DOSMEM_GetBlock(size,&(lpDosTask->dpmi_seg));
|
||||
|
||||
lpDosTask->dpmi_sel = SELECTOR_AllocBlock( start, size, SEGMENT_CODE, FALSE, FALSE );
|
||||
|
||||
memcpy(start,enter_pm,sizeof(enter_pm));
|
||||
}
|
||||
|
||||
static WORD MZ_InitEnvironment( LPDOSTASK lpDosTask, LPCSTR env, LPCSTR name )
|
||||
static WORD MZ_InitEnvironment( LPCSTR env, LPCSTR name )
|
||||
{
|
||||
unsigned sz=0;
|
||||
WORD seg;
|
||||
|
@ -191,39 +149,33 @@ static WORD MZ_InitEnvironment( LPDOSTASK lpDosTask, LPCSTR env, LPCSTR name )
|
|||
return seg;
|
||||
}
|
||||
|
||||
static BOOL MZ_InitMemory( LPDOSTASK lpDosTask )
|
||||
static BOOL MZ_InitMemory(void)
|
||||
{
|
||||
if (lpDosTask->img) return TRUE; /* already allocated */
|
||||
int mm_fd;
|
||||
void *img_base;
|
||||
|
||||
/* allocate 1MB+64K shared memory */
|
||||
lpDosTask->img_ofs=START_OFFSET;
|
||||
#ifdef MZ_MAPSELF
|
||||
lpDosTask->img=VirtualAlloc(NULL,0x110000,MEM_COMMIT,PAGE_READWRITE);
|
||||
/* make sure mmap accepts it */
|
||||
((char*)lpDosTask->img)[0x10FFFF]=0;
|
||||
#else
|
||||
tmpnam(lpDosTask->mm_name);
|
||||
/* strcpy(lpDosTask->mm_name,"/tmp/mydosimage"); */
|
||||
lpDosTask->mm_fd=open(lpDosTask->mm_name,O_RDWR|O_CREAT /* |O_TRUNC */,S_IRUSR|S_IWUSR);
|
||||
if (lpDosTask->mm_fd<0) ERR("file %s could not be opened\n",lpDosTask->mm_name);
|
||||
/* expand file to 1MB+64K */
|
||||
ftruncate(lpDosTask->mm_fd,0x110000);
|
||||
/* map it in */
|
||||
lpDosTask->img=mmap(NULL,0x110000-START_OFFSET,PROT_READ|PROT_WRITE,MAP_SHARED,lpDosTask->mm_fd,0);
|
||||
#endif
|
||||
if (lpDosTask->img==(LPVOID)-1) {
|
||||
ERR("could not map shared memory, error=%s\n",strerror(errno));
|
||||
return FALSE;
|
||||
}
|
||||
TRACE("DOS VM86 image mapped at %08lx\n",(DWORD)lpDosTask->img);
|
||||
/* initialize the memory */
|
||||
TRACE("Initializing DOS memory structures\n");
|
||||
DOSMEM_Init(TRUE);
|
||||
|
||||
/* initialize the memory */
|
||||
TRACE("Initializing DOS memory structures\n");
|
||||
DOSMEM_Init(TRUE);
|
||||
MZ_InitHandlers(lpDosTask);
|
||||
MZ_InitXMS(lpDosTask);
|
||||
MZ_InitDPMI(lpDosTask);
|
||||
return TRUE;
|
||||
/* allocate 1MB+64K shared memory */
|
||||
tmpnam(mm_name);
|
||||
/* strcpy(mm_name,"/tmp/mydosimage"); */
|
||||
mm_fd = open(mm_name,O_RDWR|O_CREAT /* |O_TRUNC */,S_IRUSR|S_IWUSR);
|
||||
if (mm_fd < 0) ERR("file %s could not be opened\n",mm_name);
|
||||
/* fill the file with the DOS memory */
|
||||
if (write( mm_fd, NULL, 0x110000 ) != 0x110000) ERR("cannot write DOS mem\n");
|
||||
/* map it in */
|
||||
img_base = mmap(NULL,0x110000,PROT_READ|PROT_WRITE|PROT_EXEC,MAP_SHARED|MAP_FIXED,mm_fd,0);
|
||||
close( mm_fd );
|
||||
|
||||
if (img_base)
|
||||
{
|
||||
ERR("could not map shared memory, error=%s\n",strerror(errno));
|
||||
return FALSE;
|
||||
}
|
||||
MZ_InitHandlers();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL MZ_LoadImage( HMODULE module, HANDLE hFile, LPCSTR filename )
|
||||
|
@ -235,7 +187,7 @@ BOOL MZ_LoadImage( HMODULE module, HANDLE hFile, LPCSTR filename )
|
|||
BYTE*psp_start,*load_start;
|
||||
int x, old_com=0, alloc=0;
|
||||
SEGPTR reloc;
|
||||
WORD env_seg;
|
||||
WORD env_seg, load_seg;
|
||||
DWORD len;
|
||||
|
||||
win_hdr->OptionalHeader.Subsystem = IMAGE_SUBSYSTEM_WINDOWS_CUI;
|
||||
|
@ -244,7 +196,6 @@ BOOL MZ_LoadImage( HMODULE module, HANDLE hFile, LPCSTR filename )
|
|||
if (!lpDosTask) {
|
||||
alloc=1;
|
||||
lpDosTask = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DOSTASK));
|
||||
lpDosTask->mm_fd = -1;
|
||||
dos_current = lpDosTask;
|
||||
}
|
||||
|
||||
|
@ -269,10 +220,10 @@ BOOL MZ_LoadImage( HMODULE module, HANDLE hFile, LPCSTR filename )
|
|||
max_size=image_size+((DWORD)mz_header.e_maxalloc<<4)+(PSP_SIZE<<4);
|
||||
}
|
||||
|
||||
MZ_InitMemory(lpDosTask);
|
||||
MZ_InitMemory();
|
||||
|
||||
/* allocate environment block */
|
||||
env_seg=MZ_InitEnvironment(lpDosTask,GetEnvironmentStringsA(),filename);
|
||||
env_seg=MZ_InitEnvironment(GetEnvironmentStringsA(),filename);
|
||||
|
||||
/* allocate memory for the executable */
|
||||
TRACE("Allocating DOS memory (min=%ld, max=%ld)\n",min_size,max_size);
|
||||
|
@ -289,7 +240,7 @@ BOOL MZ_LoadImage( HMODULE module, HANDLE hFile, LPCSTR filename )
|
|||
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
||||
goto load_error;
|
||||
}
|
||||
lpDosTask->load_seg=lpDosTask->psp_seg+(old_com?0:PSP_SIZE);
|
||||
load_seg=lpDosTask->psp_seg+(old_com?0:PSP_SIZE);
|
||||
load_start=psp_start+(PSP_SIZE<<4);
|
||||
MZ_CreatePSP(psp_start, env_seg);
|
||||
|
||||
|
@ -311,19 +262,19 @@ BOOL MZ_LoadImage( HMODULE module, HANDLE hFile, LPCSTR filename )
|
|||
SetLastError(ERROR_BAD_FORMAT);
|
||||
goto load_error;
|
||||
}
|
||||
*(WORD*)SEGPTR16(load_start,reloc)+=lpDosTask->load_seg;
|
||||
*(WORD*)SEGPTR16(load_start,reloc)+=load_seg;
|
||||
}
|
||||
}
|
||||
|
||||
lpDosTask->init_cs=lpDosTask->load_seg+mz_header.e_cs;
|
||||
lpDosTask->init_ip=mz_header.e_ip;
|
||||
lpDosTask->init_ss=lpDosTask->load_seg+mz_header.e_ss;
|
||||
lpDosTask->init_sp=mz_header.e_sp;
|
||||
init_cs = load_seg+mz_header.e_cs;
|
||||
init_ip = mz_header.e_ip;
|
||||
init_ss = load_seg+mz_header.e_ss;
|
||||
init_sp = mz_header.e_sp;
|
||||
|
||||
TRACE("entry point: %04x:%04x\n",lpDosTask->init_cs,lpDosTask->init_ip);
|
||||
TRACE("entry point: %04x:%04x\n",init_cs,init_ip);
|
||||
|
||||
if (!MZ_InitTask(lpDosTask)) {
|
||||
MZ_KillTask(lpDosTask);
|
||||
if (!MZ_InitTask()) {
|
||||
MZ_KillTask();
|
||||
SetLastError(ERROR_GEN_FAILURE);
|
||||
return FALSE;
|
||||
}
|
||||
|
@ -333,12 +284,7 @@ BOOL MZ_LoadImage( HMODULE module, HANDLE hFile, LPCSTR filename )
|
|||
load_error:
|
||||
if (alloc) {
|
||||
dos_current = NULL;
|
||||
if (lpDosTask->mm_name[0]!=0) {
|
||||
if (lpDosTask->img!=NULL) munmap(lpDosTask->img,0x110000-START_OFFSET);
|
||||
if (lpDosTask->mm_fd>=0) close(lpDosTask->mm_fd);
|
||||
unlink(lpDosTask->mm_name);
|
||||
} else
|
||||
if (lpDosTask->img!=NULL) VirtualFree(lpDosTask->img,0x110000,MEM_RELEASE);
|
||||
if (mm_name[0]!=0) unlink(mm_name);
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
|
@ -349,14 +295,14 @@ LPDOSTASK MZ_AllocDPMITask( void )
|
|||
LPDOSTASK lpDosTask = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DOSTASK));
|
||||
|
||||
if (lpDosTask) {
|
||||
lpDosTask->mm_fd = -1;
|
||||
dos_current = lpDosTask;
|
||||
MZ_InitMemory(lpDosTask);
|
||||
MZ_InitMemory();
|
||||
MZ_InitTask();
|
||||
}
|
||||
return lpDosTask;
|
||||
}
|
||||
|
||||
static void MZ_InitTimer( LPDOSTASK lpDosTask, int ver )
|
||||
static void MZ_InitTimer( int ver )
|
||||
{
|
||||
if (ver<1) {
|
||||
/* can't make timer ticks */
|
||||
|
@ -367,55 +313,42 @@ static void MZ_InitTimer( LPDOSTASK lpDosTask, int ver )
|
|||
/* start dosmod timer at 55ms (18.2Hz) */
|
||||
func=DOSMOD_SET_TIMER;
|
||||
tim.tv_sec=0; tim.tv_usec=54925;
|
||||
write(lpDosTask->write_pipe,&func,sizeof(func));
|
||||
write(lpDosTask->write_pipe,&tim,sizeof(tim));
|
||||
write(write_pipe,&func,sizeof(func));
|
||||
write(write_pipe,&tim,sizeof(tim));
|
||||
}
|
||||
}
|
||||
|
||||
BOOL MZ_InitTask( LPDOSTASK lpDosTask )
|
||||
static BOOL MZ_InitTask(void)
|
||||
{
|
||||
int write_fd[2],x_fd;
|
||||
pid_t child;
|
||||
char *fname,*farg,arg[16],fproc[64],path[256],*fpath;
|
||||
char path[256],*fpath;
|
||||
|
||||
if (!lpDosTask) return FALSE;
|
||||
/* create pipes */
|
||||
if (!CreatePipe(&(lpDosTask->hReadPipe),&(lpDosTask->hXPipe),NULL,0)) return FALSE;
|
||||
if (!CreatePipe(&hReadPipe,&hWritePipe,NULL,0)) return FALSE;
|
||||
if (pipe(write_fd)<0) {
|
||||
CloseHandle(lpDosTask->hReadPipe);
|
||||
CloseHandle(lpDosTask->hXPipe);
|
||||
CloseHandle(hReadPipe);
|
||||
CloseHandle(hWritePipe);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
lpDosTask->read_pipe = FILE_GetUnixHandle( lpDosTask->hReadPipe, GENERIC_READ );
|
||||
x_fd = FILE_GetUnixHandle( lpDosTask->hXPipe, GENERIC_WRITE );
|
||||
read_pipe = FILE_GetUnixHandle( hReadPipe, GENERIC_READ );
|
||||
x_fd = FILE_GetUnixHandle( hWritePipe, GENERIC_WRITE );
|
||||
|
||||
TRACE("win32 pipe: read=%d, write=%d, unix pipe: read=%d, write=%d\n",
|
||||
lpDosTask->hReadPipe,lpDosTask->hXPipe,lpDosTask->read_pipe,x_fd);
|
||||
hReadPipe,hWritePipe,read_pipe,x_fd);
|
||||
TRACE("outbound unix pipe: read=%d, write=%d, pid=%d\n",write_fd[0],write_fd[1],getpid());
|
||||
|
||||
lpDosTask->write_pipe=write_fd[1];
|
||||
|
||||
lpDosTask->hConInput=GetStdHandle(STD_INPUT_HANDLE);
|
||||
lpDosTask->hConOutput=GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
/* if we have a mapping file, use it */
|
||||
fname=lpDosTask->mm_name; farg=NULL;
|
||||
if (!fname[0]) {
|
||||
/* otherwise, map our own memory image */
|
||||
sprintf(fproc,"/proc/%d/mem",getpid());
|
||||
sprintf(arg,"%ld",(unsigned long)lpDosTask->img);
|
||||
fname=fproc; farg=arg;
|
||||
}
|
||||
write_pipe=write_fd[1];
|
||||
|
||||
TRACE("Loading DOS VM support module\n");
|
||||
if ((child=fork())<0) {
|
||||
close(write_fd[0]);
|
||||
close(lpDosTask->read_pipe);
|
||||
close(lpDosTask->write_pipe);
|
||||
close(read_pipe);
|
||||
close(write_pipe);
|
||||
close(x_fd);
|
||||
CloseHandle(lpDosTask->hReadPipe);
|
||||
CloseHandle(lpDosTask->hXPipe);
|
||||
CloseHandle(hReadPipe);
|
||||
CloseHandle(hWritePipe);
|
||||
return FALSE;
|
||||
}
|
||||
if (child!=0) {
|
||||
|
@ -424,21 +357,21 @@ BOOL MZ_InitTask( LPDOSTASK lpDosTask )
|
|||
|
||||
close(write_fd[0]);
|
||||
close(x_fd);
|
||||
lpDosTask->task=child;
|
||||
dosmod_pid = child;
|
||||
/* wait for child process to signal readiness */
|
||||
while (1) {
|
||||
if (read(lpDosTask->read_pipe,&ret,sizeof(ret))==sizeof(ret)) break;
|
||||
if (read(read_pipe,&ret,sizeof(ret))==sizeof(ret)) break;
|
||||
if ((errno==EINTR)||(errno==EAGAIN)) continue;
|
||||
/* failure */
|
||||
ERR("dosmod has failed to initialize\n");
|
||||
if (lpDosTask->mm_name[0]!=0) unlink(lpDosTask->mm_name);
|
||||
if (mm_name[0]!=0) unlink(mm_name);
|
||||
return FALSE;
|
||||
}
|
||||
/* the child has now mmaped the temp file, it's now safe to unlink.
|
||||
* do it here to avoid leaving a mess in /tmp if/when Wine crashes... */
|
||||
if (lpDosTask->mm_name[0]!=0) unlink(lpDosTask->mm_name);
|
||||
if (mm_name[0]!=0) unlink(mm_name);
|
||||
/* start simulated system timer */
|
||||
MZ_InitTimer(lpDosTask,ret);
|
||||
MZ_InitTimer(ret);
|
||||
if (ret<2) {
|
||||
ERR("dosmod version too old! Please install newer dosmod properly\n");
|
||||
ERR("If you don't, the new dosmod event handling system will not work\n");
|
||||
|
@ -446,8 +379,8 @@ BOOL MZ_InitTask( LPDOSTASK lpDosTask )
|
|||
/* all systems are now go */
|
||||
} else {
|
||||
/* child process */
|
||||
close(lpDosTask->read_pipe);
|
||||
close(lpDosTask->write_pipe);
|
||||
close(read_pipe);
|
||||
close(write_pipe);
|
||||
/* put our pipes somewhere dosmod can find them */
|
||||
dup2(write_fd[0],0); /* stdin */
|
||||
dup2(x_fd,1); /* stdout */
|
||||
|
@ -457,16 +390,16 @@ BOOL MZ_InitTask( LPDOSTASK lpDosTask )
|
|||
fpath=strrchr(strcpy(path,full_argv0),'/');
|
||||
if (fpath) {
|
||||
strcpy(fpath,"/dosmod");
|
||||
execl(path,fname,farg,NULL);
|
||||
execl(path,mm_name,NULL);
|
||||
strcpy(fpath,"/loader/dos/dosmod");
|
||||
execl(path,fname,farg,NULL);
|
||||
execl(path,mm_name,NULL);
|
||||
}
|
||||
/* okay, it wasn't there, try in the path */
|
||||
execlp("dosmod",fname,farg,NULL);
|
||||
execlp("dosmod",mm_name,NULL);
|
||||
/* last desperate attempts: current directory */
|
||||
execl("dosmod",fname,farg,NULL);
|
||||
execl("dosmod",mm_name,NULL);
|
||||
/* and, just for completeness... */
|
||||
execl("loader/dos/dosmod",fname,farg,NULL);
|
||||
execl("loader/dos/dosmod",mm_name,NULL);
|
||||
/* if failure, exit */
|
||||
ERR("Failed to spawn dosmod, error=%s\n",strerror(errno));
|
||||
exit(1);
|
||||
|
@ -477,53 +410,29 @@ BOOL MZ_InitTask( LPDOSTASK lpDosTask )
|
|||
static void MZ_Launch(void)
|
||||
{
|
||||
LPDOSTASK lpDosTask = MZ_Current();
|
||||
BYTE *psp_start = (BYTE*)lpDosTask->img + ((DWORD)lpDosTask->psp_seg << 4);
|
||||
CONTEXT context;
|
||||
TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
|
||||
BYTE *psp_start = PTR_REAL_TO_LIN( lpDosTask->psp_seg, 0 );
|
||||
|
||||
MZ_FillPSP(psp_start, GetCommandLineA());
|
||||
pTask->flags |= TDBF_WINOLDAP;
|
||||
|
||||
DOSVM_Enter(NULL);
|
||||
memset( &context, 0, sizeof(context) );
|
||||
context.SegCs = init_cs;
|
||||
context.Eip = init_ip;
|
||||
context.SegSs = init_ss;
|
||||
context.Esp = init_sp;
|
||||
context.SegDs = lpDosTask->psp_seg;
|
||||
context.SegEs = lpDosTask->psp_seg;
|
||||
context.EFlags = 0x00080000; /* virtual interrupt flag */
|
||||
DOSVM_Enter( &context );
|
||||
}
|
||||
|
||||
void MZ_KillTask( LPDOSTASK lpDosTask )
|
||||
static void MZ_KillTask(void)
|
||||
{
|
||||
DOSEVENT *event,*p_event;
|
||||
DOSSYSTEM *sys,*p_sys;
|
||||
|
||||
TRACE("killing DOS task\n");
|
||||
VGA_Clean();
|
||||
if (lpDosTask->mm_name[0]!=0) {
|
||||
munmap(lpDosTask->img,0x110000-START_OFFSET);
|
||||
close(lpDosTask->mm_fd);
|
||||
} else VirtualFree(lpDosTask->img,0x110000,MEM_RELEASE);
|
||||
close(lpDosTask->read_pipe);
|
||||
close(lpDosTask->write_pipe);
|
||||
CloseHandle(lpDosTask->hReadPipe);
|
||||
CloseHandle(lpDosTask->hXPipe);
|
||||
kill(lpDosTask->task,SIGTERM);
|
||||
/* free memory allocated for events and systems */
|
||||
#define DFREE(var,pvar,svar) \
|
||||
var = lpDosTask->svar; \
|
||||
while (var) { \
|
||||
if (var->data) free(var->data); \
|
||||
pvar = var->next; free(var); var = pvar; \
|
||||
}
|
||||
|
||||
DFREE(event,p_event,pending)
|
||||
DFREE(event,p_event,current)
|
||||
DFREE(sys,p_sys,sys)
|
||||
|
||||
#undef DFREE
|
||||
|
||||
#if 0
|
||||
/* FIXME: this seems to crash */
|
||||
if (lpDosTask->dpmi_sel)
|
||||
SELECTOR_FreeBlock(lpDosTask->dpmi_sel, 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
LPDOSTASK MZ_Current( void )
|
||||
{
|
||||
return dos_current;
|
||||
kill(dosmod_pid,SIGTERM);
|
||||
}
|
||||
|
||||
#else /* !MZ_SUPPORTED */
|
||||
|
@ -535,9 +444,15 @@ BOOL MZ_LoadImage( HMODULE module, HANDLE hFile, LPCSTR filename )
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
LPDOSTASK MZ_Current( void )
|
||||
LPDOSTASK MZ_AllocDPMITask( void )
|
||||
{
|
||||
return NULL;
|
||||
ERR("Actual real-mode calls not supported on this architecture!\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
#endif /* !MZ_SUPPORTED */
|
||||
|
||||
LPDOSTASK MZ_Current( void )
|
||||
{
|
||||
return dos_current;
|
||||
}
|
||||
|
|
|
@ -29,13 +29,13 @@
|
|||
#include "winsock.h"
|
||||
#include "syslevel.h"
|
||||
#include "debugtools.h"
|
||||
#include "dosexe.h"
|
||||
#include "services.h"
|
||||
#include "server.h"
|
||||
|
||||
DEFAULT_DEBUG_CHANNEL(task)
|
||||
DECLARE_DEBUG_CHANNEL(relay)
|
||||
DECLARE_DEBUG_CHANNEL(toolhelp)
|
||||
|
||||
DEFAULT_DEBUG_CHANNEL(task);
|
||||
DECLARE_DEBUG_CHANNEL(relay);
|
||||
DECLARE_DEBUG_CHANNEL(toolhelp);
|
||||
|
||||
/* Min. number of thunks allocated when creating a new segment */
|
||||
#define MIN_THUNKS 32
|
||||
|
@ -243,7 +243,6 @@ BOOL TASK_Create( NE_MODULE *pModule, UINT16 cmdShow, TEB *teb, LPCSTR cmdline,
|
|||
/* NOTE: for 16-bit tasks, the instance handles are updated later on
|
||||
in NE_InitProcess */
|
||||
}
|
||||
if (MZ_Current() && MZ_Current()->load_seg) pTask->flags |= TDBF_WINOLDAP;
|
||||
|
||||
pTask->version = pModule->expected_version;
|
||||
pTask->hModule = pModule->self;
|
||||
|
|
|
@ -29,7 +29,7 @@ inline static void add_stack( CONTEXT86 *context, int offset )
|
|||
|
||||
inline static void *make_ptr( CONTEXT86 *context, DWORD seg, DWORD off, int long_addr )
|
||||
{
|
||||
if (ISV86(context)) return DOSMEM_MemoryBase() + (seg << 4) + LOWORD(off);
|
||||
if (ISV86(context)) return PTR_REAL_TO_LIN( seg, off );
|
||||
if (IS_SELECTOR_SYSTEM(seg)) return (void *)off;
|
||||
if (!long_addr) off = LOWORD(off);
|
||||
return PTR_SEG_OFF_TO_LIN( seg, off );
|
||||
|
@ -38,7 +38,7 @@ inline static void *make_ptr( CONTEXT86 *context, DWORD seg, DWORD off, int long
|
|||
inline static void *get_stack( CONTEXT86 *context )
|
||||
{
|
||||
if (ISV86(context))
|
||||
return DOSMEM_MemoryBase() + (context->SegSs << 4) + LOWORD(context->Esp);
|
||||
return PTR_REAL_TO_LIN( context->SegSs, context->Esp );
|
||||
if (IS_SELECTOR_SYSTEM(context->SegSs))
|
||||
return (void *)context->Esp;
|
||||
if (IS_SELECTOR_32BIT(context->SegSs))
|
||||
|
|
|
@ -47,8 +47,9 @@ typedef struct {
|
|||
|
||||
#define CON_BUFFER 128
|
||||
|
||||
#define SYSTEM_STRATEGY_NUL 0x0100
|
||||
#define SYSTEM_STRATEGY_CON 0x0101
|
||||
enum strategy { SYSTEM_STRATEGY_NUL, SYSTEM_STRATEGY_CON, NB_SYSTEM_STRATEGIES };
|
||||
|
||||
static void *strategy_data[NB_SYSTEM_STRATEGIES];
|
||||
|
||||
#define NONEXT ((DWORD)-1)
|
||||
|
||||
|
@ -150,20 +151,19 @@ static void do_lret(CONTEXT86*ctx)
|
|||
static void do_strategy(CONTEXT86*ctx, int id, int extra)
|
||||
{
|
||||
REQUEST_HEADER *hdr = CTX_SEG_OFF_TO_LIN(ctx, ES_reg(ctx), EBX_reg(ctx));
|
||||
void **hdr_ptr = DOSVM_GetSystemData(id);
|
||||
void **hdr_ptr = strategy_data[id];
|
||||
|
||||
if (!hdr_ptr) {
|
||||
hdr_ptr = calloc(1,sizeof(void *)+extra);
|
||||
DOSVM_SetSystemData(id, hdr_ptr);
|
||||
strategy_data[id] = hdr_ptr;
|
||||
}
|
||||
|
||||
*hdr_ptr = hdr;
|
||||
do_lret(ctx);
|
||||
}
|
||||
|
||||
static REQUEST_HEADER * get_hdr(int id, void**extra)
|
||||
{
|
||||
void **hdr_ptr = DOSVM_GetSystemData(id);
|
||||
void **hdr_ptr = strategy_data[id];
|
||||
if (extra)
|
||||
*extra = hdr_ptr ? (void*)(hdr_ptr+1) : (void *)NULL;
|
||||
return hdr_ptr ? *hdr_ptr : (void *)NULL;
|
||||
|
@ -209,7 +209,6 @@ static void WINAPI con_interrupt(CONTEXT86*ctx)
|
|||
BYTE *curbuffer = (lol->offs_unread_CON) ?
|
||||
(((BYTE*)dataseg) + lol->offs_unread_CON) : (BYTE*)NULL;
|
||||
DOS_DEVICE_HEADER *con = dataseg->dev;
|
||||
LPDOSTASK lpDosTask = MZ_Current();
|
||||
|
||||
switch (hdr->command) {
|
||||
case CMD_INPUT:
|
||||
|
@ -301,7 +300,7 @@ static void WINAPI con_interrupt(CONTEXT86*ctx)
|
|||
/* a character */
|
||||
if ((len+1)<CON_BUFFER) {
|
||||
linebuffer[len] = LOBYTE(data);
|
||||
WriteFile(lpDosTask->hConOutput, &linebuffer[len++], 1, NULL, NULL);
|
||||
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), &linebuffer[len++], 1, NULL, NULL);
|
||||
}
|
||||
/* else beep, but I don't like noise */
|
||||
}
|
||||
|
@ -309,7 +308,7 @@ static void WINAPI con_interrupt(CONTEXT86*ctx)
|
|||
case '\b':
|
||||
if (len>0) {
|
||||
len--;
|
||||
WriteFile(lpDosTask->hConOutput, "\b \b", 3, NULL, NULL);
|
||||
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), "\b \b", 3, NULL, NULL);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -377,7 +376,7 @@ static void WINAPI con_interrupt(CONTEXT86*ctx)
|
|||
SELECTOROF(io->buffer),
|
||||
(DWORD)OFFSETOF(io->buffer));
|
||||
DWORD result = 0;
|
||||
WriteFile(lpDosTask->hConOutput, buffer, io->count, &result, NULL);
|
||||
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buffer, io->count, &result, NULL);
|
||||
io->count = result;
|
||||
hdr->status = STAT_DONE;
|
||||
}
|
||||
|
|
225
msdos/dosmem.c
225
msdos/dosmem.c
|
@ -5,30 +5,33 @@
|
|||
* Copyright 1996 Marcus Meissner
|
||||
*/
|
||||
|
||||
#include "config.h"
|
||||
|
||||
#include <signal.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef HAVE_SYS_MMAN_H
|
||||
# include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#include "winbase.h"
|
||||
#include "wine/winbase16.h"
|
||||
|
||||
#include "global.h"
|
||||
#include "ldt.h"
|
||||
#include "selectors.h"
|
||||
#include "miscemu.h"
|
||||
#include "vga.h"
|
||||
#include "dosexe.h"
|
||||
#include "debugtools.h"
|
||||
|
||||
DEFAULT_DEBUG_CHANNEL(dosmem)
|
||||
DECLARE_DEBUG_CHANNEL(selector)
|
||||
DEFAULT_DEBUG_CHANNEL(dosmem);
|
||||
DECLARE_DEBUG_CHANNEL(selector);
|
||||
|
||||
HANDLE16 DOSMEM_BiosDataSeg; /* BIOS data segment at 0x40:0 */
|
||||
HANDLE16 DOSMEM_BiosSysSeg; /* BIOS ROM segment at 0xf000:0 */
|
||||
|
||||
static char *DOSMEM_dosmem;
|
||||
|
||||
DWORD DOSMEM_CollateTable;
|
||||
|
||||
DWORD DOSMEM_ErrorCall;
|
||||
DWORD DOSMEM_ErrorBuffer;
|
||||
DWORD DOSMEM_CollateTable;
|
||||
|
||||
/* use 2 low bits of 'size' for the housekeeping */
|
||||
|
||||
|
@ -57,6 +60,17 @@ typedef struct {
|
|||
#define VM_STUB(x) (0x90CF00CD|(x<<8)) /* INT x; IRET; NOP */
|
||||
#define VM_STUB_SEGMENT 0xf000 /* BIOS segment */
|
||||
|
||||
/* DOS memory base */
|
||||
static char *DOSMEM_dosmem;
|
||||
/* bios area; normally at offset 0x400, but can be moved to trap NULL pointers */
|
||||
static void *DOSMEM_biosdata;
|
||||
|
||||
/* various real-mode code stubs */
|
||||
WORD DOSMEM_wrap_seg;
|
||||
WORD DOSMEM_xms_seg;
|
||||
WORD DOSMEM_dpmi_seg;
|
||||
WORD DOSMEM_dpmi_sel;
|
||||
|
||||
/***********************************************************************
|
||||
* DOSMEM_MemoryBase
|
||||
*
|
||||
|
@ -64,12 +78,7 @@ typedef struct {
|
|||
*/
|
||||
char *DOSMEM_MemoryBase(void)
|
||||
{
|
||||
LPDOSTASK lpDosTask = MZ_Current();
|
||||
|
||||
if (lpDosTask && lpDosTask->img)
|
||||
return lpDosTask->img;
|
||||
else
|
||||
return DOSMEM_dosmem;
|
||||
return DOSMEM_dosmem;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
|
@ -79,7 +88,7 @@ char *DOSMEM_MemoryBase(void)
|
|||
*/
|
||||
static char *DOSMEM_MemoryTop(void)
|
||||
{
|
||||
return DOSMEM_MemoryBase()+0x9FFFC; /* 640K */
|
||||
return DOSMEM_dosmem+0x9FFFC; /* 640K */
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
|
@ -89,7 +98,7 @@ static char *DOSMEM_MemoryTop(void)
|
|||
*/
|
||||
static dosmem_info *DOSMEM_InfoBlock(void)
|
||||
{
|
||||
return (dosmem_info*)(DOSMEM_MemoryBase()+0x10000); /* 64K */
|
||||
return (dosmem_info*)(DOSMEM_dosmem+0x10000); /* 64K */
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
|
@ -118,7 +127,7 @@ static dosmem_entry *DOSMEM_RootBlock(void)
|
|||
*/
|
||||
static void DOSMEM_FillIsrTable(void)
|
||||
{
|
||||
SEGPTR *isr = (SEGPTR*)DOSMEM_MemoryBase();
|
||||
SEGPTR *isr = (SEGPTR*)DOSMEM_dosmem;
|
||||
DWORD *stub = (DWORD*)((char*)isr + (VM_STUB_SEGMENT << 4));
|
||||
int x;
|
||||
|
||||
|
@ -133,24 +142,66 @@ static void DOSMEM_FillIsrTable(void)
|
|||
*/
|
||||
static void DOSMEM_InitDPMI(void)
|
||||
{
|
||||
extern UINT16 DPMI_wrap_seg;
|
||||
static char wrap_code[]={
|
||||
LPSTR ptr;
|
||||
|
||||
static const char wrap_code[]={
|
||||
0xCD,0x31, /* int $0x31 */
|
||||
0xCB /* lret */
|
||||
};
|
||||
LPSTR wrapper = (LPSTR)DOSMEM_GetBlock(sizeof(wrap_code), &DPMI_wrap_seg);
|
||||
|
||||
memcpy(wrapper, wrap_code, sizeof(wrap_code));
|
||||
static const char enter_xms[]=
|
||||
{
|
||||
/* XMS hookable entry point */
|
||||
0xEB,0x03, /* jmp entry */
|
||||
0x90,0x90,0x90, /* nop;nop;nop */
|
||||
/* entry: */
|
||||
/* real entry point */
|
||||
/* for simplicity, we'll just use the same hook as DPMI below */
|
||||
0xCD,0x31, /* int $0x31 */
|
||||
0xCB /* lret */
|
||||
};
|
||||
|
||||
static const char enter_pm[]=
|
||||
{
|
||||
0x50, /* pushw %ax */
|
||||
0x52, /* pushw %dx */
|
||||
0x55, /* pushw %bp */
|
||||
0x89,0xE5, /* movw %sp,%bp */
|
||||
/* get return CS */
|
||||
0x8B,0x56,0x08, /* movw 8(%bp),%dx */
|
||||
/* just call int 31 here to get into protected mode... */
|
||||
/* it'll check whether it was called from dpmi_seg... */
|
||||
0xCD,0x31, /* int $0x31 */
|
||||
/* we are now in the context of a 16-bit relay call */
|
||||
/* need to fixup our stack;
|
||||
* 16-bit relay return address will be lost, but we won't worry quite yet */
|
||||
0x8E,0xD0, /* movw %ax,%ss */
|
||||
0x66,0x0F,0xB7,0xE5, /* movzwl %bp,%esp */
|
||||
/* set return CS */
|
||||
0x89,0x56,0x08, /* movw %dx,8(%bp) */
|
||||
0x5D, /* popw %bp */
|
||||
0x5A, /* popw %dx */
|
||||
0x58, /* popw %ax */
|
||||
0xCB /* lret */
|
||||
};
|
||||
|
||||
ptr = DOSMEM_GetBlock( sizeof(wrap_code), &DOSMEM_wrap_seg );
|
||||
memcpy( ptr, wrap_code, sizeof(wrap_code) );
|
||||
ptr = DOSMEM_GetBlock( sizeof(enter_xms), &DOSMEM_xms_seg );
|
||||
memcpy( ptr, enter_xms, sizeof(enter_xms) );
|
||||
ptr = DOSMEM_GetBlock( sizeof(enter_pm), &DOSMEM_dpmi_seg );
|
||||
memcpy( ptr, enter_pm, sizeof(enter_pm) );
|
||||
DOSMEM_dpmi_sel = SELECTOR_AllocBlock( ptr, sizeof(enter_pm), SEGMENT_CODE, FALSE, FALSE );
|
||||
}
|
||||
|
||||
BIOSDATA * DOSMEM_BiosData()
|
||||
{
|
||||
return (BIOSDATA *)(DOSMEM_MemoryBase()+0x400);
|
||||
return (BIOSDATA *)DOSMEM_biosdata;
|
||||
}
|
||||
|
||||
BYTE * DOSMEM_BiosSys()
|
||||
{
|
||||
return DOSMEM_MemoryBase()+0xf0000;
|
||||
return DOSMEM_dosmem+0xf0000;
|
||||
}
|
||||
|
||||
struct _DOS_LISTOFLISTS * DOSMEM_LOL()
|
||||
|
@ -293,6 +344,7 @@ static void DOSMEM_InitCollateTable()
|
|||
*/
|
||||
static void DOSMEM_InitErrorTable()
|
||||
{
|
||||
#if 0 /* no longer used */
|
||||
DWORD x;
|
||||
char *call;
|
||||
|
||||
|
@ -336,7 +388,7 @@ static void DOSMEM_InitErrorTable()
|
|||
call = DOSMEM_MapRealToLinear(DOSMEM_ErrorCall);
|
||||
|
||||
memset(call, 0, SIZE_TO_ALLOCATE);
|
||||
|
||||
#endif
|
||||
/* Fixme - Copy assembly into buffer here */
|
||||
}
|
||||
|
||||
|
@ -368,28 +420,56 @@ static void DOSMEM_InitMemory(void)
|
|||
;
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* DOSMEM_MovePointers
|
||||
/**********************************************************************
|
||||
* setup_dos_mem
|
||||
*
|
||||
* Relocates any pointers into DOS memory to a new address space.
|
||||
* Setup the first megabyte for DOS memory access
|
||||
*/
|
||||
static void DOSMEM_MovePointers(LPVOID dest, LPVOID src, DWORD size)
|
||||
static void setup_dos_mem( int dos_init )
|
||||
{
|
||||
unsigned long delta = (char *) dest - (char *) src;
|
||||
unsigned cnt;
|
||||
ldt_entry ent;
|
||||
int bios_offset = 0x400;
|
||||
int page_size = VIRTUAL_GetPageSize();
|
||||
void *addr = VIRTUAL_mmap( -1, (void *)page_size, 0x110000-page_size, 0,
|
||||
PROT_READ | PROT_WRITE | PROT_EXEC, 0 );
|
||||
if (addr == (void *)page_size) /* we got what we wanted */
|
||||
{
|
||||
/* now map from address 0 */
|
||||
addr = VIRTUAL_mmap( -1, NULL, 0x110000, 0,
|
||||
PROT_READ | PROT_WRITE | PROT_EXEC, MAP_FIXED );
|
||||
if (addr)
|
||||
{
|
||||
ERR("MAP_FIXED failed at address 0 for DOS address space\n" );
|
||||
ExitProcess(1);
|
||||
}
|
||||
|
||||
/* relocate base addresses of any selectors pointing into memory */
|
||||
for (cnt=FIRST_LDT_ENTRY_TO_ALLOC; cnt<LDT_SIZE; cnt++) {
|
||||
LDT_GetEntry(cnt, &ent);
|
||||
if ((ent.base >= (unsigned long)src) && \
|
||||
(ent.base < ((unsigned long)src + size))) {
|
||||
ent.base += delta;
|
||||
LDT_SetEntry(cnt, &ent);
|
||||
/* inform the memory manager that there is a mapping here */
|
||||
VirtualAlloc( addr, 0x110000, MEM_RESERVE | MEM_SYSTEM, PAGE_EXECUTE_READWRITE );
|
||||
|
||||
/* protect the first 64K to catch NULL pointers */
|
||||
if (!dos_init)
|
||||
{
|
||||
VirtualProtect( addr, 0x10000, PAGE_NOACCESS, NULL );
|
||||
/* move the BIOS data from 0x400 to 0xf0400 */
|
||||
bios_offset += 0xf0000;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ERR("Cannot use first megabyte for DOS address space, please report\n" );
|
||||
if (dos_init) ExitProcess(1);
|
||||
/* allocate the DOS area somewhere else */
|
||||
addr = VirtualAlloc( NULL, 0x110000, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
|
||||
if (!addr)
|
||||
{
|
||||
ERR( "Cannot allocate DOS memory\n" );
|
||||
ExitProcess(1);
|
||||
}
|
||||
}
|
||||
DOSMEM_dosmem = addr;
|
||||
DOSMEM_biosdata = (char *)addr + bios_offset;
|
||||
}
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* DOSMEM_Init
|
||||
*
|
||||
|
@ -398,44 +478,41 @@ static void DOSMEM_MovePointers(LPVOID dest, LPVOID src, DWORD size)
|
|||
*/
|
||||
BOOL DOSMEM_Init(BOOL dos_init)
|
||||
{
|
||||
LPVOID base = DOSMEM_MemoryBase();
|
||||
BOOL do_init = dos_init && !DOSMEM_dosmem;
|
||||
static int already_done;
|
||||
|
||||
if (!base)
|
||||
if (!already_done)
|
||||
{
|
||||
/* Allocate 1 MB dosmemory
|
||||
*/
|
||||
DOSMEM_dosmem = VirtualAlloc( NULL, 0x100000, MEM_COMMIT,
|
||||
PAGE_EXECUTE_READWRITE );
|
||||
if (!DOSMEM_dosmem)
|
||||
{
|
||||
WARN("Could not allocate DOS memory.\n" );
|
||||
return FALSE;
|
||||
}
|
||||
DOSMEM_BiosDataSeg = GLOBAL_CreateBlock(GMEM_FIXED,DOSMEM_dosmem+0x400,
|
||||
0x100, 0, FALSE, FALSE, FALSE );
|
||||
DOSMEM_BiosSysSeg = GLOBAL_CreateBlock(GMEM_FIXED,DOSMEM_dosmem+0xf0000,
|
||||
0x10000, 0, FALSE, FALSE, FALSE );
|
||||
base = DOSMEM_dosmem;
|
||||
do_init = TRUE;
|
||||
}
|
||||
setup_dos_mem( dos_init );
|
||||
|
||||
if (do_init) {
|
||||
DOSMEM_FillIsrTable();
|
||||
DOSMEM_BiosDataSeg = GLOBAL_CreateBlock(GMEM_FIXED,DOSMEM_biosdata,
|
||||
0x100, 0, FALSE, FALSE, FALSE );
|
||||
DOSMEM_BiosSysSeg = GLOBAL_CreateBlock(GMEM_FIXED,DOSMEM_dosmem+0xf0000,
|
||||
0x10000, 0, FALSE, FALSE, FALSE );
|
||||
DOSMEM_FillBiosSegments();
|
||||
DOSMEM_InitMemory();
|
||||
DOSMEM_InitCollateTable();
|
||||
DOSMEM_InitErrorTable();
|
||||
DOSMEM_InitDPMI();
|
||||
DOSDEV_InstallDOSDevices();
|
||||
DOSDEV_InstallDOSDevices();
|
||||
already_done = 1;
|
||||
}
|
||||
else if (dos_init)
|
||||
{
|
||||
/* bootstrap the new V86 task with a copy of the "system" memory */
|
||||
memcpy(base, DOSMEM_dosmem, 0x100000);
|
||||
/* then move existing selectors to it */
|
||||
DOSMEM_MovePointers(base, DOSMEM_dosmem, 0x100000);
|
||||
if (DOSMEM_dosmem)
|
||||
{
|
||||
ERR( "Needs access to the first megabyte for DOS mode\n" );
|
||||
ExitProcess(1);
|
||||
}
|
||||
MESSAGE( "Warning: unprotecting the first 64KB of memory to allow real-mode calls.\n"
|
||||
" NULL pointer accesses will no longer be caught.\n" );
|
||||
VirtualProtect( NULL, 0x10000, PAGE_EXECUTE_READWRITE, NULL );
|
||||
/* copy the BIOS area down to 0x400 */
|
||||
memcpy( DOSMEM_dosmem + 0x400, DOSMEM_biosdata, 0x100 );
|
||||
DOSMEM_biosdata = DOSMEM_dosmem + 0x400;
|
||||
SetSelectorBase( DOSMEM_BiosDataSeg, 0x400 );
|
||||
}
|
||||
/* interrupt table is at addr 0, so only do this when setting up DOS mode */
|
||||
if (dos_init) DOSMEM_FillIsrTable();
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
@ -513,7 +590,7 @@ LPVOID DOSMEM_GetBlock(UINT size, UINT16* pseg)
|
|||
|
||||
info_block->blocks++;
|
||||
info_block->free -= dm->size;
|
||||
if( pseg ) *pseg = (block - DOSMEM_MemoryBase()) >> 4;
|
||||
if( pseg ) *pseg = (block - DOSMEM_dosmem) >> 4;
|
||||
#ifdef __DOSMEM_DEBUG__
|
||||
dm->size |= DM_BLOCK_DEBUG;
|
||||
#endif
|
||||
|
@ -535,7 +612,7 @@ BOOL DOSMEM_FreeBlock(void* ptr)
|
|||
|
||||
if( ptr >= (void*)(((char*)DOSMEM_RootBlock()) + sizeof(dosmem_entry)) &&
|
||||
ptr < (void*)DOSMEM_MemoryTop() && !((((char*)ptr)
|
||||
- DOSMEM_MemoryBase()) & 0xf) )
|
||||
- DOSMEM_dosmem) & 0xf) )
|
||||
{
|
||||
dosmem_entry *dm = (dosmem_entry*)(((char*)ptr) - sizeof(dosmem_entry));
|
||||
|
||||
|
@ -565,11 +642,11 @@ LPVOID DOSMEM_ResizeBlock(void* ptr, UINT size, UINT16* pseg)
|
|||
|
||||
if( ptr >= (void*)(((char*)DOSMEM_RootBlock()) + sizeof(dosmem_entry)) &&
|
||||
ptr < (void*)DOSMEM_MemoryTop() && !((((char*)ptr)
|
||||
- DOSMEM_MemoryBase()) & 0xf) )
|
||||
- DOSMEM_dosmem) & 0xf) )
|
||||
{
|
||||
dosmem_entry *dm = (dosmem_entry*)(((char*)ptr) - sizeof(dosmem_entry));
|
||||
|
||||
if( pseg ) *pseg = ((char*)ptr - DOSMEM_MemoryBase()) >> 4;
|
||||
if( pseg ) *pseg = ((char*)ptr - DOSMEM_dosmem) >> 4;
|
||||
|
||||
if( !(dm->size & (DM_BLOCK_FREE | DM_BLOCK_TERMINAL))
|
||||
)
|
||||
|
@ -689,9 +766,9 @@ UINT DOSMEM_Available(void)
|
|||
*/
|
||||
UINT DOSMEM_MapLinearToDos(LPVOID ptr)
|
||||
{
|
||||
if (((char*)ptr >= DOSMEM_MemoryBase()) &&
|
||||
((char*)ptr < DOSMEM_MemoryBase() + 0x100000))
|
||||
return (UINT)ptr - (UINT)DOSMEM_MemoryBase();
|
||||
if (((char*)ptr >= DOSMEM_dosmem) &&
|
||||
((char*)ptr < DOSMEM_dosmem + 0x100000))
|
||||
return (UINT)ptr - (UINT)DOSMEM_dosmem;
|
||||
return (UINT)ptr;
|
||||
}
|
||||
|
||||
|
@ -703,7 +780,7 @@ UINT DOSMEM_MapLinearToDos(LPVOID ptr)
|
|||
*/
|
||||
LPVOID DOSMEM_MapDosToLinear(UINT ptr)
|
||||
{
|
||||
if (ptr < 0x100000) return (LPVOID)(ptr + (UINT)DOSMEM_MemoryBase());
|
||||
if (ptr < 0x100000) return (LPVOID)(ptr + (UINT)DOSMEM_dosmem);
|
||||
return (LPVOID)ptr;
|
||||
}
|
||||
|
||||
|
@ -717,7 +794,7 @@ LPVOID DOSMEM_MapRealToLinear(DWORD x)
|
|||
{
|
||||
LPVOID lin;
|
||||
|
||||
lin=DOSMEM_MemoryBase()+(x&0xffff)+(((x&0xffff0000)>>16)*16);
|
||||
lin=DOSMEM_dosmem+(x&0xffff)+(((x&0xffff0000)>>16)*16);
|
||||
TRACE_(selector)("(0x%08lx) returns 0x%p.\n", x, lin );
|
||||
return lin;
|
||||
}
|
||||
|
|
135
msdos/dpmi.c
135
msdos/dpmi.c
|
@ -62,7 +62,6 @@ typedef struct tagRMCB {
|
|||
|
||||
static RMCB *FirstRMCB = NULL;
|
||||
|
||||
UINT16 DPMI_wrap_seg;
|
||||
|
||||
/**********************************************************************
|
||||
* DPMI_xalloc
|
||||
|
@ -261,7 +260,7 @@ static void DPMI_CallRMCBProc( CONTEXT86 *context, RMCB *rmcb, WORD flag )
|
|||
DWORD esp,edi;
|
||||
|
||||
INT_SetRealModeContext((REALMODECALL *)PTR_SEG_OFF_TO_LIN( rmcb->regs_sel, rmcb->regs_ofs ), context);
|
||||
ss = SELECTOR_AllocBlock( DOSMEM_MemoryBase() + (DWORD)(SS_reg(context)<<4), 0x10000, SEGMENT_DATA, FALSE, FALSE );
|
||||
ss = SELECTOR_AllocBlock( (void *)(SS_reg(context)<<4), 0x10000, SEGMENT_DATA, FALSE, FALSE );
|
||||
esp = ESP_reg(context);
|
||||
|
||||
FIXME("untested!\n");
|
||||
|
@ -354,17 +353,12 @@ callrmproc_again:
|
|||
CurrRMCB = CurrRMCB->next;
|
||||
|
||||
if (!(CurrRMCB || lpDosTask)) {
|
||||
#ifdef MZ_SUPPORTED
|
||||
FIXME("DPMI real-mode call using DOS VM task system, not fully tested!\n");
|
||||
TRACE("creating VM86 task\n");
|
||||
if (!MZ_InitTask( lpDosTask = MZ_AllocDPMITask() )) {
|
||||
if (!(lpDosTask = MZ_AllocDPMITask() )) {
|
||||
ERR("could not setup VM86 task\n");
|
||||
return 1;
|
||||
}
|
||||
#else
|
||||
ERR("Actual real-mode calls not supported on this architecture!\n");
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
if (!already) {
|
||||
if (!SS_reg(context)) {
|
||||
|
@ -388,7 +382,7 @@ callrmproc_again:
|
|||
*stack16 = LOWORD(EFL_reg(context));
|
||||
}
|
||||
/* push return address (return to interrupt wrapper) */
|
||||
*(--stack16) = DPMI_wrap_seg;
|
||||
*(--stack16) = DOSMEM_wrap_seg;
|
||||
*(--stack16) = 0;
|
||||
/* adjust stack */
|
||||
ESP_reg(context) -= 2*sizeof(WORD);
|
||||
|
@ -399,21 +393,16 @@ callrmproc_again:
|
|||
/* RMCB call, invoke protected-mode handler directly */
|
||||
DPMI_CallRMCBProc(context, CurrRMCB, lpDosTask ? lpDosTask->dpmi_flag : 0);
|
||||
/* check if we returned to where we thought we would */
|
||||
if ((CS_reg(context) != DPMI_wrap_seg) ||
|
||||
if ((CS_reg(context) != DOSMEM_wrap_seg) ||
|
||||
(LOWORD(EIP_reg(context)) != 0)) {
|
||||
/* we need to continue at different address in real-mode space,
|
||||
so we need to set it all up for real mode again */
|
||||
goto callrmproc_again;
|
||||
}
|
||||
} else {
|
||||
#ifdef MZ_SUPPORTED
|
||||
TRACE("entering real mode...\n");
|
||||
DOSVM_Enter( context );
|
||||
TRACE("returned from real-mode call\n");
|
||||
#else
|
||||
/* we should never get here, but... */
|
||||
ERR("cannot perform real-mode call\n");
|
||||
#endif
|
||||
}
|
||||
if (alloc) DOSMEM_FreeBlock( addr );
|
||||
return 0;
|
||||
|
@ -585,16 +574,14 @@ void WINAPI DPMI_FreeInternalRMCB( FARPROC16 proc )
|
|||
}
|
||||
|
||||
|
||||
#ifdef MZ_SUPPORTED
|
||||
/* (see loader/dos/module.c, function MZ_InitDPMI) */
|
||||
/* (see dosmem.c, function DOSMEM_InitDPMI) */
|
||||
|
||||
static void StartPM( CONTEXT86 *context, LPDOSTASK lpDosTask )
|
||||
{
|
||||
char *base = DOSMEM_MemoryBase();
|
||||
UINT16 cs, ss, ds, es;
|
||||
CONTEXT86 pm_ctx;
|
||||
DWORD psp_ofs = (DWORD)(lpDosTask->psp_seg<<4);
|
||||
PDB16 *psp = (PDB16 *)(base + psp_ofs);
|
||||
PDB16 *psp = (PDB16 *)psp_ofs;
|
||||
HANDLE16 env_seg = psp->environment;
|
||||
int is32;
|
||||
|
||||
|
@ -602,22 +589,22 @@ static void StartPM( CONTEXT86 *context, LPDOSTASK lpDosTask )
|
|||
lpDosTask->dpmi_flag = AX_reg(context);
|
||||
is32 = lpDosTask->dpmi_flag & 1;
|
||||
/* our mode switch wrapper have placed the desired CS into DX */
|
||||
cs = SELECTOR_AllocBlock( base + (DWORD)(DX_reg(context)<<4), 0x10000, SEGMENT_CODE, FALSE, FALSE );
|
||||
cs = SELECTOR_AllocBlock( (void *)(DX_reg(context)<<4), 0x10000, SEGMENT_CODE, FALSE, FALSE );
|
||||
/* due to a flaw in some CPUs (at least mine), it is best to mark stack segments as 32-bit if they
|
||||
can be used in 32-bit code. Otherwise, these CPUs may not set the high word of esp during a
|
||||
ring transition (from kernel code) to the 16-bit stack, and this causes trouble if executing
|
||||
32-bit code using this stack. */
|
||||
ss = SELECTOR_AllocBlock( base + (DWORD)(SS_reg(context)<<4), 0x10000, SEGMENT_DATA, is32, FALSE );
|
||||
ss = SELECTOR_AllocBlock( (void *)(SS_reg(context)<<4), 0x10000, SEGMENT_DATA, is32, FALSE );
|
||||
/* do the same for the data segments, just in case */
|
||||
if (DS_reg(context) == SS_reg(context)) ds = ss;
|
||||
else ds = SELECTOR_AllocBlock( base + (DWORD)(DS_reg(context)<<4), 0x10000, SEGMENT_DATA, is32, FALSE );
|
||||
es = SELECTOR_AllocBlock( base + psp_ofs, 0x100, SEGMENT_DATA, is32, FALSE );
|
||||
else ds = SELECTOR_AllocBlock( (void *)(DS_reg(context)<<4), 0x10000, SEGMENT_DATA, is32, FALSE );
|
||||
es = SELECTOR_AllocBlock( psp, 0x100, SEGMENT_DATA, is32, FALSE );
|
||||
/* convert environment pointer, as the spec says, but we're a bit lazy about the size here... */
|
||||
psp->environment = SELECTOR_AllocBlock( base + (DWORD)(env_seg<<4),
|
||||
psp->environment = SELECTOR_AllocBlock( (void *)(env_seg<<4),
|
||||
0x10000, SEGMENT_DATA, FALSE, FALSE );
|
||||
|
||||
pm_ctx = *context;
|
||||
CS_reg(&pm_ctx) = lpDosTask->dpmi_sel;
|
||||
CS_reg(&pm_ctx) = DOSMEM_dpmi_sel;
|
||||
/* our mode switch wrapper expects the new CS in DX, and the new SS in AX */
|
||||
EAX_reg(&pm_ctx) = ss;
|
||||
EDX_reg(&pm_ctx) = cs;
|
||||
|
@ -697,26 +684,6 @@ void WINAPI DPMI_RawModeSwitch( SIGCONTEXT *context )
|
|||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
#if 0
|
||||
void WINAPI DPMI_RawModeSwitch( SIGCONTEXT *context )
|
||||
{
|
||||
ERR("don't even think about DPMI raw mode switch without DOS support!\n");
|
||||
ExitProcess(1);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define DOS_APP_ISDOS(addr,base) ((addr) < 0x110000)
|
||||
#define DOS_WINE_ISDOS(addr,base) (((addr) >= (base)) && ((addr) < (base) + 0x110000))
|
||||
#define DOS_UC_APPTOWINE(addr,base) ((addr) + (base))
|
||||
#define DOS_UC_WINETOAPP(addr,base) ((addr) - (base))
|
||||
#define DOS_APPTOWINE(addr,base) (DOS_APP_ISDOS(addr,base) ? DOS_UC_APPTOWINE(addr,base) : (addr))
|
||||
#define DOS_WINETOAPP(addr,base) (DOS_WINE_ISDOS(addr,base) ? DOS_UC_WINETOAPP(addr,base) : (addr))
|
||||
#define DOS_BADLIMIT(addr,base,limit) \
|
||||
((limit == 0xffffffff) || /* disallow "fat DS" for now */ \
|
||||
(DOS_WINE_ISDOS(addr,base) && \
|
||||
((addr) + (limit) > (base) + 0x110000)))
|
||||
|
||||
/**********************************************************************
|
||||
* INT_Int31Handler
|
||||
|
@ -738,16 +705,15 @@ void WINAPI INT_Int31Handler( CONTEXT86 *context )
|
|||
|
||||
LPDOSTASK lpDosTask = MZ_Current();
|
||||
|
||||
#ifdef MZ_SUPPORTED
|
||||
if (ISV86(context) && lpDosTask) {
|
||||
/* Called from real mode, check if it's our wrapper */
|
||||
TRACE("called from real mode\n");
|
||||
if (CS_reg(context)==lpDosTask->dpmi_seg) {
|
||||
if (CS_reg(context)==DOSMEM_dpmi_seg) {
|
||||
/* This is the protected mode switch */
|
||||
StartPM(context,lpDosTask);
|
||||
return;
|
||||
} else
|
||||
if (CS_reg(context)==lpDosTask->xms_seg) {
|
||||
if (CS_reg(context)==DOSMEM_xms_seg) {
|
||||
/* This is the XMS driver entry point */
|
||||
XMS_Handler(context);
|
||||
return;
|
||||
|
@ -766,7 +732,6 @@ void WINAPI INT_Int31Handler( CONTEXT86 *context )
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
RESET_CFLAG(context);
|
||||
switch(AX_reg(context))
|
||||
|
@ -849,12 +814,6 @@ void WINAPI INT_Int31Handler( CONTEXT86 *context )
|
|||
}
|
||||
else
|
||||
{
|
||||
#ifdef MZ_SUPPORTED
|
||||
if (lpDosTask) {
|
||||
DWORD base = (DWORD)DOSMEM_MemoryBase();
|
||||
dw = DOS_WINETOAPP(dw, base);
|
||||
}
|
||||
#endif
|
||||
CX_reg(context) = HIWORD(W32S_WINE2APP(dw, offset));
|
||||
DX_reg(context) = LOWORD(W32S_WINE2APP(dw, offset));
|
||||
}
|
||||
|
@ -865,40 +824,12 @@ void WINAPI INT_Int31Handler( CONTEXT86 *context )
|
|||
BX_reg(context),
|
||||
W32S_APP2WINE(MAKELONG(DX_reg(context),CX_reg(context)), offset));
|
||||
dw = W32S_APP2WINE(MAKELONG(DX_reg(context), CX_reg(context)), offset);
|
||||
#ifdef MZ_SUPPORTED
|
||||
if (lpDosTask) {
|
||||
DWORD base = (DWORD)DOSMEM_MemoryBase();
|
||||
dw = DOS_APPTOWINE(dw, base);
|
||||
}
|
||||
#endif
|
||||
SetSelectorBase(BX_reg(context), dw);
|
||||
break;
|
||||
|
||||
case 0x0008: /* Set selector limit */
|
||||
TRACE("set selector limit (0x%04x,0x%08lx)\n",BX_reg(context),MAKELONG(DX_reg(context),CX_reg(context)));
|
||||
dw = MAKELONG( DX_reg(context), CX_reg(context) );
|
||||
#ifdef MZ_SUPPORTED
|
||||
if (lpDosTask) {
|
||||
DWORD base = (DWORD)DOSMEM_MemoryBase();
|
||||
DWORD sbase = GetSelectorBase( BX_reg(context) );
|
||||
if (!sbase) {
|
||||
/* the app has set the limit without setting the base,
|
||||
* it must be relying on that the default should be DOS space;
|
||||
* so set the base address now */
|
||||
SetSelectorBase( BX_reg(context), sbase = base );
|
||||
if (dw == 0xffffffff) {
|
||||
/* djgpp does this without checking (in _dos_ds setup, crt1.c),
|
||||
* so we have to override the limit here */
|
||||
dw = 0x110000;
|
||||
}
|
||||
}
|
||||
if (DOS_BADLIMIT(sbase, base, dw)) {
|
||||
AX_reg(context) = 0x8021; /* invalid value */
|
||||
SET_CFLAG(context);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
SetSelectorLimit16( BX_reg(context), dw );
|
||||
break;
|
||||
|
||||
|
@ -921,12 +852,6 @@ void WINAPI INT_Int31Handler( CONTEXT86 *context )
|
|||
{
|
||||
ldt_entry entry;
|
||||
LDT_GetEntry( SELECTOR_TO_ENTRY( BX_reg(context) ), &entry );
|
||||
#ifdef MZ_SUPPORTED
|
||||
if (lpDosTask) {
|
||||
DWORD base = (DWORD)DOSMEM_MemoryBase();
|
||||
entry.base = DOS_WINETOAPP(entry.base, base);
|
||||
}
|
||||
#endif
|
||||
entry.base = W32S_WINE2APP(entry.base, offset);
|
||||
|
||||
/* FIXME: should use ES:EDI for 32-bit clients */
|
||||
|
@ -942,18 +867,6 @@ void WINAPI INT_Int31Handler( CONTEXT86 *context )
|
|||
LDT_BytesToEntry( PTR_SEG_OFF_TO_LIN( ES_reg(context),
|
||||
DI_reg(context) ), &entry );
|
||||
entry.base = W32S_APP2WINE(entry.base, offset);
|
||||
#ifdef MZ_SUPPORTED
|
||||
if (lpDosTask) {
|
||||
DWORD base = (DWORD)DOSMEM_MemoryBase();
|
||||
entry.base = DOS_APPTOWINE(entry.base, base);
|
||||
if (DOS_BADLIMIT(entry.base, base, entry.limit)) {
|
||||
AX_reg(context) = 0x8021; /* invalid value */
|
||||
SET_CFLAG(context);
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
LDT_SetEntry( SELECTOR_TO_ENTRY( BX_reg(context) ), &entry );
|
||||
}
|
||||
break;
|
||||
|
@ -1032,7 +945,7 @@ void WINAPI INT_Int31Handler( CONTEXT86 *context )
|
|||
/* we probably won't need this kind of state saving */
|
||||
AX_reg(context) = 0;
|
||||
/* real mode: just point to the lret */
|
||||
BX_reg(context) = DPMI_wrap_seg;
|
||||
BX_reg(context) = DOSMEM_wrap_seg;
|
||||
ECX_reg(context) = 2;
|
||||
/* protected mode: don't have any handler yet... */
|
||||
FIXME("no protected-mode dummy state save/restore handler yet\n");
|
||||
|
@ -1042,18 +955,12 @@ void WINAPI INT_Int31Handler( CONTEXT86 *context )
|
|||
|
||||
case 0x0306: /* Get Raw Mode Switch Addresses */
|
||||
TRACE("get raw mode switch addresses\n");
|
||||
if (lpDosTask) {
|
||||
/* real mode, point to standard DPMI return wrapper */
|
||||
BX_reg(context) = DPMI_wrap_seg;
|
||||
ECX_reg(context) = 0;
|
||||
/* protected mode, point to DPMI call wrapper */
|
||||
SI_reg(context) = lpDosTask->dpmi_sel;
|
||||
EDI_reg(context) = 8; /* offset of the INT 0x31 call */
|
||||
} else {
|
||||
ERR("win app attempting to get raw mode switch!\n");
|
||||
AX_reg(context) = 0x8001; /* unsupported function */
|
||||
SET_CFLAG(context);
|
||||
}
|
||||
/* real mode, point to standard DPMI return wrapper */
|
||||
BX_reg(context) = DOSMEM_wrap_seg;
|
||||
ECX_reg(context) = 0;
|
||||
/* protected mode, point to DPMI call wrapper */
|
||||
SI_reg(context) = DOSMEM_dpmi_sel;
|
||||
EDI_reg(context) = 8; /* offset of the INT 0x31 call */
|
||||
break;
|
||||
case 0x0400: /* Get DPMI version */
|
||||
TRACE("get DPMI version\n");
|
||||
|
|
|
@ -15,9 +15,11 @@ DEFAULT_DEBUG_CHANNEL(int);
|
|||
|
||||
#define QUEUELEN 31
|
||||
|
||||
typedef struct {
|
||||
static struct
|
||||
{
|
||||
BYTE queuelen,queue[QUEUELEN],ascii[QUEUELEN];
|
||||
} KBDSYSTEM;
|
||||
} kbdinfo;
|
||||
|
||||
|
||||
/**********************************************************************
|
||||
* INT_Int09Handler
|
||||
|
@ -58,48 +60,35 @@ void WINAPI INT_Int09Handler( CONTEXT86 *context )
|
|||
DOSVM_PIC_ioport_out(0x20, 0x20); /* send EOI */
|
||||
}
|
||||
|
||||
static void KbdRelay( LPDOSTASK lpDosTask, CONTEXT86 *context, void *data )
|
||||
static void KbdRelay( CONTEXT86 *context, void *data )
|
||||
{
|
||||
KBDSYSTEM *sys = (KBDSYSTEM *)DOSVM_GetSystemData(0x09);
|
||||
|
||||
if (sys && sys->queuelen) {
|
||||
if (kbdinfo.queuelen) {
|
||||
/* cleanup operation, called from DOSVM_PIC_ioport_out:
|
||||
* we'll remove current scancode from keyboard buffer here,
|
||||
* rather than in ReadScan, because some DOS apps depend on
|
||||
* the scancode being available for reading multiple times... */
|
||||
if (--sys->queuelen) {
|
||||
memmove(sys->queue,sys->queue+1,sys->queuelen);
|
||||
memmove(sys->ascii,sys->ascii+1,sys->queuelen);
|
||||
if (--kbdinfo.queuelen) {
|
||||
memmove(kbdinfo.queue,kbdinfo.queue+1,kbdinfo.queuelen);
|
||||
memmove(kbdinfo.ascii,kbdinfo.ascii+1,kbdinfo.queuelen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void WINAPI INT_Int09SendScan( BYTE scan, BYTE ascii )
|
||||
{
|
||||
KBDSYSTEM *sys = (KBDSYSTEM *)DOSVM_GetSystemData(0x09);
|
||||
if (!sys) {
|
||||
sys = calloc(1,sizeof(KBDSYSTEM));
|
||||
DOSVM_SetSystemData(0x09,sys);
|
||||
}
|
||||
if (sys->queuelen == QUEUELEN) {
|
||||
if (kbdinfo.queuelen == QUEUELEN) {
|
||||
ERR("keyboard queue overflow\n");
|
||||
return;
|
||||
}
|
||||
/* add scancode to queue */
|
||||
sys->queue[sys->queuelen] = scan;
|
||||
sys->ascii[sys->queuelen++] = ascii;
|
||||
kbdinfo.queue[kbdinfo.queuelen] = scan;
|
||||
kbdinfo.ascii[kbdinfo.queuelen++] = ascii;
|
||||
/* tell app to read it by triggering IRQ 1 (int 09) */
|
||||
DOSVM_QueueEvent(1,DOS_PRIORITY_KEYBOARD,KbdRelay,NULL);
|
||||
}
|
||||
|
||||
BYTE WINAPI INT_Int09ReadScan( BYTE*ascii )
|
||||
{
|
||||
KBDSYSTEM *sys = (KBDSYSTEM *)DOSVM_GetSystemData(0x09);
|
||||
if (sys) {
|
||||
if (ascii) *ascii = sys->ascii[0];
|
||||
return sys->queue[0];
|
||||
} else {
|
||||
if (ascii) *ascii = 0;
|
||||
return 0;
|
||||
}
|
||||
if (ascii) *ascii = kbdinfo.ascii[0];
|
||||
return kbdinfo.queue[0];
|
||||
}
|
||||
|
|
|
@ -115,9 +115,7 @@ void WINAPI INT_Int2fHandler( CONTEXT86 *context )
|
|||
break;
|
||||
case 0x10: /* XMS v2+ get driver address */
|
||||
{
|
||||
LPDOSTASK lpDosTask = MZ_Current();
|
||||
|
||||
ES_reg(context) = lpDosTask ? lpDosTask->xms_seg : 0;
|
||||
ES_reg(context) = DOSMEM_xms_seg;
|
||||
BX_reg(context) = 0;
|
||||
break;
|
||||
}
|
||||
|
@ -349,15 +347,13 @@ static void do_int2f_16( CONTEXT86 *context )
|
|||
#endif
|
||||
{
|
||||
SYSTEM_INFO si;
|
||||
LPDOSTASK lpDosTask = MZ_Current();
|
||||
|
||||
GetSystemInfo(&si);
|
||||
AX_reg(context) = 0x0000; /* DPMI Installed */
|
||||
BX_reg(context) = 0x0001; /* 32bits available */
|
||||
CL_reg(context) = si.wProcessorLevel;
|
||||
DX_reg(context) = 0x005a; /* DPMI major/minor 0.90 */
|
||||
SI_reg(context) = 0; /* # of para. of DOS extended private data */
|
||||
ES_reg(context) = lpDosTask ? lpDosTask->dpmi_seg : 0;
|
||||
ES_reg(context) = DOSMEM_dpmi_seg;
|
||||
DI_reg(context) = 0; /* ES:DI is DPMI switch entry point */
|
||||
break;
|
||||
}
|
||||
|
@ -402,9 +398,8 @@ static void MSCDEX_Dump(char* pfx, BYTE* req, int dorealmode)
|
|||
case 3:
|
||||
case 12:
|
||||
ptr += sprintf(ptr, "\n\t\t\t\tIO_struct => ");
|
||||
ios = (dorealmode) ?
|
||||
DOSMEM_MapRealToLinear(MAKELONG(PTR_AT(req, 14, WORD), PTR_AT(req, 16, WORD))) :
|
||||
PTR_SEG_OFF_TO_LIN(PTR_AT(req, 16, WORD), PTR_AT(req, 14, WORD));
|
||||
ios = (dorealmode) ? PTR_REAL_TO_LIN( PTR_AT(req, 16, WORD), PTR_AT(req, 14, WORD)) :
|
||||
PTR_SEG_OFF_TO_LIN(PTR_AT(req, 16, WORD), PTR_AT(req, 14, WORD));
|
||||
|
||||
for (i = 0; i < PTR_AT(req, 18, WORD); i++) {
|
||||
ptr += sprintf(ptr, "%02x ", ios[i]);
|
||||
|
@ -476,9 +471,7 @@ static void MSCDEX_Handler(CONTEXT86* context)
|
|||
BYTE Error = 255; /* No Error */
|
||||
int dorealmode = ISV86(context);
|
||||
|
||||
driver_request = (dorealmode) ?
|
||||
DOSMEM_MapRealToLinear(MAKELONG(BX_reg(context), ES_reg(context))) :
|
||||
PTR_SEG_OFF_TO_LIN(ES_reg(context), BX_reg(context));
|
||||
driver_request = CTX_SEG_OFF_TO_LIN(context, context->SegEs, context->Ebx);
|
||||
|
||||
if (!driver_request) {
|
||||
/* FIXME - to be deleted ?? */
|
||||
|
@ -517,7 +510,7 @@ static void MSCDEX_Handler(CONTEXT86* context)
|
|||
switch (driver_request[2]) {
|
||||
case 3:
|
||||
io_stru = (dorealmode) ?
|
||||
DOSMEM_MapRealToLinear(MAKELONG(PTR_AT(driver_request, 14, WORD), PTR_AT(driver_request, 16, WORD))) :
|
||||
PTR_REAL_TO_LIN( PTR_AT(driver_request, 16, WORD), PTR_AT(driver_request, 14, WORD) ) :
|
||||
PTR_SEG_OFF_TO_LIN(PTR_AT(driver_request, 16, WORD), PTR_AT(driver_request, 14, WORD));
|
||||
|
||||
TRACE(" --> IOCTL INPUT <%d>\n", io_stru[0]);
|
||||
|
@ -682,7 +675,7 @@ static void MSCDEX_Handler(CONTEXT86* context)
|
|||
|
||||
case 12:
|
||||
io_stru = (dorealmode) ?
|
||||
DOSMEM_MapRealToLinear(MAKELONG(PTR_AT(driver_request, 14, WORD), PTR_AT(driver_request, 16, WORD))) :
|
||||
PTR_REAL_TO_LIN( PTR_AT(driver_request, 16, WORD), PTR_AT(driver_request, 14, WORD)) :
|
||||
PTR_SEG_OFF_TO_LIN(PTR_AT(driver_request, 16, WORD), PTR_AT(driver_request, 14, WORD));
|
||||
|
||||
TRACE(" --> IOCTL OUTPUT <%d>\n", io_stru[0]);
|
||||
|
|
|
@ -13,11 +13,12 @@
|
|||
|
||||
DEFAULT_DEBUG_CHANNEL(int)
|
||||
|
||||
typedef struct {
|
||||
static struct
|
||||
{
|
||||
DWORD x, y, but;
|
||||
FARPROC16 callback;
|
||||
WORD callmask;
|
||||
} MOUSESYSTEM;
|
||||
} mouse_info;
|
||||
|
||||
/**********************************************************************
|
||||
* INT_Int33Handler
|
||||
|
@ -26,15 +27,12 @@ typedef struct {
|
|||
*/
|
||||
void WINAPI INT_Int33Handler( CONTEXT86 *context )
|
||||
{
|
||||
MOUSESYSTEM *sys = (MOUSESYSTEM *)DOSVM_GetSystemData(0x33);
|
||||
|
||||
switch (AX_reg(context)) {
|
||||
case 0x00:
|
||||
TRACE("Reset mouse driver and request status\n");
|
||||
AX_reg(context) = 0xFFFF; /* installed */
|
||||
BX_reg(context) = 3; /* # of buttons */
|
||||
sys = calloc(1,sizeof(MOUSESYSTEM));
|
||||
DOSVM_SetSystemData(0x33, sys);
|
||||
memset( &mouse_info, 0, sizeof(mouse_info) );
|
||||
break;
|
||||
case 0x01:
|
||||
FIXME("Show mouse cursor\n");
|
||||
|
@ -44,9 +42,9 @@ void WINAPI INT_Int33Handler( CONTEXT86 *context )
|
|||
break;
|
||||
case 0x03:
|
||||
TRACE("Return mouse position and button status\n");
|
||||
BX_reg(context) = sys->but;
|
||||
CX_reg(context) = sys->x;
|
||||
DX_reg(context) = sys->y;
|
||||
BX_reg(context) = mouse_info.but;
|
||||
CX_reg(context) = mouse_info.x;
|
||||
DX_reg(context) = mouse_info.y;
|
||||
break;
|
||||
case 0x04:
|
||||
FIXME("Position mouse cursor\n");
|
||||
|
@ -65,8 +63,8 @@ void WINAPI INT_Int33Handler( CONTEXT86 *context )
|
|||
break;
|
||||
case 0x0C:
|
||||
TRACE("Define mouse interrupt subroutine\n");
|
||||
sys->callmask = CX_reg(context);
|
||||
sys->callback = (FARPROC16)PTR_SEG_OFF_TO_SEGPTR(ES_reg(context), DX_reg(context));
|
||||
mouse_info.callmask = CX_reg(context);
|
||||
mouse_info.callback = (FARPROC16)PTR_SEG_OFF_TO_SEGPTR(ES_reg(context), DX_reg(context));
|
||||
break;
|
||||
case 0x10:
|
||||
FIXME("Define screen region for update\n");
|
||||
|
@ -81,7 +79,7 @@ typedef struct {
|
|||
WORD mask,but,x,y,mx,my;
|
||||
} MCALLDATA;
|
||||
|
||||
static void MouseRelay(LPDOSTASK lpDosTask,CONTEXT86 *context,void *mdata)
|
||||
static void MouseRelay(CONTEXT86 *context,void *mdata)
|
||||
{
|
||||
MCALLDATA *data = (MCALLDATA *)mdata;
|
||||
CONTEXT86 ctx = *context;
|
||||
|
@ -100,58 +98,56 @@ static void MouseRelay(LPDOSTASK lpDosTask,CONTEXT86 *context,void *mdata)
|
|||
|
||||
void WINAPI INT_Int33Message(UINT message,WPARAM wParam,LPARAM lParam)
|
||||
{
|
||||
MOUSESYSTEM *sys = (MOUSESYSTEM *)DOSVM_GetSystemData(0x33);
|
||||
WORD mask = 0;
|
||||
unsigned Height, Width, SX=1, SY=1;
|
||||
|
||||
if (!sys) return;
|
||||
if (!VGA_GetMode(&Height,&Width,NULL)) {
|
||||
/* may need to do some coordinate scaling */
|
||||
SX = 640/Width;
|
||||
if (!SX) SX=1;
|
||||
}
|
||||
sys->x = LOWORD(lParam) * SX;
|
||||
sys->y = HIWORD(lParam) * SY;
|
||||
mouse_info.x = LOWORD(lParam) * SX;
|
||||
mouse_info.y = HIWORD(lParam) * SY;
|
||||
switch (message) {
|
||||
case WM_MOUSEMOVE:
|
||||
mask |= 0x01;
|
||||
break;
|
||||
case WM_LBUTTONDOWN:
|
||||
case WM_LBUTTONDBLCLK:
|
||||
sys->but |= 0x01;
|
||||
mouse_info.but |= 0x01;
|
||||
mask |= 0x02;
|
||||
break;
|
||||
case WM_LBUTTONUP:
|
||||
sys->but &= ~0x01;
|
||||
mouse_info.but &= ~0x01;
|
||||
mask |= 0x04;
|
||||
break;
|
||||
case WM_RBUTTONDOWN:
|
||||
case WM_RBUTTONDBLCLK:
|
||||
sys->but |= 0x02;
|
||||
mouse_info.but |= 0x02;
|
||||
mask |= 0x08;
|
||||
break;
|
||||
case WM_RBUTTONUP:
|
||||
sys->but &= ~0x02;
|
||||
mouse_info.but &= ~0x02;
|
||||
mask |= 0x10;
|
||||
break;
|
||||
case WM_MBUTTONDOWN:
|
||||
case WM_MBUTTONDBLCLK:
|
||||
sys->but |= 0x04;
|
||||
mouse_info.but |= 0x04;
|
||||
mask |= 0x20;
|
||||
break;
|
||||
case WM_MBUTTONUP:
|
||||
sys->but &= ~0x04;
|
||||
mouse_info.but &= ~0x04;
|
||||
mask |= 0x40;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((mask & sys->callmask) && sys->callback) {
|
||||
if ((mask & mouse_info.callmask) && mouse_info.callback) {
|
||||
MCALLDATA *data = calloc(1,sizeof(MCALLDATA));
|
||||
data->proc = sys->callback;
|
||||
data->mask = mask & sys->callmask;
|
||||
data->but = sys->but;
|
||||
data->x = sys->x;
|
||||
data->y = sys->y;
|
||||
data->proc = mouse_info.callback;
|
||||
data->mask = mask & mouse_info.callmask;
|
||||
data->but = mouse_info.but;
|
||||
data->x = mouse_info.x;
|
||||
data->y = mouse_info.y;
|
||||
DOSVM_QueueEvent(-1, DOS_PRIORITY_MOUSE, MouseRelay, data);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue