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
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).
Instructions
Instructions
Thiscomputabledocument
interface
Activate assembler
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[]=
Note that this should be done before using the dynamic interface or, if this option is available, loading any code below.
Preliminaries
Preliminaries
This file:
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 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
Introduction
Thiscomputabledocument
interface
AQA “cheat sheet”
AQA “cheat sheet”
Limitations, extensions and variations from the published specifications
Limitations, extensions and variations from the published specifications
Blank lines
Blank lines
You can now include blank lines in your code.
Comments
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
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
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
Setup
Load and save directories
Load and save directories
In[]:=
originalDirectory=Directory[];NotebookDirectory[]
In[]:=
SetDirectory[NotebookDirectory[]]
In[]:=
Needs["FunctionalParsers`"]
Utilities
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_]:=pasmRegProcess=asmReg[ToExpression[StringDrop[#,1]]]&;asmLiteralProcess[n_]:=asmLiteral[to2sComplement[n,$wordLength]](*thisisnotperfectbutrepresentsacompromisesolution*)
The parser generator
The parser generator
The grammar of the assembly language
The grammar of the assembly language
Grammar for recognition only
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
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
This next short § generates the parser definitions e.g. pLINE
The operational assembler
The operational assembler
Running
Input and output of code
Input and output of code
Interface to loading from file
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)
Code examples (inline)
Example 1
Example 1
Run the code for 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
Example 2
Final version of counter/timer
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
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
2-s complement arithmetic
Notes
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
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
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
by Matthew Fairtlough
Wolfram Community, STAFF PICKS, September 13, 2024
https://community.wolfram.com/groups/-/m/t/3273551