diff --git a/.gitignore b/.gitignore index 25aae0e..fc66526 100644 --- a/.gitignore +++ b/.gitignore @@ -3,8 +3,11 @@ **/*.EXE **/*.exe **/*.out +**/*.elf +**/*.bin **/*.BAK **/*.wdl +**/*.DX *.sublime-* Player/player diff --git a/Common/cfg.h b/Common/cfg.h index fb8187d..300b211 100644 --- a/Common/cfg.h +++ b/Common/cfg.h @@ -2,6 +2,8 @@ Configuration file. */ +/* TODO: Move to root or Shared/ folder? */ + #ifndef COM_CFG_H #define COM_CFG_H diff --git a/Player/Display/dos.c b/Player/Display/dos.c index 4fc8b4d..ca1a112 100644 --- a/Player/Display/dos.c +++ b/Player/Display/dos.c @@ -1,17 +1,16 @@ #include "display.h" +#include +#include +#include #include -#include -#define WIDTH 640 -#define HEIGHT 480 +#define WIDTH 320 +#define HEIGHT 200 #define MODE_VGA_320_200_8 0x13 #define MODE_VESA_640_480_8 0x101 -/* - TODO: https://github.com/depp/elf2dos/blob/master/elf/elf.go - https://www.edm2.com/index.php/LX_-_Linear_eXecutable_Module_Format_Description - Do not use anything but Clang in the end. - */ +/* https://www.phatcode.net/res/262/files/dpmi09.html */ +/* http://www.techhelpmanual.com/27-dos__bios___extensions_service_index.html */ // 8-color VGA palette gradient, lighter shades of lavender to pink #define COLOR_OFFSET 0x50 @@ -132,13 +131,183 @@ // return NULL; // } +/* Not really needed i guess? */ +// static inline bool real_mode_interrupt(uint8_t interrupt) { +// bool carry_clear; +// __asm__ volatile("int $0x31" +// : "=@ccnc"(carry_clear) +// : "a"(0x0300), "b"(interrupt), "c"(0), "D"(&dpmi_regs) +// : "memory", "cc"); +// return !carry_clear; +// } + +typedef struct { + uint16_t rm_segment; + uint16_t pm_selector; +} LowMemBlock; + +// Allocates low memory under 1MB +static inline LowMemBlock allocate_low_mem(uint32_t size_in_bytes) { + LowMemBlock block = {0, 0}; + uint32_t paragraphs = + (size_in_bytes + 15) >> 4; // Convert bytes to paragraphs + + uint16_t seg, sel; + uint8_t failed = 0; + + __asm__ __volatile__( + "movw $0x0100, %%ax \n\t" // DPMI allocate DOS memory block + "int $0x31 \n\t" + "setc %2 \n\t" // Check Carry Flag for error + : "=a"(seg), "=d"(sel), "=r"(failed) + : "b"(paragraphs) + : "cc"); + + if (!failed) { + block.rm_segment = seg; + block.pm_selector = sel; + } + return block; +} + +static inline void copy_to_low_mem(LowMemBlock block, char *data, + uint32_t size) { + __asm__ volatile("movw %0, %%es\n" + "movw %1, %%di\n" + "movw %2, %%si\n" + "movw .data, %%ax\n" + "movw %3, %%cx\n" + "cld\n" + "rep movsb" ::"r"(block.rm_segment), + "r"(block.pm_selector), "r"(data), "r"(size) + : "memory", "cc", "ax", "bx", "cx", "esi", "di"); +} + +// Frees the allocated low memory (DPMI Ax=0101h) +static inline void free_low_mem(uint16_t selector) { + __asm__ __volatile__("movw $0x0101, %%ax \n\t" // DPMI free DOS memory block + "int $0x31" + : + : "d"(selector) + : "ax", "cc"); +} + +static inline void set_video_mode(uint16_t mode) { + __asm__ volatile("int $0x10" : : "a"(mode) : "memory", "cc"); +} + +/* http://www.techhelpmanual.com/439-dos_fn_40h__write_to_file_via_handle.html + */ +static inline void writetest(void) { + __asm__ volatile("int $0x21" : : "a"(0x0200), "d"('Y') : "memory", "cc"); +} + +/* http://www.techhelpmanual.com/630-int_31h_0800h__physical_address_mapping.html + */ +static inline void *map_physical_address(uint32_t physical_addr, + uint32_t size) { + uint16_t high, low; + bool carry_clear; + __asm__ volatile("int $0x31" + : "=b"(high), "=c"(low), "=@ccnc"(carry_clear) + : "a"(0x0800), "b"(physical_addr >> 16), + "c"(physical_addr & 0xFFFF), "S"(size >> 16), + "D"(size & 0xFFFF) + : "memory", "cc"); + return !carry_clear ? NULL : (void *)(intptr_t)((high << 16) | low); +} + +static inline uint16_t alloc_ldt_descriptor(void) { + bool carry_clear; + uint16_t selector; + __asm__ volatile("int $0x31" + : "=a"(selector), "=@ccnc"(carry_clear) + : "a"(0x0000), "c"(1) + : "memory", "cc"); + return !carry_clear ? selector : 0; +} + +static inline bool set_segment_base_address(uint16_t selector, + uint32_t baseaddr) { + bool carry_clear; + __asm__ volatile("int $0x31" + : "=a"(selector), "=@ccnc"(carry_clear) + : "a"(0x0007), "c"(baseaddr >> 16), "d"(baseaddr & 0xFFFF) + : "memory", "cc"); + return !carry_clear; +} + +static inline bool set_segment_limit(uint16_t selector, uint32_t size) { + bool carry_clear; + __asm__ volatile("int $0x31" + : "=@ccnc"(carry_clear) + : "a"(0x0008), "b"(selector), "c"(size >> 16), + "d"(size & 0xFFFF) + : "memory", "cc"); + return !carry_clear; +} + +static inline bool set_segment_rights(uint16_t selector, bool read, + bool write) { + bool carry_clear; + uint16_t flags = + 1 | (write << 1) | (1 << 4) | (1 << 7) | (1 << 14) | (1 << 15); + __asm__ volatile("int $0x31" + : "=@ccnc"(carry_clear) + : "a"(0x0009), "b"(selector), "c"(flags) + : "memory", "cc"); + return !carry_clear; +} + +static inline void use_segment(uint16_t selector) { + __asm__ volatile("" : "=S"(selector)::); +} + +/* https://tippach.hier-im-netz.de/wdosx/int21api.html#f09 */ +static inline void print_to_console(char *const str) { + __asm__ volatile("int $0x21" ::"a"(0x0900), "d"(str) + : "memory", "cc", "esi", "edi", "esp"); +} + +static uint32_t test = 0x1420124; + // extern int plr_display_dos_main(int argc, char *argv[]) { extern int plr_display_dos_main(void) { - char *buf = (char *)0xA0000; // VGA memory address + print_to_console("Fuck you stupid shit"); + return -1; + + uint16_t base = alloc_ldt_descriptor(); + if (!base) { + print_to_console("Cannot allocate LDT descriptor"); + return -1; + } + + if (!set_segment_base_address(base, 0xA0000)) { + print_to_console("Cannot set LDT base address"); + return -1; + } + + if (!set_segment_limit(base, 0x10000)) { + print_to_console("Cannot set LDT limit"); + return -1; + } + + use_segment(base); + + // char *volatile buf = (void *)(intptr_t)0xA0000; // VGA memory address + // char *buf = map_physical_address(0xA0000, 0x10000); // VGA memory + char *buf = map_physical_address(0xA0000, 0x10000); // VGA memory + // address + if (!buf) + return -1; int x, y; + // if (!buf) + // return -1; + // unsigned char saved_mode = get_video_mode(); // Save original mode - // set_vesa_video_mode(MODE_VESA_640_480_8); // Set our VGA mode + // set_vesa_video_mode(MODE_VESA_640_480_8); // Set our VGA mode + set_video_mode(MODE_VGA_320_200_8); // Set our VGA mode // buf = find_display_memory(); @@ -162,9 +331,25 @@ extern int plr_display_dos_main(void) { for (y = 0; y < HEIGHT; y++) { for (x = 0; x < WIDTH; x++) { buf[y * WIDTH + x] = COLOR_OFFSET + y / COLOR_STEP; + // __asm__ volatile("int $0x10" + // : + // : "a"(0x0C << 8 | COLOR_OFFSET + y / COLOR_STEP), + // "b"(0), "c"(x), "d"(y) + // : "memory", "cc"); } } + // writetest(); + __asm__ volatile("int $0x16" : : "a"(0x00) : "memory", "cc"); } + // dpmi_regs = (struct dpmi_regs){.eax = 0x00004C00}; + // __asm__ volatile("movw $0x0300, %%ax\n" + // "movb $0x21, %%bl\n" + // "xorb %%bh, %%bh\n" + // "xorw %%cx, %%cx\n" + // "movl $dpmi_regs, %%edi\n" + // "int $0x31" :: + // : "memory", "cc", "ax", "bx", "cx", "edi"); + return 0; } diff --git a/Player/Main/dos.s b/Player/Main/dos.s index 81849f5..d6c7ed3 100644 --- a/Player/Main/dos.s +++ b/Player/Main/dos.s @@ -1,47 +1,18 @@ .section .data .align 4 +# TODO: Make sure it's placed under 1 MiB. # The DPMI Register Structure (50 bytes total) # This holds the register states injected into Real Mode. -dpmi_regs: - .long 0 # edi (offset 0) - .long 0 # esi (offset 4) - .long 0 # ebp (offset 8) - .long 0 # esp / reserved (offset 12) - .long 0 # ebx (offset 16) - .long 0 # edx (offset 20) - .long 0 # ecx (offset 24) -dpmi_eax: - .long 0 # eax (offset 28) - .short 0 # flags(offset 32) - .short 0 # es (offset 34) - .short 0 # ds (offset 36) - .short 0 # fs (offset 38) - .short 0 # gs (offset 40) - .short 0 # ip (offset 42) - .short 0 # cs (offset 44) - .short 0 # sp (offset 46) - .short 0 # ss (offset 48) .section .text .global _start _start: + push %ebp + mov %esp, %ebp + cld + call plr_display_dos_main - # 1. Set up the Real-Mode registers inside the DPMI structure - # AH = 4Ch (DOS Exit Function), AL = 00h (Return Code 0) - movl $0x00004C00, dpmi_eax - - # 2. Call DPMI Simulate Real Mode Interrupt - movw $0x0300, %ax # DPMI Function: Simulate Real Mode Interrupt - movb $0x21, %bl # Interrupt number to trigger (INT 21h) - movb $0x00, %bh # Flags (Must be 0) - movw $0x0000, %cx # Number of words to copy to RM stack (0) - - # ES:EDI points to our DPMI register block. - # In WDOSX flat model, %es already targets the flat data descriptor. - movl $dpmi_regs, %edi - - int $0x31 # Trigger DPMI host to execute the RM interrupt - - # --- Execution halts here as DOS terminates the program --- + movb $0x4C, %ah + int $0x21 diff --git a/Player/Makefile b/Player/Makefile index 82573e9..a82125b 100644 --- a/Player/Makefile +++ b/Player/Makefile @@ -3,6 +3,7 @@ RELEASE ?= 0 # Optimize. DEBUG ?= 1 # Emit debug info. SQUEEZE ?= 0 # Nuclear option to save space. BITS ?= 64 +RUN ?= 0 CFLAGS = -Wall -std=gnu99 -flto=thin -ffast-math -fno-ident -fvisibility=hidden -fuse-ld=lld ifeq ($(MAKECMDGOALS),linux) CFLAGS += -fno-pic -fno-pie @@ -64,13 +65,19 @@ wasm: $(CC) -o player.wasm $(WASM) $(CFLAGS) # https://tippach.hier-im-netz.de/wdosx -dos: CFLAGS += -ffreestanding -m32 -fno-PIC -fno-PIE -no-pie -nostdlib -nodefaultlibs -dos: - $(CC) -c ./Main/dos.s -o ./Main/dos.o $(CFLAGS) +dos: CFLAGS += -ffreestanding -m32 -fno-PIC -fno-PIE -nostdlib -nodefaultlibs +dos: + $(CC) -c ./Main/dos.s -o ./Main/dos.o -m32 $(CC) -c ./Display/dos.c -o ./Display/dos.o $(CFLAGS) - ld.lld -m elf_i386 --oformat binary -Ttext 0x0 --image-base=0x0 ./Main/dos.o ./Display/dos.o -o dos.bin - dosbox -noconsole -c "mount c $(PWD)" -c "c:" -c "WDOSX\STUBIT.EXE DOS.BIN" -exit - mv ./DOS.BIN ./DOS.EXE - + ld.lld -m elf_i386 -T dos.ld ./Main/dos.o ./Display/dos.o -o dos.elf + objcopy -O binary --strip-all dos.elf dos.bin + cat WDosX/WDOSX.DX dos.bin > DOS.EXE +ifeq ($(RUN), 1) +ifeq ($(DEBUG), 1) + dosbox -c "mount c $(PWD)" -c "c:" -c "WDOSX\WUDEBUG.EXE DOS.EXE" +else + dosbox -c "mount c $(PWD)" -c "c:" -c "DOS.EXE" +endif +endif clean: rm -r *.o ../*.o diff --git a/Player/WDosX/STUBIT.EXE b/Player/WDosX/STUBIT.EXE deleted file mode 100644 index a19d397..0000000 Binary files a/Player/WDosX/STUBIT.EXE and /dev/null differ diff --git a/Player/WDosX/advapi32.wdl b/Player/WDosX/advapi32.wdl deleted file mode 100644 index 538a3c2..0000000 Binary files a/Player/WDosX/advapi32.wdl and /dev/null differ diff --git a/Player/WDosX/kernel32.wdl b/Player/WDosX/kernel32.wdl deleted file mode 100644 index 5258fd2..0000000 Binary files a/Player/WDosX/kernel32.wdl and /dev/null differ diff --git a/Player/WDosX/oleaut32.wdl b/Player/WDosX/oleaut32.wdl deleted file mode 100644 index 8313c86..0000000 Binary files a/Player/WDosX/oleaut32.wdl and /dev/null differ diff --git a/Player/WDosX/user32.wdl b/Player/WDosX/user32.wdl deleted file mode 100644 index dc21fe1..0000000 Binary files a/Player/WDosX/user32.wdl and /dev/null differ diff --git a/Player/dos.ld b/Player/dos.ld new file mode 100644 index 0000000..369d55b --- /dev/null +++ b/Player/dos.ld @@ -0,0 +1,41 @@ +SECTIONS +{ + . = 0x00000; + + /* TODO: Make sure WDOSX.DX is placed first, before .text */ + textstart = .; + .text ALIGN(16) : { + *(.text*) + } + textend = .; + + /* We will need to manually map this. */ + /* At runtime we can figure this out from mapped code segment. */ + datastart = .; + .rodata : { + *(.rodata*) + } + + .data : { + *(.data*) + } + dataend = .; + + /* TODO: We need to manually map this, as loader is incapable of doing so with flat binary. */ + bssstart = .; + .bss : { + *(.bss*) + *(COMMON) + } + bssend = .; + + /* Will be discarded with objcopy. */ + .shstrtab : { + *(.shstrtab*) + } + + /* Discard bloat and unnecessary ELF sections */ + /DISCARD/ : { + *(*) + } +}