Skip to content

Commit

Permalink
Merge pull request #93 from skx/embed
Browse files Browse the repository at this point in the history
Simplify our embedding of the CCPs
  • Loading branch information
skx authored May 25, 2024
2 parents baf8f1b + 35f6301 commit 83cefc0
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 37 deletions.
38 changes: 26 additions & 12 deletions ccp/ccp.go
Original file line number Diff line number Diff line change
@@ -1,19 +1,24 @@
// Package ccp contains a pair of embedded CCP binaries, which can
// be used by the emulator as shells.
//
// At build-time we include "*.BIN" from the ccp/ directory, which
// means it's easy to add a new CCP driver - however we must also
// ensure there is a matching name-entry added to the code, so it isn't
// 100% automatic.
package ccp

import (
_ "embed"
"embed"
"fmt"
"strings"
)

// ccps contains the global array of the CCP variants we have.
var ccps []Flavour

// Flavour contains details about a possible CCP the user might run.
type Flavour struct {
// Name has the name of the CCP.
//
// NOTE: This name is visible to end-users, and will be used in the "-ccp" command-line flag,
// or as the name when changing at run-time via the "CCP.COM" utility.
Name string

// Description has the description of the CCP.
Expand All @@ -26,27 +31,34 @@ type Flavour struct {
Start uint16
}

//go:embed DR.BIN
var ccpBin []uint8
var (
// ccps contains the global array of the CCP variants we have.
ccps []Flavour

//go:embed CCPZ.BIN
var ccpzBin []uint8
//go:embed *.BIN
ccpFiles embed.FS
)

// init sets up our global ccp array, by adding the two embedded CCPs to
// the array, with suitable names/offsets.
func init() {

// Load the CCP from DR
ccp, _ := ccpFiles.ReadFile("DR.BIN")
ccps = append(ccps, Flavour{
Name: "ccp",
Description: "CP/M v2.2",
Description: "CP/M v2.2skx",
Start: 0xDE00,
Bytes: ccpBin,
Bytes: ccp,
})

// Load the alternative CCP
ccpz, _ := ccpFiles.ReadFile("CCPZ.BIN")
ccps = append(ccps, Flavour{
Name: "ccpz",
Description: "CCPZ v4.1skx",
Start: 0xDE00,
Bytes: ccpzBin,
Bytes: ccpz,
})
}

Expand All @@ -57,13 +69,15 @@ func GetAll() []Flavour {

// Get returns the CCP version specified, by name, if it exists.
//
// If the given name is invalid then an error will be returned.
// If the given name is invalid then an error will be returned instead.
func Get(name string) (Flavour, error) {

valid := []string{}

for _, ent := range ccps {

// When changing at runtime, via "CCP.COM", we will have had
// the name upper-cased by the CCP so we need to downcase here.
if strings.ToLower(name) == ent.Name {
return ent, nil
}
Expand Down
2 changes: 1 addition & 1 deletion ccp/ccp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func TestCCPTrivial(t *testing.T) {

// The CCPs are small, but bigger than 1k and smaller than 8k
if len(bytes) < 1024 {
t.Fatalf("CCP %s is too small", n.Name)
t.Fatalf("CCP %s is too small got %d bytes", n.Name, len(bytes))
}

if len(bytes) > 8192 {
Expand Down
68 changes: 44 additions & 24 deletions cpm/cpm_bios.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,27 @@ func BiosSysCallReserved1(cpm *CPM) error {
c := cpm.CPU.States.BC.Lo
de := cpm.CPU.States.DE.U16()

//
// Helper to read a null/space terminated string from
// memory.
//
// Here because our custom syscalls read a string when
// setting both CCP and DisplayDriver.
//
getStringFromMemory := func(addr uint16) string {
str := ""
c := cpm.Memory.Get(addr)
for c != ' ' && c != 0x00 {
str += string(c)
addr++
c = cpm.Memory.Get(addr)
}

// Useful when the CCP has passed a string, because
// that uppercases all input
return strings.ToLower(str)
}

switch hl {
case 0x0000:
// Magic values in the registers
Expand All @@ -209,6 +230,7 @@ func BiosSysCallReserved1(cpm *CPM) error {
end--
}

// now populate with our name/version/information
for i, c := range vers {
cpm.Memory.Set(addr+uint16(i), uint8(c))
}
Expand All @@ -220,17 +242,10 @@ func BiosSysCallReserved1(cpm *CPM) error {
} else {
cpm.input.SetInterruptCount(int(c))
}
case 0x0002:
str := ""

c := cpm.Memory.Get(de)
for c != ' ' && c != 0x00 {
str += string(c)
de++
c = cpm.Memory.Get(de)
}

str = strings.ToLower(str)
case 0x0002:
// Get the string pointed to by DE
str := getStringFromMemory(de)

// Output driver needs to be created
driver, err := consoleout.New(str)
Expand All @@ -243,41 +258,46 @@ func BiosSysCallReserved1(cpm *CPM) error {
}

old := cpm.output.GetName()
cpm.output = driver

// when running quietly don't show any output
if cpm.quiet {
return nil
}

if old != str {
fmt.Printf("Console driver changed from %s to %s.\n", cpm.output.GetName(), driver.GetName())
cpm.output = driver
} else {
fmt.Printf("console driver is already %s, making no change.\n", str)
}
case 0x0003:
str := ""

c := cpm.Memory.Get(de)
for c != ' ' && c != 0x00 {
str += string(c)
de++
c = cpm.Memory.Get(de)
}
case 0x0003:

// CP/M will upper-case command-lines (and therefor their arguments)
str = strings.ToLower(str)
// Get the string pointed to by DE
str := getStringFromMemory(de)

// See if the CCP exists
_, err := ccp.Get(str)
entry, err := ccp.Get(str)
if err != nil {
fmt.Printf("Invalid CCP name %s\n", str)
return nil
}

// old value
old := cpm.ccp
cpm.ccp = str

// when running quietly don't show any output
if cpm.quiet {
return nil
}

if old != str {
fmt.Printf("CCP changed from %s to %s.\n", old, str)
cpm.ccp = str
fmt.Printf("CCP changed to %s [%s] Size:0x%04X Entry-Point:0x%04X\n", str, entry.Description, len(entry.Bytes), entry.Start)
} else {
fmt.Printf("CCP is already %s, making no change.\n", str)
}

case 0x0004:
if c == 0x00 {
cpm.quiet = true
Expand Down

0 comments on commit 83cefc0

Please sign in to comment.