I have an emulator up and running and access to the asimov ftp site, and I want to learn how to program assembly language on the Apple IIe. What assembler should I use (Merlin? LISA?) and what book or books are the best? Ultimately my goal is to write some games.
I realize the OP asked specifically about Assembly Language, but I felt strongly enough about the quality of "Machine Language for Beginners" that I wanted to post it as an answer.
Given that the Apple IIe has a pretty decent built-in monitor, this book is a natural fit for getting the basics down. I completely understand the utility of a good assembler, but sometimes (especially in the 8 bit world) you can get along just fine writing ML.
You can find the PDF version here.
For getting your first taste of 6502 assembly, I recommend doing the web-based tutorial Easy 6502. You should be able to get through it in a few hours.
Once you've got the basic ideas down, if you're going to learn 6502 assembler at the level of writing non-trivial routines and programs you're going to have to write a fair amount of it.
I recently wrote a post describing my experience getting set up with a traditional self-hosted development system, EDASM on the Apple II. It's certainly worthwhile to learn and interesting to play with, but I don't recommend self-hosting for serious software development as it's compartively slow and painful for no real benefit.
For doing this you should consider cross-development: assembling and doing at least some of your testing on a larger machine (such as a modern Linux or Windows box) and then moving your code over to a full target machine simulator or a target machine itself for the final testing. This general technique is not ahistorical: the first versions of Microsoft BASIC, for example, were written, assembled and tested (in an 8080 simulator) on a PDP-10 before being run on its first target platform, the Altair 8800.
If you're familiar with (and, even better, comfortable with) automated unit testing, I've found this to be an immense aid to learning and programming assembly. It does take some ingenuity, though.
My 8bit development system, which I use for generic 6502 code and Apple I and Apple II development (but which supports other CPUs and platforms) has the following components:
- Git for revision control.
- Linux as the host platform, though Windows could work just as well if you're comfortable with that.
- Vim for editing and command-line scripts for building and testing.
- The Macro Assembler AS for assembly, though I've also used ASxxxx in the past. My scripts are able to download and build both of these, and install them into a directory in the project.
- py65 as the 6502 CPU simulator in which I run my unit tests.
- A unit test framework ("testmc", which I wrote myself) which loads the object files output by the assembler into simulator memory, loads the symbol table used by my tests, and runs pytest unit tests against the code.
- Various (again, custom-written) scripts for transfer of code to target systems, such as wozmon-deposit to generate "hex records" for input for the Apple 1 monitor and a1send to send the output of that across a serial line to my Apple 1 clone. Similar scripts can be used to generate disk images, start a complete system simulator running your code, and so on.
@pytest.mark.parametrize('char, num', [ ('0', 0), ('1', 1), ('8', 8), ('9', 9), ('A',10), ('a',10), ('F',15), ('f',15), ('G',16), ('g',16), ('Z',35), ('z',35), ('_', 40), ('\x7F', 40) ]) def test_convascdigit_good(M, char, num): M.call(M.symtab.convascdigit, R(a=ord(char), N=1)) assert R(a=num, N=0) == M.regs
This is actually 14 unit tests, each of which has an input character
and expected output value. It loads the A register with that
character, sets the N flag, calls
convascdigit, and then asserts
that after the routine has returned the A register contains the
expected output and the N flag is clear (indicating no error). You'll
note that it's heavily oriented toward testing edge cases.
Especially when you're starting out, having tests at this level of detail can be very helpful. Issues like off-by-one errors are rampant in assembly as compared to higher-level languages, and often manifest themselves by mysterious crashes, frequently in some different part of the code becuase you made a mistake setting up a value somewhere. (Think about the first time you learned about pointers in C, and then multiply that by ten.)