If you are into the retrocomputing stuff and write or maintain code in Z80 assembler, chances are that you are using the MACRO-80 assembler+linker+library manager suite. These programs were written in Z80 themselves and run in CP/M, so the only way to use them is to run a CP/M emulator.
This project turns the original M80, L80 and LIB80 applications into modern native applications. This means:
- Multiplatform native applications for Windows 32 bit, Windows 64 bit, Windows ARM, Linux 64 bit, Linux ARM and macOS, thanks to the magic of .NET Core (plus a really portable, although not single-file edition). No separate CP/M emulator is needed.
- Extra command line options to specify the working directory where files will be read from and written to, and to specify additional file search directories for files to be read (the original applications work on the current directory only).
- On error, the applications terminate instead of going back to interactive mode (unless explicitly ran in interactive mode via command line switch).
- Colored console output for warnings and errors (can be disabled via command line switch).
- Regular messages are written to the standard output handle, warnings and errors are written to the standard error handle.
- Applications terminate with non-zero error code in case of warnings and errors.
A limitation that still exists is that filenames still need to contain pure ASCII characters only, and they must conform to the old 8.3 format.
This project makes use of the disassembled sources of the original M80, L80 and LIB80 tools as generated by Werner Cirsovius and hosted in the z80.info site.
The native executables contain the original Z80 application binaries and an embedded Z80+CP/M emulator that runs them. The Z80 emulator is a (very) simplified version of the Z80.NET project, where everything that is not strictly needed has been removed for the sake of performance. Conversely, the CP/M part implements only the function calls that are used by the original applications.
The Z80 binaries used are slightly modified versions of the originals; the changes incorporated are needed to implement the extra features like color printing, exit codes, sopporting 8 bit characters and terminating instead of going to interactive mode. Other than that, there's only one feature change: M80 assumes now that the target CPU is Z80 by default (in the original tool the default target is the Intel 8080).
Download the latest release and choose the variant you want to use:
- Portable: Consists of multiple
.dll
files and runs on any platform having the .NET Runtime 6.0 installed. - Framework-dependant: Single file, platform-specific applications that also require the .NET Runtime 6.0 to be installed. This is the perhaps the most convenient variant for frequent use.
- Self-contained: Single file, platform-specific applications that have the .NET Core Runtime built-in and thus have no dependencies (at the expense of much bigger file sizes). These may be convenient for casual usage, or if you just want to give it a try before installing the .NET Runtime.
Place the chosen variant in a PATH-accessible directory and then run
M80|L80|LIB80[.exe] [<wrapper command line>] <original program command line>
where <original program command line>
is the command line you want to pass to the original application, and <wrapper command line>
are optional extra switches implemented for this project as described below. Example: M80 -w ~/MyProject -8 =FILE
.
For the portable version you'll need to use the dotnet
tool (part of the .NET Core Runtime) to run the applications from the .dll
files (so dotnet M80.dll|L80.dll|LIB80.dll ...
), other than that it works the same way as the platform-specific versions.
For the command line originally defined by each application, see the applications manual (retrieved from the MSX archive site).
The wrapper defines the following command line arguments. Except for -w
, they are all switches that come in pairs so that they can be enabled or disabled. You'll see a summary of these arguments if you run the applications without any argument.
-w <working directory>
The original applications don't support subdirectories, and thus all the files they read must be located in the same directory, which is also the place where new files will be created. By default the current working directory will be used for that, but -w
can be used to specify a different location.
-p <path>[,<path>...]
and-np
This option specifies a comma separated list of additional paths to search for files to be read. When any of the applications tries to open a file, it will be searched in the working directory first, and then in any paths specified with this argument in the order in which they were specified. Other file operations (create, delete, rename) will be performed on the working directory only.
Relative paths can be specified, these will be converted to absolute paths based on the implicit or explicit (-w
) working directory.
The option can be specified multiple times: -p foo,bar -p fizz,buzz
is equivalent to -p foo,bar,fizz,buzz
. Thus you can specify some paths via environment variables and then add some more via command line.
The -np
option will start over the paths list, that is, it will invalidate any previous -p
option found; thus -p foo -np -p bar
is equivalent to -p bar
. This can be useful too in combination with the specification of paths via environment variables.
-i
and-ni
If -i
is specified then the application will run in interactive mode, that is, a prompt will appear and the application will wait for a command to be entered. This is equivalent to running the original application without command line arguments (therefore <original program command line>
will be ignored in this case). -ni
will cause the application to read its command line, process it and then terminate, even if there are errors (this is the default).
-b
and-nb
If -nb
is specified then the initial banner showing the application name, wrapper version and author will not be displayed. -b
will cause it to be displayed (this is the default).
-t
and-nt
If -t
is specified then after the application finishes, with or without error, information about how much time the execution took. -nt
will cause this information not to be displayed (this is the default).
-a
and-na
If -a
is specified then warnings and errors will be output to the console using ANSI colors (this is the default). -na
will cause all console output to be in the default color.
-c
and-nc
By default (and also when -nc
is used) the applications will perform a case-insensitive file search when trying to locate an existing file; that is, an existing foobar.mac
will be a match when searching for FOOBAR.MAC
. The -c
option will force the search to be case-sensitive instead.
This options pair and the described search behavior were introduced in v1.1. In v1.0 file search was always case-insensitive in Windows and always case-sensitive in Linux and macOS.
-8
and-n8
(only for M80)
M80 doesn't support 8 bit characters by default: all characters read from the source files will have their MSB zeroed before being processed. This will likely cause assembly errors if the source contains strings with 8 bit characters (encoded in some old extended character set), so the -8
switch is offered to suppress this MSB zeroing of source bytes. -n8
will keep the original MSB-zeroing behavior (this is the default).
-l
and-nl
(only for M80)
Originally M80 expects source files to use the CR character (ASCII code 13) to signal line endings, ignoring LF characters (ASCII code 10), and will fail when processing source files that use the Linux convention for line endings (a single LF character).
To prevent this from happening, while still being able to process files using CR+LF line endings, the wrapper will modify the contents of the source files as they are read so that CR characters are converted to spaces and LF characters are converted to CR characters.
This happens by default, and also if the -l
switch is supplied. You can use the -nl
switch if for some reason you need to disable this conversion.
Additionally to passing wrapper command line arguments directly when running the applications, you can use environment variables for that too. There are four of these: X80_COMMAND_LINE
applies to all three applications; M80_COMMAND_LINE
, L80_COMMAND_LINE
and LIB80_COMMAND_LINE
are application-specific.
For example, if you want to permanently disable color console output and enable the execution time information in all three applications, you can do set X80_COMMAND_LINE=-na -t
.
Arguments specified directly in the command line have precedence over those specified in application-specific environment items; and these in turn have precedence over those specified via X80_COMMAND_LINE
.
Upon termination the applications will return one of the following exit codes:
- 0: Success (or the application was run without arguments an help has been displayed)
- 1: Warnings were displayed (for M80 only). The process completed but the generated object file might be incorrect. Example: unterminated conditional assembly block.
- 2: Errors were encountered (for M80 and L80 only). The process completed but the generated file is incorrect. Example: unknown symbol found.
- 3: A fatal error aborted the process and no files were generated whatsoever. Examples: file not found, unknown command. This is also the error code that will be returned in case of unexpected error thrown by the wrapper.
When running in interactive mode the applications will terminate when pressing CTRL+C or when issuing the appropriate exit command (as defined in the applications manual), the exit code is undefined in this case.
The Shared
project has a MACRO80_Sources
directory holding the source code of the original applications (*.MAC
files plus XX80.LIB
) with the appropriate modifications. Search for ;>>> ADDED
and ;>>> CHANGED
comments if you want to see what are the exact changes.
The WINBUILD.BAT
script will use cpm.exe
and the original M80.COM
and L80.COM
applications to build the modified versions and copy them to each C# project. You could of course also use the new .NET binaries instead (and that would be really cool by the way).
To build the wrappers you'll need the .NET 6 SDK. The Shared
project has a PublishProfiles
folder containing a build.sh
script and several .pubxml
files, each containing a publish profile for each of the variants. The script (for Linux, it works on Windows 10 with WSL too) uses the dotnet
tool (part of the .NET SDK) to generate all the variants in the Release
folder in the root directory of the project, you can just run it to build all the variants or pass the name of a publish profile (filename without the .pubxml
) as a single argument.
You can also use Visual Studio 2022 for compiling the wrappers: copy all the .pubxml
files to the Properties/PublishProfiles
directory of each project, then right-click the project in Visual Studio and select "Publish".
...if you like this project please consider donating! My kids need moar shoes!