About Archive Tags RSS Feed

 

Entries tagged cpmulator

A simple golang CP/M emulator

15 April 2024 21:00

A couple of years ago I wrote a simple text-based adventure game in Z80 assembly language, to amuse our child. The game was written for CP/M, because that is the operating system my single-board Z80-based computer runs upon.

Later I ported the game to the ZX Spectrum 48k.

Recently I went through a burst of enthusiasm and started to overhaul the code a little, adding word-wrapping and fixing a couple of bugs. That lead to a new release, and also a brief amount of (positive) feedback on hacker news.

After mulling it over I realized that the number of CP/M BIOS functions I was using was very minimal, almost only the minimum you'd expect:

  • Write a character to STDOUT.
  • Write a $-terminated string to STDOUT.
  • Read a character from STDIN.
  • Read a line from STDIN.

It crossed my mind that implementing those syscalls should be trivial, and if I bundled implementations with a Z80 emulator library I'd have a means of running the game without a real CP/M installation, and without using the ZX Spectrum port.

So I picked a golang-based Z80 emulator, and started hacking.

After a day I had a working system, and I added a few more syscalls:

  • Open File, Create File, Delete File, Close File.
  • Console I/O.
  • Read Record.

After that? I can now play Zork 1, Zork 2, Zork 3, and The Hitchhiker's guide to the Galaxy, from Infocom.

I suspect I'm "done" for now, though it might be nice to add WriteRecord and the other missing functions there's no obvious use for yet another CP/M, especially with a CCP.

Still CP/M. In Golang. For text-based adventures:

| No comments

 

The CP/M emulator development continues

4 May 2024 12:00

In my previous post I introduced a toy CP/M Emulator I'd been working on.

At the time it was capable of running the Infocom text-based adventure games, so I thought it was done. Of course I also wanted to run Microsoft's original BASIC and it turned out that was a challenge because the coding of their interpreter didn't use the standard CP/M entry-point for making syscalls (call 0x0005).

Instead of calling 0x0005 to invoke the BDOS/BIOS functions the BASIC interpreter used the single-byte CALL instructions which are available on the Z80 processor. There are a bunch of these instructions:

  • RST 00
  • RST 08
  • RST 10
  • RST 18
  • RST 20
  • RST 28
  • RST 30
  • RST 38

Each of those instructions is equivalent to a call instruction with a fixed offset, "call 0x0010", "call 0x0020", etc. I had to rework the emulator to cope with this approach, which causes repetition but nothing too surprising. The end result is that now my emulator can run Microsoft Basic, Tasty Basic, and some more programs.

Things work but a couple of the syscalls are of the form "Return true if there is a pending keystroke", or "wait until there is keyboard input present and return the first character". I have some busy-loops which peg the CPU, which sucks but works. On the downside running the code on a MacOS machine has some weird issues with repeated keys and similar. So I need to look into fixing that for my own sense of peace.

I put together a little repository of binaries for playing with though, and that's been helpful. My emulator has a special flag which treats sub-directories as "Drives". So A: points to A/, B: points to B/, etc. That makes distributing and working with things easy!

| No comments

 

The CP/M emulator is working well

25 May 2024 12:00

In my recent posts I've talked about implementing BDOS and BIOS syscalls for my cp/m emulator. I've now implemented enough of the calls that I can run many of the standard binaries:

  • The Aztech C Compiler
  • Microsoft BASIC
  • Turbo Pascal
  • Wordstar
  • etc

Of course I've not implemented all the syscalls, so the emulation isn't 100% perfect and many binaries won't run. But I sent myself on a detour by implementing extra syscalls, custom syscalls.

Traditionally CP/M systems are "rebooted" by pressing Ctrl-C at the CCP prompt. I thought that was something I'd press by accident so I implemented the restart behaviour only when the user pressed Ctrl-C twice in a row. But then I added a custom syscall that lets you change hte value:

A>ctrlc
The Ctrl-C count is currently set to 2
A>ctrlc 1
The Ctrl-C count is currently set to 1
A>

So you can now change the value at runtime. Similarly there is support for switching CCP at runtime, and even changing the default output-device from ADM-3A to ANSI, or vice-versa. It's kinda neat to make these kind of extensions, and happily the traditional BIOS has two syscalls reserved for custom use so I just used one of those.

I've added support for testing whether a binary is running under my emulator, or not, using a custom syscall. So I can run:

A>test
This binary is running under cpmulator:

cpmulator unreleased
https://github.com/skx/cpmulator/

On another emulator I see this:

A>test
Illegal BIOS call 31
No, this binary is not running under cpmulator.

Anyway I'm happy with the current state of things, and I fixed a couple of bugs which means I now have support for SUBMIT.COM which is a real time-saver.

| No comments