Skip to content

Commit c9cdd10

Browse files
authored
Merge pull request #237 from skx/236-cache
Update to avoid modifying the FCB when file open/made
2 parents ff2314d + 215a572 commit c9cdd10

4 files changed

Lines changed: 62 additions & 50 deletions

File tree

cpm/cpm.go

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -130,12 +130,12 @@ type CPM struct {
130130
// ccp contains the name of the CCP we should load
131131
ccp string
132132

133-
// files is the cache we use for File handles.
133+
// files is the cache we use for File handles - to avoid having to open
134+
// close files on the host-side during every operation.
134135
//
135-
// NOTE: The key is the address of the FCB which we assume is static, but
136-
// I'm increasingly of the opinion this is wrong - we should cache on the
137-
// filepath.
138-
files map[uint16]FileCache
136+
// The key is the name of the CP/M file, inside the guest. (i.e. "FOO.BAR"
137+
// rather than A/FOO.BAR which might be the ultimate path on the host.)
138+
files map[string]FileCache
139139

140140
// virtual contains a reference to a static filesystem which
141141
// is embedded within our binary, if any.
@@ -623,7 +623,7 @@ func New(options ...Option) (*CPM, error) {
623623
ccp: DefaultCCP,
624624
dma: DefaultDMAAddress,
625625
drives: make(map[string]string),
626-
files: make(map[uint16]FileCache),
626+
files: make(map[string]FileCache),
627627
input: iDriver, // default
628628
output: oDriver, // default
629629
prnPath: DefaultPrinterPath,
@@ -894,13 +894,13 @@ func (cpm *CPM) Execute(args []string) error {
894894
// Reset any cached filehandles.
895895
//
896896
// This is only required when running the CCP, as there we're persistent.
897-
for fcb, obj := range cpm.files {
897+
for name, obj := range cpm.files {
898898
slog.Debug("Closing handle in FileCache",
899-
slog.String("path", obj.name),
900-
slog.Int("fcb", int(fcb)))
899+
slog.String("host", obj.name),
900+
slog.String("guest", name))
901901
obj.handle.Close()
902902
}
903-
cpm.files = make(map[uint16]FileCache)
903+
cpm.files = make(map[string]FileCache)
904904

905905
// Create the CPU, pointing to our memory, and setting the initial program counter
906906
// to point to our expected entry-point.

cpm/cpm_bdos.go

Lines changed: 10 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,7 @@ func BdosSysCallFileOpen(cpm *CPM) error {
394394

395395
// Yes we can!
396396
// Save the file handle in our cache.
397-
cpm.files[ptr] = FileCache{name: fileName, handle: nil}
397+
cpm.files[fcbPtr.GetCacheKey()] = FileCache{name: fileName, handle: nil}
398398

399399
// Get file size, in blocks
400400
fLen := uint8(len(virt) / blkSize)
@@ -405,10 +405,6 @@ func BdosSysCallFileOpen(cpm *CPM) error {
405405
fcbPtr.RC = fLen
406406
}
407407

408-
// Write our cache-key in the FCB
409-
fcbPtr.Al[0] = uint8(ptr & 0xFF)
410-
fcbPtr.Al[1] = uint8(ptr >> 8)
411-
412408
// Update the FCB in memory.
413409
cpm.Memory.SetRange(ptr, fcbPtr.AsBytes()...)
414410

@@ -443,7 +439,7 @@ func BdosSysCallFileOpen(cpm *CPM) error {
443439
}
444440

445441
// Save the file handle in our cache.
446-
cpm.files[ptr] = FileCache{name: fileName, handle: file}
442+
cpm.files[fcbPtr.GetCacheKey()] = FileCache{name: fileName, handle: file}
447443

448444
// Get file size, in bytes
449445
fi, err := file.Stat()
@@ -469,10 +465,6 @@ func BdosSysCallFileOpen(cpm *CPM) error {
469465
slog.Int("record_count", int(fcbPtr.RC)),
470466
slog.Int64("file_size", fileSize))
471467

472-
// Write our cache-key in the FCB
473-
fcbPtr.Al[0] = uint8(ptr & 0xFF)
474-
fcbPtr.Al[1] = uint8(uint16(ptr >> 8))
475-
476468
// Update the FCB in memory.
477469
cpm.Memory.SetRange(ptr, fcbPtr.AsBytes()...)
478470

@@ -498,14 +490,11 @@ func BdosSysCallFileClose(cpm *CPM) error {
498490
// Create a structure with the contents
499491
fcbPtr := fcb.FromBytes(xxx)
500492

501-
// Get our cache-key from the FCB
502-
key := uint16(uint16(fcbPtr.Al[1])<<8 + uint16(fcbPtr.Al[0]))
503-
504493
// Get the file handle from our cache.
505-
obj, ok := cpm.files[key]
494+
obj, ok := cpm.files[fcbPtr.GetCacheKey()]
506495
if !ok {
507496
slog.Debug("SysCallFileClose tried to close a file that wasn't open",
508-
slog.Int("fcb", int(ptr)))
497+
slog.String("guest", fcbPtr.GetCacheKey()))
509498
cpm.CPU.States.HL.SetU16(0x00FF)
510499
return nil
511500
}
@@ -545,11 +534,8 @@ func BdosSysCallFileClose(cpm *CPM) error {
545534
}
546535

547536
// delete the entry from the cache.
548-
delete(cpm.files, key)
537+
delete(cpm.files, fcbPtr.GetCacheKey())
549538

550-
// Update the FCB in RAM
551-
fcbPtr.Al[0] = 0x00
552-
fcbPtr.Al[1] = 0x00
553539
cpm.Memory.SetRange(ptr, fcbPtr.AsBytes()...)
554540

555541
// Record success
@@ -768,11 +754,8 @@ func BdosSysCallRead(cpm *CPM) error {
768754
// Create a structure with the contents
769755
fcbPtr := fcb.FromBytes(xxx)
770756

771-
// Get our cache-key from the FCB
772-
key := uint16(uint16(fcbPtr.Al[1])<<8 + uint16(fcbPtr.Al[0]))
773-
774757
// Get the file handle in our cache.
775-
obj, ok := cpm.files[key]
758+
obj, ok := cpm.files[fcbPtr.GetCacheKey()]
776759
if !ok {
777760
slog.Error("SysCallRead: Attempting to read from a file that isn't open",
778761
slog.String("filename", fcbPtr.GetFileName()))
@@ -883,11 +866,8 @@ func BdosSysCallWrite(cpm *CPM) error {
883866
// Create a structure with the contents
884867
fcbPtr := fcb.FromBytes(xxx)
885868

886-
// Get our cache-key from the FCB
887-
key := uint16(uint16(fcbPtr.Al[1])<<8 + uint16(fcbPtr.Al[0]))
888-
889869
// Get the file handle in our cache.
890-
obj, ok := cpm.files[key]
870+
obj, ok := cpm.files[fcbPtr.GetCacheKey()]
891871
if !ok {
892872
slog.Error("SysCallWrite: Attempting to write to a file that isn't open")
893873
cpm.CPU.States.HL.SetU16(0x00FF)
@@ -1028,12 +1008,8 @@ func BdosSysCallMakeFile(cpm *CPM) error {
10281008
fcbPtr.RC = fLen
10291009
}
10301010

1031-
// Write our cache-key in the FCB
1032-
fcbPtr.Al[0] = uint8(ptr & 0xFF)
1033-
fcbPtr.Al[1] = uint8(ptr >> 8)
1034-
10351011
// Save the file-handle
1036-
cpm.files[ptr] = FileCache{name: fileName, handle: file}
1012+
cpm.files[fcbPtr.GetCacheKey()] = FileCache{name: fileName, handle: file}
10371013

10381014
l.Debug("result:OK",
10391015
slog.Int("fcb", int(ptr)),
@@ -1264,11 +1240,8 @@ func BdosSysCallReadRand(cpm *CPM) error {
12641240
// Create a structure with the contents
12651241
fcbPtr := fcb.FromBytes(xxx)
12661242

1267-
// Get our cache-key from the FCB
1268-
key := uint16(uint16(fcbPtr.Al[1])<<8 + uint16(fcbPtr.Al[0]))
1269-
12701243
// Get the file handle in our cache.
1271-
obj, ok := cpm.files[key]
1244+
obj, ok := cpm.files[fcbPtr.GetCacheKey()]
12721245
if !ok {
12731246
slog.Error("SysCallReadRand: Attempting to read from a file that isn't open",
12741247
slog.String("filename", fcbPtr.GetFileName()))
@@ -1361,11 +1334,8 @@ func BdosSysCallWriteRand(cpm *CPM) error {
13611334
// Create a structure with the contents
13621335
fcbPtr := fcb.FromBytes(xxx)
13631336

1364-
// Get our cache-key from the FCB
1365-
key := uint16(uint16(fcbPtr.Al[1])<<8 + uint16(fcbPtr.Al[0]))
1366-
13671337
// Get the file handle in our cache.
1368-
obj, ok := cpm.files[key]
1338+
obj, ok := cpm.files[fcbPtr.GetCacheKey()]
13691339
if !ok {
13701340
slog.Error("SysCallWriteRand: Attempting to write to a file that isn't open")
13711341
cpm.CPU.States.HL.SetU16(0x00FF)

fcb/fcb.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,33 @@ func (f *FCB) GetFileName() string {
115115
return strings.TrimSpace(name)
116116
}
117117

118+
// GetCacheKey returns a string which can be used for caching this
119+
// object in some way - it's the name of the file, as seen by the
120+
// CP/M system.
121+
func (f *FCB) GetCacheKey() string {
122+
t := ""
123+
124+
// Name
125+
for _, c := range f.Name {
126+
if unicode.IsPrint(rune(c)) {
127+
t += string(c)
128+
} else {
129+
t += " "
130+
}
131+
}
132+
133+
// Suffix
134+
for _, c := range f.Type {
135+
if unicode.IsPrint(rune(c)) {
136+
t += string(c)
137+
} else {
138+
t += " "
139+
}
140+
}
141+
return t
142+
143+
}
144+
118145
// AsBytes returns the entry of the FCB in a format suitable
119146
// for copying to RAM.
120147
func (f *FCB) AsBytes() []uint8 {

fcb/fcb_test.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ func TestFCBFromString(t *testing.T) {
7878
if f.GetType() != " " {
7979
t.Fatalf("unexpected suffix '%v'", f.GetType())
8080
}
81+
if f.GetCacheKey() != "FOO " {
82+
t.Fatalf("name wrong, got '%v'", f.GetCacheKey())
83+
}
8184

8285
// Try a long name, to confirm it is truncated
8386
f = FromString("c:this-is-a-long-name")
@@ -105,6 +108,9 @@ func TestFCBFromString(t *testing.T) {
105108
if f.GetFileName() != "THIS-IS-.LON" {
106109
t.Fatalf("wrong name returned, got %v", f.GetFileName())
107110
}
111+
if f.GetCacheKey() != "THIS-IS-LON" {
112+
t.Fatalf("wrong cache returned, got %v", f.GetCacheKey())
113+
}
108114

109115
// wildcard
110116
f = FromString("c:steve*.*")
@@ -129,6 +135,15 @@ func TestFCBFromString(t *testing.T) {
129135
t.Fatalf("name wrong, got '%v'", f.GetName())
130136
}
131137

138+
f = FromString("")
139+
f.Name[0] = 0x00
140+
f.Name[1] = 0x01
141+
f.Type[0] = 0x00
142+
f.Type[1] = 0x01
143+
if f.GetCacheKey() != " " {
144+
t.Fatalf("wrong cache returned, got %v", f.GetCacheKey())
145+
}
146+
132147
}
133148

134149
func TestDoesMatch(t *testing.T) {

0 commit comments

Comments
 (0)