{"slug": "llama-2-llm-on-dos-2025", "title": "Llama 2 LLM on DOS (2025)", "summary": "A developer has ported Meta's Llama 2 large language model to run locally on vintage DOS machines, including a 1996 Toshiba Satellite with a 200 MHz Pentium MMX processor. The project, called DOS Llama 2, is open-sourced on GitHub and demonstrates that LLM inference is possible on extremely resource-constrained hardware without GPUs.", "body_md": "Ever thought of running a local Large Language Model (LLM) on a vintage PC running DOS? Now you can!\n\n## Demos\n\nThis video shows DOS Llama 2 LLM client running on 2 vintage DOS machines.\n\nRunning on Thinkpad T42 (2004) and Toshiba Satellite 315CDT (1996). Their CPUs are Pentium M 735 1.7 Ghz and Pentium MMX 200Mhz respectively.\n\nRunning on FreeDOS 1.4 on Thinkpad X13 Gen 1 (2020) with Core i5-10310U 1.7Ghz.\n\nEverything is open sourced with executable available here: [https://github.com/yeokm1/dosllam2](https://github.com/yeokm1/dosllam2)\n\n## Background\n\n2 years ago I wrote a [DOS ChatGPT client](/2023/03/building-a-dos-chatgpt-client-in-2023/) and many retrocomputing enthusiasts in the community wrote similar clients for other vintage platforms. They all had a similar issue, dependency on some remote service. What about running things locally?\n\nConventional wisdom states that running LLMs locally will require computers with high performance specifications especially GPUs with lots of VRAM. But is this actually true?\n\nIn 2023, famous machine learning expert [Andrej Karpathy](https://en.wikipedia.org/wiki/Andrej_Karpathy) released his open-source [llama2.c](https://github.com/karpathy/llama2.c) project which allows inferencing of Meta’s Llama 2 models with just a single C-file. Other developers then ported his project to run on many platforms including some vintage ones like [Windows 98](https://blog.exolabs.net/day-4/) and\n[Powerbook G4](https://www.macrumors.com/2025/03/24/powerbook-g4-generative-ai/). Someone even [ported it to run on a microcontroller](https://github.com/maxbbraun/llama4micro).\n\nThis then inspired me to think, could the same thing be done for an even older platform like DOS?\n\nBased on the demo video and screenshots, it is obvious I managed to do this so this blog post will show how I did it.\n\n## Technical details\n\n### The original code\n\nllama2.c is written in a single C-file designed to inference only fp32 models of the Llama-2 architecture.\n\nThis repo still cares about efficiency, but not at the cost of simplicity, readability or portability.\n\n[https://github.com/karpathy/llama2.c]\n\nFor ease of testing of smaller models, Karpathy trained several small models on the TinyStories dataset that only have sizes of 260K, 15M, 42M and 110M. This is to enable some basic level of LLM functionality on resource-constraint systems.\n\nllama2.c although written for portability in mind, still has some challenges when it comes to making the codebase work for vintage systems.\n\n### Compilation and DOS Extender\n\nThe compiler I selected is [Open Watcom v2 (OWC)](https://open-watcom.github.io/). I used this as I was familiar with it having used it for my DOS ChatGPT client as well.\n\nOWC provides a GUI IDE but I prefer to use a Makefile to keep the project relatively lightweight and easier to manage.\n\nThis is the OWC Makefile I used to compile my project:\n\n```\nTARGET = dosllam2\nOBJS = dosllam2.obj\nCFLAGS = -za99\nLDFLAGS = SYSTEM dos32a NAME $(TARGET)\n\nall: clean $(TARGET).exe\n\n$(TARGET).obj: dosllam2.c\n\twcc386 $(CFLAGS) dosllam2.c\n\n$(TARGET).exe: $(OBJS)\n\twlink $(LDFLAGS) FILE $(OBJS)\n```\n\nThe compiler toolchain uses `wcc386`\n\nand `wlink`\n\n. I used `-za99`\n\nto enable C99 support otherwise it will revert to C90.\n\nSpecial attention is given to the `dos32a`\n\nflag. For most DOS programs, you generally can compile for 2 typical modes:\n\n- Traditional 16-bit architecture compatible with the Intel 8088 used in the first IBM PC\n- 32-bit DOS extender support for Intel 386 CPUs or newer\n\nSince the llama2.c codebase is written for modern systems of at least 32-bit, I must go for the latter option. The memory requirement even for the tiniest of LLMs demands at least a 32-bit system too.\n\nOne common 32-bit DOS extender used in the DOS-era is [DOS/4G](https://en.wikipedia.org/wiki/DOS/4G). This extender allows programs to access extended memory above the 640KB conventional memory and Upper Memory Area by switching the CPU to 32-bit protected mode. One very common use case is that of games which required gobs of memory. Famous examples using extenders are Doom and Descent.\n\nOWC supports DOS/4G as a choice of extender as well as another newer and open-source [DOS/32](https://en.wikipedia.org/wiki/DOS/32) by Narech Koumar released in 2006.\n\nThis extender can be embedded into your program as a runtime or run separately. Either way, most DOS-extenders will show a startup banner like the above.\n\n### Porting efforts\n\nThe original llama2.c codebase still assumes a reasonably modern-featured C compiler. Although the OWC project is still being maintained, there was a bit of porting effort required for me to get the code to compile.\n\n#### Floating point operations\n\nThe inferencing requires heavy use of floating point operations which OWC does not support well.\n\n```\n#define sqrtf(x) ((float)sqrt((double)(x)))\n#define powf(x, y) ((float)pow((double)(x), (double)(y)))\n#define cosf(x) ((float)cos((double)(x)))\n#define sinf(x) ((float)sin((double)(x)))\n#define expf(x)  ((float)exp((double)(x)))\n```\n\nSo I had to define some macros using existing `double`\n\nfunctions.\n\n#### Memory map\n\nModern compilers support the `mmap`\n\nor similar functions which allow files in storage to be mapped into memory. The contents of the file are not all read into memory at once but are lazy-loaded i.e only necessary portions are read when required helping to speed up the use of especially large files.\n\nOWC does not support this thus I have to replace all memory-mapping calls to load the entire LLM file into memory. This has the effect of increasing initial load times.\n\nI did consider writing my own version of `mmap`\n\n. However given the LLM use case where pretty much most (if not all) the weights will be used eventually, it is just postponing loading the entire file. So far easier to just keep things simple and load the entire file all at once initially.\n\n#### Timing\n\nTo measure inference speed, the program requires use of timing APIs:\n\n```\nlong time_in_ms() {\n\tstruct timespec time;\n\tclock_gettime(CLOCK_REALTIME, &time);\n\treturn time.tv_sec * 1000 + time.tv_nsec / 1000000;\n}\n```\n\nThose APIs are not available thus I have to change to something els.\n\n```\nlong time_in_ms() {\n\treturn (clock() * 1000L) / CLOCKS_PER_SEC;\n}\n```\n\nOWC provides the `clock()`\n\nwhich counts processor tick time. Seconds can be obtained by dividing by the internal macro `CLOCKS_PER_SEC`\n\n.\n\n### Filename length limits\n\nDOS has a [8.3 filename](https://en.wikipedia.org/wiki/8.3_filename) limit. This also impacts the C code used to read the files.\n\n```\n// Previous\nchar *tokenizer_path = \"tokenizer.bin\";\n\n//New\nchar *tokenizer_path = \"tokenize.bin\";\n```\n\nThe original program loads a tokenizer file containing a hard-coded path which does not adhere to the 8.3 rule. Although the solution to shorten the filename by one character is simple, it caused me some troubleshooting time initially as I wondered why this loading did not work on my vintage system.\n\n## Benchmarks\n\nI did some benchmarks using PCs of 3-decades ago all the way to modern systems.\n\nData is in tokens per second. All the operating systems are in MS-DOS 6.22 with no memory manager loaded.\n\n| System | CPU | RAM | 260K | 15M | 42M | 110M |\n|---|---|---|---|---|---|---|\n| Generic 486 system (1990s) | 486 DX-2 66Mhz | 32MB EDO | 2.08 | - | - | - |\n| Toshiba Satellite 315CDT (1996) | Pentium MMX 200Mhz | 96MB EDO | 15.32 | 0.43 | - | - |\n| Tweener PC (2001) | Pentium III 667Mhz | 512MB SDRAM | 80.04 | 2.35 | 0.89 | - |\n| ThinkPad T42 (2004) | Pentium M 735 1.7 Ghz | 2GB DDR | 331.6 | 11.55 | 4.42 | 1.71 |\n| ThinkPad X61 (2007) | Core 2 Duo T7300 2.00Ghz | 4GB DDR2 | 171.89 | 5.26 | 2.00 | 1.00 |\n| ThinkPad X301 (2008) | Core 2 Duo SU9400 1.4 GHz | 8GB DDR3 | 171.95 | 5.30 | 1.98 | 0.77 |\n| ThinkPad X13G1 (2020) | Core i5-10310U 1.7Ghz | 16GB DDR4 | 386.36 | 10.51 | 3.89 | 1.53 |\n| Modern Desktop (2024) | Ryzen 5 7600 3.8 Ghz | 128GB DDR5 | 927.27 | 15.95 | - | - |\n\nAs expected, the more modern the system, the faster the inference speed.\n\nAn interesting observation is that my desktop with a modern Ryzen CPU and 128GB RAM cannot load the larger models due to memory allocation error. Perhaps some kind of overflow/bug in the DOS extender or protected mode implementation of the AMD CPU/motherboard.\n\nThe performance of the Thinkpad T42 beats out some newer laptops and is similar to my Thinkpad X13G1 which is 16 years newer.\n\nMore details about my vintage PCs in that table can be found [here](https://github.com/yeokm1/retro-configs).\n\n## Conclusion\n\nThe porting effort I made, testing and documenting this is a simple weekend job. Most of the efforts have to be attributed to the original developer of this project Andrej Karpathy without which this would not have been possible.\n\nStill, I’m amazed what can still be accomplished with vintage systems.", "url": "https://wpnews.pro/news/llama-2-llm-on-dos-2025", "canonical_source": "https://yeokhengmeng.com/2025/04/llama2-llm-on-dos/", "published_at": "2026-07-09 03:31:33+00:00", "updated_at": "2026-07-09 03:42:30.489110+00:00", "lang": "en", "topics": ["large-language-models", "artificial-intelligence", "ai-research"], "entities": ["Meta", "Llama 2", "Andrej Karpathy", "llama2.c", "Open Watcom", "DOS/4G", "Thinkpad T42", "Toshiba Satellite 315CDT"], "alternates": {"html": "https://wpnews.pro/news/llama-2-llm-on-dos-2025", "markdown": "https://wpnews.pro/news/llama-2-llm-on-dos-2025.md", "text": "https://wpnews.pro/news/llama-2-llm-on-dos-2025.txt", "jsonld": "https://wpnews.pro/news/llama-2-llm-on-dos-2025.jsonld"}}