Cross Assembly Using Visual Studio Code
Apple II development with VS Code
Sometime back, I decided to undertake some 6502 assembly language programming to run on my Apple II+ and IIe. Until recently, I had been using Merlin Pro in an Emulator on my MacBook and on the IIe itself. This has worked fine for me previously, however, as my project has grown and the number of source files has increased, it is starting to become a little tedious. Therefore, I decided to investigate cross-compiling on my MacBook with automatic deployment to the emulator.
My aim was to develop a simple workflow that would allow me to edit the code on my Mac and provide an automatic means to assemble, deploy and run it in the emulator. The steps I needed to consider were:
- assemble the code
- transfer the assembled binary file to a suitable 140k disk image
- launch an Apple II emulator with the disk image ‘inserted’
- run the software in the emulator
This all turned out to be rather straight forward, all that was required was to install and configure a few software packages and add a couple of very simple bash scripts into the mix. For this post, I tried two different cross assemblers - each has its merits and I will leave you to decide what suits you best. Configuration details for each are shown below.
The software used was:
- Visual Studio Code
- Either Merlin 32 or SBASM cross assembler
- AppleCommander
- Virtual ][ Emulator
Software Dependencies
The SBASM cross assembler has a dependency on Python3 and Apple Commander has a dependency on Java. I suspect that most, like me, already have these installed.
Visual Studio Code
https://code.visualstudio.com/
This is a free open source package and runs on most platforms. I have used VSCode in the past for some .NET Core development. It is a great product so I thought I would use it as there is a 6502 syntax highlighting extension (BEEB VSC) that works reasonably well. It also has a task manager that is capable of running the build and test scripts (see below).
Merlin 32
Merlin 32 is a multi-pass Cross Assembler running under Windows, Linux and Mac OS X targeting 8 bit processors in the 6502 series (such as 6502 and 65c02) and the 16 bit 65c816 processor. It is compatible with Glen Bredon’s Merlin 16+ syntax, including support for Macros, Pre-processor, Logical Expressions, Conditional Operations, Variables, Loops, Local Labels, etc.
The package is available from Brutal Delux. Expanding the zip file will show the platform dependent binarys and Library (Macro) folder and simply requires that the correct binary and the Library folder be added to the project folder.
To invoke the assembler, I created a small bash script called build-assemble. Note that my source is located in a project sub-folder called src.
./Merlin32 -V ./Library ./src/$1.s
mv ./src/sgmail .
</code>
The script can be invoked as follows, however later I will describe how this can be handled as a VS Code Task.
./build-assemble sgmail
The parameter sgmail
is the name of my main source file (sgmail.asm).
Note the move statement (mv
). This is required in my case as the source files are stored in a src folder which is where the compiled binary ends up. What I wanted was for the binary to end up in the folder above ready for the next stage.
SBASM Cross Assembler
http://www.sbprojects.com/sbasm/
This is a free product although, I would ask that you to support the author if you are able to. It requires a manual install, however, it is simple process. In fact, in my case, I pretty much ignored the instructions and placed all of the files required within my project directory.
After downloading and unzipping the package, I created a project directory for my project. The project is called SGMail
so, quite naturally that was the name of my project directory. Within this directory, I created a bin directory. I copied, from the SBASM package, the sbapack directory and the sbasm file to this bin directory. The headers and opcodes directories were copied from the SBASM package to the root of my project directory (SGMail
).
At this point, I started converting my source (.asm) files. It took me about 5 minutes using search and replace and a little tweaking, to convert all of my Merlin source files to the SBASM flavour. Only the psuedo opcodes (directives) and the ASC string
s needed to be adjusted, although in my case I also adjusted the local labels as the local labels in Merlin are considered to be Macro labels in SBASM.
Note: If you come to convert any merlin ASC directives to the SBASM .AS directive, take a few minutes to understand the subtly different, but often more suitable, .AT directive.
To invoke the assembler, I created a small bash script called build-assemble.
python3 ./bin/sbasm src/$1.asm
The script can be invoked as follows, however later, I will describe how this can be handled as a VS Code Task.
./build-assemble sgmail
The parameter sgmail
is the name of my main source file (sgmail.asm).
AppleCommander
AppleCommander is used to update a disk image with the newly assembled binary in preparation for running in the Virtual ][ emulator.
This is installed using…
I started with a standard bootable ProDos 2.4.1 disk image called SGMail.po and created a small bash script to invoke the process of updating the disk image with the newly assembled binary file. This could be integrated with the above script, however, it would be wise to include some form of exception handling in case something fails during assembly. I chose to keep mine separate as I wanted a two tasks (assemble and test) in VS Code.
java -jar /Applications/AppleCommander.app/Contents/Resources/Java/AppleCommander.jar -d $1.po $1
java -jar /Applications/AppleCommander.app/Contents/Resources/Java/AppleCommander.jar
-p $1.po $1 bin $2 < $1
The script can be invoked as follows, however, as previously mentioned, I will describe how this can be handled as a VS Code Task.
./build-test sgmail 0x8000
The first parameter sgmail
is the name of my assembled binary file. The last parameter is the address used in the ORG directive (.OR
) within the code (see sample code below). ProDos needs this additional information in order that it can be associated with the file.
Virtual ][ Emulation
This is the MacOS Apple II emulator I chose to use and can be invoked using AppleScript. This is not a free product but is very reasonably priced and can be highly recommended. A small bash script is used to invoke the AppleScript which in turn invokes the emulator. Here is the bash script.
# Load Disk in Emulator and run using AppleScript
osascript "Virtual][Emulation.scpt"
I simply combined this with the above script as follows and called it build-test.
java -jar /Applications/AppleCommander.app/Contents/Resources/Java/AppleCommander.jar -d $1.po $1
java -jar /Applications/AppleCommander.app/Contents/Resources/Java/AppleCommander.jar
-p $1.po $1 bin $2 < $1
osascript "Virtual][Emulation.scpt"
Here is the Virtual][Emulation.scpt as created in the MacOS Script Editor, naturally the paths, etc. are tailored for my environment.
Script Editor
The script launches the default machine and I have that set up as an Apple II+ as that is what I am currently targeting. The AppleScript can be tailored to launch any specific machine configuration as required.
VS Code Tasks
Setting up a build and test task for the build-assemble and build-test bash scripts is fairly straight forward.
I simply edited the tasks.json file (in the .vscode directory) the tasks.json file to be as follows:
{
"version": "0.1.0",
"command": "",
"isShellCommand": true,
"args": [],
"showOutput": "always",
"echoCommand": true,
"suppressTaskName": true,
"tasks":[
{
"taskName": "build-assemble",
"isBuildCommand": true,
"command": "./build-assemble",
"isShellCommand": true,
"args": ["sgmail"],
"showOutput":"always"
},
{
"taskName": "build-test",
"isTestCommand": true,
"command": "./build-test",
"isShellCommand": true
"args": ["sgmail", "0x8000"],
"showOutput":"always"
}
]
}
To build (assemble) the code, I use the standard VSCode shortcut shift+cmd+B. This assembles the code and reports in the internal terminal.
I created a new shortcut to run the test task shift+cmd+T. This deletes any previous binary from the disk image before adding the newly compiled one, it then launches the Virtual II emulator at 3x speed, inserts the disk, boots to ProDos and invokes the binary.
Naturally, the whole process could be automated further but for me, this is a very workable solution. Once I am happy with the code in the emulator, the latest disk image can be popped on to the physical machine using something like ADTPro. In my case, I simply copy it to the SD card of a Floppy EMU device.
Enjoy!