SDL_dos.c 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. /*
  2. Simple DirectMedia Layer
  3. Copyright (C) 1997-2026 Sam Lantinga <slouken@libsdl.org>
  4. This software is provided 'as-is', without any express or implied
  5. warranty. In no event will the authors be held liable for any damages
  6. arising from the use of this software.
  7. Permission is granted to anyone to use this software for any purpose,
  8. including commercial applications, and to alter it and redistribute it
  9. freely, subject to the following restrictions:
  10. 1. The origin of this software must not be misrepresented; you must not
  11. claim that you wrote the original software. If you use this software
  12. in a product, an acknowledgment in the product documentation would be
  13. appreciated but is not required.
  14. 2. Altered source versions must be plainly marked as such, and must not be
  15. misrepresented as being the original software.
  16. 3. This notice may not be removed or altered from any source distribution.
  17. */
  18. #include "SDL_internal.h"
  19. #if defined(SDL_PLATFORM_DOS)
  20. #include "SDL_dos.h"
  21. void *DOS_AllocateConventionalMemory(const int len, _go32_dpmi_seginfo *seginfo)
  22. {
  23. seginfo->size = (len + 15) / 16; // this is in "paragraphs"
  24. if (_go32_dpmi_allocate_dos_memory(seginfo) != 0) {
  25. SDL_OutOfMemory();
  26. return NULL;
  27. }
  28. // No need to lock: DPMI 0.9 §3.3 guarantees the first megabyte is always
  29. // committed and locked. CWSDPMI (the DJGPP DPMI host) doesn't support
  30. // virtual memory at all, so conventional memory is never paged out.
  31. return DOS_PhysicalToLinear(seginfo->rm_segment * 16);
  32. }
  33. void *DOS_AllocateDMAMemory(const int len, _go32_dpmi_seginfo *seginfo)
  34. {
  35. // ISA DMA transfers cannot cross a 64 KB physical page boundary (hardware
  36. // limitation of the 8237 DMA controller). Allocating 2× the requested size
  37. // guarantees at least one contiguous `len`-byte region that doesn't straddle
  38. // a boundary. This is the standard technique used by Allegro, MIDAS, and
  39. // every other DOS audio library; allocate-check-retry would add complexity
  40. // for zero benefit.
  41. uint8_t *ptr = (uint8_t *)DOS_AllocateConventionalMemory(len * 2, seginfo);
  42. if (!ptr) {
  43. return NULL;
  44. }
  45. // if we're past the end of a page, use the second half of the block.
  46. const uint32_t physical = (seginfo->rm_segment * 16);
  47. if ((physical >> 16) != ((physical + len) >> 16)) {
  48. ptr += len;
  49. }
  50. return ptr;
  51. }
  52. void DOS_FreeConventionalMemory(_go32_dpmi_seginfo *seginfo)
  53. {
  54. _go32_dpmi_free_dos_memory(seginfo);
  55. }
  56. char *DOS_GetFarPtrCString(const Uint32 segoffset)
  57. {
  58. if (!segoffset) { // let's just treat this as a NULL pointer.
  59. return NULL;
  60. }
  61. const unsigned long ofs = (unsigned long)(((segoffset & 0xFFFF0000) >> 12) + (segoffset & 0xFFFF));
  62. size_t len;
  63. for (len = 0; _farpeekb(_dos_ds, ofs + len) != '\0'; len++) {
  64. }
  65. len++; // null terminator.
  66. char *retval = SDL_malloc(len);
  67. if (!retval) {
  68. return NULL;
  69. }
  70. for (size_t i = 0; i < len; i++) {
  71. retval[i] = (char)_farpeekb(_dos_ds, ofs + i);
  72. }
  73. return retval;
  74. }
  75. void DOS_HookInterrupt(int irq, DOS_InterruptHookFn fn, DOS_InterruptHook *hook)
  76. {
  77. SDL_assert(irq >= 0 && irq <= 15);
  78. SDL_assert(fn != NULL);
  79. SDL_assert(hook != NULL);
  80. hook->fn = fn;
  81. hook->irq = irq;
  82. hook->interrupt_vector = DOS_IRQToVector(irq);
  83. hook->irq_handler_seginfo.pm_selector = _go32_my_cs();
  84. hook->irq_handler_seginfo.pm_offset = (uint32_t)fn;
  85. _go32_dpmi_get_protected_mode_interrupt_vector(hook->interrupt_vector, &hook->original_irq_handler_seginfo);
  86. _go32_dpmi_chain_protected_mode_interrupt_vector(hook->interrupt_vector, &hook->irq_handler_seginfo);
  87. // enable interrupt on the correct PIC
  88. if (irq > 7) {
  89. outportb(PIC2_DATA, inportb(PIC2_DATA) & ~(1 << (irq - 8))); // unmask on slave PIC
  90. outportb(PIC1_DATA, inportb(PIC1_DATA) & ~(1 << 2)); // ensure cascade (IRQ2) is unmasked
  91. } else {
  92. outportb(PIC1_DATA, inportb(PIC1_DATA) & ~(1 << irq)); // unmask on master PIC
  93. }
  94. }
  95. void DOS_UnhookInterrupt(DOS_InterruptHook *hook, bool disable_interrupt)
  96. {
  97. if (!hook || !hook->fn) {
  98. return;
  99. }
  100. SDL_assert(hook->interrupt_vector > 0);
  101. SDL_assert(hook->irq > 0);
  102. if (disable_interrupt) {
  103. if (hook->irq > 7) {
  104. outportb(PIC2_DATA, inportb(PIC2_DATA) | (1 << (hook->irq - 8))); // mask on slave PIC
  105. } else {
  106. outportb(PIC1_DATA, inportb(PIC1_DATA) | (1 << hook->irq)); // mask on master PIC
  107. }
  108. }
  109. _go32_dpmi_set_protected_mode_interrupt_vector(hook->interrupt_vector, &hook->original_irq_handler_seginfo);
  110. // Note: _go32_dpmi_chain_protected_mode_interrupt_vector internally
  111. // allocates a wrapper, but it is NOT safe to free it with
  112. // _go32_dpmi_free_iret_wrapper — that function expects a wrapper
  113. // allocated by _go32_dpmi_allocate_iret_wrapper, and calling it on
  114. // a chain-allocated wrapper causes a page fault on exit. The wrapper
  115. // is a few bytes of conventional memory that the OS reclaims when the
  116. // process terminates, so the leak is harmless.
  117. SDL_zerop(hook);
  118. }
  119. #endif // defined(SDL_PLATFORM_DOS)