diff --git a/.gitignore b/.gitignore index f506ee1..25aae0e 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,8 @@ **/*.EXE **/*.exe **/*.out +**/*.BAK +**/*.wdl *.sublime-* Player/player diff --git a/Player/Display/display.h b/Player/Display/display.h index f815f52..a199239 100644 --- a/Player/Display/display.h +++ b/Player/Display/display.h @@ -2,6 +2,6 @@ #define PLR_DISPLAY_H __attribute__((used)) extern int plr_display_x11_main(int argc, char *argv[]); -extern int plr_display_dos_main(int argc, char *argv[]); +__attribute__((used)) extern int plr_display_dos_main(void); #endif diff --git a/Player/Display/dos.c b/Player/Display/dos.c index 812c35d..4fc8b4d 100644 --- a/Player/Display/dos.c +++ b/Player/Display/dos.c @@ -7,146 +7,164 @@ #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. + */ + // 8-color VGA palette gradient, lighter shades of lavender to pink #define COLOR_OFFSET 0x50 #define COLOR_NUM 8 #define COLOR_STEP (HEIGHT / COLOR_NUM) -/* https://wiki.osdev.org/VESA_Video_Modes */ -_Packed struct VbeInfoBlock { - char VbeSignature[4]; // == "VESA" - uint16_t VbeVersion; // == 0x0300 for VBE 3.0 - uint16_t OemStringPtr[2]; // isa vbeFarPtr - uint8_t Capabilities[4]; - uint16_t VideoModePtr[2]; // isa vbeFarPtr - uint16_t TotalMemory; // as # of 64KB blocks - uint8_t Reserved[492]; -}; +// /* https://wiki.osdev.org/VESA_Video_Modes */ +// _Packed struct VbeInfoBlock { +// char VbeSignature[4]; // == "VESA" +// uint16_t VbeVersion; // == 0x0300 for VBE 3.0 +// uint16_t OemStringPtr[2]; // isa vbeFarPtr +// uint8_t Capabilities[4]; +// uint16_t VideoModePtr[2]; // isa vbeFarPtr +// uint16_t TotalMemory; // as # of 64KB blocks +// uint8_t Reserved[492]; +// }; -_Packed struct ModeInfoBlock { - uint16_t - attributes; // deprecated, only bit 7 should be of interest to you, and it - // indicates the mode supports a linear frame buffer. - uint8_t window_a; // deprecated - uint8_t window_b; // deprecated - uint16_t granularity; // deprecated; used while calculating bank numbers - uint16_t window_size; - uint16_t segment_a; - uint16_t segment_b; - uint32_t win_func_ptr; // deprecated; used to switch banks from protected mode - // without returning to real mode - uint16_t pitch; // number of bytes per horizontal line - uint16_t width; // width in pixels - uint16_t height; // height in pixels - uint8_t w_char; // unused... - uint8_t y_char; // ... - uint8_t planes; - uint8_t bpp; // bits per pixel in this mode - uint8_t banks; // deprecated; total number of banks in this mode - uint8_t memory_model; - uint8_t bank_size; // deprecated; size of a bank, almost always 64 KB but may - // be 16 KB... - uint8_t image_pages; - uint8_t reserved0; +// _Packed struct ModeInfoBlock { +// uint16_t +// attributes; // deprecated, only bit 7 should be of interest to you, and +// it +// // indicates the mode supports a linear frame buffer. +// uint8_t window_a; // deprecated +// uint8_t window_b; // deprecated +// uint16_t granularity; // deprecated; used while calculating bank numbers +// uint16_t window_size; +// uint16_t segment_a; +// uint16_t segment_b; +// uint32_t win_func_ptr; // deprecated; used to switch banks from protected +// mode +// // without returning to real mode +// uint16_t pitch; // number of bytes per horizontal line +// uint16_t width; // width in pixels +// uint16_t height; // height in pixels +// uint8_t w_char; // unused... +// uint8_t y_char; // ... +// uint8_t planes; +// uint8_t bpp; // bits per pixel in this mode +// uint8_t banks; // deprecated; total number of banks in this mode +// uint8_t memory_model; +// uint8_t bank_size; // deprecated; size of a bank, almost always 64 KB but +// may +// // be 16 KB... +// uint8_t image_pages; +// uint8_t reserved0; - uint8_t red_mask; - uint8_t red_position; - uint8_t green_mask; - uint8_t green_position; - uint8_t blue_mask; - uint8_t blue_position; - uint8_t reserved_mask; - uint8_t reserved_position; - uint8_t direct_color_attributes; +// uint8_t red_mask; +// uint8_t red_position; +// uint8_t green_mask; +// uint8_t green_position; +// uint8_t blue_mask; +// uint8_t blue_position; +// uint8_t reserved_mask; +// uint8_t reserved_position; +// uint8_t direct_color_attributes; - uint32_t framebuffer; // physical address of the linear frame buffer; write - // here to draw to the screen - uint32_t off_screen_mem_off; - uint16_t off_screen_mem_size; // size of memory in the framebuffer but not - // being displayed on the screen - uint8_t reserved1[206]; -}; +// uint32_t framebuffer; // physical address of the linear frame buffer; write +// // here to draw to the screen +// uint32_t off_screen_mem_off; +// uint16_t off_screen_mem_size; // size of memory in the framebuffer but not +// // being displayed on the screen +// uint8_t reserved1[206]; +// }; -static char get_video_mode(); -#pragma aux get_video_mode = "mov ah, 0x0f" \ - "int 0x10" value[al] modify[ah]; +// static char get_video_mode(); +// #pragma aux get_video_mode = "mov ah, 0x0f" \ +// "int 0x10" value[al] modify[ah]; -static void set_video_mode(unsigned char); -#pragma aux set_video_mode = "mov ah, 0x00" \ - "int 0x10" parm[al] modify[ah]; +// static void set_video_mode(unsigned char); +// #pragma aux set_video_mode = "mov ah, 0x00" \ +// "int 0x10" parm[al] modify[ah]; -static void set_vesa_video_mode(unsigned short); -#pragma aux set_vesa_video_mode = "mov ax, 0x4f02" \ - "int 0x10" parm[bx] modify[ah]; +// static void set_vesa_video_mode(unsigned short); +// #pragma aux set_vesa_video_mode = "mov ax, 0x4f02" \ +// "int 0x10" parm[bx] modify[ah]; -static void wait_for_key(); -#pragma aux wait_for_key = "mov ah, 0x00" \ - "int 0x16" modify[ah]; +// static void wait_for_key(); +// #pragma aux wait_for_key = "mov ah, 0x00" \ +// "int 0x16" modify[ah]; -static short get_vesa_controller_info(struct VbeInfoBlock *); -#pragma aux get_vesa_controller_info = "mov ax, 0x4f00" \ - "int 0x10" parm[es di] value[ax]; +// static short get_vesa_controller_info(struct VbeInfoBlock *); +// #pragma aux get_vesa_controller_info = "mov ax, 0x4f00" \ +// "int 0x10" parm[es di] value[ax]; -static short get_vesa_mode_info(uint16_t mode, struct ModeInfoBlock *); -#pragma aux get_vesa_mode_info = "mov ax, 0x4F01" \ - "int 0x10" parm[cx][es di] value[ax]; +// static short get_vesa_mode_info(uint16_t mode, struct ModeInfoBlock *); +// #pragma aux get_vesa_mode_info = "mov ax, 0x4F01" \ +// "int 0x10" parm[cx][es di] value[ax]; -/* https://wiki.osdev.org/User:Omarrx024/VESA_Tutorial*/ -static char *find_display_memory(void) { - struct VbeInfoBlock *ctrl = (struct VbeInfoBlock *)0x2000; - struct ModeInfoBlock *inf = (struct ModeInfoBlock *)0x3000; - uint16_t *modes; - int i; +// /* https://wiki.osdev.org/User:Omarrx024/VESA_Tutorial*/ +// static char *find_display_memory(void) { +// struct VbeInfoBlock *ctrl = (struct VbeInfoBlock *)0x2000; +// struct ModeInfoBlock *inf = (struct ModeInfoBlock *)0x3000; +// uint16_t *modes; +// int i; - strncpy(ctrl->VbeSignature, "VBE2", 4); - if (get_vesa_controller_info(ctrl) != 0x004F) - return NULL; +// strncpy(ctrl->VbeSignature, "VBE2", 4); +// if (get_vesa_controller_info(ctrl) != 0x004F) +// return NULL; - modes = (uint16_t *)(ctrl->VideoModePtr); - for (i = 0; modes[i] != 0xFFFF; ++i) { - if (get_vesa_mode_info(modes[i], inf) != 0x004F) - continue; +// modes = (uint16_t *)(ctrl->VideoModePtr); +// for (i = 0; modes[i] != 0xFFFF; ++i) { +// if (get_vesa_mode_info(modes[i], inf) != 0x004F) +// continue; - // Check if this is a graphics mode with linear frame buffer support - if ((inf->attributes & 0x80) != 0x80) - continue; +// // Check if this is a graphics mode with linear frame buffer support +// if ((inf->attributes & 0x80) != 0x80) +// continue; - // Check if this is a packed pixel or direct color mode - if (inf->memory_model != 4 && inf->memory_model != 6) - continue; +// // Check if this is a packed pixel or direct color mode +// if (inf->memory_model != 4 && inf->memory_model != 6) +// continue; - // Check if this is exactly the mode we're looking for - if (WIDTH == inf->width && HEIGHT == inf->height && 256 == inf->pitch) - return (char *)inf->framebuffer; - } +// // Check if this is exactly the mode we're looking for +// if (WIDTH == inf->width && HEIGHT == inf->height && 256 == inf->pitch) +// return (char *)inf->framebuffer; +// } - return NULL; -} +// return NULL; +// } -extern int plr_display_dos_main(int argc, char *argv[]) { - char far *buf = (char far *)0xA0000; // VGA memory address +// extern int plr_display_dos_main(int argc, char *argv[]) { +extern int plr_display_dos_main(void) { + char *buf = (char *)0xA0000; // VGA memory address int x, y; - unsigned char saved_mode = get_video_mode(); // Save original mode - set_vesa_video_mode(MODE_VESA_640_480_8); // Set our VGA mode + // unsigned char saved_mode = get_video_mode(); // Save original mode + // set_vesa_video_mode(MODE_VESA_640_480_8); // Set our VGA mode - buf = find_display_memory(); + // buf = find_display_memory(); /* TODO: Fetch info about the mode, to know the availability */ /* as well as base address in memory */ /* https://wiki.osdev.org/VESA_Video_Modes */ - // Now draw. We draw pixel-by-pixel by directly setting the video memory like - // it was an array. We step the color every 25 pixels so that we have 8 bands - // of even height. - for (y = 0; y < HEIGHT; y++) { - for (x = 0; x < WIDTH; x++) { - buf[y * WIDTH + x] = COLOR_OFFSET + y / COLOR_STEP; + // Now draw. We draw pixel-by-pixel by directly setting the video memory + // like it was an array. We step the color every 25 pixels so that we have 8 + // bands of even height. + // for (y = 0; y < HEIGHT; y++) { + // for (x = 0; x < WIDTH; x++) { + // buf[y * WIDTH + x] = COLOR_OFFSET + y / COLOR_STEP; + // } + // } + + // wait_for_key(); + // set_video_mode(saved_mode); // Restore original mode + + while (1) { + for (y = 0; y < HEIGHT; y++) { + for (x = 0; x < WIDTH; x++) { + buf[y * WIDTH + x] = COLOR_OFFSET + y / COLOR_STEP; + } } } - wait_for_key(); - set_video_mode(saved_mode); // Restore original mode - return 0; } diff --git a/Player/Main/dos.s b/Player/Main/dos.s new file mode 100644 index 0000000..81849f5 --- /dev/null +++ b/Player/Main/dos.s @@ -0,0 +1,47 @@ +.section .data + .align 4 +# 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: + 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 --- diff --git a/Player/Makefile b/Player/Makefile index 71ca6a6..82573e9 100644 --- a/Player/Makefile +++ b/Player/Makefile @@ -3,8 +3,9 @@ RELEASE ?= 0 # Optimize. DEBUG ?= 1 # Emit debug info. SQUEEZE ?= 0 # Nuclear option to save space. BITS ?= 64 -CFLAGS = -Wall -std=gnu99 -flto=thin -ffast-math -fno-ident -fvisibility=hidden +CFLAGS = -Wall -std=gnu99 -flto=thin -ffast-math -fno-ident -fvisibility=hidden -fuse-ld=lld ifeq ($(MAKECMDGOALS),linux) + CFLAGS += -fno-pic -fno-pie ifeq ($(RELEASE), 1) CFLAGS += -Wl,--ignore-data-address-equality -Wl,--ignore-function-address-equality \ -Wl,--optimize-bb-jumps @@ -30,7 +31,7 @@ else CFLAGS += -DNDEBUG -Wl,--strip-all endif ifeq ($(RELEASE), 1) - CFLAGS += -O1 -fuse-ld=lld -fmerge-all-constants -Wl,-O3 \ + CFLAGS += -O3 -fmerge-all-constants -Wl,-O3 \ -ffunction-sections -fdata-sections -Wl,--gc-sections \ -Wl,-mllvm,--vectorize-slp=true \ -Wl,-mllvm,--enable-gvn-hoist=true @@ -49,6 +50,9 @@ DOS = maindos.c Display/dos.c %.o: %.c $(DEPS) $(CC) -c -o $@ $< $(CFLAGS) +%.o: %.s $(DEPS) + $(CC) -c -o $@ $< $(CFLAGS) + # TODO: Make it work with no stdlib. linux: CFLAGS += -static -mbmi2 -nostdlib linux: $(LINUX) | $(SRC) @@ -59,9 +63,14 @@ wasm: CFLAGS += --target=wasm32 -nostdlib -Wl,--no-entry -msimd128 -mbulk-memory wasm: $(CC) -o player.wasm $(WASM) $(CFLAGS) -# TODO: Use https://github.com/amindlost/pmodew -dos: $(DOS) $(DEPS) - wcl386 -bt=dos4g -ox -l=dos4g $(DOS) +# 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) + $(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 clean: rm -r *.o ../*.o diff --git a/Player/maindos.c b/Player/maindos.c deleted file mode 100644 index f21c1e5..0000000 --- a/Player/maindos.c +++ /dev/null @@ -1,5 +0,0 @@ -#include "Display/display.h" - -extern int main(int argc, char *argv[]) { - return plr_display_dos_main(argc, argv); -}