Coder Social home page Coder Social logo

iz-cpm's Introduction

iz-cpm -- CP/M 2.2 environment

What is this?

This is a CP/M 2.2 execution environment. It provides everything needed to run a standard CP/M for Z80 or 8080 binary.

Uses my iz80 library for Zilog Z80 and Intel 8080 emulation.

Made with Rust

Note that iz-cpm is a very basic implementation, mostly for educational purposes. I recommend MockbaTheBorg's RunCPM or Udo Munk's Z80pack for a much more complete CP/M emulation.

Installation

Extract the latest zip for Linux, MacOS or Windows. Optionally run download.sh or download.bat to download the CP/M 2.2 system disk, Microsoft Basic, Turbo Pascal, Lisp and some games.

Build from source

To build from source, install the latest Rust compiler, clone the repo and run cargo build --release. To cross compile to Windows, install the target with rustup and run cargo build --release --target x86_64-pc-windows-gnu.

Usage examples

Execute iz-cpm to open the CP/M command prompt (the CCP) on the current directory:

casa@servidor:~/software/cpm22$ ls
ASM.COM      CCSINIT.COM   DISKDEF.LIB  iz-cpm        STAT.COM
CBIOS.ASM    CCSYSGEN.COM  DUMP.ASM     LOAD.COM      STDBIOS.ASM
CCBIOS.ASM   CPM24CCS.COM  DUMP.COM     MOVCPM.COM    SUBMIT.COM
CCBOOT.ASM   DDT.COM       ED.COM       PIP.COM       SYSGEN.COM
-CCSCPM.251  DEBLOCK.ASM   GENMOD.COM   RLOCBIOS.COM
casa@servidor:~/software/cpm22$ ../../iz-cpm 
iz-cpm https://github.com/ivanizag/iz-cpm
CP/M 2.2 Copyright (c) 1979 by Digital Research
Press ctrl-c ctrl-c to return to host

A>dir
A: CCSINIT  COM : MOVCPM   COM : CPM24CCS COM : STAT     COM
A: SYSGEN   COM : STDBIOS  ASM : IZ-CPM       : LOAD     COM
A: DISKDEF  LIB : ASM      COM : RLOCBIOS COM : DUMP     COM
A: -CCSCPM  251 : CCBIOS   ASM : DUMP     ASM : CCSYSGEN COM
A: DEBLOCK  ASM : ED       COM : SUBMIT   COM : DDT      COM
A: PIP      COM : CBIOS    ASM : CCBOOT   ASM : GENMOD   COM
A>

Execute iz-cpm with a file to execute a CP/M binary directly, bypassing the CPP:

casa@servidor:~$ ./iz-cpm software/OBASIC.COM 
44531 Bytes free
BASIC Rev. 4.51
[CP/M Version]
Copyright 1977 (C) by Microsoft
Ok
print "hello"
hello
Ok

Map up to 16 directories as CP/M drives:

casa@servidor:~$ ./iz-cpm --disk-a software/cpm22 --disk-b software/zork --disk-e .
iz-cpm https://github.com/ivanizag/iz-cpm
CP/M 2.2 Copyright (c) 1979 by Digital Research
Press ctrl-c ctrl-c to return to host

A>b:
B>dir
B: ZORK2    DAT : ZORK1    COM : ZORK3    DAT : ZORK3    COM
B: FILE_ID  DIZ : ZORK1    DAT : ZORK1    SAV : ZORK2    COM
B>zork1
ZORK I: The Great Underground Empire
Copyright (c) 1981, 1982, 1983 Infocom, Inc. All rights
reserved.
ZORK is a registered trademark of Infocom, Inc.
Revision 88 / Serial number 840726

West of House
You are standing in an open field west of a white house, with
a boarded front door.
There is a small mailbox here.

>

Usage

iz-cpm https://github.com/ivanizag/iz-cpm
CP/M 2.2 Copyright (c) 1979 by Digital Research
Press ctrl-c ctrl-c to return to host 

USAGE:
    iz-cpm [FLAGS] [OPTIONS] [ARGS]

FLAGS:
    -t, --call-trace        Traces BDOS calls excluding screen I/O
    -T, --call-trace-all    Traces BDOS and BIOS calls
    -z, --cpu-trace         Traces Z80 instructions execution
    -h, --help              Prints help information
    -s, --slow              Runs slower
    -V, --version           Prints version information

OPTIONS:
        --cpu <model>            cpu model z80 or 8080 [default: z80]
    -a, --disk-a <path>          directory to map disk A: [default: .]
    -b, --disk-b <path>          directory to map disk B:
    -c, --disk-c <path>          directory to map disk C:
    -d, --disk-d <path>          directory to map disk D:
        --disk-e <path>          directory to map disk E:
        --disk-f <path>          directory to map disk F:
        --disk-g <path>          directory to map disk G:
        --disk-h <path>          directory to map disk H:
        --disk-i <path>          directory to map disk I:
        --disk-j <path>          directory to map disk J:
        --disk-k <path>          directory to map disk K:
        --disk-l <path>          directory to map disk L:
        --disk-m <path>          directory to map disk M:
        --disk-n <path>          directory to map disk N:
        --disk-o <path>          directory to map disk O:
        --disk-p <path>          directory to map disk P:
        --terminal <terminal>    Terminal emulation ADM-3A or ANSI [default: adm3a]


ARGS:
    <CMD>     The binay image to run, 
    <ARGS>    Parameters for the given command

Features

  • Execution of 8080 and Z80 binaries on top of CP/M
  • Direct usage of the host computer filesystem
  • Terminal emulation of ADM-3A as used in the KAYPRO computers
  • Z80 emulation validated with ZEXALL
  • CPU execution tracing
  • BDOS and BIOS tracing
  • Portable, runs in Linux, MacOS and Windows. Mmm, not in CP/M.

How does it work

CP/M was designed to be portable to a variety of devices using Intel 8080, Intel 8085 or Zilog Z80 processors thanks to a tiered architecture. The main parts of CP/M are:

  • The BIOS: Basic Input/Output system. Interface with the hardware. CP/M defines a very small interface: 16 entrypoints to manage I/O and access to disk sectors. The hardware vendors provided the BIOS for their device based on source code available on the CP/M distribution.
  • The BDOS: Basic Disk Operating System. Main component of CP/M. Provided by Digital Research and common to all systems. Provides the high level entrypoints to be used by application developpers.
  • The CCP: Console Command Processor. The CP/M Command prompt.

To emulate this environment using the host filesystem, we have to provide a replacement BDOS translating as we don't want to relay on the physical disk sectors abstraction of the BIOS. The main components are:

  • Z80 emulator. It uses iz80
  • BIOS emulator. Only the I/O entrypoints. In theory, it shouldn't be necessary, but some programs use it directly, bypassing BDOS.
  • BDOS emulator. Traps the calls and executes code on the host.
  • CPP. Runs natively, no emulation needed. We use ZCPR1 an open source alternative. See cpmish for other open source alternatives to the CP/M binaries. The CPP binary from CP/M 2.2 can be used optionally.
  • Terminal emulator. CP/M does not define how the terminal should work. Applications needed to be aware and usually could be configured for several leading options, like ADM-3a, VT-52, Hazeltine 1500 and Osborne. This emulator supports ADM-3a used also on the very popular Kaypro computers.

Useful links:

TODO

  • Proper documentation
  • File level read-only option (I won't do that, the host can control that)
  • BIOS support for punch cards (Nope)
  • BIOS support for track/sector access to disks (Not needed)

iz-cpm's People

Contributors

ivanizag avatar skx avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

iz-cpm's Issues

Compute File Size is incorrect

Hi,

File size -> It returns -> What it should return
(wrong) 0 bytes -> 1 record -> 0 records
(correct) 1 byte -> 1 record -> 1 record (correct)
(correct) 127 bytes -> 1 record -> 1 record (correct)
(wrong) 128 bytes -> 3 records -> 1 records
(wrong) 129 bytes -> 3 records -> 2 records
(wrong) 255 bytes -> 3 records -> 2 records
(wrong) 256 bytes -> 4 records -> 2 records
(wrong) 257 bytes -> 4 records -> 3 records

I looked at the function and it looks like it adds 1 twice for some reason, but It should only add a 1 a single time and then only if the filesize % RECORD_SIZE is non-zero (% being a modulus operator).

File not found when running Pascal/MT+ from the host command line

[Reported by email]

I would like to report bug in this project. I tried compile something in PascalMt+ so I called iz-cpm from Window's command line like this:

iz-cpm mtplus.com test.pas

It doesn't works. Files mtplus.com and test.pas are in the same directory with iz-cpm.exe. It looks like iz-cpm provides incorrect CP/M command tail to mtplus.com. When I tried compile my code directly from iz-cpm command line it worked OK. I have attached screenshot. I marked issues by red rectangles. Can you solve it?

screenshot

Wordstar saves only to drive A -- Possibly not a WS issue

I haven't run it down yet, but for some reason WordStar always saves to drive a: -- could be disk handling or could be some problem with the WS configuration. Try this:

../../target/debug/iz-cpm --disk-a . --disk-b ../wordstar33 --disk-c ../zork --disk-d ..
b:
ws
N        (new non document)
B:TEST.TXT     (or just TEST.TXT)
Anything        (type anything)
^KX

The file TEST.TXT will now be on A: Even stranger, it tries to read from B. So if you do a ^KS instead of ^KX the file will be saved but it will reread from B where the file is blank. The same thing happens on other drives. The save winds up on A and the reread comes from the drive you intended. So if you save to drive A it works fine. Any other drive, no.

A related problem is a side effect. If you are testing and do ^KS to save, your file goes blank (because it saves to A and rereads from B). If you now save again (say a ^KX to get out), you will write a blank file over your original file on A so now when you type the file on drive A it will be blank. Not really a bug because it would not happen if the reread were picking up the file which it would if the save had occurred on that drive.

I'll let you know if I find anything else out. This is on a clone of the repo, by the way. I have not tried it with the release version.

Is there a chance to change the "double-ctrl-c" to a more "secure" key-combination?

Hi,
Thanks for the interesting emulator! :)
Is there a chance to get instructions how to get iz-cpm compiled under a ARM linux OS (armbian)?

When I try to cargo check/build I do get the error-message:

root@npi-neo2-23(192.168.6.23):/toshiba/iz-cpm-1.0# cargo check
error: failed to parse lock file at: /toshiba/iz-cpm-1.0/Cargo.lock

Caused by:
invalid serialized PackageId for key package.dependencies

Which dependencies do I need and how do I install them?

Kind Regards
Guido

Debian compile error

Installing on Debian bullseye. Had to enable the sid repository in order to get a version of cargo that could compile. Nonetheless, getting the following error:

error: unknown --json option future-incompat

error: unknown --json option future-incompat
error: unknown --json option future-incompat

error: could not compile ansi_term
warning: build failed, waiting for other jobs to finish...
error: could not compile unicode-width
error: could not compile libc
error: unknown --json option future-incompat

error: could not compile strsim

CCP should reload on Ctrl-C

The documentation for Function 10: Read Console Buffer says, in the keybinding section:

  • CTRL-C reboots when at the beginning of line

That's something I've been used to pressing at the CCP prompt, because it can trigger the execution of a $$$.SUB file (and also allowed you to swap disks back in the day)

However your emulator doesn't reload - while #13 is open you could see the difference in "running" vs "restart" by watching the change of user-number, or drive, and of course until #16 is resolved SUBMIT is a non-issue too.

You catch Ctrl-C for your "exit emulation" so I guess this might need some reworking. I did the same thing for a long time, but eventually patched in a HALT, EXIT, and QUIT command to the CCP - they just run HALT instructions which is enough for my emulator to terminate.

I also defaulted to requiring two CtrlCs in a row to trigger the reboot, but later relented and made this configurable at run-time, via a bios extension and/or CLI flag.

I guess this is a minor issue as reloads happen after control returns from child processes, but I admit I've become very familiar with pressing the key as a matter of habit nowadays.

CP/M licensing

I've got good news and bad news...

The bad news is that DR's CP/M isn't open source. It was semi-freed back in 2001 with a mostly-but-not-quite license (excerpted from http://www.gaby.de/cpm/license.html):

Let this email represent a right to use, distribute, modify, enhance and
otherwise make available in a nonexclusive manner the CP/M technology as
part of the "Unofficial CP/M Web Site" with its maintainers, developers and
community.

So, it can't be legally distributed anywhere other that www.gaby.de/cpm. It's very unlikely that anyone would be sued for distributing it, but it is a license violation and so will prevent distribution in, for example, Debian. (I've been looking into the licensing of CP/M software for a project of mine where I've been trying to produce a properly open source CP/M distribution, at http://github.com/davidgiven/cpmish.)

The good news is that ZSDOS and ZCPR1 are drop-in replacements and are distributable. At least, they do to the best of my knowledge; licensing back then was a mess. But I'm pretty confident that ZCPR1 is public domain (but not subsequent versions) and ZSDOS is actually GPLd. I've got copies that have been patched to build with zmac at https://github.com/davidgiven/cpmish/tree/master/third_party/zcpr1 and https://github.com/davidgiven/cpmish/tree/master/third_party/zsdos if you want them.

SUBMIT.COM doesn't work

I suspect resolving this will involve a number of changes, but the short version is simple enough:

  • SUBMIT.COM does not work under your emulator.

There is a related utility I found called "slash", source/binary available within my distribution of utilities:

The short version is that both of these utilities work the same way, they allow the submission of a number of sequential commands to be automatically executed.

SUBMIT.COM will read a text file containing commands, with optional parameter expansion. It will then generate a file $$$.SUB on A:, which should be processed the next time the CCP is reloaded.

SLASH.COM is similar, but it allows you to create the $$$.SUB file without the need for a temporary file, for example:

SLASH C:HELLO ; A:HELLO ; C:ECHO TEST

Both of these utilities should produce a file named $$$.SUB. That file will contain N 128-byte records, one record for each command. The records will be in reverse order:

  • CCP will recognize the file is present when it starts.
  • CCP will execute the command in the last record.
  • It will then truncate the file by one record length (i.e. 128 bytes)
  • Repeating until error, or the file is empty (at which point it is deleted).

I played around with fixing this but I hit a bunch of errors which both annoyed and confused me, so I think this will be better placed for you to attack if you wish!

The first issue is that the drive-reset function 13, DRV_ALLRESET, is supposed to return 0xFF if there is a $$$.SUB file present, but ast the moment you hard-code the return value as 0x00. I thought that updating that function to return 0xFF would be OK - because if the file were present it would then be processed, and in the case where opening failed the CCP would move on. But unfortunately it wasn't that simple.

If I make this change:

@@ -166,6 +166,7 @@ impl Bdos {
                 },
                 13 => { // DRV_ALLRESET - Reset disk system
                     env.state.reset();
+                    res8 = Some(0xff)
                 },
                 14 => { // DRV_SET - Select disk
                     bdos_drive::select(env, arg8);
@@ -293,4 +294,4 @@ fn get_version() -> u16 {
     and random access functions.
     */
     0x0022 // CP/M 2.2 for Z80
-}
\ No newline at end of file
+}

The end result is actually a failure to open the file:

$ ./target/debug/iz-cpm --disk-a /home/skx/Repos/github.com/skx/cpm-dist/A
iz-cpm https://github.com/ivanizag/iz-cpm
CP/M 2.2 Emulation
Press ctrl-c ctrl-c Y to return to host

Athread 'main' panicked at 'attempt to add with overflow', src/fcb.rs:162:18
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

Running with tracing that's trying to open the file, which does exist:

[[BDOS command 32: F_USERNUM(0000)]][[=>00]]
[[BDOS command 13: DRV_ALLRESET(0000)]][[=>ff]]
[[BDOS command 17: F_SFIRST(f07a)]][[DIR start A:$$$.SUB]][[=>00]]

[[BDOS command 25: DRV_GET(f00a)]][[=>00]]
A[[BDOS command 32: F_USERNUM(f0ff)]][[=>00]]
[[BDOS command 15: F_OPEN(f07a)]][[Open file A:$$$.SUB]][[=>00]]
thread 'main' panicked at 'attempt to add with overflow', src/fcb.rs:162:18
stack backtrace:

I stopped investigating there.

However I suspect if you resolve this problem you'll hit another, I see you don't truncate files when they're closed, and so I suspect that if you return the value to indicate the SUB-file is present, and fix the opening you'll end up with an infinite loop:

  1. Open $$$.SUB
  2. Read the last record.
  3. Close the file
  4. Execute the command.
  5. GoTO 2

Unless/until the truncation of the last record happens it'll just run forever, until cancelled.

File not found with CalcStart Dump vr_1.45

Using Calcstar on an exising file with 3072 bytes:

CalcStar Dump vr_1.45 
CalcStar file name (<Return> to quit): demo.csd

[[BDOS command 26: F_DMAOFF(0080)]]
[[BDOS command 15: F_OPEN(cd1a)]][[Open file A:DEMO.CSD]][[=>00]]
[[BDOS command 35: F_SIZE(cd1a)]][[Size of A:DEMO.CSD]]
[[BDOS command 26: F_DMAOFF(cbbe)]]
[[BDOS command 33: F_READRAND(cd1a)]][Read random record 18 into cbbe][[=>01]]
** demo.csd not found on disk
CalcStar file name (<Return> to quit): 

Reading record 0x18 goes just to the end of the file

Panic after running DDT

First of all thank-you for sharing your code. I've been slowly working on a CP/M emulator of my own over the past month or two - mine in golang, and I appreciate the comments you left in your code which were a helpful resource at times - to the extent I linked to your project from my README.

Anyway I figured I should finally actually test your project, and I found an interesting issue. Sorry!

So I've put together a repository of "standard" software, much like you setup with your download.sh script. My repository is here:

Clone that to follow along. My initial attempt was to compile a hello-world assembly-program, and run it:

$ RUST_BACKTRACE=1 ./iz-cpm --disk-a /home/skx/Repos/github.com/skx/cpm-dist/A/ --disk-b /home/skx/Repos/github.com/skx/cpm-dist/B/
iz-cpm https://github.com/ivanizag/iz-cpm
CP/M 2.2 Emulation
Press ctrl-c ctrl-c Y to return to host

A>asm hello
CP/M ASSEMBLER - VER 2.0
011B
000H USE FACTOR
END OF ASSEMBLY

A>load hello

FIRST ADDRESS 0100
LAST  ADDRESS 011A
BYTES READ    001B
RECORDS WRITTEN 01


A>hello

Hello, World!

Looks good.

Now I run DDT.COM and using that JUMP to address 0x0000, which is a boot:

A>ddt
DDT VERS 2.2
-g0000

A>asm hello             MI
Hello, World!
$
CP/M ASSEMBLER - VER 2.0
011B
000H USE FACTOR
END OF ASSEMBLY

Notice there that there is some Hello, world! output? And yet the previous time we compiled that we just got the compilation as expected.

Let us try to load the .COM file from the generated .HEX - again starting from the top:

frodo ~/Downloads/iz-cpm-linux-v1.3.1 $ RUST_BACKTRACE=full ./iz-cpm --disk-a /home/skx/Repos/github.com/skx/cpm-dist/A/ --disk-b /home/skx/Repos/github.com/skx/cpm-dist/B/
iz-cpm https://github.com/ivanizag/iz-cpm
CP/M 2.2 Emulation
Press ctrl-c ctrl-c Y to return to host

A>asm hello
CP/M ASSEMBLER - VER 2.0
011B
000H USE FACTOR
END OF ASSEMBLY

A>load hello

FIRST ADDRESS 0100
LAST  ADDRESS 011A
BYTES READ    001B
RECORDS WRITTEN 01


A>ddt
DDT VERS 2.2
-g0000

A>asm hello             MI
Hello, World!
$
CP/M ASSEMBLER - VER 2.0
011B
000H USE FACTOR
END OF ASSEMBLY

A>load hello        :100100000E09110901CD0500C90D0A48656C6C6F17
:0B0110002C20576F726C64210D0A2434
:0000000000

thread 'main' panicked at src/bdos_environment.rs:107:19:
index out of bounds: the len is 16 but the index is 204
stack backtrace:
   0:     0x7f76720b52dc - std::backtrace_rs::backtrace::libunwind::trace::ha752d28ad8b59776
                               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/../../backtrace/src/backtrace/libunwind.rs:104:5
   1:     0x7f76720b52dc - std::backtrace_rs::backtrace::trace_unsynchronized::h19085f9ccfa8c39c
                               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5
   2:     0x7f76720b52dc - std::sys_common::backtrace::_print_fmt::h7fa8e07ec2f6097e
                               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/sys_common/backtrace.rs:67:5
   3:     0x7f76720b52dc - <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt::hf031f84ba50c0cfa
                               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/sys_common/backtrace.rs:44:22
   4:     0x7f76720ea2e0 - core::fmt::rt::Argument::fmt::h41c4ec8113ce6748
                               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/fmt/rt.rs:142:9
   5:     0x7f76720ea2e0 - core::fmt::write::h9d6dcce53fee8498
                               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/fmt/mod.rs:1120:17
   6:     0x7f76720b2fdf - std::io::Write::write_fmt::h1592172be29051fa
                               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/io/mod.rs:1762:15
   7:     0x7f76720b50c4 - std::sys_common::backtrace::_print::h077b25e4211c79a3
                               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/sys_common/backtrace.rs:47:5
   8:     0x7f76720b50c4 - std::sys_common::backtrace::print::h0992ac2bfe77cad2
                               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/sys_common/backtrace.rs:34:9
   9:     0x7f76720b65d7 - std::panicking::default_hook::{{closure}}::h2cf07aea02dfbfab
  10:     0x7f76720b633f - std::panicking::default_hook::h85b79716976ec2d8
                               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/panicking.rs:292:9
  11:     0x7f76720b6a58 - std::panicking::rust_panic_with_hook::h8687ef8cbe256fb2
                               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/panicking.rs:779:13
  12:     0x7f76720b693e - std::panicking::begin_panic_handler::{{closure}}::ha182011c2febc20e
                               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/panicking.rs:657:13
  13:     0x7f76720b57a6 - std::sys_common::backtrace::__rust_end_short_backtrace::h8ea229012037d9c8
                               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/sys_common/backtrace.rs:170:18
  14:     0x7f76720b66a2 - rust_begin_unwind
                               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/panicking.rs:645:5
  15:     0x7f7672012ae5 - core::panicking::panic_fmt::h4a1a6d8dbc505935
                               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/panicking.rs:72:14
  16:     0x7f7672012ca2 - core::panicking::panic_bounds_check::h7ccafe65706d62d1
                               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/panicking.rs:190:5
  17:     0x7f767201b8e2 - iz_cpm::bdos_environment::BdosEnvironment::get_directory::h47ac382745bda551
  18:     0x7f76720152c4 - iz_cpm::bdos_file::find_host_files::hc275de410d62360d
  19:     0x7f76720136bf - iz_cpm::bdos_file::open::h052ea4f54962a05d
  20:     0x7f767201cf9b - iz_cpm::bdos::Bdos::execute::hed741525d0a22a20
  21:     0x7f7672019ba4 - iz_cpm::main::hcddeec579b7c7c85
  22:     0x7f767201d2f3 - std::sys_common::backtrace::__rust_begin_short_backtrace::h54006916c45c37ab
  23:     0x7f767201cb19 - std::rt::lang_start::{{closure}}::he9f5b6b38ffdf303
  24:     0x7f76720ae96e - core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once::hf47534de04578a83
                               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/core/src/ops/function.rs:284:13
  25:     0x7f76720ae96e - std::panicking::try::do_call::he270afe99f162c7b
                               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/panicking.rs:552:40
  26:     0x7f76720ae96e - std::panicking::try::hc551f5c5a344ecad
                               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/panicking.rs:516:19
  27:     0x7f76720ae96e - std::panic::catch_unwind::h6790e138972db2df
                               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/panic.rs:142:14
  28:     0x7f76720ae96e - std::rt::lang_start_internal::{{closure}}::haa35f81c7641c384
                               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/rt.rs:148:48
  29:     0x7f76720ae96e - std::panicking::try::do_call::h4e42cde918b15a44
                               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/panicking.rs:552:40
  30:     0x7f76720ae96e - std::panicking::try::hb3bfb1061a5e948b
                               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/panicking.rs:516:19
  31:     0x7f76720ae96e - std::panic::catch_unwind::h4789ec49cbf5f558
                               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/panic.rs:142:14
  32:     0x7f76720ae96e - std::rt::lang_start_internal::h141a6b3fc711a41c
                               at /rustc/82e1608dfa6e0b5569232559e3d385fea5a93112/library/std/src/rt.rs:148:20
  33:     0x7f767201cb0e - std::rt::lang_start::haf5dbd08b5b7a937


I'm not going to try to guess what is going on there, but it looks like the boot, via "JMP 0x0000" puts things into a bad state, after which there is oddity in many commands.

BBC BASIC doesn't save/load programs

[ugly PR coming shortly. Feel free to ignore/close]

The version of BBC BASIC you have in your download script doesn't fully work - specifically SAVE and LOAD both fail:

$ ./target/debug/iz-cpm --disk-a ./software/bbcbasic/
iz-cpm https://github.com/ivanizag/iz-cpm
CP/M 2.2 Emulation
Press ctrl-c ctrl-c Y to return to host

A>bbcbasic
BBC BASIC (Z80) Version 3.00
(C) Copyright R.T.Russell 1987
>10 PRINT "STEVE"
>SAVE "FOO"
CP/M Error
>SAVE "FOO.BBC"
CP/M Error
>*CPM
A>
Press Y to exit iz-cpm. Any other key to continue.

I had the same issue in my emulator, which I worked around but it looks like I'm going to have to go back and fix it properly.

Looking at what happens when a successfull result is achieved we end up
with the following series of calls:

>SAVE "FOO"
[[BDOS command 19: F_DELETE(005c)]][[Delete file FOO.BBC]][[=>0000]]
[[BDOS command 22: F_MAKE(005c)]][[Create file FOO.BBC]][[=>0000]]
[[BDOS command 26: F_DMAOFF(3e00)]][[BDOS command 40: F_WRITEZ(005c)]][Write random record 0 into 3e00][[=>0000]]
[[BDOS command 16: F_CLOSE(005c)]][[=>0000]]

If the F_DELETE fails then the F_MAKE is skipped and we get an error. Similarly the rest of the calls - if one of them fails the chain is broken and the save fails.

I eventually worked out that the issue here seems to be that H/L are tested rather than A, and that meant we needed to use your 16-bit return value.

Additionally a failure to delete the file, if it didn't exist, killed things dead too. So I faked an OK result in that in my PR.

Failure to persist user/drive state

[I have a patch ready for this, which I will submit shortly]

When I was writing my emulator I wrote some test programs to cement my understanding of some of the expected behaviour. One simple script I wrote was ret.z80, which is a trivial binary which just tries to exit in a couple of different ways:

  • JP 0x0000
  • RST 0
  • RET
  • LD C, 0x00; CALL 0x0005

There is a binary which is located within that same directory which can be used for testing - but note that you really want to map this to a non-default drive. In my case I see this when I test:

$ ./target/debug/iz-cpm --disk-a /home/skx/Repos/github.com/skx/cpm-dist/A/ --disk-b  /home/skx/Repos/github.com/skx/cpmulator/samples/ 
iz-cpm https://github.com/ivanizag/iz-cpm
CP/M 2.2 Emulation
Press ctrl-c ctrl-c Y to return to host

A>b:
B>dir ret.*
RET     .COM  |  RET     .Z80
B>ret 1

B>ret 2

B>ret 3

B>ret 4

A>

Here you see that the execution of the binary succeeded in each of the invokations, each time the binary was launched and then terminated. However The last run switched back to the default A-drive.

You correctly pass this value along when reseting via the boot:

                      let user_drive = machine.peek(CCP_USER_DRIVE_ADDRESS);
                    cpu.registers().set8(Reg8::C, user_drive);

However that value is never set. I've made a couple of other changes to remove the "= 0" from the drive/user setting in the rest/state, and added the missing stores.

That allows things to work as expected.

BDOS Function 3 - A_READ should not echo input

I put together a simple input-testing binary, because I spent a lot of time fighting with console input, the source and binary can be found here:

Your emulator fails two tests, I think. This bug report is for the first failure.

Not true - second issue is a non-issue - I thought you were behaving incorrectly by terminating input of function 10: Read Console Buffer when the buffer is full, but I think on reflection that if the input size is 10 bytes you should return when the user enters 10 characters - without the need for RETURN. I guess I did the wrong thing in mine :)

I think reading the documentation that Function 3 / A_READ should not echo input. I'm basing that upon reading this guide:

Here we see:

  • BDOS function 1 (C_READ) - Console input
    • Wait for a character from the keyboard; then echo it to the screen and return it.
  • BDOS function 3 (A_READ) - Auxiliary (Reader) input
    • Reads a character from the console without checking for ^S / ^Q.

Specifically note that in the second entry there is no reference to "echo".

Similarly in the bigger manual, http://www.gaby.de/cpm/manuals/archive/cpm22htm/ch5.htm#Section_5.2 we see a similar writeup. There is a reference to echo in C_READ but not in A_READ.

So I think my behaviour is correct, but I appreciate this might be a matter of interpretation.

Output from my emulator:

 ./cpmulator samples/intest.com 
Simple input-test program, by Steve.


C_READ Test:
  This test allows you to enter FIVE characters, one by one.
  The characters SHOULD be echoed as you type them.
12345
  Test complete - you entered '12345'.

A_READ Test:
  This test allows you to enter FIVE characters, one by one.
  The characters should NOT be echoed as you type them.
  Test complete - you entered '12345'.

C_RAWIO Test:
  This uses polling to read characters.
  Echo should NOT be enabled.
  Press 'q' to proceed/complete this test.
+

Output from yours:

$ ./target/debug/iz-cpm /home/skx/Repos/github.com/skx/cpmulator/samples/intest.com 
Simple input-test program, by Steve.


C_READ Test:
  This test allows you to enter FIVE characters, one by one.
  The characters SHOULD be echoed as you type them.
12345
  Test complete - you entered '12345'.

A_READ Test:
  This test allows you to enter FIVE characters, one by one.
  The characters should NOT be echoed as you type them.
12345  Test complete - you entered '12345'.

C_RAWIO Test:
  This uses polling to read characters.
  Echo should NOT be enabled.
  Press 'q' to proceed/complete this test.
+

Note the echo'd characters in the section section.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.