4 * Copyright 1995 Alexandre Julliard
5 * Copyright 1996 Marcus Meissner
20 HANDLE16 DOSMEM_BiosSeg
; /* BIOS data segment at 0x40:0 */
26 WORD Com1Addr
; /* 00: COM1 I/O address */
27 WORD Com2Addr
; /* 02: COM2 I/O address */
28 WORD Com3Addr
; /* 04: COM3 I/O address */
29 WORD Com4Addr
; /* 06: COM4 I/O address */
30 WORD Lpt1Addr
; /* 08: LPT1 I/O address */
31 WORD Lpt2Addr
; /* 0a: LPT2 I/O address */
32 WORD Lpt3Addr
; /* 0c: LPT3 I/O address */
33 WORD Lpt4Addr
; /* 0e: LPT4 I/O address */
34 WORD InstalledHardware
; /* 10: Installed hardware flags */
35 BYTE POSTstatus
; /* 12: Power-On Self Test status */
36 WORD MemSize WINE_PACKED
; /* 13: Base memory size in Kb */
37 WORD unused1 WINE_PACKED
; /* 15: Manufacturing test scratch pad */
38 BYTE KbdFlags1
; /* 17: Keyboard flags 1 */
39 BYTE KbdFlags2
; /* 18: Keyboard flags 2 */
40 BYTE unused2
; /* 19: Keyboard driver workspace */
41 WORD NextKbdCharPtr
; /* 1a: Next character in kbd buffer */
42 WORD FirstKbdCharPtr
; /* 1c: First character in kbd buffer */
43 WORD KbdBuffer
[16]; /* 1e: Keyboard buffer */
44 BYTE DisketteStatus1
; /* 3e: Diskette recalibrate status */
45 BYTE DisketteStatus2
; /* 3f: Diskette motor status */
46 BYTE DisketteStatus3
; /* 40: Diskette motor timeout */
47 BYTE DisketteStatus4
; /* 41: Diskette last operation status */
48 BYTE DiskStatus
[7]; /* 42: Disk status/command bytes */
49 BYTE VideoMode
; /* 49: Video mode */
50 WORD VideoColumns
; /* 4a: Number of columns */
51 WORD VideoPageSize
; /* 4c: Video page size in bytes */
52 WORD VideoPageStartAddr
; /* 4e: Video page start address */
53 BYTE VideoCursorPos
[16]; /* 50: Cursor position for 8 pages */
54 WORD VideoCursorType
; /* 60: Video cursor type */
55 BYTE VideoCurPage
; /* 62: Video current page */
56 WORD VideoCtrlAddr WINE_PACKED
; /* 63: Video controller address */
57 BYTE VideoReg1
; /* 65: Video mode select register */
58 BYTE VideoReg2
; /* 66: Video CGA palette register */
59 DWORD ResetEntry WINE_PACKED
; /* 67: Warm reset entry point */
60 BYTE LastIRQ
; /* 6b: Last unexpected interrupt */
61 DWORD Ticks
; /* 6c: Ticks since midnight */
62 BYTE TicksOverflow
; /* 70: Timer overflow if past midnight */
63 BYTE CtrlBreakFlag
; /* 71: Ctrl-Break flag */
64 WORD ResetFlag
; /* 72: POST Reset flag */
65 BYTE DiskOpStatus
; /* 74: Last hard-disk operation status */
66 BYTE NbHardDisks
; /* 75: Number of hard disks */
67 BYTE DiskCtrlByte
; /* 76: Disk control byte */
68 BYTE DiskIOPort
; /* 77: Disk I/O port offset */
69 BYTE LptTimeout
[4]; /* 78: Timeouts for parallel ports */
70 BYTE ComTimeout
[4]; /* 7c: Timeouts for serial ports */
71 WORD KbdBufferStart
; /* 80: Keyboard buffer start */
72 WORD KbdBufferEnd
; /* 82: Keyboard buffer end */
77 static BIOSDATA
*pBiosData
= NULL
;
78 static char *DOSMEM_dosmem
;
80 DWORD DOSMEM_CollateTable
;
82 DWORD DOSMEM_ErrorCall
;
83 DWORD DOSMEM_ErrorBuffer
;
85 /* use 2 low bits of 'size' for the housekeeping */
87 #define DM_BLOCK_DEBUG 0xABE00000
88 #define DM_BLOCK_TERMINAL 0x00000001
89 #define DM_BLOCK_FREE 0x00000002
90 #define DM_BLOCK_MASK 0x001FFFFC
93 #define __DOSMEM_DEBUG__
105 #define NEXT_BLOCK(block) \
106 (dosmem_entry*)(((char*)(block)) + \
107 sizeof(dosmem_entry) + ((block)->size & DM_BLOCK_MASK))
109 #define VM_STUB(x) (0x90CF00CD|(x<<8)) /* INT x; IRET; NOP */
110 #define VM_STUB_SEGMENT 0xf000 /* BIOS segment */
112 /***********************************************************************
115 * Gets the DOS memory base.
117 char *DOSMEM_MemoryBase(HMODULE16 hModule
)
119 TDB
*pTask
= hModule
? NULL
: (TDB
*)GlobalLock16( GetCurrentTask() );
120 NE_MODULE
*pModule
= (hModule
|| pTask
) ? NE_GetPtr( hModule
? hModule
: pTask
->hModule
) : NULL
;
122 GlobalUnlock16( GetCurrentTask() );
123 if (pModule
&& pModule
->dos_image
)
124 return pModule
->dos_image
;
126 return DOSMEM_dosmem
;
129 /***********************************************************************
132 * Gets the DOS memory top.
134 static char *DOSMEM_MemoryTop(HMODULE16 hModule
)
136 return DOSMEM_MemoryBase(hModule
)+0x9FFFC; /* 640K */
139 /***********************************************************************
142 * Gets the DOS memory info block.
144 static dosmem_info
*DOSMEM_InfoBlock(HMODULE16 hModule
)
146 return (dosmem_info
*)(DOSMEM_MemoryBase(hModule
)+0x10000); /* 64K */
149 /***********************************************************************
152 * Gets the DOS memory root block.
154 static dosmem_entry
*DOSMEM_RootBlock(HMODULE16 hModule
)
156 /* first block has to be paragraph-aligned */
157 return (dosmem_entry
*)(((char*)DOSMEM_InfoBlock(hModule
)) +
158 ((((sizeof(dosmem_info
) + 0xf) & ~0xf) - sizeof(dosmem_entry
))));
161 /***********************************************************************
162 * DOSMEM_FillIsrTable
164 * Fill the interrupt table with fake BIOS calls to BIOSSEG (0xf000).
167 * Linux normally only traps INTs performed from or destined to BIOSSEG
168 * for us to handle, if the int_revectored table is empty. Filling the
169 * interrupt table with calls to INT stubs in BIOSSEG allows DOS programs
170 * to hook interrupts, as well as use their familiar retf tricks to call
171 * them, AND let Wine handle any unhooked interrupts transparently.
173 static void DOSMEM_FillIsrTable(HMODULE16 hModule
)
175 SEGPTR
*isr
= (SEGPTR
*)DOSMEM_MemoryBase(hModule
);
176 DWORD
*stub
= (DWORD
*)((char*)isr
+ (VM_STUB_SEGMENT
<< 4));
179 for (x
=0; x
<256; x
++) isr
[x
]=PTR_SEG_OFF_TO_SEGPTR(VM_STUB_SEGMENT
,x
*4);
180 for (x
=0; x
<256; x
++) stub
[x
]=VM_STUB(x
);
183 /***********************************************************************
184 * DOSMEM_FillBiosSegment
186 * Fill the BIOS data segment with dummy values.
188 static void DOSMEM_FillBiosSegment(void)
190 pBiosData
= (BIOSDATA
*)GlobalLock16( DOSMEM_BiosSeg
);
192 /* Clear all unused values */
193 memset( pBiosData
, 0, sizeof(*pBiosData
) );
195 /* FIXME: should check the number of configured drives and ports */
197 pBiosData
->Com1Addr
= 0x3e8;
198 pBiosData
->Com2Addr
= 0x2e8;
199 pBiosData
->Lpt1Addr
= 0x378;
200 pBiosData
->Lpt2Addr
= 0x278;
201 pBiosData
->InstalledHardware
= 0x8443;
202 pBiosData
->MemSize
= 640;
203 pBiosData
->NextKbdCharPtr
= 0x1e;
204 pBiosData
->FirstKbdCharPtr
= 0x1e;
205 pBiosData
->VideoMode
= 0;
206 pBiosData
->VideoColumns
= 80;
207 pBiosData
->VideoPageSize
= 80 * 25 * 2;
208 pBiosData
->VideoPageStartAddr
= 0xb800;
209 pBiosData
->VideoCtrlAddr
= 0x3d4;
210 pBiosData
->Ticks
= INT1A_GetTicksSinceMidnight();
211 pBiosData
->NbHardDisks
= 2;
212 pBiosData
->KbdBufferStart
= 0x1e;
213 pBiosData
->KbdBufferEnd
= 0x3e;
216 /***********************************************************************
217 * DOSMEM_InitCollateTable
219 * Initialises the collate table (character sorting, language dependent)
221 static void DOSMEM_InitCollateTable()
227 x
= GlobalDOSAlloc(258);
228 DOSMEM_CollateTable
= MAKELONG(0,(x
>>16));
229 tbl
= DOSMEM_MapRealToLinear(DOSMEM_CollateTable
);
232 for ( i
= 0; i
< 0x100; i
++) *tbl
++ = i
;
235 /***********************************************************************
236 * DOSMEM_InitErrorTable
238 * Initialises the error tables (DOS 5+)
240 static void DOSMEM_InitErrorTable()
245 /* We will use a snippet of real mode code that calls */
246 /* a WINE-only interrupt to handle moving the requested */
247 /* message into the buffer... */
249 /* FIXME - There is still something wrong... */
251 /* FIXME - Find hex values for opcodes...
253 (On call, AX contains message number
254 DI contains 'offset' (??)
255 Resturn, ES:DI points to counted string )
259 MOV AX, (arbitrary subfunction number)
260 INT (WINE-only interrupt)
267 const int buffer
= 80;
268 const int SIZE_TO_ALLOCATE
= code
+ buffer
;
270 /* FIXME - Complete rewrite of the table system to save */
271 /* precious DOS space. Now, we return the 0001:???? as */
272 /* DOS 4+ (??, it seems to be the case in MS 7.10) treats that */
273 /* as a special case and programs will use the alternate */
274 /* interface (a farcall returned with INT 24 (AX = 0x122e, DL = */
275 /* 0x08) which lets us have a smaller memory footprint anyway. */
277 x
= GlobalDOSAlloc(SIZE_TO_ALLOCATE
);
279 DOSMEM_ErrorCall
= MAKELONG(0,(x
>>16));
280 DOSMEM_ErrorBuffer
= DOSMEM_ErrorCall
+ code
;
282 call
= DOSMEM_MapRealToLinear(DOSMEM_ErrorCall
);
284 memset(call
, 0, SIZE_TO_ALLOCATE
);
286 /* Fixme - Copy assembly into buffer here */
289 /***********************************************************************
292 * Initialises the DOS memory structures.
294 static void DOSMEM_InitMemory(HMODULE16 hModule
)
296 /* Low 64Kb are reserved for DOS/BIOS so the useable area starts at
297 * 1000:0000 and ends at 9FFF:FFEF. */
299 dosmem_info
* info_block
= DOSMEM_InfoBlock(hModule
);
300 dosmem_entry
* root_block
= DOSMEM_RootBlock(hModule
);
303 root_block
->size
= DOSMEM_MemoryTop(hModule
) - (((char*)root_block
) + sizeof(dosmem_entry
));
305 info_block
->blocks
= 0;
306 info_block
->free
= root_block
->size
;
308 dm
= NEXT_BLOCK(root_block
);
309 dm
->size
= DM_BLOCK_TERMINAL
;
310 root_block
->size
|= DM_BLOCK_FREE
311 #ifdef __DOSMEM_DEBUG__
317 /***********************************************************************
320 * Create the dos memory segments, and store them into the KERNEL
323 BOOL32
DOSMEM_Init(HMODULE16 hModule
)
327 /* Allocate 1 MB dosmemory
328 * - it is mostly wasted but we use can some of it to
329 * store internal translation tables, etc...
331 DOSMEM_dosmem
= VirtualAlloc( NULL
, 0x100000, MEM_COMMIT
,
332 PAGE_EXECUTE_READWRITE
);
335 WARN(dosmem
, "Could not allocate DOS memory.\n" );
338 DOSMEM_BiosSeg
= GLOBAL_CreateBlock(GMEM_FIXED
,DOSMEM_dosmem
+0x400,0x100,
339 0, FALSE
, FALSE
, FALSE
, NULL
);
340 DOSMEM_FillIsrTable(0);
341 DOSMEM_FillBiosSegment();
342 DOSMEM_InitMemory(0);
343 DOSMEM_InitCollateTable();
344 DOSMEM_InitErrorTable();
349 DOSMEM_FillIsrTable(hModule
);
350 DOSMEM_InitMemory(hModule
);
352 /* bootstrap the new V86 task with a copy of the "system" memory */
353 memcpy(DOSMEM_MemoryBase(hModule
), DOSMEM_dosmem
, 0x100000);
360 /***********************************************************************
363 * Increment the BIOS tick counter. Called by timer signal handler.
365 void DOSMEM_Tick(void)
367 if (pBiosData
) pBiosData
->Ticks
++;
370 /***********************************************************************
373 * Carve a chunk of the DOS memory block (without selector).
375 LPVOID
DOSMEM_GetBlock(HMODULE16 hModule
, UINT32 size
, UINT16
* pseg
)
379 dosmem_info
*info_block
= DOSMEM_InfoBlock(hModule
);
381 #ifdef __DOSMEM_DEBUG_
382 dosmem_entry
*prev
= NULL
;
385 if( size
> info_block
->free
) return NULL
;
386 dm
= DOSMEM_RootBlock(hModule
);
388 while (dm
&& dm
->size
!= DM_BLOCK_TERMINAL
)
390 #ifdef __DOSMEM_DEBUG__
391 if( (dm
->size
& DM_BLOCK_DEBUG
) != DM_BLOCK_DEBUG
)
393 WARN(dosmem
,"MCB overrun! [prev = 0x%08x]\n", 4 + (UINT32
)prev
);
398 if( dm
->size
& DM_BLOCK_FREE
)
400 dosmem_entry
*next
= NEXT_BLOCK(dm
);
402 while( next
->size
& DM_BLOCK_FREE
) /* collapse free blocks */
404 dm
->size
+= sizeof(dosmem_entry
) + (next
->size
& DM_BLOCK_MASK
);
405 next
->size
= (DM_BLOCK_FREE
| DM_BLOCK_TERMINAL
);
406 next
= NEXT_BLOCK(dm
);
409 blocksize
= dm
->size
& DM_BLOCK_MASK
;
410 if( blocksize
>= size
)
412 block
= ((char*)dm
) + sizeof(dosmem_entry
);
413 if( blocksize
- size
> 0x20 )
415 /* split dm so that the next one stays
416 * paragraph-aligned (and dm loses free bit) */
418 dm
->size
= (((size
+ 0xf + sizeof(dosmem_entry
)) & ~0xf) -
419 sizeof(dosmem_entry
));
420 next
= (dosmem_entry
*)(((char*)dm
) +
421 sizeof(dosmem_entry
) + dm
->size
);
422 next
->size
= (blocksize
- (dm
->size
+
423 sizeof(dosmem_entry
))) | DM_BLOCK_FREE
424 #ifdef __DOSMEM_DEBUG__
428 } else dm
->size
&= DM_BLOCK_MASK
;
430 info_block
->blocks
++;
431 info_block
->free
-= dm
->size
;
432 if( pseg
) *pseg
= (block
- DOSMEM_MemoryBase(hModule
)) >> 4;
433 #ifdef __DOSMEM_DEBUG__
434 dm
->size
|= DM_BLOCK_DEBUG
;
440 else dm
= NEXT_BLOCK(dm
);
442 return (LPVOID
)block
;
445 /***********************************************************************
448 BOOL32
DOSMEM_FreeBlock(HMODULE16 hModule
, void* ptr
)
450 dosmem_info
*info_block
= DOSMEM_InfoBlock(hModule
);
452 if( ptr
>= (void*)(((char*)DOSMEM_RootBlock(hModule
)) + sizeof(dosmem_entry
)) &&
453 ptr
< (void*)DOSMEM_MemoryTop(hModule
) && !((((char*)ptr
)
454 - DOSMEM_MemoryBase(hModule
)) & 0xf) )
456 dosmem_entry
*dm
= (dosmem_entry
*)(((char*)ptr
) - sizeof(dosmem_entry
));
458 if( !(dm
->size
& (DM_BLOCK_FREE
| DM_BLOCK_TERMINAL
))
459 #ifdef __DOSMEM_DEBUG__
460 && ((dm
->size
& DM_BLOCK_DEBUG
) == DM_BLOCK_DEBUG
)
464 info_block
->blocks
--;
465 info_block
->free
+= dm
->size
;
467 dm
->size
|= DM_BLOCK_FREE
;
474 /***********************************************************************
477 LPVOID
DOSMEM_ResizeBlock(HMODULE16 hModule
, void* ptr
, UINT32 size
, UINT16
* pseg
)
480 dosmem_info
*info_block
= DOSMEM_InfoBlock(hModule
);
482 if( ptr
>= (void*)(((char*)DOSMEM_RootBlock(hModule
)) + sizeof(dosmem_entry
)) &&
483 ptr
< (void*)DOSMEM_MemoryTop(hModule
) && !((((char*)ptr
)
484 - DOSMEM_MemoryBase(hModule
)) & 0xf) )
486 dosmem_entry
*dm
= (dosmem_entry
*)(((char*)ptr
) - sizeof(dosmem_entry
));
488 if( pseg
) *pseg
= ((char*)ptr
- DOSMEM_MemoryBase(hModule
)) >> 4;
490 if( !(dm
->size
& (DM_BLOCK_FREE
| DM_BLOCK_TERMINAL
))
493 dosmem_entry
*next
= NEXT_BLOCK(dm
);
494 UINT32 blocksize
, orgsize
= dm
->size
& DM_BLOCK_MASK
;
496 while( next
->size
& DM_BLOCK_FREE
) /* collapse free blocks */
498 dm
->size
+= sizeof(dosmem_entry
) + (next
->size
& DM_BLOCK_MASK
);
499 next
->size
= (DM_BLOCK_FREE
| DM_BLOCK_TERMINAL
);
500 next
= NEXT_BLOCK(dm
);
503 blocksize
= dm
->size
& DM_BLOCK_MASK
;
504 if (blocksize
>= size
)
506 block
= ((char*)dm
) + sizeof(dosmem_entry
);
507 if( blocksize
- size
> 0x20 )
509 /* split dm so that the next one stays
510 * paragraph-aligned (and next gains free bit) */
512 dm
->size
= (((size
+ 0xf + sizeof(dosmem_entry
)) & ~0xf) -
513 sizeof(dosmem_entry
));
514 next
= (dosmem_entry
*)(((char*)dm
) +
515 sizeof(dosmem_entry
) + dm
->size
);
516 next
->size
= (blocksize
- (dm
->size
+
517 sizeof(dosmem_entry
))) | DM_BLOCK_FREE
519 } else dm
->size
&= DM_BLOCK_MASK
;
521 info_block
->free
+= orgsize
- dm
->size
;
523 block
= DOSMEM_GetBlock(hModule
, size
, pseg
);
525 info_block
->blocks
--;
526 info_block
->free
+= dm
->size
;
528 dm
->size
|= DM_BLOCK_FREE
;
533 return (LPVOID
)block
;
537 /***********************************************************************
540 UINT32
DOSMEM_Available(HMODULE16 hModule
)
542 UINT32 blocksize
, available
= 0;
545 dm
= DOSMEM_RootBlock(hModule
);
547 while (dm
&& dm
->size
!= DM_BLOCK_TERMINAL
)
549 #ifdef __DOSMEM_DEBUG__
550 if( (dm
->size
& DM_BLOCK_DEBUG
) != DM_BLOCK_DEBUG
)
552 WARN(dosmem
,"MCB overrun! [prev = 0x%08x]\n", 4 + (UINT32
)prev
);
557 if( dm
->size
& DM_BLOCK_FREE
)
559 dosmem_entry
*next
= NEXT_BLOCK(dm
);
561 while( next
->size
& DM_BLOCK_FREE
) /* collapse free blocks */
563 dm
->size
+= sizeof(dosmem_entry
) + (next
->size
& DM_BLOCK_MASK
);
564 next
->size
= (DM_BLOCK_FREE
| DM_BLOCK_TERMINAL
);
565 next
= NEXT_BLOCK(dm
);
568 blocksize
= dm
->size
& DM_BLOCK_MASK
;
569 if ( blocksize
> available
) available
= blocksize
;
572 else dm
= NEXT_BLOCK(dm
);
578 /***********************************************************************
579 * DOSMEM_MapLinearToDos
581 * Linear address to the DOS address space.
583 UINT32
DOSMEM_MapLinearToDos(LPVOID ptr
)
585 if (((char*)ptr
>= DOSMEM_MemoryBase(0)) &&
586 ((char*)ptr
< DOSMEM_MemoryBase(0) + 0x100000))
587 return (UINT32
)ptr
- (UINT32
)DOSMEM_MemoryBase(0);
592 /***********************************************************************
593 * DOSMEM_MapDosToLinear
595 * DOS linear address to the linear address space.
597 LPVOID
DOSMEM_MapDosToLinear(UINT32 ptr
)
599 if (ptr
< 0x100000) return (LPVOID
)(ptr
+ (UINT32
)DOSMEM_MemoryBase(0));
604 /***********************************************************************
605 * DOSMEM_MapRealToLinear
607 * Real mode DOS address into a linear pointer
609 LPVOID
DOSMEM_MapRealToLinear(DWORD x
)
613 lin
=DOSMEM_MemoryBase(0)+(x
&0xffff)+(((x
&0xffff0000)>>16)*16);
614 TRACE(selector
,"(0x%08lx) returns 0x%p.\n",
619 /***********************************************************************
620 * DOSMEM_AllocSelector
622 * Allocates a protected mode selector for a realmode segment.
624 WORD
DOSMEM_AllocSelector(WORD realsel
)
626 HMODULE16 hModule
= GetModuleHandle16("KERNEL");
629 sel
=GLOBAL_CreateBlock(
630 GMEM_FIXED
,DOSMEM_dosmem
+realsel
*16,0x10000,
631 hModule
,FALSE
,FALSE
,FALSE
,NULL
633 TRACE(selector
,"(0x%04x) returns 0x%04x.\n",