This is a review of the softcover book “8086/88 Assembly Language Programming” by Leo J. Scanlon with ISBN 0-89303-424-X under restrictive copyright licensing from 1984.

One may claim that the publication is pretty outdated and therefore of little practical use, and this assessment is partially correct in a way. Obviously, for only covering the assembly programming language of the ASM86 assembler and the 16-bit Intel 8086 central processing unit (CPU) instruction set of its time, a reader doesn’t get any information about more modern, later x86 assembly language versions. Similarly, there’s almost no hint about the mechanisms of the operating system (OS) besides interrupt dispatch, as a translated executable program is simply invoked by a DOS and returns back to such. Meaning, there is no discussion of address relocation enacted by memory virtualization/mapping (granted, this might be semi-“invisible” to the running program anyway). Also, nothing about threading, and little interfacing with operating system affordances (just obtaining input from the keyboard is mimicked) can be found. In that regard, CPU programming is presented in an early, rather “pure” and simple fashion.

Threading, memory protection, and the actions of a loader to support the former are left to custom implementation by an OS, not done differently from the assembly programmer who develops a “user space” application. On the other hand, the 8086 chip introduced segments to enhance the maximum of addressable memory despite the 16-bit instruction operand size limitation, so to even avoid this extension and complication, the option might be to go down to the 8080/8085 CPUs where segmenting was not baked into the addressing scheme yet. In that regard, it’s really up to the selection of the chip model which determines how bare-bone or convoluted the description of the processor operations and related instruction set gets. The 8086 marks a certain spot that became the basis for later development – while historically still sufficiently simple, it already benefitted from various additional features for improved convenience. This choice of model makes for a decent intro to basic assembly language programming and CPU operation concepts, despite not being much applicable for modern work. The differences to the 8088 are minimal.

Some prior experience with programming or familiarity with the general operations of computational hardware is helpful, as the text doesn’t spend much effort on explaining elementary programming concepts. Here too the typical assumption is that the reader has a background in programming with high-level languages, and now takes a look at assembly for performance optimization. No surprise one finds the obligatory complaint about slow interpreted languages, and of course BASIC is cited in particular.

Contentwise, regarding examples for what a reader can still get from the book: it becomes apparent that an assembly language is a separate language in its own right. The actual CPU instruction set is only a subset, besides which there’s an additional layer on top comprised of labels, named declarations, a macro facility, and logic/arithmetic for calculating values during “assemble-time”, all of which get resolved to constant/literal data in the executable. The superset controls the assembler in terms of how to assemble what. As the processor relies on pre-set state in CPU register memory during the execution of an instruction and writes results back to registers, a programmer needs to spend a lot more effort on the low-level logistics of moving data between regular memory, CPU registers, operation result flag indicators and the stack memory, considering details like whether a control flow jump target is in near or far memory for potential cost of a code segment change.

Extensions for improved performance and convenience get their respective treatment – string instructions had been new in the 8086, and the numeric data processor 8087 had become available as well. Especially the NDP needs some extra coordination with the 8086 CPU for exchanging data over the bus, and leaves to wonder if halting the 8086 to wait for the 8087 is an indication of another “duct-taped” quick&dirty solution, instead of a proper parallelized, concurrent approach. Additional topics include: the implementation of numeric calculations, which offers very useful recipes for the essential math operators on operands that exceed the maximum value range of the instruction operand size (fortunately, some are safe regarding carry and borrow per naturally not running the risk of overflow or underflow). However, one can’t save the hassle in the case of taking a many-byte product from multiplication. Therefore, depending on the desired application, one might need to develop at least some custom math package parts to overcome the lack of native hardware support. Another extra chapter dives into the handling of lists and data structures. Today, these might be of less interest, as most people are used to the widespread container constructs in high-level languages, or databases, etc. If coding such where needed in assembler, it’s not unreasonable to assume that a programmer would do so in a more sophisticated, dynamic manner than in the 80’s style as outlined in the book.

A few exercises are suggested, along with their solutions. Also, a printed table allows the calculation of instruction cycle time costs depending on the addressing mode used. Instruction indexes are organized in alphabetical order or by functional groups (with columns dedicated to show which flags are affected). The classical number system conversion tables could now be seen as filler, but without general Internet access or a computer or pre-existing utility software, these might have served their quick lookup purpose. The occasional flow-charts seem justified when programming in assembly, because assembler code tends to be unwieldy to read, therefore diagramming helps to reason about correctness and flow.

Negative points could be that no toolchain or debugging guidance is provided. It’s essentially strictly a language/programming book, leaving it to the user to already have obtained an assembler. Back in the day, a developer might either already have gotten one, or not. The instruction set is listed by mnemonics only, without the corresponding numeric machine opcodes. Subsequently, the reference is not sufficient for developing an assembler, compiler (backend or optimizer), or loader. Sometimes it’s left difficult to follow, for constructs being used without much explanation and no back links to their introduction. In various places, page references would have been nice. The table of contents doesn’t expand down to 3rd-level sub-headers, and finding a section via the sparse index might often not work out for lack of an entry.

Layout and typesetting are decent. The few typos, sometimes in the code listing, can be excused, as the manuscript might not have been written and prepared on a computer. Anyhow, this book is probably one of the better presentations of the material, didactically leaning towards the gentle approach and not too confusing nor convoluted.

Copyright (C) 2025 Stephan Kreutzer. This text is licensed under the GNU Affero General Public License 3 + any later version and/or under the Creative Commons Attribution-ShareAlike 4.0 International.