====== NEC PC-9801/9821 Development Tools ====== The NEC PC-98 (PC-9801 and PC-9821) are are a range of x86 compatible desktop computers from NEC in the mid to late 80's, and early to mid 90's. They were very popular within Japan. {{:blog:nec_pc_9801_png_by_framerater_ddcb3m8-fullview.png?200|}} {{:blog:nec_pc-9821apu2.jpg?300|}} The earlier PC-9801 is approximately equivalent to the IBM PC XT class machines; 8086 (or rather the NEC Vxx equivalent), with the PC-9821 being aligned with the IBM AT, 386 and above. They both have some level of MS-DOS/Windows compatibility, but they are very definitely their own platform, with their own ports of popular applications and, more importantly, //games// :-) This page is my attempt to try and collate the available material that exists, //in English//, for development tools for those platforms. ===== Reference Material ===== This is mostly in Japanese, of course, but contains very useful information on the hardware structure of the machine, as well as the different BIOS calls and memory map. These documents do focus more on the //earlier// PC-9801 machines somewhat. {{:blog:pc-9800technicaldatabookbios.png?200|}} - {{ :blog:pc-9800technicaldatabookbios.pdf |}} {{:blog:pc-9800technicaldatabookhardware.png?200|}} - {{ :blog:pc-9800technicaldatabookhardware.pdf |}} {{:blog:pc-9801programmersbible.png?200|}} - {{:blog:pc-9801programmersbible.pdf|}} Other reference websites: * [[http://radioc.web.fc2.com/column/pc98bas/pc98disphw_en.htm|PC-98 Display Types]] - Explanation of the various graphics hardware, in English * [[http://web.archive.org/web/20041225164712/http://www2.muroran-it.ac.jp/circle/mpc/program/pc98dos/index.html|PC-98 DOS/Hardware programming]] - at Archive.org ({{ :blog:pc98doshw.zip |local copy}}), both in Japanese * [[http://www.webtech.co.jp/company/doc/undocumented_mem/index.html|Undocumented IO functions]] - Listing and explanation of various undocumented IO functions of the PC-98 range, in Japanese * http://www.satotomi.com/sl9821/sl9821_tec5.html - Description of the PEGC graphics hardware IO ports, in Japanese * http://nickle.shimbe.net/pegc.html - Another description of the PEGC graphics hardware IO ports, in Japanese * https://github.com/techfury90/pc98stuff - Sample C code for interacting with the hardware/graphics * https://github.com/PC-98/devwiki - Short wiki about potential development tools for PC-98 * http://darudarudan.syuriken.jp/kai/pc9821.htm - Some short examples of interacting with PC-98 hardware, in Japanese * https://forums.bannister.org/ubbthreads.php?ubb=showflat&Number=102975&page=all - Discussion about implementing PEGC packed-pixel support for PC-98 within MAME ==== Graphics Hardware Differences ==== Of particular interest is this diagram (from [[http://radioc.web.fc2.com/column/pc98bas/pc98disphw_en.htm|here]]) which shows the matrix of the various graphics options of the PC-98 series: {{:blog:pc98disphw_2_en.png?600|}} You can see that the 256 colour mode is available on the PC-9821 range, but its functionality is split; planar and packed-pixel/chunky. Fortunately packed-pixel is the easiest to work with (and closest to IBM PC style VGA), where one byte is one pixel, and is available on all of the PC-9821 range. ===== Compilers ===== The options for C compilers for the PC-98 are limited; although it the PC-98 runs a version of MS-DOS, it isn't a PC compatible in the strictest sense of the word: * Different BIOS interface * Different set of interrupts * Different memory map * Different types of peripherals * Different sound hardware * Different video hardware There's no ISA bus, no VGA, or anything like that. So the hardware interface side of things is quite different. You can run well behaved DOS applications, but only if they use the DOS api and don't hit the hardware directly; so nothing that does video or sound, for example. ==== Legacy Compilers ==== These compilers were written to run directly on the hardware itself, compatability with current C standards and syntax is likely to be low, but they are well proven (most of them were previously commercial packages): == Assemblers == * {{ :blog:turbo_debugger_tools_fd_.zip |Borland Turbo Assembler & Debugger 2.0}} - (1.2MB PC-98 FD Images) * {{ :blog:microsoft_macro_assembler_version_5.1a_fd_.zip |Microsoft Macro Assembler 5.1}} - (1.2MB PC-98 FD Images) == C Compilers == * {{ :blog:turbo_c_v2.0_fd_.zip |Borland Turbo C 2.0}} - (1.2MB PC-98 FD Images) * {{ :blog:turbo_c_v2.0c_fd_.zip |Borland Turbo C 2.0c}} - (1.2MB PC-98 FD Images) * {{ :blog:quickc_fd_.zip |Microsoft Quick C}} - (1.2MB PC-98 FD Images) == C++ Compilers == * {{ :blog:turbo_c_second_edition_fd_.zip |Borland Turbo C++ 2.0}} - (1.2MB PC-98 FD Images) * {{ :blog:turbo_c_4.0_fd_.zip |Borland Turbo C++ 4.0}} - (1.2MB PC-98 FD Images) ==== GCC ==== There doesn't appear to be a specific PC-98 target for GCC (either native, or as a cross-compiler option), but there //does exist some support// in DJGPP (the port of GCC to MS-DOS). ==== DJGPP ==== https://www.vector.co.jp/vpack/filearea/dos/prog/c/gcc/index.html Firstly, this is all of the disparate legacy DJGPP stuff that was released years ago. Unfortunately there isn't a single, self-contained distribution containing all of the necessary parts for a working build/runtime environment: === DJGPP 1.12 === * {{ :blog:djgpp98p_go32.zip |DJGPP go32}} - 32bit DPMI server and graphics drivers for DJGPP protected mode binaries. Executable binaries + source. * {{ :blog:djdev112.zip |DJGPP 1.12}} - Full version of DJGPP 1.12, including headers and libc. * {{ :blog:gcc271bn.zip | GNU GCC 2.7.1}} * {{ :blog:bnu252bn.zip | GNU binutils 2.5.2}} * {{ :blog:mak371bn.zip | GNU make 3.7.1}} * {{ :blog:gas23dj3.zip | GNU assembler 2.3}} === DJGPP 2.03 === There exists a [[https://www.mfp.gr.jp/users/takas/prog/djgpp.html|patch]] to add in PC-98 support to DJGPP 2.03, but it's a patch against //both// the source code/headers, //and// a binary diff against the version of libc.a included with DJGPP, which is __unusual__, to say the least. A translated description of the patch is linked [[:blog:pc98_djgpp_patchnotes|here]]. //Note: If you just want a fully working DJGPP build environment and don't want to know how to set it up from scratch, skip to [[blog:pc98_devtools#djgpp_203_end_step|the end step]] to download a pre-build archive.// This is how you build the patch: - Unzip DJGPP 2.03 binaries {{ :blog:djdev203.zip |}} (into **./djgpp/**) - Unzip DJGPP 2.03 source {{ :blog:djlsr203.zip |}} (into **./djsrc/**) - Unzip DJGPP 2.03 libc patch {{ :blog:djgpp203980_libcpatch.zip |}}, which contains **dj203980.dif** and **dj203980.ldf** - Convert patch file to unix line breaks and apply patch $ cd djsrc djsrc$ dos2unix ../dj203980.dif dos2unix: converting file ../dj203980.dif to Unix format... djsrc$ patch -p0 -b --binary < ../dj203980.dif checking file include/libc/pc9800.h checking file src/libc/bios/b_time.c checking file src/libc/bios/bioscom.c checking file src/libc/bios/biosdisk.c checking file src/libc/bios/biosequi.c checking file src/libc/bios/bioskey.c checking file src/libc/bios/biosmem.c checking file src/libc/bios/biosprin.c checking file src/libc/bios/biostime.c checking file src/libc/compat/time/rawclock.c checking file src/libc/crt0/crt1.c Hunk #2 succeeded at 20 with fuzz 1 (offset 1 line). Hunk #3 succeeded at 33 (offset 1 line). Hunk #4 succeeded at 86 (offset 26 lines). Hunk #5 succeeded at 141 (offset 26 lines). checking file src/libc/dos/dos/delay.c checking file src/libc/dos/io/flushdc.c checking file src/libc/go32/dpmiexcp.c Hunk #2 FAILED at 22. Hunk #3 succeeded at 107 (offset 1 line). Hunk #4 FAILED at 193. Hunk #5 succeeded at 519 with fuzz 2 (offset 1 line). Hunk #6 succeeded at 553 (offset 1 line). Hunk #7 succeeded at 570 (offset 1 line). 2 out of 7 hunks FAILED checking file src/libc/pc_hw/co80/conio.c checking file src/libc/pc_hw/co80/conio98.c checking file src/libc/pc_hw/co80/scattrib.c checking file src/libc/pc_hw/co80/scclear.c checking file src/libc/pc_hw/co80/sccols.c checking file src/libc/pc_hw/co80/scgetc.c checking file src/libc/pc_hw/co80/scgetch.c checking file src/libc/pc_hw/co80/scmode.c checking file src/libc/pc_hw/co80/scputc.c checking file src/libc/pc_hw/co80/scputs.c checking file src/libc/pc_hw/co80/scretr.c checking file src/libc/pc_hw/co80/scrows.c checking file src/libc/pc_hw/co80/scsetc.c checking file src/libc/pc_hw/co80/scupdate.c checking file src/libc/pc_hw/co80/scupdl.c checking file src/libc/pc_hw/co80/scvbell.c checking file src/libc/pc_hw/kb/getkey.c checking file src/libc/pc_hw/kb/getxkey.c checking file src/libc/pc_hw/kb/kbhit.c checking file src/libc/pc_hw/mono/mono.c checking file src/libc/pc_hw/sound/sound.c checking file src/libc/pc_hw/timer/clock.c checking file src/libc/pc_hw/timer/uclock.c checking file src/libc/posix/termios/tcflush.c checking file src/libc/posix/termios/tminit.c checking file src/libc/posix/unistd/getpid.c djsrc$ Here's a zip of the source tree with the patch applied: {{ :blog:djsrc203.zip |}} You then have to copy the the new **./include/libc/pc9800.h** header to the place where you unzipped the DJGPP 2.03 binaries; there should already be an **./include/libc/** tree as part of the install, just drop the file in there. You can then either build a new **libc.a** from the updated source files, above, or patch the existing libc.a in the existing DJGPP 2.03 binary install directory. The install notes for the patch suggest using the {{ :blog:ldf103.zip |ldf tool}} to apply dj203980.ldf, but try as I might, I simply cannot get this patch to apply properly - it either complains that the timestamp or size of the original DJGPP 2.03 libc.a is incorrect... and if you force the patch, you end up with a libc.a with the same md5sum as the original one. So, back to the drawing board and try to get DJGPP to compile itself, with the patched source tree from above... So we install the following packages into, say, **./djgpp/**: * {{ :blog:bnu2951b.zip |}} * {{ :blog:bsn135b.zip |}} * {{ :blog:dif272b.zip |}} * {{ :blog:flx254b.zip |}} * {{ :blog:gcc2952b.zip |}} * {{ :blog:gpp2952b.zip |}} * {{ :blog:gwk306b.zip |}} * {{ :blog:mak3791b.zip |}} * {{ :blog:pat253b.zip |}} * {{ :blog:rdln22b.zip |}} * {{ :blog:sed3028b.zip |}} * {{ :blog:tar112ab.zip |}} * {{ :blog:txi42b.zip |}} * {{ :blog:txt20b.zip |}} Startup Dosbox, set your PATH variable to the directory where the above packages are installed to and change to the DJGPP source tree that you patched, and run **make all**, for example: C:\ C:\ SET PATH=%PATH%;C:\DJGPP\BIN C:\ SET DJGPP=C:\DJGPP\DJGPP.ENV C:\ C:\ CD C:\DJSRC\SRC C:\DJSRC\SRC\ C:\ MAKE ALL gcc ... -c file1.c ... gcc ... -c file2.c ... gcc ... -c file3.c ... ... ... ... You then wait. //And wait. And wait some more.// Seriously, even at 100% cycles, this takes a long, long time. I/O on Dosbox isn't it's strongpoint, and this is a fairly worst-case scenario. As long as the compile process starts, and your Dosbox session has enough RAM (I set it to 64MB), it should be okay. Just leave it for a a good half hour and check on it later. One more issue that raises its head at this point is that several of the DJGPP source files are named longer than the traditional 8+3 characters allowed in DOS... (this appears to be mainly relate to src/libm/math/*.c), so the associate Makefile for that subirectory needs to be modified to refer to the truncated names. === DJGPP 2.03 End Step === So you end up with a built tree of DJGPP source code, and a magic **libc.a** containing those patched PC-98 specific functions. Simply copy over the libc.a to **./djgpp/lib/** and remember that any code you compile and link to libc.a on this setup will be for a __PC-98__ target, //not// standard __PC__. The archive below contains everything, already setup and ready to go: * {{ :blog:djgpp_203_pc98.zip |Download DJGPP v2.03 and GCC 2.95.2}} development environment for PC-98 MS-DOS / PC MS-DOS The modified system calls that the patch creates are detailed ./djgpp/include/libc/pc9800.h, a direct link to that file is included below: /* * PC9800.H : djgpp v2.03 libc for PC-AT/PC-9800 PATCH Rel.0 * * Copyright (C) 1997-2000 takas / tantan / Wataru Kaneko / Naoki Takano */ #ifndef __PC9800_H_ #define __PC9800_H_ #include #include #define PCAT 0x00 /* IBM PC-AT and Compatible */ #define DOSV 0x01 /* IBM PC-AT and Compatible with DOS/V */ #define PC98 0x10 /* NEC PC-9800 Series */ #define PC98H 0x11 /* NEC PC-H98 Series - No Support */ #define ISPCAT(mtype) ((mtype & 0xf0) == PCAT) #define ISPC98(mtype) ((mtype & 0xf0) == PC98) #define PC98_KEYS 0x6C #define TXA_98AT(txa) conv_98at_txatbl[(unsigned char)(txa)] /* at to 98 */ #define TXA_AT98(txa) conv_at98_txatbl[(unsigned char)(txa)] /* 98 to at */ /* src/libc/bios/b_time.c */ unsigned _bios_timeofday_at(unsigned _cmd, unsigned long *_timeval); unsigned _bios_timeofday_98(unsigned _cmd, unsigned long *_timeval); /* src/libc/bios/bioscom.c */ int bioscom_at(int cmd, char data, int port); int bioscom_98(int cmd, char data, int port); unsigned char conv_98at_com_mode(int data); int conv_at98_com_status(int cx); /* src/libc/bios/biosdisk.c */ int biosdisk_at(int _cmd, int _drive, int _head, int _track, int _sector, int _nsects, void *_buffer); int biosdisk_98(int _cmd, int _drive, int _head, int _track, int _sector, int _nsects, void *_buffer); unsigned _bios_disk_at(unsigned _cmd, struct _diskinfo_t *_di); unsigned _bios_disk_98(unsigned _cmd, struct _diskinfo_t *_di); /* src/libc/bios/biosequi.c */ int biosequip_at(void); int biosequip_98(void); /* src/libc/bios/bioskey.c */ int bioskey_at(int cmd); int bioskey_98(int cmd); int conv_at98_bioskey(unsigned char ah); /* src/libc/bios/biosmem.c */ int biosmemory_at(void); int biosmemory_98(void); /* src/libc/bios/biosprin.c */ int biosprint_at(int _cmd, int _byte, int _port); int biosprint_98(int _cmd, int _byte, int _port); /* src/libc/bios/biostime.c */ long biostime_at(int _cmd, long _newtime); long biostime_98(int _cmd, long _newtime); /* src/libc/crt0/crt1.c */ extern int __crt0_mtype; /* src/libc/dos/delay.c */ void delay_at(unsigned msec); void delay_98(unsigned msec); /* src/libc/pc_hw/co80/conio.c */ int puttext_at(int c, int r, int c2, int r2, void *buf); int puttext_98(int c, int r, int c2, int r2, void *buf); int gettext_at(int c, int r, int c2, int r2, void *buf); int gettext_98(int c, int r, int c2, int r2, void *buf); void textmode_at(int mode); void textmode_98(int mode); void _setcursortype_at(int type); void _setcursortype_98(int type); void fillrow(int row, int left, int right, int fill); void clrscr_at(void); void clrscr_98(void); void insline_at(void); void insline_98(void); void insline_dv(void); void delline_at(void); void delline_98(void); void delline_dv(void); int cputs_at(const char *s); int cputs_98(const char *s); int cputs_dv(const char *s); void _set_screen_lines_at(int nlines); void _set_screen_lines_98(int nlines); void blinkvideo_at(void); void blinkvideo_98(void); void intensevideo_at(void); void intensevideo_98(void); void UpdateDOSV(int row, int col, int chars); /* src/libc/pc_hw/co80/scattrib.c */ extern unsigned char conv_98at_txatbl[]; /* at to 98 */ extern unsigned char conv_at98_txatbl[]; /* 98 to at */ /* src/libc/pc_hw/co80/scclear.c */ void ScreenClear_at(void); void ScreenClear_98(void); /* src/libc/pc_hw/co80/sccols.c */ int ScreenCols_at(void); int ScreenCols_98(void); /* src/libc/pc_hw/co80/scgetc.c */ void ScreenGetCursor_at(int *_row, int *_col); void ScreenGetCursor_98(int *_row, int *_col); /* src/libc/pc_hw/co80/scgetch.c */ void ScreenGetChar_at(int *_ch, int *_attr, int _x, int _y); void ScreenGetChar_98(int *_ch, int *_attr, int _x, int _y); /* src/libc/pc_hw/co80/scmode.c */ int ScreenMode_at(void); int ScreenMode_98(void); /* src/libc/pc_hw/co80/scputc.c */ void ScreenPutChar_at(int _ch, int _attr, int _x, int _y); void ScreenPutChar_98(int _ch, int _attr, int _x, int _y); /* src/libc/pc_hw/co80/scputs.c */ void ScreenPutString_at(const char *_ch, int _attr, int _x, int _y); void ScreenPutString_98(const char *_ch, int _attr, int _x, int _y); /* src/libc/pc_hw/co80/scretr.c */ void ScreenRetrieve_at(void *_virtual_screen); void ScreenRetrieve_98(void *_virtual_screen); /* src/libc/pc_hw/co80/scrows.c */ int ScreenRows_at(void); int ScreenRows_98(void); /* src/libc/pc_hw/co80/scsetc.c */ void ScreenSetCursor_at(int _row, int _col); void ScreenSetCursor_98(int _row, int _col); /* src/libc/pc_hw/co80/scupdate.c */ void ScreenUpdate_at(const void *_virtual_screen); void ScreenUpdate_98(const void *_virtual_screen); /* src/libc/pc_hw/co80/scupdl.c */ void ScreenUpdateLine_at(const void *_virtual_screen_line, int _row); void ScreenUpdateLine_98(const void *_virtual_screen_line, int _row); /* src/libc/pc_hw/co80/scvbell.c */ void ScreenVisualBell_at(void); void ScreenVisualBell_98(void); /* src/libc/pc_hw/kb/getkey.c */ int getkey_at(void); int getkey_98(void); int conv_at98_pchwkey(unsigned char ah, unsigned char xflag); /* src/libc/pc_hw/kb/getxkey.c */ int getxkey_at(void); int getxkey_98(void); /* src/libc/pc_hw/kb/kbhit.c */ int kbhit_at(void); int kbhit_98(void); /* src/libc/pc_hw/mono/mono.c */ void _mono_clear_at(void); void _mono_clear_98(void); void _mono_putc_at(int ch); void _mono_putc_98(int ch); /* src/libc/pc_hw/sound/sound.c */ void sound_at(int freq); void sound_98(int freq); /* src/libc/pc_hw/clock/clock.c */ clock_t clock_at(void); clock_t clock_98(void); /* src/libc/pc_hw/clock/uclock.c */ uclock_t uclock_at(void); uclock_t uclock_98(void); #endif === DJGPP 2.05 (and later === There isn't a useable version of DJGPP greater than 2.03 at this time, but there are several ongoing projects that are trying to get one working: * https://github.com/PC-98/djlsr - DJGPP 2.05 libc updates * https://github.com/lpproj/djgpp-cvs.nec/commit/1fb73a6e4689e44e3ebb62fad4dc1896b5ed1d3b === Supporting Toolset === Most of the standard GNU tools (binutils, diffutils, gawk, sed, make, bison, patch etc.) are already included in the v2.03 development environment archive above, so should not be needed. However, they can all be downloaded seperately from: * ftp://ftp.delorie.com/pub/djgpp/deleted/v2gnu/ * ftp://ftp.delorie.com/pub/djgpp/deleted/v2apps/ * ftp://ftp.delorie.com/pub/djgpp/deleted/v2misc/ Be sure not to use any of the versions found //outside// the //deleted// sub-folder, as these will almost certainly be too new to work with the version of libc and other supporting libraries included in the environment targetting the PC-98. ---- ===== DJGPP libc Reference Manual ===== DJGPP aims to be be as compatible as possible with mainstream libc functionality (i.e ANSI C), but it's a DOS target at the end of the day, so some things will be different. Here's the full libc reference manual: * {{ :blog:djgpp-libc-docs.zip |libc reference}} (HTML) * {{ :blog:djgpp-libc.ps |}} (Postscript) * {{ :blog:djgpp-libc.pdf |}} (PDF version of above) ===== Makefiles & Compiling ===== A sample makefile is included below: # Names of the compiler and friends APP_BASE = C:\DJGPP AS = $(APP_BASE)\as.exe CC = $(APP_BASE)\gcc.exe LD = $(APP_BASE)\ld.exe OBJCOPY = $(APP_BASE)\objcopy.exe STRIP = $(APP_BASE)\strip.exe # libraries and paths LIBS = INCLUDES = # Compiler flags ASM_FLAGS = -mcpu=i486 -march=i486 LDFLAGS = CFLAGS = -mcpu=i486 -march=i486 -O3 LDSCRIPT = OCFLAGS = # What our application is named TARGET = sample.exe all: $(TARGET) OBJFILES = sample.o $(TARGET): $(OBJFILES) gcc.exe $(LDFLAGS) $(OBJFILES) $(LIBS) -o $(TARGET) strip.exe $(TARGET) sample.o: sample.c gcc.exe $(CFLAGS) -c sample.c -o sample.o clean: del $(OBJFILES) del $(TARGET) You also need to distribute with your application the DJGPP go32-v2.exe DOS extender, and a DPMI server, of which for the PC-9821 there are relatively few. Apart from the fact that [[https://virtuallyfun.com/wordpress/2016/06/21/ms-dos-5-0-dpmi/|there is one distributed along with PC-98 MS-DOS 5.00]]//!!!// * go32-v2.exe & MS-DOS 5.00 dpmi32.exe * {{ :blog:go32_dpmi.zip|go32-v2.exe & MS-DOS 6.22 dpmi.exe}} You can of course optimise for //-mcpu=i586// and //-mcpu=pentium//, but they're a subset of the PC-9821 range. If you want your code to run on PC-9801 to PC-9821, your only real option is //-mcpu=i386//, though you may want to run two passes of your application to produce (for example) a generic 386 binary and one tuned for Pentium. More optimisation options and compile-time flags are available on the [[https://gcc.gnu.org/onlinedocs/gcc-2.95.3/gcc_2.html#SEC31|GCC documentation pages]]. ==== Example Code ==== Here's a very trivial piece of code that uses the new PC98 system calls to identify whether the application is running on PC MS-DOS, or on PC-98xx MS-DOS: #include #include int main(){ int m; m = __crt0_mtype; if(ISPCAT(m)){ printf("Hello, PC world\n"); } if(ISPC98(m)){ printf("Hello, PC98 world\n"); } return 0; } Here's the code compiling and running on Dosbox: {{:blog:pc98_platform_test.png?500|}} ... and the exact same binary running on Neko Project II Kai (PC-98 emulator): {{:blog:pc98_platform_98test.png?500|}} ===== Troubleshooting on Real Hardware ====== First of all, **DPMI32.exe** (from the PC-98 DOS 5.00 disks) **DPMI.EXE** (from the MS-DOS 6.22 distribution) and **go32-v2.exe** (which we built from the patched sources, above) work on real PC-9821 hardware, just as they do within the NekoProject II Kai emulator, as long as you have a VCPI service running (either EMM386, VEMM486 or similar), the result is the same. So boot with typical memory managers in place: {{:blog:img20200814120730.jpg?500|}} ... and then try to start DPMI server and the DJGPP go32 DOS extender: {{:blog:img20200814115811.jpg?500|}} So, we have the same behaviour as on the emulated PC-98 system, and the emulated IBM PC system //However//, I struggle to get **make.exe** to actually run a Makefile. No matter what I try to do, make just will not detect the presence of a makefile in the current directory. Given a directory of files like this: {{:blog:img20200814120842.jpg?500|}} ... a simple 'make' command should find MAKEFILE, right?: {{:blog:img20200814120855.jpg?500|}} //Noooo!// Even explicitly specifing the file, like: make.exe -f MAKEFILE doesn't seem to work, because if I go the belts-and-braces approach and rename MAKEFILE to something sane in the DOS world like //MAKEFILE.TXT//, and then capture the output: {{:blog:img20200814121409.jpg?500|}} You can see that make never actually finds the file. There's definitely something strange going on with the way the make.exe is interacting with the files on the PC-98 filesystem is being accessed, compared to the same make.exe accessing files within Dosbox. It's not the entire GNU toolchain, because calling gcc.exe and other commands manually works: {{:blog:img20200814122848.jpg?500|}} But //not a single variation of the make incantation// will make it find and run the makefile: {{:blog:img20200814124159.jpg?500|}} Argh!!!! //What is going on?// Disaster, as I've now found that the same stuff works fine within the emulator: {{:blog:pc98_make_emulated.png?500|}} ==== The Real Problem Is... ==== So, I figured out that there's something broken in the DJGPP build of make **3.71**, if instead you use make **4.3**, it works perfectly, both in the emulator and on a FAT32 filesystem on the real hardware: {{:blog:img20200814165108.jpg?500|}} The upside is that nothing else needs changing, just the single **make.exe** binary. Here's an updated version of the GCC + DJGPP development environment, included the updated version of make: * {{ :blog:pc-9801_gcc-v2.95.2_djgpp-v2.03.zip |}} Note that this version also includes renamed libreadline.a (libread.a), libstdc++ (libc++.a) and libhistory (libhist.a) libraries, as the defaults supplied with DJGPP are over the 8+3 filename length of non-Windows 95 systems. ===== Next Steps ===== Go forth, create code for another obsolete computer platform, and be merry! Or, alternatively, [[:blog:pc98_devcode|have a look at my set of real-world programming examples for the NEC PC-9821 and its graphics hardware]].