4 * Copyright 1995 Alexandre Julliard
5 * Copyright 1996 Marcus Meissner
21 HANDLE16 DOSMEM_BiosSeg
; /* BIOS data segment at 0x40:0 */
27 WORD Com1Addr
; /* 00: COM1 I/O address */
28 WORD Com2Addr
; /* 02: COM2 I/O address */
29 WORD Com3Addr
; /* 04: COM3 I/O address */
30 WORD Com4Addr
; /* 06: COM4 I/O address */
31 WORD Lpt1Addr
; /* 08: LPT1 I/O address */
32 WORD Lpt2Addr
; /* 0a: LPT2 I/O address */
33 WORD Lpt3Addr
; /* 0c: LPT3 I/O address */
34 WORD Lpt4Addr
; /* 0e: LPT4 I/O address */
35 WORD InstalledHardware
; /* 10: Installed hardware flags */
36 BYTE POSTstatus
; /* 12: Power-On Self Test status */
37 WORD MemSize WINE_PACKED
; /* 13: Base memory size in Kb */
38 WORD unused1 WINE_PACKED
; /* 15: Manufacturing test scratch pad */
39 BYTE KbdFlags1
; /* 17: Keyboard flags 1 */
40 BYTE KbdFlags2
; /* 18: Keyboard flags 2 */
41 BYTE unused2
; /* 19: Keyboard driver workspace */
42 WORD NextKbdCharPtr
; /* 1a: Next character in kbd buffer */
43 WORD FirstKbdCharPtr
; /* 1c: First character in kbd buffer */
44 WORD KbdBuffer
[16]; /* 1e: Keyboard buffer */
45 BYTE DisketteStatus1
; /* 3e: Diskette recalibrate status */
46 BYTE DisketteStatus2
; /* 3f: Diskette motor status */
47 BYTE DisketteStatus3
; /* 40: Diskette motor timeout */
48 BYTE DisketteStatus4
; /* 41: Diskette last operation status */
49 BYTE DiskStatus
[7]; /* 42: Disk status/command bytes */
50 BYTE VideoMode
; /* 49: Video mode */
51 WORD VideoColumns
; /* 4a: Number of columns */
52 WORD VideoPageSize
; /* 4c: Video page size in bytes */
53 WORD VideoPageStartAddr
; /* 4e: Video page start address */
54 BYTE VideoCursorPos
[16]; /* 50: Cursor position for 8 pages */
55 WORD VideoCursorType
; /* 60: Video cursor type */
56 BYTE VideoCurPage
; /* 62: Video current page */
57 WORD VideoCtrlAddr WINE_PACKED
; /* 63: Video controller address */
58 BYTE VideoReg1
; /* 65: Video mode select register */
59 BYTE VideoReg2
; /* 66: Video CGA palette register */
60 DWORD ResetEntry WINE_PACKED
; /* 67: Warm reset entry point */
61 BYTE LastIRQ
; /* 6b: Last unexpected interrupt */
62 DWORD Ticks
; /* 6c: Ticks since midnight */
63 BYTE TicksOverflow
; /* 70: Timer overflow if past midnight */
64 BYTE CtrlBreakFlag
; /* 71: Ctrl-Break flag */
65 WORD ResetFlag
; /* 72: POST Reset flag */
66 BYTE DiskOpStatus
; /* 74: Last hard-disk operation status */
67 BYTE NbHardDisks
; /* 75: Number of hard disks */
68 BYTE DiskCtrlByte
; /* 76: Disk control byte */
69 BYTE DiskIOPort
; /* 77: Disk I/O port offset */
70 BYTE LptTimeout
[4]; /* 78: Timeouts for parallel ports */
71 BYTE ComTimeout
[4]; /* 7c: Timeouts for serial ports */
72 WORD KbdBufferStart
; /* 80: Keyboard buffer start */
73 WORD KbdBufferEnd
; /* 82: Keyboard buffer end */
78 static BIOSDATA
*pBiosData
= NULL
;
79 static char *DOSMEM_dosmem
;
81 DWORD DOSMEM_CollateTable
;
83 DWORD DOSMEM_ErrorCall
;
84 DWORD DOSMEM_ErrorBuffer
;
86 /* use 2 low bits of 'size' for the housekeeping */
88 #define DM_BLOCK_DEBUG 0xABE00000
89 #define DM_BLOCK_TERMINAL 0x00000001
90 #define DM_BLOCK_FREE 0x00000002
91 #define DM_BLOCK_MASK 0x001FFFFC
94 #define __DOSMEM_DEBUG__
106 #define NEXT_BLOCK(block) \
107 (dosmem_entry*)(((char*)(block)) + \
108 sizeof(dosmem_entry) + ((block)->size & DM_BLOCK_MASK))
110 #define VM_STUB(x) (0x90CF00CD|(x<<8)) /* INT x; IRET; NOP */
111 #define VM_STUB_SEGMENT 0xf000 /* BIOS segment */
113 /***********************************************************************
116 * Gets the DOS memory base.
118 char *DOSMEM_MemoryBase(HMODULE16 hModule
)
120 TDB
*pTask
= hModule
? NULL
: (TDB
*)GlobalLock16( GetCurrentTask() );
121 NE_MODULE
*pModule
= (hModule
|| pTask
) ? NE_GetPtr( hModule
? hModule
: pTask
->hModule
) : NULL
;
123 GlobalUnlock16( GetCurrentTask() );
124 if (pModule
&& pModule
->dos_image
)
125 return pModule
->dos_image
;
127 return DOSMEM_dosmem
;
130 /***********************************************************************
133 * Gets the DOS memory top.
135 static char *DOSMEM_MemoryTop(HMODULE16 hModule
)
137 return DOSMEM_MemoryBase(hModule
)+0x9FFFC; /* 640K */
140 /***********************************************************************
143 * Gets the DOS memory info block.
145 static dosmem_info
*DOSMEM_InfoBlock(HMODULE16 hModule
)
147 return (dosmem_info
*)(DOSMEM_MemoryBase(hModule
)+0x10000); /* 64K */
150 /***********************************************************************
153 * Gets the DOS memory root block.
155 static dosmem_entry
*DOSMEM_RootBlock(HMODULE16 hModule
)
157 /* first block has to be paragraph-aligned */
158 return (dosmem_entry
*)(((char*)DOSMEM_InfoBlock(hModule
)) +
159 ((((sizeof(dosmem_info
) + 0xf) & ~0xf) - sizeof(dosmem_entry
))));
162 /***********************************************************************
163 * DOSMEM_FillIsrTable
165 * Fill the interrupt table with fake BIOS calls to BIOSSEG (0xf000).
168 * Linux normally only traps INTs performed from or destined to BIOSSEG
169 * for us to handle, if the int_revectored table is empty. Filling the
170 * interrupt table with calls to INT stubs in BIOSSEG allows DOS programs
171 * to hook interrupts, as well as use their familiar retf tricks to call
172 * them, AND let Wine handle any unhooked interrupts transparently.
174 static void DOSMEM_FillIsrTable(HMODULE16 hModule
)
176 SEGPTR
*isr
= (SEGPTR
*)DOSMEM_MemoryBase(hModule
);
177 DWORD
*stub
= (DWORD
*)((char*)isr
+ (VM_STUB_SEGMENT
<< 4));
180 for (x
=0; x
<256; x
++) isr
[x
]=PTR_SEG_OFF_TO_SEGPTR(VM_STUB_SEGMENT
,x
*4);
181 for (x
=0; x
<256; x
++) stub
[x
]=VM_STUB(x
);
184 /***********************************************************************
185 * DOSMEM_FillBiosSegment
187 * Fill the BIOS data segment with dummy values.
189 static void DOSMEM_FillBiosSegment(void)
191 pBiosData
= (BIOSDATA
*)GlobalLock16( DOSMEM_BiosSeg
);
193 /* Clear all unused values */
194 memset( pBiosData
, 0, sizeof(*pBiosData
) );
196 /* FIXME: should check the number of configured drives and ports */
198 pBiosData
->Com1Addr
= 0x3e8;
199 pBiosData
->Com2Addr
= 0x2e8;
200 pBiosData
->Lpt1Addr
= 0x378;
201 pBiosData
->Lpt2Addr
= 0x278;
202 pBiosData
->InstalledHardware
= 0x8443;
203 pBiosData
->MemSize
= 640;
204 pBiosData
->NextKbdCharPtr
= 0x1e;
205 pBiosData
->FirstKbdCharPtr
= 0x1e;
206 pBiosData
->VideoMode
= 3;
207 pBiosData
->VideoColumns
= 80;
208 pBiosData
->VideoPageSize
= 80 * 25 * 2;
209 pBiosData
->VideoPageStartAddr
= 0xb800;
210 pBiosData
->VideoCtrlAddr
= 0x3d4;
211 pBiosData
->Ticks
= INT1A_GetTicksSinceMidnight();
212 pBiosData
->NbHardDisks
= 2;
213 pBiosData
->KbdBufferStart
= 0x1e;
214 pBiosData
->KbdBufferEnd
= 0x3e;
217 /***********************************************************************
218 * DOSMEM_InitCollateTable
220 * Initialises the collate table (character sorting, language dependent)
222 static void DOSMEM_InitCollateTable()
228 x
= GlobalDOSAlloc(258);
229 DOSMEM_CollateTable
= MAKELONG(0,(x
>>16));
230 tbl
= DOSMEM_MapRealToLinear(DOSMEM_CollateTable
);
233 for ( i
= 0; i
< 0x100; i
++) *tbl
++ = i
;
236 /***********************************************************************
237 * DOSMEM_InitErrorTable
239 * Initialises the error tables (DOS 5+)
241 static void DOSMEM_InitErrorTable()
246 /* We will use a snippet of real mode code that calls */
247 /* a WINE-only interrupt to handle moving the requested */
248 /* message into the buffer... */
250 /* FIXME - There is still something wrong... */
252 /* FIXME - Find hex values for opcodes...
254 (On call, AX contains message number
255 DI contains 'offset' (??)
256 Resturn, ES:DI points to counted string )
260 MOV AX, (arbitrary subfunction number)
261 INT (WINE-only interrupt)
268 const int buffer
= 80;
269 const int SIZE_TO_ALLOCATE
= code
+ buffer
;
271 /* FIXME - Complete rewrite of the table system to save */
272 /* precious DOS space. Now, we return the 0001:???? as */
273 /* DOS 4+ (??, it seems to be the case in MS 7.10) treats that */
274 /* as a special case and programs will use the alternate */
275 /* interface (a farcall returned with INT 24 (AX = 0x122e, DL = */
276 /* 0x08) which lets us have a smaller memory footprint anyway. */
278 x
= GlobalDOSAlloc(SIZE_TO_ALLOCATE
);
280 DOSMEM_ErrorCall
= MAKELONG(0,(x
>>16));
281 DOSMEM_ErrorBuffer
= DOSMEM_ErrorCall
+ code
;
283 call
= DOSMEM_MapRealToLinear(DOSMEM_ErrorCall
);
285 memset(call
, 0, SIZE_TO_ALLOCATE
);
287 /* Fixme - Copy assembly into buffer here */
290 /***********************************************************************
293 * Initialises the DOS memory structures.
295 static void DOSMEM_InitMemory(HMODULE16 hModule
)
297 /* Low 64Kb are reserved for DOS/BIOS so the useable area starts at
298 * 1000:0000 and ends at 9FFF:FFEF. */
300 dosmem_info
* info_block
= DOSMEM_InfoBlock(hModule
);
301 dosmem_entry
* root_block
= DOSMEM_RootBlock(hModule
);
304 root_block
->size
= DOSMEM_MemoryTop(hModule
) - (((char*)root_block
) + sizeof(dosmem_entry
));
306 info_block
->blocks
= 0;
307 info_block
->free
= root_block
->size
;
309 dm
= NEXT_BLOCK(root_block
);
310 dm
->size
= DM_BLOCK_TERMINAL
;
311 root_block
->size
|= DM_BLOCK_FREE
312 #ifdef __DOSMEM_DEBUG__
318 /***********************************************************************
321 * Create the dos memory segments, and store them into the KERNEL
324 BOOL32
DOSMEM_Init(HMODULE16 hModule
)
328 /* Allocate 1 MB dosmemory
329 * - it is mostly wasted but we use can some of it to
330 * store internal translation tables, etc...
332 DOSMEM_dosmem
= VirtualAlloc( NULL
, 0x100000, MEM_COMMIT
,
333 PAGE_EXECUTE_READWRITE
);
336 WARN(dosmem
, "Could not allocate DOS memory.\n" );
339 DOSMEM_BiosSeg
= GLOBAL_CreateBlock(GMEM_FIXED
,DOSMEM_dosmem
+0x400,0x100,
340 0, FALSE
, FALSE
, FALSE
, NULL
);
341 DOSMEM_FillIsrTable(0);
342 DOSMEM_FillBiosSegment();
343 DOSMEM_InitMemory(0);
344 DOSMEM_InitCollateTable();
345 DOSMEM_InitErrorTable();
350 DOSMEM_FillIsrTable(hModule
);
351 DOSMEM_InitMemory(hModule
);
353 /* bootstrap the new V86 task with a copy of the "system" memory */
354 memcpy(DOSMEM_MemoryBase(hModule
), DOSMEM_dosmem
, 0x100000);
361 /***********************************************************************
364 * Increment the BIOS tick counter. Called by timer signal handler.
366 void DOSMEM_Tick( WORD timer
)
368 if (pBiosData
) pBiosData
->Ticks
++;
371 /***********************************************************************
374 * Carve a chunk of the DOS memory block (without selector).
376 LPVOID
DOSMEM_GetBlock(HMODULE16 hModule
, UINT32 size
, UINT16
* pseg
)
380 dosmem_info
*info_block
= DOSMEM_InfoBlock(hModule
);
382 #ifdef __DOSMEM_DEBUG_
383 dosmem_entry
*prev
= NULL
;
386 if( size
> info_block
->free
) return NULL
;
387 dm
= DOSMEM_RootBlock(hModule
);
389 while (dm
&& dm
->size
!= DM_BLOCK_TERMINAL
)
391 #ifdef __DOSMEM_DEBUG__
392 if( (dm
->size
& DM_BLOCK_DEBUG
) != DM_BLOCK_DEBUG
)
394 WARN(dosmem
,"MCB overrun! [prev = 0x%08x]\n", 4 + (UINT32
)prev
);
399 if( dm
->size
& DM_BLOCK_FREE
)
401 dosmem_entry
*next
= NEXT_BLOCK(dm
);
403 while( next
->size
& DM_BLOCK_FREE
) /* collapse free blocks */
405 dm
->size
+= sizeof(dosmem_entry
) + (next
->size
& DM_BLOCK_MASK
);
406 next
->size
= (DM_BLOCK_FREE
| DM_BLOCK_TERMINAL
);
407 next
= NEXT_BLOCK(dm
);
410 blocksize
= dm
->size
& DM_BLOCK_MASK
;
411 if( blocksize
>= size
)
413 block
= ((char*)dm
) + sizeof(dosmem_entry
);
414 if( blocksize
- size
> 0x20 )
416 /* split dm so that the next one stays
417 * paragraph-aligned (and dm loses free bit) */
419 dm
->size
= (((size
+ 0xf + sizeof(dosmem_entry
)) & ~0xf) -
420 sizeof(dosmem_entry
));
421 next
= (dosmem_entry
*)(((char*)dm
) +
422 sizeof(dosmem_entry
) + dm
->size
);
423 next
->size
= (blocksize
- (dm
->size
+
424 sizeof(dosmem_entry
))) | DM_BLOCK_FREE
425 #ifdef __DOSMEM_DEBUG__
429 } else dm
->size
&= DM_BLOCK_MASK
;
431 info_block
->blocks
++;
432 info_block
->free
-= dm
->size
;
433 if( pseg
) *pseg
= (block
- DOSMEM_MemoryBase(hModule
)) >> 4;
434 #ifdef __DOSMEM_DEBUG__
435 dm
->size
|= DM_BLOCK_DEBUG
;
441 else dm
= NEXT_BLOCK(dm
);
443 return (LPVOID
)block
;
446 /***********************************************************************
449 BOOL32
DOSMEM_FreeBlock(HMODULE16 hModule
, void* ptr
)
451 dosmem_info
*info_block
= DOSMEM_InfoBlock(hModule
);
453 if( ptr
>= (void*)(((char*)DOSMEM_RootBlock(hModule
)) + sizeof(dosmem_entry
)) &&
454 ptr
< (void*)DOSMEM_MemoryTop(hModule
) && !((((char*)ptr
)
455 - DOSMEM_MemoryBase(hModule
)) & 0xf) )
457 dosmem_entry
*dm
= (dosmem_entry
*)(((char*)ptr
) - sizeof(dosmem_entry
));
459 if( !(dm
->size
& (DM_BLOCK_FREE
| DM_BLOCK_TERMINAL
))
460 #ifdef __DOSMEM_DEBUG__
461 && ((dm
->size
& DM_BLOCK_DEBUG
) == DM_BLOCK_DEBUG
)
465 info_block
->blocks
--;
466 info_block
->free
+= dm
->size
;
468 dm
->size
|= DM_BLOCK_FREE
;
475 /***********************************************************************
478 LPVOID
DOSMEM_ResizeBlock(HMODULE16 hModule
, void* ptr
, UINT32 size
, UINT16
* pseg
)
481 dosmem_info
*info_block
= DOSMEM_InfoBlock(hModule
);
483 if( ptr
>= (void*)(((char*)DOSMEM_RootBlock(hModule
)) + sizeof(dosmem_entry
)) &&
484 ptr
< (void*)DOSMEM_MemoryTop(hModule
) && !((((char*)ptr
)
485 - DOSMEM_MemoryBase(hModule
)) & 0xf) )
487 dosmem_entry
*dm
= (dosmem_entry
*)(((char*)ptr
) - sizeof(dosmem_entry
));
489 if( pseg
) *pseg
= ((char*)ptr
- DOSMEM_MemoryBase(hModule
)) >> 4;
491 if( !(dm
->size
& (DM_BLOCK_FREE
| DM_BLOCK_TERMINAL
))
494 dosmem_entry
*next
= NEXT_BLOCK(dm
);
495 UINT32 blocksize
, orgsize
= dm
->size
& DM_BLOCK_MASK
;
497 while( next
->size
& DM_BLOCK_FREE
) /* collapse free blocks */
499 dm
->size
+= sizeof(dosmem_entry
) + (next
->size
& DM_BLOCK_MASK
);
500 next
->size
= (DM_BLOCK_FREE
| DM_BLOCK_TERMINAL
);
501 next
= NEXT_BLOCK(dm
);
504 blocksize
= dm
->size
& DM_BLOCK_MASK
;
505 if (blocksize
>= size
)
507 block
= ((char*)dm
) + sizeof(dosmem_entry
);
508 if( blocksize
- size
> 0x20 )
510 /* split dm so that the next one stays
511 * paragraph-aligned (and next gains free bit) */
513 dm
->size
= (((size
+ 0xf + sizeof(dosmem_entry
)) & ~0xf) -
514 sizeof(dosmem_entry
));
515 next
= (dosmem_entry
*)(((char*)dm
) +
516 sizeof(dosmem_entry
) + dm
->size
);
517 next
->size
= (blocksize
- (dm
->size
+
518 sizeof(dosmem_entry
))) | DM_BLOCK_FREE
520 } else dm
->size
&= DM_BLOCK_MASK
;
522 info_block
->free
+= orgsize
- dm
->size
;
524 block
= DOSMEM_GetBlock(hModule
, size
, pseg
);
526 info_block
->blocks
--;
527 info_block
->free
+= dm
->size
;
529 dm
->size
|= DM_BLOCK_FREE
;
534 return (LPVOID
)block
;
538 /***********************************************************************
541 UINT32
DOSMEM_Available(HMODULE16 hModule
)
543 UINT32 blocksize
, available
= 0;
546 dm
= DOSMEM_RootBlock(hModule
);
548 while (dm
&& dm
->size
!= DM_BLOCK_TERMINAL
)
550 #ifdef __DOSMEM_DEBUG__
551 if( (dm
->size
& DM_BLOCK_DEBUG
) != DM_BLOCK_DEBUG
)
553 WARN(dosmem
,"MCB overrun! [prev = 0x%08x]\n", 4 + (UINT32
)prev
);
558 if( dm
->size
& DM_BLOCK_FREE
)
560 dosmem_entry
*next
= NEXT_BLOCK(dm
);
562 while( next
->size
& DM_BLOCK_FREE
) /* collapse free blocks */
564 dm
->size
+= sizeof(dosmem_entry
) + (next
->size
& DM_BLOCK_MASK
);
565 next
->size
= (DM_BLOCK_FREE
| DM_BLOCK_TERMINAL
);
566 next
= NEXT_BLOCK(dm
);
569 blocksize
= dm
->size
& DM_BLOCK_MASK
;
570 if ( blocksize
> available
) available
= blocksize
;
573 else dm
= NEXT_BLOCK(dm
);
579 /***********************************************************************
580 * DOSMEM_MapLinearToDos
582 * Linear address to the DOS address space.
584 UINT32
DOSMEM_MapLinearToDos(LPVOID ptr
)
586 if (((char*)ptr
>= DOSMEM_MemoryBase(0)) &&
587 ((char*)ptr
< DOSMEM_MemoryBase(0) + 0x100000))
588 return (UINT32
)ptr
- (UINT32
)DOSMEM_MemoryBase(0);
593 /***********************************************************************
594 * DOSMEM_MapDosToLinear
596 * DOS linear address to the linear address space.
598 LPVOID
DOSMEM_MapDosToLinear(UINT32 ptr
)
600 if (ptr
< 0x100000) return (LPVOID
)(ptr
+ (UINT32
)DOSMEM_MemoryBase(0));
605 /***********************************************************************
606 * DOSMEM_MapRealToLinear
608 * Real mode DOS address into a linear pointer
610 LPVOID
DOSMEM_MapRealToLinear(DWORD x
)
614 lin
=DOSMEM_MemoryBase(0)+(x
&0xffff)+(((x
&0xffff0000)>>16)*16);
615 TRACE(selector
,"(0x%08lx) returns 0x%p.\n",
620 /***********************************************************************
621 * DOSMEM_AllocSelector
623 * Allocates a protected mode selector for a realmode segment.
625 WORD
DOSMEM_AllocSelector(WORD realsel
)
627 HMODULE16 hModule
= GetModuleHandle16("KERNEL");
630 sel
=GLOBAL_CreateBlock(
631 GMEM_FIXED
,DOSMEM_dosmem
+realsel
*16,0x10000,
632 hModule
,FALSE
,FALSE
,FALSE
,NULL
634 TRACE(selector
,"(0x%04x) returns 0x%04x.\n",