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 ¶
- Variables
- func BiosSysCallAuxInputStatus(cpm *CPM) error
- func BiosSysCallAuxOutputStatus(cpm *CPM) error
- func BiosSysCallBoot(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 SysCallAuxRead(cpm *CPM) error
- func SysCallAuxWrite(cpm *CPM) error
- func SysCallBDOSVersion(cpm *CPM) error
- func SysCallConsoleStatus(cpm *CPM) error
- func SysCallDeleteFile(cpm *CPM) error
- func SysCallDirectScreenFunctions(cpm *CPM) error
- func SysCallDriveAllReset(cpm *CPM) error
- func SysCallDriveAlloc(cpm *CPM) error
- func SysCallDriveGet(cpm *CPM) error
- func SysCallDriveROVec(cpm *CPM) error
- func SysCallDriveReset(cpm *CPM) error
- func SysCallDriveSet(cpm *CPM) error
- func SysCallDriveSetRO(cpm *CPM) error
- func SysCallExit(cpm *CPM) error
- func SysCallFileClose(cpm *CPM) error
- func SysCallFileOpen(cpm *CPM) error
- func SysCallFileSize(cpm *CPM) error
- func SysCallFindFirst(cpm *CPM) error
- func SysCallFindNext(cpm *CPM) error
- func SysCallGetDriveDPB(cpm *CPM) error
- func SysCallGetIOByte(cpm *CPM) error
- func SysCallLoginVec(cpm *CPM) error
- func SysCallMakeFile(cpm *CPM) error
- func SysCallPrinterWrite(cpm *CPM) error
- func SysCallRawIO(cpm *CPM) error
- func SysCallRead(cpm *CPM) error
- func SysCallReadChar(cpm *CPM) error
- func SysCallReadRand(cpm *CPM) error
- func SysCallReadString(cpm *CPM) error
- func SysCallRenameFile(cpm *CPM) error
- func SysCallSetDMA(cpm *CPM) error
- func SysCallSetFileAttributes(cpm *CPM) error
- func SysCallSetIOByte(cpm *CPM) error
- func SysCallTime(cpm *CPM) error
- func SysCallUserNumber(cpm *CPM) error
- func SysCallWrite(cpm *CPM) error
- func SysCallWriteChar(cpm *CPM) error
- func SysCallWriteRand(cpm *CPM) error
- func SysCallWriteString(cpm *CPM) error
- type CPM
- func (cpm *CPM) BiosHandler(val uint8)
- func (cpm *CPM) Cleanup()
- func (cpm *CPM) Execute(args []string) error
- func (cpm *CPM) GetCCPName() string
- func (cpm *CPM) GetOutputDriver() string
- func (cpm *CPM) GetQuiet() bool
- func (cpm *CPM) In(addr uint8) uint8
- func (cpm *CPM) LoadBinary(filename string) error
- func (cpm *CPM) LoadCCP() error
- func (cpm *CPM) Out(addr uint8, val uint8)
- func (cpm *CPM) RunAutoExec()
- func (cpm *CPM) SetDrives(enabled bool)
- func (cpm *CPM) SetQuiet(state bool)
- func (cpm *CPM) SetStaticFilesystem(fs embed.FS)
- type CPMHandler
- type CPMHandlerType
- type FileCache
Constants ¶
This section is empty.
Variables ¶
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
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 BiosSysCallBoot ¶ added in v0.7.0
BiosSysCallBoot handles a warm/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. We fake it
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 SysCallAuxRead ¶ added in v0.5.0
SysCallAuxRead reads a single character from the auxiliary input.
Note: Echo is not enabled in this function.
func SysCallAuxWrite ¶ added in v0.5.0
SysCallAuxWrite writes the single character in the C register auxiliary / punch output.
func SysCallBDOSVersion ¶ added in v0.3.0
SysCallBDOSVersion returns version details
func SysCallConsoleStatus ¶ added in v0.4.0
SysCallConsoleStatus tests if we have pending console (character) input.
func SysCallDeleteFile ¶
SysCallDeleteFile deletes the filename(s) matching the pattern specified by the FCB in DE.
func SysCallDirectScreenFunctions ¶ added in v0.10.0
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
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
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 ¶
SysCallDriveGet returns the number of the active drive.
func SysCallDriveROVec ¶ added in v0.6.0
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
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 ¶
SysCallDriveSet updates the current drive number.
func SysCallDriveSetRO ¶ added in v0.6.0
SysCallDriveSetRO will mark the current drive as being read-only.
This call is faked.
func SysCallFileClose ¶
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 ¶
SysCallFileOpen opens the filename that matches the pattern on the FCB supplied in DE
func SysCallFileSize ¶ added in v0.6.0
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 ¶
SysCallFindFirst finds the first filename, on disk, that matches the glob in the FCB supplied in DE.
func SysCallFindNext ¶
SysCallFindNext finds the next filename that matches the glob set in the FCB in DE.
func SysCallGetDriveDPB ¶
SysCallGetDriveDPB returns the address of the DPB, which is faked.
func SysCallGetIOByte ¶ added in v0.5.0
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
SysCallLoginVec returns the list of logged in drives.
func SysCallMakeFile ¶
SysCallMakeFile creates the file named in the FCB given in DE
func SysCallPrinterWrite ¶ added in v0.6.0
SysCallPrinterWrite should send a single character to the printer, we fake that by writing to a file instead.
func SysCallRawIO ¶
SysCallRawIO handles both simple character output, and input.
func SysCallRead ¶ added in v0.2.0
SysCallRead reads a record from the file named in the FCB given in DE
func SysCallReadChar ¶
SysCallReadChar reads a single character from the console.
func SysCallReadRand ¶
SysCallReadRand reads a random block from the FCB pointed to by DE into the DMA area.
func SysCallReadString ¶
SysCallReadString reads a string from the console, into the buffer pointed to by DE.
func SysCallRenameFile ¶ added in v0.4.0
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
SysCallSetDMA updates the address of the DMA area, which is used for block I/O.
func SysCallSetFileAttributes ¶ added in v0.6.0
SysCallSetFileAttributes should update the attributes of the given file, but it fakes it.
func SysCallSetIOByte ¶ added in v0.5.0
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 SysCallUserNumber ¶
SysCallUserNumber gets, or sets, the user-number.
func SysCallWrite ¶ added in v0.2.0
SysCallWrite writes a record to the file named in the FCB given in DE
func SysCallWriteChar ¶
SysCallWriteChar writes the single character in the E register to STDOUT.
func SysCallWriteRand ¶ added in v0.3.0
SysCallWriteRand writes a random block from DMA area to the FCB pointed to by DE.
func SysCallWriteString ¶
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 (*CPM) BiosHandler ¶ added in v0.7.0
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 ¶
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
GetCCPName returns the name of the CCP we've been configured to load.
func (*CPM) GetOutputDriver ¶ added in v0.8.0
GetOutputDriver returns the name of our configured output driver.
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) 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) 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 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 ¶
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.