├── dosbind ├── dosbind.rsp ├── makefile └── dosbind.c ├── crt ├── asm │ ├── bin │ │ ├── dbgbrk.obj │ │ ├── afuldiv.obj │ │ ├── afulmul.obj │ │ ├── afulrem.obj │ │ ├── afulshl.obj │ │ ├── afulshr.obj │ │ ├── anuldiv.obj │ │ ├── anulmul.obj │ │ ├── anulrem.obj │ │ ├── anulshl.obj │ │ └── anulshr.obj │ ├── os2asm.rsp │ ├── Makefile │ ├── dbgbrk.asm │ ├── afulmul.asm │ ├── anulmul.asm │ ├── afulrem.asm │ ├── anulrem.asm │ ├── afuldiv.asm │ ├── anuldiv.asm │ ├── afulshl.asm │ ├── afulshr.asm │ ├── anulshl.asm │ └── anulshr.asm ├── os2crt.rsp ├── makefile ├── malloc.c ├── crtpriv.h ├── printf.c ├── os2crt.h ├── mem.c ├── string.c ├── startup1.c ├── halloc.c ├── printf.inc └── salloc.c ├── .gitignore ├── os2start ├── asm │ ├── os2start.rsp │ ├── bin │ │ └── startup0.obj │ ├── Makefile │ └── startup0.asm └── Makefile ├── dir ├── zdir.def ├── szdir.def ├── Makefile └── zdir.c ├── dosfapi ├── asm │ ├── bin │ │ ├── cursor.obj │ │ ├── dosexit.obj │ │ ├── dosfind.obj │ │ ├── keybrd.obj │ │ ├── qfsinfo.obj │ │ ├── vidmode.obj │ │ ├── doswrite.obj │ │ ├── scrollup.obj │ │ ├── segalloc.obj │ │ ├── startup0.obj │ │ └── wrtchrat.obj │ ├── dosasm.rsp │ ├── Makefile │ ├── dosexit.asm │ ├── keybrd.asm │ ├── doswrite.asm │ ├── scrollup.asm │ ├── wrtchrat.asm │ ├── startup0.asm │ ├── qfsinfo.asm │ ├── cursor.asm │ ├── segalloc.asm │ ├── vidmode.asm │ └── dosfind.asm ├── dosfapi.rsp ├── makefile └── dosfapi.c ├── lib ├── os2.rsp ├── kbdcalls.def ├── nls.def ├── Makefile ├── viocalls.def └── doscalls.def ├── makefile ├── LICENSE ├── common.mk └── README.md /dosbind/dosbind.rsp: -------------------------------------------------------------------------------- 1 | DOSBIND 2 | y 3 | +dosbind.obj 4 | DOSBIND.MAP 5 | DOSBIND.LIB 6 | -------------------------------------------------------------------------------- /crt/asm/bin/dbgbrk.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malxau/os2api/HEAD/crt/asm/bin/dbgbrk.obj -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/*.exe 2 | **/*.obj 3 | **/*.map 4 | asm/*.lib 5 | crt/*.lib 6 | !*/asm/bin/*.obj 7 | -------------------------------------------------------------------------------- /crt/asm/bin/afuldiv.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malxau/os2api/HEAD/crt/asm/bin/afuldiv.obj -------------------------------------------------------------------------------- /crt/asm/bin/afulmul.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malxau/os2api/HEAD/crt/asm/bin/afulmul.obj -------------------------------------------------------------------------------- /crt/asm/bin/afulrem.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malxau/os2api/HEAD/crt/asm/bin/afulrem.obj -------------------------------------------------------------------------------- /crt/asm/bin/afulshl.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malxau/os2api/HEAD/crt/asm/bin/afulshl.obj -------------------------------------------------------------------------------- /crt/asm/bin/afulshr.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malxau/os2api/HEAD/crt/asm/bin/afulshr.obj -------------------------------------------------------------------------------- /crt/asm/bin/anuldiv.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malxau/os2api/HEAD/crt/asm/bin/anuldiv.obj -------------------------------------------------------------------------------- /crt/asm/bin/anulmul.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malxau/os2api/HEAD/crt/asm/bin/anulmul.obj -------------------------------------------------------------------------------- /crt/asm/bin/anulrem.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malxau/os2api/HEAD/crt/asm/bin/anulrem.obj -------------------------------------------------------------------------------- /crt/asm/bin/anulshl.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malxau/os2api/HEAD/crt/asm/bin/anulshl.obj -------------------------------------------------------------------------------- /crt/asm/bin/anulshr.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malxau/os2api/HEAD/crt/asm/bin/anulshr.obj -------------------------------------------------------------------------------- /os2start/asm/os2start.rsp: -------------------------------------------------------------------------------- 1 | OS2START 2 | y 3 | +startup0.obj 4 | OS2START.MAP 5 | OS2START.LIB 6 | -------------------------------------------------------------------------------- /dir/zdir.def: -------------------------------------------------------------------------------- 1 | NAME OZDIR WINDOWCOMPAT LONGNAMES 2 | EXETYPE OS2 3 | HEAPSIZE 2048 4 | STACKSIZE 4096 5 | -------------------------------------------------------------------------------- /dosfapi/asm/bin/cursor.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malxau/os2api/HEAD/dosfapi/asm/bin/cursor.obj -------------------------------------------------------------------------------- /dosfapi/asm/bin/dosexit.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malxau/os2api/HEAD/dosfapi/asm/bin/dosexit.obj -------------------------------------------------------------------------------- /dosfapi/asm/bin/dosfind.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malxau/os2api/HEAD/dosfapi/asm/bin/dosfind.obj -------------------------------------------------------------------------------- /dosfapi/asm/bin/keybrd.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malxau/os2api/HEAD/dosfapi/asm/bin/keybrd.obj -------------------------------------------------------------------------------- /dosfapi/asm/bin/qfsinfo.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malxau/os2api/HEAD/dosfapi/asm/bin/qfsinfo.obj -------------------------------------------------------------------------------- /dosfapi/asm/bin/vidmode.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malxau/os2api/HEAD/dosfapi/asm/bin/vidmode.obj -------------------------------------------------------------------------------- /dosfapi/dosfapi.rsp: -------------------------------------------------------------------------------- 1 | DOSFAPI 2 | y 3 | +asm\dosasm.lib +dosfapi.obj 4 | DOSFAPI.MAP 5 | DOSFAPI.LIB 6 | -------------------------------------------------------------------------------- /dosfapi/asm/bin/doswrite.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malxau/os2api/HEAD/dosfapi/asm/bin/doswrite.obj -------------------------------------------------------------------------------- /dosfapi/asm/bin/scrollup.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malxau/os2api/HEAD/dosfapi/asm/bin/scrollup.obj -------------------------------------------------------------------------------- /dosfapi/asm/bin/segalloc.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malxau/os2api/HEAD/dosfapi/asm/bin/segalloc.obj -------------------------------------------------------------------------------- /dosfapi/asm/bin/startup0.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malxau/os2api/HEAD/dosfapi/asm/bin/startup0.obj -------------------------------------------------------------------------------- /dosfapi/asm/bin/wrtchrat.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malxau/os2api/HEAD/dosfapi/asm/bin/wrtchrat.obj -------------------------------------------------------------------------------- /lib/os2.rsp: -------------------------------------------------------------------------------- 1 | OS2 2 | y 3 | +doscalls.lib +kbdcalls.lib +nls.lib +viocalls.lib 4 | OS2.MAP 5 | OS2.LIB 6 | -------------------------------------------------------------------------------- /os2start/asm/bin/startup0.obj: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malxau/os2api/HEAD/os2start/asm/bin/startup0.obj -------------------------------------------------------------------------------- /dir/szdir.def: -------------------------------------------------------------------------------- 1 | NAME SZDIR WINDOWCOMPAT LONGNAMES 2 | EXETYPE OS2 3 | HEAPSIZE 2048 4 | STACKSIZE 4096 5 | STUB 'dzdir.exe' 6 | -------------------------------------------------------------------------------- /crt/os2crt.rsp: -------------------------------------------------------------------------------- 1 | OS2CRT 2 | y 3 | +asm\os2asm.lib +startup1.obj +halloc.obj +malloc.obj +mem.obj +printf.obj +salloc.obj +string.obj 4 | OS2CRT.MAP 5 | OS2CRT.LIB 6 | -------------------------------------------------------------------------------- /os2start/Makefile: -------------------------------------------------------------------------------- 1 | all: os2start.lib 2 | 3 | !INCLUDE "..\common.mk" 4 | 5 | os2start.lib: asm\os2start.lib 6 | @copy asm\os2start.lib os2start.lib >NUL 2>&1 7 | -------------------------------------------------------------------------------- /lib/kbdcalls.def: -------------------------------------------------------------------------------- 1 | LIBRARY KBDCALLS 2 | 3 | EXPORTS 4 | KbdCharIn 5 | KbdFlushBuffer 6 | KbdGetStatus 7 | KbdRegister 8 | KbdSetStatus 9 | KbdStringIn 10 | KbdPeek 11 | 12 | -------------------------------------------------------------------------------- /lib/nls.def: -------------------------------------------------------------------------------- 1 | LIBRARY NLS 2 | 3 | EXPORTS 4 | DosCaseMap 5 | DosGetCollate 6 | DosGetCtryInfo 7 | DosGetDBCSEv 8 | DosGetMessage 9 | DosInsMessage 10 | DosPutMessage 11 | 12 | -------------------------------------------------------------------------------- /crt/asm/os2asm.rsp: -------------------------------------------------------------------------------- 1 | OS2ASM 2 | y 3 | +afuldiv.obj +anuldiv.obj +afulmul.obj +anulmul.obj +afulrem.obj +anulrem.obj +afulshl.obj +anulshl.obj +afulshr.obj +anulshr.obj +dbgbrk.obj 4 | OS2ASM.MAP 5 | OS2ASM.LIB 6 | -------------------------------------------------------------------------------- /dosfapi/asm/dosasm.rsp: -------------------------------------------------------------------------------- 1 | DOSASM 2 | y 3 | +cursor.obj +dosexit.obj +dosfind.obj +doswrite.obj +keybrd.obj +qfsinfo.obj +segalloc.obj +scrollup.obj +startup0.obj +vidmode.obj +wrtchrat.obj 4 | DOSASM.MAP 5 | DOSASM.LIB 6 | -------------------------------------------------------------------------------- /dosbind/makefile: -------------------------------------------------------------------------------- 1 | all: dosbind.lib 2 | 3 | !INCLUDE "..\common.mk" 4 | 5 | OBJS = dosbind.obj \ 6 | 7 | dosbind.lib: $(OBJS) dosbind.rsp 8 | @echo $@ 9 | @erase $@ 2>NUL 10 | @$(OBJLIB) $(LIBFLAGS) @dosbind.rsp 11 | -------------------------------------------------------------------------------- /dosfapi/makefile: -------------------------------------------------------------------------------- 1 | all: dosfapi.lib 2 | 3 | !INCLUDE "..\common.mk" 4 | 5 | COBJS = dosfapi.obj \ 6 | 7 | dosfapi.lib: $(COBJS) asm\dosasm.lib dosfapi.rsp 8 | @echo $@ 9 | @erase $@ 2>NUL 10 | @$(OBJLIB) $(LIBFLAGS) @dosfapi.rsp 11 | -------------------------------------------------------------------------------- /os2start/asm/Makefile: -------------------------------------------------------------------------------- 1 | all: os2start.lib 2 | 3 | !INCLUDE "..\..\common.mk" 4 | 5 | ASMOBJS = startup0.obj \ 6 | 7 | os2start.lib: $(ASMOBJS) os2start.rsp 8 | @echo $@ 9 | @erase $@ 2>NUL 10 | @$(OBJLIB) $(LIBFLAGS) @os2start.rsp 11 | -------------------------------------------------------------------------------- /lib/Makefile: -------------------------------------------------------------------------------- 1 | all: os2.lib 2 | 3 | !INCLUDE "..\common.mk" 4 | 5 | INPUTLIBS = doscalls.lib kbdcalls.lib nls.lib viocalls.lib 6 | 7 | .SUFFIXES: .def .lib 8 | 9 | os2.lib: $(INPUTLIBS) os2.rsp 10 | @echo $@ 11 | @erase $@ 2>NUL 12 | @$(OBJLIB) $(LIBFLAGS) @os2.rsp 13 | 14 | .def.lib: 15 | @$(IMPLIB) $(IMPLIBFLAGS) /nologo $@ $< 16 | -------------------------------------------------------------------------------- /crt/makefile: -------------------------------------------------------------------------------- 1 | all: os2crt.lib 2 | 3 | !INCLUDE "..\common.mk" 4 | 5 | COBJS = halloc.obj \ 6 | malloc.obj \ 7 | mem.obj \ 8 | printf.obj \ 9 | salloc.obj \ 10 | startup1.obj \ 11 | string.obj \ 12 | 13 | os2crt.lib: $(COBJS) asm\os2asm.lib os2crt.rsp 14 | @echo $@ 15 | @erase $@ 2>NUL 16 | @$(OBJLIB) $(LIBFLAGS) @os2crt.rsp 17 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | DEBUG=1 2 | PDB=1 3 | 4 | DIRS=lib os2start\asm os2start crt\asm crt dosfapi\asm dosfapi dosbind dir 5 | 6 | !IFDEF _YMAKE_VER 7 | all[dirs target=all]: $(DIRS) 8 | 9 | clean[dirs target=clean]: $(DIRS) 10 | !ELSE 11 | all: 12 | @for %%i in ($(DIRS)) do @cd %%i & $(MAKE) /nologo DEBUG=$(DEBUG) PDB=$(PDB) all & cd $(MAKEDIR) 13 | 14 | clean: 15 | @for %%i in ($(DIRS)) do @cd %%i & $(MAKE) /nologo DEBUG=$(DEBUG) PDB=$(PDB) clean & cd $(MAKEDIR) 16 | !ENDIF 17 | -------------------------------------------------------------------------------- /lib/viocalls.def: -------------------------------------------------------------------------------- 1 | LIBRARY VIOCALLS 2 | 3 | EXPORTS 4 | VioGetConfig 5 | VioGetCurPos 6 | VioGetCurType 7 | VioGetFont 8 | VioGetMode 9 | VioGetPhysBuf 10 | VioGetState 11 | VioReadCellStr 12 | VioReadCharStr 13 | VioScrLock 14 | VioScrollDn 15 | VioScrollLf 16 | VioScrollRt 17 | VioScrollUp 18 | VioScrUnLock 19 | VioSetCurPos 20 | VioSetCurType 21 | VioSetFont 22 | VioSetMode 23 | VioSetState 24 | VioWrtCellStr 25 | VioWrtCharStr 26 | VioWrtCharStrAtt 27 | VioWrtNAttr 28 | VioWrtNCell 29 | VioWrtNChar 30 | VioWrtTTY 31 | -------------------------------------------------------------------------------- /dosfapi/asm/Makefile: -------------------------------------------------------------------------------- 1 | all: dosasm.lib 2 | 3 | !INCLUDE "..\..\common.mk" 4 | 5 | ASMOBJS = cursor.obj \ 6 | dosexit.obj \ 7 | dosfind.obj \ 8 | doswrite.obj \ 9 | keybrd.obj \ 10 | qfsinfo.obj \ 11 | segalloc.obj \ 12 | scrollup.obj \ 13 | startup0.obj \ 14 | vidmode.obj \ 15 | wrtchrat.obj \ 16 | 17 | dosasm.lib: $(ASMOBJS) dosasm.rsp 18 | @echo $@ 19 | @erase $@ 2>NUL 20 | @$(OBJLIB) $(LIBFLAGS) @dosasm.rsp 21 | -------------------------------------------------------------------------------- /crt/asm/Makefile: -------------------------------------------------------------------------------- 1 | all: os2asm.lib 2 | 3 | !INCLUDE "..\..\common.mk" 4 | 5 | ASMOBJS = afuldiv.obj \ 6 | anuldiv.obj \ 7 | afulmul.obj \ 8 | anulmul.obj \ 9 | afulrem.obj \ 10 | anulrem.obj \ 11 | afulshl.obj \ 12 | anulshl.obj \ 13 | afulshr.obj \ 14 | anulshr.obj \ 15 | dbgbrk.obj \ 16 | 17 | os2asm.lib: $(ASMOBJS) os2asm.rsp 18 | @echo $@ 19 | @erase $@ 2>NUL 20 | @$(OBJLIB) $(LIBFLAGS) @os2asm.rsp 21 | -------------------------------------------------------------------------------- /dir/Makefile: -------------------------------------------------------------------------------- 1 | all: bzdir.exe dzdir.exe ozdir.exe szdir.exe 2 | 3 | !INCLUDE "..\common.mk" 4 | 5 | ozdir.exe: zdir.obj $(OS2LIBS) 6 | @echo $@ 7 | @$(LINK) $(LDFLAGS) zdir.obj,ozdir.exe,ozdir.map,$(OS2LIBS) ..\lib\os2.lib,zdir.def 8 | 9 | dzdir.exe: zdir.obj $(DOSLIBS) 10 | @echo $@ 11 | @$(LINK) $(LDFLAGS) /cp:1 zdir.obj,dzdir.exe,dzdir.map,$(DOSLIBS) ..\crt\os2crt.lib $(BINDLIBS),NUL 12 | 13 | szdir.exe: zdir.obj $(OS2LIBS) dzdir.exe 14 | @echo $@ 15 | @$(LINK) $(LDFLAGS) zdir.obj,szdir.exe,szdir.map,$(OS2LIBS) ..\lib\os2.lib,szdir.def 16 | 17 | !IF "$(BIND)"!="" 18 | bzdir.exe: ozdir.exe 19 | @echo $@ 20 | @$(BIND) /nologo ozdir.exe $(BINDLIBS) /m bzdir.map /o bzdir.exe 21 | !ELSE 22 | bzdir.exe: szdir.exe 23 | @echo $@ (copy binary) 24 | @copy szdir.exe $@ 25 | !ENDIF 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Malcolm Smith 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /dosfapi/dosfapi.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file dos/dosfapi.c 3 | * 4 | * DOS functions to implement the OS/2 Family API. 5 | * 6 | * Copyright (c) 2023 Malcolm J. Smith 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | #include 27 | 28 | // vim:sw=4:ts=4:et: 29 | -------------------------------------------------------------------------------- /crt/asm/dbgbrk.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; DBGBRK.ASM 3 | ; 4 | ; Break into the debugger. 5 | ; 6 | ; Copyright (c) 2023 Malcolm J. Smith 7 | ; 8 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 9 | ; of this software and associated documentation files (the "Software"), to deal 10 | ; in the Software without restriction, including without limitation the rights 11 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | ; copies of the Software, and to permit persons to whom the Software is 13 | ; furnished to do so, subject to the following conditions: 14 | ; 15 | ; The above copyright notice and this permission notice shall be included in 16 | ; all copies or substantial portions of the Software. 17 | ; 18 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | ; THE SOFTWARE. 25 | ; 26 | 27 | .MODEL MemModel, pascal 28 | 29 | .CODE 30 | 31 | public DebugBreak 32 | DebugBreak proc 33 | 34 | int 3 35 | ret 36 | 37 | DebugBreak endp 38 | 39 | END 40 | -------------------------------------------------------------------------------- /common.mk: -------------------------------------------------------------------------------- 1 | 2 | !IFNDEF DEBUG 3 | DEBUG=0 4 | !ENDIF 5 | 6 | !IFNDEF PDB 7 | PDB=1 8 | !ENDIF 9 | 10 | ML=ml.exe 11 | CC=cl.exe 12 | OBJLIB=lib.exe 13 | LINK=link.exe 14 | IMPLIB=implib.exe 15 | BIND=bind.exe 16 | 17 | # Check if MASM is installed. If not, we'll use preassembled binaries 18 | # for these parts. 19 | !IF [$(ML) 2>&1 | find "Microsoft" >NUL]>0 20 | ML= 21 | !ENDIF 22 | 23 | # Check if BIND is installed. If not, we can't compile bound programs, 24 | # only pure OS/2. 25 | # for these parts. 26 | !IF [$(BIND) 2>&1 | find "Microsoft" >NUL]>0 27 | BIND= 28 | !ENDIF 29 | 30 | CFLAGS=-nologo -AC -Gs -Gc -Gf -Gq -Gy -W3 -WX -I..\inc -I..\crt 31 | MLFLAGS=-nologo -Zi -Zd -DMemModel=compact 32 | LIBFLAGS=/nologo 33 | IMPLIBFLAGS=/nologo 34 | LDFLAGS=/nologo /nodefaultlibrarysearch /map:f 35 | 36 | !IF $(DEBUG)==1 37 | CFLAGS=$(CFLAGS) -Od 38 | !ELSE 39 | CFLAGS=$(CFLAGS) -Os /f- 40 | !ENDIF 41 | 42 | !IF $(PDB)==1 43 | CFLAGS=$(CFLAGS) -Z7 44 | LDFLAGS=$(LDFLAGS) /codeview 45 | !ENDIF 46 | 47 | OS2LIBS=..\os2start\os2start.lib ..\crt\os2crt.lib ..\lib\os2.lib 48 | DOSLIBS=..\dosfapi\dosfapi.lib ..\crt\os2crt.lib 49 | BINDLIBS=..\dosbind\dosbind.lib 50 | 51 | clean: 52 | -@erase *.exe 2>NUL 53 | -@erase *.map 2>NUL 54 | -@erase *.obj 2>NUL 55 | -@erase *.lib 2>NUL 56 | 57 | .c.obj: 58 | @$(CC) /c $(CFLAGS) $< 59 | 60 | !IF "$(ML)"!="" 61 | .asm.obj: 62 | @$(ML) /c $(MLFLAGS) $< 63 | !ELSE 64 | .asm.obj: 65 | @echo bin\$@ (copy binary) 66 | @copy bin\$@ . 67 | !ENDIF 68 | -------------------------------------------------------------------------------- /dosbind/dosbind.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file dos/dosbind.c 3 | * 4 | * DOS functions to extend the OS/2 Family API. 5 | * 6 | * Copyright (c) 2023 Malcolm J. Smith 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | #include 27 | 28 | SYSERR APIENTRY DosGetModHandle(PSZ szModule, PHMODULE hModule) 29 | { 30 | return ERROR_NOT_SUPPORTED; 31 | } 32 | 33 | SYSERR APIENTRY DosGetProcAddr(HMODULE hModule, PSZ pszProcName, PPFN ppFn) 34 | { 35 | return ERROR_NOT_SUPPORTED; 36 | } 37 | 38 | // vim:sw=4:ts=4:et: 39 | -------------------------------------------------------------------------------- /dosfapi/asm/dosexit.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; DOSEXIT.ASM 3 | ; 4 | ; Exit the process. 5 | ; 6 | ; Copyright (c) 2023 Malcolm J. Smith 7 | ; 8 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 9 | ; of this software and associated documentation files (the "Software"), to deal 10 | ; in the Software without restriction, including without limitation the rights 11 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | ; copies of the Software, and to permit persons to whom the Software is 13 | ; furnished to do so, subject to the following conditions: 14 | ; 15 | ; The above copyright notice and this permission notice shall be included in 16 | ; all copies or substantial portions of the Software. 17 | ; 18 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | ; THE SOFTWARE. 25 | ; 26 | 27 | .MODEL large, pascal 28 | 29 | .CODE _TEXT 30 | 31 | public DosExit 32 | DosExit proc 33 | 34 | push bp 35 | mov bp, sp 36 | 37 | ; VOID 38 | ; DosExit( 39 | ; BOOL fTerminate, [BP + 8], 40 | ; WORD wExitCode [BP + 6] 41 | ; ); 42 | 43 | mov ah, 4ch 44 | mov al, [bp + 6] 45 | int 21h 46 | 47 | mov sp, bp 48 | pop bp 49 | 50 | ret 4 51 | 52 | DosExit endp 53 | 54 | END 55 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OS/2 API 2 | 3 | This repo is a package of tools for writing 16 bit OS/2 programs with Visual C++ 1.5. 4 | 5 | ## Why OS/2? Why 16 bit? 6 | 7 | - OS/2 because I didn't have access to tools and documentation to learn it when it was current, but now I can :) 8 | - Because the real target is DOS (ba-dum tss!) DOS native API is assembly, and it's convenient to have a C based function API. The OS/2 API is as good as any. There is already an implementation of the OS/2 API on DOS, called the Family API or FAPI. 9 | 10 | ## What's included? 11 | 12 | - inc - A completely new OS/2 header file, `inc/os2.h` which is a recreation of the API based on documentation, internet research, and assembly includes to be new and unencombered. 13 | - lib - A new import library, `lib/os2.lib`, generated here, also new and unencombered. 14 | - crt - A trivial C runtime library, heavily patterned on minicrt, so that it's possible to do common C operations. This isn't as nice as a "real" one, but...well, it's unencombered. 15 | - dosbind - A library to extend the Family API on DOS. 16 | - dosfapi - An alternative implementation to the Family API on DOS. This is just enough to support dir below, and isn't a serious replacement. 17 | - dir - A demonstration program putting it all together. 18 | 19 | ## Building 20 | 21 | This is intended to be compiled with Visual C++ 1.5. The assembly parts were written with MASM 6.0, and should work with higher versions. 22 | 23 | ## Contributing 24 | 25 | I'm accepting changes, particularly if accompanied by a note indicating some interesting learning of the environment. 26 | 27 | ## License 28 | 29 | This code is available under the MIT license. 30 | 31 | -------------------------------------------------------------------------------- /lib/doscalls.def: -------------------------------------------------------------------------------- 1 | LIBRARY DOSCALLS 2 | 3 | EXPORTS 4 | DosAllocHuge @40 5 | DosAllocSeg @34 6 | DosBeep @50 7 | DosChDir @57 8 | DosChgFilePtr @58 9 | DosClose @59 10 | DosCwait @2 11 | DosCreateCSAlias @43 12 | DosDelete @60 13 | DosDevConfig @52 14 | DosDevIOCtl @53 15 | DosDupHandle @61 16 | DosError @120 17 | DosExecPgm @144 18 | DosExit @5 19 | DosFileLocks @62 20 | DosFindClose @63 21 | DosFindFirst @64 22 | DosFindNext @65 23 | DosFreeSeg @39 24 | DosGetCp @130 25 | DosGetDateTime @33 26 | DosGetEnv @91 27 | DosGetHugeShift @41 28 | DosGetMachineMode @49 29 | DosGetModHandle @47 30 | DosGetProcAddr @45 31 | DosGetPID @94 32 | DosGetVersion @92 33 | DosHoldSignal @13 34 | DosMkDir @66 35 | DosMove @67 36 | DosNewSize @68 37 | DosOpen @70 38 | DosQCurDir @71 39 | DosQCurDisk @72 40 | DosQFHandState @73 41 | DosQFileInfo @74 42 | DosQFileMode @75 43 | DosQFSInfo @76 44 | DosQHandType @77 45 | DosQVerify @78 46 | DosRead @137 47 | DosReallocHuge @42 48 | DosReallocSeg @38 49 | DosRmDir @80 50 | DosSelectDisk @81 51 | DosSetCp @153 52 | DosSetDateTime @28 53 | DosSetFHandState @82 54 | DosSetFileInfo @83 55 | DosSetFileMode @84 56 | DosSetFSInfo @97 57 | DosSetSigHandler @14 58 | DosSetVec @89 59 | DosSetVerify @86 60 | DosSizeSeg @126 61 | DosSleep @32 62 | DosSubAlloc @147 63 | DosSubFree @148 64 | DosSubSet @146 65 | DosWrite @138 66 | 67 | -------------------------------------------------------------------------------- /dosfapi/asm/keybrd.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; KEYBRD.ASM 3 | ; 4 | ; Read from the keyboard 5 | ; 6 | ; Copyright (c) 2023 Malcolm J. Smith 7 | ; 8 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 9 | ; of this software and associated documentation files (the "Software"), to deal 10 | ; in the Software without restriction, including without limitation the rights 11 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | ; copies of the Software, and to permit persons to whom the Software is 13 | ; furnished to do so, subject to the following conditions: 14 | ; 15 | ; The above copyright notice and this permission notice shall be included in 16 | ; all copies or substantial portions of the Software. 17 | ; 18 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | ; THE SOFTWARE. 25 | ; 26 | 27 | .MODEL large, pascal 28 | 29 | .CODE _TEXT 30 | 31 | public KbdCharIn 32 | KbdCharIn proc 33 | 34 | push bp 35 | mov bp, sp 36 | 37 | ; SYSERR APIENTRY 38 | ; KbdCharIn( 39 | ; PKBDKEYINFO pCharData, [High BP + 12, Low BP + 10] 40 | ; WORD wWait, [BP + 8] 41 | ; HKBD hkbd [BP + 6] 42 | ; ); 43 | 44 | push ds 45 | push bx 46 | mov ah, 0h 47 | int 16h 48 | mov ds, [bp + 12] 49 | mov bx, [bp + 10] 50 | 51 | mov word ptr [bx], ax 52 | xor ax, ax 53 | mov word ptr [bx + 2], ax 54 | mov word ptr [bx + 4], ax 55 | mov word ptr [bx + 6], ax 56 | mov word ptr [bx + 8], ax 57 | 58 | pop bx 59 | pop ds 60 | 61 | mov sp, bp 62 | pop bp 63 | 64 | ret 8 65 | 66 | KbdCharIn endp 67 | 68 | END 69 | -------------------------------------------------------------------------------- /dosfapi/asm/doswrite.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; DOSWRITE.ASM 3 | ; 4 | ; Primitive character output 5 | ; 6 | ; Copyright (c) 2023 Malcolm J. Smith 7 | ; 8 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 9 | ; of this software and associated documentation files (the "Software"), to deal 10 | ; in the Software without restriction, including without limitation the rights 11 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | ; copies of the Software, and to permit persons to whom the Software is 13 | ; furnished to do so, subject to the following conditions: 14 | ; 15 | ; The above copyright notice and this permission notice shall be included in 16 | ; all copies or substantial portions of the Software. 17 | ; 18 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | ; THE SOFTWARE. 25 | ; 26 | 27 | .MODEL large, pascal 28 | 29 | .CODE _TEXT 30 | 31 | public DosWrite 32 | DosWrite proc 33 | 34 | push bp 35 | mov bp, sp 36 | 37 | ; SYSERR APIENTRY 38 | ; DosWrite( 39 | ; WORD wHandle, [BP + 16] 40 | ; PBYTE pBuffer, [High BP + 14, Low BP + 12] 41 | ; WORD wBufferLength, [BP + 10] 42 | ; PWORD pwBytesWritten [High BP + 8, Low BP + 6] 43 | ; ); 44 | 45 | push ds 46 | push bx 47 | 48 | mov ds, [bp + 14] 49 | mov dx, [bp + 12] 50 | mov bx, [bp + 16] 51 | mov cx, [bp + 10] 52 | 53 | mov ah, 40h 54 | int 21h 55 | 56 | jb DosWriteFail 57 | 58 | mov ds, [bp + 8] 59 | mov bx, [bp + 6] 60 | mov word ptr [bx], ax 61 | 62 | xor ax, ax 63 | 64 | DosWriteFail: 65 | 66 | pop bx 67 | pop ds 68 | 69 | mov sp, bp 70 | pop bp 71 | 72 | ret 12 73 | 74 | DosWrite endp 75 | 76 | END 77 | -------------------------------------------------------------------------------- /dosfapi/asm/scrollup.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; SCROLLUP.ASM 3 | ; 4 | ; Scroll the video up. 5 | ; 6 | ; Copyright (c) 2023 Malcolm J. Smith 7 | ; 8 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 9 | ; of this software and associated documentation files (the "Software"), to deal 10 | ; in the Software without restriction, including without limitation the rights 11 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | ; copies of the Software, and to permit persons to whom the Software is 13 | ; furnished to do so, subject to the following conditions: 14 | ; 15 | ; The above copyright notice and this permission notice shall be included in 16 | ; all copies or substantial portions of the Software. 17 | ; 18 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | ; THE SOFTWARE. 25 | ; 26 | 27 | .MODEL large, pascal 28 | 29 | .CODE _TEXT 30 | 31 | public VioScrollUp 32 | VioScrollUp proc 33 | 34 | push bp 35 | mov bp, sp 36 | 37 | ; VOID 38 | ; VioScrollUp( 39 | ; WORD wTopRow, [BP + 20] 40 | ; WORD wLeftColumn, [BP + 18] 41 | ; WORD wBottomRow, [BP + 16] 42 | ; WORD wRightColumn, [BP + 14] 43 | ; WORD wBlankLines, [BP + 12] 44 | ; PWORD pwCell, [High BP + 10, Low BP + 8] 45 | ; HVIO hvio [BP + 6] 46 | ; ); 47 | 48 | push ds 49 | push bx 50 | mov ds, [bp + 10] 51 | mov bx, [bp + 8] 52 | mov bx, [bx] 53 | mov ch, [bp + 20] 54 | mov cl, [bp + 18] 55 | mov dh, [bp + 16] 56 | mov dl, [bp + 14] 57 | mov al, [bp + 12] 58 | mov ah, 06h 59 | int 10h 60 | 61 | 62 | pop bx 63 | pop ds 64 | 65 | xor ax, ax 66 | 67 | mov sp, bp 68 | pop bp 69 | 70 | ret 16 71 | 72 | VioScrollUp endp 73 | 74 | END 75 | -------------------------------------------------------------------------------- /crt/malloc.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file crt/malloc.c 3 | * 4 | * OS/2 based memory allocator wrapper. 5 | * 6 | * Copyright (c) 2023 Malcolm J. Smith 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | #include 27 | #include 28 | #include 29 | 30 | PVOID hugeAllocator; 31 | 32 | BOOL 33 | malloc_init(VOID) 34 | { 35 | hugeAllocator = HugePoolAllocNewPool(); 36 | if (hugeAllocator == NULL) { 37 | return FALSE; 38 | } 39 | 40 | return TRUE; 41 | } 42 | 43 | VOID 44 | malloc_cleanup(VOID) 45 | { 46 | if (hugeAllocator != NULL) { 47 | HugePoolFreePool(hugeAllocator); 48 | } 49 | } 50 | 51 | PVOID 52 | malloc(WORD sizeInBytes) 53 | { 54 | if (hugeAllocator == NULL) { 55 | hugeAllocator = HugePoolAllocNewPool(); 56 | if (hugeAllocator == NULL) { 57 | return NULL; 58 | } 59 | } 60 | 61 | return HugePoolAlloc(hugeAllocator, sizeInBytes); 62 | } 63 | 64 | VOID 65 | free(PVOID ptr) 66 | { 67 | HugePoolFree(ptr); 68 | } 69 | 70 | // vim:sw=4:ts=4:et: 71 | -------------------------------------------------------------------------------- /crt/asm/afulmul.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; AFULMUL.ASM 3 | ; 4 | ; Implementation for far signed and unsigned multiplication of a 32 bit 5 | ; integer by a 32 bit integer. 6 | ; 7 | ; Copyright (c) 2017-2023 Malcolm J. Smith 8 | ; 9 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 10 | ; of this software and associated documentation files (the "Software"), to deal 11 | ; in the Software without restriction, including without limitation the rights 12 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | ; copies of the Software, and to permit persons to whom the Software is 14 | ; furnished to do so, subject to the following conditions: 15 | ; 16 | ; The above copyright notice and this permission notice shall be included in 17 | ; all copies or substantial portions of the Software. 18 | ; 19 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | ; THE SOFTWARE. 26 | ; 27 | 28 | .MODEL large, pascal 29 | 30 | .CODE _TEXT 31 | 32 | public __aFulmul 33 | __aFulmul proc 34 | 35 | push bp 36 | mov bp, sp 37 | 38 | ; DWORD [High DX, Low AX] 39 | ; __aFulmul( 40 | ; DWORD Value1, [High BP + 12, Low BP + 10] 41 | ; DWORD Value2 [High BP + 8, Low BP + 6] 42 | ; ); 43 | 44 | ; Multiply low by high of both components, adding them into a temporary 45 | ; register. Multiply low by low, which might result in a high component, 46 | ; and add back the temporary result into this high component. 47 | 48 | push bx 49 | 50 | mov ax, [bp + 6] 51 | mov dx, [bp + 12] 52 | mul dx 53 | mov bx, ax 54 | 55 | mov ax, [bp + 10] 56 | mov dx, [bp + 8] 57 | mul dx 58 | add bx, ax 59 | 60 | mov ax, [bp + 6] 61 | mov dx, [bp + 10] 62 | mul dx 63 | add dx, bx 64 | 65 | pop bx 66 | mov sp, bp 67 | pop bp 68 | 69 | ret 8 70 | __aFulmul endp 71 | 72 | END 73 | -------------------------------------------------------------------------------- /crt/asm/anulmul.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; ANULMUL.ASM 3 | ; 4 | ; Implementation for near signed and unsigned multiplication of a 32 bit 5 | ; integer by a 32 bit integer. 6 | ; 7 | ; Copyright (c) 2017-2023 Malcolm J. Smith 8 | ; 9 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 10 | ; of this software and associated documentation files (the "Software"), to deal 11 | ; in the Software without restriction, including without limitation the rights 12 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | ; copies of the Software, and to permit persons to whom the Software is 14 | ; furnished to do so, subject to the following conditions: 15 | ; 16 | ; The above copyright notice and this permission notice shall be included in 17 | ; all copies or substantial portions of the Software. 18 | ; 19 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | ; THE SOFTWARE. 26 | ; 27 | 28 | .MODEL small, pascal 29 | 30 | .CODE _TEXT 31 | 32 | public __aNulmul 33 | __aNulmul proc 34 | 35 | push bp 36 | mov bp, sp 37 | 38 | ; DWORD [High DX, Low AX] 39 | ; __aNulmul( 40 | ; DWORD Value1, [High BP + 10, Low BP + 8], 41 | ; DWORD Value2 [High BP + 6, Low BP + 4] 42 | ; ); 43 | 44 | ; Multiply low by high of both components, adding them into a temporary 45 | ; register. Multiply low by low, which might result in a high component, 46 | ; and add back the temporary result into this high component. 47 | 48 | push bx 49 | 50 | mov ax, [bp + 4] 51 | mov dx, [bp + 10] 52 | mul dx 53 | mov bx, ax 54 | 55 | mov ax, [bp + 8] 56 | mov dx, [bp + 6] 57 | mul dx 58 | add bx, ax 59 | 60 | mov ax, [bp + 4] 61 | mov dx, [bp + 8] 62 | mul dx 63 | add dx, bx 64 | 65 | pop bx 66 | mov sp, bp 67 | pop bp 68 | 69 | ret 8 70 | __aNulmul endp 71 | 72 | END 73 | -------------------------------------------------------------------------------- /dosfapi/asm/wrtchrat.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; WRTCHRAT.ASM 3 | ; 4 | ; VioWrtCharStrAtt 5 | ; 6 | ; Copyright (c) 2023 Malcolm J. Smith 7 | ; 8 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 9 | ; of this software and associated documentation files (the "Software"), to deal 10 | ; in the Software without restriction, including without limitation the rights 11 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | ; copies of the Software, and to permit persons to whom the Software is 13 | ; furnished to do so, subject to the following conditions: 14 | ; 15 | ; The above copyright notice and this permission notice shall be included in 16 | ; all copies or substantial portions of the Software. 17 | ; 18 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | ; THE SOFTWARE. 25 | ; 26 | 27 | .MODEL large, pascal 28 | 29 | .CODE _TEXT 30 | 31 | public VioWrtCharStrAtt 32 | VioWrtCharStrAtt proc 33 | 34 | push bp 35 | mov bp, sp 36 | 37 | ; VOID 38 | ; VioWrtCharStrAtt( 39 | ; PCHAR pCharBuffer, [High BP + 20, Low BP + 18] 40 | ; WORD wBufferSize, [BP + 16] 41 | ; WORD wRow, [BP + 14] 42 | ; WORD wColumn, [BP + 12] 43 | ; PBYTE pAttr, [High BP + 10, Low BP + 8] 44 | ; HVIO hvio [BP + 6] 45 | ; ); 46 | 47 | push ds 48 | push es 49 | push bx 50 | push si 51 | mov ds, [bp + 10] 52 | mov si, [bp + 8] 53 | mov bx, [si] 54 | mov bh, 0 55 | mov cx, [bp + 16] 56 | mov dh, [bp + 14] 57 | mov dl, [bp + 12] 58 | mov es, [bp + 20] 59 | mov ax, [bp + 18] 60 | push bp 61 | mov bp, ax 62 | mov ax, 01300h 63 | int 10h 64 | pop bp 65 | 66 | pop si 67 | pop bx 68 | pop es 69 | pop ds 70 | 71 | xor ax, ax 72 | 73 | mov sp, bp 74 | pop bp 75 | 76 | ret 16 77 | 78 | VioWrtCharStrAtt endp 79 | 80 | END 81 | -------------------------------------------------------------------------------- /dosfapi/asm/startup0.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; STARTUP0.ASM 3 | ; 4 | ; Initial program entry point for DOS programs. 5 | ; 6 | ; Copyright (c) 2023 Malcolm J. Smith 7 | ; 8 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 9 | ; of this software and associated documentation files (the "Software"), to deal 10 | ; in the Software without restriction, including without limitation the rights 11 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | ; copies of the Software, and to permit persons to whom the Software is 13 | ; furnished to do so, subject to the following conditions: 14 | ; 15 | ; The above copyright notice and this permission notice shall be included in 16 | ; all copies or substantial portions of the Software. 17 | ; 18 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | ; THE SOFTWARE. 25 | ; 26 | 27 | .MODEL large, pascal 28 | 29 | .DOSSEG 30 | 31 | __startup PROTO FAR PASCAL 32 | 33 | .DATA 34 | public __acrtused 35 | __acrtused = 1234h 36 | 37 | .DATA? 38 | _EnvSelector WORD ? 39 | _CmdlineOffset WORD ? 40 | 41 | .STACK 6144 42 | 43 | .CODE _TEXT 44 | .STARTUP 45 | 46 | mov word ptr [_EnvSelector], es 47 | mov word ptr [_CmdlineOffset], 081h 48 | 49 | call __startup 50 | 51 | public GetProgramName 52 | GetProgramName proc 53 | 54 | xor dx, dx 55 | xor ax, ax 56 | 57 | ret 58 | 59 | GetProgramName endp 60 | 61 | public GetCommandArgs 62 | GetCommandArgs proc 63 | 64 | push ds 65 | push si 66 | mov si, [_CmdlineOffset] 67 | mov ds, [_EnvSelector] 68 | 69 | jmp GetCommandArgsStart 70 | GetCommandArgsNextChar: 71 | inc si 72 | GetCommandArgsStart: 73 | mov al, [si] 74 | cmp al, 0dh 75 | jnz GetCommandArgsNextChar 76 | 77 | mov byte ptr [si], 0 78 | mov dx, ds 79 | 80 | pop si 81 | pop ds 82 | 83 | mov ax, [_CmdlineOffset] 84 | 85 | ret 86 | 87 | GetCommandArgs endp 88 | 89 | END 90 | -------------------------------------------------------------------------------- /crt/crtpriv.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file crt/crtpriv.h 3 | * 4 | * Private declarations not intended for use outside the CRT module. 5 | * 6 | * Copyright (c) 2023 Malcolm J. Smith 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | 27 | WORD CDECL 28 | main( 29 | WORD argc, 30 | PSZ argv[] 31 | ); 32 | 33 | PSZ APIENTRY 34 | GetProgramName(); 35 | 36 | PSZ APIENTRY 37 | GetCommandArgs(); 38 | 39 | PVOID 40 | HugePoolAllocNewPool(VOID); 41 | 42 | VOID 43 | HugePoolFreePool( 44 | PVOID pool 45 | ); 46 | 47 | VOID 48 | HugePoolDump( 49 | PVOID pool 50 | ); 51 | 52 | PVOID 53 | HugePoolAlloc( 54 | PVOID pool, 55 | WORD sizeInBytes 56 | ); 57 | 58 | VOID 59 | HugePoolFree( 60 | PVOID ptr 61 | ); 62 | 63 | PVOID 64 | SmallPoolAllocNewPool( 65 | WORD requestedBytes 66 | ); 67 | 68 | VOID 69 | SmallPoolFreePool( 70 | PVOID pool 71 | ); 72 | 73 | BOOL 74 | SmallPoolFreePoolIfEmpty( 75 | PVOID pool 76 | ); 77 | 78 | VOID 79 | SmallPoolDump( 80 | PVOID pool 81 | ); 82 | 83 | PVOID 84 | SmallPoolAlloc( 85 | PVOID pool, 86 | WORD sizeInBytes 87 | ); 88 | 89 | VOID 90 | SmallPoolFree( 91 | PVOID ptr 92 | ); 93 | 94 | BOOL 95 | malloc_init(VOID); 96 | 97 | VOID 98 | malloc_cleanup(VOID); 99 | 100 | // vim:sw=4:ts=4:et: 101 | -------------------------------------------------------------------------------- /dosfapi/asm/qfsinfo.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; QFSINFO.ASM 3 | ; 4 | ; Get file system information. 5 | ; 6 | ; Copyright (c) 2023 Malcolm J. Smith 7 | ; 8 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 9 | ; of this software and associated documentation files (the "Software"), to deal 10 | ; in the Software without restriction, including without limitation the rights 11 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | ; copies of the Software, and to permit persons to whom the Software is 13 | ; furnished to do so, subject to the following conditions: 14 | ; 15 | ; The above copyright notice and this permission notice shall be included in 16 | ; all copies or substantial portions of the Software. 17 | ; 18 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | ; THE SOFTWARE. 25 | ; 26 | 27 | .MODEL large, pascal 28 | 29 | .CODE _TEXT 30 | 31 | public DosQFSInfo 32 | DosQFSInfo proc 33 | 34 | push bp 35 | mov bp, sp 36 | 37 | ; SYSERR APIENTRY 38 | ; DosQFSInfo( 39 | ; WORD wDriveNumber, [BP + 14] 40 | ; WORD wInfoLevel, [BP + 12] 41 | ; PVOID pInfoBuf, [High BP + 10, Low BP + 8] 42 | ; WORD wInfoBufLength [BP + 6] 43 | ; ); 44 | 45 | push ds 46 | push bx 47 | push di 48 | 49 | mov dl, [bp + 14] 50 | mov ah, 036h 51 | int 21h 52 | 53 | mov ds, [bp + 10] 54 | mov di, [bp + 8] 55 | 56 | ; MSFIX This is a DOS 2.0 API that doesn't look sufficient for today's 57 | ; disks...? 58 | 59 | mov word ptr [di + 4], ax ; Sectors per allocation unit (low) 60 | mov word ptr [di + 8], dx ; Total allocation units (low) 61 | mov word ptr [di + 12], bx ; Available allocation units (low) 62 | mov word ptr [di + 16], cx ; Bytes per sector 63 | 64 | xor ax, ax 65 | 66 | mov word ptr [di], ax ; File system ID 67 | mov word ptr [di + 2], ax ; File system ID 68 | mov word ptr [di + 6], ax ; Sectors per allocation unit (high) 69 | mov word ptr [di + 10], ax ; Total allocation units (high) 70 | mov word ptr [di + 14], ax ; Available allocation units (high) 71 | 72 | pop di 73 | pop bx 74 | pop ds 75 | 76 | mov sp, bp 77 | pop bp 78 | 79 | ret 10 80 | 81 | DosQFSInfo endp 82 | 83 | END 84 | -------------------------------------------------------------------------------- /dosfapi/asm/cursor.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; CURSOR.ASM 3 | ; 4 | ; Get and set cursor position. 5 | ; 6 | ; Copyright (c) 2023 Malcolm J. Smith 7 | ; 8 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 9 | ; of this software and associated documentation files (the "Software"), to deal 10 | ; in the Software without restriction, including without limitation the rights 11 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | ; copies of the Software, and to permit persons to whom the Software is 13 | ; furnished to do so, subject to the following conditions: 14 | ; 15 | ; The above copyright notice and this permission notice shall be included in 16 | ; all copies or substantial portions of the Software. 17 | ; 18 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | ; THE SOFTWARE. 25 | ; 26 | 27 | .MODEL large, pascal 28 | 29 | .CODE _TEXT 30 | 31 | public VioGetCurPos 32 | VioGetCurPos proc 33 | 34 | push bp 35 | mov bp, sp 36 | 37 | ; VOID 38 | ; VioGetCurPos( 39 | ; PWORD pwRow, [High BP + 14, Low BP + 12] 40 | ; PWORD pwColumn, [High BP + 10, Low BP + 8] 41 | ; HVIO hvio [BP + 6] 42 | ; ); 43 | 44 | push ds 45 | push bx 46 | mov ah, 03h 47 | mov bh, 0h 48 | int 10h 49 | 50 | mov ds, [bp + 14] 51 | mov bx, [bp + 12] 52 | 53 | mov byte ptr [bx], dh 54 | mov byte ptr [bx + 1], 0 55 | 56 | mov ds, [bp + 10] 57 | mov bx, [bp + 8] 58 | 59 | mov byte ptr [bx], dl 60 | mov byte ptr [bx + 1], 0 61 | 62 | pop bx 63 | pop ds 64 | 65 | xor ax, ax 66 | 67 | mov sp, bp 68 | pop bp 69 | 70 | ret 10 71 | 72 | VioGetCurPos endp 73 | 74 | public VioSetCurPos 75 | VioSetCurPos proc 76 | 77 | push bp 78 | mov bp, sp 79 | 80 | ; VOID 81 | ; VioSetCurPos( 82 | ; WORD pwRow, [BP + 10] 83 | ; WORD pwColumn, [BP + 8] 84 | ; HVIO hvio [BP + 6] 85 | ; ); 86 | 87 | push bx 88 | mov ah, 02h 89 | mov bh, 0h 90 | mov dh, [bp + 10] 91 | mov dl, [bp + 8] 92 | int 10h 93 | 94 | pop bx 95 | 96 | xor ax, ax 97 | 98 | mov sp, bp 99 | pop bp 100 | 101 | ret 6 102 | 103 | VioSetCurPos endp 104 | 105 | END 106 | -------------------------------------------------------------------------------- /os2start/asm/startup0.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; STARTUP0.ASM 3 | ; 4 | ; Initial program entry point. In OS/2 it looks like this has to be 5 | ; assembly because core state is initialized in registers. This tried to 6 | ; save that and get to C as quickly as possible. 7 | ; 8 | ; Copyright (c) 2023 Malcolm J. Smith 9 | ; 10 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 11 | ; of this software and associated documentation files (the "Software"), to deal 12 | ; in the Software without restriction, including without limitation the rights 13 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | ; copies of the Software, and to permit persons to whom the Software is 15 | ; furnished to do so, subject to the following conditions: 16 | ; 17 | ; The above copyright notice and this permission notice shall be included in 18 | ; all copies or substantial portions of the Software. 19 | ; 20 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | ; THE SOFTWARE. 27 | ; 28 | 29 | .MODEL large, pascal 30 | 31 | .DOSSEG 32 | 33 | __startup PROTO FAR PASCAL 34 | 35 | .DATA 36 | public __acrtused 37 | __acrtused = 1234h 38 | 39 | .DATA? 40 | _EnvSelector WORD ? 41 | _CmdlineOffset WORD ? 42 | _DataSegSize WORD ? 43 | 44 | .STACK 6144 45 | 46 | .CODE 47 | startup: 48 | ; OS/2 Arguments 49 | ; AX Selector of environment 50 | ; BX Command line offset within environment selector 51 | ; CX Size of data segment 52 | 53 | mov [_EnvSelector], AX 54 | mov [_CmdlineOffset], BX 55 | mov [_DataSegSize], CX 56 | 57 | call __startup 58 | 59 | ; C code is responsible for exiting the process - execution should 60 | ; not get here 61 | 62 | 63 | 64 | public GetProgramName 65 | GetProgramName proc 66 | 67 | mov dx, [_EnvSelector] 68 | mov ax, [_CmdlineOffset] 69 | 70 | ret 71 | 72 | GetProgramName endp 73 | 74 | public GetCommandArgs 75 | GetCommandArgs proc 76 | 77 | push ds 78 | push si 79 | push cx 80 | mov si, [_CmdlineOffset] 81 | mov ds, [_EnvSelector] 82 | 83 | jmp GetCommandArgsStart 84 | GetCommandArgsNextChar: 85 | inc si 86 | GetCommandArgsStart: 87 | mov cl, [si] 88 | test cl, cl 89 | jnz GetCommandArgsNextChar 90 | 91 | mov ax, si 92 | inc ax 93 | mov dx, ds 94 | 95 | pop cx 96 | pop si 97 | pop ds 98 | 99 | ret 100 | 101 | GetCommandArgs endp 102 | 103 | END startup 104 | -------------------------------------------------------------------------------- /dosfapi/asm/segalloc.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; SEGALLOC.ASM 3 | ; 4 | ; Allocate and free segments 5 | ; 6 | ; Copyright (c) 2023 Malcolm J. Smith 7 | ; 8 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 9 | ; of this software and associated documentation files (the "Software"), to deal 10 | ; in the Software without restriction, including without limitation the rights 11 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | ; copies of the Software, and to permit persons to whom the Software is 13 | ; furnished to do so, subject to the following conditions: 14 | ; 15 | ; The above copyright notice and this permission notice shall be included in 16 | ; all copies or substantial portions of the Software. 17 | ; 18 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | ; THE SOFTWARE. 25 | ; 26 | 27 | .MODEL large, pascal 28 | 29 | .CODE _TEXT 30 | 31 | public DosAllocSeg 32 | DosAllocSeg proc 33 | 34 | push bp 35 | mov bp, sp 36 | 37 | ; SYSERR APIENTRY 38 | ; DosAllocSeg( 39 | ; WORD wSize, [BP + 12] 40 | ; PSEL pSelector, [High BP + 10, Low BP + 8] 41 | ; WORD wfAlloc [BP + 6] 42 | ; ); 43 | 44 | push ds 45 | push bx 46 | mov ax, [bp + 12] 47 | cmp ax, 0fff0h 48 | ja DosAllocSegEntireSegment 49 | 50 | ; Add 15 and divide by 4 to find the rounded up multiple in paragraphs 51 | add ax, 0fh 52 | shr ax, 1 53 | shr ax, 1 54 | shr ax, 1 55 | shr ax, 1 56 | 57 | ; Treat zero as an entire segment 58 | test ax, ax 59 | jnz DosAllocSegAlloc 60 | 61 | DosAllocSegEntireSegment: 62 | 63 | mov ax, 1000h 64 | 65 | DosAllocSegAlloc: 66 | 67 | mov bx, ax 68 | mov ah, 48h 69 | int 21h 70 | 71 | jnb DosAllocSegSuccess 72 | jmp DosAllocSegReturn 73 | 74 | DosAllocSegSuccess: 75 | 76 | mov ds, [bp + 10] 77 | mov bx, [bp + 8] 78 | 79 | mov word ptr [bx], ax 80 | 81 | xor ax, ax 82 | 83 | DosAllocSegReturn: 84 | 85 | pop bx 86 | pop ds 87 | 88 | mov sp, bp 89 | pop bp 90 | 91 | ret 8 92 | 93 | DosAllocSeg endp 94 | 95 | public DosFreeSeg 96 | DosFreeSeg proc 97 | 98 | push bp 99 | mov bp, sp 100 | 101 | ; SYSERR APIENTRY 102 | ; DosFreeSeg( 103 | ; WORD Selector [BP + 6] 104 | ; ); 105 | 106 | push es 107 | mov es, [bp + 6] 108 | mov ah, 49h 109 | int 21h 110 | 111 | pop es 112 | xor ax, ax 113 | 114 | mov sp, bp 115 | pop bp 116 | 117 | ret 2 118 | 119 | DosFreeSeg endp 120 | 121 | END 122 | -------------------------------------------------------------------------------- /crt/printf.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file crt/printf.c 3 | * 4 | * OS/2 printf implementation. 5 | * 6 | * Copyright (c) 2023 Malcolm J. Smith 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | 31 | #include "printf.inc" 32 | 33 | #define PRINTF_SIZEONLY 1 34 | #include "printf.inc" 35 | 36 | int CDECL 37 | sprintf_s( 38 | PSZ szDest, 39 | WORD len, 40 | PSZ szFmt, 41 | ... 42 | ) 43 | { 44 | va_list marker; 45 | int out_len; 46 | 47 | va_start( marker, szFmt ); 48 | out_len = vsprintf(szDest, len, szFmt, marker); 49 | va_end( marker ); 50 | return out_len; 51 | } 52 | 53 | int CDECL 54 | sprintf( 55 | PSZ szDest, 56 | PSZ szFmt, 57 | ... 58 | ) 59 | { 60 | va_list marker; 61 | int out_len; 62 | 63 | va_start( marker, szFmt ); 64 | out_len = vsprintf(szDest, (WORD)-1, szFmt, marker); 65 | va_end( marker ); 66 | return out_len; 67 | } 68 | 69 | int CDECL 70 | sprintf_sz( 71 | PSZ szFmt, 72 | ... 73 | ) 74 | { 75 | va_list marker; 76 | int out_len; 77 | 78 | va_start( marker, szFmt ); 79 | out_len = vsprintf_sz(szFmt, marker); 80 | va_end( marker ); 81 | return out_len; 82 | } 83 | 84 | int CDECL 85 | printf( 86 | PSZ szFmt, 87 | ... 88 | ) 89 | { 90 | va_list marker; 91 | CHAR buf[200]; 92 | int out_len; 93 | 94 | va_start( marker, szFmt ); 95 | out_len = vsprintf(buf, sizeof(buf), szFmt, marker); 96 | va_end( marker ); 97 | 98 | if (out_len != -1 && out_len < sizeof(buf)) { 99 | WORD written; 100 | if (DosWrite(1, buf, out_len, &written) != NO_ERROR) { 101 | out_len = -1; 102 | } 103 | } 104 | return out_len; 105 | } 106 | 107 | 108 | // vim:sw=4:ts=4:et: 109 | -------------------------------------------------------------------------------- /crt/os2crt.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file crt/os2crt.h 3 | * 4 | * Declarations for a mini OS/2 C runtime library. 5 | * 6 | * Copyright (c) 2023 Malcolm J. Smith 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | 27 | VOID APIENTRY 28 | DebugBreak(); 29 | 30 | #define SelToFarPtr(selector) (PVOID)((DWORD)(selector) << 16) 31 | #define FarPtrToSel(ptr) (SEL)((DWORD)(ptr) >> 16) 32 | 33 | PVOID 34 | malloc( 35 | WORD sizeInBytes 36 | ); 37 | 38 | VOID 39 | free( 40 | PVOID ptr 41 | ); 42 | 43 | int CDECL 44 | sprintf_s( 45 | PSZ szDest, 46 | WORD len, 47 | PSZ szFmt, 48 | ... 49 | ); 50 | 51 | int CDECL 52 | sprintf( 53 | PSZ szDest, 54 | PSZ szFmt, 55 | ... 56 | ); 57 | 58 | int CDECL 59 | sprintf_sz( 60 | PSZ szFmt, 61 | ... 62 | ); 63 | 64 | int CDECL 65 | printf( 66 | PSZ szFmt, 67 | ... 68 | ); 69 | 70 | PVOID 71 | memcpy(PVOID dest, PVOID src, WORD len); 72 | 73 | PVOID 74 | memmove(PVOID dest, PVOID src, WORD len); 75 | 76 | PVOID 77 | memset(PVOID dest, CHAR c, WORD len); 78 | 79 | SHORT 80 | memcmp(PVOID buf1, PVOID buf2, WORD len); 81 | 82 | SHORT 83 | atoi (PSZ str); 84 | 85 | PSZ 86 | strcat_s(PSZ dest, WORD len, const PSZ src); 87 | 88 | PSZ 89 | strncat(PSZ dest, const PSZ src, WORD len); 90 | 91 | PSZ 92 | strchr(PSZ str, UCHAR ch); 93 | 94 | PSZ 95 | strrchr(PSZ str, UCHAR ch); 96 | 97 | WORD 98 | strlen(PSZ str); 99 | 100 | PSZ 101 | strstr(const PSZ str, PSZ search); 102 | 103 | UCHAR 104 | toupper(UCHAR c); 105 | 106 | UCHAR 107 | tolower(UCHAR c); 108 | 109 | PSZ 110 | strupr(PSZ str); 111 | 112 | PSZ 113 | strlwr(PSZ str); 114 | 115 | SHORT 116 | strncmp(PSZ str1, PSZ str2, WORD count); 117 | 118 | SHORT 119 | strcmp(PSZ str1, PSZ str2); 120 | 121 | SHORT 122 | strnicmp(PSZ str1, PSZ str2, WORD count); 123 | 124 | SHORT 125 | stricmp(PSZ str1, PSZ str2); 126 | 127 | PSZ 128 | strtok_s(PSZ str, PSZ match, PSZ * context); 129 | 130 | PSZ 131 | strtok(PSZ str, PSZ match); 132 | 133 | SHORT 134 | strspn(PSZ str, PSZ chars); 135 | 136 | SHORT 137 | strcspn(PSZ str, PSZ match); 138 | 139 | // vim:sw=4:ts=4:et: 140 | -------------------------------------------------------------------------------- /crt/asm/afulrem.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; AFULREM.ASM 3 | ; 4 | ; Implementation for far signed and unsigned division of a 32 bit integer 5 | ; by a 16 bit integer, returning the remainder (ie., mod.) 6 | ; 7 | ; Copyright (c) 2017-2023 Malcolm J. Smith 8 | ; 9 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 10 | ; of this software and associated documentation files (the "Software"), to deal 11 | ; in the Software without restriction, including without limitation the rights 12 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | ; copies of the Software, and to permit persons to whom the Software is 14 | ; furnished to do so, subject to the following conditions: 15 | ; 16 | ; The above copyright notice and this permission notice shall be included in 17 | ; all copies or substantial portions of the Software. 18 | ; 19 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | ; THE SOFTWARE. 26 | ; 27 | 28 | .MODEL large, pascal 29 | 30 | .CODE _TEXT 31 | 32 | 33 | public __aFulrem 34 | __aFulrem proc 35 | 36 | push bp 37 | mov bp, sp 38 | 39 | ; DWORD [High DX, Low AX] 40 | ; __aFulrem( 41 | ; DWORD Divisor, [High BP + 12, Low BP + 10] 42 | ; DWORD Dividend [High BP + 8, Low BP + 6] 43 | ; ); 44 | 45 | ; This implementation doesn't support 32 bit divisors. If one is specified, 46 | ; fail. 47 | 48 | mov ax, [bp + 12] 49 | test ax, ax 50 | jnz aulrem_overflow 51 | 52 | ; Divide the high 16 bits by the low 16 bits, and leave the 53 | ; remainder in dx. Then, divide the low 16 bits plus the 54 | ; remainder, which must fit in a 16 bit value. To satisfy 55 | ; the calling convention, move dx to ax, clear dx, and return. 56 | 57 | mov cx, [bp + 10] 58 | xor dx, dx 59 | mov ax, [bp + 8] 60 | div cx 61 | mov ax, [bp + 6] 62 | div cx 63 | mov ax, dx 64 | xor dx, dx 65 | mov sp, bp 66 | pop bp 67 | ret 8 68 | 69 | aulrem_overflow: 70 | int 3 71 | xor dx, dx 72 | xor ax, ax 73 | mov sp, bp 74 | pop bp 75 | ret 8 76 | 77 | __aFulrem endp 78 | 79 | 80 | ; "Assignment" function that just does the above and puts the result in a 81 | ; pointer. I can't believe the compiler is too stupid to do this itself. 82 | ; The compiler is smart enough to know that ds is initialized, so it can 83 | ; invoke the "near" version of code in a purely far model program. 84 | 85 | public __aFNaulrem 86 | __aFNaulrem proc 87 | 88 | push bp 89 | mov bp, sp 90 | 91 | ; VOID 92 | ; __aFNaulrem( 93 | ; DWORD Divisor, [High BP + 10, Low BP + 8] 94 | ; DWORD __near * Dividend [BP + 6] 95 | ; ); 96 | 97 | mov bx, [bp + 6] 98 | push [bp + 10] 99 | push [bp + 8] 100 | push [bx + 2] 101 | push [bx] 102 | 103 | call __aFulrem 104 | 105 | mov [bx + 2], dx 106 | mov [bx], ax 107 | 108 | mov sp, bp 109 | pop bp 110 | ret 6 111 | 112 | __aFNaulrem endp 113 | 114 | 115 | END 116 | -------------------------------------------------------------------------------- /crt/asm/anulrem.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; ANULREM.ASM 3 | ; 4 | ; Implementation for near signed and unsigned division of a 32 bit integer 5 | ; by a 16 bit integer, returning the remainder (ie., mod.) 6 | ; 7 | ; Copyright (c) 2017-2023 Malcolm J. Smith 8 | ; 9 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 10 | ; of this software and associated documentation files (the "Software"), to deal 11 | ; in the Software without restriction, including without limitation the rights 12 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | ; copies of the Software, and to permit persons to whom the Software is 14 | ; furnished to do so, subject to the following conditions: 15 | ; 16 | ; The above copyright notice and this permission notice shall be included in 17 | ; all copies or substantial portions of the Software. 18 | ; 19 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | ; THE SOFTWARE. 26 | ; 27 | 28 | .MODEL small, pascal 29 | 30 | .CODE _TEXT 31 | 32 | 33 | public __aNulrem 34 | __aNulrem proc 35 | 36 | push bp 37 | mov bp, sp 38 | 39 | ; DWORD [High DX, Low AX] 40 | ; __aNulrem( 41 | ; DWORD Divisor, [High BP + 10, Low BP + 8] 42 | ; DWORD Dividend [High BP + 6, Low BP + 4] 43 | ; ); 44 | 45 | ; This implementation doesn't support 32 bit divisors. If one is specified, 46 | ; fail. 47 | 48 | mov ax, [bp + 10] 49 | test ax, ax 50 | jnz aulrem_overflow 51 | 52 | ; Divide the high 16 bits by the low 16 bits, and leave the 53 | ; remainder in dx. Then, divide the low 16 bits plus the 54 | ; remainder, which must fit in a 16 bit value. To satisfy 55 | ; the calling convention, move dx to ax, clear dx, and return. 56 | 57 | mov cx, [bp + 8] 58 | xor dx, dx 59 | mov ax, [bp + 6] 60 | div cx 61 | mov ax, [bp + 4] 62 | div cx 63 | mov ax, dx 64 | xor dx, dx 65 | mov sp, bp 66 | pop bp 67 | ret 8 68 | 69 | aulrem_overflow: 70 | int 3 71 | xor dx, dx 72 | xor ax, ax 73 | mov sp, bp 74 | pop bp 75 | ret 8 76 | 77 | __aNulrem endp 78 | 79 | 80 | ; "Assignment" function that just does the above and puts the result in a 81 | ; pointer. I can't believe the compiler is too stupid to do this itself. 82 | ; The compiler is smart enough to know that ds is initialized, so it can 83 | ; invoke the "near" version of code in a purely far model program. 84 | 85 | public __aNNaulrem 86 | __aNNaulrem proc 87 | 88 | push bp 89 | mov bp, sp 90 | 91 | ; VOID 92 | ; __aNNaulrem( 93 | ; DWORD Divisor, [High BP + 8, Low BP + 6] 94 | ; DWORD __near * Dividend [BP + 4] 95 | ; ); 96 | 97 | mov bx, [bp + 4] 98 | push [bp + 8] 99 | push [bp + 6] 100 | push [bx + 2] 101 | push [bx] 102 | 103 | call __aNulrem 104 | 105 | mov [bx + 2], dx 106 | mov [bx], ax 107 | 108 | mov sp, bp 109 | pop bp 110 | ret 6 111 | 112 | __aNNaulrem endp 113 | 114 | 115 | END 116 | -------------------------------------------------------------------------------- /crt/asm/afuldiv.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; AFULDIV.ASM 3 | ; 4 | ; Implementation for far signed and unsigned division of a 32 bit integer 5 | ; by a 16 bit integer. 6 | ; 7 | ; Copyright (c) 2017-2023 Malcolm J. Smith 8 | ; 9 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 10 | ; of this software and associated documentation files (the "Software"), to deal 11 | ; in the Software without restriction, including without limitation the rights 12 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | ; copies of the Software, and to permit persons to whom the Software is 14 | ; furnished to do so, subject to the following conditions: 15 | ; 16 | ; The above copyright notice and this permission notice shall be included in 17 | ; all copies or substantial portions of the Software. 18 | ; 19 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | ; THE SOFTWARE. 26 | ; 27 | 28 | .MODEL large, pascal 29 | 30 | .CODE _TEXT 31 | 32 | 33 | public __aFuldiv 34 | __aFuldiv proc 35 | 36 | push bp 37 | mov bp, sp 38 | 39 | ; DWORD [High DX, Low AX] 40 | ; __aFuldiv( 41 | ; DWORD Divisor, [High BP + 12, Low BP + 10] 42 | ; DWORD Dividend [High BP + 8, Low BP + 6] 43 | ; ); 44 | 45 | ; This implementation doesn't support 32 bit divisors. If one is specified, 46 | ; fail. 47 | 48 | mov ax, [bp + 12] 49 | test ax, ax 50 | jnz auldiv_overflow 51 | 52 | push bx 53 | 54 | ; Divide the high 16 bits by the low 16 bits, save it in bx, 55 | ; and leave the remainder in dx. Then, divide the low 16 bits 56 | ; plus the remainder, which must fit in a 16 bit value. To satisfy 57 | ; the calling convention, move bx to dx, and return. 58 | 59 | mov cx, [bp + 10] 60 | xor dx, dx 61 | mov ax, [bp + 8] 62 | div cx 63 | mov bx, ax 64 | mov ax, [bp + 6] 65 | div cx 66 | mov dx, bx 67 | pop bx 68 | mov sp, bp 69 | pop bp 70 | ret 8 71 | 72 | auldiv_overflow: 73 | int 3 74 | xor dx, dx 75 | xor ax, ax 76 | mov sp, bp 77 | pop bp 78 | ret 8 79 | 80 | __aFuldiv endp 81 | 82 | 83 | ; "Assignment" function that just does the above and puts the result in a 84 | ; pointer. I can't believe the compiler is too stupid to do this itself. 85 | ; The compiler is smart enough to know that ds is initialized, so it can 86 | ; invoke the "near" version of code in a purely far model program. 87 | 88 | public __aFNauldiv 89 | __aFNauldiv proc 90 | 91 | push bp 92 | mov bp, sp 93 | 94 | ; VOID 95 | ; __aFNauldiv( 96 | ; DWORD Divisor, [High BP + 10, Low BP + 8] 97 | ; DWORD __near * Dividend [BP + 6] 98 | ; ); 99 | 100 | mov bx, [bp + 6] 101 | push [bp + 10] 102 | push [bp + 8] 103 | push [bx + 2] 104 | push [bx] 105 | 106 | call __aFuldiv 107 | 108 | mov [bx + 2], dx 109 | mov [bx], ax 110 | 111 | mov sp, bp 112 | pop bp 113 | ret 6 114 | 115 | __aFNauldiv endp 116 | 117 | 118 | END 119 | -------------------------------------------------------------------------------- /crt/asm/anuldiv.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; ANULDIV.ASM 3 | ; 4 | ; Implementation for near signed and unsigned division of a 32 bit integer 5 | ; by a 16 bit integer for programs with near code segments. 6 | ; 7 | ; Copyright (c) 2017-2023 Malcolm J. Smith 8 | ; 9 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 10 | ; of this software and associated documentation files (the "Software"), to deal 11 | ; in the Software without restriction, including without limitation the rights 12 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | ; copies of the Software, and to permit persons to whom the Software is 14 | ; furnished to do so, subject to the following conditions: 15 | ; 16 | ; The above copyright notice and this permission notice shall be included in 17 | ; all copies or substantial portions of the Software. 18 | ; 19 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | ; THE SOFTWARE. 26 | ; 27 | 28 | .MODEL small, pascal 29 | 30 | .CODE _TEXT 31 | 32 | 33 | public __aNuldiv 34 | __aNuldiv proc 35 | 36 | push bp 37 | mov bp, sp 38 | 39 | ; DWORD [High DX, Low AX] 40 | ; __aNuldiv( 41 | ; DWORD Divisor, [High BP + 10, Low BP + 8] 42 | ; DWORD Dividend [High BP + 6, Low BP + 4] 43 | ; ); 44 | 45 | ; This implementation doesn't support 32 bit divisors. If one is specified, 46 | ; fail. 47 | 48 | mov ax, [bp + 10] 49 | test ax, ax 50 | jnz auldiv_overflow 51 | 52 | push bx 53 | 54 | ; Divide the high 16 bits by the low 16 bits, save it in bx, 55 | ; and leave the remainder in dx. Then, divide the low 16 bits 56 | ; plus the remainder, which must fit in a 16 bit value. To satisfy 57 | ; the calling convention, move bx to dx, and return. 58 | 59 | mov cx, [bp + 8] 60 | xor dx, dx 61 | mov ax, [bp + 6] 62 | div cx 63 | mov bx, ax 64 | mov ax, [bp + 4] 65 | div cx 66 | mov dx, bx 67 | pop bx 68 | mov sp, bp 69 | pop bp 70 | ret 8 71 | 72 | auldiv_overflow: 73 | int 3 74 | xor dx, dx 75 | xor ax, ax 76 | mov sp, bp 77 | pop bp 78 | ret 8 79 | 80 | __aNuldiv endp 81 | 82 | 83 | ; "Assignment" function that just does the above and puts the result in a 84 | ; pointer. I can't believe the compiler is too stupid to do this itself. 85 | ; The compiler is smart enough to know that ds is initialized, so it can 86 | ; invoke the "near" version of code in a purely far model program. 87 | 88 | public __aNNauldiv 89 | __aNNauldiv proc 90 | 91 | push bp 92 | mov bp, sp 93 | 94 | ; VOID 95 | ; __aNNauldiv( 96 | ; DWORD Divisor, [High BP + 8, Low BP + 6] 97 | ; DWORD __near * Dividend [BP + 4] 98 | ; ); 99 | 100 | mov bx, [bp + 4] 101 | push [bp + 8] 102 | push [bp + 6] 103 | push [bx + 2] 104 | push [bx] 105 | 106 | call __aNuldiv 107 | 108 | mov [bx + 2], dx 109 | mov [bx], ax 110 | 111 | mov sp, bp 112 | pop bp 113 | ret 6 114 | 115 | __aNNauldiv endp 116 | 117 | END 118 | -------------------------------------------------------------------------------- /crt/mem.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file crt/mem.c 3 | * 4 | * OS/2 based memory manipulation routines. 5 | * 6 | * Copyright (c) 2023 Malcolm J. Smith 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | #include 27 | #include 28 | 29 | PVOID 30 | memcpy(PVOID dest, PVOID src, WORD len) 31 | { 32 | WORD i; 33 | PUCHAR char_src = (PUCHAR)src; 34 | PUCHAR char_dest = (PUCHAR)dest; 35 | for (i = 0; i < len; i++) { 36 | char_dest[i] = char_src[i]; 37 | } 38 | return dest; 39 | } 40 | 41 | PVOID 42 | memmove(PVOID dest, PVOID src, WORD len) 43 | { 44 | WORD i; 45 | PUCHAR char_src = (PUCHAR)src; 46 | PUCHAR char_dest = (PUCHAR)dest; 47 | if (char_dest > char_src) { 48 | if (len == 0) { 49 | return dest; 50 | } 51 | for (i = len - 1; ; i--) { 52 | char_dest[i] = char_src[i]; 53 | if (i==0) break; 54 | } 55 | } else { 56 | for (i = 0; i < len; i++) { 57 | char_dest[i] = char_src[i]; 58 | } 59 | } 60 | return dest; 61 | } 62 | 63 | PVOID 64 | memset(PVOID dest, CHAR c, WORD len) 65 | { 66 | WORD i; 67 | WORD fill; 68 | WORD chunks = len / sizeof(fill); 69 | PCHAR char_dest = (PCHAR)dest; 70 | PWORD word_dest = (PWORD)dest; 71 | 72 | // 73 | // Note we go from the back to the front. This is to 74 | // prevent newer compilers from noticing what we're doing 75 | // and trying to invoke the built-in memset instead of us. 76 | // 77 | 78 | fill = (c<<8) + c; 79 | 80 | for (i = len; i > chunks * sizeof(fill); i--) { 81 | char_dest[i - 1] = c; 82 | } 83 | 84 | for (i = chunks; i > 0; i--) { 85 | word_dest[i - 1] = fill; 86 | } 87 | 88 | return dest; 89 | } 90 | 91 | SHORT 92 | memcmp(PVOID buf1, PVOID buf2, WORD len) 93 | { 94 | WORD i = 0; 95 | PUCHAR char_buf1 = (PUCHAR)buf1; 96 | PUCHAR char_buf2 = (PUCHAR)buf2; 97 | for (i = 0; i < len; i++) { 98 | if (char_buf1[i] < char_buf2[i]) { 99 | return -1; 100 | } else if (char_buf1[i] > char_buf2[i]) { 101 | return 1; 102 | } 103 | } 104 | return 0; 105 | } 106 | 107 | // vim:sw=4:ts=4:et: 108 | -------------------------------------------------------------------------------- /crt/asm/afulshl.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; AFULSHL.ASM 3 | ; 4 | ; Implementation for far signed and unsigned left shift of a 32 bit integer. 5 | ; 6 | ; Copyright (c) 2017-2023 Malcolm J. Smith 7 | ; 8 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 9 | ; of this software and associated documentation files (the "Software"), to deal 10 | ; in the Software without restriction, including without limitation the rights 11 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | ; copies of the Software, and to permit persons to whom the Software is 13 | ; furnished to do so, subject to the following conditions: 14 | ; 15 | ; The above copyright notice and this permission notice shall be included in 16 | ; all copies or substantial portions of the Software. 17 | ; 18 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | ; THE SOFTWARE. 25 | ; 26 | 27 | .MODEL large, pascal 28 | .CODE _TEXT 29 | 30 | ; DWORD [High DX, Low AX] 31 | ; __aFulshl( 32 | ; DWORD Value, [High DX, Low AX] 33 | ; WORD Shift [CL] 34 | ; ); 35 | 36 | public __aFulshl 37 | __aFulshl proc 38 | 39 | ; If the shift is for more than 32 bits, all the data would be gone, so 40 | ; return zero. If the shift is for more than 16 bits, the low 16 bits 41 | ; of the result must be zero, and the high 16 bits contains the low 16 42 | ; bits of input after shifting. If the shift is less than 16 bits, then 43 | ; both components must be shifted with bits carried between the two. 44 | 45 | cmp cl,32 46 | jae ulshl_no_shift 47 | cmp cl,16 48 | jae ulshl_long_shift 49 | 50 | push bx 51 | mov bx, ax 52 | shl ax, cl 53 | shl dx, cl 54 | sub cl, 16 55 | neg cl 56 | shr bx, cl 57 | or dx, bx 58 | pop bx 59 | ret 60 | 61 | ulshl_long_shift: 62 | sub cl, 16 63 | shl ax, cl 64 | mov dx, ax 65 | xor ax, ax 66 | ret 67 | 68 | ulshl_no_shift: 69 | xor ax, ax 70 | xor dx, dx 71 | ret 72 | 73 | __aFulshl endp 74 | 75 | ; DWORD [High DX, Low AX] 76 | ; _aFlshl( 77 | ; DWORD Value, [High DX, Low AX] 78 | ; WORD Shift [CL] 79 | ; ); 80 | 81 | ; It's not clear to me what the meaning of a signed bitshift is, but for now 82 | ; give it to the unsigned implementation. 83 | 84 | public __aFlshl 85 | __aFlshl proc 86 | jmp __aFulshl 87 | __aFlshl endp 88 | 89 | ; "Assignment" function that just does the above and puts the result in a 90 | ; pointer. I can't believe the compiler is too stupid to do this itself. 91 | ; The compiler is smart enough to know that ds is initialized, so it can 92 | ; invoke the "near" version of code in a purely far model program. 93 | 94 | public __aFNaulshl 95 | __aFNaulshl proc 96 | 97 | push bp 98 | mov bp, sp 99 | 100 | ; VOID 101 | ; __aFNaulshl( 102 | ; WORD ShiftCount, [BP + 8] 103 | ; DWORD __near * Value [BP + 6] 104 | ; ); 105 | 106 | push bx 107 | mov bx, [bp + 6] 108 | mov ax, [bx] 109 | mov dx, [bx + 2] 110 | mov cx, [bp + 8] 111 | 112 | call __aFulshl 113 | 114 | mov [bx + 2], dx 115 | mov [bx], ax 116 | 117 | pop bx 118 | mov sp, bp 119 | pop bp 120 | ret 4 121 | 122 | __aFNaulshl endp 123 | 124 | 125 | END 126 | -------------------------------------------------------------------------------- /crt/asm/afulshr.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; AFULSHR.ASM 3 | ; 4 | ; Implementation for far signed and unsigned right shift of a 32 bit integer. 5 | ; 6 | ; Copyright (c) 2017-2023 Malcolm J. Smith 7 | ; 8 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 9 | ; of this software and associated documentation files (the "Software"), to deal 10 | ; in the Software without restriction, including without limitation the rights 11 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | ; copies of the Software, and to permit persons to whom the Software is 13 | ; furnished to do so, subject to the following conditions: 14 | ; 15 | ; The above copyright notice and this permission notice shall be included in 16 | ; all copies or substantial portions of the Software. 17 | ; 18 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | ; THE SOFTWARE. 25 | ; 26 | 27 | .MODEL large, pascal 28 | .CODE _TEXT 29 | 30 | ; DWORD [High DX, Low AX] 31 | ; __aFulshr( 32 | ; DWORD Value, [High DX, Low AX] 33 | ; WORD Shift [CL] 34 | ; ); 35 | 36 | public __aFulshr 37 | __aFulshr proc 38 | 39 | ; If the shift is for more than 32 bits, all the data would be gone, so 40 | ; return zero. If the shift is for more than 16 bits, the high 16 bits 41 | ; of the result must be zero, and the low 16 bits contains the high 16 42 | ; bits of input after shifting. If the shift is less than 16 bits, then 43 | ; both components must be shifted with bits carried between the two. 44 | 45 | cmp cl,32 46 | jae ulshr_no_shift 47 | cmp cl,16 48 | jae ulshr_long_shift 49 | 50 | push bx 51 | mov bx, dx 52 | shr ax, cl 53 | shr dx, cl 54 | sub cl, 16 55 | neg cl 56 | shl bx, cl 57 | or ax, bx 58 | pop bx 59 | ret 60 | 61 | ulshr_long_shift: 62 | sub cl, 16 63 | shr dx, cl 64 | mov ax, dx 65 | xor dx, dx 66 | ret 67 | 68 | ulshr_no_shift: 69 | xor ax, ax 70 | xor dx, dx 71 | ret 72 | 73 | __aFulshr endp 74 | 75 | ; DWORD [High DX, Low AX] 76 | ; _aFlshr( 77 | ; DWORD Value, [High DX, Low AX] 78 | ; WORD Shift [CL] 79 | ; ); 80 | 81 | ; It's not clear to me what the meaning of a signed bitshift is, but for now 82 | ; give it to the unsigned implementation. 83 | 84 | public __aFlshr 85 | __aFlshr proc 86 | jmp __aFulshr 87 | __aFlshr endp 88 | 89 | 90 | ; "Assignment" function that just does the above and puts the result in a 91 | ; pointer. I can't believe the compiler is too stupid to do this itself. 92 | ; The compiler is smart enough to know that ds is initialized, so it can 93 | ; invoke the "near" version of code in a purely far model program. 94 | 95 | public __aFNaulshr 96 | __aFNaulshr proc 97 | 98 | push bp 99 | mov bp, sp 100 | 101 | ; VOID 102 | ; __aFNaulshr( 103 | ; WORD ShiftCount, [BP + 8] 104 | ; DWORD __near * Value [BP + 6] 105 | ; ); 106 | 107 | push bx 108 | mov bx, [bp + 6] 109 | mov ax, [bx] 110 | mov dx, [bx + 2] 111 | mov cx, [bp + 8] 112 | 113 | call __aFulshr 114 | 115 | mov [bx + 2], dx 116 | mov [bx], ax 117 | 118 | pop bx 119 | mov sp, bp 120 | pop bp 121 | ret 4 122 | 123 | __aFNaulshr endp 124 | 125 | END 126 | -------------------------------------------------------------------------------- /crt/asm/anulshl.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; ANULSHL.ASM 3 | ; 4 | ; Implementation for near signed and unsigned left shift of a 32 bit integer. 5 | ; 6 | ; Copyright (c) 2017-2023 Malcolm J. Smith 7 | ; 8 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 9 | ; of this software and associated documentation files (the "Software"), to deal 10 | ; in the Software without restriction, including without limitation the rights 11 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | ; copies of the Software, and to permit persons to whom the Software is 13 | ; furnished to do so, subject to the following conditions: 14 | ; 15 | ; The above copyright notice and this permission notice shall be included in 16 | ; all copies or substantial portions of the Software. 17 | ; 18 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | ; THE SOFTWARE. 25 | ; 26 | 27 | .MODEL small, pascal 28 | .CODE _TEXT 29 | 30 | ; DWORD [High DX, Low AX] 31 | ; __aNulshl( 32 | ; DWORD Value, [High DX, Low AX] 33 | ; WORD Shift [CL] 34 | ; ); 35 | 36 | public __aNulshl 37 | __aNulshl proc 38 | 39 | ; If the shift is for more than 32 bits, all the data would be gone, so 40 | ; return zero. If the shift is for more than 16 bits, the low 16 bits 41 | ; of the result must be zero, and the high 16 bits contains the low 16 42 | ; bits of input after shifting. If the shift is less than 16 bits, then 43 | ; both components must be shifted with bits carried between the two. 44 | 45 | cmp cl,32 46 | jae ulshl_no_shift 47 | cmp cl,16 48 | jae ulshl_long_shift 49 | 50 | push bx 51 | mov bx, ax 52 | shl ax, cl 53 | shl dx, cl 54 | sub cl, 16 55 | neg cl 56 | shr bx, cl 57 | or dx, bx 58 | pop bx 59 | ret 60 | 61 | ulshl_long_shift: 62 | sub cl, 16 63 | shl ax, cl 64 | mov dx, ax 65 | xor ax, ax 66 | ret 67 | 68 | ulshl_no_shift: 69 | xor ax, ax 70 | xor dx, dx 71 | ret 72 | 73 | __aNulshl endp 74 | 75 | ; DWORD [High DX, Low AX] 76 | ; _aNlshl( 77 | ; DWORD Value, [High DX, Low AX] 78 | ; WORD Shift [CL] 79 | ; ); 80 | 81 | ; It's not clear to me what the meaning of a signed bitshift is, but for now 82 | ; give it to the unsigned implementation. 83 | 84 | public __aNlshl 85 | __aNlshl proc 86 | jmp __aNulshl 87 | __aNlshl endp 88 | 89 | ; "Assignment" function that just does the above and puts the result in a 90 | ; pointer. I can't believe the compiler is too stupid to do this itself. 91 | ; The compiler is smart enough to know that ds is initialized, so it can 92 | ; invoke the "near" version of code in a purely far model program. 93 | 94 | public __aNNaulshl 95 | __aNNaulshl proc 96 | 97 | push bp 98 | mov bp, sp 99 | 100 | ; VOID 101 | ; __aNNaulshl( 102 | ; WORD ShiftCount, [BP + 6] 103 | ; DWORD __near * Value [BP + 4] 104 | ; ); 105 | 106 | push bx 107 | mov bx, [bp + 4] 108 | mov ax, [bx] 109 | mov dx, [bx + 2] 110 | mov cx, [bp + 6] 111 | 112 | call __aNulshl 113 | 114 | mov [bx + 2], dx 115 | mov [bx], ax 116 | 117 | pop bx 118 | mov sp, bp 119 | pop bp 120 | ret 4 121 | 122 | __aNNaulshl endp 123 | 124 | 125 | END 126 | -------------------------------------------------------------------------------- /crt/asm/anulshr.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; ANULSHR.ASM 3 | ; 4 | ; Implementation for for signed and unsigned right shift of a 32 bit integer. 5 | ; 6 | ; Copyright (c) 2017-2023 Malcolm J. Smith 7 | ; 8 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 9 | ; of this software and associated documentation files (the "Software"), to deal 10 | ; in the Software without restriction, including without limitation the rights 11 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | ; copies of the Software, and to permit persons to whom the Software is 13 | ; furnished to do so, subject to the following conditions: 14 | ; 15 | ; The above copyright notice and this permission notice shall be included in 16 | ; all copies or substantial portions of the Software. 17 | ; 18 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | ; THE SOFTWARE. 25 | ; 26 | 27 | .MODEL small, pascal 28 | .CODE _TEXT 29 | 30 | ; DWORD [High DX, Low AX] 31 | ; __aNulshr( 32 | ; DWORD Value, [High DX, Low AX] 33 | ; WORD Shift [CL] 34 | ; ); 35 | 36 | public __aNulshr 37 | __aNulshr proc 38 | 39 | ; If the shift is for more than 32 bits, all the data would be gone, so 40 | ; return zero. If the shift is for more than 16 bits, the high 16 bits 41 | ; of the result must be zero, and the low 16 bits contains the high 16 42 | ; bits of input after shifting. If the shift is less than 16 bits, then 43 | ; both components must be shifted with bits carried between the two. 44 | 45 | cmp cl,32 46 | jae ulshr_no_shift 47 | cmp cl,16 48 | jae ulshr_long_shift 49 | 50 | push bx 51 | mov bx, dx 52 | shr ax, cl 53 | shr dx, cl 54 | sub cl, 16 55 | neg cl 56 | shl bx, cl 57 | or ax, bx 58 | pop bx 59 | ret 60 | 61 | ulshr_long_shift: 62 | sub cl, 16 63 | shr dx, cl 64 | mov ax, dx 65 | xor dx, dx 66 | ret 67 | 68 | ulshr_no_shift: 69 | xor ax, ax 70 | xor dx, dx 71 | ret 72 | 73 | __aNulshr endp 74 | 75 | ; DWORD [High DX, Low AX] 76 | ; _aNlshr( 77 | ; DWORD Value, [High DX, Low AX] 78 | ; WORD Shift [CL] 79 | ; ); 80 | 81 | ; It's not clear to me what the meaning of a signed bitshift is, but for now 82 | ; give it to the unsigned implementation. 83 | 84 | public __aNlshr 85 | __aNlshr proc 86 | jmp __aNulshr 87 | __aNlshr endp 88 | 89 | 90 | ; "Assignment" function that just does the above and puts the result in a 91 | ; pointer. I can't believe the compiler is too stupid to do this itself. 92 | ; The compiler is smart enough to know that ds is initialized, so it can 93 | ; invoke the "near" version of code in a purely far model program. 94 | 95 | public __aNNaulshr 96 | __aNNaulshr proc 97 | 98 | push bp 99 | mov bp, sp 100 | 101 | ; VOID 102 | ; __aNNaulshr( 103 | ; WORD ShiftCount, [BP + 6] 104 | ; DWORD __near * Value [BP + 4] 105 | ; ); 106 | 107 | push bx 108 | mov bx, [bp + 4] 109 | mov ax, [bx] 110 | mov dx, [bx + 2] 111 | mov cx, [bp + 6] 112 | 113 | call __aNulshr 114 | 115 | mov [bx + 2], dx 116 | mov [bx], ax 117 | 118 | pop bx 119 | mov sp, bp 120 | pop bp 121 | ret 4 122 | 123 | __aNNaulshr endp 124 | 125 | END 126 | -------------------------------------------------------------------------------- /dosfapi/asm/vidmode.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; VIDMODE.ASM 3 | ; 4 | ; Get video mode. 5 | ; 6 | ; Copyright (c) 2023 Malcolm J. Smith 7 | ; 8 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 9 | ; of this software and associated documentation files (the "Software"), to deal 10 | ; in the Software without restriction, including without limitation the rights 11 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | ; copies of the Software, and to permit persons to whom the Software is 13 | ; furnished to do so, subject to the following conditions: 14 | ; 15 | ; The above copyright notice and this permission notice shall be included in 16 | ; all copies or substantial portions of the Software. 17 | ; 18 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | ; THE SOFTWARE. 25 | ; 26 | 27 | .MODEL large, pascal 28 | 29 | .CODE _TEXT 30 | 31 | public VioGetMode 32 | VioGetMode proc 33 | 34 | push bp 35 | mov bp, sp 36 | 37 | ; SYSERR APIENTRY 38 | ; VioGetMode( 39 | ; PVIOMODEINFO pMode, [High BP + 10, Low BP + 8] 40 | ; HVIO hvio [BP + 6] 41 | ; ); 42 | 43 | push ds 44 | push bx 45 | push di 46 | mov ds, [bp + 10] 47 | mov di, [bp + 8] 48 | 49 | xor ax, ax 50 | mov word ptr [di + 2], ax 51 | mov word ptr [di + 4], ax 52 | mov word ptr [di + 6], ax 53 | mov word ptr [di + 8], ax 54 | mov word ptr [di + 10], ax 55 | 56 | ; Ask for the EGA font, which can return the highest line number on the 57 | ; display. Zero dx before the call and if it's nonzero afterwards, use 58 | ; that. 59 | push es 60 | push bp 61 | mov ax, 01130h 62 | mov bh, 03h 63 | int 10h 64 | pop bp 65 | pop es 66 | test al, al 67 | jz VioGetModeGetVideoMode 68 | 69 | mov dh, 0 70 | inc dl 71 | mov word ptr [di + 6], dx 72 | 73 | ; Query the display mode. This returns the number of columns. It also 74 | ; returns the display mode which can be used to infer the line count if 75 | ; we didn't get a better answer earlier. 76 | VioGetModeGetVideoMode: 77 | mov ah, 0fh 78 | int 10h 79 | 80 | mov bl, ah 81 | mov bh, 0 82 | 83 | mov word ptr [di + 4], bx 84 | test dx, dx 85 | jnz VioGetModeDone 86 | 87 | mov word ptr [di + 6], 25 88 | cmp al, 17 89 | je VioGetMode30Lines 90 | 91 | cmp al, 23 92 | je VioGetMode43Lines 93 | cmp al, 25 94 | je VioGetMode43Lines 95 | cmp al, 29 96 | je VioGetMode43Lines 97 | cmp al, 66 98 | je VioGetMode43Lines 99 | cmp al, 84 100 | je VioGetMode43Lines 101 | cmp al, 86 102 | je VioGetMode43Lines 103 | cmp al, 88 104 | je VioGetMode43Lines 105 | 106 | cmp al, 81 107 | je VioGetMode50Lines 108 | cmp al, 97 109 | je VioGetMode50Lines 110 | cmp al, 102 111 | je VioGetMode50Lines 112 | cmp al, 105 113 | je VioGetMode50Lines 114 | 115 | cmp al, 30 116 | je VioGetMode60Lines 117 | cmp al, 33 118 | je VioGetMode60Lines 119 | cmp al, 38 120 | je VioGetMode60Lines 121 | cmp al, 67 122 | je VioGetMode60Lines 123 | cmp al, 82 124 | je VioGetMode60Lines 125 | cmp al, 100 126 | je VioGetMode60Lines 127 | 128 | jmp VioGetModeDone 129 | 130 | VioGetMode30Lines: 131 | mov word ptr [di + 6], 30 132 | jmp VioGetModeDone 133 | 134 | VioGetMode43Lines: 135 | mov word ptr [di + 6], 43 136 | jmp VioGetModeDone 137 | 138 | VioGetMode50Lines: 139 | mov word ptr [di + 6], 50 140 | jmp VioGetModeDone 141 | 142 | VioGetMode60Lines: 143 | mov word ptr [di + 6], 60 144 | jmp VioGetModeDone 145 | 146 | 147 | VioGetModeDone: 148 | 149 | pop di 150 | pop bx 151 | pop ds 152 | 153 | xor ax, ax 154 | 155 | mov sp, bp 156 | pop bp 157 | 158 | ret 6 159 | 160 | VioGetMode endp 161 | 162 | END 163 | -------------------------------------------------------------------------------- /crt/string.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file crt/string.c 3 | * 4 | * OS/2 based string manipulation routines. 5 | * 6 | * Copyright (c) 2023 Malcolm J. Smith 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | 27 | #include 28 | #include 29 | 30 | 31 | SHORT 32 | atoi (PSZ str) 33 | { 34 | SHORT ret = 0; 35 | while (*str >= '0' && *str <= '9') { 36 | ret *= 10; 37 | ret += *str - '0'; 38 | str++; 39 | } 40 | return ret; 41 | } 42 | 43 | PSZ 44 | strcat_s(PSZ dest, WORD len, const PSZ src) 45 | { 46 | WORD i,j; 47 | for (i = 0; dest[i] != '\0' && i < len; i++); 48 | for (j = 0; src[j] != '\0' && i < len - 1; ) { 49 | dest[i++] = src[j++]; 50 | } 51 | dest[i++] = '\0'; 52 | return dest; 53 | } 54 | 55 | PSZ 56 | strncat(PSZ dest, const PSZ src, WORD len) 57 | { 58 | WORD i,j; 59 | for (i = 0; dest[i] != '\0'; i++); 60 | for (j = 0; src[j] != '\0' && j < len; ) { 61 | dest[i++] = src[j++]; 62 | } 63 | dest[i++] = '\0'; 64 | return dest; 65 | } 66 | 67 | PSZ 68 | strchr(PSZ str, UCHAR ch) 69 | { 70 | const UCHAR FAR * ptr = str; 71 | while (*ptr != '\0' && *ptr != ch) ptr++; 72 | if (*ptr == ch) return (PSZ)ptr; 73 | return NULL; 74 | } 75 | 76 | PSZ 77 | strrchr(PSZ str, UCHAR ch) 78 | { 79 | const UCHAR * ptr = str; 80 | while (*ptr != '\0') ptr++; 81 | while (*ptr != ch && ptr > str) ptr--; 82 | if (*ptr == ch) return (PSZ)ptr; 83 | return NULL; 84 | } 85 | 86 | WORD 87 | strlen(PSZ str) 88 | { 89 | WORD i = 0; 90 | while (str[i] != '\0') { 91 | i++; 92 | } 93 | return i; 94 | } 95 | 96 | PSZ 97 | strstr(const PSZ str, PSZ search) 98 | { 99 | PSZ ptr = str; 100 | int i; 101 | while (*ptr != '\0') { 102 | for (i=0;ptr[i]==search[i]&&search[i]!='\0'&&ptr[i]!='\0';i++); 103 | if (search[i]=='\0') return (PSZ)ptr; 104 | ptr++; 105 | } 106 | return NULL; 107 | } 108 | 109 | UCHAR 110 | toupper(UCHAR c) 111 | { 112 | if (c >= 'a' && c <= 'z') { 113 | return c - 'a' + 'A'; 114 | } 115 | return c; 116 | } 117 | 118 | UCHAR 119 | tolower(UCHAR c) 120 | { 121 | if (c >= 'A' && c <= 'Z') { 122 | return c - 'A' + 'a'; 123 | } 124 | return c; 125 | } 126 | 127 | PSZ 128 | strupr(PSZ str) 129 | { 130 | PSZ ptr = str; 131 | while (*ptr != '\0') { 132 | *ptr = (UCHAR)toupper(*ptr); 133 | ptr++; 134 | } 135 | return str; 136 | } 137 | 138 | PSZ 139 | strlwr(PSZ str) 140 | { 141 | PSZ ptr = str; 142 | while (*ptr != '\0') { 143 | *ptr = (UCHAR)tolower(*ptr); 144 | ptr++; 145 | } 146 | return str; 147 | } 148 | 149 | SHORT 150 | strncmp(PSZ str1, PSZ str2, WORD count) 151 | { 152 | PSZ ptr1 = str1; 153 | PSZ ptr2 = str2; 154 | WORD remaining = count; 155 | 156 | while(remaining > 0) { 157 | if (*ptr1 < *ptr2) { 158 | return -1; 159 | } else if (*ptr1 > *ptr2) { 160 | return 1; 161 | } else if (*ptr1 == '\0') { 162 | return 0; 163 | } 164 | 165 | ptr1++; 166 | ptr2++; 167 | remaining--; 168 | } 169 | return 0; 170 | } 171 | 172 | SHORT 173 | strcmp(PSZ str1, PSZ str2) 174 | { 175 | return strncmp(str1, str2, (WORD)-1); 176 | } 177 | 178 | SHORT 179 | strnicmp(PSZ str1, PSZ str2, WORD count) 180 | { 181 | PSZ ptr1 = str1; 182 | PSZ ptr2 = str2; 183 | WORD remaining = count; 184 | 185 | while(remaining > 0) { 186 | if (toupper(*ptr1) < toupper(*ptr2)) { 187 | return -1; 188 | } else if (toupper(*ptr1) > toupper(*ptr2)) { 189 | return 1; 190 | } else if (*ptr1 == '\0') { 191 | return 0; 192 | } 193 | ptr1++; 194 | ptr2++; 195 | remaining--; 196 | } 197 | return 0; 198 | } 199 | 200 | SHORT 201 | stricmp(PSZ str1, PSZ str2) 202 | { 203 | return strnicmp(str1, str2, (WORD)-1); 204 | } 205 | 206 | PSZ 207 | strtok_s(PSZ str, PSZ match, PSZ * context) 208 | { 209 | PSZ next; 210 | if (str != NULL) { 211 | *context = str; 212 | } 213 | 214 | next = *context; 215 | if (next == NULL) { 216 | return NULL; 217 | } 218 | 219 | while (*next != match[0] && *next != '\0') next++; 220 | 221 | if (*next == match[0]) { 222 | PSZ ret = *context; 223 | *next = '\0'; 224 | *context = ++next; 225 | return ret; 226 | } else { 227 | PSZ ret = *context; 228 | *context = NULL; 229 | return ret; 230 | } 231 | } 232 | 233 | PSZ strtok_ctx; 234 | 235 | PSZ 236 | strtok(PSZ str, PSZ match) 237 | { 238 | return strtok_s(str, match, &strtok_ctx); 239 | } 240 | 241 | SHORT 242 | strspn(PSZ str, PSZ chars) 243 | { 244 | WORD len = 0; 245 | WORD i; 246 | 247 | for (len = 0; str[len] != '\0'; len++) { 248 | for (i = 0; chars[i] != '\0'; i++) { 249 | if (str[len] == chars[i]) { 250 | break; 251 | } 252 | } 253 | if (chars[i] == '\0') { 254 | return len; 255 | } 256 | } 257 | 258 | return len; 259 | } 260 | 261 | SHORT 262 | strcspn(PSZ str, PSZ match) 263 | { 264 | WORD len = 0; 265 | WORD i; 266 | 267 | for (len = 0; str[len] != '\0'; len++) { 268 | for (i = 0; match[i] != '\0'; i++) { 269 | if (str[len] == match[i]) { 270 | return len; 271 | } 272 | } 273 | } 274 | 275 | return len; 276 | } 277 | 278 | // vim:sw=4:ts=4:et: 279 | -------------------------------------------------------------------------------- /crt/startup1.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file crt/startup1.c 3 | * 4 | * OS/2 C based program initialization. The real entry point must be in 5 | * assembly but complex tasks can be deferred to C. 6 | * 7 | * Copyright (c) 2023 Malcolm J. Smith 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | PPSZ 33 | CmdlineToArgcArgv( 34 | PSZ ProgramName, 35 | PSZ CmdLine, 36 | WORD MaxArgs, 37 | PWORD argc 38 | ) 39 | { 40 | WORD ArgCount = 0; 41 | WORD CharCount = 0; 42 | WORD SlashCount; 43 | PCH c; 44 | PPSZ ArgvArray; 45 | PSZ ReturnStrings; 46 | BOOL EndArg; 47 | BOOL QuoteOpen; 48 | 49 | // 50 | // Consume all spaces. After this, we're either at 51 | // the end of string, or we have an arg, and it 52 | // might start with a quote 53 | // 54 | 55 | c = CmdLine; 56 | while (*c == ' ') c++; 57 | 58 | QuoteOpen = FALSE; 59 | 60 | while (*c != '\0') { 61 | EndArg = FALSE; 62 | 63 | if (*c == '\\') { 64 | for (SlashCount = 1; c[SlashCount] == '\\'; SlashCount++); 65 | if (c[SlashCount] == '"') { 66 | 67 | // 68 | // Because one char is left for regular processing, only 69 | // adjust for one less pair. Three slashes means consume 70 | // two chars, output one; four means consume three, output 71 | // one, etc. 72 | // 73 | 74 | if ((SlashCount % 2) == 0) { 75 | SlashCount--; 76 | } 77 | CharCount += SlashCount / 2; 78 | c += SlashCount; 79 | } 80 | } else if (*c == '"') { 81 | QuoteOpen = !QuoteOpen; 82 | c++; 83 | if (ArgCount < MaxArgs && *c == '\0') { 84 | ArgCount++; 85 | } 86 | continue; 87 | } else if (!QuoteOpen && *c == ' ') { 88 | EndArg = TRUE; 89 | } 90 | 91 | if (ArgCount + 1 < MaxArgs && EndArg) { 92 | c++; 93 | while (*c == ' ') c++; 94 | ArgCount++; 95 | } else { 96 | CharCount++; 97 | 98 | // 99 | // If we hit a break char, we count the argument then. 100 | // If we hit end of string, count it here; note we're 101 | // only counting it if we counted a character before it 102 | // (ie., trailing whitespace is not an arg.) 103 | // 104 | 105 | c++; 106 | 107 | if (ArgCount < MaxArgs && *c == '\0') { 108 | ArgCount++; 109 | } 110 | } 111 | } 112 | 113 | // 114 | // OS/2 clearly separates the program name from arguments, so there 115 | // is always going to be one string for the program name. 116 | // 117 | 118 | ArgCount++; 119 | *argc = ArgCount; 120 | 121 | ArgvArray = malloc( (ArgCount * sizeof(PSZ)) + (CharCount + ArgCount) * sizeof(UCHAR)); 122 | if (ArgvArray == NULL) { 123 | *argc = 0; 124 | return NULL; 125 | } 126 | 127 | ReturnStrings = (PSZ)(ArgvArray + ArgCount); 128 | 129 | ArgvArray[0] = ProgramName; 130 | if (ArgCount == 1) { 131 | return ArgvArray; 132 | } 133 | ArgCount = 1; 134 | ArgvArray[ArgCount] = ReturnStrings; 135 | 136 | // 137 | // Consume all spaces. After this, we're either at 138 | // the end of string, or we have an arg, and it 139 | // might start with a quote 140 | // 141 | 142 | c = CmdLine; 143 | while (*c == ' ') c++; 144 | QuoteOpen = FALSE; 145 | 146 | while (*c != '\0') { 147 | EndArg = FALSE; 148 | 149 | if (*c == '\\') { 150 | for (SlashCount = 1; c[SlashCount] == '\\'; SlashCount++); 151 | if (c[SlashCount] == '"') { 152 | 153 | // 154 | // Always add one character in the regular path, below. This 155 | // code therefore needs to process each double-slash except 156 | // the last one, and advance the c pointer to skip the first 157 | // slash of the last pair. After that can either be a slash 158 | // or a double quote, which will be processed as a regular 159 | // character below. 160 | // 161 | 162 | for (CharCount = 2; CharCount < SlashCount; CharCount += 2) { 163 | *ReturnStrings = '\\'; 164 | ReturnStrings++; 165 | c += 2; 166 | } 167 | c++; 168 | } 169 | } else if (*c == '"') { 170 | QuoteOpen = !QuoteOpen; 171 | c++; 172 | if (*c == '\0') { 173 | *ReturnStrings = '\0'; 174 | } 175 | continue; 176 | } else if (!QuoteOpen && *c == ' ') { 177 | EndArg = TRUE; 178 | } 179 | 180 | if (ArgCount + 1 < MaxArgs && EndArg) { 181 | *ReturnStrings = '\0'; 182 | ReturnStrings++; 183 | 184 | c++; 185 | while (*c == ' ') c++; 186 | if (*c != '\0') { 187 | ArgCount++; 188 | ArgvArray[ArgCount] = ReturnStrings; 189 | } 190 | } else { 191 | *ReturnStrings = *c; 192 | ReturnStrings++; 193 | 194 | // 195 | // If we hit a break char, we count the argument then. 196 | // If we hit end of string, count it here; note we're 197 | // only counting it if we counted a character before it 198 | // (ie., trailing whitespace is not an arg.) 199 | // 200 | 201 | c++; 202 | 203 | if (*c == '\0') { 204 | *ReturnStrings = '\0'; 205 | } 206 | } 207 | } 208 | 209 | return ArgvArray; 210 | } 211 | 212 | VOID APIENTRY __startup() 213 | { 214 | WORD exitCode; 215 | WORD argc; 216 | PPSZ argv; 217 | 218 | if (!malloc_init()) { 219 | DosExit(EXIT_PROCESS, 255); 220 | } 221 | 222 | { 223 | PSZ programName; 224 | PSZ commandArgs; 225 | 226 | programName = GetProgramName(); 227 | commandArgs = GetCommandArgs(); 228 | 229 | argv = CmdlineToArgcArgv(programName, commandArgs, (WORD)-1, &argc); 230 | if (argv == NULL) { 231 | DosExit(EXIT_PROCESS, 255); 232 | } 233 | } 234 | 235 | exitCode = main(argc, argv); 236 | if (argv != NULL) { 237 | free(argv); 238 | } 239 | malloc_cleanup(); 240 | DosExit(EXIT_PROCESS, exitCode); 241 | } 242 | -------------------------------------------------------------------------------- /dosfapi/asm/dosfind.asm: -------------------------------------------------------------------------------- 1 | ; 2 | ; DOSFIND.ASM 3 | ; 4 | ; DosFindFirst, DosFindNext and friends 5 | ; 6 | ; Copyright (c) 2023 Malcolm J. Smith 7 | ; 8 | ; Permission is hereby granted, free of charge, to any person obtaining a copy 9 | ; of this software and associated documentation files (the "Software"), to deal 10 | ; in the Software without restriction, including without limitation the rights 11 | ; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | ; copies of the Software, and to permit persons to whom the Software is 13 | ; furnished to do so, subject to the following conditions: 14 | ; 15 | ; The above copyright notice and this permission notice shall be included in 16 | ; all copies or substantial portions of the Software. 17 | ; 18 | ; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | ; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | ; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | ; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | ; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | ; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | ; THE SOFTWARE. 25 | ; 26 | 27 | .MODEL large, pascal 28 | 29 | .DATA? 30 | DosFindBuffer BYTE 0540h DUP (?) 31 | 32 | .CODE _TEXT 33 | 34 | public DosCopyDta 35 | DosCopyDta proc 36 | 37 | ; VOID APIENTRY 38 | ; DosCopyDta( 39 | ; PFILEFINDBUF pFileFindBuf, [High ES, Low AX] 40 | ; PVOID pDta [High DS, Low BX] 41 | ; ); 42 | 43 | push si 44 | push di 45 | 46 | mov di, ax 47 | mov si, bx 48 | 49 | mov ax, [si + 016h] 50 | mov word ptr es:[di], ax ; Creation date 51 | mov word ptr es:[di + 4], ax ; Write date 52 | mov word ptr es:[di + 8], ax ; Access date 53 | mov ax, [si + 018h] 54 | mov word ptr es:[di + 2], ax ; Creation time 55 | mov word ptr es:[di + 6], ax ; Write time 56 | mov word ptr es:[di + 10], ax ; Access time 57 | mov ax, [si + 01ah] 58 | mov word ptr es:[di + 12], ax ; File size (low) 59 | mov word ptr es:[di + 16], ax ; Allocation size (low) 60 | mov ax, [si + 01ch] 61 | mov word ptr es:[di + 14], ax ; File size (high) 62 | mov word ptr es:[di + 18], ax ; Allocation size (high) 63 | mov al, [si + 015h] 64 | mov byte ptr es:[di + 20], al ; Attributes (low) 65 | mov byte ptr es:[di + 21], 0 ; Attributes (high) 66 | 67 | mov ax, di 68 | add di, 23 69 | add si, 30 70 | DosCopyDtaNameNextChar: 71 | mov cl, [si] 72 | mov es:[di], cl 73 | inc si 74 | inc di 75 | test cl, cl 76 | jnz DosCopyDtaNameNextChar 77 | 78 | mov si, ax ; si is now the original di 79 | mov ax, di ; find difference between current di and original 80 | sub ax, si 81 | sub ax, 24 ; ax now has file name bytes copied excluding NULL 82 | 83 | mov di, si 84 | mov byte ptr es:[di + 22], al 85 | 86 | pop di 87 | pop si 88 | 89 | ret 90 | 91 | DosCopyDta endp 92 | 93 | public DosCopyFindData 94 | DosCopyFindData proc 95 | 96 | ; VOID APIENTRY 97 | ; DosCopyFindData( 98 | ; PFILEFINDBUF pFileFindBuf, [High ES, Low AX] 99 | ; PVOID pFindData [High DS, Low BX] 100 | ; ); 101 | 102 | push si 103 | push di 104 | 105 | mov di, ax 106 | mov si, bx 107 | 108 | mov ax, [si + 04h] 109 | mov word ptr es:[di + 2], ax ; Creation time 110 | mov ax, [si + 06h] 111 | mov word ptr es:[di], ax ; Creation date 112 | mov ax, [si + 0ch] 113 | mov word ptr es:[di + 10], ax ; Access time 114 | mov ax, [si + 0eh] 115 | mov word ptr es:[di + 8], ax ; Access date 116 | mov ax, [si + 014h] 117 | mov word ptr es:[di + 6], ax ; Write time 118 | mov ax, [si + 016h] 119 | mov word ptr es:[di + 4], ax ; Write date 120 | mov ax, [si + 020h] 121 | mov word ptr es:[di + 12], ax ; File size (low) 122 | mov word ptr es:[di + 16], ax ; Allocation size (low) 123 | mov ax, [si + 022h] 124 | mov word ptr es:[di + 14], ax ; File size (high) 125 | mov word ptr es:[di + 18], ax ; Allocation size (high) 126 | mov ax, [si] 127 | mov word ptr es:[di + 20], ax ; Attributes 128 | 129 | mov ax, di 130 | add di, 23 131 | add si, 44 132 | DosCopyFindDataNameNextChar: 133 | mov cl, [si] 134 | mov es:[di], cl 135 | inc si 136 | inc di 137 | test cl, cl 138 | jnz DosCopyFindDataNameNextChar 139 | 140 | mov si, ax ; si is now the original di 141 | mov ax, di ; find difference between current di and original 142 | sub ax, si 143 | sub ax, 24 ; ax now has file name bytes copied excluding NULL 144 | 145 | mov di, si 146 | mov byte ptr es:[di + 22], al 147 | 148 | pop di 149 | pop si 150 | 151 | ret 152 | 153 | DosCopyFindData endp 154 | 155 | public DosFindFirst 156 | DosFindFirst proc 157 | 158 | push bp 159 | mov bp, sp 160 | 161 | ; SYSERR APIENTRY 162 | ; DosFindFirst( 163 | ; PSZ pszFileSpec, [High BP + 28, Low BP + 26] 164 | ; PHDIR pDirHandle, [High BP + 24, Low BP + 22] 165 | ; WORD wAttr, [BP + 20] 166 | ; PFILEFINDBUF pFileFindBuf, [High BP + 18, Low BP + 16] 167 | ; WORD wBufSize, [BP + 14] 168 | ; PWORD pwSearch, [High BP + 12, Low BP + 10] 169 | ; DWORD dwReserved [High BP + 8, Low BP + 6] 170 | ; ); 171 | 172 | push es 173 | push di 174 | push si 175 | push bx 176 | ; Intentionally last so it can be popped during function 177 | push ds 178 | 179 | ; Check pDirHandle is the only valid value 180 | mov ds, [bp + 24] 181 | mov di, [bp + 22] 182 | mov ax, [di] 183 | cmp ax, 1 184 | je DosFindFirstIssueCall 185 | 186 | mov ax, 6 187 | jmp DosFindFirstError 188 | 189 | DosFindFirstIssueCall: 190 | 191 | ; While the dir handle pointer is loaded, set this to -1 to indicate an 192 | ; 8.3 name call was used 193 | mov ax, 0ffffh 194 | mov [di], ax 195 | 196 | mov ax, 0714eh 197 | mov cx, [bp + 20] 198 | mov ch, 0 199 | mov si, 1 200 | mov ds, [bp + 28] 201 | mov dx, [bp + 26] 202 | 203 | ; Load the ds register into the es register. This is the output of the 204 | ; call. The search string is in the ds register. 205 | pop es 206 | push es 207 | mov di, OFFSET DosFindBuffer 208 | int 21h 209 | 210 | ; On error, try the 8.3 version 211 | jb DosFindFirstIssueClassicCall 212 | 213 | ; Save find handle 214 | mov ds, [bp + 24] 215 | mov di, [bp + 22] 216 | mov [di], ax 217 | 218 | ; Switch the output (es) segment to the data segment 219 | push es 220 | pop ds 221 | 222 | mov es, [bp + 18] 223 | mov ax, [bp + 16] 224 | mov bx, OFFSET DosFindBuffer 225 | 226 | call DosCopyFindData 227 | 228 | xor ax, ax 229 | jmp DosFindFirstError 230 | 231 | DosFindFirstIssueClassicCall: 232 | 233 | mov ds, [bp + 28] 234 | mov dx, [bp + 26] 235 | mov cx, [bp + 20] 236 | mov ah, 04eh 237 | int 21h 238 | 239 | jb DosFindFirstError 240 | 241 | mov ah, 02fh 242 | int 21h 243 | 244 | push es 245 | pop ds 246 | 247 | mov es, [bp + 18] 248 | mov ax, [bp + 16] 249 | ; MSFIX - Need to count length of file name to check buffer size 250 | 251 | call DosCopyDta 252 | 253 | xor ax, ax 254 | 255 | DosFindFirstError: 256 | 257 | pop ds 258 | pop bx 259 | pop si 260 | pop di 261 | pop es 262 | mov sp, bp 263 | pop bp 264 | 265 | ret 24 266 | 267 | DosFindFirst endp 268 | 269 | public DosFindNext 270 | DosFindNext proc 271 | 272 | push bp 273 | mov bp, sp 274 | 275 | ; SYSERR APIENTRY 276 | ; DosFindNext( 277 | ; HDIR hDirHandle, [BP + 16] 278 | ; PFILEFINDBUF pFileFindBuf, [High BP + 14, Low BP + 12] 279 | ; WORD wBufSize, [BP + 10] 280 | ; PWORD pwSearch [High BP + 8, Low BP + 6] 281 | ; ); 282 | 283 | push es 284 | push bx 285 | push si 286 | ; Intentionally last so it can be popped during function 287 | push ds 288 | 289 | mov ax, [bp + 16] 290 | cmp ax, 0ffffh 291 | je DosFindNextIssueClassicCall 292 | 293 | mov bx, ax 294 | mov ax, 0714fh 295 | mov si, 1 296 | ; Load the ds register into the es register. This is the output of the 297 | ; call. 298 | pop es 299 | push es 300 | mov di, OFFSET DosFindBuffer 301 | 302 | int 21h 303 | 304 | jb DosFindNextError 305 | 306 | ; Switch the output (es) segment to the data segment 307 | push es 308 | pop ds 309 | 310 | mov es, [bp + 14] 311 | mov ax, [bp + 12] 312 | mov bx, di 313 | 314 | call DosCopyFindData 315 | 316 | jmp DosFindNextSuccess 317 | 318 | DosFindNextIssueClassicCall: 319 | mov ah, 04fh 320 | int 21h 321 | 322 | jb DosFindNextError 323 | 324 | mov ah, 02fh 325 | int 21h 326 | 327 | push es 328 | pop ds 329 | 330 | mov es, [bp + 14] 331 | mov ax, [bp + 12] 332 | 333 | call DosCopyDta 334 | 335 | DosFindNextSuccess: 336 | 337 | xor ax, ax 338 | 339 | DosFindNextError: 340 | 341 | pop ds 342 | pop si 343 | pop bx 344 | pop es 345 | mov sp, bp 346 | pop bp 347 | 348 | ret 12 349 | 350 | DosFindNext endp 351 | 352 | public DosFindClose 353 | DosFindClose proc 354 | 355 | push bp 356 | mov bp, sp 357 | 358 | ; SYSERR APIENTRY 359 | ; DosFindClose( 360 | ; HDIR hDirHandle [BP + 6] 361 | ; ); 362 | 363 | push bx 364 | 365 | mov ax, [bp + 6] 366 | cmp ax, 0ffffh 367 | je DosFindCloseSuccess 368 | 369 | mov bx, ax 370 | mov ax, 071a1h 371 | int 21h 372 | 373 | jb DosFindCloseError 374 | 375 | DosFindCloseSuccess: 376 | xor ax, ax 377 | 378 | DosFindCloseError: 379 | 380 | pop bx 381 | 382 | mov sp, bp 383 | pop bp 384 | 385 | ret 2 386 | 387 | DosFindClose endp 388 | 389 | END 390 | -------------------------------------------------------------------------------- /crt/halloc.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file crt/halloc.c 3 | * 4 | * OS/2 huge memory allocator. This is a multi-segment allocator that 5 | * dynamically allocated segments to satisfy memory allocations from an 6 | * arbitrary segment. 7 | * 8 | * Copyright (c) 2023 Malcolm J. Smith 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy 11 | * of this software and associated documentation files (the "Software"), to deal 12 | * in the Software without restriction, including without limitation the rights 13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | * copies of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be included in 18 | * all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | * THE SOFTWARE. 27 | */ 28 | 29 | #include 30 | #include 31 | #include 32 | 33 | #define HALLOC_DBG 0 34 | 35 | typedef struct _HUGE_POOL_HDR { 36 | WORD AllocatedSegments; 37 | WORD CurrentSegments; 38 | PPVOID Segments; 39 | } HUGE_POOL_HDR; 40 | 41 | typedef HUGE_POOL_HDR FAR* PHUGE_POOL_HDR; 42 | 43 | #define HUGE_POOL_INITIAL_SEGMENT_COUNT (4) 44 | #define HUGE_POOL_SEGMENT_COUNT_INCREMENT (4) 45 | #define HUGE_POOL_SEGMENT_SHIFT (16) 46 | #define HUGE_POOL_SEGMENT_SIZE (1ul << HUGE_POOL_SEGMENT_SHIFT) 47 | 48 | PVOID 49 | HugePoolAllocNewPool(VOID) 50 | { 51 | PHUGE_POOL_HDR poolHdr; 52 | WORD segmentCount; 53 | WORD allocSize; 54 | DWORD segmentSize; 55 | PVOID initialSmallPool; 56 | 57 | 58 | segmentSize = HUGE_POOL_SEGMENT_SIZE; 59 | segmentCount = HUGE_POOL_INITIAL_SEGMENT_COUNT; 60 | #if HALLOC_DBG 61 | printf("halloc: requesting %04x segments %04x bytes each\r\n", segmentCount, (WORD)segmentSize); 62 | #endif 63 | initialSmallPool = SmallPoolAllocNewPool((WORD)segmentSize); 64 | if (initialSmallPool == NULL) { 65 | return NULL; 66 | } 67 | 68 | poolHdr = SmallPoolAlloc(initialSmallPool, sizeof(HUGE_POOL_HDR)); 69 | if (poolHdr == NULL) { 70 | #if HALLOC_DBG 71 | printf("halloc: failed to allocate huge pool header, requested %i bytes from %i byte segment\r\n", 72 | sizeof(HUGE_POOL_HDR), 73 | segmentSize); 74 | #endif 75 | 76 | DebugBreak(); 77 | SmallPoolFreePool(initialSmallPool); 78 | return NULL; 79 | } 80 | 81 | allocSize = segmentCount * sizeof(PVOID); 82 | poolHdr->Segments = SmallPoolAlloc(initialSmallPool, allocSize); 83 | if (poolHdr->Segments == NULL) { 84 | #if HALLOC_DBG 85 | printf("halloc: failed to allocate huge pool segment array, requested %i bytes from %i byte segment\r\n", 86 | allocSize, 87 | segmentSize); 88 | #endif 89 | 90 | DebugBreak(); 91 | SmallPoolFree(poolHdr); 92 | SmallPoolFreePool(initialSmallPool); 93 | return NULL; 94 | } 95 | 96 | poolHdr->AllocatedSegments = segmentCount; 97 | poolHdr->CurrentSegments = 1; 98 | poolHdr->Segments[0] = initialSmallPool; 99 | 100 | #if HALLOC_DBG 101 | printf("halloc: hdr %08lp AllocatedSegments %04x CurrentSegments %04x SegmentArray %08lp\r\n", 102 | poolHdr, poolHdr->AllocatedSegments, poolHdr->CurrentSegments, poolHdr->Segments); 103 | #endif 104 | 105 | return poolHdr; 106 | } 107 | 108 | VOID 109 | HugePoolFreePool( 110 | PVOID pool 111 | ) 112 | { 113 | WORD segmentIndex; 114 | PHUGE_POOL_HDR poolHdr; 115 | PVOID hdrSegment; 116 | PVOID arraySegment; 117 | PVOID segment; 118 | 119 | poolHdr = (PHUGE_POOL_HDR)pool; 120 | 121 | #if HALLOC_DBG 122 | printf("halloc: hdr %08lp free pool AllocatedSegments %04x CurrentSegments %04x SegmentArray %08lp\r\n", 123 | poolHdr, poolHdr->AllocatedSegments, poolHdr->CurrentSegments, poolHdr->Segments); 124 | #endif 125 | 126 | arraySegment = SelToFarPtr(FarPtrToSel(poolHdr->Segments)); 127 | hdrSegment = SelToFarPtr(FarPtrToSel(pool)); 128 | 129 | // 130 | // Free all segments except the one hosting the huge pool header 131 | // 132 | 133 | for (segmentIndex = poolHdr->CurrentSegments; segmentIndex > 0; segmentIndex--) { 134 | segment = poolHdr->Segments[segmentIndex - 1]; 135 | if (segment != hdrSegment && segment != arraySegment) { 136 | SmallPoolFreePool(segment); 137 | } 138 | } 139 | 140 | SmallPoolFree(poolHdr->Segments); 141 | if (arraySegment != hdrSegment) { 142 | SmallPoolFreePool(arraySegment); 143 | } 144 | 145 | SmallPoolFree(poolHdr); 146 | SmallPoolFreePool(hdrSegment); 147 | } 148 | 149 | VOID 150 | HugePoolDump( 151 | PVOID pool 152 | ) 153 | { 154 | PHUGE_POOL_HDR poolHdr; 155 | WORD segmentIndex; 156 | 157 | poolHdr = (PHUGE_POOL_HDR)pool; 158 | printf("Pool %08lp CurrentSegments %04x AllocatedSegments %04x Segments %08lp\r\n", 159 | poolHdr, 160 | poolHdr->CurrentSegments, 161 | poolHdr->AllocatedSegments, 162 | poolHdr->Segments); 163 | 164 | for (segmentIndex = 0; segmentIndex < poolHdr->CurrentSegments; segmentIndex++) { 165 | printf("Segment %04x\r\n", segmentIndex); 166 | SmallPoolDump(poolHdr->Segments[segmentIndex]); 167 | } 168 | } 169 | 170 | PVOID 171 | HugePoolAllocFromExistingSegments( 172 | PHUGE_POOL_HDR poolHdr, 173 | WORD sizeInBytes 174 | ) 175 | { 176 | WORD segmentIndex; 177 | PVOID userData; 178 | PVOID smallPool; 179 | 180 | for (segmentIndex = 0; segmentIndex < poolHdr->CurrentSegments; segmentIndex++) { 181 | smallPool = poolHdr->Segments[segmentIndex]; 182 | if (smallPool != SelToFarPtr(FarPtrToSel(smallPool))) { 183 | #if HALLOC_DBG 184 | printf("halloc: hdr %08lp searching small pool %08lp index %04x\r\n", 185 | poolHdr, smallPool, segmentIndex); 186 | #endif 187 | DebugBreak(); 188 | } 189 | 190 | userData = SmallPoolAlloc(smallPool, sizeInBytes); 191 | if (userData != NULL) { 192 | return userData; 193 | } 194 | } 195 | 196 | return NULL; 197 | } 198 | 199 | PVOID 200 | HugePoolAlloc( 201 | PVOID pool, 202 | WORD sizeInBytes 203 | ) 204 | { 205 | PHUGE_POOL_HDR poolHdr; 206 | PVOID newSmallPool; 207 | PVOID userData; 208 | DWORD segmentSize; 209 | WORD newSegmentCount; 210 | WORD newSegmentArrayBytes; 211 | WORD loopCount; 212 | 213 | segmentSize = HUGE_POOL_SEGMENT_SIZE; 214 | 215 | poolHdr = (PHUGE_POOL_HDR)pool; 216 | 217 | // 218 | // Loop twice looking for an allocation. The worst case for this 219 | // allocator is to add one segment, need to allocate the segment 220 | // array from it, then have no contiguous space for the user request. 221 | // On a second pass, we're guaranteed to have one completely empty 222 | // segment, since the array must be in the first new segment or the 223 | // second new segment, but not both. 224 | // 225 | 226 | for (loopCount = 0; loopCount < 2; loopCount++) { 227 | userData = HugePoolAllocFromExistingSegments(poolHdr, sizeInBytes); 228 | if (userData != NULL) { 229 | return userData; 230 | } 231 | #if HALLOC_DBG 232 | printf("halloc: hdr %08lp allocation failure %i bytes requested\r\n", 233 | poolHdr, sizeInBytes); 234 | #endif 235 | 236 | // 237 | // We need to allocate a new segment. If we don't have space in the 238 | // array, attempt to extend the array. First look for an array 239 | // allocation from the existing pool, to maximize the available space 240 | // in the new segment. 241 | // 242 | if (poolHdr->CurrentSegments == poolHdr->AllocatedSegments) { 243 | newSegmentCount = poolHdr->AllocatedSegments + HUGE_POOL_SEGMENT_COUNT_INCREMENT; 244 | newSegmentArrayBytes = newSegmentCount * sizeof(PVOID); 245 | 246 | // 247 | // The array needs to fit into a single segment. 248 | // 249 | if (newSegmentArrayBytes > segmentSize - 0x80) { 250 | return NULL; 251 | } 252 | 253 | if (newSegmentArrayBytes < sizeInBytes) { 254 | userData = HugePoolAllocFromExistingSegments(poolHdr, newSegmentArrayBytes); 255 | if (userData != NULL) { 256 | #if HALLOC_DBG 257 | printf("halloc: hdr %08lp extended segment array from existing space, AllocatedSegments %04x, SegmentArray %08lp\r\n", 258 | poolHdr, newSegmentCount, userData); 259 | #endif 260 | memcpy(userData, poolHdr->Segments, poolHdr->CurrentSegments * sizeof(PVOID)); 261 | SmallPoolFree(poolHdr->Segments); 262 | poolHdr->Segments = userData; 263 | poolHdr->AllocatedSegments = newSegmentCount; 264 | } 265 | } 266 | } 267 | 268 | // 269 | // Allocate a new segment. 270 | // 271 | 272 | newSmallPool = SmallPoolAllocNewPool((WORD)segmentSize); 273 | if (newSmallPool == NULL) { 274 | #if HALLOC_DBG 275 | printf("halloc: hdr %08lp failed to allocate new segment\r\n", 276 | poolHdr); 277 | #endif 278 | return NULL; 279 | } 280 | #if HALLOC_DBG 281 | printf("halloc: hdr %08lp allocated new segment %08lp\r\n", 282 | poolHdr, newSmallPool); 283 | #endif 284 | 285 | // 286 | // If the array is large enough, add the new segment. If not, it 287 | // means we couldn't extend the array above, and need to allocate 288 | // the array from the new segment. 289 | // 290 | 291 | if (poolHdr->AllocatedSegments > poolHdr->CurrentSegments) { 292 | poolHdr->Segments[poolHdr->CurrentSegments] = newSmallPool; 293 | poolHdr->CurrentSegments++; 294 | 295 | // 296 | // If the user request doesn't fit in a single segment, the 297 | // request cannot be supported by this allocator. 298 | // 299 | 300 | userData = SmallPoolAlloc(newSmallPool, sizeInBytes); 301 | if (userData == NULL) { 302 | #if HALLOC_DBG 303 | printf("halloc: hdr %08lp allocation for %i bytes doesn't fit in one segment\r\n", 304 | poolHdr, sizeInBytes); 305 | #endif 306 | DebugBreak(); 307 | } 308 | #if HALLOC_DBG 309 | printf("halloc: hdr %08lp returning user data %08lp after extension\r\n", 310 | poolHdr, userData); 311 | #endif 312 | return userData; 313 | } else { 314 | newSegmentCount = poolHdr->AllocatedSegments + HUGE_POOL_SEGMENT_COUNT_INCREMENT; 315 | newSegmentArrayBytes = newSegmentCount * sizeof(PVOID); 316 | userData = SmallPoolAlloc(newSmallPool, newSegmentArrayBytes); 317 | 318 | // 319 | // If this fails, we can't fit the segment array in an empty 320 | // segment, so the huge allocator has reached its limit. 321 | // 322 | if (userData == NULL) { 323 | #if HALLOC_DBG 324 | printf("halloc: hdr %08lp cannot extend extended segment array, AllocatedSegments %04x\r\n", 325 | poolHdr, poolHdr->AllocatedSegments); 326 | #endif 327 | SmallPoolFreePool(newSmallPool); 328 | return NULL; 329 | } 330 | #if HALLOC_DBG 331 | printf("halloc: hdr %08lp extended segment array from new space, AllocatedSegments %04x, loopCount %i, SegmentArray %08lp\r\n", 332 | poolHdr, poolHdr->AllocatedSegments, loopCount, userData); 333 | #endif 334 | 335 | memcpy(userData, poolHdr->Segments, poolHdr->CurrentSegments * sizeof(PVOID)); 336 | SmallPoolFree(poolHdr->Segments); 337 | poolHdr->Segments = userData; 338 | poolHdr->AllocatedSegments = newSegmentCount; 339 | 340 | poolHdr->Segments[poolHdr->CurrentSegments] = newSmallPool; 341 | poolHdr->CurrentSegments++; 342 | 343 | // 344 | // Loop again to retry the user's request. If we've already 345 | // looped twice, the user request didn't fit in a segment. 346 | // 347 | userData = NULL; 348 | if (loopCount == 1) { 349 | #if HALLOC_DBG 350 | printf("halloc: hdr %08lp allocation for %i bytes doesn't fit in one segment\r\n", 351 | poolHdr, sizeInBytes); 352 | #endif 353 | DebugBreak(); 354 | } 355 | } 356 | } 357 | 358 | return userData; 359 | } 360 | 361 | VOID 362 | HugePoolFree( 363 | PVOID ptr 364 | ) 365 | { 366 | SmallPoolFree(ptr); 367 | } 368 | 369 | 370 | // vim:sw=4:ts=4:et: 371 | -------------------------------------------------------------------------------- /crt/printf.inc: -------------------------------------------------------------------------------- 1 | /** 2 | * @file crt/printf.inc 3 | * 4 | * OS/2 printf implementation. Most logic is implemented here so it can be 5 | * instantiated twice, once to generate code to determine buffer sizes, and 6 | * once to perform the operation. 7 | * 8 | * Copyright (c) 2017-2023 Malcolm J. Smith 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy 11 | * of this software and associated documentation files (the "Software"), to deal 12 | * in the Software without restriction, including without limitation the rights 13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | * copies of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be included in 18 | * all copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 26 | * THE SOFTWARE. 27 | */ 28 | 29 | #ifdef PRINTF_FN 30 | #undef PRINTF_FN 31 | #undef PRINTF_DESTLENGTH 32 | #undef PRINTF_PUSHCHAR 33 | #endif 34 | 35 | #ifdef PRINTF_SIZEONLY 36 | 37 | #define PRINTF_FN vsprintf_sz 38 | 39 | #define PRINTF_DESTLENGTH() (1) 40 | #define PRINTF_PUSHCHAR(x) dest_offset++,x; 41 | 42 | #else // PRINTF_SIZEONLY 43 | 44 | #define PRINTF_FN vsprintf 45 | 46 | #define PRINTF_DESTLENGTH() (dest_offset < len - 1) 47 | #define PRINTF_PUSHCHAR(x) szDest[dest_offset++] = x; 48 | 49 | #endif // PRINTF_SIZEONLY 50 | 51 | int 52 | PRINTF_FN( 53 | #ifndef PRINTF_SIZEONLY 54 | PSZ szDest, 55 | WORD len, 56 | #endif 57 | PSZ szFmt, 58 | va_list marker) 59 | { 60 | WORD dest_offset = 0; 61 | WORD src_offset = 0; 62 | WORD i; 63 | 64 | BOOL leadingzero; 65 | BOOL leftalign; 66 | BOOL short_prefix; 67 | BOOL long_prefix; 68 | BOOL truncated_due_to_space; 69 | WORD element_len; 70 | 71 | truncated_due_to_space = FALSE; 72 | 73 | while (szFmt[src_offset] != '\0') { 74 | if (szFmt[src_offset] == '%') { 75 | src_offset++; 76 | leadingzero = FALSE; 77 | leftalign = FALSE; 78 | long_prefix = FALSE; 79 | short_prefix = FALSE; 80 | element_len = 0; 81 | 82 | if (szFmt[src_offset] == '-') { 83 | leftalign = TRUE; 84 | src_offset++; 85 | } 86 | if (szFmt[src_offset] == '0') { 87 | leadingzero = TRUE; 88 | src_offset++; 89 | } 90 | while (szFmt[src_offset] >= '0' && szFmt[src_offset] <= '9') { 91 | element_len = element_len * 10 + szFmt[src_offset] - '0'; 92 | src_offset++; 93 | } 94 | if (szFmt[src_offset] == 'h') { 95 | short_prefix = TRUE; 96 | src_offset++; 97 | } else if (szFmt[src_offset] == 'l') { 98 | long_prefix = TRUE; 99 | src_offset++; 100 | } 101 | 102 | if (szFmt[src_offset] == 'p') { 103 | long_prefix = TRUE; 104 | } 105 | 106 | if (element_len == 0) { 107 | element_len = (WORD) -1; 108 | } 109 | 110 | switch(szFmt[src_offset]) { 111 | case '%': 112 | if (PRINTF_DESTLENGTH()) { 113 | PRINTF_PUSHCHAR('%'); 114 | } else { 115 | truncated_due_to_space = TRUE; 116 | } 117 | break; 118 | 119 | case 'c': 120 | { 121 | // 122 | // The compiler always upconverts chars to ints when 123 | // creating variable arguments. We have to mirror 124 | // that semantic here. MSVC gets this "right" by 125 | // allowing these to be symmetrical and upconverting 126 | // both, but gcc gets it "wrong" and explodes by 127 | // upconverting one and not the other (then printing 128 | // a warning blaming this code.) 129 | // 130 | 131 | i = (UCHAR)va_arg(marker, int); 132 | if (PRINTF_DESTLENGTH()) { 133 | PRINTF_PUSHCHAR((UCHAR)i); 134 | } else { 135 | truncated_due_to_space = TRUE; 136 | } 137 | } 138 | break; 139 | case 's': 140 | { 141 | PSZ str = va_arg(marker, PSZ); 142 | 143 | if (str == NULL) { 144 | str = "(null)"; 145 | } 146 | 147 | if (element_len != (WORD)-1 && !leftalign) { 148 | WORD str_len = 0; 149 | while (str[str_len] != '\0') str_len++; 150 | 151 | while (element_len > str_len) { 152 | if (PRINTF_DESTLENGTH()) { 153 | PRINTF_PUSHCHAR(' '); 154 | } else { 155 | truncated_due_to_space = TRUE; 156 | } 157 | element_len--; 158 | } 159 | } 160 | while (*str != '\0' && element_len) { 161 | if (PRINTF_DESTLENGTH()) { 162 | PRINTF_PUSHCHAR(*str); 163 | } else { 164 | truncated_due_to_space = TRUE; 165 | } 166 | str++; 167 | element_len--; 168 | } 169 | 170 | if (leftalign) { 171 | while (element_len--) { 172 | if (PRINTF_DESTLENGTH()) { 173 | PRINTF_PUSHCHAR(' '); 174 | } else { 175 | truncated_due_to_space = TRUE; 176 | } 177 | } 178 | } 179 | } 180 | break; 181 | case 'u': 182 | case 'd': 183 | case 'i': 184 | case 'x': 185 | case 'p': 186 | if (!long_prefix) { 187 | WORD num, tempnum; 188 | WORD divisor, digits; 189 | WORD radix = 10; 190 | 191 | // 192 | // If we're %i we're base 10, if we're %x we're 193 | // base 16 194 | // 195 | 196 | if (szFmt[src_offset] == 'x' || szFmt[src_offset] == 'p') { 197 | radix = 16; 198 | } 199 | 200 | num = va_arg(marker, int); 201 | 202 | // 203 | // Count the number of digits we have in the user's 204 | // input. Stop if we hit the format specifier. 205 | // Code below will preserve low order values. 206 | // 207 | 208 | divisor = 1; 209 | digits = 1; 210 | tempnum = num; 211 | while (tempnum > radix - 1 && digits < element_len) { 212 | divisor *= radix; 213 | tempnum = num / divisor; 214 | digits++; 215 | } 216 | 217 | // 218 | // If the field specifier is larger, pad it with 219 | // either a zero or space depending on the format 220 | // If the field specifier is too small, output more 221 | // characters than the field specifier specifies. 222 | // 223 | 224 | if (element_len != (WORD)-1 && digits < element_len) { 225 | tempnum = element_len - digits; 226 | while (tempnum > 0) { 227 | if (!PRINTF_DESTLENGTH()) { 228 | truncated_due_to_space = TRUE; 229 | break; 230 | } 231 | if (leadingzero) { 232 | PRINTF_PUSHCHAR('0'); 233 | } else { 234 | PRINTF_PUSHCHAR(' '); 235 | } 236 | tempnum--; 237 | } 238 | } 239 | 240 | do { 241 | tempnum = (num / divisor) % radix; 242 | 243 | if (!PRINTF_DESTLENGTH()) { 244 | truncated_due_to_space = TRUE; 245 | break; 246 | } 247 | 248 | if (tempnum > 9) { 249 | PRINTF_PUSHCHAR((UCHAR)(tempnum + 'a' - 10)); 250 | } else { 251 | PRINTF_PUSHCHAR((UCHAR)(tempnum + '0')); 252 | } 253 | 254 | divisor /= radix; 255 | digits--; 256 | 257 | } while(digits > 0); 258 | } else { 259 | DWORD num, tempnum; 260 | WORD digits; 261 | WORD digitcount; 262 | WORD radix = 10; 263 | 264 | // 265 | // If we're %i we're base 10, if we're %x we're 266 | // base 16 267 | // 268 | 269 | if (szFmt[src_offset] == 'x' || szFmt[src_offset] == 'p') { 270 | radix = 16; 271 | } 272 | 273 | num = va_arg(marker, DWORD); 274 | 275 | // 276 | // Count the number of digits we have in the user's 277 | // input. Stop if we hit the format specifier. 278 | // Code below will preserve low order values. 279 | // 280 | 281 | digits = 1; 282 | tempnum = num; 283 | while (tempnum > radix - 1) { 284 | tempnum = tempnum / radix; 285 | digits++; 286 | } 287 | 288 | // 289 | // If the field specifier is larger, pad it with 290 | // either a zero or space depending on the format 291 | // If the field specifier is too small, output more 292 | // characters than the field specifier specifies. 293 | // 294 | 295 | if (element_len != (WORD)-1 && digits < element_len) { 296 | tempnum = element_len - digits; 297 | while (tempnum > 0) { 298 | if (!PRINTF_DESTLENGTH()) { 299 | truncated_due_to_space = TRUE; 300 | break; 301 | } 302 | if (leadingzero) { 303 | PRINTF_PUSHCHAR('0'); 304 | } else { 305 | PRINTF_PUSHCHAR(' '); 306 | } 307 | tempnum--; 308 | } 309 | } 310 | 311 | do { 312 | tempnum = num; 313 | for (digitcount = 1; digitcount < digits; digitcount++) { 314 | tempnum = tempnum / radix; 315 | } 316 | tempnum = tempnum % radix; 317 | 318 | if (!PRINTF_DESTLENGTH()) { 319 | truncated_due_to_space = TRUE; 320 | break; 321 | } 322 | 323 | if (tempnum > 9) { 324 | PRINTF_PUSHCHAR((UCHAR)(tempnum + 'a' - 10)); 325 | } else { 326 | PRINTF_PUSHCHAR((UCHAR)(tempnum + '0')); 327 | } 328 | 329 | digits--; 330 | } while (digits > 0); 331 | } 332 | break; 333 | default: 334 | { 335 | PSZ szErr = "FMTERR"; 336 | i = 0; 337 | while (szErr[i] != '\0') { 338 | if (PRINTF_DESTLENGTH()) { 339 | PRINTF_PUSHCHAR(szErr[i++]); 340 | } else { 341 | truncated_due_to_space = TRUE; 342 | } 343 | } 344 | } 345 | break; 346 | } 347 | 348 | src_offset++; 349 | 350 | } else { 351 | if (PRINTF_DESTLENGTH()) { 352 | PRINTF_PUSHCHAR(szFmt[src_offset++]); 353 | } else { 354 | truncated_due_to_space = TRUE; 355 | } 356 | } 357 | 358 | if (truncated_due_to_space) { 359 | break; 360 | } 361 | } 362 | 363 | #ifndef PRINTF_SIZEONLY 364 | if (dest_offset >= len || szFmt[src_offset] != '\0') { 365 | szDest[0] = '\0'; 366 | return -1; 367 | } 368 | #endif 369 | PRINTF_PUSHCHAR('\0'); 370 | 371 | #ifndef PRINTF_SIZEONLY 372 | dest_offset--; 373 | #endif 374 | 375 | return dest_offset; 376 | } 377 | 378 | // vim:sw=4:ts=4:et: 379 | -------------------------------------------------------------------------------- /crt/salloc.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file crt/salloc.c 3 | * 4 | * OS/2 based small memory allocator. This allocates memory within a single 5 | * 64Kb segment and is currently quite lame. 6 | * 7 | * Copyright (c) 2023 Malcolm J. Smith 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in 17 | * all copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 25 | * THE SOFTWARE. 26 | */ 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | #define SALLOC_DBG 0 33 | 34 | typedef struct _SMALL_POOL_HDR { 35 | WORD TotalBytes; 36 | WORD LargestFreeBlock; 37 | WORD FirstFreeHeader; 38 | WORD Reserved; 39 | } SMALL_POOL_HDR; 40 | 41 | typedef SMALL_POOL_HDR FAR* PSMALL_POOL_HDR; 42 | 43 | typedef struct _ALLOC_HDR { 44 | WORD OffsetToPrev; 45 | WORD OffsetToNext; 46 | } ALLOC_HDR; 47 | 48 | typedef ALLOC_HDR FAR* PALLOC_HDR; 49 | 50 | #define ALLOC_HDR_FLAG_ALLOCATED 1 51 | #define ALLOC_HDR_OFFSET_MASK 0xfffc 52 | 53 | #define AllocHdrIsAllocated(Hdr) (Hdr->OffsetToPrev & ALLOC_HDR_FLAG_ALLOCATED) 54 | #define AllocHdrMarkAllocated(Hdr) Hdr->OffsetToPrev = Hdr->OffsetToPrev | ALLOC_HDR_FLAG_ALLOCATED 55 | #define AllocHdrMarkFree(Hdr) Hdr->OffsetToPrev = Hdr->OffsetToPrev & ~(ALLOC_HDR_FLAG_ALLOCATED) 56 | #define AllocHdrOffsetToPrev(Hdr) (Hdr->OffsetToPrev & ALLOC_HDR_OFFSET_MASK) 57 | #define AllocHdrOffsetToNext(Hdr) (Hdr->OffsetToNext & ALLOC_HDR_OFFSET_MASK) 58 | #define AllocHdrNextHdr(Hdr) (PALLOC_HDR)((PUCHAR)Hdr + AllocHdrOffsetToNext(Hdr)) 59 | #define AllocHdrPrevHdr(Hdr) (PALLOC_HDR)((PUCHAR)Hdr - AllocHdrOffsetToPrev(Hdr)) 60 | #define AllocHdrUsableBytes(Hdr) (Hdr->OffsetToNext - sizeof(ALLOC_HDR)) 61 | #define AllocHdrUserDataFromHdr(Hdr) ((PUCHAR)Hdr + sizeof(ALLOC_HDR)) 62 | #define AllocHdrHdrFromUserData(UserData) (PALLOC_HDR)((PUCHAR)UserData - sizeof(ALLOC_HDR)) 63 | #define AllocHdrOffsetFromStart(PoolHdr, Hdr) (WORD)((PUCHAR)Hdr - (PUCHAR)poolHdr) 64 | #define AllocHdrFromOffset(PoolHdr, Offset) (PALLOC_HDR)((PUCHAR)PoolHdr + Offset) 65 | #define AllocHdrBeyondPool(PoolHdr, Hdr) (AllocHdrOffsetFromStart(PoolHdr, Hdr) - sizeof(SMALL_POOL_HDR) >= poolHdr->TotalBytes) 66 | 67 | 68 | PVOID 69 | SmallPoolAllocNewPool( 70 | WORD requestedBytes 71 | ) 72 | { 73 | WORD sizeToRequest; 74 | SEL selector; 75 | WORD err; 76 | PSMALL_POOL_HDR poolHdr; 77 | PALLOC_HDR allocHdr; 78 | 79 | sizeToRequest = requestedBytes; 80 | if (sizeToRequest != 0) { 81 | if (sizeToRequest < sizeof(SMALL_POOL_HDR) + sizeof(ALLOC_HDR)) { 82 | return NULL; 83 | } 84 | } 85 | 86 | err = DosAllocSeg(sizeToRequest, &selector, 0); 87 | if (err != NO_ERROR) { 88 | return NULL; 89 | } 90 | 91 | poolHdr = (PSMALL_POOL_HDR)SelToFarPtr(selector); 92 | poolHdr->TotalBytes = sizeToRequest - sizeof(SMALL_POOL_HDR); 93 | poolHdr->LargestFreeBlock = poolHdr->TotalBytes - sizeof(ALLOC_HDR); 94 | poolHdr->FirstFreeHeader = sizeof(SMALL_POOL_HDR); 95 | 96 | allocHdr = (PALLOC_HDR)(poolHdr + 1); 97 | allocHdr->OffsetToNext = poolHdr->TotalBytes; 98 | allocHdr->OffsetToPrev = 0; 99 | 100 | #if SALLOC_DBG 101 | printf("New pool sel %04x %p total %i largest %i allocHdr %p prev %04x next %04x\r\n", 102 | selector, 103 | poolHdr, 104 | poolHdr->TotalBytes, 105 | poolHdr->LargestFreeBlock, 106 | allocHdr, 107 | allocHdr->OffsetToPrev, 108 | allocHdr->OffsetToNext); 109 | #endif 110 | 111 | return poolHdr; 112 | } 113 | 114 | BOOL 115 | SmallPoolIsPoolEmpty( 116 | PVOID pool 117 | ) 118 | { 119 | PSMALL_POOL_HDR poolHdr; 120 | PALLOC_HDR allocHdr; 121 | 122 | poolHdr = (PSMALL_POOL_HDR)pool; 123 | allocHdr = (PALLOC_HDR)(poolHdr + 1); 124 | 125 | if (AllocHdrIsAllocated(allocHdr) || 126 | !AllocHdrBeyondPool(poolHdr, AllocHdrNextHdr(allocHdr))) { 127 | 128 | return FALSE; 129 | } 130 | 131 | return TRUE; 132 | } 133 | 134 | VOID 135 | SmallPoolFreePool( 136 | PVOID pool 137 | ) 138 | { 139 | SEL selector; 140 | 141 | if (!SmallPoolIsPoolEmpty(pool)) { 142 | #if SALLOC_DBG 143 | printf("alloc: pool %p freed but not empty\r\n", pool); 144 | SmallPoolDump(pool); 145 | #endif 146 | DebugBreak(); 147 | } 148 | 149 | selector = FarPtrToSel(pool); 150 | DosFreeSeg(selector); 151 | } 152 | 153 | BOOL 154 | SmallPoolFreePoolIfEmpty( 155 | PVOID pool 156 | ) 157 | { 158 | if (SmallPoolIsPoolEmpty(pool)) { 159 | SmallPoolFreePool(pool); 160 | return TRUE; 161 | } 162 | 163 | return FALSE; 164 | } 165 | 166 | VOID 167 | SmallPoolDump( 168 | PVOID pool 169 | ) 170 | { 171 | PSMALL_POOL_HDR poolHdr; 172 | PALLOC_HDR allocHdr; 173 | 174 | poolHdr = (PSMALL_POOL_HDR)pool; 175 | printf("Pool %08p TotalBytes %04x LargestFree %04x FirstFree %04x\r\n", 176 | poolHdr, 177 | poolHdr->TotalBytes, 178 | poolHdr->LargestFreeBlock, 179 | poolHdr->FirstFreeHeader); 180 | 181 | allocHdr = (PALLOC_HDR)(poolHdr + 1); 182 | while (!AllocHdrBeyondPool(pool, allocHdr)) { 183 | printf(" Hdr %08p Prev %04x Next %04x\r\n", allocHdr, allocHdr->OffsetToPrev, allocHdr->OffsetToNext); 184 | if (allocHdr->OffsetToNext == 0) { 185 | break; 186 | } 187 | allocHdr = AllocHdrNextHdr(allocHdr); 188 | } 189 | } 190 | 191 | PALLOC_HDR 192 | SmallPoolAllocStartOffset( 193 | PSMALL_POOL_HDR poolHdr, 194 | WORD sizeToAllocate, 195 | WORD startOffset 196 | ) 197 | { 198 | BOOL entireScan = FALSE; 199 | PALLOC_HDR allocHdr; 200 | PALLOC_HDR nextHdr; 201 | WORD largestUsable; 202 | WORD usableBytes; 203 | WORD hdrOffset; 204 | 205 | if (startOffset <= sizeof(SMALL_POOL_HDR)) { 206 | startOffset = sizeof(SMALL_POOL_HDR); 207 | entireScan = TRUE; 208 | } 209 | 210 | #if SALLOC_DBG 211 | printf("alloc: searching %lp for %i bytes startOffset %04x\r\n", 212 | poolHdr, 213 | sizeToAllocate, 214 | startOffset); 215 | #endif 216 | 217 | allocHdr = AllocHdrFromOffset(poolHdr, startOffset); 218 | usableBytes = 0; 219 | largestUsable = 0; 220 | 221 | while (TRUE) { 222 | 223 | #if SALLOC_DBG 224 | printf("alloc: hdr %lp prev %04x next %04x\r\n", allocHdr, allocHdr->OffsetToPrev, allocHdr->OffsetToNext); 225 | #endif 226 | 227 | if (!AllocHdrIsAllocated(allocHdr)) { 228 | hdrOffset = AllocHdrOffsetFromStart(poolHdr, allocHdr); 229 | if (hdrOffset < poolHdr->FirstFreeHeader) { 230 | poolHdr->FirstFreeHeader = hdrOffset; 231 | } 232 | usableBytes = AllocHdrUsableBytes(allocHdr); 233 | if (usableBytes > largestUsable) { 234 | largestUsable = usableBytes; 235 | } 236 | 237 | if (usableBytes >= sizeToAllocate) { 238 | #if SALLOC_DBG 239 | printf("alloc: found usable hdr %lp usable bytes %i\r\n", allocHdr, usableBytes); 240 | #endif 241 | break; 242 | } 243 | } 244 | 245 | nextHdr = AllocHdrNextHdr(allocHdr); 246 | if (AllocHdrBeyondPool(poolHdr, nextHdr)) { 247 | #if SALLOC_DBG 248 | printf("alloc: hdr %lp beyond pool end\r\n", nextHdr); 249 | #endif 250 | allocHdr = NULL; 251 | break; 252 | } 253 | 254 | if (nextHdr == allocHdr) { 255 | #if SALLOC_DBG 256 | printf("alloc: allocHdr %lp zero length, prev %04x next %04x\r\n", 257 | allocHdr, 258 | allocHdr->OffsetToPrev, 259 | allocHdr->OffsetToNext); 260 | #endif 261 | DebugBreak(); 262 | allocHdr = NULL; 263 | break; 264 | } 265 | 266 | if (AllocHdrOffsetToNext(allocHdr) != AllocHdrOffsetToPrev(nextHdr)) { 267 | #if SALLOC_DBG 268 | printf("alloc: prev/next mismatch, hdr %lp next %04x nextHdr %lp prev %04x\r\n", 269 | allocHdr, 270 | AllocHdrOffsetToNext(allocHdr), 271 | nextHdr, 272 | AllocHdrOffsetToPrev(nextHdr)); 273 | #endif 274 | DebugBreak(); 275 | allocHdr = NULL; 276 | break; 277 | } 278 | 279 | allocHdr = nextHdr; 280 | } 281 | 282 | if (allocHdr == NULL) { 283 | if (entireScan) { 284 | #if SALLOC_DBG 285 | printf("alloc: setting %08lp LargestFreeBlock to %i bytes\r\n", 286 | poolHdr, 287 | largestUsable); 288 | #endif 289 | 290 | poolHdr->LargestFreeBlock = largestUsable; 291 | } 292 | return NULL; 293 | } 294 | 295 | return allocHdr; 296 | } 297 | 298 | PVOID 299 | SmallPoolAlloc( 300 | PVOID pool, 301 | WORD sizeInBytes 302 | ) 303 | { 304 | PSMALL_POOL_HDR poolHdr; 305 | PALLOC_HDR allocHdr; 306 | PALLOC_HDR nextHdr; 307 | PVOID userData; 308 | WORD usableBytes; 309 | WORD sizeToAllocate; 310 | WORD hdrOffset; 311 | 312 | poolHdr = (PSMALL_POOL_HDR)pool; 313 | if (sizeInBytes > poolHdr->LargestFreeBlock) { 314 | #if SALLOC_DBG 315 | printf("alloc: %lp has largest free block %i bytes, failing %i request\r\n", 316 | poolHdr, 317 | poolHdr->LargestFreeBlock, 318 | sizeInBytes); 319 | #endif 320 | return NULL; 321 | } 322 | 323 | // Round up the size to allocate to be 32 bit aligned. 324 | sizeToAllocate = (sizeInBytes + sizeof(DWORD) - 1) & ~(sizeof(DWORD) - 1); 325 | allocHdr = NULL; 326 | 327 | // 328 | // If there's a hint to the first free block, start the scan there 329 | // 330 | 331 | if (poolHdr->FirstFreeHeader > sizeof(SMALL_POOL_HDR)) { 332 | allocHdr = SmallPoolAllocStartOffset(poolHdr, sizeToAllocate, poolHdr->FirstFreeHeader); 333 | } 334 | 335 | // 336 | // If nothing was found, start the search from the beginning. The 337 | // header already indicated that LargestFreeBlock can hold this, so 338 | // this scan either succeeds or recalculates LargestFreeBlock to 339 | // prevent this scan happening again. 340 | // 341 | 342 | if (allocHdr == NULL) { 343 | allocHdr = SmallPoolAllocStartOffset(poolHdr, sizeToAllocate, sizeof(SMALL_POOL_HDR)); 344 | } 345 | 346 | if (allocHdr == NULL) { 347 | return NULL; 348 | } 349 | 350 | #if SALLOC_DBG 351 | printf("alloc: poolHdr %lp allocHdr %lp\r\n", poolHdr, allocHdr); 352 | #endif 353 | hdrOffset = AllocHdrOffsetFromStart(poolHdr, allocHdr); 354 | usableBytes = AllocHdrUsableBytes(allocHdr); 355 | 356 | // 357 | // If the block is large enough that it can be split, split it now. 358 | // The test here is saying there must be additional bytes as well as 359 | // the header to make the split worthwhile. 360 | // 361 | 362 | if (usableBytes > sizeToAllocate + sizeof(ALLOC_HDR)) { 363 | PALLOC_HDR nextNextHdr; 364 | 365 | nextHdr = (PALLOC_HDR)((PUCHAR)allocHdr + sizeof(ALLOC_HDR) + sizeToAllocate); 366 | #if SALLOC_DBG 367 | printf("alloc: set nextHdr %p usableBytes %i sizeToAllocate %i\r\n", nextHdr, usableBytes, sizeToAllocate); 368 | #endif 369 | nextHdr->OffsetToPrev = sizeToAllocate + sizeof(ALLOC_HDR); 370 | nextHdr->OffsetToNext = allocHdr->OffsetToNext - nextHdr->OffsetToPrev; 371 | #if SALLOC_DBG 372 | printf("alloc: set nextHdr %p prev %04x next %04x\r\n", nextHdr, nextHdr->OffsetToPrev, nextHdr->OffsetToNext); 373 | printf("alloc: FirstFreeHeader %04x hdrOffset %04x\r\n", poolHdr->FirstFreeHeader, hdrOffset); 374 | #endif 375 | if (poolHdr->FirstFreeHeader == hdrOffset) { 376 | #if SALLOC_DBG 377 | printf("alloc: set FirstFreeHeader to %04x\r\n", hdrOffset); 378 | #endif 379 | poolHdr->FirstFreeHeader = AllocHdrOffsetFromStart(poolHdr, nextHdr); 380 | } 381 | 382 | nextNextHdr = AllocHdrNextHdr(nextHdr); 383 | if (!AllocHdrBeyondPool(poolHdr, nextNextHdr)) { 384 | nextNextHdr->OffsetToPrev = nextHdr->OffsetToNext; 385 | #if SALLOC_DBG 386 | printf("alloc: set nextNextHdr %p prev %04x next %04x\r\n", nextNextHdr, nextNextHdr->OffsetToPrev, nextNextHdr->OffsetToNext); 387 | #endif 388 | } 389 | allocHdr->OffsetToNext = nextHdr->OffsetToPrev; 390 | #if SALLOC_DBG 391 | printf("alloc: allocHdr %p prev %04x next %04x\r\n", allocHdr, allocHdr->OffsetToPrev, allocHdr->OffsetToNext); 392 | #endif 393 | } 394 | 395 | AllocHdrMarkAllocated(allocHdr); 396 | userData = AllocHdrUserDataFromHdr(allocHdr); 397 | #if SALLOC_DBG 398 | printf("alloc: returning %lp, hdr %lp prev %04x next %04x\r\n", 399 | userData, 400 | allocHdr, 401 | allocHdr->OffsetToPrev, 402 | allocHdr->OffsetToNext); 403 | #endif 404 | return userData; 405 | } 406 | 407 | VOID 408 | SmallPoolFree( 409 | PVOID ptr 410 | ) 411 | { 412 | SEL selector; 413 | PSMALL_POOL_HDR poolHdr; 414 | PALLOC_HDR allocHdr; 415 | PALLOC_HDR nextHdr; 416 | PALLOC_HDR prevHdr; 417 | WORD usableBytes; 418 | WORD hdrOffset; 419 | 420 | selector = FarPtrToSel(ptr); 421 | poolHdr = (PSMALL_POOL_HDR)SelToFarPtr(selector); 422 | 423 | allocHdr = AllocHdrHdrFromUserData(ptr); 424 | if (!AllocHdrIsAllocated(allocHdr)) { 425 | #if SALLOC_DBG 426 | printf("free: allocHdr %p not allocated, prev %04i next %04i\r\n", allocHdr, allocHdr->OffsetToPrev, allocHdr->OffsetToNext); 427 | #endif 428 | DebugBreak(); 429 | } 430 | 431 | AllocHdrMarkFree(allocHdr); 432 | hdrOffset = AllocHdrOffsetFromStart(poolHdr, allocHdr); 433 | if (hdrOffset < poolHdr->FirstFreeHeader) { 434 | poolHdr->FirstFreeHeader = hdrOffset; 435 | } 436 | #if SALLOC_DBG 437 | printf("free: allocHdr %p free, prev %04x next %04x\r\n", allocHdr, allocHdr->OffsetToPrev, allocHdr->OffsetToNext); 438 | #endif 439 | 440 | // 441 | // If nextHdr is in range and free, merge the current allocHdr with it. 442 | // this means allocHdr points over nextHdr, and the one after nextHdr 443 | // points back to allocHdr. Note that nextNextHdr is either allocated 444 | // or beyond range; the logic below preserves its allocation flag. 445 | // 446 | 447 | nextHdr = AllocHdrNextHdr(allocHdr); 448 | if (!AllocHdrBeyondPool(poolHdr, nextHdr) && 449 | !AllocHdrIsAllocated(nextHdr)) { 450 | 451 | PALLOC_HDR nextNextHdr; 452 | nextNextHdr = AllocHdrNextHdr(nextHdr); 453 | 454 | #if SALLOC_DBG 455 | printf("free: merging right allocHdr %p nextHdr %p nextNextHdr %p\r\n", allocHdr, nextHdr, nextNextHdr); 456 | #endif 457 | 458 | allocHdr->OffsetToNext = allocHdr->OffsetToNext + nextHdr->OffsetToNext; 459 | if (!AllocHdrBeyondPool(poolHdr, nextNextHdr)) { 460 | nextNextHdr->OffsetToPrev = nextNextHdr->OffsetToPrev + nextHdr->OffsetToPrev; 461 | } 462 | usableBytes = AllocHdrUsableBytes(allocHdr); 463 | if (usableBytes > poolHdr->LargestFreeBlock) { 464 | poolHdr->LargestFreeBlock = usableBytes; 465 | } 466 | } 467 | 468 | prevHdr = AllocHdrPrevHdr(allocHdr); 469 | if (allocHdr != prevHdr && 470 | !AllocHdrIsAllocated(prevHdr)) { 471 | 472 | nextHdr = AllocHdrNextHdr(allocHdr); 473 | 474 | #if SALLOC_DBG 475 | printf("free: merging left allocHdr %p prevHdr %p nextHdr %p\r\n", allocHdr, prevHdr, nextHdr); 476 | #endif 477 | 478 | if (!AllocHdrBeyondPool(poolHdr, nextHdr)) { 479 | nextHdr->OffsetToPrev = nextHdr->OffsetToPrev + allocHdr->OffsetToPrev; 480 | } 481 | prevHdr->OffsetToNext = prevHdr->OffsetToNext + allocHdr->OffsetToNext; 482 | 483 | usableBytes = AllocHdrUsableBytes(prevHdr); 484 | if (usableBytes > poolHdr->LargestFreeBlock) { 485 | poolHdr->LargestFreeBlock = usableBytes; 486 | } 487 | 488 | hdrOffset = AllocHdrOffsetFromStart(poolHdr, prevHdr); 489 | if (hdrOffset < poolHdr->FirstFreeHeader) { 490 | poolHdr->FirstFreeHeader = hdrOffset; 491 | } 492 | } 493 | } 494 | 495 | 496 | // vim:sw=4:ts=4:et: 497 | -------------------------------------------------------------------------------- /dir/zdir.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @file dir/zdir.c 3 | * 4 | * OS/2 directory enumerator to demonstrate the rest of this code. 5 | * 6 | * Copyright (c) 2023 Malcolm J. Smith 7 | * 8 | * Permission is hereby granted, free of charge, to any person obtaining a copy 9 | * of this software and associated documentation files (the "Software"), to deal 10 | * in the Software without restriction, including without limitation the rights 11 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 12 | * copies of the Software, and to permit persons to whom the Software is 13 | * furnished to do so, subject to the following conditions: 14 | * 15 | * The above copyright notice and this permission notice shall be included in 16 | * all copies or substantial portions of the Software. 17 | * 18 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 21 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 23 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 24 | * THE SOFTWARE. 25 | */ 26 | 27 | #include 28 | #include 29 | 30 | // 31 | // Files are sorted, and sorting requires a table of pointers to sort. 32 | // This limits the size of that table to some arbitrary value. Note that 33 | // due to 64Kb segments, this program can only allocate around 63Kb of 34 | // contiguous buffer, and with 32 bits per pointer that implies a limit 35 | // of 16000 files due to memory layout. 36 | // 37 | #define ZDIR_MAX_FILES 4096 38 | 39 | WORD 40 | ZDirSortFilesInternal( 41 | PFILEFINDBUF * Files, 42 | WORD Count 43 | ) 44 | { 45 | WORD FirstOffset; 46 | WORD Index; 47 | FirstOffset = 0; 48 | while (TRUE) { 49 | WORD BreakPoint; 50 | WORD LastOffset; 51 | WORD SizeRequired; 52 | FILEFINDBUF MidPoint; 53 | PFILEFINDBUF TempForSwap; 54 | 55 | BreakPoint = Count / 2; 56 | SizeRequired = FIELD_OFFSET(FILEFINDBUF, achName) + Files[BreakPoint]->cchName + 1; 57 | memcpy(&MidPoint, Files[BreakPoint], SizeRequired); 58 | 59 | FirstOffset = 0; 60 | LastOffset = Count - 1; 61 | 62 | // 63 | // Scan from the beginning looking for an item that should be sorted 64 | // after the midpoint. 65 | // 66 | 67 | for (FirstOffset = 0; FirstOffset < LastOffset; FirstOffset++) { 68 | if (stricmp(Files[FirstOffset]->achName,MidPoint.achName) >= 0) { 69 | for (; LastOffset > FirstOffset; LastOffset--) { 70 | if (stricmp(Files[LastOffset]->achName,MidPoint.achName) < 0) { 71 | TempForSwap = Files[FirstOffset]; 72 | Files[FirstOffset] = Files[LastOffset]; 73 | Files[LastOffset] = TempForSwap; 74 | LastOffset--; 75 | break; 76 | } 77 | } 78 | if (LastOffset <= FirstOffset) { 79 | break; 80 | } 81 | } 82 | } 83 | 84 | FirstOffset = LastOffset; 85 | if (stricmp(Files[FirstOffset]->achName, MidPoint.achName) < 0) { 86 | FirstOffset++; 87 | } 88 | 89 | // 90 | // If no copies occurred, check if everything in the list is 91 | // sorted. 92 | // 93 | 94 | if (FirstOffset == 0 || FirstOffset == Count) { 95 | for (Index = 0; Index < Count - 1; Index++) { 96 | if (stricmp(Files[Index]->achName, Files[Index + 1]->achName) > 0) { 97 | break; 98 | } 99 | } 100 | if (Index == Count - 1) { 101 | return 0; 102 | } 103 | 104 | // 105 | // Nothing was copied and it's not because it's identical. This 106 | // implies a very bad choice of midpoint that belongs either at 107 | // the beginning or the end (so nothing could move past it.) 108 | // Move it around and repeat the process (which will get a new 109 | // midpoint.) 110 | // 111 | 112 | if (FirstOffset == 0) { 113 | if (stricmp(Files[0]->achName, MidPoint.achName) > 0) { 114 | TempForSwap = Files[0]; 115 | Files[0] = Files[BreakPoint]; 116 | Files[BreakPoint] = TempForSwap; 117 | } 118 | } else { 119 | TempForSwap = Files[Count - 1]; 120 | Files[Count - 1] = Files[BreakPoint]; 121 | Files[BreakPoint] = TempForSwap; 122 | } 123 | } else { 124 | break; 125 | } 126 | } 127 | 128 | return FirstOffset; 129 | } 130 | 131 | VOID 132 | ZDirSortFiles( 133 | PFILEFINDBUF * Files, 134 | WORD Count, 135 | WORD Depth 136 | ) 137 | { 138 | WORD FirstOffset; 139 | 140 | if (Count <= 1) { 141 | return; 142 | } 143 | 144 | FirstOffset = ZDirSortFilesInternal(Files, Count); 145 | 146 | // 147 | // If there are two sets (FirstOffset is not at either end), recurse. 148 | // 149 | 150 | if (FirstOffset && (Count - FirstOffset)) { 151 | if (FirstOffset > 0) { 152 | ZDirSortFiles(Files, FirstOffset, Depth + 1); 153 | } 154 | if ((Count - FirstOffset) > 0) { 155 | ZDirSortFiles(&Files[FirstOffset], Count - FirstOffset, Depth + 1); 156 | } 157 | } 158 | } 159 | 160 | typedef struct _ZDIR_FMTCHAR { 161 | UCHAR Char; 162 | UCHAR Attr; 163 | } ZDIR_FMTCHAR; 164 | 165 | typedef ZDIR_FMTCHAR FAR* PZDIR_FMTCHAR; 166 | 167 | BOOL 168 | ZDirWrite ( 169 | PZDIR_FMTCHAR str, 170 | WORD count, 171 | WORD cursorRow 172 | ) 173 | { 174 | WORD i, j; 175 | UCHAR CacheAttr; 176 | UCHAR CharCache[64]; 177 | HVIO hvio; 178 | WORD Err; 179 | WORD cursorColumn; 180 | 181 | hvio = 0; 182 | cursorColumn = 0; 183 | 184 | // 185 | // We firstly load as much as we can into a stack buffer, then write it 186 | // out. Those syscalls are expensive - run in a slow VM to see. We 187 | // have to write out the buffer either when the next character has 188 | // different color (so we can't use the same WriteConsole call) or when 189 | // the stack buffer is full. 190 | // 191 | 192 | j = 0; 193 | for (i = 0; i < count; i++) { 194 | 195 | CacheAttr = str[i].Attr; 196 | CharCache[j] = str[i].Char; 197 | j++; 198 | 199 | if ((i + 1 < count && CacheAttr != str[i + 1].Attr) || 200 | (j >= sizeof(CharCache)/sizeof(CharCache[0]))) { 201 | 202 | Err = VioWrtCharStrAtt(CharCache, j, cursorRow, cursorColumn, &CacheAttr, hvio); 203 | if (Err != NO_ERROR) { 204 | return FALSE; 205 | } 206 | cursorColumn = cursorColumn + j; 207 | j = 0; 208 | } 209 | } 210 | 211 | // 212 | // If we have anything left, flush it now. 213 | // 214 | 215 | if (j > 0) { 216 | Err = VioWrtCharStrAtt(CharCache, j, cursorRow, cursorColumn, &CacheAttr, hvio); 217 | if (Err != NO_ERROR) { 218 | return FALSE; 219 | } 220 | } 221 | 222 | return TRUE; 223 | } 224 | 225 | BOOL 226 | ZDirNewline( 227 | PWORD CursorLine, 228 | PVIOMODEINFO ModeInfo 229 | ) 230 | { 231 | WORD Line; 232 | HVIO hvio; 233 | 234 | hvio = 0; 235 | Line = *CursorLine; 236 | Line++; 237 | VioSetCurPos(Line, 0, hvio); 238 | *CursorLine = Line; 239 | 240 | return TRUE; 241 | } 242 | 243 | BOOL 244 | ZDirScrollForMoreRows( 245 | PVIOMODEINFO ModeInfo, 246 | PWORD CursorRow, 247 | WORD RowsToDisplay 248 | ) 249 | { 250 | WORD RowsAvailableOnScreen; 251 | WORD RowsToScroll; 252 | WORD Line; 253 | WORD BlankCell; 254 | HVIO hvio; 255 | 256 | hvio = 0; 257 | Line = *CursorRow; 258 | RowsAvailableOnScreen = ModeInfo->wRow - Line; 259 | RowsToScroll = 0; 260 | 261 | if (RowsToDisplay > RowsAvailableOnScreen) { 262 | if (RowsToDisplay >= Line) { 263 | RowsToScroll = Line; 264 | } else { 265 | RowsToScroll = RowsToDisplay; 266 | } 267 | BlankCell = 0x07 << 8 | ' '; 268 | VioScrollUp(0, 0, Line - 1, ModeInfo->wColumn - 1, RowsToScroll, &BlankCell, hvio); 269 | Line = Line - RowsToScroll; 270 | VioSetCurPos(Line, 0, hvio); 271 | *CursorRow = Line; 272 | } 273 | return TRUE; 274 | } 275 | 276 | BOOL 277 | ZDirPasteStrAndPad ( 278 | PZDIR_FMTCHAR str, 279 | PUCHAR src, 280 | UCHAR attr, 281 | WORD count, 282 | WORD padsize 283 | ) 284 | { 285 | WORD i; 286 | 287 | for (i = 0; i < count && i < padsize; i++) { 288 | str[i].Char = src[i]; 289 | str[i].Attr = attr; 290 | } 291 | 292 | for (; i < padsize; i++) { 293 | str[i].Char = ' '; 294 | str[i].Attr = attr; 295 | } 296 | 297 | return TRUE; 298 | } 299 | 300 | WORD 301 | ZDirCollectFiles( 302 | PSZ UserSearchCriteria, 303 | PFILEFINDBUF * SortTable, 304 | PUCHAR DriveNumber, 305 | PWORD FilesFound 306 | ) 307 | { 308 | WORD DirHandle; 309 | WORD FileCount; 310 | WORD FindFirstContext; 311 | FILEFINDBUF StackFileBuf; 312 | PFILEFINDBUF HeapFileBuf; 313 | PSZ SearchCriteria; 314 | WORD Index; 315 | WORD Err; 316 | WORD Result; 317 | WORD SizeRequired; 318 | BOOL WildFound; 319 | 320 | FileCount = 0; 321 | SearchCriteria = UserSearchCriteria; 322 | Result = 0; 323 | 324 | *DriveNumber = 0; 325 | if ((SearchCriteria[0] >= 'A' && SearchCriteria[0] <= 'Z') && 326 | SearchCriteria[1] == ':') { 327 | 328 | *DriveNumber = SearchCriteria[0] - 'A' + 1; 329 | 330 | } else if ((SearchCriteria[0] >= 'a' && SearchCriteria[0] <= 'z') && 331 | SearchCriteria[1] == ':') { 332 | 333 | *DriveNumber = SearchCriteria[0] - 'a' + 1; 334 | } 335 | 336 | while(TRUE) { 337 | DirHandle = HDIR_SYSTEM; 338 | FindFirstContext = 1; 339 | Err = DosFindFirst(SearchCriteria, &DirHandle, FILE_HIDDEN | FILE_SYSTEM | FILE_DIRECTORY, &StackFileBuf, sizeof(StackFileBuf), &FindFirstContext, 0); 340 | if (Err != NO_ERROR && 341 | Err != ERROR_NO_MORE_FILES && 342 | Err != ERROR_PATH_NOT_FOUND) { 343 | 344 | printf("Error %i: %s\r\n", Err, SearchCriteria); 345 | Result = 1; 346 | break; 347 | } 348 | 349 | // 350 | // If the string doesn't contain a wildcard, and it matches a 351 | // directory (should only match one), append a \*.* and look again. 352 | // 353 | 354 | WildFound = FALSE; 355 | for (Index = 0; SearchCriteria[Index] != '\0'; Index++) { 356 | if (SearchCriteria[Index] == '?' || SearchCriteria[Index] == '*') { 357 | WildFound = TRUE; 358 | } 359 | } 360 | 361 | // 362 | // ERROR_NO_MORE_FILES can be returned for a root directory, where 363 | // unlike other directories, there's no way to return the directory 364 | // itself. However it also might be a specific object that doesn't 365 | // exist. DOS uses ERROR_PATH_NOT_FOUND for this condition. 366 | // 367 | 368 | if (Err == ERROR_NO_MORE_FILES || Err == ERROR_PATH_NOT_FOUND) { 369 | 370 | // 371 | // Truncate trailing slashes. If this results in an empty 372 | // string, the criteria had to be for the root (and it can be 373 | // added back as start of \*.* below), or if it results in a 374 | // drive letter this is a root for some other drive (also 375 | // handled the same way.) 376 | // 377 | 378 | while (Index > 0 && 379 | (SearchCriteria[Index - 1] == '\\' || SearchCriteria[Index - 1] == '/')) { 380 | Index--; 381 | } 382 | SearchCriteria[Index] = '\0'; 383 | 384 | if (Index > 2 || (Index == 2 && SearchCriteria[1] != ':')) { 385 | printf("No matching files found.\r\n"); 386 | Result = 1; 387 | break; 388 | } 389 | } else if (WildFound || ((StackFileBuf.attrFile & FILE_DIRECTORY) == 0)) { 390 | break; 391 | } 392 | 393 | SearchCriteria = malloc(Index + sizeof("\\*.*")); 394 | if (SearchCriteria == NULL) { 395 | printf("out of memory\r\n"); 396 | Result = 1; 397 | break; 398 | } 399 | 400 | sprintf(SearchCriteria, "%s\\*.*", UserSearchCriteria); 401 | } 402 | 403 | if (SearchCriteria != UserSearchCriteria) { 404 | free(SearchCriteria); 405 | } 406 | 407 | if (Result != 0) { 408 | *FilesFound = 0; 409 | return Result; 410 | } 411 | 412 | do { 413 | SizeRequired = FIELD_OFFSET(FILEFINDBUF, achName) + StackFileBuf.cchName + 1; 414 | HeapFileBuf = malloc(SizeRequired); 415 | if (HeapFileBuf == NULL) { 416 | printf("out of memory\r\n"); 417 | Result = 1; 418 | break; 419 | } 420 | memcpy(HeapFileBuf, &StackFileBuf, SizeRequired); 421 | SortTable[FileCount] = HeapFileBuf; 422 | FileCount++; 423 | if (FileCount >= ZDIR_MAX_FILES) { 424 | printf("too many files\r\n"); 425 | Result = 1; 426 | break; 427 | } 428 | Err = DosFindNext(DirHandle, &StackFileBuf, sizeof(StackFileBuf), &FindFirstContext); 429 | } while(Err == 0); 430 | 431 | DosFindClose(DirHandle); 432 | *FilesFound = FileCount; 433 | return Result; 434 | } 435 | 436 | BOOL 437 | ZDirFileSizeToString( 438 | PUCHAR String, 439 | DWORD FileSize, 440 | UCHAR SuffixShift 441 | ) 442 | { 443 | UCHAR Suffixes[] = {'b', 'k', 'm', 'g', 't', '?'}; 444 | WORD SuffixLevel = 0; 445 | WORD LengthInChars; 446 | DWORD Size; 447 | DWORD OldSize; 448 | 449 | Size = FileSize; 450 | SuffixLevel = SuffixShift; 451 | OldSize = Size; 452 | 453 | 454 | while (Size > 9999) { 455 | SuffixLevel++; 456 | OldSize = Size; 457 | 458 | // 459 | // Conceptually we want to divide by 1024. 460 | // 461 | 462 | Size = Size / 1024; 463 | } 464 | 465 | if (SuffixLevel >= sizeof(Suffixes)/sizeof(CHAR)) { 466 | SuffixLevel = sizeof(Suffixes)/sizeof(UCHAR) - 1; 467 | } 468 | 469 | if (Size < 100 && SuffixLevel > 0) { 470 | OldSize = (OldSize % 1024) * 10 / 1024; 471 | LengthInChars = sprintf(String, "%2i.%1i%c", (WORD)Size, (WORD)OldSize, Suffixes[SuffixLevel]); 472 | } else { 473 | LengthInChars = sprintf(String, "%4i%c", (WORD)Size, Suffixes[SuffixLevel]); 474 | } 475 | return TRUE; 476 | } 477 | 478 | UCHAR 479 | ZDirAttributesForFile( 480 | PFILEFINDBUF fb 481 | ) 482 | { 483 | WORD Index; 484 | PSZ Ext; 485 | 486 | if (fb->attrFile & FILE_DIRECTORY) { 487 | return 0x0D; 488 | } 489 | 490 | for (Index = fb->cchName; Index > 0; Index--) { 491 | if (fb->achName[Index - 1] == '.') { 492 | break; 493 | } 494 | } 495 | 496 | if (Index == 0) { 497 | return 0x07; 498 | } 499 | 500 | Ext = &fb->achName[Index]; 501 | if (stricmp(Ext, "exe") == 0 || 502 | stricmp(Ext, "com") == 0) { 503 | 504 | return 0x0B; 505 | } else if (stricmp(Ext, "bat") == 0 || 506 | stricmp(Ext, "cmd") == 0 || 507 | stricmp(Ext, "ys1") == 0) { 508 | return 0x0C; 509 | } 510 | 511 | return 0x07; 512 | } 513 | 514 | 515 | BOOL 516 | ZDirIsPharLap(VOID) 517 | { 518 | HMODULE hPhapi; 519 | WORD Err; 520 | 521 | Err = DosGetModHandle("PHAPI", &hPhapi); 522 | if (Err != NO_ERROR) { 523 | return FALSE; 524 | } 525 | 526 | return TRUE; 527 | } 528 | 529 | 530 | WORD 531 | ZDirGenerateSummary( 532 | PZDIR_FMTCHAR Line, 533 | UCHAR DriveNumber, 534 | PFILEFINDBUF * SortTable, 535 | WORD FileCount 536 | ) 537 | { 538 | WORD Index; 539 | WORD TotalFiles; 540 | WORD TotalDirectories; 541 | WORD LineIndex; 542 | WORD StrLen; 543 | UCHAR Str[16]; 544 | DWORD TotalUsed; 545 | DWORD BytesPerAllocationUnit; 546 | UCHAR SuffixShift; 547 | PFILEFINDBUF fb; 548 | FSALLOCATE FsSpace; 549 | 550 | TotalFiles = 0; 551 | TotalDirectories = 0; 552 | TotalUsed = 0; 553 | 554 | for (Index = 0; Index < FileCount; Index++) { 555 | fb = SortTable[Index]; 556 | if (fb->attrFile & FILE_DIRECTORY) { 557 | if (strcmp(fb->achName, ".") != 0 && 558 | strcmp(fb->achName, "..") != 0) { 559 | 560 | TotalDirectories++; 561 | } 562 | } else { 563 | TotalFiles++; 564 | TotalUsed = TotalUsed + fb->cbFile; 565 | } 566 | } 567 | 568 | LineIndex = 0; 569 | 570 | // 571 | // This space is so that command.com inherits this color when it 572 | // scrolls after the program exits 573 | // 574 | StrLen = sizeof(" ") - 1; 575 | ZDirPasteStrAndPad(&Line[LineIndex], " ", 0x7, StrLen, StrLen); 576 | LineIndex = LineIndex + StrLen; 577 | StrLen = sprintf(Str, "%i", TotalFiles); 578 | ZDirPasteStrAndPad(&Line[LineIndex], Str, 0xA, StrLen, StrLen); 579 | LineIndex = LineIndex + StrLen; 580 | StrLen = sizeof(" files,") - 1; 581 | ZDirPasteStrAndPad(&Line[LineIndex], " files,", 0x7, StrLen, StrLen); 582 | LineIndex = LineIndex + StrLen; 583 | StrLen = sprintf(Str, " %i", TotalDirectories); 584 | ZDirPasteStrAndPad(&Line[LineIndex], Str, 0xA, StrLen, StrLen); 585 | LineIndex = LineIndex + StrLen; 586 | StrLen = sizeof(" dirs") - 1; 587 | ZDirPasteStrAndPad(&Line[LineIndex], " dirs", 0x7, StrLen, StrLen); 588 | LineIndex = LineIndex + StrLen; 589 | 590 | if (ZDirIsPharLap()) { 591 | return LineIndex; 592 | } 593 | 594 | StrLen = sizeof(", ") - 1; 595 | ZDirPasteStrAndPad(&Line[LineIndex], ", ", 0x7, StrLen, StrLen); 596 | LineIndex = LineIndex + StrLen; 597 | ZDirFileSizeToString(Str, TotalUsed, 0); 598 | StrLen = 5; 599 | ZDirPasteStrAndPad(&Line[LineIndex], Str, 0xE, StrLen, StrLen); 600 | LineIndex = LineIndex + StrLen; 601 | StrLen = sizeof(" used, ") - 1; 602 | ZDirPasteStrAndPad(&Line[LineIndex], " used, ", 0x7, StrLen, StrLen); 603 | LineIndex = LineIndex + StrLen; 604 | 605 | DosQFSInfo(DriveNumber, 1, &FsSpace, sizeof(FsSpace)); 606 | 607 | // 608 | // Free space is returned with two 32 bit numbers and a 16 bit number. 609 | // It looks like the system intended to support free space larger than 610 | // 32 bits. Calculate a 32 bit allocation unit size, and see if it 611 | // is a multiple of kilobytes. If so, we can reclaim 10 bits here and 612 | // use them to describe larger amounts of disk space. 613 | // 614 | 615 | SuffixShift = 0; 616 | BytesPerAllocationUnit = FsSpace.SectorsPerAllocationUnit * FsSpace.BytesPerSector; 617 | while (BytesPerAllocationUnit >= 1024) { 618 | BytesPerAllocationUnit = BytesPerAllocationUnit / 1024; 619 | SuffixShift++; 620 | } 621 | 622 | TotalUsed = FsSpace.TotalAllocationUnits * BytesPerAllocationUnit; 623 | 624 | ZDirFileSizeToString(Str, TotalUsed, SuffixShift); 625 | StrLen = 5; 626 | ZDirPasteStrAndPad(&Line[LineIndex], Str, 0xE, StrLen, StrLen); 627 | LineIndex = LineIndex + StrLen; 628 | StrLen = sizeof(" vol size, ") - 1; 629 | ZDirPasteStrAndPad(&Line[LineIndex], " vol size, ", 0x7, StrLen, StrLen); 630 | LineIndex = LineIndex + StrLen; 631 | 632 | TotalUsed = FsSpace.AllocationUnitsAvailable * BytesPerAllocationUnit; 633 | 634 | ZDirFileSizeToString(Str, TotalUsed, SuffixShift); 635 | StrLen = 5; 636 | ZDirPasteStrAndPad(&Line[LineIndex], Str, 0xE, StrLen, StrLen); 637 | LineIndex = LineIndex + StrLen; 638 | StrLen = sizeof(" vol free") - 1; 639 | ZDirPasteStrAndPad(&Line[LineIndex], " vol free", 0x7, StrLen, StrLen); 640 | LineIndex = LineIndex + StrLen; 641 | 642 | return LineIndex; 643 | } 644 | 645 | #define ZDIR_MAX_WIDTH 160 646 | 647 | #define ZDIR_LINE_ELEMENT_HORIZ 0 648 | #define ZDIR_LINE_ELEMENT_TOP_T 1 649 | #define ZDIR_LINE_ELEMENT_BOTTOM_T 2 650 | #define ZDIR_LINE_ELEMENT_VERT 3 651 | 652 | const UCHAR ZDirLineElements[] = {196, 194, 193, 179}; 653 | 654 | #define ZDIR_GRID_COLOR 0x2 655 | 656 | WORD 657 | ZDirDisplayFiles( 658 | PFILEFINDBUF * SortTable, 659 | WORD FileCount, 660 | UCHAR DriveNumber, 661 | BOOL PauseEnabled 662 | ) 663 | { 664 | PFILEFINDBUF fb; 665 | WORD Index; 666 | WORD FileIndex; 667 | ZDIR_FMTCHAR Line[ZDIR_MAX_WIDTH]; 668 | VIOMODEINFO ModeInfo; 669 | WORD CursorRow; 670 | WORD CursorColumn; 671 | WORD LongestDisplayName; 672 | WORD MetadataWidth; 673 | WORD BufferRows; 674 | WORD ColumnWidth; 675 | WORD LinesThisPage; 676 | WORD Columns; 677 | WORD GridCellCount; 678 | WORD Err; 679 | WORD ActiveColumn; 680 | WORD CurrentChar; 681 | HVIO hvio; 682 | 683 | hvio = 0; 684 | 685 | ModeInfo.cb = sizeof(ModeInfo); 686 | Err = VioGetMode(&ModeInfo, hvio); 687 | if (Err != NO_ERROR) { 688 | printf("VioGetMode returned %i\r\n", Err); 689 | return Err; 690 | } 691 | 692 | // printf("\r\n"); 693 | Err = VioGetCurPos(&CursorRow, &CursorColumn, hvio); 694 | if (Err != NO_ERROR) { 695 | return Err; 696 | } 697 | // VioSetCurPos(CursorRow - 1, CursorColumn, hvio); 698 | // printf("Display %ix%i, Cursor %i, %i\r\n", ModeInfo.wColumn, ModeInfo.wRow, CursorColumn, CursorRow); 699 | 700 | LongestDisplayName = 0; 701 | for (Index = 0; Index < FileCount; Index++) { 702 | if (SortTable[Index]->cchName > LongestDisplayName) { 703 | LongestDisplayName = SortTable[Index]->cchName; 704 | } 705 | } 706 | 707 | ColumnWidth = ModeInfo.wColumn; 708 | if (ColumnWidth > ZDIR_MAX_WIDTH) { 709 | ColumnWidth = ZDIR_MAX_WIDTH; 710 | } 711 | 712 | // 713 | // Five chars for size, plus space, plus seperator 714 | // MSFIX This needs to get smarter 715 | // 716 | 717 | MetadataWidth = 7; 718 | 719 | Columns = ColumnWidth / (LongestDisplayName + MetadataWidth); 720 | if (Columns > 0) { 721 | ColumnWidth = ColumnWidth / Columns; 722 | } else { 723 | Columns = 1; 724 | } 725 | 726 | BufferRows = (FileCount + Columns - 1) / Columns; 727 | 728 | // 729 | // In addition to rows containing files, we have two grid lines and 730 | // a summary line. 731 | // 732 | 733 | ZDirScrollForMoreRows(&ModeInfo, &CursorRow, BufferRows + 3); 734 | 735 | // 736 | // Draw the top grid line. 737 | // 738 | 739 | for (Index = 0; Index < ColumnWidth * Columns; Index++) { 740 | if (Index % ColumnWidth == ColumnWidth - 1 && Index < (ColumnWidth*Columns-1)) { 741 | Line[Index].Char = ZDirLineElements[ZDIR_LINE_ELEMENT_TOP_T]; 742 | } else { 743 | Line[Index].Char = ZDirLineElements[ZDIR_LINE_ELEMENT_HORIZ]; 744 | } 745 | 746 | Line[Index].Attr = ZDIR_GRID_COLOR; 747 | } 748 | 749 | ZDirWrite(Line, Index, CursorRow); 750 | ZDirNewline(&CursorRow, &ModeInfo); 751 | 752 | GridCellCount = BufferRows * Columns; 753 | ActiveColumn = 0; 754 | CurrentChar = 0; 755 | LinesThisPage = 1; 756 | 757 | for (Index = 0; Index < GridCellCount; Index++) { 758 | 759 | FileIndex = ActiveColumn * BufferRows + Index / Columns; 760 | if (FileIndex < FileCount) { 761 | fb = SortTable[FileIndex]; 762 | } else { 763 | fb = NULL; 764 | } 765 | 766 | if (fb == NULL) { 767 | ZDirPasteStrAndPad(&Line[CurrentChar], NULL, 0x7, 0, ColumnWidth - 1); 768 | CurrentChar = CurrentChar + ColumnWidth - 1; 769 | } else { 770 | UCHAR FileSizeString[7]; 771 | WORD MaxLength; 772 | WORD FileNameLength; 773 | UCHAR Attr; 774 | 775 | MaxLength = ColumnWidth - MetadataWidth; 776 | FileNameLength = MaxLength; 777 | if (fb->cchName < FileNameLength) { 778 | FileNameLength = fb->cchName; 779 | } 780 | 781 | Attr = ZDirAttributesForFile(fb); 782 | ZDirPasteStrAndPad(&Line[CurrentChar], fb->achName, Attr, FileNameLength, MaxLength); 783 | FileSizeString[0] = ' '; 784 | if (fb->attrFile & FILE_DIRECTORY) { 785 | sprintf(&FileSizeString[1], ""); 786 | Attr = 0xd; 787 | } else { 788 | FileSizeString[1] = '\0'; 789 | ZDirFileSizeToString(&FileSizeString[1], fb->cbFile, 0); 790 | Attr = 0xe; 791 | } 792 | ZDirPasteStrAndPad(&Line[CurrentChar + MaxLength], FileSizeString, Attr, 6, 6); 793 | 794 | CurrentChar = CurrentChar + ColumnWidth - 1; 795 | } 796 | 797 | ActiveColumn++; 798 | if (ActiveColumn % Columns == 0) { 799 | WORD LinesRemaining; 800 | LinesRemaining = BufferRows - (Index / Columns) + 2; 801 | ZDirWrite(Line, CurrentChar, CursorRow); 802 | CurrentChar = 0; 803 | ActiveColumn = 0; 804 | ZDirNewline(&CursorRow, &ModeInfo); 805 | LinesThisPage++; 806 | if (LinesThisPage == ModeInfo.wRow - 1) { 807 | if (PauseEnabled) { 808 | WORD StrLen; 809 | KBDKEYINFO CharData; 810 | StrLen = sizeof("Press any key to continue...") - 1; 811 | ZDirPasteStrAndPad(Line, "Press any key to continue...", 0xD, StrLen, StrLen); 812 | ZDirWrite(Line, StrLen, CursorRow); 813 | VioSetCurPos(CursorRow, StrLen, hvio); 814 | KbdCharIn(&CharData, 0, 0); 815 | if (CharData.cChar == 'q') { 816 | return NO_ERROR; 817 | } 818 | 819 | // 820 | // Pretend the cursor is on the next line so this 821 | // message is included in the text that gets scrolled 822 | // 823 | CursorRow = CursorRow + 1; 824 | } 825 | LinesThisPage = 0; 826 | ZDirScrollForMoreRows(&ModeInfo, &CursorRow, LinesRemaining); 827 | } 828 | } else { 829 | Line[CurrentChar].Char = ZDirLineElements[ZDIR_LINE_ELEMENT_VERT]; 830 | Line[CurrentChar].Attr = ZDIR_GRID_COLOR; 831 | CurrentChar++; 832 | } 833 | } 834 | 835 | // 836 | // Draw the bottom grid line. 837 | // 838 | 839 | for (Index = 0; Index < ColumnWidth * Columns; Index++) { 840 | if (Index % ColumnWidth == ColumnWidth - 1 && Index < (ColumnWidth*Columns-1)) { 841 | Line[Index].Char = ZDirLineElements[ZDIR_LINE_ELEMENT_BOTTOM_T]; 842 | } else { 843 | Line[Index].Char = ZDirLineElements[ZDIR_LINE_ELEMENT_HORIZ]; 844 | } 845 | 846 | Line[Index].Attr = ZDIR_GRID_COLOR; 847 | } 848 | 849 | ZDirWrite(Line, Index, CursorRow); 850 | ZDirNewline(&CursorRow, &ModeInfo); 851 | 852 | Index = ZDirGenerateSummary(Line, DriveNumber, SortTable, FileCount); 853 | if (Index > 0) { 854 | ZDirWrite(Line, Index, CursorRow); 855 | 856 | // 857 | // Move the cursor to after the summary on the same line. This 858 | // hints to Yori to move to the next line; other shells do this 859 | // unconditionally. 860 | // 861 | 862 | VioSetCurPos(CursorRow, Index, hvio); 863 | } 864 | 865 | return NO_ERROR; 866 | } 867 | 868 | VOID 869 | ZDirFreeFiles( 870 | PFILEFINDBUF * SortTable, 871 | WORD FileCount 872 | ) 873 | { 874 | WORD Index; 875 | for (Index = 0; Index < FileCount; Index++) { 876 | free(SortTable[Index]); 877 | SortTable[Index] = NULL; 878 | } 879 | } 880 | 881 | BOOL 882 | ZDirIsCommandLineOptionChar( 883 | UCHAR Char 884 | ) 885 | { 886 | if (Char == '/' || Char == '-') { 887 | return TRUE; 888 | } 889 | return FALSE; 890 | } 891 | 892 | VOID 893 | ZDirHelp(VOID) 894 | { 895 | printf("ZDir [-?] [-license] [-p|-pn] [FileSpec]\r\n\r\n"); 896 | printf(" -p Pause after each screen\r\n"); 897 | printf(" -pn Don't pause after each screen\r\n"); 898 | } 899 | 900 | const 901 | CHAR strMitLicenseText[] = 902 | "Copyright (c) 2023 Malcolm J. Smith\r\n" 903 | "\r\n" 904 | "Permission is hereby granted, free of charge, to any person obtaining a copy\r\n" 905 | "of this software and associated documentation files (the \"Software\"), to deal\r\n" 906 | "in the Software without restriction, including without limitation the rights\r\n" 907 | "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r\n" 908 | "copies of the Software, and to permit persons to whom the Software is\r\n" 909 | "furnished to do so, subject to the following conditions:\r\n" 910 | "\n" 911 | "The above copyright notice and this permission notice shall be included in\r\n" 912 | "all copies or substantial portions of the Software.\r\n" 913 | "\n" 914 | "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r\n" 915 | "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r\n" 916 | "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r\n" 917 | "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r\n" 918 | "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r\n" 919 | "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r\n" 920 | "THE SOFTWARE.\r\n"; 921 | 922 | VOID 923 | ZDirLicense(VOID) 924 | { 925 | WORD BytesWritten; 926 | DosWrite(1, (PSZ)strMitLicenseText, sizeof(strMitLicenseText) - 1, &BytesWritten); 927 | } 928 | 929 | 930 | WORD CDECL 931 | main( 932 | WORD argc, 933 | PSZ argv[] 934 | ) 935 | { 936 | WORD FilesFound; 937 | PFILEFINDBUF * SortTable; 938 | WORD ArgIndex; 939 | PSZ Arg; 940 | WORD Result; 941 | WORD StartArg; 942 | UCHAR DriveNumber; 943 | BOOL OutputGenerated; 944 | BOOL PauseEnabled; 945 | 946 | PauseEnabled = TRUE; 947 | 948 | StartArg = 0; 949 | for (ArgIndex = 1; ArgIndex < argc; ArgIndex++) { 950 | Arg = argv[ArgIndex]; 951 | if (ZDirIsCommandLineOptionChar(Arg[0])) { 952 | if (stricmp(&Arg[1], "?") == 0) { 953 | ZDirHelp(); 954 | return Result; 955 | } else if (stricmp(&Arg[1], "license") == 0) { 956 | ZDirLicense(); 957 | return Result; 958 | } else if (stricmp(&Arg[1], "p") == 0) { 959 | PauseEnabled = TRUE; 960 | } else if (stricmp(&Arg[1], "pn") == 0) { 961 | PauseEnabled = FALSE; 962 | } 963 | } else { 964 | StartArg = ArgIndex; 965 | } 966 | } 967 | 968 | SortTable = malloc(ZDIR_MAX_FILES * sizeof(PFILEFINDBUF)); 969 | if (SortTable == NULL) { 970 | return 1; 971 | } 972 | 973 | // 974 | // If no usable argument was found, start the loop beyond all of the 975 | // arguments, which causes it to default to *.* 976 | // 977 | 978 | if (StartArg == 0) { 979 | StartArg = argc; 980 | } 981 | 982 | Result = 0; 983 | ArgIndex = StartArg; 984 | OutputGenerated = FALSE; 985 | 986 | do { 987 | if (ArgIndex < argc) { 988 | Arg = argv[ArgIndex]; 989 | if (ZDirIsCommandLineOptionChar(Arg[0])) { 990 | ArgIndex++; 991 | continue; 992 | } 993 | } else if (!OutputGenerated) { 994 | Arg = "*.*"; 995 | } else { 996 | break; 997 | } 998 | 999 | OutputGenerated = TRUE; 1000 | 1001 | Result = ZDirCollectFiles(Arg, SortTable, &DriveNumber, &FilesFound); 1002 | if (Result != NO_ERROR) { 1003 | break; 1004 | } 1005 | 1006 | ZDirSortFiles(SortTable, FilesFound, 1); 1007 | Result = ZDirDisplayFiles(SortTable, FilesFound, DriveNumber, PauseEnabled); 1008 | if (Result != NO_ERROR) { 1009 | break; 1010 | } 1011 | 1012 | ZDirFreeFiles(SortTable, FilesFound); 1013 | FilesFound = 0; 1014 | 1015 | ArgIndex++; 1016 | } while (ArgIndex < argc); 1017 | 1018 | if (FilesFound > 0) { 1019 | ZDirFreeFiles(SortTable, FilesFound); 1020 | FilesFound = 0; 1021 | } 1022 | 1023 | free(SortTable); 1024 | 1025 | return Result; 1026 | } 1027 | --------------------------------------------------------------------------------