9

I'm working on a project that intends to implement a dynamically typed language on 8-bit systems. (That is, a CPU with 16-bit pointers and a maximum of 64 kB of RAM. Some language tricks are involved, which I can explain in more detail if you want. Development is at https://git.sr.ht/~jpivarski/zippy)

I'd like to test it in some realistic way during development, either by running (for example) a Z80 system in emulation or by connecting a hobby kit to my Linux computer (USB?), downloading compiled code to it, running it, and seeing the results in a window or little screen.

I've done enough research to know that SDCC is a good compiler—if I compile my no-libraries, no-malloc C code with that, then I'll find out if I'm using C features that are incompatible with these small systems. I've also found hundreds of emulators and hardware kits online, some do-it-yourself, others more pre-packaged (I'd want the latter).

However, I'm still confused about how to get started on a runtime system, in some basic ways. I've seen a lot of references to a CP/M operating system, but I don't think the device would need to have an operating system. I assume that my compiled C code would go into real or emulated ROM, and I'd have unrestricted 64 kB of address space to play with.

I understand that this depends on the whole architecture, not just the CPU, but any generic/reasonable architecture would be sufficient to determine whether I'm writing realistic code or not.

I guess I'm imagining a situation that's more like GP-GPU development: there's a device that I can load code into, that code computes something, using its RAM as a scratch-pad, and then returns results somehow, possibly as an emulated screen in a window, possibly a real screen that I look at.

What kinds of tools should I be looking for to get this kind of development experience (for both the emulated and real-hardware cases)? Do you have any suggestions of where to start?

6
  • SDCC tends to add machine specific features resulting in an SDCC-times-machine specific C dialect, eg for 8051 numerous storage classes that are not in standard C are added.... Dec 31, 2022 at 10:40
  • OT: What are these "C features that are incompatible with these small systems"? Since SDCC has a lot of modern C features, what alternative do you consider? Dec 31, 2022 at 22:55
  • What I'm worried about is writing the whole program and then finding out that I can't use some basic feature, like recursion (a limitation of Small C—and one that I don't think I'd be able to work around, anyway). Therefore, I want to start test-compiling and test-running it early in development, so if there are any limitations, I will discover them early and will work around them while it's still easy to do so. If there aren't limitations, then great! Jan 1, 2023 at 19:13
  • You need something to communicate with the user accepting input and showing output. You might also want to read and write files to disk. This is more or less what CP/M offers in a standard package with tons of existing emulators many of these having most of the memory available to the program running. Jan 2, 2023 at 2:48
  • Also there is a lot of Z80 plugins for Visual Studio Code. Some even include a full debugging environment. Jan 2, 2023 at 2:50

4 Answers 4

7

Your computer needs input and output. You can provide this in a great number of ways, but emulating an entire screen is probably too much work. One of the simplest approaches is an asynchronous serial port. This is what is done on most CP/M machines, and the basic RC2014, and lots of other single-board computers too.

For an emulated system, you connect your (emulated) serial port to standard I/O. There are tons of preexisting emulators for this purpose, so you don’t need to write an emulator (unless you want to, but that’s a different kind of fun!). Using a serial port on standard in/standard out means you can connect test harnesses directly to the emulator, greatly speeding your development cycle.

For physical hardware, you connect your physical Zilog SIO chip (or DART, MC6850, 16550, or whatever UART you have chosen) to a cheap-o USB-TTL serial adapter, and connect from your host machine to the physical hardware via (e.g.) screen or minicom. You can even get a RS232 adapter to use a physical terminal, though those are very bulky and rare these days! Or use the built-in serial port on an older PC, though that will require an additional chip and two extra voltages (+/- 12V usually) on your computer for level shifting.

With a simple boot loader, you can even use the same serial port to load programs into your 8-bit system. In fact, this is what many preexisting development boards do.


However, if you really want to be extremely simple, just use RAM as your input and output! An emulator can easily write and read to RAM. There is a physical type of “EPROM Emulator” board that can do this too, but you’re venturing deep into higher costs at that point.

7
  • "There are tons of preexisting emulators": that's true, but many of them are discontinued projects or designed for playing old games. Do you have a favorite modern emulator that would suit this purpose well? I don't have a preferred CPU; anything with 16-bit pointers will do. Communicating by overwriting the entire RAM and extracting the entire RAM sounds like the easiest way to go (and resembles GP-GPU programming quite a bit...). Dec 31, 2022 at 16:03
  • 1
    @JimPivarski github.com/GiovanniScotti/z80cpuEmulator looks like a pretty good base, it even uses stdin/stdout for its serial chip. I haven’t used it, but it’s identical to the architecture I mentioned above. Just provide a .HEX file to set the ROM. en.wikipedia.org/wiki/Intel_HEX Dec 31, 2022 at 16:25
  • Buttons and lights are also highly recommended forms of I/O. A group of 74HC373 chips controlled via 74HC138 can be used very conveniently to add groups of eight outputs/lights or buttons/inputs without multiplexing. Another alternative for outputs is a 74HC259. Unlike the 74HC373 which requires code to output eight bits at once, the 74HC259 flips one bit in response to each access. If the data input is wired to an address pin, a bit can be set or cleared by performing a store operation whose data will be ignored. This approach was used in the Apple II, but for some reason hasn't been...
    – supercat
    Dec 31, 2022 at 19:16
  • ...terribly popular elsewhere even though being able to simply say "STA $C055" to switch to display page 2 is more convenient, efficient, and robust, than e.g. "LDA $C050 / ORA #$04 / STA $C050" would be.
    – supercat
    Dec 31, 2022 at 19:19
  • @supercat Strongly agree, lights and switches on latches are so simple when you’re bringing up a breadboard computer! I only suggested a serial port because it’s much easier to get text in/out of, and is a very good fit for the Linux “everything is a file” model of OP’s host development system. Dec 31, 2022 at 19:40
15

Ever thought about using an 8086?

Wait, hear me out:

The 8086 is exactly what your requirements seem to be:

  • 8 Bit registers
  • (and 8 bit bus in case of an 8088)
  • 16 bit pointers
  • 64 KiB address space

That is if you set all segment registers to the same value. Which is the Tiny programming model used for COM programs under DOS - an environment especially made for (often) automatic translation of 8-bit code from 8080 systems to 8086. Even better DOS offers since 1.0 calls equivalent to CP/M nicely fitting 8 bit code.

After all, the 8086 was developed as an almost direct replacement in terms of hard and software. While the hardware is very close to the 8085 bus, the 8086 ISA set is essentially an extended 8080. All 8080 registers (and thus all basic Z80 registers (*1)) have direct counter parts:

  • AL is A as the general purpose 8 bit accumulator,
  • CH/CL is B/C as these were the general purpose pair and counter
  • DH/DL is D/E, second general purpose 16 bit pair,
  • BH/BL is H/L as the primary pointer register.

Also, almost all instruction are available with the same addressing mode (*2). Exceptions are:

  • PUSH/POP PSW which on the 8080 pushes Flags and A while the 8086 handles them separate - might even simplify design
  • LDAX/STAX - to be replaced by moving CX or DX to SI or DI first
  • All conditional CALL - to be replaced by conditional JMP around a CALL
  • All conditional RETURN- to be replaced by conditional JMP around a RET

Either of these may not be important at all in your project.

Going with the 8086 as 8 bit platform provides several advantages:

  • No need to start to build hardware - any modern (or old) PC will do it
  • No need to write an emulator - there are several - like DOSBox - availible for many modern OS
  • No need to learn the quirks of some 80s computer
  • No need to learn a new OS (*3)
  • Seem less integration in file system and environment of your OS
  • Use the computer right in front of you
  • Use the many tools you're familiar with

By restricting yourself to only 8080 type instructions a backport to real 8 bit hardware will be a no-brainer, as all 8086 (and DOS) specifica can be handled by a very slim wraper.

And in case you're using a C supporting DOS, all you have to do is switch for COM generation.


P.S.: Maybe I'm just not familiar with that repository, but I had a hard time to find any spec for that language...


*1 - Ignoring the second register set, which is in most programming situations not helpful anyway.

*2 - Rather easy as the 8080 doesn't know many

*3 - Well, maybe relearning DOS :)

6
  • 1
    That sounds appealing—the quickest way to a realistic test! I have an AMD-based Ubuntu Linux system in front of me. How would you recommend running DOS (FreeDOS?), compiling C code for it, and ensuring that it's running in "tiny" mode? Should I run a C compiler inside the DOS emulation, so that it produces the right bytecode? I have no problem with DOS commands, communicating with the emulation using Telnet/FTP/qemu-img/whatever it takes. Dec 31, 2022 at 15:58
  • 2
    @JimPivarski For the quickest results I would recommend to use an environment like DOSbox (DOSBox, DOSbox-X both have ready to use RPM), which is well proven to give reliable results under various OS and CPUs. It needs no DOS as it emulates that as well. It runs in a window. Integration to outside tools is simple. Just mount a directory as a drive. For compilation using a genuine DOS C compiler like MS or Borland within DOSbox sounds like a good idea. Borland might be the most simple to use.
    – Raffzahn
    Dec 31, 2022 at 16:58
  • 1
    An important thing to understand about the 8088/8086 is while its design makes it difficult to do programmatic address calculations that combine a segment and offset, it largely eliminates the need to do such calculations. Code which either deals with individual objects that are no bigger than 65,520 bytes or manages 16-byte chunks as units, can use 16-bit math with no need for shifts beyond converting an allocation request size measured in bytes into a number of 16-byte paragraphs. DMA requests require dealing with linear addresses, but otherwise if one finds oneself dealing with...
    – supercat
    Dec 31, 2022 at 19:11
  • 1
    @Jim: One downside to 8086 is that is makes 16-bit integers much more efficient, just one instruction to add/sub/shift/multiply them. vs. on an 8-bit target, code using C int instead of signed char or int8_t will find itself needing more instructions. If you're worried about code-size limits, this might distort things. As for what compiler, ia16-gcc seems to be mature enough for FreeDOS. Compile outside the guest environment with the full power of modern GCC's optimizer. github.com/tkchia/gcc-ia16 / gitlab.com/tkchia/build-ia16/-/releases is what came up in google. Jan 1, 2023 at 12:03
  • 1
    @JimPivarski: For bare-metal debugging (without DOS, just BIOS "system calls", if you build a legacy BIOS MBR bootloader instead of a .com), Bochs is good; it has a built-in debugger. Otherwise QEMU can act as a remote for GDB to connect to; GDB doesn't understand segmentation but with a Tiny model that's not a problem. DOSBox does also work under Linux, IIRC, and I think might have an option to build with a debugger as part of DOSBox. Otherwise you'd need a debugger inside the DOS guest, like \@ecm's pushbx.org/ecm/web/#projects-ldebug Also stackoverflow.com/tags/x86-16/info Jan 1, 2023 at 12:09
5

I would definitely go for an emulated ZX Spectrum 48K because:

  1. There are tons of free emulators for it

  2. No need for injection of code

    You simply create and load *.tap file with your code into emulator (I did not see any that would not support tap files) and on ZX side simply type this in BASIC:

    LOAD ""
    

    or

    LOAD "" CODE
    

    If you encode your .tap file as code starting at some address like 32768 then you can run your code from BASIC like this:

    RANDOMIZE USR 32768
    

    If not you can do it from BASIC too IIRC:

    LOAD "" CODE 32768 
    RANDOMIZE USR 32768
    
  3. Input/output

    You can use ZX keyboard (matrix), repro (1bit digital sound) and screen (direct VRAM access at 16384 of 256*192 dots and 32*24 color attributes ... 6912 Bytes total) so either you write your own handlers or use BASIC ROM subroutines (there are lots of docs describing them)

  4. ZX is notoriously known so there are tons of docs for it

    You can find commented ROM disassembly listings, source for "any" subroutine or examples you can think of ... which might be compiled with any Z80 cross compiler and then you just use that as binary ...

  5. Many emulators have build in debuggers

    So debugging your runtime/language engine code will be much easier...

which means no work on the 8bit machine side is needed and very little work on runtime stuff...

2
  • So maybe this emulator: github.com/chernandezba/zesarux ? I found it on worldofspectrum.org/tools/emulators , filtering for Linux and preferring ones that are being maintained. Is this the right idea: I compile my C code with SDCC on my host computer (?), write it into a TAP file, load it in the emulator, and execute it from BASIC? Dec 31, 2022 at 16:12
  • @JimPivarski 1. not using linux so do not know what emulator is a good one there (I use my own C++/VCL on Win32) ... 2. yes that's the idea ...
    – Spektre
    Dec 31, 2022 at 16:45
1

If you want to use a Z80 emulator under Linux I am currently looking at RunCPM for doing some hobby work in assembly where the plan is ending up with a Github Action (but that is another story!)

The reason why I ended up with it is because it can simulate floppies from Unix directories, so you generate your binary on your host in the appropriate directory, and then launch the emulator where it can immediately be seen and run (this can be automated).

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .