cpm

package
v0.16.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 25, 2025 License: MIT Imports: 21 Imported by: 0

Documentation

Overview

Package cpm is the main package for our emulator, it contains the Z80 emulator we're using, along with the memory the running binaries inhabit, and the appropriate glue to wire things together.

The package mostly contains the implementation of the syscalls that CP/M programs would expect - along with a little machinery to wire up the Z80 emulator we're using and deal with FCB structures.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrHalt will be used to note that the Z80 emulator executed a HALT
	// operation, and that terminated the execution of code.
	//
	// It should be handled and expected by callers.
	ErrHalt = errors.New("HALT")

	// ErrBoot will be used to note that the Z80 emulator executed code should
	// reboot / restart.
	//
	// This is mostly used to handle the CCP restarting after a client application
	// has terminated.
	//
	// It should be handled and expected by callers.
	ErrBoot = errors.New("BOOT")

	// ErrTimeout is used when a timeout occurs
	ErrTimeout = errors.New("TIMEOUT")

	// ErrUnimplemented will be used to handle a CP/M binary calling an unimplemented syscall.
	//
	// It should be handled and expected by callers.
	ErrUnimplemented = errors.New("UNIMPLEMENTED")

	// DefaultInputDriver contains the name of the default console input driver.
	DefaultInputDriver string = "term"

	// DefaultOutputDriver contains the name of the default console output driver.
	DefaultOutputDriver string = "adm-3a"

	// DefaultDMAAddress is the default address of the DMA area, post-boot.
	DefaultDMAAddress uint16 = 0x0080
)

Functions

func BdosSysCallAuxRead added in v0.11.0

func BdosSysCallAuxRead(cpm *CPM) error

BdosSysCallAuxRead reads a single character from the auxiliary input.

Note: Echo is not enabled in this function.

func BdosSysCallAuxWrite added in v0.11.0

func BdosSysCallAuxWrite(cpm *CPM) error

BdosSysCallAuxWrite writes the single character in the C register auxiliary / punch output.

func BdosSysCallBDOSVersion added in v0.11.0

func BdosSysCallBDOSVersion(cpm *CPM) error

BdosSysCallBDOSVersion returns version details

func BdosSysCallConsoleStatus added in v0.11.0

func BdosSysCallConsoleStatus(cpm *CPM) error

BdosSysCallConsoleStatus tests if we have pending console (character) input.

func BdosSysCallDeleteFile added in v0.11.0

func BdosSysCallDeleteFile(cpm *CPM) error

BdosSysCallDeleteFile deletes the filename(s) matching the pattern specified by the FCB in DE.

func BdosSysCallDirectScreenFunctions added in v0.11.0

func BdosSysCallDirectScreenFunctions(cpm *CPM) error

BdosSysCallDirectScreenFunctions receives a pointer in DE to a parameter block, which specifies which function to run. I've only seen this invoked in TurboPascal when choosing the "Execute" or "Run" options.

func BdosSysCallDriveAllReset added in v0.11.0

func BdosSysCallDriveAllReset(cpm *CPM) error

BdosSysCallDriveAllReset resets the drives.

If there is a file named "$..." then we need to return 0xFF in A, which will be read by the CCP - as created by SUBMIT.COM

func BdosSysCallDriveAlloc added in v0.11.0

func BdosSysCallDriveAlloc(cpm *CPM) error

BdosSysCallDriveAlloc will return the address of the allocation bitmap (which blocks are used and which are free) in HL.

func BdosSysCallDriveGet added in v0.11.0

func BdosSysCallDriveGet(cpm *CPM) error

BdosSysCallDriveGet returns the number of the active drive.

func BdosSysCallDriveROVec added in v0.11.0

func BdosSysCallDriveROVec(cpm *CPM) error

BdosSysCallDriveROVec will return a bitfield describing which drives are read-only.

Bit 7 of H corresponds to P: while bit 0 of L corresponds to A:. A bit is set if the corresponding drive is set to read-only in software. As we never set drives to read-only we return 0x0000

func BdosSysCallDriveReset added in v0.11.0

func BdosSysCallDriveReset(cpm *CPM) error

BdosSysCallDriveReset allows resetting specific drives, via the bits in DE Bit 7 of D corresponds to P: while bit 0 of E corresponds to A:. A bit is set if the corresponding drive should be reset. Resetting a drive removes its software read-only status.

func BdosSysCallDriveSet added in v0.11.0

func BdosSysCallDriveSet(cpm *CPM) error

BdosSysCallDriveSet updates the current drive number.

func BdosSysCallDriveSetRO added in v0.11.0

func BdosSysCallDriveSetRO(cpm *CPM) error

BdosSysCallDriveSetRO will mark the current drive as being read-only.

This call is faked.

func BdosSysCallErrorMode added in v0.11.0

func BdosSysCallErrorMode(cpm *CPM) error

BdosSysCallErrorMode implements a NOP version of F_ERRMODE.

func BdosSysCallExit added in v0.11.0

func BdosSysCallExit(cpm *CPM) error

BdosSysCallExit implements the Exit syscall

func BdosSysCallFileClose added in v0.11.0

func BdosSysCallFileClose(cpm *CPM) error

BdosSysCallFileClose closes the filename that matches the pattern on the FCB supplied in DE.

To handle SUBMIT we need to also do more than close an existing file handle, and remove it from our cache. It seems that we can also be required to _truncate_ a file. Because I'm unsure exactly how much this is in-use I'm going to only implement it for files with "$" in their name.

func BdosSysCallFileOpen added in v0.11.0

func BdosSysCallFileOpen(cpm *CPM) error

BdosSysCallFileOpen opens the filename that matches the pattern on the FCB supplied in DE

func BdosSysCallFileSize added in v0.11.0

func BdosSysCallFileSize(cpm *CPM) error

BdosSysCallFileSize updates the Random Record bytes of the given FCB to the number of records in the file.

func BdosSysCallFindFirst added in v0.11.0

func BdosSysCallFindFirst(cpm *CPM) error

BdosSysCallFindFirst finds the first filename, on disk, that matches the glob in the FCB supplied in DE.

func BdosSysCallFindNext added in v0.11.0

func BdosSysCallFindNext(cpm *CPM) error

BdosSysCallFindNext finds the next filename that matches the glob set in the FCB in DE.

func BdosSysCallGetDriveDPB added in v0.11.0

func BdosSysCallGetDriveDPB(cpm *CPM) error

BdosSysCallGetDriveDPB returns the address of the DPB, which is faked.

func BdosSysCallGetIOByte added in v0.11.0

func BdosSysCallGetIOByte(cpm *CPM) error

BdosSysCallGetIOByte gets the IOByte, which is used to describe which devices are used for I/O. No CP/M utilities use it, except for STAT and PIP.

The IOByte lives at 0x0003 in RAM, so it is often accessed directly when it is used.

func BdosSysCallLoginVec added in v0.11.0

func BdosSysCallLoginVec(cpm *CPM) error

BdosSysCallLoginVec returns the list of logged in drives.

func BdosSysCallMakeFile added in v0.11.0

func BdosSysCallMakeFile(cpm *CPM) error

BdosSysCallMakeFile creates the file named in the FCB given in DE

func BdosSysCallPrinterWrite added in v0.11.0

func BdosSysCallPrinterWrite(cpm *CPM) error

BdosSysCallPrinterWrite should send a single character to the printer, we fake that by writing to a file instead.

func BdosSysCallRandRecord added in v0.11.0

func BdosSysCallRandRecord(cpm *CPM) error

BdosSysCallRandRecord Sets the random record count bytes of the FCB to the number of the last record read/written by the sequential I/O calls.

func BdosSysCallRawIO added in v0.11.0

func BdosSysCallRawIO(cpm *CPM) error

BdosSysCallRawIO handles both simple character output, and input.

Note that we have to poll and determine if character input is present in this function, otherwise games and things don't work well without it.

Blocking in the handler for 0xFF will make ZORK X work, but not other things this is the single hardest function to work with. Meh.

func BdosSysCallRead added in v0.11.0

func BdosSysCallRead(cpm *CPM) error

BdosSysCallRead reads a record from the file named in the FCB given in DE

func BdosSysCallReadChar added in v0.11.0

func BdosSysCallReadChar(cpm *CPM) error

BdosSysCallReadChar reads a single character from the console.

func BdosSysCallReadRand added in v0.11.0

func BdosSysCallReadRand(cpm *CPM) error

BdosSysCallReadRand reads a random block from the FCB pointed to by DE into the DMA area.

func BdosSysCallReadString added in v0.11.0

func BdosSysCallReadString(cpm *CPM) error

BdosSysCallReadString reads a string from the console, into the buffer pointed to by DE.

func BdosSysCallRenameFile added in v0.11.0

func BdosSysCallRenameFile(cpm *CPM) error

BdosSysCallRenameFile will handle a rename operation. Note that this will not handle cross-directory renames (i.e. file moving).

func BdosSysCallSetDMA added in v0.11.0

func BdosSysCallSetDMA(cpm *CPM) error

BdosSysCallSetDMA updates the address of the DMA area, which is used for block I/O.

func BdosSysCallSetFileAttributes added in v0.11.0

func BdosSysCallSetFileAttributes(cpm *CPM) error

BdosSysCallSetFileAttributes should update the attributes of the given file, but it fakes it.

func BdosSysCallSetIOByte added in v0.11.0

func BdosSysCallSetIOByte(cpm *CPM) error

BdosSysCallSetIOByte sets the IOByte, which is used to describe which devices are used for I/O. No CP/M utilities use it, except for STAT and PIP.

The IOByte lives at 0x0003 in RAM, so it is often accessed directly when it is used.

func BdosSysCallTime added in v0.11.0

func BdosSysCallTime(cpm *CPM) error

BdosSysCallTime implements a NOP version of T_GET.

func BdosSysCallUptime added in v0.14.0

func BdosSysCallUptime(cpm *CPM) error

BdosSysCallUptime returns the number of "ticks" since the system was booted, it is a custom syscall which is implemented by RunCPM which we implement for compatibility, notable users include v5 of BBC BASIC.

func BdosSysCallUserNumber added in v0.11.0

func BdosSysCallUserNumber(cpm *CPM) error

BdosSysCallUserNumber gets, or sets, the user-number.

func BdosSysCallWrite added in v0.11.0

func BdosSysCallWrite(cpm *CPM) error

BdosSysCallWrite writes a record to the file named in the FCB given in DE

func BdosSysCallWriteChar added in v0.11.0

func BdosSysCallWriteChar(cpm *CPM) error

BdosSysCallWriteChar writes the single character in the E register to STDOUT.

func BdosSysCallWriteRand added in v0.11.0

func BdosSysCallWriteRand(cpm *CPM) error

BdosSysCallWriteRand writes a random block from DMA area to the FCB pointed to by DE.

func BdosSysCallWriteString added in v0.11.0

func BdosSysCallWriteString(cpm *CPM) error

BdosSysCallWriteString writes the $-terminated string pointed to by DE to STDOUT

func BiosSysCallAuxInputStatus added in v0.7.0

func BiosSysCallAuxInputStatus(cpm *CPM) error

BiosSysCallAuxInputStatus returns status of current auxiliary input device.

This is fake, and always returns "ready".

func BiosSysCallAuxOutputStatus added in v0.7.0

func BiosSysCallAuxOutputStatus(cpm *CPM) error

BiosSysCallAuxOutputStatus returns status of current auxiliary output device.

This is fake, and always returns "ready".

func BiosSysCallColdBoot added in v0.11.0

func BiosSysCallColdBoot(cpm *CPM) error

BiosSysCallColdBoot handles a cold boot.

func BiosSysCallConsoleInput added in v0.7.0

func BiosSysCallConsoleInput(cpm *CPM) error

BiosSysCallConsoleInput should block for a single character of input, and return the character pressed in the A-register.

func BiosSysCallConsoleOutput added in v0.7.0

func BiosSysCallConsoleOutput(cpm *CPM) error

BiosSysCallConsoleOutput should write a single character, in the C-register, to the console.

func BiosSysCallConsoleStatus added in v0.7.0

func BiosSysCallConsoleStatus(cpm *CPM) error

BiosSysCallConsoleStatus should return 0x00 if there is no input pending, otherwise 0xFF.

func BiosSysCallPrintChar added in v0.7.0

func BiosSysCallPrintChar(cpm *CPM) error

BiosSysCallPrintChar should print the specified character, in the C-register, to the printer. We fake that and write to a file instead.

func BiosSysCallPrinterStatus added in v0.7.0

func BiosSysCallPrinterStatus(cpm *CPM) error

BiosSysCallPrinterStatus returns status of current printer device.

This is fake, and always returns "ready".

func BiosSysCallReserved1 added in v0.8.0

func BiosSysCallReserved1(cpm *CPM) error

BiosSysCallReserved1 is a helper to get/set the values of the CPM interpreter from within the system. Neat.

func BiosSysCallScreenOutputStatus added in v0.7.0

func BiosSysCallScreenOutputStatus(cpm *CPM) error

BiosSysCallScreenOutputStatus returns status of current screen output device.

This is fake, and always returns "ready".

func BiosSysCallWarmBoot added in v0.11.0

func BiosSysCallWarmBoot(cpm *CPM) error

BiosSysCallWarmBoot handles a warm boot.

func WithCCP added in v0.13.0

func WithCCP(name string) cpmoption

WithCCP lets the default CCP to be changed in our constructor.

func WithContext added in v0.16.0

func WithContext(ctx context.Context) cpmoption

WithContext allows a context to be passed to the evaluator.

func WithHostExec added in v0.15.0

func WithHostExec(prefix string) cpmoption

WithHostExec allows executing commands on the host, by prefixing them with a custom prefix in the Readline primitive.

func WithInputDriver added in v0.14.0

func WithInputDriver(name string) cpmoption

WithInputDriver allows the default console input driver to be changed in our constructor.

func WithOutputDriver added in v0.14.0

func WithOutputDriver(name string) cpmoption

WithOutputDriver allows the default console output driver to be changed in our constructor.

func WithPrinterPath added in v0.13.0

func WithPrinterPath(path string) cpmoption

WithPrinterPath allows the printer output to changed in our constructor.

Types

type CPM

type CPM struct {

	// BDOSSyscalls contains details of the BDOS syscalls we
	// know how to emulate, indexed by their ID.
	BDOSSyscalls map[uint8]CPMHandler

	// BIOSSyscalls contains details of the BIOS syscalls we
	// know how to emulate, indexed by their ID.
	BIOSSyscalls map[uint8]CPMHandler

	// Memory contains the memory the system runs with.
	Memory *memory.Memory

	// CPU contains a pointer to the virtual CPU we use to execute
	// code.  The CP/M we're implementing is Z80-based, so we need to
	// be able to emulate that.
	CPU z80.CPU
	// contains filtered or unexported fields
}

CPM is the object that holds our emulator state.

func New

func New(options ...cpmoption) (*CPM, error)

New returns a new emulation object. We support default options, and new defaults may be specified via WithOutputDriver, etc, etc.

func (*CPM) Execute

func (cpm *CPM) Execute(args []string) error

Execute executes our named binary, with the specified arguments.

The function will not return until the process being executed terminates, and any error will be returned.

func (*CPM) GetBDOSAddress added in v0.15.0

func (cpm *CPM) GetBDOSAddress() uint16

GetBDOSAddress returns the address the fake BDOS is deployed at.

This is used for the startup banner.

func (*CPM) GetBIOSAddress added in v0.15.0

func (cpm *CPM) GetBIOSAddress() uint16

GetBIOSAddress returns the address the fake BIOS is deployed at.

This is used for the startup banner.

func (*CPM) GetCCPName added in v0.8.0

func (cpm *CPM) GetCCPName() string

GetCCPName returns the name of the CCP we've been configured to load.

func (*CPM) GetInputDriver added in v0.14.0

func (cpm *CPM) GetInputDriver() consolein.ConsoleInput

GetInputDriver returns the configured input driver.

func (*CPM) GetOutputDriver added in v0.8.0

func (cpm *CPM) GetOutputDriver() consoleout.ConsoleOutput

GetOutputDriver returns the configured output driver.

func (*CPM) IOSetup added in v0.14.0

func (cpm *CPM) IOSetup() error

IOSetup ensures that our I/O is ready.

func (*CPM) IOTearDown added in v0.14.0

func (cpm *CPM) IOTearDown() error

IOTearDown cleans up the state of the terminal, if necessary.

func (*CPM) In added in v0.5.0

func (cpm *CPM) In(addr uint8) uint8

In is called to handle the I/O reading of a Z80 port.

This is called by our embedded Z80 emulator.

func (*CPM) LoadBinary added in v0.3.0

func (cpm *CPM) LoadBinary(filename string) error

LoadBinary loads the given CP/M binary at the default address of 0x0100, where it can then be launched by Execute.

func (*CPM) LoadCCP added in v0.3.0

func (cpm *CPM) LoadCCP() error

LoadCCP loads the CCP into RAM, to be executed instead of an external binary.

This function modifies the "start" attribute, to ensure the CCP is loaded and executed at a higher address than the default of 0x0100.

func (*CPM) LogNoisy added in v0.12.0

func (cpm *CPM) LogNoisy()

LogNoisy enables logging support for each of the functions which would otherwise be disabled

func (*CPM) Out added in v0.5.0

func (cpm *CPM) Out(addr uint8, val uint8)

Out is called to handle the I/O writing to a Z80 port.

This is called by our embedded Z80 emulator, and this will be used by any system which used RST instructions to invoke the CP/M syscalls, rather than using "CALL 0x0005". Notable offenders include Microsoft's BASIC.

The functions called here BIOS functions, NOT BDOS functions.

BDOS functions are implemented in our Execute method, via a lookup of the C register. The functions here are invoked with their number in the A register and there are far far fewer of them.

func (*CPM) RunAutoExec added in v0.9.0

func (cpm *CPM) RunAutoExec()

RunAutoExec is called once, if we're running in CCP-mode, rather than running a simple binary.

If A:SUBMIT.COM and A:AUTOEXEC.SUB exist then we stuff the input-buffer with a command to process them.

func (*CPM) SetDrivePath added in v0.11.0

func (cpm *CPM) SetDrivePath(drive string, path string)

SetDrivePath allows a caller to setup a custom path for a given drive.

func (*CPM) SetDrives added in v0.3.0

func (cpm *CPM) SetDrives(enabled bool)

SetDrives enables/disables the use of subdirectories upon the host system to represent CP/M drives.

We use a map to handle the drive->path mappings, and if directories are not used we just store "." in the appropriate entry.

func (*CPM) SetStaticFilesystem added in v0.9.0

func (cpm *CPM) SetStaticFilesystem(fs embed.FS)

SetStaticFilesystem allows adding a reference to an embedded filesystem.

func (*CPM) StuffText added in v0.13.0

func (cpm *CPM) StuffText(input string)

StuffText inserts text into the read-buffer of the console input-driver.

This is used for two purposes; to drive the "SUBMIT AUTOEXEC" integration at run-time, and to support integration tests to be written.

type CPMHandler

type CPMHandler struct {
	// Desc contain the human-readable name of the given CP/M syscall.
	Desc string

	// Handler contains the function which should be invoked for
	// this syscall.
	Handler CPMHandlerType

	// Fake stores a quick comment on the completeness of the syscall
	// implementation.  If Fake is set to true then the syscall is
	// faked, or otherwise incompletely implemented.
	//
	// This might mean completely bogus behaviour, or it might mean
	// "good enough, even if wrong".
	Fake bool

	// Noisy is set when a given function is noisy, that is it is called
	// a lot and the debugging logs from the function are not so useful.
	//
	// This is primarily used to mark console I/O functions, and disables
	// their logging by default.  Logging these functions is still possible,
	// but requires a call to LogNoisy()
	Noisy bool
}

CPMHandler contains details of a specific call we implement.

While we mostly need a "number to handler", having a name as well is useful for the logs we produce, and we mark those functions that don't do 100% of what they should as "Fake".

type CPMHandlerType

type CPMHandlerType func(cpm *CPM) error

CPMHandlerType contains the signature of a function we use to emulate a CP/M BIOS or BDOS function.

It is not expected that outside packages will want to add custom BIOS functions, or syscalls, but this is public so that it could be done if necessary.

type FileCache added in v0.3.0

type FileCache struct {
	// contains filtered or unexported fields
}

FileCache is used to cache filehandles on the host-side of the system, which have been opened by the CP/M binary/CCP.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL
JackTT - Gopher 🇻🇳