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 ¶
- Variables
- func BdosSysCallAuxRead(cpm *CPM) error
- func BdosSysCallAuxWrite(cpm *CPM) error
- func BdosSysCallBDOSVersion(cpm *CPM) error
- func BdosSysCallConsoleStatus(cpm *CPM) error
- func BdosSysCallDeleteFile(cpm *CPM) error
- func BdosSysCallDirectScreenFunctions(cpm *CPM) error
- func BdosSysCallDriveAllReset(cpm *CPM) error
- func BdosSysCallDriveAlloc(cpm *CPM) error
- func BdosSysCallDriveGet(cpm *CPM) error
- func BdosSysCallDriveROVec(cpm *CPM) error
- func BdosSysCallDriveReset(cpm *CPM) error
- func BdosSysCallDriveSet(cpm *CPM) error
- func BdosSysCallDriveSetRO(cpm *CPM) error
- func BdosSysCallErrorMode(cpm *CPM) error
- func BdosSysCallExit(cpm *CPM) error
- func BdosSysCallFileClose(cpm *CPM) error
- func BdosSysCallFileOpen(cpm *CPM) error
- func BdosSysCallFileSize(cpm *CPM) error
- func BdosSysCallFindFirst(cpm *CPM) error
- func BdosSysCallFindNext(cpm *CPM) error
- func BdosSysCallGetDriveDPB(cpm *CPM) error
- func BdosSysCallGetIOByte(cpm *CPM) error
- func BdosSysCallLoginVec(cpm *CPM) error
- func BdosSysCallMakeFile(cpm *CPM) error
- func BdosSysCallPrinterWrite(cpm *CPM) error
- func BdosSysCallRandRecord(cpm *CPM) error
- func BdosSysCallRawIO(cpm *CPM) error
- func BdosSysCallRead(cpm *CPM) error
- func BdosSysCallReadChar(cpm *CPM) error
- func BdosSysCallReadRand(cpm *CPM) error
- func BdosSysCallReadString(cpm *CPM) error
- func BdosSysCallRenameFile(cpm *CPM) error
- func BdosSysCallSetDMA(cpm *CPM) error
- func BdosSysCallSetFileAttributes(cpm *CPM) error
- func BdosSysCallSetIOByte(cpm *CPM) error
- func BdosSysCallTime(cpm *CPM) error
- func BdosSysCallUptime(cpm *CPM) error
- func BdosSysCallUserNumber(cpm *CPM) error
- func BdosSysCallWrite(cpm *CPM) error
- func BdosSysCallWriteChar(cpm *CPM) error
- func BdosSysCallWriteRand(cpm *CPM) error
- func BdosSysCallWriteString(cpm *CPM) error
- func BiosSysCallAuxInputStatus(cpm *CPM) error
- func BiosSysCallAuxOutputStatus(cpm *CPM) error
- func BiosSysCallColdBoot(cpm *CPM) error
- func BiosSysCallConsoleInput(cpm *CPM) error
- func BiosSysCallConsoleOutput(cpm *CPM) error
- func BiosSysCallConsoleStatus(cpm *CPM) error
- func BiosSysCallPrintChar(cpm *CPM) error
- func BiosSysCallPrinterStatus(cpm *CPM) error
- func BiosSysCallReserved1(cpm *CPM) error
- func BiosSysCallScreenOutputStatus(cpm *CPM) error
- func BiosSysCallWarmBoot(cpm *CPM) error
- func WithCCP(name string) cpmoption
- func WithContext(ctx context.Context) cpmoption
- func WithHostExec(prefix string) cpmoption
- func WithInputDriver(name string) cpmoption
- func WithOutputDriver(name string) cpmoption
- func WithPrinterPath(path string) cpmoption
- type CPM
- func (cpm *CPM) Execute(args []string) error
- func (cpm *CPM) GetBDOSAddress() uint16
- func (cpm *CPM) GetBIOSAddress() uint16
- func (cpm *CPM) GetCCPName() string
- func (cpm *CPM) GetInputDriver() consolein.ConsoleInput
- func (cpm *CPM) GetOutputDriver() consoleout.ConsoleOutput
- func (cpm *CPM) IOSetup() error
- func (cpm *CPM) IOTearDown() error
- func (cpm *CPM) In(addr uint8) uint8
- func (cpm *CPM) LoadBinary(filename string) error
- func (cpm *CPM) LoadCCP() error
- func (cpm *CPM) LogNoisy()
- func (cpm *CPM) Out(addr uint8, val uint8)
- func (cpm *CPM) RunAutoExec()
- func (cpm *CPM) SetDrivePath(drive string, path string)
- func (cpm *CPM) SetDrives(enabled bool)
- func (cpm *CPM) SetStaticFilesystem(fs embed.FS)
- func (cpm *CPM) StuffText(input string)
- type CPMHandler
- type CPMHandlerType
- type FileCache
Constants ¶
This section is empty.
Variables ¶
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
BdosSysCallAuxRead reads a single character from the auxiliary input.
Note: Echo is not enabled in this function.
func BdosSysCallAuxWrite ¶ added in v0.11.0
BdosSysCallAuxWrite writes the single character in the C register auxiliary / punch output.
func BdosSysCallBDOSVersion ¶ added in v0.11.0
BdosSysCallBDOSVersion returns version details
func BdosSysCallConsoleStatus ¶ added in v0.11.0
BdosSysCallConsoleStatus tests if we have pending console (character) input.
func BdosSysCallDeleteFile ¶ added in v0.11.0
BdosSysCallDeleteFile deletes the filename(s) matching the pattern specified by the FCB in DE.
func BdosSysCallDirectScreenFunctions ¶ added in v0.11.0
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
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
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
BdosSysCallDriveGet returns the number of the active drive.
func BdosSysCallDriveROVec ¶ added in v0.11.0
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
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
BdosSysCallDriveSet updates the current drive number.
func BdosSysCallDriveSetRO ¶ added in v0.11.0
BdosSysCallDriveSetRO will mark the current drive as being read-only.
This call is faked.
func BdosSysCallErrorMode ¶ added in v0.11.0
BdosSysCallErrorMode implements a NOP version of F_ERRMODE.
func BdosSysCallExit ¶ added in v0.11.0
BdosSysCallExit implements the Exit syscall
func BdosSysCallFileClose ¶ added in v0.11.0
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
BdosSysCallFileOpen opens the filename that matches the pattern on the FCB supplied in DE
func BdosSysCallFileSize ¶ added in v0.11.0
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
BdosSysCallFindFirst finds the first filename, on disk, that matches the glob in the FCB supplied in DE.
func BdosSysCallFindNext ¶ added in v0.11.0
BdosSysCallFindNext finds the next filename that matches the glob set in the FCB in DE.
func BdosSysCallGetDriveDPB ¶ added in v0.11.0
BdosSysCallGetDriveDPB returns the address of the DPB, which is faked.
func BdosSysCallGetIOByte ¶ added in v0.11.0
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
BdosSysCallLoginVec returns the list of logged in drives.
func BdosSysCallMakeFile ¶ added in v0.11.0
BdosSysCallMakeFile creates the file named in the FCB given in DE
func BdosSysCallPrinterWrite ¶ added in v0.11.0
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
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
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
BdosSysCallRead reads a record from the file named in the FCB given in DE
func BdosSysCallReadChar ¶ added in v0.11.0
BdosSysCallReadChar reads a single character from the console.
func BdosSysCallReadRand ¶ added in v0.11.0
BdosSysCallReadRand reads a random block from the FCB pointed to by DE into the DMA area.
func BdosSysCallReadString ¶ added in v0.11.0
BdosSysCallReadString reads a string from the console, into the buffer pointed to by DE.
func BdosSysCallRenameFile ¶ added in v0.11.0
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
BdosSysCallSetDMA updates the address of the DMA area, which is used for block I/O.
func BdosSysCallSetFileAttributes ¶ added in v0.11.0
BdosSysCallSetFileAttributes should update the attributes of the given file, but it fakes it.
func BdosSysCallSetIOByte ¶ added in v0.11.0
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
BdosSysCallTime implements a NOP version of T_GET.
func BdosSysCallUptime ¶ added in v0.14.0
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
BdosSysCallUserNumber gets, or sets, the user-number.
func BdosSysCallWrite ¶ added in v0.11.0
BdosSysCallWrite writes a record to the file named in the FCB given in DE
func BdosSysCallWriteChar ¶ added in v0.11.0
BdosSysCallWriteChar writes the single character in the E register to STDOUT.
func BdosSysCallWriteRand ¶ added in v0.11.0
BdosSysCallWriteRand writes a random block from DMA area to the FCB pointed to by DE.
func BdosSysCallWriteString ¶ added in v0.11.0
BdosSysCallWriteString writes the $-terminated string pointed to by DE to STDOUT
func BiosSysCallAuxInputStatus ¶ added in v0.7.0
BiosSysCallAuxInputStatus returns status of current auxiliary input device.
This is fake, and always returns "ready".
func BiosSysCallAuxOutputStatus ¶ added in v0.7.0
BiosSysCallAuxOutputStatus returns status of current auxiliary output device.
This is fake, and always returns "ready".
func BiosSysCallColdBoot ¶ added in v0.11.0
BiosSysCallColdBoot handles a cold boot.
func BiosSysCallConsoleInput ¶ added in v0.7.0
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
BiosSysCallConsoleOutput should write a single character, in the C-register, to the console.
func BiosSysCallConsoleStatus ¶ added in v0.7.0
BiosSysCallConsoleStatus should return 0x00 if there is no input pending, otherwise 0xFF.
func BiosSysCallPrintChar ¶ added in v0.7.0
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
BiosSysCallPrinterStatus returns status of current printer device.
This is fake, and always returns "ready".
func BiosSysCallReserved1 ¶ added in v0.8.0
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
BiosSysCallScreenOutputStatus returns status of current screen output device.
This is fake, and always returns "ready".
func BiosSysCallWarmBoot ¶ added in v0.11.0
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
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 ¶
New returns a new emulation object. We support default options, and new defaults may be specified via WithOutputDriver, etc, etc.
func (*CPM) Execute ¶
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
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
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
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) IOTearDown ¶ added in v0.14.0
IOTearDown cleans up the state of the terminal, if necessary.
func (*CPM) In ¶ added in v0.5.0
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
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
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
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
SetDrivePath allows a caller to setup a custom path for a given drive.
func (*CPM) SetDrives ¶ added in v0.3.0
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
SetStaticFilesystem allows adding a reference to an embedded filesystem.
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 ¶
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.