cpm

package
v0.10.0 Latest Latest
Warning

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

Go to latest
Published: Jun 2, 2024 License: MIT Imports: 18 Imported by: 0

Documentation

Overview

Package cpm is the main package for our emulator, it uses memory to emulate execution of things at the bios level.

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 (
	// ErrExit will be used to handle a CP/M binary calling Exit.
	//
	// It should be handled and expected by callers.
	ErrExit = errors.New("EXIT")

	// 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
	// at 0x0000 - i.e. a boot attempt
	//
	// It should be handled and expected by callers.
	ErrBoot = errors.New("BOOT")

	// 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")
)

Functions

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 BiosSysCallBoot added in v0.7.0

func BiosSysCallBoot(cpm *CPM) error

BiosSysCallBoot handles a warm/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. We fake it

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 SysCallAuxRead added in v0.5.0

func SysCallAuxRead(cpm *CPM) error

SysCallAuxRead reads a single character from the auxiliary input.

Note: Echo is not enabled in this function.

func SysCallAuxWrite added in v0.5.0

func SysCallAuxWrite(cpm *CPM) error

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

func SysCallBDOSVersion added in v0.3.0

func SysCallBDOSVersion(cpm *CPM) error

SysCallBDOSVersion returns version details

func SysCallConsoleStatus added in v0.4.0

func SysCallConsoleStatus(cpm *CPM) error

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

func SysCallDeleteFile

func SysCallDeleteFile(cpm *CPM) error

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

func SysCallDirectScreenFunctions added in v0.10.0

func SysCallDirectScreenFunctions(cpm *CPM) error

SysCallDirectScreenFunctions 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 SysCallDriveAllReset added in v0.2.0

func SysCallDriveAllReset(cpm *CPM) error

SysCallDriveAllReset 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 SysCallDriveAlloc added in v0.6.0

func SysCallDriveAlloc(cpm *CPM) error

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

TODO: Fake me better. Right now I just return "random memory".

func SysCallDriveGet

func SysCallDriveGet(cpm *CPM) error

SysCallDriveGet returns the number of the active drive.

func SysCallDriveROVec added in v0.6.0

func SysCallDriveROVec(cpm *CPM) error

SysCallDriveROVec 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 SysCallDriveReset added in v0.8.0

func SysCallDriveReset(cpm *CPM) error

SysCallDriveReset 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 SysCallDriveSet

func SysCallDriveSet(cpm *CPM) error

SysCallDriveSet updates the current drive number.

func SysCallDriveSetRO added in v0.6.0

func SysCallDriveSetRO(cpm *CPM) error

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

This call is faked.

func SysCallExit

func SysCallExit(cpm *CPM) error

SysCallExit implements the Exit syscall

func SysCallFileClose

func SysCallFileClose(cpm *CPM) error

SysCallFileClose 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 SysCallFileOpen

func SysCallFileOpen(cpm *CPM) error

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

func SysCallFileSize added in v0.6.0

func SysCallFileSize(cpm *CPM) error

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

Returns the result in the A record

func SysCallFindFirst

func SysCallFindFirst(cpm *CPM) error

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

func SysCallFindNext

func SysCallFindNext(cpm *CPM) error

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

func SysCallGetDriveDPB

func SysCallGetDriveDPB(cpm *CPM) error

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

func SysCallGetIOByte added in v0.5.0

func SysCallGetIOByte(cpm *CPM) error

SysCallGetIOByte 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 SysCallLoginVec added in v0.3.0

func SysCallLoginVec(cpm *CPM) error

SysCallLoginVec returns the list of logged in drives.

func SysCallMakeFile

func SysCallMakeFile(cpm *CPM) error

SysCallMakeFile creates the file named in the FCB given in DE

func SysCallPrinterWrite added in v0.6.0

func SysCallPrinterWrite(cpm *CPM) error

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

func SysCallRawIO

func SysCallRawIO(cpm *CPM) error

SysCallRawIO handles both simple character output, and input.

func SysCallRead added in v0.2.0

func SysCallRead(cpm *CPM) error

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

func SysCallReadChar

func SysCallReadChar(cpm *CPM) error

SysCallReadChar reads a single character from the console.

func SysCallReadRand

func SysCallReadRand(cpm *CPM) error

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

func SysCallReadString

func SysCallReadString(cpm *CPM) error

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

func SysCallRenameFile added in v0.4.0

func SysCallRenameFile(cpm *CPM) error

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

func SysCallSetDMA added in v0.3.0

func SysCallSetDMA(cpm *CPM) error

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

func SysCallSetFileAttributes added in v0.6.0

func SysCallSetFileAttributes(cpm *CPM) error

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

func SysCallSetIOByte added in v0.5.0

func SysCallSetIOByte(cpm *CPM) error

SysCallSetIOByte 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 SysCallTime added in v0.6.0

func SysCallTime(cpm *CPM) error

func SysCallUserNumber

func SysCallUserNumber(cpm *CPM) error

SysCallUserNumber gets, or sets, the user-number.

func SysCallWrite added in v0.2.0

func SysCallWrite(cpm *CPM) error

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

func SysCallWriteChar

func SysCallWriteChar(cpm *CPM) error

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

func SysCallWriteRand added in v0.3.0

func SysCallWriteRand(cpm *CPM) error

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

func SysCallWriteString

func SysCallWriteString(cpm *CPM) error

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

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

	// Logger holds a logger which we use for debugging and diagnostics.
	Logger *slog.Logger
	// contains filtered or unexported fields
}

CPM is the object that holds our emulator state.

func New

func New(logger *slog.Logger, prn string, condriver string, ccp string) (*CPM, error)

New returns a new emulation object

func (*CPM) BiosHandler added in v0.7.0

func (cpm *CPM) BiosHandler(val uint8)

BiosHandler is involved when a BIOS syscall needs to be executed, which is handled via a small trampoline.

These are looked up in the BIOSSyscalls map.

func (*CPM) Cleanup added in v0.7.0

func (cpm *CPM) Cleanup()

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

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) 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) GetOutputDriver added in v0.8.0

func (cpm *CPM) GetOutputDriver() string

GetOutputDriver returns the name of our configured output driver.

func (*CPM) GetQuiet added in v0.9.0

func (cpm *CPM) GetQuiet() bool

GetQuiet returns the status of the quiet-flag.

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) 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) 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) SetQuiet added in v0.9.0

func (cpm *CPM) SetQuiet(state bool)

SetQuiet updates the state of the quiet-flag.

func (*CPM) SetStaticFilesystem added in v0.9.0

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

SetStaticFilesystem allows adding a reference to an embedded filesyste,.

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
}

CPMHandler contains details of a specific call we implement.

While we mostly need a "number to handler", mapping having a name 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, against FCB addresses.

This is primarily done as a speed optimization.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL