https://nanochess.org/emulator.html
[logo]
Main page
Intel 8080 emulator
Chess programs
Contests
Store
Retrogaming
FAQ
Links
About me
Ver en espanol
Intel 8080 emulator. 19th IOCCC. Best of Show.
Sections:
* Source code
* How to compile it
* A little course on CP/M
* What is an 8080?
* Porting it
* How it works
* Other notes
* Useful links
After winning the IOCCC for the first time, I had the idea of writing
an emulator of the 8080 processor in 2000 characters of C, after
patterning experimentally the more than 200 instructions and doing
measures of byte count, I realized that it was possible and I made
it. Then I added CP/M support as a extra feature. I was completely
astonished when I won Best of Show of 19th IOCCC :).
This program was one of three winning entries I sent for the 19th
edition, the other two were a graphical chess program and a knight's
tour solver.
Source code
This program emulates a complete Intel(r) 8080 processor, along with a
teletype and a disk controller, just like at the start of the
personal computers revolution (circa 1975).
Here is the source code, written in C language:
#include
#define n(o,p,e)=y=(z=a(e)%16 p x%16 p o,a(e)p x p o),h(
#define s 6[o]
#define p z=l[d(9)]|l[d(9)+1]<<8,1<(9[o]+=2)||++8[o]
#define Q a(7)
#define w 254>(9[o]-=2)||--8[o],l[d(9)]=z,l[1+d(9)]=z>>8
#define O )):((
#define b (y&1?~s:s)>>"\6\0\2\7"[y/2]&1?0:(
#define S )?(z-=
#define a(f)*((7&f)-6?&o[f&7]:&l[d(5)])
#define C S 5 S 3
#define D(E)x/8!=16+E&198+E*8!=x?
#define B(C)fclose((C))
#define q (c+=2,0[c-2]|1[c-2]<<8)
#define m x=64&x?*c++:a(x),
#define A(F)=fopen((F),"rb+")
unsigned char o[10],l[78114],*c=l,*k=l
#define d(e)o[e]+256*o[e-1]
#define h(l)s=l>>8&1|128&y|!(y&255)*64|16&z|2,y^=y>>4,y^=y<<2,y^=~y>>1,s|=y&4
+64506; FILE *u, *v, *e, *V; int x,y,z,Z; main(r,U)char**U;{
{ { { } } } { { { } } } { { { } } } { { { } } }
{ { { } } } { { { } } } { { { } } } { { { } } }
{ { { } } } { { { } } } { { { } } } { { { } } }
{ { { } } } { { { } } } { { { } } } { { { } } }
{ { { } } } { { { } } } { { { } } } { { { } } }
{ { { } } } { { { } } } { { { } } } { { { } } }
{ { ; } } { { { } } } { { ; } } { { { } } }
{ { { } } } { { { } } } { { { } } } { { { } } }
{ { { } } } { { { } } } { { { } } } { { { } } }
{ { { } } } { { { } } } { { { } } } { { { } } }
{ { { } } } { { { } } } { { { } } } { { { } } }
{ { { } } } { { { } } } { { { } } } { { { } } }
{ { { } } } { { { } } } { { { } } } { { { } } }
for(v A((u A((e A((r-2?0:(V A(1[U])),"C")
),system("stty raw -echo min 0"),fread(l,78114,1,e),B(e),"B")),"A")); 118-(x
=*c++); (y=x/8%8,z=(x&199)-4 S 1 S 1 S 186 S 2 S 2 S 3 S 0,r=(y>5)*2+y,z=(x&
207)-1 S 2 S 6 S 2 S 182 S 4)?D(0)D(1)D(2)D(3)D(4)D(5)D(6)D(7)(z=x-2 C C C C
C C C C+129 S 6 S 4 S 6 S 8 S 8 S 6 S 2 S 2 S 12)?x/64-1?((0 O a(y)=a(x) O 9
[o]=a(5),8[o]=a(4) O 237==*c++?((int (*)())(2-*c++?fwrite:fread))(l+*k+1[k]*
256,128,1,(fseek(e=5[k]-1?u:v,((3[k]|4[k]<<8)<<7|2[k])<<7,Q=0),e)):0 O y=a(5
),z=a(4),a(5)=a(3),a(4)=a(2),a(3)=y,a(2)=z O c=l+d(5) O y=l[x=d(9)],z=l[++x]
,x[l]=a(4),l[--x]=a(5),a(5)=y,a(4)=z O 2-*c?Z||read(0,&Z,1),1&*c++?Q=Z,Z=0:(
Q=!!Z):(c++,Q=r=V?fgetc(V):-1,s=s&~1|r<0) O++c,write(1,&7[o],1) O z=c+2-l,w,
c=l+q O p,c=l+z O c=l+q O s^=1 O Q=q[l] O s|=1 O q[l]=Q O Q=~Q O a(5)=l[x=q]
,a(4)=l[++x] O s|=s&16|9159?Q+=96,1:0,y=Q,h(s<<8)
O l[x=q]=a(5),l[++x]=a(4) O x=Q%2,Q=Q/2+s%2*128,s=s&~1|x O Q=l[d(3)]O x=Q /
128,Q=Q*2+s%2,s=s&~1|x O l[d(3)]=Q O s=s&~1|1&Q,Q=Q/2|Q<<7 O Q=l[d(1)]O s=~1
&s|Q>>7,Q=Q*2|Q>>7 O l[d(1)]=Q O m y n(0,-,7)y) O m z=0,y=Q|=x,h(y) O m z=0,
y=Q^=x,h(y) O m z=Q*2|2*x,y=Q&=x,h(y) O m Q n(s%2,-,7)y) O m Q n(0,-,7)y) O
m Q n(s%2,+,7)y) O m Q n(0,+,7)y) O z=r-8?d(r+1):s|Q<<8,w O p,r-8?o[r+1]=z,r
[o]=z>>8:(s=~40&z|2,Q=z>>8) O r[o]--||--o[r-1]O a(5)=z=a(5)+r[o],a(4)=z=a(4)
+o[r-1]+z/256,s=~1&s|z>>8 O ++o[r+1]||r[o]++O o[r+1]=*c++,r[o]=*c++O z=c-l,w
,c=y*8+l O x=q,b z=c-l,w,c=l+x) O x=q,b c=l+x) O b p,c=l+z) O a(y)=*c++O r=y
,x=0,a(r)n(1,-,y)s<<8) O r=y,x=0,a(r)n(1,+,y)s<<8))));
system("stty cooked echo"); B((B((V?B(V):0,u)),v)); }
How to compile it
First, download the source code from here. It requires an *NIX
compatible system (find porting notes below).
To compile use:
cc toledo2.c -o toledo2
The emulator needs an initial memory image to do something usable, so
it will need two files (c_basic.bin and c_bios.bin). Rename
c_basic.bin to C and run the emulator, and et voila! you have the
public domain Palo Alto Tiny BASIC (by Li-Chen Wang), published on
the very first volume of the now extinct Dr. Dobb's Journal magazine.
Type using uppercase letters, here are three example programs, press
Enter after each line:
10 PRINT "Hello, world!"
LIST
RUN
10 FOR A=1 TO 10 10 INPUT A
20 PRINT A 20 INPUT B
30 NEXT A 30 PRINT A+B
LIST LIST
RUN RUN
Press Ctrl+Z to quit, by the way, the segmentation fault is normal at
this point.
All good programmers started learning BASIC, now, what about a CP/M
emulator?
Download the following file (not included because of possible
copyright and blah, blah):
http://www.retroarchive.org/cpm/os/KAYPROII.ZIP
Extract CPM64.COM from the SOURCE directory, and copy it to files
named A and B (these will be the disk drives). Now rename the
provided c_bios.bin to C and run the emulator.
Now you have a running CP/M operating system!, with two files on A:
drive, HALT.COM to stop the emulator (so it closes drives) and
IMPORT.COM to introduce new files.
To get a complete CP/M system, you will need the following files from
the KAYPROII.ZIP, SOURCE directory:
ASM.COM DDT.COM DUMP.COM ED.COM LOAD.COM
PIP.COM STAT.COM SUBMIT.COM XSUB.COM
To import them, you must run the emulator with an argument, by
example:
prog DDT.COM
When the A> prompt appears, do:
IMPORT DDT.COM
When it ends, do HALT, so the file is saved, and you can start the
same process with another file.
The WS33-KPR directory of the KAYPROII.ZIP file also contains a
Wordstar version that works with this emulator. There is also an
interesting game, the classical Adventure by Crowther and Woods in
the ADVENTUR directory, at that time was amazing that a microcomputer
could contain such big game.
At this time I have tested successfully the following software from
www.retroarchive.org:
Languages
http://www.retroarchive.org/cpm/lang/c80v30.zip
http://www.retroarchive.org/cpm/lang/Mbasic.com
Spreadsheet
http://www.retroarchive.org/cpm/business/MULTPLAN.ZIP
Games
http://www.retroarchive.org/cpm/games/zork123_80.zip
Utilities
http://www.retroarchive.org/cpm/cdrom/CPM/UTILS/ARC-LBR/UNARC16.ARK
http://www.retroarchive.org/cpm/cdrom/CPM/UTILS/ARC-LBR/UNARC16.MSG
http://www.retroarchive.org/cpm/cdrom/CPM/UTILS/ARC-LBR/DELBR12.ARK
http://www.retroarchive.org/cpm/cdrom/CPM/UTILS/SQUSQ/USQ-20.COM
http://www.retroarchive.org/cpm/cdrom/CPM/UTILS/SQUSQ/UNCR8080.LBR
http://www.retroarchive.org/cpm/cdrom/CPM/UTILS/DIRUTL/XDIR3-12.LBR
http://www.retroarchive.org/cpm/cdrom/CPM/UTILS/DIRUTL/CU.LBR
http://www.retroarchive.org/cpm/cdrom/CPM/UTILS/FILCPY/SWEEP40.LBR
Some programs require installation to configure the terminal, locate
ANSI or VT-100.
A little course on CP/M
The CP/M user's manuals are available on www.retroarchive.org. But if
you remember how to use MS-DOS then you can use CP/M very easily.
Here is a little reference of the command line:
Internal commands:
A: Change current drive to A
B: Change current drive to B
DIR List files in drive
DIR *.TXT List all files with TXT extension
TYPE FILE.TXT Shows content of FILE.TXT
ERA FILE.TXT Erases file FILE.TXT
USER 1 Change to user 1 (0-15 available)
It is something as subdirectories.
So you can separate your files.
External commands:
STAT Show used/free space on drive
STAT *.* Show file sizes.
DDT PROG.COM Debug PROG.COM.
To quit use Ctrl+C
Dump address 0100 (hex):
D0100
Emulator running Wordstar under CP/M
Emulator running Wordstar under CP/M.
What is an 8080?
It is simply the little brother of the Zilog Z80, it has no extended
registers (AF', BC', DE', HL', IX or IY), no relative jumps, and
every instruction beginning with CB, DD, ED or FD doesn't exist.
The flags are only S (Sign, bit 7), Z (Zero, bit 6), P (Parity, bit
2) and C (Carry, bit 0).
The 8080 processor was created by Federico Faggin and Masatoshi Shima
in 1974, afterwards both would design the Zilog Z80 in 1976, these
two processors were pretty important and influential for the rise of
microcomputers.
Porting it
It is easy if your platform has getch/kbhit and ANSI terminal
read --> Z=kbhit()?getch():0
write --> putchar(7[o])
system --> nothing
Also add the following to trap Ctrl-C:
#include
signal(SIGINT, SIG_IGN);
On PC/DOS you will need to add ANSI.SYS to CONFIG.SYS
In *NIX the min 0 for stty is required, circa 2001 it was not
required.
How it works (SPOILER)
The l array contains the 64K memory, it is initialized with a boot
image loaded from the 'C' file, the program counter is the c pointer,
and registers are on o[]. The main loops reads every op-code and
separates it in one of three common forms, a lot of trinary operators
selects the instruction.
Execution starts at 0000, you can write your own boot or monitor
program, or even your own operating system playing with the 'C' file.
o[0] = B register o[1] = C register
o[2] = D register o[3] = E register
o[4] = H register o[5] = L register
o[6] = Flags o[7] = A or accumulator
The following instructions do peripheral operation:
76 Quits emulator
DB 00 Reads key pressed status
DB 01 Reads key
DB 02 Reads byte from file (Carry=EOF)
D3 xx Writes byte from acc. to console
ED ED 02 Reads sector (128 bytes)
ED ED 03 Writes sector (128 bytes)
Memory addresses:
FBFA = Low source/target address
FBFB - High source/target address
FBFC - Sector (0-127)
FBFD - Cylinder (low byte)
FBFE - Cylinder (high byte)
FBFF - Drive (1/2)
The BIOS is tailor made for this emulator and helps to simplify it.
Though the size of each virtual hard disk drive can reach 1 GB, CP/M
only handles up to 8 MB.
Other notes:
* The 8080 runs at your computer speed divided between a number
that I have not calculated.
* This obfuscated processor was created using obfuscated code
produced by an obfuscated mind, no brains were harmed during its
production, except those that tried to read the code.
* The original version of this code was eaten by my C++ dog.
* I intended to make it simple to understand, it uses only four C
keywords.
* Also I discovered that braces are very useful for commenting.
* Why to bother with prototypes?, every good C programmer can
develop its C programs using only one function.
And now it is 2024
This emulator was developed eighteen years ago when the computers had
32-bit processors and it used a hole in the C language syntax where
you could pass a pointer on an integer. In fact, this is the IOCCC
objective: make C compilers to do things these shouldn't be supposed
to do.
However, the C compilers for 64-bit processors don't allow it anymore
as pointers are 64-bit and the int types are 32-bit, so compilers
stop with an error (in especial in macOS because clang).
The source code in this webpage is already updated to use FILE *
instead of int, and this way we brought CP/M to year 2024, hehe.
If you are curious about it, the previous version of the emulator can
be downloaded here or in the IOCCC website. In 2020, Mark Abene sent
me a workaround for x86-64 computers:
gcc -static toledo2.c -o toledo2
But this doesn't work in macOS.
Useful links:
* Palo Alto Tiny BASIC source code, this is a modified version of
the original.
* CP/M source code, the foundation of an operating system:
monotask, no memory manager, only console and file services
provided.
* Someone received an 8080 computer as a gift, dumped the ROM, and
someone else found a way to test them with my 8080 emulator. :)
Last modified: Feb/06/2024