I thought I’d share this here for anyone who may be interested in tools for teaching Computer Science. It is a(n almost) faithful reproduction of the assembly language specification of the AQA syllabus for A level Computer Science, and I developed it in the course of my teaching work at South Devon College. My students found it helpful in learning assembly language. We explored the tool together in implementing Bubble sort (another part of the specification, not usually connected to this one). However, using the published specification, Bubble sort cannot be implemented, as the next paragraph explains.
​
AQA have not precisely defined the syntax or meaning of <memory ref>, but previous exam questions have interpreted this simply as a (decimal) number. However, this limitation has the effect of creating a very weakly expressive language. I have checked with the chief examiner and he confirmed that this was the examiners’ intention. However, this limitation means that non-trivial tasks-such as iterating over a consecutive set of memory locations-cannot be implemented. Expanding the language to permit the coding of such tasks means that indirect memory addressing must be allowed (in this notebook, only through registers, not through memory). In practice, given their existing (and necessary) familiarity with variables that act as array indices, students have no trouble with using this concept, especially if they have first been shown examples of its use. Thus, <memory ref> can, in this implementation, either be a plain (decimal) number or a register Rm. So LDR Rd, Rm means: load the value stored in the memory location (specified by the contents of register m) into register d; STR Rd, Rm means: store the value that is in register d into the memory location stored in register m. This change is enough to make the language Turing-powerful.
​
I make heavy use of Anton Antonov’s Functional Parsers library. I cannot recommend it highly enough! Functional parsers, Mathematica package, MathematicaForPrediction at GitHub
AS/A level Computer Science, South Devon College
Copyright © Matthew Fairtlough & South Devon College, November, 2024. Published under the Creative Commons Licence CC BY-SA 3.0 (Attribution-ShareAlike 3.0 Unported).
Comments, bug reports and suggestions are welcome.
Email the author
.

Instructions

Thiscomputabledocument
implements an assembler for the assembly language specified for the AQA AS and A level qualifications in Computer Science. If you want to get stuck in immediately, check out the
interface
to load and run your assembly language programs. A couple of these are distributed with this document and it is expected that more will be added as time goes on.

Activate assembler

Click the button below to evaluate initialization cells to add the code for assembly language execution to this and other Wolfram documents:
In[]:=
DynamicModule[{activated=False,message="Activate assembler"},​​Button[Dynamic@message,If[activated,message="Assembler already activated",(FrontEndExecute[FrontEndToken[InputNotebook[],"EvaluateInitialization"]];message="Assembler activated";activated=True)]]]
Out[]=
Assembler activated
Note that this should be done before using the dynamic interface or, if this option is available, loading any code below.

Preliminaries

This file:

In[]:=
NotebookFileName[]
Out[]=
C:\Users\ahmed\Documents\MOD trials\SP Processed posts\Teaching A level assembly language (AQA specification)\695648475.nb

Version and word length

Version 2.21w1 (word version)
In[]:=
$wordLength=16; ​​(* change me before activating assembler to alter the word length *)
In[]:=
$version="2.21w1";
In[]:=
$assemblyVersion=2.21;

Introduction

Thiscomputabledocument
implements an assembler for the assembly language specified for the AQA AS and A level qualifications in Computer Science. If you want to get stuck in immediately, check out the
interface
to load and run your assembly language programs. A couple of these are distributed with this document. Alternatively, read on to learn more about AQA assembly language and its grammar and implementation in this computable document.

AQA “cheat sheet”


Limitations, extensions and variations from the published specifications

Blank lines

You can now include blank lines in your code.

Comments

Comments in code are now supported; they must begin and end with ‘;’. The colon after an instruction label must have at least one space before and after. I recommend exactly one space in each case for maximum compatibility with the published syntax.
See the code examples below for what is required.

Numerical literals

Support for binary or hexadecimal notation for numerical literals is not provided; all values are in decimal although the interface provides the option to view register contents in binary. This accords with the format of exam questions, by the way. Bit shifts and logical operations work by first translating to binary, then carrying out the operation and finally translating back to decimal. There is also limited and undocumented support for exploring 2’s complement formats. Arithmetic overflow may result in negative decimal numbers.

Indirect addressing

Note that AQA have not precisely defined the syntax or meaning of <memory ref>, but previous exam questions have interpreted this simply as a (decimal) number. However, this limitation has the effect of creating a very weakly expressive language. I have checked with the chief examiner and he confirms that this was the examiners’ intention. However, I found that this limitation means that non-trivial tasks—such as iterating over a consecutive set of memory locations—cannot be implemented. Expanding the language to permit the coding of such tasks means that indirect memory addressing must be allowed (in this case, only through registers, not through memory). In practice, given their existing (and necessary) familiarity with variables that act as array indices, students have no trouble with using this concept, especially if they have first been shown examples of its use. Thus, <memory ref> can, in this implementation, either be a plain (decimal) number or a register Rm. So LDR Rd, Rm means: load the value stored in the memory location (specified by the contents of register m) into register d; STR Rd, Rm means: store the value that is in register d into the memory location stored in register m. An alternative would be to replace the instructions LDR Rd <memory ref> and STR Rd <memory ref> by LDR Rd <operand2> and STR Rd <operand2> respectively. This would make the syntax of LDR and STR more consistent with that of the other instructions. I have not checked whether this would also be consistent with the original ARM instruction set.
Coding
The cdf version of this document is (necessarily) standalone and thus the code used to build it cannot be run with the CDF player, but only with a full Mathematica system. The full code has however been included under a Creative Commons licence. All code below has been written by the author but building the parsers depends on importing the Functional Parsers library [1].

Setup

Load and save directories

In[]:=
originalDirectory=Directory[];​​NotebookDirectory[]
In[]:=
SetDirectory[NotebookDirectory[]]
In[]:=
Needs["FunctionalParsers`"]

Utilities

In[]:=
mkConst[f_]:=(ClearAll[f];f[]:=f)
In[]:=
mkConst/@{asmBcond,asmMemRef,asmInstr,asmReg,asmLabel,asmOperand,asmLiteral,asmLine};
In[]:=
Clear[asmBcond,asmRegProcess,asmLiteralProcess]​​(*itispossiblejusttosetupthecomputationsthatthefunctionsdoinadvancee.g.*)​​(*asmBcond["BEQ"]:=asmBranchOnEq*)​​asmBcond[p_]:=p​​asmRegProcess=asmReg[ToExpression[StringDrop[#,1]]]&;​​asmLiteralProcess[n_]:=asmLiteral[to2sComplement[n,$wordLength]]​​(*thisisnotperfectbutrepresentsacompromisesolution*)

The parser generator

The grammar of the assembly language

Grammar for recognition only

This grammar is in EBNF (Extended Backus-Naur Form), which is a common format for specifying and implementing grammars. BNF is not an AS-level topic but is useful background information and will be examined in the A level (but not Extended BNF). It formally specifies the grammar of the assembly language implemented in this notebook. The grammar below does not specify a result for the parser; it just parses it, essentially returning a yes/no answer, where ‘yes’ means: the program to be parsed is syntactically valid and ‘no’ means: the program to be parsed is not valid.
Further development could allow you to check your assembly language program for syntactic correctness before trying to run it.
In[]:=
assemblyGrammar="<assembly> = <line> | (<line> , <assembly>) ;<line> = [<labelling>] , <instruction> , [(';' , [<comments>], ';')] ;<comments> = <comment> | (<comment> , <comments>) ;<comment> = '_WordString' ;<labelling> = <label> , ':' ;<label> = '_WordString' ;<instruction> = (<opcode1> , (<reg> , ',') , <mem-ref>) | (<opcode2> , (<reg> , ',') , ((<reg> , ',') , <operand>)) | (<opcode3> , (<reg> , ',') , <operand>) | ('CMP' , (<reg> , ',') , <operand>) | (<bcond> , <label>) | 'HALT' ;<opcode1> = 'LDR' | 'STR' ;<opcode2> = 'ADD' | 'SUB' | 'AND' | 'ORR' | 'EOR' | 'LSL' | 'LSR' ;<opcode3> = 'MOV' | 'CMP' | 'MVN' ;<bcond> = 'B' | 'BEQ' | 'BNE' | 'BGT' | 'BLT' ;<reg> = ('R0' | 'R1' | 'R2' | 'R3' | 'R4' | 'R5' | 'R6' | 'R7' | 'R8' | 'R9' | 'R10' | 'R11' | 'R12') ;<mem-ref> = 'Range[0,65535]' ;<literal> = ('#' , 'Range[-4294967296,4294967295]') ;<operand> = (<literal> | <reg>) ;";

Grammar to compute elements of the parse tree

The grammar below is written in EBNF (Extended Backus-Naur Form) and has two functions: the first being precisely to document the syntax of the assembly language implemented in this computable document and the second to build a parser for the language that turns the text of a program into a usable parse tree; in fact one which is used to execute individual instructions in the assembly language interpreter.
In[]:=
operationalAssemblyGrammar="<assembly> = <line> | (<line> , <assembly>) ;<line> = ([<labelling>] , [<instruction>]) ⊲ [(';' , [<comments>], ';')] <@ asmLine ;<comments> = <comment> | (<comment> , <comments>) ;<comment> = '_WordString' ;<labelling> = <label> ⊲ ':' <@ asmLabel ;<label> = '_WordString' ;<instruction> = ('OUT' , '_WordString') |(<opcode1> , (<reg> ⊲ ',') , <mem-ref>) | (<opcode2> , (<reg> ⊲ ',') , ((<reg> ⊲ ',') , <operand>)) | (<opcode3> , (<reg> ⊲ ',') , <operand>) | ('CMP' , (<reg> ⊲ ',') , <operand>) | (<bcond> , <label>) | 'HALT' <@ asmInstr ;<opcode1> = 'LDR' | 'STR' ;<opcode2> = 'ADD' | 'SUB' | 'AND' | 'ORR' | 'EOR' | 'LSL' | 'LSR' ;<opcode3> = 'MOV' | 'CMP' | 'MVN' ;<bcond> = 'B' | 'BEQ' | 'BNE' | 'BGT' | 'BLT' ;<reg> = ('R0' | 'R1' | 'R2' | 'R3' | 'R4' | 'R5' | 'R6' | 'R7' | 'R8' | 'R9' | 'R10' | 'R11' | 'R12') <@ asmRegProcess ;<mem-ref> = 'Range[0,65535]' | <reg> <@ asmMemRef ;<literal> = ('#' ⊳ 'Range[-4294967296,4294967295]') <@ asmLiteralProcess ;<operand> = (<literal> | <reg>) <@ asmOperand ;";

This next short § generates the parser definitions e.g. pLINE

The operational assembler

Running

Input and output of code

Interface to loading from file

Use the interface below to load your code and assemble and run it. Make sure you have activated the assembler first!

Code examples (inline)

Example 1

Run the code for example 1

Use the runMyProgram function to execute your program and inspect the results. Currently the registers hold the value of their own register value as this aids testing. If you want to reset all registers to zero, try adding assembler code to do this before the main body of your code.

Example 2

Final version of counter/timer

Test below fails if more than one step is executed, and breaks if code is stepped through to the second “instruction”. This is basically an “out of memory” crash, compounded by the fact that there is no HALT instruction so the machine tries to execute the next instruction when there is none. An object lesson in how (not) to fail gracefully. The moral is: always include a HALT instruction at the end of your code.

More sophisticated memory addressing

The code below accesses memory locations which are stored in registers. This allows us to actually do useful things with memory.

2-s complement arithmetic

Notes

We want to mimic typical chip design constraints on word size and the arithmetic issues of over (or under-) flow that arise from this. Mathematica has a large word size (in fact, unlimited!) but we want to explore the issues of word size using a much smaller value to make these issues clearer.

Credits and references

This document would not be possible without the prior work of Anton Antonov [1] on implementing Functional Parsers in Mathematica.
[1] Anton Antonov, Functional parsers, Mathematica package, MathematicaForPrediction at GitHub, 2014.

CITE THIS NOTEBOOK

Teaching A level assembly language (AQA specification)​
by Matthew Fairtlough​
Wolfram Community, STAFF PICKS, September 13, 2024
​https://community.wolfram.com/groups/-/m/t/3273551