├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── example ├── Makefile └── main.c ├── include ├── conio.h ├── curses.h ├── dos.h ├── errno.h ├── math.h ├── rtctimer.h ├── stdarg.h ├── stddef.h ├── stdio.h ├── stdlib.h ├── string.h └── time.h ├── lib └── doscom.ld ├── mk ├── config.mk ├── doscom.mk ├── doslib.mk ├── flags.mk ├── objs.mk └── silent.mk └── src ├── conio.c ├── curses ├── curses_addch.c ├── curses_addstr.c ├── curses_bkgd.c ├── curses_border.c ├── curses_core.c ├── curses_erase.c ├── curses_printw.c └── curses_win.c ├── doscrt0.c ├── dosversion.c ├── errno.c ├── rtctimer.c ├── stdio ├── stdio_core.c └── stdio_printf.c ├── stdlib ├── stdlib_malloc.c └── stdlib_rand.c ├── string ├── string_mem.c ├── string_strcpy.c ├── string_strerror.c ├── string_strlen.c └── string_strtok.c └── time ├── time_completetm.c ├── time_getrtctm.c ├── time_mktime.c ├── time_readrtc.c └── time_time.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *~ 3 | *.a 4 | *.s 5 | *.o 6 | *.d 7 | *.com 8 | conf.mk 9 | asm/ 10 | obj/ 11 | 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Felix Palmen 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | 25 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: lib/libdos.a 2 | 3 | include mk/doslib.mk 4 | 5 | libdos_TARGET:= libdos.a 6 | libdos_TGTDIR:= lib 7 | libdos_SRCDIR:= src 8 | libdos_OBJDIR:= obj 9 | libdos_ASMDIR:= asm 10 | libdos_MODULES:= doscrt0 dosversion errno conio rtctimer \ 11 | time/time_completetm time/time_getrtctm time/time_mktime \ 12 | time/time_readrtc time/time_time \ 13 | stdlib/stdlib_malloc stdlib/stdlib_rand \ 14 | string/string_mem string/string_strerror string/string_strtok \ 15 | string/string_strcpy string/string_strlen \ 16 | stdio/stdio_core stdio/stdio_printf \ 17 | curses/curses_core curses/curses_win curses/curses_bkgd \ 18 | curses/curses_addch curses/curses_addstr curses/curses_erase \ 19 | curses/curses_printw curses/curses_border 20 | 21 | T:= libdos 22 | $(eval $(DOSLIBRULES)) 23 | 24 | clean: 25 | rm -f $(CLEAN) 26 | rm -fr asm 27 | rm -fr obj 28 | 29 | distclean: clean 30 | rm -f lib/libdos.a 31 | 32 | .PHONY: all clean distclean 33 | 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # clang-libdos 2 | 3 | This is work in progress, but the parts finished are usable. 4 | 5 | ### What's this? 6 | 7 | In short: A little collection trying to mimic the most basic parts of a C 8 | standard library for *real mode* DOS programs (`.COM` files). 9 | 10 | If you try creating a *real mode* DOS program, you either use some ages-old 11 | compilers (or, compilers using ages-old syntax ... *K&R* that is), or you're 12 | completely on your own, left with nothing but the BIOS and DOS *interrupt 13 | routines*, accessible through inline assembly. This random collection of code 14 | tries to fill the gap. 15 | 16 | `LLVM/clang` (and, more recently, `GCC`) provide the `-m16` switch for 17 | compiling to *real mode* code. It's a bit counter-intuitive, because this 18 | still creates code making use of all the *long* instructions and 32bit 19 | registers, so the code will not work on any processor that doesn't support the 20 | whole *i386* instruction set. It just means it will run in the 16bit-"based" 21 | *real mode*, so it's possible to create a DOS `.COM` executable with a little 22 | linker magic. Read [this very interesting blog post by Christopher 23 | Wellons](http://nullprogram.com/blog/2014/12/09/) for more details -- it's a 24 | great read and it was written before the `-m16` switch was introduced, so 25 | things were even more complicated. 26 | 27 | ### How does this relate to clang? 28 | 29 | Well, `GCC` seems still a bit more *buggy* with `-m16` than `clang`. `clang` 30 | already does a great job optimizing the code without breaking it for *real 31 | mode*. Apart from that, there's one issue: *real mode* applications typically 32 | use BIOS and DOS *interrupt routines*. Some BIOSes are buggy. There's at least 33 | one routine (the one for scrolling the console, so something needed regularly) 34 | I know of that clobbers the `ebp` (base pointer for the current stack frame) 35 | register on buggy BIOSes. Declaring that in your inline assembly, `GCC` just 36 | refuses to compile.[1] `clang` doesn't complain. As I don't know of any other 37 | *modern* compilers targeting *real mode*, this project is now tied to `clang`. 38 | 39 | ## Features 40 | 41 | - basic heap management (`malloc()` and friends). `realloc()` is currently 42 | untested, `calloc()` is not (yet?) supported. 43 | 44 | - A non-standard `conio.h` interface aiming to provide an API around the most 45 | commonly used `int 10h` BIOS routines. 46 | 47 | - A non-standard `rtctimer.h` providing a waitable timer on top of BIOS RTC 48 | timer routines. Values are given in microseconds, but the typical precision 49 | available is 977 µs. This doesn't seem to work with every BIOS (works in 50 | DOSBox, *doesn't* work in VirtualBox), so maybe replace with some assembly 51 | implementation using the RTC directly later ... 52 | 53 | - very limited `libc` functionality, including the `*printf()` family [2], 54 | error handling through `errno.h`, *some* `string.h` functions, *some* 55 | `time.h` functions, a very simple PRNG for `rand()` and *fake* stdio 56 | streams just for `stdin`, `stdout` and `stderr`. See the source for 57 | details. 58 | 59 | - **very** limited `curses.h` emulation, which could be enough for very basic 60 | curses-based programs. Handling of WINDOWs and comparison of physical and 61 | virtual screen *are* implemented, but be warned that this consumes memory 62 | ... a precious thing in a `.COM` file limited to 64KB. So, if you don't 63 | need curses, just leave `curses.o` out when linking. 64 | 65 | ## Why? 66 | 67 | Because we can. My goal right now is to implement enough *runtime foo* to get 68 | my [cursedsnake game](https://github.com/Zirias/cursedsnake) to compile to a 69 | working `.COM` file. Just for fun. **Update 2015-08-07**: This is finally 70 | working. Have a look at the `libdos.mk` Makefile over there on how to use this 71 | project in a real-world app/game. 72 | 73 | ## How to use? 74 | 75 | See the Makefile in this repository. It has some options enabled to save space 76 | like passing up to 3 arguments in registers and letting the linker eliminate 77 | unused code and data. At the time being, the `clang` available in Debian 78 | stable messes up at the assembler step with at least one instruction (`lea`) 79 | when compiling for *real mode*. This would lead to strange 80 | *[heisenbugs](http://stackoverflow.com/questions/31865276/)* in the resulting 81 | binary. So, for now, the Makefile lets `clang` only create optimized assembly 82 | source, which is then passed to the GNU assembler. 83 | 84 | **Important**: `core.o` **must** be the first object file on your linking 85 | stage commandline. It contains the startup code and as `.COM` doesn't have any 86 | headers, whatever comes first in the binary will be executed at startup. 87 | 88 | If you don't need commandline arguments in your program (so, if your main 89 | function is declared `int main(void)`), pass `-DNOARGV` while compiling to 90 | save a few bytes in the startup code. 91 | 92 | ## Footnotes 93 | 94 | - [1] The `Linux` kernel uses `GCC` for its bootcode. They came up with a big 95 | wrapper done in assembly around BIOS *interrupt routines* saving and 96 | restoring **all** registers, obviously being fed up with BIOS bugs. Of 97 | course, that's a nice approach for boot code, but in your *real mode* 98 | DOS application, you probably don't want to do such a thing. 99 | 100 | - [2] In the current implementation, the only format specifiers allowed are 101 | `s`, `c`, `d`, `u`, `x` and `X`. Flags for padding (` ` and `0`) and 102 | field-widths are understood as well as length specifiers from `hh` to 103 | `l`. No advanced features of `printf()` are supported and there's 104 | barely any error-checking, so be careful with your format strings. For 105 | `s` and `c`, any flags and field-widths are currently ignored. 106 | 107 | -------------------------------------------------------------------------------- /example/Makefile: -------------------------------------------------------------------------------- 1 | all: example.com 2 | 3 | LIBDOSPATH:= .. 4 | 5 | include $(LIBDOSPATH)/mk/doscom.mk 6 | 7 | example_TARGET:= example.com 8 | example_SRCDIR:= . 9 | example_MODULES:= main 10 | 11 | T:= example 12 | $(eval $(DOSCOMRULES)) 13 | 14 | clean: 15 | rm -f $(CLEAN) 16 | 17 | distclean: clean 18 | rm -f example.com 19 | 20 | .PHONY: all clean distclean 21 | 22 | -------------------------------------------------------------------------------- /example/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #ifdef NOARGV 10 | int main() 11 | { 12 | puts("Command line disabled."); 13 | #else 14 | int main(int argc, char **argv) 15 | { 16 | puts("Testing command line:"); 17 | for (int i = 0; i < argc; ++i) 18 | { 19 | printf("argv[%2d] = %s\n", i, argv[i]); 20 | } 21 | #endif 22 | puts("Any key to continue ..."); 23 | getch(); 24 | 25 | puts("Testing RTC:"); 26 | struct tm tm; 27 | if (getrtctm(&tm) < 0) 28 | { 29 | puts("Error reading RTC"); 30 | } 31 | else 32 | { 33 | printf("%04d-%02d-%02d %02d:%02d:%02d\n", tm.tm_year+1900, 34 | tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec); 35 | } 36 | puts("Any key to continue ..."); 37 | getch(); 38 | 39 | /* 40 | puts("Testing RTC timer ..."); 41 | for (int i = 0; i < 4; ++i) 42 | { 43 | rtctset(500000); 44 | rtctwait(); 45 | puts(" ..."); 46 | } 47 | */ 48 | 49 | initscr(); 50 | curs_set(0); 51 | 52 | init_pair(0, COLOR_WHITE, COLOR_BLACK); 53 | init_pair(1, COLOR_BLACK, COLOR_CYAN); 54 | init_pair(2, COLOR_WHITE, COLOR_BLUE); 55 | 56 | WINDOW *status = newwin(1, 0, 0, 0); 57 | WINDOW *field = newwin(0, 0, 1, 0); 58 | 59 | wbkgd(status, COLOR_PAIR(1)|A_BLINK); 60 | mvwaddstr(status, 0, 1, "example title bar"); 61 | wrefresh(status); 62 | wbkgd(field, COLOR_PAIR(0)|A_BLINK); 63 | wrefresh(field); 64 | 65 | WINDOW *dlg = subwin(field, 10, 40, 5, 20); 66 | wbkgd(dlg, COLOR_PAIR(2)|A_BOLD); 67 | box(dlg, 0, 0); 68 | mvwaddstr(dlg, 2, 12, "EXAMPLE DIALOG"); 69 | mvwprintw(dlg, 4, 2, "Test: %d", 42); 70 | double sqr2 = sqrt(2); 71 | mvwprintw(dlg, 5, 2, "Testing sqrt(): %u", 72 | (unsigned int)(sqr2 * 1000000000.0)); 73 | wrefresh(dlg); 74 | delwin(dlg); 75 | 76 | beep(); 77 | flash(); 78 | 79 | getch(); 80 | 81 | delwin(field); 82 | delwin(status); 83 | endwin(); 84 | 85 | putstr("rand() test -- ESC to quit:\n"); 86 | 87 | srand(time(0)); 88 | while (getch() != KEY_ESC) 89 | { 90 | printf("rand(): %d\n", rand()); 91 | } 92 | 93 | return 0; 94 | } 95 | -------------------------------------------------------------------------------- /include/conio.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBDOS_CONIO_H 2 | #define LIBDOS_CONIO_H 3 | 4 | int setpage(int page); 5 | int getpage(void); 6 | void setattr(int attr); 7 | void setscroll(int scroll); 8 | void setcursor(int show); 9 | void setblink(int blink); 10 | void setdelay(int msecs); 11 | void clrscr(void); 12 | void gotoxy(int x, int y); 13 | void getxy(int *x, int *y); 14 | int getch(void); 15 | void putchrp(int c, int repeat); 16 | void putch(int c); 17 | int putstr(const char *s); 18 | void beep(void); 19 | void flash(void); 20 | 21 | enum keys 22 | { 23 | KEY_UP = 0x4800, 24 | KEY_DOWN = 0x5000, 25 | KEY_LEFT = 0x4b00, 26 | KEY_RIGHT = 0x4d00, 27 | KEY_ENTER = 0x1c00, 28 | KEY_BS = 0x0e00, 29 | KEY_INS = 0x5200, 30 | KEY_DEL = 0x5300, 31 | KEY_HOME = 0x4700, 32 | KEY_END = 0x4f00, 33 | KEY_PGUP = 0x4900, 34 | KEY_PGDOWN = 0x5100, 35 | KEY_TAB = 0x0f00, 36 | KEY_ESC = 0x0100, 37 | KEY_F1 = 0x3b00, 38 | KEY_F2 = 0x3c00, 39 | KEY_F3 = 0x3d00, 40 | KEY_F4 = 0x3e00, 41 | KEY_F5 = 0x3f00, 42 | KEY_F6 = 0x4000, 43 | KEY_F7 = 0x4100, 44 | KEY_F8 = 0x4200, 45 | KEY_F9 = 0x4300, 46 | KEY_F10 = 0x4400, 47 | KEY_F11 = 0x8500, 48 | KEY_F12 = 0x8600, 49 | KEY_C_A = 0x1e00, 50 | KEY_C_B = 0x3000, 51 | KEY_C_C = 0x2e00, 52 | KEY_C_D = 0x2000, 53 | KEY_C_E = 0x1200, 54 | KEY_C_F = 0x2100, 55 | KEY_C_G = 0x2200, 56 | KEY_C_H = 0x2300, 57 | KEY_C_I = 0x1700, 58 | KEY_C_J = 0x2400, 59 | KEY_C_K = 0x2500, 60 | KEY_C_L = 0x2600, 61 | KEY_C_M = 0x3200, 62 | KEY_C_N = 0x3100, 63 | KEY_C_O = 0x1800, 64 | KEY_C_P = 0x1900, 65 | KEY_C_Q = 0x1000, 66 | KEY_C_R = 0x1300, 67 | KEY_C_S = 0x1f00, 68 | KEY_C_T = 0x1400, 69 | KEY_C_U = 0x1600, 70 | KEY_C_V = 0x2f00, 71 | KEY_C_W = 0x1100, 72 | KEY_C_X = 0x2d00, 73 | KEY_C_Y = 0x1500, 74 | KEY_C_Z = 0x2c00 75 | }; 76 | 77 | #define KEY_BREAK KEY_C_C 78 | #define KEY_BELL KEY_C_G 79 | #define KEY_LF KEY_C_J 80 | #define KEY_FF KEY_C_L 81 | #define KEY_CR KEY_C_M 82 | #define KEY_CUT KEY_C_X 83 | #define KEY_COPY KEY_C_C 84 | #define KEY_PASTE KEY_C_V 85 | 86 | #define COL_PAIR(fg, bg) ((bg)<<4 | (fg)) 87 | 88 | enum colors 89 | { 90 | COL_BLACK = 0, 91 | COL_BLUE, 92 | COL_GREEN, 93 | COL_CYAN, 94 | COL_RED, 95 | COL_MAGENTA, 96 | COL_BROWN, 97 | COL_LIGHTGRAY, 98 | COL_GRAY, 99 | COL_LIGHTBLUE, 100 | COL_LIGHTGREEN, 101 | COL_LIGHTCYAN, 102 | COL_LIGHTRED, 103 | COL_LIGHTMAGENTA, 104 | COL_YELLOW, 105 | COL_WHITE 106 | }; 107 | 108 | #endif 109 | -------------------------------------------------------------------------------- /include/curses.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBDOS_CURSES_H 2 | #define LIBDOS_CURSES_H 3 | 4 | #include 5 | #include 6 | 7 | typedef unsigned short chtype; 8 | 9 | typedef struct WINDOW WINDOW; 10 | struct WINDOW 11 | { 12 | int row; 13 | int col; 14 | int rows; 15 | int cols; 16 | int y; 17 | int x; 18 | WINDOW *parent; 19 | chtype bkgd; 20 | chtype data[80*25]; 21 | }; 22 | 23 | extern WINDOW * const stdscr; 24 | extern chtype colpairs[]; 25 | 26 | #define COLOR_BLACK 0 27 | #define COLOR_RED 1 28 | #define COLOR_GREEN 2 29 | #define COLOR_YELLOW 3 30 | #define COLOR_BLUE 4 31 | #define COLOR_MAGENTA 5 32 | #define COLOR_CYAN 6 33 | #define COLOR_WHITE 7 34 | 35 | #define timeout(x) setdelay((x)) 36 | #define wgetch(x) getch() 37 | #define curs_set(x) setcursor((x)) 38 | 39 | #define raw() 40 | #define nonl() 41 | #define noecho() 42 | #define keypad(x, y) 43 | #define start_color() 44 | #define leaveok(x, y) 45 | 46 | #define ERR -1 47 | #define OK 0 48 | #define COLOR_PAIRS 64 49 | 50 | #define COLOR_PAIR(x) colpairs[(x)] 51 | 52 | #define getyx(win, uy, ux) do { (uy) = (win)->y; (ux) = (win)->x; } while (0) 53 | #define getparyx(win, uy, ux) do { \ 54 | if (!((win)->parent)) { (uy) = -1; (ux) = -1; } \ 55 | else { (uy) = (win)->row - (win)->parent->row; \ 56 | (ux) = (win)->col - (win)->parent->col; } \ 57 | } while (0) 58 | #define getbegyx(win, uy, ux) \ 59 | do { (uy) = (win)->row; (ux) = (win)->col; } while (0) 60 | #define getmaxyx(win, uy, ux) \ 61 | do { (uy) = (win)->rows; (ux) = (win)->cols; } while (0) 62 | 63 | #define wmove(win, uy, ux) do { (win)->y = (uy); (win)->x = (ux); } while (0) 64 | #define move(uy, ux) wmove((uy), (ux)) 65 | 66 | #define A_BOLD 0x0800U 67 | #define A_BLINK 0x8000U 68 | #define A_ALTCHARSET 0 69 | 70 | enum acs 71 | { 72 | ACS_BLOCK = 0xdb, 73 | ACS_BOARD = 0xfe, 74 | ACS_BTEE = 0xc1, 75 | ACS_BULLET = 0x07, 76 | ACS_CKBOARD = 0xb1, 77 | ACS_DARROW = 0x19, 78 | ACS_DEGREE = 0xf8, 79 | ACS_DIAMOND = 0x04, 80 | ACS_GEQUAL = 0x3e, 81 | ACS_HLINE = 0xc4, 82 | ACS_LANTERN = 0x0f, 83 | ACS_LARROW = 0x1b, 84 | ACS_LEQUAL = 0x3c, 85 | ACS_LLCORNER = 0xc0, 86 | ACS_LRCORNER = 0xd9, 87 | ACS_LTEE = 0xc3, 88 | ACS_NEQUAL = 0x21, 89 | ACS_PI = 0x14, 90 | ACS_PLMINUS = 0xf1, 91 | ACS_PLUS = 0x2b, 92 | ACS_RARROW = 0x1a, 93 | ACS_RTEE = 0xb4, 94 | ACS_S1 = 0xee, 95 | ACS_S3 = 0xc4, 96 | ACS_S7 = 0xc4, 97 | ACS_S9 = 0xf2, 98 | ACS_STERLING = 0x66, 99 | ACS_TTEE = 0xc2, 100 | ACS_UARROW = 0x18, 101 | ACS_ULCORNER = 0xda, 102 | ACS_URCORNER = 0xbf, 103 | ACS_VLINE = 0xb3 104 | }; 105 | 106 | WINDOW *initscr(void); 107 | int endwin(void); 108 | 109 | WINDOW *newwin(int nlines, int ncols, int begin_y, int begin_x); 110 | WINDOW *subwin(WINDOW *orig, int nlines, int nclos, int begin_y, int begin_x); 111 | 112 | int delwin(WINDOW *win); 113 | 114 | #define refresh() wrefresh(stdscr) 115 | int wrefresh(WINDOW *win); 116 | int wnoutrefresh(WINDOW *win); 117 | int doupdate(void); 118 | 119 | #define bkgd(x) wbkgd(stdscr, (x)) 120 | int wbkgd(WINDOW *win, chtype ch); 121 | 122 | #define clear() wclear(stdscr) 123 | #define wclear(x) werase((x)) 124 | #define erase() werase(stdscr) 125 | int werase(WINDOW *win); 126 | 127 | #define addch(x) waddch(stdscr, (x)) 128 | int waddch(WINDOW *win, chtype ch); 129 | #define mvaddch(y, x, ch) mvwaddch(stdscr, (y), (x), (ch)) 130 | int mvwaddch(WINDOW *win, int y, int x, const chtype ch); 131 | 132 | #define border(ls, rs, ts, bs, tl, tr, bl, br) \ 133 | wborder(stdscr, (ls), (rs), (ts), (bs), (tl), (tr), (bl), (br)) 134 | #define box(win, verch, horch) \ 135 | wborder((win), (verch), (verch), (horch), (horch), 0, 0, 0, 0) 136 | int wborder(WINDOW *win, chtype ls, chtype rs, chtype ts, chtype bs, 137 | chtype tl, chtype tr, chtype bl, chtype br); 138 | 139 | #define addstr(str) waddstr(stdscr, (str)) 140 | #define addnstr(str, n) waddnstr(stdscr, (str), (n)) 141 | #define waddstr(win, str) waddnstr((win), (str), -1) 142 | #define waddnstr(win, str, n) mvwaddnstr((win), -1, -1, (str), (n)) 143 | #define mvaddstr(y, x, str) mvwaddstr(stdscr, (y), (x), (str)) 144 | #define mvaddnstr(y, x, str, n) mvwaddnstr(stdscr, (y), (x), (str), (n)) 145 | #define mvwaddstr(win, y, x, str) mvwaddnstr((win), (y), (x), (str), -1) 146 | int mvwaddnstr(WINDOW *win, int y, int x, const char *str, int n); 147 | 148 | #define printw(fmt, ...) wprintw(stdscr, (fmt), __VA_ARGS__) 149 | #define wprintw(win, fmt, ...) mvwprintw((win), -1, -1, (fmt), __VA_ARGS__) 150 | #define mvprintw(y, x, fmt, ...) \ 151 | mvwprintw(stdscr, (y), (x), (fmt), __VA_ARGS__) 152 | int mvwprintw(WINDOW *win, int y, int x, const char *fmt, ...); 153 | #define vwprintw(win, fmt, varglist) vw_printw((win), (fmt), (varglist)) 154 | int vw_printw(WINDOW *win, const char *fmt, va_list varglist); 155 | 156 | int init_pair(short pair, short f, short b); 157 | 158 | #endif 159 | -------------------------------------------------------------------------------- /include/dos.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBDOS_DOS_H 2 | #define LIBDOS_DOS_H 3 | 4 | unsigned short dosversion(void); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /include/errno.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBDOS_ERRNO_H 2 | #define LIBDOS_ERRNO_H 3 | 4 | extern int errno; 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /include/math.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBDOS_MATH_H 2 | #define LIBDOS_MATH_H 3 | 4 | static inline double sqrt(double x) 5 | { 6 | __asm__ ( 7 | "fldl %0 \n\t" \ 8 | "fsqrt \n\t" \ 9 | "fstpl %0 \n\t" \ 10 | : "=m" ((x)) 11 | : "m" ((x)) 12 | ); 13 | return x; 14 | } 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /include/rtctimer.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBDOS_RTCTIMER_H 2 | #define LIBDOS_RTCTIMER_H 3 | 4 | #include "stddef.h" 5 | 6 | /* typical precision: 977 usecs */ 7 | int rtctset(unsigned int usecs); 8 | int rtctstop(void); 9 | int rtctwait(void); 10 | int rtctpoll(void); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /include/stdarg.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBDOS_STDARG_H 2 | #define LIBDOS_STDARG_H 3 | 4 | #include "stddef.h" 5 | 6 | #ifndef _VA_LIST 7 | typedef __builtin_va_list va_list; 8 | #define _VA_LIST 9 | #endif 10 | #define va_start(ap, param) __builtin_va_start(ap, param) 11 | #define va_end(ap) __builtin_va_end(ap) 12 | #define va_arg(ap, type) __builtin_va_arg(ap, type) 13 | #define va_copy(dest, src) __builtin_va_copy(dest, src) 14 | 15 | int vprintf(const char *format, va_list ap); 16 | int vfprintf(FILE *stream, const char *format, va_list ap); 17 | int vsprintf(char *str, const char *format, va_list ap); 18 | int vsnprintf(char *str, size_t size, const char *format, va_list ap); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /include/stddef.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBDOS_STDDEF_H 2 | #define LIBDOS_STDDEF_H 3 | 4 | typedef int const FILE; 5 | typedef unsigned short size_t; 6 | 7 | enum 8 | { 9 | EINVAL = 1, 10 | ENOSYS, 11 | ENODEV, 12 | ECANCELED, 13 | EBUSY, 14 | ENOMEM 15 | }; 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /include/stdio.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBDOS_STDIO_H 2 | #define LIBDOS_STDIO_H 3 | 4 | #include "stddef.h" 5 | 6 | #define EOF -1 7 | 8 | extern const FILE *stdin; 9 | extern const FILE *stdout; 10 | extern const FILE *stderr; 11 | 12 | int printf(const char *format, ...) 13 | __attribute__((format(__printf__, 1, 2))); 14 | 15 | int fprintf(FILE *stream, const char *format, ...) 16 | __attribute__((format(__printf__, 2, 3))); 17 | 18 | int sprintf(char *str, const char *format, ...) 19 | __attribute__((format(__printf__, 2, 3))); 20 | 21 | int snprintf(char *str, size_t size, const char *format, ...) 22 | __attribute__((format(__printf__, 3, 4))); 23 | 24 | int puts(const char *s); 25 | int fputs(const char *s, FILE *stream); 26 | void perror(const char *s); 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /include/stdlib.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBDOS_STDLIB_H 2 | #define LIBDOS_STDLIB_H 3 | 4 | #include "stddef.h" 5 | 6 | #define RAND_MAX 0x7fffffff 7 | 8 | void exit(int status) __attribute__((__noreturn__)); 9 | 10 | void *malloc(size_t size); 11 | void free(void *ptr); 12 | void *realloc(void *ptr, size_t size); 13 | #define calloc(nmemb, size) memset(malloc((nmemb)*(size)), 0, (nmemb)*(size)) 14 | 15 | int rand(void); 16 | void srand(unsigned int seed); 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /include/string.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBDOS_STRING_H 2 | #define LIBDOS_STRING_H 3 | 4 | #include "stddef.h" 5 | 6 | size_t strlen(const char *s); 7 | char *strcpy(char *dest, const char *src); 8 | char *strtok(char *str, const char *delim); 9 | void *memcpy(void *dest, const void *src, size_t n); 10 | void *memset(void *s, int c, size_t n); 11 | char *strerror(int errnum); 12 | 13 | /* ---- NON-STANDARD ---- */ 14 | 15 | /* works like strtok(), but handles escapes and quotes 16 | * added for command-line parsing, but might be useful for other code. 17 | * HINT: inside a quote, an escape is only considered for the quoting chr */ 18 | char *strqetok(char *str, const char *quot, const char *esc, const char *delim); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /include/time.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBDOS_TIME_H 2 | #define LIBDOS_TIME_H 3 | 4 | #include "stddef.h" 5 | 6 | typedef int time_t; 7 | 8 | struct tm 9 | { 10 | int tm_sec; 11 | int tm_min; 12 | int tm_hour; 13 | int tm_mday; 14 | int tm_mon; 15 | int tm_year; 16 | int tm_wday; 17 | int tm_yday; 18 | int tm_isdst; 19 | }; 20 | 21 | int readrtc(struct tm *tm); 22 | void completetm(struct tm *tm); 23 | int getrtctm(struct tm *tm); 24 | time_t mktime(struct tm *tm); 25 | time_t time(time_t *t); 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /lib/doscom.ld: -------------------------------------------------------------------------------- 1 | OUTPUT_FORMAT(binary) 2 | ENTRY(__com__start) 3 | SECTIONS 4 | { 5 | . = 0x0100; 6 | .text : 7 | { 8 | *doscrt0.o(.comstartup); 9 | *(.text .text.*); 10 | } 11 | .data : 12 | { 13 | *(.data .data.*); 14 | *(.rodata .rodata.*); 15 | *(COMMON); 16 | *(.bss .bss.*); 17 | } 18 | /DISCARD/ : 19 | { 20 | *(.note*); 21 | *(.comment*); 22 | } 23 | _heap = ALIGN(4); 24 | } 25 | 26 | -------------------------------------------------------------------------------- /mk/config.mk: -------------------------------------------------------------------------------- 1 | #default config 2 | USECC?= gcc 3 | USEAS?= as 4 | USEREGPARM?= 1 5 | USEARGV?= 1 6 | 7 | # save / compare config 8 | ifneq ($(MAKECMDGOALS),clean) 9 | ifneq ($(MAKECMDGOALS),distclean) 10 | conf.mk: 11 | $(VGENT) 12 | $(VR)echo "C_USECC := $(USECC)" >conf.mk 13 | $(VR)echo "C_USEAS := $(USEAS)" >>conf.mk 14 | $(VR)echo "C_USEREGPARM := $(USEREGPARM)" >>conf.mk 15 | $(VR)echo "C_USEARGV := $(USEARGV)" >>conf.mk 16 | 17 | -include conf.mk 18 | 19 | ifneq ($(strip $(C_USECC))_$(strip $(C_USEAS))_$(strip $(C_USEREGPARM))_$(strip $(C_USEARGV)),$(strip $(USECC))_$(strip $(USEAS))_$(strip $(USEREGPARM))_$(strip $(USEARGV))) 20 | .PHONY: conf.mk 21 | endif 22 | endif 23 | endif 24 | 25 | ifneq ($(strip $(USECC)),) 26 | CC:= $(USECC) 27 | endif 28 | ifneq ($(strip $(USEAS)),) 29 | AS:= $(USEAS) 30 | endif 31 | VTAGS+= [$(CC)] 32 | 33 | CFLAGS?= -Wall -Wextra -pedantic 34 | ifeq ($(USEREGPARM),1) 35 | CFLAGS+= -mregparm=3 36 | VTAGS+= [REGPARM] 37 | else 38 | VTAGS+= [STACKPARM] 39 | endif 40 | ifeq ($(USEARGV),1) 41 | VTAGS+= [ARGV] 42 | else 43 | CFLAGS+= -DNOARGV 44 | VTAGS+= [NOARGV] 45 | endif 46 | 47 | CLEAN+= conf.mk 48 | 49 | -------------------------------------------------------------------------------- /mk/doscom.mk: -------------------------------------------------------------------------------- 1 | LIBDOSPATH?= . 2 | 3 | include $(LIBDOSPATH)/mk/silent.mk 4 | include $(LIBDOSPATH)/mk/config.mk 5 | include $(LIBDOSPATH)/mk/flags.mk 6 | include $(LIBDOSPATH)/mk/objs.mk 7 | 8 | define DOSCOMRULES 9 | $(OBJRULES) 10 | 11 | $$($(T)_TGTDIR)/$$($(T)_TARGET): $$($(T)_OBJS) | $$($(T)_TGTDIR) 12 | $$(VCCLD) 13 | $$(VR)$$(CC) -o$$@ $$($(T)_CFLAGS) $$(CFLAGS) \ 14 | $$($(T)_LDFLAGS) $$(LDFLAGS) $$($(T)_OBJS) \ 15 | $$($(T)_LIBRARIES) $$(LIBRARIES) 16 | 17 | $$($(T)_TGTDIR)/$$($(T)_TARGET): $$(LIBDOSPATH)/lib/libdos.a 18 | 19 | ifneq ($$(strip $$(wildcard $$(LIBDOSPATH)/Makefile)),) 20 | export USEREGPARM 21 | export USEARGV 22 | $$(LIBDOSPATH)/lib/libdos.a: conf.mk 23 | $$(MAKE) -C $$(LIBDOSPATH) 24 | $$(VR)touch $$(LIBDOSPATH)/lib/libdos.a 25 | endif 26 | 27 | endef 28 | 29 | -------------------------------------------------------------------------------- /mk/doslib.mk: -------------------------------------------------------------------------------- 1 | LIBDOSPATH?= . 2 | 3 | include $(LIBDOSPATH)/mk/silent.mk 4 | include $(LIBDOSPATH)/mk/config.mk 5 | include $(LIBDOSPATH)/mk/flags.mk 6 | include $(LIBDOSPATH)/mk/objs.mk 7 | 8 | define DOSLIBRULES 9 | $(OBJRULES) 10 | 11 | $$($(T)_TGTDIR)/$$($(T)_TARGET): $$($(T)_OBJS) | $$($(T)_TGTDIR) 12 | $$(VAR) 13 | $$(VR)rm -f $$@ 14 | $$(VR)ar rcs $$@ $$^ 15 | 16 | endef 17 | -------------------------------------------------------------------------------- /mk/flags.mk: -------------------------------------------------------------------------------- 1 | # base flags needed for building .COM files with clang-libdos 2 | 3 | CFLAGS+= -std=c99 -Os -nostdlib -m16 -march=i386 -ffreestanding -fno-builtin -fno-pic -fno-pie -fno-strict-aliasing -fomit-frame-pointer -fno-stack-protector -mno-mmx -mno-sse 4 | INCLUDES+= -nostdinc -I$(LIBDOSPATH)/include 5 | LDFLAGS+= -Wl,--omagic,-melf_i386,--script=$(LIBDOSPATH)/lib/doscom.ld \ 6 | -L$(LIBDOSPATH)/lib -fuse-ld=bfd 7 | LIBRARIES+= -ldos 8 | 9 | -------------------------------------------------------------------------------- /mk/objs.mk: -------------------------------------------------------------------------------- 1 | define OBJRULES 2 | 3 | $(T)_TGTDIR?= $$($(T)_SRCDIR) 4 | $(T)_ASMDIR?= $$($(T)_SRCDIR) 5 | $(T)_OBJDIR?= $$($(T)_SRCDIR) 6 | 7 | $(T)_SOURCES:= $$(addprefix $$($(T)_SRCDIR)/, \ 8 | $$(addsuffix .c,$$($(T)_MODULES))) 9 | $(T)_OBJS:= $$(addprefix $$($(T)_OBJDIR)/, \ 10 | $$(addsuffix .o,$$($(T)_MODULES))) 11 | 12 | CLEAN+= $$($(T)_OBJS:.o=.d) $$($(T)_OBJS) 13 | 14 | ifneq ($$(strip $$($(T)_TGTDIR)),$$(strip $$($(T)_SRCDIR))) 15 | $$($(T)_TGTDIR): 16 | $$(VMD) 17 | $$(VR)mkdir -p $$(addprefix $$($(T)_TGTDIR)/, $$(dir $$($(T)_MODULES))) 18 | 19 | endif 20 | ifneq ($$(strip $$($(T)_OBJDIR)),$$(strip $$($(T)_SRCDIR))) 21 | $$($(T)_OBJDIR): 22 | $$(VMD) 23 | $$(VR)mkdir -p $$(addprefix $$($(T)_OBJDIR)/, $$(dir $$($(T)_MODULES))) 24 | 25 | endif 26 | 27 | %.o: %.c 28 | 29 | $$($(T)_OBJDIR)/%.d: $$($(T)_SRCDIR)/%.c Makefile conf.mk | $$($(T)_OBJDIR) 30 | $$(VDEP) 31 | $$(VR)$$(CC) -MM -MT"$$@ $$(@:.d=.o)" -MF$$@ \ 32 | $$($(T)_CFLAGS) $$(CFLAGS) $$($(T)_INCLUDES) $$(INCLUDES) $$< 33 | 34 | ifneq ($$(MAKECMDGOALS),clean) 35 | ifneq ($$(MAKECMDGOALS),distclean) 36 | -include $$($(T)_OBJS:.o=.d) 37 | endif 38 | endif 39 | 40 | $$($(T)_OBJDIR)/%.o: $$($(T)_SRCDIR)/%.c Makefile conf.mk | $$($(T)_OBJDIR) 41 | $$(VCC) 42 | $$(VR)$$(CC) -c -o$$@ $$($(T)_CFLAGS) $$(CFLAGS) \ 43 | $$($(T)_INCLUDES) $$(INCLUDES) $$< 44 | 45 | endef 46 | -------------------------------------------------------------------------------- /mk/silent.mk: -------------------------------------------------------------------------------- 1 | V?= 0 2 | 3 | ifeq ($(V),1) 4 | VCC:= 5 | VAS:= 6 | VDEP:= 7 | VLD:= 8 | VAR:= 9 | VCCLD:= 10 | VMD:= 11 | VGEN:= 12 | VGENT:= 13 | VR:= 14 | else 15 | VCC= @echo " [CC] $@" 16 | VAS= @echo " [AS] $@" 17 | VDEP= @echo " [DEP] $@" 18 | VLD= @echo " [LD] $@" 19 | VAR= @echo " [AR] $@" 20 | VCCLD= @echo " [CCLD] $@" 21 | VMD= @echo " [MD] $@" 22 | VGEN= @echo " [GEN] $@" 23 | VGENT= @echo " [GEN] $@: $(VTAGS)" 24 | VR:= @ 25 | endif 26 | 27 | -------------------------------------------------------------------------------- /src/conio.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static int cpage = 0; 5 | static int crow; 6 | static int ccol; 7 | static unsigned short ccsh; 8 | static int cattr = 0x07; 9 | static int cscroll = 1; 10 | static int delay = -1; 11 | 12 | void _getcinfo(void) 13 | { 14 | unsigned int pos; 15 | __asm__ ( 16 | "mov $0x0300, %%ax \n\t" 17 | "int $0x10 \n\t" 18 | : "=c" (ccsh), "=d" (pos) 19 | : "b" (cpage) 20 | : "ax" 21 | ); 22 | ccol = pos & 0xff; 23 | crow = (pos >> 8) & 0xff; 24 | 25 | } 26 | 27 | static void _gotoxy(void) 28 | { 29 | __asm__ volatile ( 30 | "mov $0x02, %%ah \n\t" 31 | "int $0x10 \n\t" 32 | : 33 | : "b" (cpage << 8), "d" (crow << 8 | ccol) 34 | : "ax" 35 | ); 36 | } 37 | 38 | static void _scroll(int lines) 39 | { 40 | __asm__ volatile ( 41 | "int $0x10 \n\t" 42 | : 43 | : "a" (lines | 0x0600), "b" (cattr << 8), 44 | "c" (0), "d" (24 << 8 | 79) 45 | : "ebp" 46 | ); 47 | } 48 | 49 | int setpage(int page) 50 | { 51 | if (page == cpage) return page; 52 | __asm__ ( 53 | "mov $0x05, %%ah \n\t" 54 | "int $0x10 \n\t" 55 | "mov $0x0f, %%ah \n\t" 56 | "int $0x10 \n\t" 57 | : "=b" (page) 58 | : "a" (page) 59 | ); 60 | page = (page >> 8) & 0xff; 61 | if (page != cpage) 62 | { 63 | _getcinfo(); 64 | cpage = page; 65 | } 66 | return page; 67 | } 68 | 69 | int getpage(void) 70 | { 71 | return cpage; 72 | } 73 | 74 | void setattr(int attr) 75 | { 76 | cattr = attr; 77 | } 78 | 79 | void setscroll(int scroll) 80 | { 81 | cscroll = scroll; 82 | } 83 | 84 | void setcursor(int show) 85 | { 86 | if (show) ccsh &= 0x1fff; 87 | else ccsh |= 0x2000; 88 | __asm__ volatile ( 89 | "mov $0x01, %%ah \n\t" 90 | "int $0x10 \n\t" 91 | : 92 | : "c" (ccsh) 93 | ); 94 | } 95 | 96 | void setblink(int blink) 97 | { 98 | __asm__ volatile ( 99 | "mov $0x1003, %%ax \n\t" 100 | "int $0x10 \n\t" 101 | : 102 | : "b" (!!blink) 103 | ); 104 | } 105 | 106 | void setdelay(int msecs) 107 | { 108 | delay = msecs; 109 | } 110 | 111 | void clrscr(void) 112 | { 113 | _scroll(0); 114 | gotoxy(0,0); 115 | } 116 | 117 | void gotoxy(int x, int y) 118 | { 119 | crow = y; 120 | ccol = x; 121 | _gotoxy(); 122 | } 123 | 124 | void getxy(int *x, int *y) 125 | { 126 | *x = ccol; 127 | *y = crow; 128 | } 129 | 130 | static int _getch(void) 131 | { 132 | int ch; 133 | __asm__ ( 134 | "mov $0x10, %%ah \n\t" 135 | "int $0x16 \n\t" 136 | "and $0xffff, %%eax \n\t" 137 | : "=a" (ch) 138 | ); 139 | return ch; 140 | } 141 | 142 | static int _checkch(void) 143 | { 144 | int key; 145 | __asm__ ( 146 | "movl $0xffffffff, %0 \n\t" 147 | "mov $0x11, %%ah \n\t" 148 | "int $0x16 \n\t" 149 | "je 1f \n\t" 150 | "movzwl %%ax, %0 \n" 151 | "1: \n\t" 152 | : "=r" (key) 153 | : 154 | : "ax" 155 | ); 156 | return key; 157 | } 158 | 159 | int getch(void) 160 | { 161 | int ch; 162 | 163 | if (delay < 0) goto havekey; 164 | else if (delay) 165 | { 166 | if (rtctset((unsigned int)delay * 1000) < 0) return -1; 167 | while (!rtctpoll()) 168 | { 169 | if (_checkch() >= 0) 170 | { 171 | rtctstop(); 172 | goto havekey; 173 | } 174 | __asm__ volatile ("hlt"); 175 | } 176 | } 177 | else if (_checkch() >= 0) goto havekey; 178 | return -1; 179 | 180 | havekey: 181 | ch = _getch(); 182 | if (((ch&0xff) >= ' ') && ((ch&0xff) <= '~')) ch &= 0xff; 183 | else ch &= 0xff00; 184 | return ch; 185 | } 186 | 187 | void putchrp(int c, int repeat) 188 | { 189 | unsigned short attr; 190 | if (c & 0xff00) attr = c >> 8; 191 | else attr = cattr; 192 | __asm__ volatile ( 193 | "mov $0x09, %%ah \n\t" 194 | "int $0x10 \n\t" 195 | : "+c" (repeat) 196 | : "a" (c), "b" ((cpage << 8) | attr) 197 | ); 198 | } 199 | 200 | void putch(int c) 201 | { 202 | putchrp(c, 1); 203 | } 204 | 205 | int putstr(const char *s) 206 | { 207 | int l = 0; 208 | const char *p = s; 209 | 210 | while (*p) 211 | { 212 | switch (*p) 213 | { 214 | case '\r': 215 | ccol = 0; 216 | _gotoxy(); 217 | break; 218 | 219 | case '\n': 220 | ccol = 0; 221 | if (crow < 24) ++crow; 222 | else if (cscroll) _scroll(1); 223 | _gotoxy(); 224 | break; 225 | 226 | default: 227 | putch((unsigned char)*p); 228 | if (++ccol > 79) 229 | { 230 | ccol = 0; 231 | if (crow < 24) ++crow; 232 | else if (cscroll) _scroll(1); 233 | } 234 | _gotoxy(); 235 | break; 236 | } 237 | ++p; 238 | ++l; 239 | } 240 | return l; 241 | } 242 | 243 | void beep(void) 244 | { 245 | __asm__ volatile( 246 | "mov $0x0e07, %%ax \n\t" 247 | "int $0x10 \n\t" 248 | : 249 | : "b" (cpage << 8) 250 | : "ax" 251 | ); 252 | } 253 | 254 | void flash(void) 255 | { 256 | int srow = crow; 257 | int scol = ccol; 258 | for (int i = 0; i < 2; ++i) 259 | { 260 | for (crow = 0; crow < 25; crow ++) 261 | { 262 | for (ccol = 0; ccol < 80; ccol ++) 263 | { 264 | _gotoxy(); 265 | __asm__ volatile ( 266 | "mov $0x08, %%ah \n\t" 267 | "int $0x10 \n\t" 268 | "ror $4, %%ah \n\t" 269 | "mov %%ah, %%bl \n\t" 270 | "mov $1, %%cx \n\t" 271 | "mov $0x09, %%ah \n\t" 272 | "int $0x10 \n\t" 273 | : 274 | : "b" (cpage << 8) 275 | : "ax", "cx" 276 | ); 277 | } 278 | } 279 | } 280 | crow = srow; 281 | ccol = scol; 282 | _gotoxy(); 283 | } 284 | 285 | -------------------------------------------------------------------------------- /src/curses/curses_addch.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int waddch(WINDOW *win, const chtype ch) 4 | { 5 | if (win->parent) 6 | { 7 | return mvwaddch(win->parent, win->row - win->parent->row + win->y, 8 | win->col - win->parent->col + win->x, ch); 9 | } 10 | if (ch & 0xff00) 11 | { 12 | win->data[win->y * win->cols + win->x] = ch; 13 | } 14 | else 15 | { 16 | win->data[win->y * win->cols + win->x] = 17 | (win->data[win->y * win->cols + win->x] & 0xff00) | ch; 18 | } 19 | ++win->x; 20 | if (win->x >= win->cols) 21 | { 22 | win->x = 0; 23 | if (win->y < win->rows-1) ++win->y; 24 | } 25 | return OK; 26 | } 27 | 28 | int mvwaddch(WINDOW *win, int y, int x, const chtype ch) 29 | { 30 | wmove(win, y, x); 31 | return waddch(win, ch); 32 | } 33 | 34 | -------------------------------------------------------------------------------- /src/curses/curses_addstr.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static void _waddnstr(WINDOW *win, int idx, const char *str, int n) 4 | { 5 | int wr = 0; 6 | const char *p = str; 7 | 8 | while (*p && wr != n) 9 | { 10 | win->data[idx] = (win->data[idx]&0xff00) | (unsigned char)*p++; 11 | ++idx; 12 | ++wr; 13 | } 14 | win->x += wr; 15 | while (win->x > win->cols) 16 | { 17 | if (win->y < win->rows-2) ++win->y; 18 | win->x -= win->cols; 19 | } 20 | } 21 | 22 | int mvwaddnstr(WINDOW *win, int y, int x, const char *str, int n) 23 | { 24 | if (y >= 0 && x >= 0) wmove(win, y, x); 25 | if (win->parent) _waddnstr(win->parent, 26 | (win->y+win->row-win->parent->row)*win->parent->cols 27 | +win->x+win->col-win->parent->col, str, n); 28 | else _waddnstr(win, win->cols*win->y+win->x, str, n); 29 | 30 | return OK; 31 | } 32 | 33 | -------------------------------------------------------------------------------- /src/curses/curses_bkgd.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static void _wbkgd(WINDOW *win, chtype ch, int sr, int sc, int nr, int nc) 4 | { 5 | for (int r = sr; r < sr + nr; ++r) 6 | { 7 | for (int c = sc; c < sc + nc; ++c) 8 | { 9 | if ((win->data[r*win->cols+c] & 0xff) == (win->bkgd & 0xff)) 10 | { 11 | win->data[r*win->cols+c] = ch; 12 | } 13 | else 14 | { 15 | win->data[r*win->cols+c] = 16 | (win->data[r*win->cols+c] & 0xff) | (ch & 0xff00); 17 | } 18 | } 19 | } 20 | } 21 | 22 | int wbkgd(WINDOW *win, chtype ch) 23 | { 24 | if (win->parent) _wbkgd(win->parent, ch, 25 | win->row - win->parent->row, win->col - win->parent->col, 26 | win->rows, win->cols); 27 | else _wbkgd(win, ch, 0, 0, win->rows, win->cols); 28 | win->bkgd = ch; 29 | return OK; 30 | } 31 | 32 | -------------------------------------------------------------------------------- /src/curses/curses_border.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static void _wborder(WINDOW *win, chtype ls, chtype rs, chtype ts, chtype bs, 4 | chtype tl, chtype tr, chtype bl, chtype br, int r, int c, int h, int w) 5 | { 6 | if (!ls) ls = ACS_VLINE; 7 | if (!rs) rs = ACS_VLINE; 8 | if (!ts) ts = ACS_HLINE; 9 | if (!bs) bs = ACS_HLINE; 10 | if (!tl) tl = ACS_ULCORNER; 11 | if (!tr) tr = ACS_URCORNER; 12 | if (!bl) bl = ACS_LLCORNER; 13 | if (!br) br = ACS_LRCORNER; 14 | 15 | win->data[r*win->cols+c] = (win->data[r*win->cols+c]&0xff00) | tl; 16 | win->data[r*win->cols+c+w-1] = (win->data[r*win->cols+c+w-1]&0xff00) | tr; 17 | win->data[(r+h-1)*win->cols+c] = 18 | (win->data[(r+h-1)*win->cols+c]&0xff00) | bl; 19 | win->data[(r+h-1)*win->cols+c+w-1] = 20 | (win->data[(r+h-1)*win->cols+c+w-1]&0xff00) | br; 21 | 22 | for (int ri = r+1; ri < r+h-1; ++ri) 23 | { 24 | win->data[ri*win->cols+c] = (win->data[ri*win->cols+c]&0xff00) | ls; 25 | win->data[ri*win->cols+c+w-1] = 26 | (win->data[ri*win->cols+c+w-1]&0xff00) | rs; 27 | } 28 | 29 | for (int ci = c+1; ci < c+w-1; ++ci) 30 | { 31 | win->data[r*win->cols+ci] = (win->data[r*win->cols+ci]&0xff00) | ts; 32 | win->data[(r+h-1)*win->cols+ci] = 33 | (win->data[(r+h-1)*win->cols+ci]&0xff00) | bs; 34 | } 35 | } 36 | 37 | 38 | int wborder(WINDOW *win, chtype ls, chtype rs, chtype ts, chtype bs, 39 | chtype tl, chtype tr, chtype bl, chtype br) 40 | { 41 | if (win->parent) _wborder(win->parent, ls, rs, ts, bs, tl, tr, bl, br, 42 | win->row - win->parent->row, win->col - win->parent->col, 43 | win->rows, win->cols); 44 | else _wborder(win, ls, rs, ts, bs, tl, tr, bl, br, 45 | 0, 0, win->rows, win->cols); 46 | return OK; 47 | } 48 | 49 | -------------------------------------------------------------------------------- /src/curses/curses_core.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static int active = 0; 4 | static WINDOW std; 5 | WINDOW * const stdscr = &std; 6 | 7 | static chtype phys[80*25]; 8 | static chtype virt[80*25]; 9 | 10 | chtype colpairs[COLOR_PAIRS]; 11 | 12 | WINDOW *initscr(void) 13 | { 14 | if (active) return stdscr; 15 | std.rows = 25; 16 | std.cols = 80; 17 | std.bkgd = 0x0720; 18 | std.parent = 0; 19 | setpage(1); 20 | setblink(0); 21 | setattr(0x07); 22 | clrscr(); 23 | gotoxy(0,0); 24 | for (int i = 0; i < 80*25; ++i) 25 | { 26 | phys[i] = 0x0720; 27 | virt[i] = 0x0720; 28 | std.data[i] = 0x0720; 29 | } 30 | active = 1; 31 | return stdscr; 32 | } 33 | 34 | int endwin(void) 35 | { 36 | if (!active) return ERR; 37 | active = 0; 38 | setpage(0); 39 | setblink(1); 40 | setcursor(1); 41 | return OK; 42 | } 43 | 44 | int wnoutrefresh(WINDOW *win) 45 | { 46 | if (win->parent) return (wnoutrefresh(win->parent)); 47 | for (int r = 0; r < win->rows; ++r) 48 | { 49 | for (int c = 0; c < win->cols; ++c) 50 | { 51 | virt[80*(r+win->row)+c+win->col] = win->data[win->cols*r+c]; 52 | } 53 | } 54 | return OK; 55 | } 56 | 57 | int doupdate(void) 58 | { 59 | for (int r = 0; r < 25; ++r) 60 | { 61 | for (int c = 0; c < 80; ++c) 62 | { 63 | if (phys[80*r+c] != virt[80*r+c]) 64 | { 65 | gotoxy(c,r); 66 | putch(virt[80*r+c]); 67 | phys[80*r+c] = virt[80*r+c]; 68 | } 69 | } 70 | } 71 | return OK; 72 | } 73 | 74 | int wrefresh(WINDOW *win) 75 | { 76 | wnoutrefresh(win); 77 | doupdate(); 78 | return OK; 79 | } 80 | 81 | int init_pair(short pair, short f, short b) 82 | { 83 | static const int permut[8] = {0,4,2,6,1,5,3,7}; 84 | if (pair < 0 || pair > COLOR_PAIRS-1) return ERR; 85 | colpairs[pair] = COL_PAIR(permut[f], permut[b]) << 8; 86 | return OK; 87 | } 88 | 89 | -------------------------------------------------------------------------------- /src/curses/curses_erase.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int werase(WINDOW *win) 4 | { 5 | chtype ech = (win->bkgd & 0xff00) | 0x20; 6 | if (win->parent) 7 | { 8 | for (int r = win->row - win->parent->row; 9 | r < win->row - win->parent->row + win->rows; ++r) 10 | { 11 | for (int c = win->col - win->parent->col; 12 | c < win->col - win->parent->col + win->cols; ++c) 13 | { 14 | win->parent->data[r*win->parent->cols + c] = ech; 15 | } 16 | } 17 | return OK; 18 | } 19 | for (int i = 0; i < win->rows * win->cols; ++i) win->data[i] = ech; 20 | return OK; 21 | } 22 | 23 | -------------------------------------------------------------------------------- /src/curses/curses_printw.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | int vw_printw(WINDOW *win, const char *fmt, va_list varglist) 6 | { 7 | char buf[800]; 8 | vsnprintf(buf, 800, fmt, varglist); 9 | return mvwaddnstr(win, -1, -1, buf, -1); 10 | } 11 | 12 | int mvwprintw(WINDOW *win, int y, int x, const char *fmt, ...) 13 | { 14 | va_list ap; 15 | int rc; 16 | 17 | if (y >= 0 && x >= 0) wmove(win, y, x); 18 | va_start(ap, fmt); 19 | rc = vw_printw(win, fmt, ap); 20 | va_end(ap); 21 | return rc; 22 | } 23 | 24 | -------------------------------------------------------------------------------- /src/curses/curses_win.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | WINDOW *newwin(int nlines, int ncols, int begin_y, int begin_x) 6 | { 7 | if (!nlines) nlines = 25 - begin_y; 8 | if (!ncols) ncols = 80 - begin_x; 9 | WINDOW *win = malloc(sizeof(WINDOW) + 10 | (nlines * ncols - 80 * 25) * sizeof (chtype)); 11 | win->rows = nlines; 12 | win->cols = ncols; 13 | win->row = begin_y; 14 | win->col = begin_x; 15 | win->y = 0; 16 | win->x = 0; 17 | win->parent = 0; 18 | win->bkgd = 0x0720; 19 | for (int i = 0; i < nlines * ncols; ++i) 20 | { 21 | win->data[i] = 0x0720; 22 | } 23 | return win; 24 | } 25 | 26 | WINDOW *subwin(WINDOW *orig, int nlines, int ncols, int begin_y, int begin_x) 27 | { 28 | if (orig->parent) return 0; 29 | if (!nlines) nlines = 25 - begin_y; 30 | if (!ncols) ncols = 80 - begin_x; 31 | WINDOW *win = malloc(sizeof(WINDOW) - 80*25*sizeof(chtype)); 32 | win->rows = nlines; 33 | win->cols = ncols; 34 | win->row = begin_y; 35 | win->col = begin_x; 36 | win->y = 0; 37 | win->x = 0; 38 | win->parent = orig; 39 | win->bkgd = win->parent->bkgd; 40 | return win; 41 | } 42 | 43 | int delwin(WINDOW *win) 44 | { 45 | if (!win) return ERR; 46 | free(win); 47 | return OK; 48 | } 49 | 50 | -------------------------------------------------------------------------------- /src/doscrt0.c: -------------------------------------------------------------------------------- 1 | int main(); 2 | void _getcinfo(void); 3 | 4 | #ifndef NOARGV 5 | #include 6 | #include 7 | 8 | static void argstart(void) __attribute__((__noreturn__, __used__)); 9 | static char *progname(void); 10 | static int argc = 0; 11 | static char *argv[32]; 12 | #endif 13 | 14 | __asm__ ( 15 | " .section .comstartup \n" 16 | " .globl __com__start \n" 17 | "__com__start: \n" 18 | " call _getcinfo \n" 19 | #ifdef NOARGV 20 | " call main \n" 21 | " jmp exit \n" 22 | #else 23 | " jmp argstart \n" 24 | #endif 25 | " .text \n"); 26 | 27 | void __attribute__((__noreturn__)) exit(int status) 28 | { 29 | __asm__ volatile ( 30 | "mov $0x4c, %%ah \n\t" 31 | "int $0x21 \n\t" 32 | : 33 | : "a" (status) 34 | ); 35 | __builtin_unreachable(); 36 | } 37 | 38 | #ifndef NOARGV 39 | static void argstart(void) 40 | { 41 | char *cmdline = (char *)0x81; 42 | argc = 1; 43 | argv[0] = progname(); 44 | 45 | if ((argv[argc] = strqetok(cmdline, "\"'", "\\", " \t\r\n"))) 46 | { 47 | ++argc; 48 | while (argc < 32) 49 | { 50 | if (!(argv[argc] = strqetok(0, "\"'", "\\", " \t\r\n"))) break; 51 | ++argc; 52 | } 53 | } 54 | 55 | exit(main(argc, argv)); 56 | } 57 | 58 | static char *progname(void) 59 | { 60 | if (dosversion() < 0x0300) return ""; 61 | 62 | unsigned int envaddr = (*(char *)0x2d << 8 | *(char *)0x2c) ; 63 | char *envptr = (char *)envaddr; 64 | 65 | while (*envptr) envptr += strlen(envptr) + 1; 66 | return envptr + 3; 67 | } 68 | #endif 69 | 70 | -------------------------------------------------------------------------------- /src/dosversion.c: -------------------------------------------------------------------------------- 1 | unsigned short dosversion(void) 2 | { 3 | unsigned short v1, v2; 4 | __asm__ ( 5 | "mov $0x30, %%ah \n\t" 6 | "int $0x21 \n\t" 7 | : "=a" (v1) 8 | : 9 | : "bx", "cx" 10 | ); 11 | if (!(v1 & 0xff)) v1 = 0x0001; 12 | v2 = v1 >> 8 & 0xff; 13 | v1 = v1 << 8 | v2; 14 | return v1; 15 | } 16 | 17 | -------------------------------------------------------------------------------- /src/errno.c: -------------------------------------------------------------------------------- 1 | int errno = 0; 2 | -------------------------------------------------------------------------------- /src/rtctimer.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | static volatile unsigned char timer = 0; 5 | 6 | int rtctset(unsigned int usecs) 7 | { 8 | unsigned short dx = usecs & 0xffff; 9 | unsigned short cx = usecs >> 16; 10 | int err; 11 | 12 | __asm__ ( 13 | "movl $0, %0 \n\t" 14 | "mov $0x8300, %%ax \n\t" 15 | "clc \n\t" 16 | "int $0x15 \n\t" 17 | "jnc 1f \n\t" 18 | "movzx %%ah, %0 \n" 19 | "1: \n\t" 20 | : "=rm" (err) 21 | : "b" (&timer), "c" (cx), "d" (dx) 22 | : "ax" 23 | ); 24 | if (err) 25 | { 26 | if ((err & 0xff) == 0x86) errno = ENOSYS; 27 | else errno = EBUSY; 28 | return -1; 29 | } 30 | timer = 1; 31 | return 0; 32 | } 33 | 34 | int rtctstop(void) 35 | { 36 | int err; 37 | __asm__ ( 38 | "movl $0, %0 \n\t" 39 | "mov $0x8301, %%ax \n\t" 40 | "clc \n\t" 41 | "int $0x15 \n\t" 42 | "jnc 1f \n\t" 43 | "movl $1, %0 \n" 44 | "1: \n\t" 45 | : "=rm" (err) 46 | : 47 | : "ax" 48 | ); 49 | if (err) 50 | { 51 | errno = EBUSY; 52 | return -1; 53 | } 54 | timer = 0; 55 | return 0; 56 | } 57 | 58 | int rtctwait(void) 59 | { 60 | if (!timer) 61 | { 62 | errno = ECANCELED; 63 | return -1; 64 | } 65 | while (!(timer & 0x80)) __asm__ volatile ("hlt"); 66 | timer = 0; 67 | return 0; 68 | } 69 | 70 | int rtctpoll(void) 71 | { 72 | if (timer & 0x80) 73 | { 74 | timer = 0; 75 | return 1; 76 | } 77 | return 0; 78 | } 79 | 80 | -------------------------------------------------------------------------------- /src/stdio/stdio_core.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | static const int stdinfd = 0; 8 | static const int stdoutfd = 1; 9 | static const int stderrfd = 2; 10 | 11 | const FILE *stdin = &stdinfd; 12 | const FILE *stdout = &stdoutfd; 13 | const FILE *stderr = &stderrfd; 14 | 15 | int puts(const char *s) 16 | { 17 | putstr(s); 18 | putstr("\n"); 19 | return 1; 20 | } 21 | 22 | int fputs(const char *s, FILE *stream) 23 | { 24 | if (stream != stdout && stream != stderr) 25 | { 26 | errno = ENOSYS; 27 | return EOF; 28 | } 29 | return putstr(s); 30 | } 31 | 32 | void perror(const char *s) 33 | { 34 | if (s && *s) printf("%s: %s\n", s, strerror(errno)); 35 | else puts(strerror(errno)); 36 | } 37 | -------------------------------------------------------------------------------- /src/stdio/stdio_printf.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static char fpbuf[32]; 9 | 10 | typedef enum farglen 11 | { 12 | FAL_HH, 13 | FAL_H, 14 | FAL_DEF, 15 | FAL_L 16 | } farglen; 17 | 18 | static size_t _fargnbuf(char **buf, const char **s, size_t n) 19 | { 20 | while ((!n || n > 1) && **s) 21 | { 22 | *(*buf)++ = *(*s)++; 23 | if (n) --n; 24 | } 25 | **buf = 0; 26 | while (**s) ++(*s); 27 | return n; 28 | } 29 | 30 | static size_t _ffmtnbuf(char **buf, const char **s, size_t n) 31 | { 32 | while ((!n || n > 1) && **s && **s != '%') 33 | { 34 | *(*buf)++ = *(*s)++; 35 | if (n) --n; 36 | } 37 | **buf = 0; 38 | while (**s && **s != '%') ++(*s); 39 | return n; 40 | } 41 | 42 | static void _fnumarg(long num, char pad, size_t minwidth, int sign, int hex) 43 | { 44 | char tmp[11]; 45 | int i = 0; 46 | char *s = fpbuf; 47 | 48 | if (minwidth > 31) minwidth = 31; 49 | 50 | if (sign) 51 | { 52 | if (num < 0) 53 | { 54 | *s++ = '-'; 55 | if (minwidth) --minwidth; 56 | num = -num; 57 | } 58 | else if (pad == ' ') 59 | { 60 | *s++ = ' '; 61 | if (minwidth) --minwidth; 62 | } 63 | } 64 | if (pad != '0') pad = ' '; 65 | do 66 | { 67 | if (hex) 68 | { 69 | tmp[i++] = (unsigned long)num % 16; 70 | num = (unsigned long)num / 16; 71 | } 72 | else 73 | { 74 | tmp[i++] = (unsigned long)num % 10; 75 | num = (unsigned long)num / 10; 76 | } 77 | } while (num > 0); 78 | while (minwidth-- > i) *s++ = pad; 79 | while (--i >= 0) 80 | { 81 | *s = tmp[i] + '0'; 82 | if (*s > '9') 83 | { 84 | if (hex == 2) *s += 'A' - '9' - 1; 85 | else *s += 'a' - '9' - 1; 86 | } 87 | ++s; 88 | } 89 | *s = 0; 90 | } 91 | 92 | static const char *_fftcharg(const char **s, va_list *ap) 93 | { 94 | char pad = 0; 95 | size_t minwidth = 0; 96 | farglen len = FAL_DEF; 97 | unsigned long num; 98 | int hex; 99 | 100 | while (1) 101 | { 102 | hex = 0; 103 | (*s)++; 104 | switch (**s) 105 | { 106 | case 'd': 107 | switch (len) 108 | { 109 | case FAL_HH: 110 | num = (signed char) va_arg(*ap, int); 111 | break; 112 | case FAL_H: 113 | num = (short) va_arg(*ap, int); 114 | break; 115 | case FAL_DEF: 116 | num = va_arg(*ap, int); 117 | break; 118 | case FAL_L: 119 | num = va_arg(*ap, unsigned long); 120 | } 121 | _fnumarg((long)num, pad, minwidth, 1, 0); 122 | (*s)++; 123 | return fpbuf; 124 | 125 | case 'X': 126 | ++hex; 127 | // fall through 128 | case 'x': 129 | ++hex; 130 | // fall through 131 | case 'u': 132 | switch (len) 133 | { 134 | case FAL_HH: 135 | num = (unsigned char) va_arg(*ap, unsigned int); 136 | break; 137 | case FAL_H: 138 | num = (unsigned short) va_arg(*ap, unsigned int); 139 | break; 140 | case FAL_DEF: 141 | num = va_arg(*ap, unsigned int); 142 | break; 143 | case FAL_L: 144 | num = va_arg(*ap, unsigned long); 145 | } 146 | _fnumarg((long)num, pad, minwidth, 0, hex); 147 | (*s)++; 148 | return fpbuf; 149 | 150 | case 's': 151 | (*s)++; 152 | return va_arg(*ap, const char*); 153 | 154 | case 'c': 155 | (*s)++; 156 | fpbuf[0] = (unsigned char) va_arg(*ap, unsigned int); 157 | fpbuf[1] = 0; 158 | return fpbuf; 159 | 160 | case '0': 161 | if (minwidth) minwidth *= 10; 162 | else pad = '0'; 163 | break; 164 | 165 | case ' ': 166 | pad = ' '; 167 | break; 168 | 169 | case 'h': 170 | if (len > FAL_HH) --len; 171 | break; 172 | 173 | case 'l': 174 | if (len < FAL_L) ++len; 175 | break; 176 | 177 | default: 178 | if (!**s) return 0; 179 | if (**s >= '1' && **s <= '9') 180 | { 181 | minwidth *= 10; 182 | minwidth += **s - '0'; 183 | } 184 | } 185 | } 186 | } 187 | 188 | static size_t _ffmtlen(const char *format) 189 | { 190 | size_t len = 0; 191 | while (*format && *(format++) != '%') ++len; 192 | return len; 193 | } 194 | 195 | int vsnprintf(char *str, size_t size, const char *format, va_list ap) 196 | { 197 | int n = 0; 198 | 199 | while (*format) 200 | { 201 | if (*format == '%') 202 | { 203 | const char *arg = _fftcharg(&format, &ap); 204 | n += strlen(arg); 205 | size = _fargnbuf(&str, &arg, size); 206 | } 207 | else 208 | { 209 | n += _ffmtlen(format); 210 | size = _ffmtnbuf(&str, &format, size); 211 | } 212 | } 213 | 214 | return n; 215 | } 216 | 217 | int vfprintf(FILE *stream, const char *format, va_list ap) 218 | { 219 | if (stream != stdout && stream != stderr) 220 | { 221 | errno = ENOSYS; 222 | return -1; 223 | } 224 | return vprintf(format, ap); 225 | } 226 | 227 | int vsprintf(char *str, const char *format, va_list ap) 228 | { 229 | return vsnprintf(str, 0, format, ap); 230 | } 231 | 232 | int vprintf(const char *format, va_list ap) 233 | { 234 | int n = 0; 235 | char *buf; 236 | 237 | while (*format) 238 | { 239 | if (*format == '%') n += putstr(_fftcharg(&format, &ap)); 240 | else 241 | { 242 | buf = fpbuf; 243 | _ffmtnbuf(&buf, &format, 32); 244 | n += putstr(fpbuf); 245 | } 246 | } 247 | return n; 248 | } 249 | 250 | int snprintf(char *str, size_t size, const char *format, ...) 251 | { 252 | va_list ap; 253 | 254 | va_start(ap, format); 255 | int ret = vsnprintf(str, size, format, ap); 256 | va_end(ap); 257 | 258 | return ret; 259 | } 260 | 261 | int sprintf(char *str, const char *format, ...) 262 | { 263 | va_list ap; 264 | 265 | va_start(ap, format); 266 | int ret = vsprintf(str, format, ap); 267 | va_end(ap); 268 | 269 | return ret; 270 | } 271 | 272 | int fprintf(FILE *stream, const char *format, ...) 273 | { 274 | va_list ap; 275 | 276 | va_start(ap, format); 277 | int ret = vfprintf(stream, format, ap); 278 | va_end(ap); 279 | 280 | return ret; 281 | } 282 | 283 | int printf(const char *format, ...) 284 | { 285 | va_list ap; 286 | 287 | va_start(ap, format); 288 | int ret = vprintf(format, ap); 289 | va_end(ap); 290 | 291 | return ret; 292 | } 293 | -------------------------------------------------------------------------------- /src/stdlib/stdlib_malloc.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #ifndef MALLOC_STACK_GAP 7 | #define MALLOC_STACK_GAP 0x40 8 | #endif 9 | 10 | #define HDR_CNEXT(x) ((char *)((unsigned int)(x)->parts.next)) 11 | #define HDR_HNEXT(x) ((hhdr *)((unsigned int)(x)->parts.next)) 12 | #define HDR_FREE(x) ((x)->parts.free) 13 | #define SHORTPTR(x) ((unsigned short)(unsigned int)(x)) 14 | 15 | typedef union hhdr hhdr; 16 | union hhdr { 17 | void *ptr; 18 | struct 19 | { 20 | unsigned short next; 21 | unsigned short free; 22 | } parts; 23 | }; 24 | 25 | extern char _heap; 26 | static char *hbreak = &_heap; 27 | static hhdr hhead = { &_heap }; 28 | 29 | unsigned short newchunk(size_t size) 30 | { 31 | char *stack; 32 | __asm__("mov %%esp, %0": "=rm" (stack)); 33 | if (hbreak + size > stack - MALLOC_STACK_GAP) return 0; 34 | if (size < 1024) size = 1024; 35 | hhdr *chunk = (hhdr *)hbreak; 36 | hbreak += size; 37 | if (hbreak > stack - MALLOC_STACK_GAP) hbreak = stack - MALLOC_STACK_GAP; 38 | chunk->ptr = hbreak; 39 | chunk->parts.free = 1; 40 | return SHORTPTR(chunk); 41 | } 42 | 43 | void *malloc(size_t size) 44 | { 45 | if (!size) return 0; 46 | if (size % sizeof(hhdr)) size += sizeof(hhdr) - (size % sizeof(hhdr)); 47 | 48 | hhdr *hdr = &hhead; 49 | while (HDR_CNEXT(hdr) < hbreak) 50 | { 51 | hdr = HDR_HNEXT(hdr); 52 | if (HDR_FREE(hdr) && 53 | HDR_CNEXT(hdr) - (char *)hdr - sizeof(hhdr) >= size) 54 | { 55 | if (HDR_CNEXT(hdr) - (char *)hdr - 2*sizeof(hhdr) > size) 56 | { 57 | hhdr *hdr2 = (hhdr *)((char *)hdr + sizeof(hhdr) + size); 58 | hdr2->parts.free = 1; 59 | hdr2->parts.next = hdr->parts.next; 60 | hdr->parts.next = SHORTPTR(hdr2); 61 | } 62 | hdr->parts.free = 0; 63 | return (char *)hdr + sizeof(hhdr); 64 | } 65 | } 66 | 67 | if (!(hdr->parts.next = newchunk(size + sizeof(hhdr)))) 68 | { 69 | errno = ENOMEM; 70 | return 0; 71 | } 72 | return malloc(size); 73 | } 74 | 75 | void free(void *ptr) 76 | { 77 | if (!ptr) return; 78 | hhdr *hdr = (hhdr *)((char *)ptr - sizeof(hhdr)); 79 | hdr->parts.free = 1; 80 | if (hdr != (hhdr *)hhead.ptr) 81 | { 82 | hhdr *hdr2 = (hhdr *)hhead.ptr; 83 | while (hdr2->parts.next != SHORTPTR(hdr)) hdr2 = HDR_HNEXT(hdr2); 84 | if (HDR_FREE(hdr2)) hdr = hdr2; 85 | } 86 | hhdr *next = HDR_HNEXT(hdr); 87 | while ((char *)next < hbreak) 88 | { 89 | if (!HDR_FREE(next)) break; 90 | hdr->parts.next = SHORTPTR(next); 91 | next = HDR_HNEXT(next); 92 | } 93 | if ((char *)next == hbreak) hbreak = (char *)hdr; 94 | } 95 | 96 | void *realloc(void *ptr, size_t size) 97 | { 98 | if (!ptr) return malloc(size); 99 | if (!size) 100 | { 101 | free(ptr); 102 | return 0; 103 | } 104 | if (size % sizeof(hhdr)) size += sizeof(hhdr) - (size % sizeof(hhdr)); 105 | hhdr *hdr = (hhdr *)((char *)ptr - sizeof(hhdr)); 106 | size_t oldsize = HDR_CNEXT(hdr) - (char *)ptr; 107 | if (size <= oldsize) 108 | { 109 | if ((unsigned int)(oldsize - size) <= 2*sizeof(hhdr)) return ptr; 110 | hhdr *hdr2 = (hhdr *)((char *)ptr + size); 111 | hdr2->ptr = hdr->ptr; 112 | hdr->ptr = hdr2; 113 | free((char *)hdr2 + sizeof(hhdr)); 114 | return ptr; 115 | } 116 | else 117 | { 118 | if (HDR_CNEXT(hdr) < hbreak 119 | && HDR_FREE(HDR_HNEXT(hdr))) 120 | { 121 | hdr->ptr = HDR_HNEXT(HDR_HNEXT(hdr)); 122 | return realloc(ptr, size); 123 | } 124 | else 125 | { 126 | void *ptr2 = malloc(size); 127 | if (ptr2) memcpy(ptr2, ptr, size); 128 | free(ptr); 129 | return ptr2; 130 | } 131 | } 132 | } 133 | 134 | -------------------------------------------------------------------------------- /src/stdlib/stdlib_rand.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static unsigned long long int randval = 1; 4 | 5 | void srand(unsigned int seed) 6 | { 7 | randval = seed; 8 | } 9 | 10 | int rand(void) 11 | { 12 | randval *= 1103515245; 13 | randval += 12345; 14 | return (int)((randval / 65536) & 0x7fffffff); 15 | } 16 | 17 | -------------------------------------------------------------------------------- /src/string/string_mem.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void *memcpy(void *dest, const void *src, size_t n) 4 | { 5 | if (!dest) return 0; 6 | for (size_t i = 0; i < n; ++i) 7 | { 8 | ((unsigned char *)dest)[i] = ((const unsigned char *)src)[i]; 9 | } 10 | return dest; 11 | } 12 | 13 | void *memset(void *s, int c, size_t n) 14 | { 15 | if (!s) return 0; 16 | for (size_t i = 0; i < n; ++i) 17 | { 18 | ((unsigned char *)s)[i] = (unsigned char)c; 19 | } 20 | return s; 21 | } 22 | 23 | -------------------------------------------------------------------------------- /src/string/string_strcpy.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | char *strcpy(char *dest, const char *src) 4 | { 5 | char *p = dest; 6 | while ((*p++ = *src++)); 7 | return dest; 8 | } 9 | 10 | -------------------------------------------------------------------------------- /src/string/string_strerror.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | char *strerror(int errnum) 7 | { 8 | static char unknown[18]; 9 | switch (errnum) 10 | { 11 | case EINVAL: return "Invalid argument"; 12 | case ENOSYS: return "Function not implemented"; 13 | case ENODEV: return "No such device"; 14 | case ECANCELED: return "Operation canceled"; 15 | case EBUSY: return "Device or resource busy"; 16 | case ENOMEM: return "Not enough space"; 17 | } 18 | errno = EINVAL; 19 | snprintf(unknown, 18, "Unknown error %03d", errnum); 20 | return unknown; 21 | } 22 | 23 | -------------------------------------------------------------------------------- /src/string/string_strlen.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | size_t strlen(const char *s) 4 | { 5 | size_t l = 0; 6 | while (*s++) ++l; 7 | return l; 8 | } 9 | 10 | -------------------------------------------------------------------------------- /src/string/string_strtok.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | static int _isinstr(const char c, const char *str) 4 | { 5 | while (*str) 6 | { 7 | if (c == *str) return 1; 8 | ++str; 9 | } 10 | return 0; 11 | } 12 | 13 | char *strtok(char *str, const char *delim) 14 | { 15 | static char *strtokptr = 0; 16 | char *p; 17 | int t = 0; 18 | 19 | if (!str) str = strtokptr; 20 | if (!str) return 0; 21 | 22 | p = str; 23 | while (*p) 24 | { 25 | if (_isinstr(*p, delim)) 26 | { 27 | if (!t) goto cont; 28 | *p = 0; 29 | strtokptr = p+1; 30 | return str; 31 | } 32 | if (!t) 33 | { 34 | t = 1; 35 | str = p; 36 | } 37 | cont: 38 | ++p; 39 | } 40 | if (t && p > str) 41 | { 42 | strtokptr = 0; 43 | return str; 44 | } 45 | return 0; 46 | } 47 | 48 | char *strqetok(char *str, const char *quot, const char *esc, const char *delim) 49 | { 50 | static char *r, *w; 51 | static unsigned char e; 52 | static char q; 53 | char *t = 0; 54 | 55 | if (str) 56 | { 57 | r = w = str; 58 | e = 0; 59 | q = '\0'; 60 | } 61 | 62 | if (!r) return 0; 63 | 64 | while (*r) 65 | { 66 | if (e) 67 | { 68 | if (!t) t = w; 69 | *w++ = *r; 70 | e = 0; 71 | } 72 | else if (q) 73 | { 74 | if (*r == q) 75 | { 76 | q = '\0'; 77 | } 78 | else if (_isinstr(*r, esc) && r[1] && 79 | (r[1] == q || _isinstr(r[1], esc))) 80 | { 81 | e = 1; 82 | } 83 | else 84 | { 85 | if (!t) t = w; 86 | *w++ = *r; 87 | } 88 | } 89 | else 90 | { 91 | if (_isinstr(*r, delim)) 92 | { 93 | if (t) 94 | { 95 | *w++ = '\0'; 96 | return t; 97 | } 98 | } 99 | else if (_isinstr(*r, esc)) 100 | { 101 | e = 1; 102 | } 103 | else if (_isinstr(*r, quot)) 104 | { 105 | if (!t) t = w; 106 | q = *r; 107 | } 108 | else 109 | { 110 | if (!t) t = w; 111 | *w++ = *r; 112 | } 113 | } 114 | ++r; 115 | } 116 | if (t) *w = '\0'; 117 | return t; 118 | } 119 | 120 | -------------------------------------------------------------------------------- /src/time/time_completetm.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void completetm(struct tm *tm) 4 | { 5 | static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4, 6, 2, 4}; 6 | static int d[] = {31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30}; 7 | 8 | int y = tm->tm_year+1900; 9 | 10 | d[1] = (!(y%4) && (!(y%400) || y%100)) ? 29 : 28; 11 | tm->tm_yday = 0; 12 | for (int i = 0; i < tm->tm_mon; ++i) tm->tm_yday += d[i]; 13 | tm->tm_yday += tm->tm_mday - 1; 14 | 15 | y -= (tm->tm_mon < 2); 16 | tm->tm_wday = (y + y/4 - y/100 + y/400 + t[tm->tm_mon] + tm->tm_mday) % 7; 17 | } 18 | 19 | -------------------------------------------------------------------------------- /src/time/time_getrtctm.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int getrtctm(struct tm *tm) 4 | { 5 | if (readrtc(tm) < 0) return -1; 6 | completetm(tm); 7 | 8 | return 0; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /src/time/time_mktime.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | time_t mktime(struct tm *tm) 4 | { 5 | time_t time = 0; 6 | 7 | if (tm->tm_yday < 0) completetm(tm); 8 | int y = tm->tm_year+1900; 9 | int s = y < 1970 ? -1 : 1; 10 | while (y != 1970) 11 | { 12 | time += s * ((!(y%4) && (!(y%400) || y%100)) ? 31622400 : 31536000); 13 | y -= s; 14 | } 15 | time += 86400 * tm->tm_yday + 3600 * tm->tm_hour 16 | + 60 * tm->tm_min + tm->tm_sec; 17 | return time; 18 | } 19 | 20 | -------------------------------------------------------------------------------- /src/time/time_readrtc.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | 5 | int readrtc(struct tm *tm) 6 | { 7 | unsigned int bcdcy = 0; 8 | unsigned int bcdmd = 0; 9 | unsigned int bcdhm = 0; 10 | unsigned int bcdsd = 0; 11 | int try; 12 | int err; 13 | 14 | try = 2; 15 | do 16 | { 17 | __asm__ ( 18 | "movl $0, %0 \n\t" 19 | "mov $0x04, %%ah \n\t" 20 | "clc \n\t" 21 | "int $0x1a \n\t" 22 | "jnc 1f \n\t" 23 | "movl $1, %0 \n" 24 | "1: \n\t" 25 | : "=rm" (err), "=c" (bcdcy), "=d" (bcdmd) 26 | : 27 | : "ax" 28 | ); 29 | --try; 30 | } while (try && (err || (!bcdcy && !bcdmd))); 31 | 32 | if (err) 33 | { 34 | errno = EBUSY; 35 | return -1; 36 | } 37 | 38 | try = 2; 39 | do 40 | { 41 | __asm__ ( 42 | "movl $0, %0 \n\t" 43 | "mov $0x02, %%ah \n\t" 44 | "clc \n\t" 45 | "int $0x1a \n\t" 46 | "jnc 1f \n\t" 47 | "movl $1, %0 \n" 48 | "1: \n\t" 49 | : "=rm" (err), "=c" (bcdhm), "=d" (bcdsd) 50 | : 51 | : "ax" 52 | ); 53 | --try; 54 | } while (try && err); 55 | 56 | if (err) 57 | { 58 | errno = EBUSY; 59 | return -1; 60 | } 61 | 62 | if (!bcdcy && !bcdmd && !bcdhm && !bcdsd) 63 | { 64 | errno = ENODEV; 65 | return -1; 66 | } 67 | 68 | tm->tm_year = ((bcdcy >> 12) & 0xf) * 1000 + ((bcdcy >> 8) & 0xf) * 100 69 | + ((bcdcy >> 4) & 0xf) * 10 + (bcdcy & 0xf) - 1900; 70 | tm->tm_mon = ((bcdmd >> 12) & 0xf) * 10 + ((bcdmd >> 8) & 0xf) -1; 71 | tm->tm_mday = ((bcdmd >> 4) & 0xf) * 10 + (bcdmd & 0xf); 72 | tm->tm_hour = ((bcdhm >> 12) & 0xf) * 10 + ((bcdhm >> 8) & 0xf); 73 | tm->tm_min = ((bcdhm >> 4) & 0xf) * 10 + (bcdhm & 0xf); 74 | tm->tm_sec = ((bcdsd >> 12) & 0xf) * 10 + ((bcdsd >> 8) & 0xf); 75 | tm->tm_isdst = bcdsd & 0xf; 76 | tm->tm_yday = -1; 77 | tm->tm_wday = -1; 78 | 79 | return 0; 80 | } 81 | -------------------------------------------------------------------------------- /src/time/time_time.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | time_t time(time_t *t) 4 | { 5 | struct tm tm; 6 | time_t time; 7 | 8 | if (getrtctm(&tm) < 0) 9 | { 10 | unsigned short cx, dx; 11 | __asm__ ( 12 | "mov $0, %%ah \n\t" 13 | "int $0x1a \n\t" 14 | : "=c" (cx), "=d" (dx) 15 | : 16 | : "ax" 17 | ); 18 | time = cx; 19 | time <<= 16; 20 | time |= dx; 21 | time = (time_t) ((double)time / 18.2); 22 | } 23 | else 24 | { 25 | time = mktime(&tm); 26 | } 27 | if (t) *t = time; 28 | return time; 29 | } 30 | --------------------------------------------------------------------------------