1 /******************************************************************************
2 * Copyright (c) 2004, 2008 IBM Corporation
3 * Copyright (c) 2009 Pattrick Hueper <phueper@hueper.net>
5 * This program and the accompanying materials
6 * are made available under the terms of the BSD License
7 * which accompanies this distribution, and is available at
8 * http://www.opensource.org/licenses/bsd-license.php
11 * IBM Corporation - initial implementation
12 *****************************************************************************/
17 #include "x86emu/x86emu.h"
20 #include "compat/time.h"
22 // define a check for access to certain (virtual) memory regions (interrupt handlers, BIOS Data Area, ...)
23 #if CONFIG_X86EMU_DEBUG
24 static u8 in_check = 0; // to avoid recursion...
28 //TODO: these macros have grown so large, that they should be changed to an inline function,
29 //just for the sake of readability...
31 #define DEBUG_CHECK_VMEM_READ(_addr, _rval) \
32 if ((debug_flags & DEBUG_CHECK_VMEM_ACCESS) && (in_check == 0)) { \
34 /* determine ebda_segment and size \
35 * since we are using my_rdx calls, make sure, this is after setting in_check! */ \
36 /* offset 03 in BDA is EBDA segment */ \
37 ebda_segment = my_rdw(0x40e); \
38 /* first value in ebda is size in KB */ \
39 ebda_size = my_rdb(ebda_segment << 4) * 1024; \
40 /* check Interrupt Vector Access (0000:0000h - 0000:0400h) */ \
41 if (_addr < 0x400) { \
42 DEBUG_PRINTF_CS_IP("%s: read from Interrupt Vector %x --> %x\n", \
43 __func__, _addr / 4, _rval); \
45 /* access to BIOS Data Area (0000:0400h - 0000:0500h)*/ \
46 else if ((_addr >= 0x400) && (addr < 0x500)) { \
47 DEBUG_PRINTF_CS_IP("%s: read from BIOS Data Area: addr: %x --> %x\n", \
48 __func__, _addr, _rval); \
49 /* dump registers */ \
50 /* x86emu_dump_xregs(); */ \
52 /* access to first 64k of memory... */ \
53 else if (_addr < 0x10000) { \
54 DEBUG_PRINTF_CS_IP("%s: read from segment 0000h: addr: %x --> %x\n", \
55 __func__, _addr, _rval); \
56 /* dump registers */ \
57 /* x86emu_dump_xregs(); */ \
59 /* read from PMM_CONV_SEGMENT */ \
60 else if ((_addr <= ((PMM_CONV_SEGMENT << 4) | 0xffff)) && (_addr >= (PMM_CONV_SEGMENT << 4))) { \
61 DEBUG_PRINTF_CS_IP("%s: read from PMM Segment %04xh: addr: %x --> %x\n", \
62 __func__, PMM_CONV_SEGMENT, _addr, _rval); \
64 /* dump registers */ \
65 /* x86emu_dump_xregs(); */ \
67 /* read from PNP_DATA_SEGMENT */ \
68 else if ((_addr <= ((PNP_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (PNP_DATA_SEGMENT << 4))) { \
69 DEBUG_PRINTF_CS_IP("%s: read from PnP Data Segment %04xh: addr: %x --> %x\n", \
70 __func__, PNP_DATA_SEGMENT, _addr, _rval); \
72 /* dump registers */ \
73 /* x86emu_dump_xregs(); */ \
75 /* read from EBDA Segment */ \
76 else if ((_addr <= ((ebda_segment << 4) | (ebda_size - 1))) && (_addr >= (ebda_segment << 4))) { \
77 DEBUG_PRINTF_CS_IP("%s: read from Extended BIOS Data Area %04xh, size: %04x: addr: %x --> %x\n", \
78 __func__, ebda_segment, ebda_size, _addr, _rval); \
80 /* read from BIOS_DATA_SEGMENT */ \
81 else if ((_addr <= ((BIOS_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (BIOS_DATA_SEGMENT << 4))) { \
82 DEBUG_PRINTF_CS_IP("%s: read from BIOS Data Segment %04xh: addr: %x --> %x\n", \
83 __func__, BIOS_DATA_SEGMENT, _addr, _rval); \
84 /* for PMM debugging */ \
85 /*if (_addr == BIOS_DATA_SEGMENT << 4) { \
87 M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F; \
89 /* dump registers */ \
90 /* x86emu_dump_xregs(); */ \
94 #define DEBUG_CHECK_VMEM_WRITE(_addr, _val) \
95 if ((debug_flags & DEBUG_CHECK_VMEM_ACCESS) && (in_check == 0)) { \
97 /* determine ebda_segment and size \
98 * since we are using my_rdx calls, make sure, this is after setting in_check! */ \
99 /* offset 03 in BDA is EBDA segment */ \
100 ebda_segment = my_rdw(0x40e); \
101 /* first value in ebda is size in KB */ \
102 ebda_size = my_rdb(ebda_segment << 4) * 1024; \
103 /* check Interrupt Vector Access (0000:0000h - 0000:0400h) */ \
104 if (_addr < 0x400) { \
105 DEBUG_PRINTF_CS_IP("%s: write to Interrupt Vector %x <-- %x\n", \
106 __func__, _addr / 4, _val); \
108 /* access to BIOS Data Area (0000:0400h - 0000:0500h)*/ \
109 else if ((_addr >= 0x400) && (addr < 0x500)) { \
110 DEBUG_PRINTF_CS_IP("%s: write to BIOS Data Area: addr: %x <-- %x\n", \
111 __func__, _addr, _val); \
112 /* dump registers */ \
113 /* x86emu_dump_xregs(); */ \
115 /* access to first 64k of memory...*/ \
116 else if (_addr < 0x10000) { \
117 DEBUG_PRINTF_CS_IP("%s: write to segment 0000h: addr: %x <-- %x\n", \
118 __func__, _addr, _val); \
119 /* dump registers */ \
120 /* x86emu_dump_xregs(); */ \
122 /* write to PMM_CONV_SEGMENT... */ \
123 else if ((_addr <= ((PMM_CONV_SEGMENT << 4) | 0xffff)) && (_addr >= (PMM_CONV_SEGMENT << 4))) { \
124 DEBUG_PRINTF_CS_IP("%s: write to PMM Segment %04xh: addr: %x <-- %x\n", \
125 __func__, PMM_CONV_SEGMENT, _addr, _val); \
126 /* dump registers */ \
127 /* x86emu_dump_xregs(); */ \
129 /* write to PNP_DATA_SEGMENT... */ \
130 else if ((_addr <= ((PNP_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (PNP_DATA_SEGMENT << 4))) { \
131 DEBUG_PRINTF_CS_IP("%s: write to PnP Data Segment %04xh: addr: %x <-- %x\n", \
132 __func__, PNP_DATA_SEGMENT, _addr, _val); \
133 /* dump registers */ \
134 /* x86emu_dump_xregs(); */ \
136 /* write to EBDA Segment... */ \
137 else if ((_addr <= ((ebda_segment << 4) | (ebda_size - 1))) && (_addr >= (ebda_segment << 4))) { \
138 DEBUG_PRINTF_CS_IP("%s: write to Extended BIOS Data Area %04xh, size: %04x: addr: %x <-- %x\n", \
139 __func__, ebda_segment, ebda_size, _addr, _val); \
141 /* write to BIOS_DATA_SEGMENT... */ \
142 else if ((_addr <= ((BIOS_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (BIOS_DATA_SEGMENT << 4))) { \
143 DEBUG_PRINTF_CS_IP("%s: write to BIOS Data Segment %04xh: addr: %x <-- %x\n", \
144 __func__, BIOS_DATA_SEGMENT, _addr, _val); \
145 /* dump registers */ \
146 /* x86emu_dump_xregs(); */ \
148 /* write to current CS segment... */ \
149 else if ((_addr < ((M.x86.R_CS << 4) | 0xffff)) && (_addr > (M.x86.R_CS << 4))) { \
150 DEBUG_PRINTF_CS_IP("%s: write to CS segment %04xh: addr: %x <-- %x\n", \
151 __func__, M.x86.R_CS, _addr, _val); \
152 /* dump registers */ \
153 /* x86emu_dump_xregs(); */ \
158 #define DEBUG_CHECK_VMEM_READ(_addr, _rval)
159 #define DEBUG_CHECK_VMEM_WRITE(_addr, _val)
162 void update_time(u32);
164 #if !defined(CONFIG_YABEL_DIRECTHW) || (!CONFIG_YABEL_DIRECTHW)
165 // read byte from memory
169 unsigned long translated_addr = addr;
170 u8 translated = biosemu_dev_translate_address(&translated_addr);
172 if (translated != 0) {
173 //translation successfull, access VGA Memory (BAR or Legacy...)
174 DEBUG_PRINTF_MEM("%s(%08x): access to VGA Memory\n",
176 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr);
178 rval = *((u8 *) translated_addr);
180 DEBUG_PRINTF_MEM("%s(%08x) VGA --> %02x\n", __func__, addr,
183 } else if (addr > M.mem_size) {
184 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
186 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
189 /* read from virtual memory */
190 rval = *((u8 *) (M.mem_base + addr));
191 DEBUG_CHECK_VMEM_READ(addr, rval);
197 //read word from memory
201 unsigned long translated_addr = addr;
202 u8 translated = biosemu_dev_translate_address(&translated_addr);
204 if (translated != 0) {
205 //translation successfull, access VGA Memory (BAR or Legacy...)
206 DEBUG_PRINTF_MEM("%s(%08x): access to VGA Memory\n",
208 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr);
209 // check for legacy memory, because of the remapping to BARs, the reads must
211 if ((addr >= 0xa0000) && (addr < 0xc0000)) {
212 //read bytes a using my_rdb, because of the remapping to BARs
213 //words may not be contiguous in memory, so we need to translate
215 rval = ((u8) my_rdb(addr)) |
216 (((u8) my_rdb(addr + 1)) << 8);
218 if ((translated_addr & (u64) 0x1) == 0) {
219 // 16 bit aligned access...
221 rval = in16le((void *) translated_addr);
224 // unaligned access, read single bytes
226 rval = (*((u8 *) translated_addr)) |
227 (*((u8 *) translated_addr + 1) << 8);
231 DEBUG_PRINTF_MEM("%s(%08x) VGA --> %04x\n", __func__, addr,
234 } else if (addr > M.mem_size) {
235 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
237 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
240 /* read from virtual memory */
241 rval = in16le((void *) (M.mem_base + addr));
242 DEBUG_CHECK_VMEM_READ(addr, rval);
248 //read long from memory
252 unsigned long translated_addr = addr;
253 u8 translated = biosemu_dev_translate_address(&translated_addr);
255 if (translated != 0) {
256 //translation successfull, access VGA Memory (BAR or Legacy...)
257 DEBUG_PRINTF_MEM("%s(%x): access to VGA Memory\n",
259 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr);
260 // check for legacy memory, because of the remapping to BARs, the reads must
262 if ((addr >= 0xa0000) && (addr < 0xc0000)) {
263 //read bytes a using my_rdb, because of the remapping to BARs
264 //dwords may not be contiguous in memory, so we need to translate
266 rval = ((u8) my_rdb(addr)) |
267 (((u8) my_rdb(addr + 1)) << 8) |
268 (((u8) my_rdb(addr + 2)) << 16) |
269 (((u8) my_rdb(addr + 3)) << 24);
271 if ((translated_addr & (u64) 0x3) == 0) {
272 // 32 bit aligned access...
274 rval = in32le((void *) translated_addr);
277 // unaligned access, read single bytes
279 rval = (*((u8 *) translated_addr)) |
280 (*((u8 *) translated_addr + 1) << 8) |
281 (*((u8 *) translated_addr + 2) << 16) |
282 (*((u8 *) translated_addr + 3) << 24);
286 DEBUG_PRINTF_MEM("%s(%08x) VGA --> %08x\n", __func__, addr,
290 } else if (addr > M.mem_size) {
291 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
293 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
296 /* read from virtual memory */
297 rval = in32le((void *) (M.mem_base + addr));
300 //BDA Time Data, update it, before reading
302 rval = in32le((void *) (M.mem_base + addr));
305 DEBUG_CHECK_VMEM_READ(addr, rval);
311 //write byte to memory
313 my_wrb(u32 addr, u8 val)
315 unsigned long translated_addr = addr;
316 u8 translated = biosemu_dev_translate_address(&translated_addr);
317 if (translated != 0) {
318 //translation successfull, access VGA Memory (BAR or Legacy...)
319 DEBUG_PRINTF_MEM("%s(%x, %x): access to VGA Memory\n",
320 __func__, addr, val);
321 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr);
323 *((u8 *) translated_addr) = val;
325 } else if (addr > M.mem_size) {
326 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
328 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
331 /* write to virtual memory */
332 DEBUG_CHECK_VMEM_WRITE(addr, val);
333 *((u8 *) (M.mem_base + addr)) = val;
338 my_wrw(u32 addr, u16 val)
340 unsigned long translated_addr = addr;
341 u8 translated = biosemu_dev_translate_address(&translated_addr);
342 if (translated != 0) {
343 //translation successfull, access VGA Memory (BAR or Legacy...)
344 DEBUG_PRINTF_MEM("%s(%x, %x): access to VGA Memory\n",
345 __func__, addr, val);
346 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr);
347 // check for legacy memory, because of the remapping to BARs, the reads must
349 if ((addr >= 0xa0000) && (addr < 0xc0000)) {
350 //read bytes a using my_rdb, because of the remapping to BARs
351 //words may not be contiguous in memory, so we need to translate
353 my_wrb(addr, (u8) (val & 0x00FF));
354 my_wrb(addr + 1, (u8) ((val & 0xFF00) >> 8));
356 if ((translated_addr & (u64) 0x1) == 0) {
357 // 16 bit aligned access...
359 out16le((void *) translated_addr, val);
362 // unaligned access, write single bytes
364 *((u8 *) translated_addr) =
366 *((u8 *) translated_addr + 1) =
367 (u8) ((val & 0xFF00) >> 8);
371 } else if (addr > M.mem_size) {
372 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
374 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
377 /* write to virtual memory */
378 DEBUG_CHECK_VMEM_WRITE(addr, val);
379 out16le((void *) (M.mem_base + addr), val);
383 my_wrl(u32 addr, u32 val)
385 unsigned long translated_addr = addr;
386 u8 translated = biosemu_dev_translate_address(&translated_addr);
387 if (translated != 0) {
388 //translation successfull, access VGA Memory (BAR or Legacy...)
389 DEBUG_PRINTF_MEM("%s(%x, %x): access to VGA Memory\n",
390 __func__, addr, val);
391 //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __func__, addr, translated_addr);
392 // check for legacy memory, because of the remapping to BARs, the reads must
394 if ((addr >= 0xa0000) && (addr < 0xc0000)) {
395 //read bytes a using my_rdb, because of the remapping to BARs
396 //words may not be contiguous in memory, so we need to translate
398 my_wrb(addr, (u8) (val & 0x000000FF));
399 my_wrb(addr + 1, (u8) ((val & 0x0000FF00) >> 8));
400 my_wrb(addr + 2, (u8) ((val & 0x00FF0000) >> 16));
401 my_wrb(addr + 3, (u8) ((val & 0xFF000000) >> 24));
403 if ((translated_addr & (u64) 0x3) == 0) {
404 // 32 bit aligned access...
406 out32le((void *) translated_addr, val);
409 // unaligned access, write single bytes
411 *((u8 *) translated_addr) =
412 (u8) (val & 0x000000FF);
413 *((u8 *) translated_addr + 1) =
414 (u8) ((val & 0x0000FF00) >> 8);
415 *((u8 *) translated_addr + 2) =
416 (u8) ((val & 0x00FF0000) >> 16);
417 *((u8 *) translated_addr + 3) =
418 (u8) ((val & 0xFF000000) >> 24);
422 } else if (addr > M.mem_size) {
423 DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n",
425 //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1);
428 /* write to virtual memory */
429 DEBUG_CHECK_VMEM_WRITE(addr, val);
430 out32le((void *) (M.mem_base + addr), val);
453 my_wrb(u32 addr, u8 val)
459 my_wrw(u32 addr, u16 val)
465 my_wrl(u32 addr, u32 val)
471 //update time in BIOS Data Area
472 //DWord at offset 0x6c is the timer ticks since midnight, timer is running at 18Hz
473 //byte at 0x70 is timer overflow (set if midnight passed since last call to interrupt 1a function 00
474 //cur_val is the current value, of offset 6c...
476 update_time(u32 cur_val)
478 //for convenience, we let the start of timebase be at midnight, we currently dont support
479 //real daytime anyway...
480 u64 ticks_per_day = tb_freq * 60 * 24;
481 // at 18Hz a period is ~55ms, converted to ticks (tb_freq is ticks/second)
482 u32 period_ticks = (55 * tb_freq) / 1000;
483 u64 curr_time = get_time();
484 u64 ticks_since_midnight = curr_time % ticks_per_day;
485 u32 periods_since_midnight = ticks_since_midnight / period_ticks;
486 // if periods since midnight is smaller than last value, set overflow
487 // at BDA Offset 0x70
488 if (periods_since_midnight < cur_val) {
491 // store periods since midnight at BDA offset 0x6c
492 my_wrl(0x46c, periods_since_midnight);