Skip to content
This repository was archived by the owner on Sep 10, 2025. It is now read-only.

Commit 2458e06

Browse files
authored
Merge pull request #105 from skx/104-virtual
Implement a naive virtual filesystem
2 parents 42f46c8 + 0075c3e commit 2458e06

File tree

20 files changed

+336
-81
lines changed

20 files changed

+336
-81
lines changed

Makefile

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
###
2+
## Hacky makefile which does too much recompilation.
3+
##
4+
## "go build" / "go install" will do the right thing, unless
5+
## you're changing the CCP or modifying the static binaries we
6+
## added.
7+
##
8+
###
9+
10+
11+
ALL: ccp static cpmulator
12+
13+
14+
#
15+
# CCP is fast to build.
16+
#
17+
.PHONY: ccp
18+
ccp: $(wildcard ccp/*.ASM)
19+
cd ccp && make
20+
21+
#
22+
# Static helpers are fast to build.
23+
#
24+
.PHONY: static
25+
static: $(wildcard ccp/*.z80)
26+
cd static && make
27+
28+
29+
#
30+
# cpmulator is fast to build.
31+
#
32+
cpmulator: $(wildcard *.go */*.go)
33+
go build .

README.md

Lines changed: 31 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ This emulator is written using golang, so if you have a working golang toolchain
3333
go install github.com/skx/cpmulator@latest
3434
```
3535

36-
If you were to clone this repository to your local system you could then build and install by running:
36+
If you were to clone this repository to your local system you could build, and install it, by running:
3737

3838
```
3939
go build .
@@ -104,8 +104,7 @@ You can terminate the CCP by typing `EXIT`. The following built-in commands are
104104
* `CLS`
105105
* Clear the screen.
106106
* `DIR`
107-
* List files, by default this uses "`*.*`".
108-
* Try "`DIR *.COM`" if you want to see something more specific, for example.
107+
* Try "`DIR *.COM`" if you want to see only executables, for example.
109108
* `EXIT` / `HALT` / `QUIT`
110109
* Terminate the CCP.
111110
* `ERA`
@@ -122,7 +121,7 @@ There are currently a pair of CCP implementations included within the emulator,
122121

123122
* "ccp"
124123
* This is the default, but you can choose it explicitly via `cpmulator -ccp=ccp ..`.
125-
* The original/default one, from Digital Research
124+
* The original/default one, from Digital Research.
126125
* "ccpz"
127126
* Launch this via `cpmulate -ccp=ccpz ..`
128127
* An enhanced one with extra built-in commands.
@@ -136,7 +135,11 @@ You can also launch a binary directly by specifying it's path upon the command-l
136135
$ cpmulator /path/to/binary [optional-args]
137136
```
138137

139-
Other options are shown in the output of `cpmulator -help`, but in brief:
138+
139+
140+
## Command Line Flags
141+
142+
There are several command-line options which are shown in the output of `cpmulator -help`, but in brief:
140143

141144
* `-cd /path/to/directory`
142145
* Change to the given directory before running.
@@ -151,7 +154,9 @@ Other options are shown in the output of `cpmulator -help`, but in brief:
151154
* `-syscalls`
152155
* Dump the list of implemented BDOS and BIOS syscalls.
153156
* `-version`
154-
* Show our version number.
157+
* Show the version number of the emulator, and exit.
158+
159+
Note that some of these options can be changed at runtime.
155160

156161

157162

@@ -161,7 +166,7 @@ When the CCP is launched for interactive execution, we allow commands to be exec
161166

162167
* If `SUBMIT.COM` **and** `AUTOEXEC.SUB` exist on A:
163168
* Then the contents of `AUTOEXEC.SUB` will be executed.
164-
* We secretly run "`SUBMIT AUTOEXEC`" to achieve this.
169+
* We secretly run "`SUBMIT AUTOEXEC`" to achieve this.
165170

166171
This allows you to customize the emulator, or perform other "one-time" setup via the options described in the next section.
167172

@@ -171,24 +176,26 @@ This allows you to customize the emulator, or perform other "one-time" setup via
171176

172177
There are a small number of [extensions](EXTENSIONS.md) added to the BIOS functionality we provide, and these extensions allow changing the behaviour of the emulator at runtime.
173178

179+
The behaviour changing is achieved by having a small number of .COM files invoke the extension functions, and these binaries are embedded within our emulator to improve ease of use, via the [static/](static/) directory in our source-tree - This means no matter what you'll always find some binaries installed on A:, despite not being present in reality.
180+
174181

175182
### CCP Handling
176183

177-
We default to loading the Digital Research CCP, but allow the CCPZ to be selected via the `-ccp` command-line flag. The binary `samples/ccp.com` lets you change CCP at runtime.
184+
We default to loading the Digital Research CCP, but allow the CCPZ to be selected via the `-ccp` command-line flag. The binary `A:CCP.COM` lets you change CCP at runtime.
178185

179186

180187
### Ctrl-C Handling
181188

182189
Traditionally pressing `Ctrl-C` would reload the CCP, via a soft boot. I think that combination is likely to be entered by accident, so in `cpmulator` we default to requiring you to press Ctrl-C _twice_ in a row to reboot the CCP.
183190

184-
I've added a binary `samples/ctrlc.com` which lets you change this at runtime. Run `ctrlc 0` to disable the Ctrl-C behaviour, or `ctrlc N` to require N consecutive Ctrl-C keystrokes to trigger the restart-behaviour (max: 9).
191+
The binary `A:CTRLC.COM` which lets you change this at runtime. Run `A:CTRLC 0` to disable the Ctrl-C behaviour, or `A:CTRLC N` to require N consecutive Ctrl-C keystrokes to trigger the restart-behaviour (max: 9).
185192

186193

187194
### Console Output
188195

189-
We default to pretending our output device is an ADM-3A terminal, this can be changed via the `-console` command-line flag at startup. Additionally it can be changed at runtime via the `samples/console.com` command.
196+
We default to pretending our output device is an ADM-3A terminal, this can be changed via the `-console` command-line flag at startup. Additionally it can be changed at runtime via `A:CONSOLE.COM`.
190197

191-
Run `console ansi` to disable the output emulation, or `console adm-3a` to restore it.
198+
Run `A:CONSOLE ansi` to disable the output emulation, or `A:CONSOLE adm-3a` to restore it.
192199

193200
You'll see that the [cpm-dist](https://github.com/skx/cpm-dist) repository contains a version of Wordstar, and that behaves differently depending on the selected output handler. Changing the handler at run-time is a neat bit of behaviour.
194201

@@ -197,7 +204,7 @@ You'll see that the [cpm-dist](https://github.com/skx/cpm-dist) repository conta
197204

198205
When CCP is soft/warm-booted it prints a banner showing the currently active CCP, and the console-output device which is in-use.
199206

200-
Running `samples/quiet.com` will silence this output, essentially enabling "quiet mode".
207+
Running `A:QUIET 1` will silence this output, essentially enabling "quiet mode", running with no arguments will show the current state, and running `A:QUIET 0` will restore the default behaviour.
201208

202209

203210

@@ -342,36 +349,23 @@ For reference the memory map of our CP/M looks like this:
342349

343350

344351

345-
# Sample Programs
346-
347-
You'll see some Z80 assembly programs beneath [samples](samples/) which are used to check my understanding. If you have the `pasmo` compiler enabled you can build them all by running "make", in case you don't I've also committed the generated binaries.
348-
349-
352+
# Credits and References
350353

351354

352-
# Credits
353-
354355
* Much of the functionality of this repository comes from the [excellent Z80 emulator library](https://github.com/koron-go/z80) it is using, written by [@koron-go](https://github.com/koron-go).
355-
* The CCP comes from [my fork](https://github.com/skx/z80-playground-cpm-fat/) of the original [cpm-fat](https://github.com/z80playground/cpm-fat/)
356+
* The default CCP comes from [my fork](https://github.com/skx/z80-playground-cpm-fat/) of the original [cpm-fat](https://github.com/z80playground/cpm-fat/)
356357
* However this is largely unchanged from the [original CCP](http://www.cpm.z80.de/source.html) from Digital Research, although I did add the `CLS`, `EXIT`, `HALT` & `QUIT` built-in commands.
357-
358-
When I was uncertain of how to implement a specific system call the following two emulators were also useful:
359-
360-
* [https://github.com/ivanizag/iz-cpm](https://github.com/ivanizag/iz-cpm)
361-
* Portable CP/M emulation to run CP/M 2.2 binaries for Z80.
358+
* Reference Documentation
359+
* [CP/M BDOS function reference](https://www.seasip.info/Cpm/bdos.html).
360+
* [CP/M BIOS function reference](https://www.seasip.info/Cpm/bios.html).
361+
* Other emulators which were useful resources when some functionality was unclear:
362+
* [https://github.com/ivanizag/iz-cpm](https://github.com/ivanizag/iz-cpm)
363+
* Portable CP/M emulation to run CP/M 2.2 binaries for Z80.
362364
* Has a handy "download" script to fetch some CP/M binaries, including BASIC, Turbo Pascal, and WordStar.
363-
* Written in Rust.
364-
* [https://github.com/jhallen/cpm](https://github.com/jhallen/cpm)
365-
* Run CP/M commands in Linux/Cygwin with this Z80 / BDOS / ADM-3A emulator.
366-
* Written in C.
367-
368-
369-
370-
371-
# References
372-
373-
* [Digital Research - CP/M Operating System Manual](http://www.gaby.de/cpm/manuals/archive/cpm22htm/)
374-
* Particularly the syscall reference in [Section 5: CP/M 2 System Interface](http://www.gaby.de/cpm/manuals/archive/cpm22htm/ch5.htm).
365+
* Written in Rust.
366+
* [https://github.com/jhallen/cpm](https://github.com/jhallen/cpm)
367+
* Run CP/M commands in Linux/Cygwin with this Z80 / BDOS / ADM-3A emulator.
368+
* Written in C.
375369

376370

377371

@@ -394,8 +388,6 @@ The testing that I should do before a release:
394388
* [ ] Test BE.COM
395389
* [ ] Test STAT.COM
396390
* [ ] Test some built-in shell-commands; ERA, TYPE, and EXIT.
397-
* [ ] Test `samples/INTEST.COM` `samples/READ.COM`, `samples/WRITE.COM`.
398-
* These demonstrate core primitives are not broken.
399391

400392

401393

cpm/cpm.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ package cpm
88

99
import (
1010
"context"
11+
"embed"
1112
"errors"
1213
"fmt"
1314
"log/slog"
@@ -108,6 +109,10 @@ type CPM struct {
108109
// files is the cache we use for File handles.
109110
files map[uint16]FileCache
110111

112+
// virtual contains a reference to a static filesystem which
113+
// is embedded within our binary, if any.
114+
static embed.FS
115+
111116
// input is our interface for reading from the console.
112117
//
113118
// This needs to take account of echo/no-echo status.
@@ -841,6 +846,11 @@ func (cpm *CPM) RunAutoExec() {
841846
cpm.input.StuffInput("SUBMIT AUTOEXEC")
842847
}
843848

849+
// SetStaticFilesystem allows adding a reference to an embedded filesyste,.
850+
func (cpm *CPM) SetStaticFilesystem(fs embed.FS) {
851+
cpm.static = fs
852+
}
853+
844854
// SetDrives enables/disables the use of subdirectories upon the host system
845855
// to represent CP/M drives.
846856
//

0 commit comments

Comments
 (0)