@@ -14,6 +14,7 @@ import (
1414 "log/slog"
1515 "os"
1616 "path/filepath"
17+ "strconv"
1718 "strings"
1819 "time"
1920
@@ -147,6 +148,16 @@ type CPM struct {
147148 // the programs it launches.
148149 start uint16
149150
151+ // biosAddress contains the address of the BIOS we've faked.
152+ //
153+ // This might need to be moved, in rare situations.
154+ biosAddress uint16
155+
156+ // bdosAddress contains the address of the fake BDOS we've deployed.
157+ //
158+ // This might need to be moved, in rare situations.
159+ bdosAddress uint16
160+
150161 // BDOSSyscalls contains details of the BDOS syscalls we
151162 // know how to emulate, indexed by their ID.
152163 BDOSSyscalls map [uint8 ]CPMHandler
@@ -536,6 +547,24 @@ func New(options ...cpmoption) (*CPM, error) {
536547 return nil , err
537548 }
538549
550+ // Helper to return a number, if it is present in the environment
551+ envNumber := func (name string , defValue uint16 ) uint16 {
552+
553+ val := os .Getenv (name )
554+ if val == "" {
555+ return defValue
556+ }
557+
558+ // base is implied, so "0xFEFE" works.
559+ num , err := strconv .ParseInt (val , 00 , 32 )
560+ if err != nil {
561+ return defValue
562+ }
563+
564+ // Truncate.
565+ return uint16 (num & 0xFFFF )
566+ }
567+
539568 // Create the emulator object and return it
540569 tmp := & CPM {
541570 BDOSSyscalls : bdos ,
@@ -549,6 +578,8 @@ func New(options ...cpmoption) (*CPM, error) {
549578 prnPath : "printer.log" , // default
550579 start : 0x0100 ,
551580 launchTime : time .Now (),
581+ biosAddress : envNumber ("BIOS_ADDRESS" , 0xCE00 ),
582+ bdosAddress : envNumber ("BDOS_ADDRESS" , 0xC000 ),
552583 }
553584
554585 // Allow options to override our defaults
@@ -587,6 +618,20 @@ func (cpm *CPM) GetCCPName() string {
587618 return cpm .ccp
588619}
589620
621+ // GetBIOSAddress returns the address the fake BIOS is deployed at.
622+ //
623+ // This is used for the startup banner.
624+ func (cpm * CPM ) GetBIOSAddress () uint16 {
625+ return cpm .biosAddress
626+ }
627+
628+ // GetBDOSAddress returns the address the fake BDOS is deployed at.
629+ //
630+ // This is used for the startup banner.
631+ func (cpm * CPM ) GetBDOSAddress () uint16 {
632+ return cpm .bdosAddress
633+ }
634+
590635// LogNoisy enables logging support for each of the functions which
591636// would otherwise be disabled
592637func (cpm * CPM ) LogNoisy () {
@@ -646,18 +691,25 @@ func (cpm *CPM) LoadBinary(filename string) error {
646691}
647692
648693// fixupRAM is misnamed - but it patches the RAM with Z80 code to
649- // handle "badly behaved" programs that invoke CP/M functions via RST XX
650- // instructions, rather than calls to 0x0005
694+ // handle "badly behaved" programs that invoke CP/M functions by working out
695+ // the address of the BIOS/BDOS and jumping there directly.
651696//
652697// We put some code to call the handlers via faked OUT 0xFF,N - where N
653698// is the syscall to run.
654699//
655700// The precise region we patch is unimportant, but we want to make sure
656701// we don't overlap with our CCP, or "large programs" loaded at 0x0100.
702+ //
703+ // We store the addresses we can use in the cpm-structure, and they can
704+ // be changed by the user via the BIOS_ADDRESS and BDOS_ADDRESS environmental
705+ // variables.
657706func (cpm * CPM ) fixupRAM () {
658707 i := 0
659- BIOS := 0xFE00
660- BDOS := 0xF000
708+
709+ // The two addresses which are important
710+ BIOS := int (cpm .biosAddress )
711+ BDOS := int (cpm .bdosAddress )
712+
661713 NENTRY := 30
662714
663715 SETMEM := func (a int , v int ) {
@@ -808,8 +860,8 @@ func (cpm *CPM) Execute(args []string) error {
808860 // Set the same value in RAM
809861 cpm .Memory .Set (0x0004 , cpm .CPU .States .BC .Lo )
810862
811- BIOS := uint16 ( 0xFE00 )
812- BDOS := uint16 ( 0xF000 )
863+ BIOS := cpm . biosAddress
864+ BDOS := cpm . bdosAddress
813865
814866 // Setup our breakpoints.
815867 //
0 commit comments