|
|
1.1 ! root 1: /****************************************************************************** ! 2: * Copyright (c) 2004, 2008 IBM Corporation ! 3: * All rights reserved. ! 4: * This program and the accompanying materials ! 5: * are made available under the terms of the BSD License ! 6: * which accompanies this distribution, and is available at ! 7: * http://www.opensource.org/licenses/bsd-license.php ! 8: * ! 9: * Contributors: ! 10: * IBM Corporation - initial implementation ! 11: *****************************************************************************/ ! 12: ! 13: #include <stdio.h> ! 14: #include <stdint.h> ! 15: #include <cpu.h> ! 16: #include "debug.h" ! 17: #include "device.h" ! 18: #include "x86emu/x86emu.h" ! 19: #include "biosemu.h" ! 20: #include <time.h> ! 21: ! 22: // define a check for access to certain (virtual) memory regions (interrupt handlers, BIOS Data Area, ...) ! 23: #ifdef DEBUG ! 24: static uint8_t in_check = 0; // to avoid recursion... ! 25: uint16_t ebda_segment; ! 26: uint32_t ebda_size; ! 27: ! 28: //TODO: these macros have grown so large, that they should be changed to an inline function, ! 29: //just for the sake of readability... ! 30: ! 31: //declare prototypes of the functions to follow, for use in DEBUG_CHECK_VMEM_ACCESS ! 32: uint8_t my_rdb(uint32_t); ! 33: uint16_t my_rdw(uint32_t); ! 34: uint32_t my_rdl(uint32_t); ! 35: ! 36: #define DEBUG_CHECK_VMEM_READ(_addr, _rval) \ ! 37: if ((debug_flags & DEBUG_CHECK_VMEM_ACCESS) && (in_check == 0)) { \ ! 38: in_check = 1; \ ! 39: /* determine ebda_segment and size \ ! 40: * since we are using my_rdx calls, make sure, this is after setting in_check! */ \ ! 41: /* offset 03 in BDA is EBDA segment */ \ ! 42: ebda_segment = my_rdw(0x40e); \ ! 43: /* first value in ebda is size in KB */ \ ! 44: ebda_size = my_rdb(ebda_segment << 4) * 1024; \ ! 45: /* check Interrupt Vector Access (0000:0000h - 0000:0400h) */ \ ! 46: if (_addr < 0x400) { \ ! 47: DEBUG_PRINTF_CS_IP("%s: read from Interrupt Vector %x --> %x\n", \ ! 48: __FUNCTION__, _addr / 4, _rval); \ ! 49: } \ ! 50: /* access to BIOS Data Area (0000:0400h - 0000:0500h)*/ \ ! 51: else if ((_addr >= 0x400) && (addr < 0x500)) { \ ! 52: DEBUG_PRINTF_CS_IP("%s: read from BIOS Data Area: addr: %x --> %x\n", \ ! 53: __FUNCTION__, _addr, _rval); \ ! 54: /* dump registers */ \ ! 55: /* x86emu_dump_xregs(); */ \ ! 56: } \ ! 57: /* access to first 64k of memory... */ \ ! 58: else if (_addr < 0x10000) { \ ! 59: DEBUG_PRINTF_CS_IP("%s: read from segment 0000h: addr: %x --> %x\n", \ ! 60: __FUNCTION__, _addr, _rval); \ ! 61: /* dump registers */ \ ! 62: /* x86emu_dump_xregs(); */ \ ! 63: } \ ! 64: /* read from PMM_CONV_SEGMENT */ \ ! 65: else if ((_addr <= ((PMM_CONV_SEGMENT << 4) | 0xffff)) && (_addr >= (PMM_CONV_SEGMENT << 4))) { \ ! 66: DEBUG_PRINTF_CS_IP("%s: read from PMM Segment %04xh: addr: %x --> %x\n", \ ! 67: __FUNCTION__, PMM_CONV_SEGMENT, _addr, _rval); \ ! 68: /* HALT_SYS(); */ \ ! 69: /* dump registers */ \ ! 70: /* x86emu_dump_xregs(); */ \ ! 71: } \ ! 72: /* read from PNP_DATA_SEGMENT */ \ ! 73: else if ((_addr <= ((PNP_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (PNP_DATA_SEGMENT << 4))) { \ ! 74: DEBUG_PRINTF_CS_IP("%s: read from PnP Data Segment %04xh: addr: %x --> %x\n", \ ! 75: __FUNCTION__, PNP_DATA_SEGMENT, _addr, _rval); \ ! 76: /* HALT_SYS(); */ \ ! 77: /* dump registers */ \ ! 78: /* x86emu_dump_xregs(); */ \ ! 79: } \ ! 80: /* read from EBDA Segment */ \ ! 81: else if ((_addr <= ((ebda_segment << 4) | (ebda_size - 1))) && (_addr >= (ebda_segment << 4))) { \ ! 82: DEBUG_PRINTF_CS_IP("%s: read from Extended BIOS Data Area %04xh, size: %04x: addr: %x --> %x\n", \ ! 83: __FUNCTION__, ebda_segment, ebda_size, _addr, _rval); \ ! 84: } \ ! 85: /* read from BIOS_DATA_SEGMENT */ \ ! 86: else if ((_addr <= ((BIOS_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (BIOS_DATA_SEGMENT << 4))) { \ ! 87: DEBUG_PRINTF_CS_IP("%s: read from BIOS Data Segment %04xh: addr: %x --> %x\n", \ ! 88: __FUNCTION__, BIOS_DATA_SEGMENT, _addr, _rval); \ ! 89: /* for PMM debugging */ \ ! 90: /*if (_addr == BIOS_DATA_SEGMENT << 4) { \ ! 91: X86EMU_trace_on(); \ ! 92: M.x86.debug &= ~DEBUG_DECODE_NOPRINT_F; \ ! 93: }*/ \ ! 94: /* dump registers */ \ ! 95: /* x86emu_dump_xregs(); */ \ ! 96: } \ ! 97: in_check = 0; \ ! 98: } ! 99: #define DEBUG_CHECK_VMEM_WRITE(_addr, _val) \ ! 100: if ((debug_flags & DEBUG_CHECK_VMEM_ACCESS) && (in_check == 0)) { \ ! 101: in_check = 1; \ ! 102: /* determine ebda_segment and size \ ! 103: * since we are using my_rdx calls, make sure, this is after setting in_check! */ \ ! 104: /* offset 03 in BDA is EBDA segment */ \ ! 105: ebda_segment = my_rdw(0x40e); \ ! 106: /* first value in ebda is size in KB */ \ ! 107: ebda_size = my_rdb(ebda_segment << 4) * 1024; \ ! 108: /* check Interrupt Vector Access (0000:0000h - 0000:0400h) */ \ ! 109: if (_addr < 0x400) { \ ! 110: DEBUG_PRINTF_CS_IP("%s: write to Interrupt Vector %x <-- %x\n", \ ! 111: __FUNCTION__, _addr / 4, _val); \ ! 112: } \ ! 113: /* access to BIOS Data Area (0000:0400h - 0000:0500h)*/ \ ! 114: else if ((_addr >= 0x400) && (addr < 0x500)) { \ ! 115: DEBUG_PRINTF_CS_IP("%s: write to BIOS Data Area: addr: %x <-- %x\n", \ ! 116: __FUNCTION__, _addr, _val); \ ! 117: /* dump registers */ \ ! 118: /* x86emu_dump_xregs(); */ \ ! 119: } \ ! 120: /* access to first 64k of memory...*/ \ ! 121: else if (_addr < 0x10000) { \ ! 122: DEBUG_PRINTF_CS_IP("%s: write to segment 0000h: addr: %x <-- %x\n", \ ! 123: __FUNCTION__, _addr, _val); \ ! 124: /* dump registers */ \ ! 125: /* x86emu_dump_xregs(); */ \ ! 126: } \ ! 127: /* write to PMM_CONV_SEGMENT... */ \ ! 128: else if ((_addr <= ((PMM_CONV_SEGMENT << 4) | 0xffff)) && (_addr >= (PMM_CONV_SEGMENT << 4))) { \ ! 129: DEBUG_PRINTF_CS_IP("%s: write to PMM Segment %04xh: addr: %x <-- %x\n", \ ! 130: __FUNCTION__, PMM_CONV_SEGMENT, _addr, _val); \ ! 131: /* dump registers */ \ ! 132: /* x86emu_dump_xregs(); */ \ ! 133: } \ ! 134: /* write to PNP_DATA_SEGMENT... */ \ ! 135: else if ((_addr <= ((PNP_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (PNP_DATA_SEGMENT << 4))) { \ ! 136: DEBUG_PRINTF_CS_IP("%s: write to PnP Data Segment %04xh: addr: %x <-- %x\n", \ ! 137: __FUNCTION__, PNP_DATA_SEGMENT, _addr, _val); \ ! 138: /* dump registers */ \ ! 139: /* x86emu_dump_xregs(); */ \ ! 140: } \ ! 141: /* write to EBDA Segment... */ \ ! 142: else if ((_addr <= ((ebda_segment << 4) | (ebda_size - 1))) && (_addr >= (ebda_segment << 4))) { \ ! 143: DEBUG_PRINTF_CS_IP("%s: write to Extended BIOS Data Area %04xh, size: %04x: addr: %x <-- %x\n", \ ! 144: __FUNCTION__, ebda_segment, ebda_size, _addr, _val); \ ! 145: } \ ! 146: /* write to BIOS_DATA_SEGMENT... */ \ ! 147: else if ((_addr <= ((BIOS_DATA_SEGMENT << 4) | 0xffff)) && (_addr >= (BIOS_DATA_SEGMENT << 4))) { \ ! 148: DEBUG_PRINTF_CS_IP("%s: write to BIOS Data Segment %04xh: addr: %x <-- %x\n", \ ! 149: __FUNCTION__, BIOS_DATA_SEGMENT, _addr, _val); \ ! 150: /* dump registers */ \ ! 151: /* x86emu_dump_xregs(); */ \ ! 152: } \ ! 153: /* write to current CS segment... */ \ ! 154: else if ((_addr < ((M.x86.R_CS << 4) | 0xffff)) && (_addr > (M.x86.R_CS << 4))) { \ ! 155: DEBUG_PRINTF_CS_IP("%s: write to CS segment %04xh: addr: %x <-- %x\n", \ ! 156: __FUNCTION__, M.x86.R_CS, _addr, _val); \ ! 157: /* dump registers */ \ ! 158: /* x86emu_dump_xregs(); */ \ ! 159: } \ ! 160: in_check = 0; \ ! 161: } ! 162: #else ! 163: #define DEBUG_CHECK_VMEM_READ(_addr, _rval) ! 164: #define DEBUG_CHECK_VMEM_WRITE(_addr, _val) ! 165: #endif ! 166: ! 167: //defined in net-snk/kernel/timer.c ! 168: extern uint64_t get_time(void); ! 169: ! 170: void update_time(uint32_t); ! 171: ! 172: // read byte from memory ! 173: uint8_t ! 174: my_rdb(uint32_t addr) ! 175: { ! 176: uint64_t translated_addr = addr; ! 177: uint8_t translated = dev_translate_address(&translated_addr); ! 178: uint8_t rval; ! 179: if (translated != 0) { ! 180: //translation successfull, access VGA Memory (BAR or Legacy...) ! 181: DEBUG_PRINTF_MEM("%s(%08x): access to VGA Memory\n", ! 182: __FUNCTION__, addr); ! 183: //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __FUNCTION__, addr, translated_addr); ! 184: set_ci(); ! 185: rval = *((uint8_t *) translated_addr); ! 186: clr_ci(); ! 187: DEBUG_PRINTF_MEM("%s(%08x) VGA --> %02x\n", __FUNCTION__, addr, ! 188: rval); ! 189: return rval; ! 190: } else if (addr > M.mem_size) { ! 191: DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n", ! 192: __FUNCTION__, addr); ! 193: //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1); ! 194: HALT_SYS(); ! 195: } else { ! 196: /* read from virtual memory */ ! 197: rval = *((uint8_t *) (M.mem_base + addr)); ! 198: DEBUG_CHECK_VMEM_READ(addr, rval); ! 199: return rval; ! 200: } ! 201: return -1; ! 202: } ! 203: ! 204: //read word from memory ! 205: uint16_t ! 206: my_rdw(uint32_t addr) ! 207: { ! 208: uint64_t translated_addr = addr; ! 209: uint8_t translated = dev_translate_address(&translated_addr); ! 210: uint16_t rval; ! 211: if (translated != 0) { ! 212: //translation successfull, access VGA Memory (BAR or Legacy...) ! 213: DEBUG_PRINTF_MEM("%s(%08x): access to VGA Memory\n", ! 214: __FUNCTION__, addr); ! 215: //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __FUNCTION__, addr, translated_addr); ! 216: // check for legacy memory, because of the remapping to BARs, the reads must ! 217: // be byte reads... ! 218: if ((addr >= 0xa0000) && (addr < 0xc0000)) { ! 219: //read bytes a using my_rdb, because of the remapping to BARs ! 220: //words may not be contiguous in memory, so we need to translate ! 221: //every address... ! 222: rval = ((uint8_t) my_rdb(addr)) | ! 223: (((uint8_t) my_rdb(addr + 1)) << 8); ! 224: } else { ! 225: if ((translated_addr & (uint64_t) 0x1) == 0) { ! 226: // 16 bit aligned access... ! 227: set_ci(); ! 228: rval = in16le((void *) translated_addr); ! 229: clr_ci(); ! 230: } else { ! 231: // unaligned access, read single bytes ! 232: set_ci(); ! 233: rval = (*((uint8_t *) translated_addr)) | ! 234: (*((uint8_t *) translated_addr + 1) << 8); ! 235: clr_ci(); ! 236: } ! 237: } ! 238: DEBUG_PRINTF_MEM("%s(%08x) VGA --> %04x\n", __FUNCTION__, addr, ! 239: rval); ! 240: return rval; ! 241: } else if (addr > M.mem_size) { ! 242: DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n", ! 243: __FUNCTION__, addr); ! 244: //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1); ! 245: HALT_SYS(); ! 246: } else { ! 247: /* read from virtual memory */ ! 248: rval = in16le((void *) (M.mem_base + addr)); ! 249: DEBUG_CHECK_VMEM_READ(addr, rval); ! 250: return rval; ! 251: } ! 252: return -1; ! 253: } ! 254: ! 255: //read long from memory ! 256: uint32_t ! 257: my_rdl(uint32_t addr) ! 258: { ! 259: uint64_t translated_addr = addr; ! 260: uint8_t translated = dev_translate_address(&translated_addr); ! 261: uint32_t rval; ! 262: if (translated != 0) { ! 263: //translation successfull, access VGA Memory (BAR or Legacy...) ! 264: DEBUG_PRINTF_MEM("%s(%x): access to VGA Memory\n", ! 265: __FUNCTION__, addr); ! 266: //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __FUNCTION__, addr, translated_addr); ! 267: // check for legacy memory, because of the remapping to BARs, the reads must ! 268: // be byte reads... ! 269: if ((addr >= 0xa0000) && (addr < 0xc0000)) { ! 270: //read bytes a using my_rdb, because of the remapping to BARs ! 271: //dwords may not be contiguous in memory, so we need to translate ! 272: //every address... ! 273: rval = ((uint8_t) my_rdb(addr)) | ! 274: (((uint8_t) my_rdb(addr + 1)) << 8) | ! 275: (((uint8_t) my_rdb(addr + 2)) << 16) | ! 276: (((uint8_t) my_rdb(addr + 3)) << 24); ! 277: } else { ! 278: if ((translated_addr & (uint64_t) 0x3) == 0) { ! 279: // 32 bit aligned access... ! 280: set_ci(); ! 281: rval = in32le((void *) translated_addr); ! 282: clr_ci(); ! 283: } else { ! 284: // unaligned access, read single bytes ! 285: set_ci(); ! 286: rval = (*((uint8_t *) translated_addr)) | ! 287: (*((uint8_t *) translated_addr + 1) << 8) | ! 288: (*((uint8_t *) translated_addr + 2) << 16) | ! 289: (*((uint8_t *) translated_addr + 3) << 24); ! 290: clr_ci(); ! 291: } ! 292: } ! 293: DEBUG_PRINTF_MEM("%s(%08x) VGA --> %08x\n", __FUNCTION__, addr, ! 294: rval); ! 295: //HALT_SYS(); ! 296: return rval; ! 297: } else if (addr > M.mem_size) { ! 298: DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n", ! 299: __FUNCTION__, addr); ! 300: //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1); ! 301: HALT_SYS(); ! 302: } else { ! 303: /* read from virtual memory */ ! 304: rval = in32le((void *) (M.mem_base + addr)); ! 305: switch (addr) { ! 306: case 0x46c: ! 307: //BDA Time Data, update it, before reading ! 308: update_time(rval); ! 309: rval = in32le((void *) (M.mem_base + addr)); ! 310: break; ! 311: } ! 312: DEBUG_CHECK_VMEM_READ(addr, rval); ! 313: return rval; ! 314: } ! 315: return -1; ! 316: } ! 317: ! 318: //write byte to memory ! 319: void ! 320: my_wrb(uint32_t addr, uint8_t val) ! 321: { ! 322: uint64_t translated_addr = addr; ! 323: uint8_t translated = dev_translate_address(&translated_addr); ! 324: if (translated != 0) { ! 325: //translation successfull, access VGA Memory (BAR or Legacy...) ! 326: DEBUG_PRINTF_MEM("%s(%x, %x): access to VGA Memory\n", ! 327: __FUNCTION__, addr, val); ! 328: //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __FUNCTION__, addr, translated_addr); ! 329: set_ci(); ! 330: *((uint8_t *) translated_addr) = val; ! 331: clr_ci(); ! 332: } else if (addr > M.mem_size) { ! 333: DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n", ! 334: __FUNCTION__, addr); ! 335: //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1); ! 336: HALT_SYS(); ! 337: } else { ! 338: /* write to virtual memory */ ! 339: DEBUG_CHECK_VMEM_WRITE(addr, val); ! 340: *((uint8_t *) (M.mem_base + addr)) = val; ! 341: } ! 342: } ! 343: ! 344: void ! 345: my_wrw(uint32_t addr, uint16_t val) ! 346: { ! 347: uint64_t translated_addr = addr; ! 348: uint8_t translated = dev_translate_address(&translated_addr); ! 349: if (translated != 0) { ! 350: //translation successfull, access VGA Memory (BAR or Legacy...) ! 351: DEBUG_PRINTF_MEM("%s(%x, %x): access to VGA Memory\n", ! 352: __FUNCTION__, addr, val); ! 353: //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __FUNCTION__, addr, translated_addr); ! 354: // check for legacy memory, because of the remapping to BARs, the reads must ! 355: // be byte reads... ! 356: if ((addr >= 0xa0000) && (addr < 0xc0000)) { ! 357: //read bytes a using my_rdb, because of the remapping to BARs ! 358: //words may not be contiguous in memory, so we need to translate ! 359: //every address... ! 360: my_wrb(addr, (uint8_t) (val & 0x00FF)); ! 361: my_wrb(addr + 1, (uint8_t) ((val & 0xFF00) >> 8)); ! 362: } else { ! 363: if ((translated_addr & (uint64_t) 0x1) == 0) { ! 364: // 16 bit aligned access... ! 365: set_ci(); ! 366: out16le((void *) translated_addr, val); ! 367: clr_ci(); ! 368: } else { ! 369: // unaligned access, write single bytes ! 370: set_ci(); ! 371: *((uint8_t *) translated_addr) = ! 372: (uint8_t) (val & 0x00FF); ! 373: *((uint8_t *) translated_addr + 1) = ! 374: (uint8_t) ((val & 0xFF00) >> 8); ! 375: clr_ci(); ! 376: } ! 377: } ! 378: } else if (addr > M.mem_size) { ! 379: DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n", ! 380: __FUNCTION__, addr); ! 381: //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1); ! 382: HALT_SYS(); ! 383: } else { ! 384: /* write to virtual memory */ ! 385: DEBUG_CHECK_VMEM_WRITE(addr, val); ! 386: out16le((void *) (M.mem_base + addr), val); ! 387: } ! 388: } ! 389: void ! 390: my_wrl(uint32_t addr, uint32_t val) ! 391: { ! 392: uint64_t translated_addr = addr; ! 393: uint8_t translated = dev_translate_address(&translated_addr); ! 394: if (translated != 0) { ! 395: //translation successfull, access VGA Memory (BAR or Legacy...) ! 396: DEBUG_PRINTF_MEM("%s(%x, %x): access to VGA Memory\n", ! 397: __FUNCTION__, addr, val); ! 398: //DEBUG_PRINTF_MEM("%s(%08x): translated_addr: %llx\n", __FUNCTION__, addr, translated_addr); ! 399: // check for legacy memory, because of the remapping to BARs, the reads must ! 400: // be byte reads... ! 401: if ((addr >= 0xa0000) && (addr < 0xc0000)) { ! 402: //read bytes a using my_rdb, because of the remapping to BARs ! 403: //words may not be contiguous in memory, so we need to translate ! 404: //every address... ! 405: my_wrb(addr, (uint8_t) (val & 0x000000FF)); ! 406: my_wrb(addr + 1, (uint8_t) ((val & 0x0000FF00) >> 8)); ! 407: my_wrb(addr + 2, (uint8_t) ((val & 0x00FF0000) >> 16)); ! 408: my_wrb(addr + 3, (uint8_t) ((val & 0xFF000000) >> 24)); ! 409: } else { ! 410: if ((translated_addr & (uint64_t) 0x3) == 0) { ! 411: // 32 bit aligned access... ! 412: set_ci(); ! 413: out32le((void *) translated_addr, val); ! 414: clr_ci(); ! 415: } else { ! 416: // unaligned access, write single bytes ! 417: set_ci(); ! 418: *((uint8_t *) translated_addr) = ! 419: (uint8_t) (val & 0x000000FF); ! 420: *((uint8_t *) translated_addr + 1) = ! 421: (uint8_t) ((val & 0x0000FF00) >> 8); ! 422: *((uint8_t *) translated_addr + 2) = ! 423: (uint8_t) ((val & 0x00FF0000) >> 16); ! 424: *((uint8_t *) translated_addr + 3) = ! 425: (uint8_t) ((val & 0xFF000000) >> 24); ! 426: clr_ci(); ! 427: } ! 428: } ! 429: } else if (addr > M.mem_size) { ! 430: DEBUG_PRINTF("%s(%08x): Memory Access out of range!\n", ! 431: __FUNCTION__, addr); ! 432: //disassemble_forward(M.x86.saved_cs, M.x86.saved_ip, 1); ! 433: HALT_SYS(); ! 434: } else { ! 435: /* write to virtual memory */ ! 436: DEBUG_CHECK_VMEM_WRITE(addr, val); ! 437: out32le((void *) (M.mem_base + addr), val); ! 438: } ! 439: } ! 440: ! 441: //update time in BIOS Data Area ! 442: //DWord at offset 0x6c is the timer ticks since midnight, timer is running at 18Hz ! 443: //byte at 0x70 is timer overflow (set if midnight passed since last call to interrupt 1a function 00 ! 444: //cur_val is the current value, of offset 6c... ! 445: void ! 446: update_time(uint32_t cur_val) ! 447: { ! 448: //for convenience, we let the start of timebase be at midnight, we currently dont support ! 449: //real daytime anyway... ! 450: uint64_t ticks_per_day = tb_freq * 60 * 24; ! 451: // at 18Hz a period is ~55ms, converted to ticks (tb_freq is ticks/second) ! 452: uint32_t period_ticks = (55 * tb_freq) / 1000; ! 453: uint64_t curr_time = get_time(); ! 454: uint64_t ticks_since_midnight = curr_time % ticks_per_day; ! 455: uint32_t periods_since_midnight = ticks_since_midnight / period_ticks; ! 456: // if periods since midnight is smaller than last value, set overflow ! 457: // at BDA Offset 0x70 ! 458: if (periods_since_midnight < cur_val) { ! 459: my_wrb(0x470, 1); ! 460: } ! 461: // store periods since midnight at BDA offset 0x6c ! 462: my_wrl(0x46c, periods_since_midnight); ! 463: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.