{"id":10559,"date":"2025-05-07T03:00:00","date_gmt":"2025-05-07T07:00:00","guid":{"rendered":"https:\/\/www.both.org\/?p=10559"},"modified":"2025-05-04T12:56:39","modified_gmt":"2025-05-04T16:56:39","slug":"code-like-its-the-1980s","status":"publish","type":"post","link":"https:\/\/www.both.org\/?p=10559","title":{"rendered":"Code like it\u2019s the 1980s"},"content":{"rendered":"<div class=\"pld-like-dislike-wrap pld-template-1\">\r\n    <div class=\"pld-like-wrap  pld-common-wrap\">\r\n    <a href=\"javascript:void(0)\" class=\"pld-like-trigger pld-like-dislike-trigger  \" title=\"\" data-post-id=\"10559\" data-trigger-type=\"like\" data-restriction=\"cookie\" data-already-liked=\"0\">\r\n                        <i class=\"fas fa-thumbs-up\"><\/i>\r\n                <\/a>\r\n    <span class=\"pld-like-count-wrap pld-count-wrap\">4    <\/span>\r\n<\/div><\/div>\n<p>Our computers are so powerful today that it\u2019s hard for younger developers to understand what early desktop computing was like. Even more experienced folks can forget what it was like to boot an early desktop PC and run applications from floppy disks.<\/p>\n\n\n\n<p>My computer has gobs of memory (32 GB), CPU cycles to spare (4-core Intel CPU at 3.1 GHz), and more disk space than I can use (256 GB NVME). This is an \u201caverage\u201d computer in 2025. But in the 1980s, we measured CPU speeds in single-digit <strong>megaHertz<\/strong> and memory in <strong>kilobytes<\/strong>. Hard disks were rare and expensive in the early 1980s, yet so small compared to today: a 5 <strong>megabyte<\/strong> drive was quite large back then.<\/p>\n\n\n\n<p>Let\u2019s take a step back in time to see what it was like to use a computer like our primitive forebears might have done.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"from-small-beginnings\">From small beginnings<\/h2>\n\n\n\n<p>For my experiment, I wanted to see how small a system could be, which meant using the fewest resources possible. I started by defining a 5 MB disk image in QEMU. By today\u2019s standards, 5 MB is nothing; in the 1980s, in the era of the 360 <strong>kilobyte<\/strong> floppy disk, a 5 MB hard drive was huge.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ qemu-img create -f qcow2 small.qcow2 5M<\/code><\/pre>\n\n\n\n<p>To do my experiment, I needed only a simple editor and a C compiler. <a href=\"https:\/\/www.freedos.org\/\">FreeDOS 1.4<\/a> includes a bunch of modern tools, including a variety of editors and compilers. For example, the Open Watcom C compiler is quite nice, and I use that to write DOS programs. FreeDOS 1.4 also includes the IA-16 version of the GNU C Compiler, which is great if you want to have the same tool for DOS that you use on Linux. But both Open Watcom and GCC are quite large, so neither is suitable for my experiment on a tiny system.<\/p>\n\n\n\n<p>FreeDOS also includes BCC (\u201cBruce\u2019s C Compiler\u201d), a very early C compiler that basically <em>translates<\/em> C code into Assembly language, then uses an Assembler to create DOS \u201cCOM\u201d programs. BCC is quite small, only a few megabytes. That\u2019s the one I used.<\/p>\n\n\n\n<p>To install a minimum FreeDOS system, with a simple editor plus BCC, I needed to do a neat \u201ctrick\u201d with QEMU. The FreeDOS 1.4 Live CD image is bootable, but BCC is provided on the Bonus CD. To access the Bonus CD, I need to load CD drivers, which I don\u2019t plan to install on my tiny system. Instead, I started QEMU with two CD-ROM drives, plus my hard drive image:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ qemu-system-i386 -enable-kvm -drive bus=0,unit=0,media=disk,file=small.qcow2 -drive bus=1,unit=0,media=cdrom,file=FD14LIVE.iso -drive bus=1,unit=1,media=cdrom,file=FD14BNS.iso -boot order=d<\/code><\/pre>\n\n\n\n<p>That\u2019s a very long command line, but each <code>-drive<\/code> option tells QEMU to add a new drive to the virtual machine. QEMU emulates an IDE-based system, which can attach drives on bus 0 or 1, and each bus can have unit 0 or 1. So I connected my hard drive on bus 0 unit 0, and my two CD-ROM images on bus 1 (the Live CD on unit 0, and the Bonus CD on unit 1). This allows me to boot the FreeDOS 1.4 Live CD as <code>D<\/code> and still be able to access the packages on the Bonus CD as the <code>E<\/code> drive.<\/p>\n\n\n\n<p>After that, it was a simple matter to use <strong>fdisk<\/strong> to partition the disk, reboot, <strong>format<\/strong> to create a DOS filesystem, and <strong>sys<\/strong> to transfer the system files.<\/p>\n\n\n\n<p>The rest of the setup requires copying over files from the installation media to the new test disk. First, I copied the Edlin editor, which is a line-based editor for DOS, similar to the <strong>ed<\/strong> editor on Linux and Unix.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>D:\\&gt;copy freedos\\bin\\edlin.exe C:\nfreedos\\bin\\edlin.exe =&gt;&gt; C:edlin.exe<\/code><\/pre>\n\n\n\n<p>Installing BCC requires extracting the zip package from the Bonus CD. FreeDOS recognized the second CD-ROM drive (that\u2019s bus 1, unit 1) as the <code>E<\/code> drive, and that\u2019s the Bonus CD. The packages for the FreeDOS installer are stored under a <code>packages<\/code> directory, and then a subdirectory for each package group; for example, the development packages are stored under <code>packages\\devel<\/code>. FreeDOS packages are just zip files with extra metadata, which makes it easy to use the <strong>unzip<\/strong> command to extract BCC to the hard disk:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>D:\\&gt;unzip E:\\packages\\devel\\bcc.zip -d C:\\<\/code><\/pre>\n\n\n\n<p>This includes some extra files that I don\u2019t need for this experiment: the metadata was extracted to an <code>appinfo<\/code> directory, and the source code was installed in a <code>source<\/code> directory. But BCC is so small that there\u2019s no point in removing it. On a 5 MB disk, extracting the entire BCC package requires a little over 2 MB, which you can see if you run the <strong>dir<\/strong> command on the drive:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>D:\\&gt;dir C:\n Volume in drive C is TEST\n Volume Serial Number is 1A3A-1F17\n\n Directory of C:\\\n\nAPPINFO              &lt;DIR&gt;  05\/03\/2025 11:53p\nDEVEL                &lt;DIR&gt;  05\/03\/2025 11:53p\nSOURCE               &lt;DIR&gt;  05\/03\/2025 11:54p\nCOMMAND  COM        87,772  04\/02\/2025 10:22a\nEDLIN    EXE        31,492  05\/30\/2024  6:33a\nKERNEL   SYS        46,256  04\/02\/2025 10:22a\n         3 file(s)        165,520 bytes\n         3 dir(s)       2,809,856 bytes free<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"booting-the-tiny-system\">Booting the tiny system<\/h2>\n\n\n\n<p>With the new disk set up, I could boot this tiny system for the first time. I don\u2019t need a lot of memory to go with this DOS machine. By default, DOS can only use the first 640 kB of memory; after that, you\u2019re using either <em>expanded<\/em> memory or <em>extended<\/em> memory, depending on what driver you load. Since I didn\u2019t install any <em>drivers<\/em>, this tiny system can only manage the <em>conventional<\/em> memory. That means 1 MB of memory using an old-style \u201cISA\u201d PC will be enough for my needs.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ qemu-system-i386 -machine isapc -m 1 -hda small.qcow2 <\/code><\/pre>\n\n\n\n<p>When you boot the system for the first time, you\u2019ll be prompted for the date and time. That\u2019s because of how DOS boots: when the kernel starts up, it looks for a file called <code>CONFIG.SYS<\/code> that contains configuration information, such as where to find the command shell. If the kernel can\u2019t find this file, it assumes certain defaults then loads the shell. When <strong>command.com<\/strong> starts up, it looks for a \u201cbatch\u201d file called <code>AUTOEXEC.BAT<\/code> that it can run to set the DOS environment and run any initial commands. If that file doesn\u2019t exist, <strong>command.com<\/strong> instead prompts the user for the date and time.<\/p>\n\n\n\n<p>To get around this, create an initial <code>AUTOEXEC.BAT<\/code> file using the Edlin editor. I only need to set the search path for programs; the Edlin editor is at the \u201croot\u201d directory, and the C compiler programs are in <code>\\devel\\bcc\\bin<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>C:\\&gt;edlin autoexec.bat\nautoexec.bat: New file.\n*a\n : PATH \\\\;\\\\devel\\\\bcc\\\\bin\n : .\n*w\nautoexec.bat: 1 line written\n*q\nReally quit (Y\/N)? y<\/code><\/pre>\n\n\n\n<p>Edlin interprets the backslash to create \u201cescaped\u201d characters, like <code>\\n<\/code> for a literal newline and <code>\\t<\/code> for a tab. To insert a regular backslash, I had to use <code>\\\\<\/code> instead.<\/p>\n\n\n\n<p>With that <code>AUTOEXEC.BAT<\/code> file, whenever I reboot the system, I\u2019ll immediately set the search path, and the shell will put me at a command prompt. You can add other commands to the <code>AUTOEXEC.BAT<\/code> file, such as aliases to other commands. For example, Linux users might be more comfortable typing <strong>ls<\/strong> instead of <strong>dir<\/strong> to list the files in the current directory, or <strong>rm<\/strong> instead of <strong>del<\/strong> to delete files, and <strong>cat<\/strong> instead of <strong>type<\/strong> to display the contents of files. You can use Edlin to modify the <code>AUTOEXEC.BAT<\/code> file by appending (the <code>a<\/code> command) these commands to the file:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>C:\\&gt;edlin autoexec.bat \nautoexec.bat: 1 line read\n*a\n : alias ls=dir \/o:ne \/l \/b \/w\n : alias rm=del\n : alias cat=type\n : .\n*w\nautoexec.bat: 4 lines written\n*q\nReally quit (Y\/N)? y<\/code><\/pre>\n\n\n\n<p>In this example, I\u2019ve added a few options to the <strong>dir<\/strong> command to make the output look like <strong>ls<\/strong> on Linux: <strong>\/o:ne<\/strong> sorts (orders) the file list by name then extension, <strong>\/l<\/strong> displays filenames in lowercase, <strong>\/b<\/strong> creates a \u201cbare\u201d listing without the usual information about the disk size or volume label, and <strong>\/w<\/strong> displays a \u201cwide\u201d listing. If you \u201crun\u201d the <code>AUTOEXEC.BAT<\/code> file, you\u2019ll see each instruction as the shell executes it. This will set the search path and define the macros:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>C:\\&gt;autoexec.bat\nC:\\&gt;PATH \\;\\devel\\bcc\\bin\nC:\\&gt;alias ls=dir \/o:ne \/l \/b \/w\nC:\\&gt;alias rm=del\nC:\\&gt;alias cat=type<\/code><\/pre>\n\n\n\n<p>Now my tiny DOS system \u201cfeels\u201d a bit more like Linux:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>C:\\&gt;ls  \n&#91;appinfo]      autoexec.bak   autoexec.bat   command.com    &#91;devel]\nedlin.exe      kernel.sys     &#91;source]<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"writing-our-first-program\">Writing our first program<\/h2>\n\n\n\n<p>One program that I like to have at hand is an ASCII chart. Traditional ASCII defines only the values from 0 to 127, and the printable values start at ASCII 32 (space). DOS defines an <em>extended<\/em> character set that goes up to 255. Let\u2019s use Edlin to write a simple program to iterate through the printable characters from 32 to 255.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>C:\\&gt;mkdir src\n\nC:\\&gt;cd src\n\nC:\\SRC&gt;edlin ascii.c\nascii.c: New file.\n*i\n : #include &lt;stdio.h&gt;\n : \n : int main()\n : {\n :   int row,col;\n : \n :   for (row=32; row&lt;255; row+=16) {\n :     for (col=0; col&lt;16; col++) {\n :       putchar(row+col);\n :       putchar(' ');\n :     }\n :     putchar('\\\\n');\n :   }\n : \n :   return 0;\n : }\n : .\n*w\nascii.c: 16 lines written\n*q\nReally quit (Y\/N)? y<\/code><\/pre>\n\n\n\n<p>To write the output as a \u201ctable,\u201d I\u2019ll step through \u201crow\u201d values starting at 32, incrementing each row by 16. Within each row, I\u2019ll print 16 characters starting at the initial \u201crow\u201d value.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>C:\\SRC&gt;bcc -o ascii.com ascii.c\n\nC:\\SRC&gt;ascii\n  ! \" # $ % &amp; ' ( ) * + , - . \/\n0 1 2 3 4 5 6 7 8 9 : ; &lt; = &gt; ?\n@ A B C D E F G H I J K L M N O\nP Q R S T U V W X Y Z &#91; \\ ] ^ _\n` a b c d e f g h i j k l m n o\np q r s t u v w x y z { | } ~ \u2302\n\u00c7 \u00fc \u00e9 \u00e2 \u00e4 \u00e0 \u00e5 \u00e7 \u00ea \u00eb \u00e8 \u00ef \u00ee \u00ec \u00c4 \u00c5\n\u00c9 \u00e6 \u00c6 \u00f4 \u00f6 \u00f2 \u00fb \u00f9 \u00ff \u00d6 \u00dc \u00a2 \u00a3 \u00a5 \u20a7 \u0192\n\u00e1 \u00ed \u00f3 \u00fa \u00f1 \u00d1 \u00aa \u00ba \u00bf \u2310 \u00ac \u00bd \u00bc \u00a1 \u00ab \u00bb\n\u2591 \u2592 \u2593 \u2502 \u2524 \u2561 \u2562 \u2556 \u2555 \u2563 \u2551 \u2557 \u255d \u255c \u255b \u2510\n\u2514 \u2534 \u252c \u251c \u2500 \u253c \u255e \u255f \u255a \u2554 \u2569 \u2566 \u2560 \u2550 \u256c \u2567\n\u2568 \u2564 \u2565 \u2559 \u2558 \u2552 \u2553 \u256b \u256a \u2518 \u250c \u2588 \u2584 \u258c \u2590 \u2580\n\u03b1 \u00df \u0393 \u03c0 \u03a3 \u03c3 \u00b5 \u03c4 \u03a6 \u0398 \u03a9 \u03b4 \u221e \u03c6 \u03b5 \u2229\n\u2261 \u00b1 \u2265 \u2264 \u2320 \u2321 \u00f7 \u2248 \u00b0 \u2219 \u00b7 \u221a \u207f \u00b2 \u25a0 &nbsp;<\/code><\/pre>\n\n\n\n<p>As a programmer, it\u2019s more useful to know the ASCII values for each character, usually in hexadecimal notation. Let\u2019s update the program to add starting hex values to each row:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>C:\\SRC&gt;edlin ascii.c\nascii.c: 16 lines read\n*l\n1:*#include &lt;stdio.h&gt;\n2:\n3: int main()\n4: {\n5:   int row,col;\n6:\n7:   for (row=32; row&lt;255; row+=16) {\n8:     for (col=0; col&lt;16; col++) {\n9:       putchar(row+col);\n10:       putchar(' ');\n11:     }\n12:     putchar('\\n');\n13:   }\n14:\n15:   return 0;\n16: }\n*8i\n :     printf(\"%x \", row);\n : .\n*w\nascii.c: 17 lines written<\/code><\/pre>\n\n\n\n<p>And again to add the column headers:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>*6i\n : \n :   fputs(\"-- \", stdout);\n :   for (col=0; col&lt;16; col++) {\n :     printf(\"%x \", col);\n :   }\n :   putchar('\\\\n');\n : .\n*w\nascii.c: 23 lines written\n*q\nReally quit (Y\/N)? y<\/code><\/pre>\n\n\n\n<p>This generates a nice table that makes it easy to reference characters by value:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>C:\\SRC&gt;bcc -o ascii.com ascii.c\n\nC:\\SRC&gt;ascii\n-- 0 1 2 3 4 5 6 7 8 9 a b c d e f\n20   ! \" # $ % &amp; ' ( ) * + , - . \/\n30 0 1 2 3 4 5 6 7 8 9 : ; &lt; = &gt; ?\n40 @ A B C D E F G H I J K L M N O\n50 P Q R S T U V W X Y Z &#91; \\ ] ^ _\n60 ` a b c d e f g h i j k l m n o\n70 p q r s t u v w x y z { | } ~ \u2302\n80 \u00c7 \u00fc \u00e9 \u00e2 \u00e4 \u00e0 \u00e5 \u00e7 \u00ea \u00eb \u00e8 \u00ef \u00ee \u00ec \u00c4 \u00c5\n90 \u00c9 \u00e6 \u00c6 \u00f4 \u00f6 \u00f2 \u00fb \u00f9 \u00ff \u00d6 \u00dc \u00a2 \u00a3 \u00a5 \u20a7 \u0192\na0 \u00e1 \u00ed \u00f3 \u00fa \u00f1 \u00d1 \u00aa \u00ba \u00bf \u2310 \u00ac \u00bd \u00bc \u00a1 \u00ab \u00bb\nb0 \u2591 \u2592 \u2593 \u2502 \u2524 \u2561 \u2562 \u2556 \u2555 \u2563 \u2551 \u2557 \u255d \u255c \u255b \u2510\nc0 \u2514 \u2534 \u252c \u251c \u2500 \u253c \u255e \u255f \u255a \u2554 \u2569 \u2566 \u2560 \u2550 \u256c \u2567\nd0 \u2568 \u2564 \u2565 \u2559 \u2558 \u2552 \u2553 \u256b \u256a \u2518 \u250c \u2588 \u2584 \u258c \u2590 \u2580\ne0 \u03b1 \u00df \u0393 \u03c0 \u03a3 \u03c3 \u00b5 \u03c4 \u03a6 \u0398 \u03a9 \u03b4 \u221e \u03c6 \u03b5 \u2229\nf0 \u2261 \u00b1 \u2265 \u2264 \u2320 \u2321 \u00f7 \u2248 \u00b0 \u2219 \u00b7 \u221a \u207f \u00b2 \u25a0 &nbsp;<\/code><\/pre>\n\n\n\n<p>We can use the extended characters to define a nice-looking border around the table, so the table is easier to read. Let\u2019s use the vertical bar character (0xb3) for the left border, the horizontal bar character (0xc4) for the top border, and the cross bar character (0xc5) for the intersection:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>C:\\SRC&gt;edlin ascii.c\nascii.c: 23 lines read\n*1,10l\n1:*#include &lt;stdio.h&gt;\n2:\n3: int main()\n4: {\n5:   int row,col;\n6:\n7:   fputs(\"-- \", stdout);\n8:   for (col=0; col&lt;16; col++) {\n9:     printf(\"%x \", col);\n10:   }\n*7\n7:*  fputs(\"-- \", stdout);\n7:   fputs(\"--\", stdout);\n*8i      \n :   putchar(0xb3);\n : .\n*5,15l\n5:   int row,col;\n6:\n7:   fputs(\"--\", stdout);\n8:   putchar(0xb3);\n9:*  for (col=0; col&lt;16; col++) {\n10:     printf(\"%x \", col);\n11:   }\n12:   putchar('\\n');\n13:\n14:   for (row=32; row&lt;255; row+=16) {\n15:     printf(\"%x \", row);\n*13i\n : \n :   putchar(0xc4); putchar(0xc5);\n :   for (col=0; col&lt;16; col++) {\n :     putchar(0xc4); putchar(0xc4);\n :   }\n :   putchar('\\\\n');\n : .\n*15,25l\n15:   for (col=0; col&lt;16; col++) {\n16:     putchar(0xc4); putchar(0xc4);\n17:   }\n18:   putchar('\\n');\n19:*\n20:   for (row=32; row&lt;255; row+=16) {\n21:     printf(\"%x \", row);\n22:     for (col=0; col&lt;16; col++) {\n23:       putchar(row+col);\n24:       putchar(' ');\n25:     }\n*21\n21:*    printf(\"%x \", row);\n21:     printf(\"%x\", row); putchar(0xb3);\n*w\nascii.c: 30 lines written\n*q\nReally quit (Y\/N)? y<\/code><\/pre>\n\n\n\n<p>If we compile and run this program, we&#8217;ll see this has a small bug; the program should print two horizontal bar characters (0xc4) for the top border, but it only prints one:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>C:\\SRC>bcc -o ascii.com ascii.c\n\nC:\\SRC>ascii\n--\u25020 1 2 3 4 5 6 7 8 9 a b c d e f\n\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n20\u2502  ! \" # $ % &amp; ' ( ) * + , - . \/\n30\u25020 1 2 3 4 5 6 7 8 9 : ; &lt; = > ?\n40\u2502@ A B C D E F G H I J K L M N O\n50\u2502P Q R S T U V W X Y Z &#91; \\ ] ^ _\n60\u2502` a b c d e f g h i j k l m n o\n70\u2502p q r s t u v w x y z { | } ~ \u2302\n80\u2502\u00c7 \u00fc \u00e9 \u00e2 \u00e4 \u00e0 \u00e5 \u00e7 \u00ea \u00eb \u00e8 \u00ef \u00ee \u00ec \u00c4 \u00c5\n90\u2502\u00c9 \u00e6 \u00c6 \u00f4 \u00f6 \u00f2 \u00fb \u00f9 \u00ff \u00d6 \u00dc \u00a2 \u00a3 \u00a5 \u20a7 \u0192\na0\u2502\u00e1 \u00ed \u00f3 \u00fa \u00f1 \u00d1 \u00aa \u00ba \u00bf \u2310 \u00ac \u00bd \u00bc \u00a1 \u00ab \u00bb\nb0\u2502\u2591 \u2592 \u2593 \u2502 \u2524 \u2561 \u2562 \u2556 \u2555 \u2563 \u2551 \u2557 \u255d \u255c \u255b \u2510\nc0\u2502\u2514 \u2534 \u252c \u251c \u2500 \u253c \u255e \u255f \u255a \u2554 \u2569 \u2566 \u2560 \u2550 \u256c \u2567\nd0\u2502\u2568 \u2564 \u2565 \u2559 \u2558 \u2552 \u2553 \u256b \u256a \u2518 \u250c \u2588 \u2584 \u258c \u2590 \u2580\ne0\u2502\u03b1 \u00df \u0393 \u03c0 \u03a3 \u03c3 \u00b5 \u03c4 \u03a6 \u0398 \u03a9 \u03b4 \u221e \u03c6 \u03b5 \u2229\nf0\u2502\u2261 \u00b1 \u2265 \u2264 \u2320 \u2321 \u00f7 \u2248 \u00b0 \u2219 \u00b7 \u221a \u207f \u00b2 \u25a0 \u00a0<\/code><\/pre>\n\n\n\n<p>Let\u2019s make the final bug fix with Edlin:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>C:\\SRC&gt;edlin ascii.c\nascii.c: 30 lines read\n*10,20l\n10:     printf(\"%x \", col);\n11:   }\n12:   putchar('\\n');\n13:\n14:   putchar(0xc4); putchar(0xc5);\n15:   for (col=0; col&lt;16; col++) {\n16:     putchar(0xc4); putchar(0xc4);\n17:   }\n18:   putchar('\\n');\n19:\n20:   for (row=32; row&lt;255; row+=16) {\n*14\n14:*  putchar(0xc4); putchar(0xc5);\n14:   putchar(0xc4); putchar(0xc4); putchar(0xc5);\n*w\nascii.c: 30 lines written\n*q\nReally quit (Y\/N)? y<\/code><\/pre>\n\n\n\n<p>The new program generates a very nice-looking \u201cextended\u201d ASCII table:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>C:\\SRC&gt;bcc -o ascii.com ascii.c\n\nC:\\SRC&gt;ascii\n--\u25020 1 2 3 4 5 6 7 8 9 a b c d e f\n\u2500\u2500\u253c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n20\u2502  ! \" # $ % &amp; ' ( ) * + , - . \/\n30\u25020 1 2 3 4 5 6 7 8 9 : ; &lt; = &gt; ?\n40\u2502@ A B C D E F G H I J K L M N O\n50\u2502P Q R S T U V W X Y Z &#91; \\ ] ^ _\n60\u2502` a b c d e f g h i j k l m n o\n70\u2502p q r s t u v w x y z { | } ~ \u2302\n80\u2502\u00c7 \u00fc \u00e9 \u00e2 \u00e4 \u00e0 \u00e5 \u00e7 \u00ea \u00eb \u00e8 \u00ef \u00ee \u00ec \u00c4 \u00c5\n90\u2502\u00c9 \u00e6 \u00c6 \u00f4 \u00f6 \u00f2 \u00fb \u00f9 \u00ff \u00d6 \u00dc \u00a2 \u00a3 \u00a5 \u20a7 \u0192\na0\u2502\u00e1 \u00ed \u00f3 \u00fa \u00f1 \u00d1 \u00aa \u00ba \u00bf \u2310 \u00ac \u00bd \u00bc \u00a1 \u00ab \u00bb\nb0\u2502\u2591 \u2592 \u2593 \u2502 \u2524 \u2561 \u2562 \u2556 \u2555 \u2563 \u2551 \u2557 \u255d \u255c \u255b \u2510\nc0\u2502\u2514 \u2534 \u252c \u251c \u2500 \u253c \u255e \u255f \u255a \u2554 \u2569 \u2566 \u2560 \u2550 \u256c \u2567\nd0\u2502\u2568 \u2564 \u2565 \u2559 \u2558 \u2552 \u2553 \u256b \u256a \u2518 \u250c \u2588 \u2584 \u258c \u2590 \u2580\ne0\u2502\u03b1 \u00df \u0393 \u03c0 \u03a3 \u03c3 \u00b5 \u03c4 \u03a6 \u0398 \u03a9 \u03b4 \u221e \u03c6 \u03b5 \u2229\nf0\u2502\u2261 \u00b1 \u2265 \u2264 \u2320 \u2321 \u00f7 \u2248 \u00b0 \u2219 \u00b7 \u221a \u207f \u00b2 \u25a0 &nbsp;<\/code><\/pre>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"720\" height=\"427\" src=\"http:\/\/www.both.org\/wp-content\/uploads\/2025\/05\/qemu-ascii.png\" alt=\"QEMU window displaying an extended ASCII table\" class=\"wp-image-10558\"\/><figcaption class=\"wp-element-caption\">Displaying the &#8220;extended ASCII&#8221; character set<\/figcaption><\/figure>\n\n\n\n<p>Here&#8217;s the full listing, if you want to try this out on your own:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#include &lt;stdio.h>\n\nint main()\n{\n  int row,col;\n\n  fputs(\"--\", stdout);\n  putchar(0xb3);\n  for (col=0; col&lt;16; col++) {\n    printf(\"%x \", col);\n  }\n  putchar('\\n');\n\n  putchar(0xc4); putchar(0xc4); putchar(0xc5);\n  for (col=0; col&lt;16; col++) {\n    putchar(0xc4); putchar(0xc4);\n  }\n  putchar('\\n');\n\n  for (row=32; row&lt;255; row+=16) {\n    printf(\"%x\", row); putchar(0xb3);\n    for (col=0; col&lt;16; col++) {\n      putchar(row+col);\n      putchar(' ');\n    }\n    putchar('\\n');\n  }\n\n  return 0;\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"code-like-its-the-1980s\">Code like it\u2019s the 1980s<\/h2>\n\n\n\n<p>With only an editor and C compiler, you can create your own programs to do things the way <em>you<\/em> want to do them. This is just a simple example to create an ASCII table of the DOS extended character set. From this starting point, you can create your own programs to do all kinds of things.<\/p>\n\n\n\n<p>While BCC is a tiny C compiler, it\u2019s also incomplete. The documentation describes BCC as \u201ca simple C compiler that produces 8086 assembler\u201d and notes that the compiler \u201cunderstands traditional K&amp;R C.\u201d To use ANSI C prototypes, you must add the <code>-ansi<\/code> command line option, although the BCC documentation warns that \u201cit is definitly NOT a true ansi-C compiler.\u201d<\/p>\n\n\n\n<p>At the same time, it\u2019s important to note that Dennis Ritchie created the C programming language around 1972, and C gained popularity throughout the 1980s. The \u201cANSI C\u201d version was established in 1989, so this \u201cincomplete\u201d BCC implementation would have been similar to other C compilers available during the 1980s.<\/p>\n\n\n\n<p>If you\u2019re interested in <em>retrocomputing<\/em> and <em>retroprogramming<\/em>, download the <a href=\"https:\/\/www.freedos.org\/download\/\">FreeDOS 1.4<\/a> distribution and set up your own tiny <em>retro<\/em> environment where you can code like it\u2019s the 1980s.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>4 Our computers are so powerful today that it&rsquo;s hard for younger developers to understand what early desktop<\/p>\n","protected":false},"author":33,"featured_media":3514,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_lmt_disableupdate":"","_lmt_disable":"","footnotes":""},"categories":[340,69,150],"tags":[267,147,152],"class_list":["post-10559","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-freedos","category-fun","category-programming","tag-freedos","tag-fun","tag-programming"],"modified_by":"Jim Hall","_links":{"self":[{"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/10559","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/users\/33"}],"replies":[{"embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=10559"}],"version-history":[{"count":1,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/10559\/revisions"}],"predecessor-version":[{"id":10560,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/posts\/10559\/revisions\/10560"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=\/wp\/v2\/media\/3514"}],"wp:attachment":[{"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=10559"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=10559"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.both.org\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=10559"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}