In 2020, I released a new simulator called ARMlite in conjunction with a new A-level textbook on Assembly Language Programming written by Richard Pawson. ARMlite has more memory (1 MB), a cleaner interface, an extended instruction set (interrupts, subroutines, a stack etc.) and is much higher performance (1-10 million instructions per second, depending on your browser and computer). You can get the student version of Richard's book from the ARMlite documentation page or the teachers' version by registering with CAS.
The latest Version of this AQA Simulation is V0.08 - see the end of this document for the new features in V0.07 and V0.08
The object of this simulator is to allow students to gain some familiarity with the Assembly Language specified by AQA for use in the AS and A level computer science papers. The implementation took my previous LMC simulator as a base and anyone familiar with that will hopefully find this easy to use. You can write a program in the Assembly Language area, change the contents of memory or the PC and show memory in signed (default), unsigned, hex or binary. You can add spaces and comments in sensible places.
There is a project for the AQA simulation written by Richard Pawson Write a complete Snake game in AQA Assembly Language.
AQA do not specify their language in enough detail to create an assembler so some assumptions have to be made.
Word Size. AQA do not specify a word size, in one exam paper they draw a register as 8 bits and in another have the answer to a question as 860 (which is more than 8 bits). In one 2017 A level question they say "Each main memory location and register can store a 16-bit value" - but that is in the question and not in the general specification. Since they have thirteen registers, the more complex instructions (like ADD R2,R3,#100) cannot be encoded in less than about 20 bits. So a word size of 16 bits is impossible and 32 bits is what I have implemented.
Instruction Encoding. I decided to use the ARM 32 bit instruction encoding exactly (and not to extend or simplify it). This will be a problem for anyone looking at the instructions (because it is complex) and an advantage to anyone using the Raspberry Pi or MicroBit at a low level.
Addressing. The ARM computers (and most modern computers) address memory in terms of bytes. The AQA example questions (with addresses 100,101,102, 103) suggest this might be the way that AQA was thinking but then they use the LDR instruction rather than LDRB and talk about line numbers in the program. I did originally implement byte addressing (you can select "byte mode" to see this) but decided that word addressing was much easier for the visual display. In a recent note AQA says that labels must be used for memory addressing so the 8/32 bit addressing choice is merely one of display.
PC and MAR Display. These change if the addressing mode changes. ARM uses byte mode and the PC holds the byte address of the next instruction. The simulator defaults to word mode and the PC is shown divided by 4 - this is much easier to understand. Similarly byte extraction is normally a CPU function and so the MAR is correct in word mode but probably wrong in byte mode. (This is beyond the syllabus.)
Compare. In ARM, BGT and BLT are 2's complement compare instructions so that is the way they work in this simulator. If AQA wanted an unsigned comparison the instructions are BLO and BHI which they do not have (but I might implement as an extension if I get enough requests). Note that with 32 bits most simple calculations will not see a problem.
HALT. Halt is not an ARM instruction. The simulation uses the ARM instruction "software interrupt" for this. This is the way calls to an operating system would normally be handled.
Size of Immediate Field. AQA do not specify this but I have not found any values greater than 255. So 8 bits might be a reasonable assumption. After implementing this, I found that not being able to do things like AND R2,R2,#0x8000 to test single bits (of course you really need ANDS) or having to do MOV,LSL,ADD to get constants greater than 255 (when 2 instructions would do) was burdensome. So the simulator now implements the same rules as the ARM i.e. any 8 bit pattern rotated an even number of places (e.g. 512, 0xC8000003, 260, 4096 and many others are valid).
Labels. AQA have adopted a format where the label is terminated by a colon (:) and usually on a line of its own. Since every assembler I have ever used allowed the label to be on the same line as the instruction at that address, the simulator will accept both formats and I have changed some examples to match the "line on own" method. Since labels do not occupy memory, not having an instruction causes a discrepancy between program line numbers and memory addresses. I have added the option "line number" to show line numbers in the Assembly Language area and the option "address" to show addresses (by not numbering label definitions on a line of their own). The default is address and this seems the most useful. (A comment on a line on its own is never numbered by the simulator - but AQA do not have comments anyway.)
The AQA Instruction Set.
LDR Rd, <memory ref> Load the value stored in the memory location specified by <memory ref> into register d.
STR Rd, <memory ref> Store the value that is in register d into the memory location specified by <memory ref>.
ADD Rd, Rn, <operand2> Add the value specified in <operand2> to the value in register n and store the result in register d.
SUB Rd, Rn, <operand2> Subtract the value specified by <operand2> from the value in register n and store the result in register d.
MOV Rd, <operand2> Copy the value specified by <operand2> into register d.
CMP Rn, <operand2> Compare the value stored in register n with the value specified by <operand2>.
B <label> Always branch to the instruction at position <label> in the program.
B<condition> <label> Conditionally branch to the instruction at position <label> in the program if the last comparison met the criteria specified by the <condition>. Possible values for <condition> and their meaning are: EQ:Equal to, NE:Not equal to, GT:Greater than, LT:Less than.
AND Rd, Rn, <operand2> Perform a bitwise logical AND operation between the value in register n and the value specified by <operand2> and store the result in register d.
ORR Rd, Rn, <operand2> Perform a bitwise logical OR operation between the value in register n and the value specified by <operand2> and store the result in register d.
EOR Rd, Rn, <operand2> Perform a bitwise logical exclusive or (XOR) operation between the value in register n and the value specified by <operand2> and store the result in register d.
MVN Rd, <operand2> Perform a bitwise logical NOT operation on the value specified by <operand2> and store the result in register d.
LSL Rd, Rn, <operand2> Logically shift left the value stored in register n by the number of bits specified by <operand2> and store the result in register d.
LSR Rd, Rn, <operand2> Logically shift right the value stored in register n by the number of bits specified by <operand2> and store the result in register d.
HALT Stop the execution of the program.
<operand2> can be #nnn or Rm to use either a constant or the contents of register Rm.
Extension - data
The pseudo-instruction DAT allows you to put a number into a memory cell using the assembler. You can leave out the DAT and just put the number. A label as data will also work.
Extensions - INP and OUT.
These work the same way as on the LMC. INP Rd,2 reads a number into register d and OUT Rd,4 outputs the number from register d. For OUT, device 4 is treated as signed but you can output unsigned (device 5), hex (device 6) or character (device 7). You can input hex as 0xnnn everywhere a number is expected. Like HALT, INP and OUT use the Software Interrupt instruction - this is similar to the way programs call the Operating System or the BIOS to do I/O.
In the 2017 AS paper, AQA use a memory address to control a motor and many modern computers do I/O through addresses rather than special purpose instructions. However on all systems I know of the I/O addresses are not near the memory addresses and the STR R0, 17 (used in the question) could not be assembled using any part of the ARM instruction set. (The correct code is MOV R1,#17; STR R0, [R1] but AQA don't have indirect addressing either.)
Extension - Indirect Addressing.
You can, as people did with the LMC, address a list (or array) by building your own instruction and then executing it. Since AQA do not specify the instruction format you cannot do that with their instructions and neither is there any way to find out what number is associated with a label or memory ref (in the AQA specification). Most modern computers do not allow you to modify your own instructions (or in some the result may be unpredictable). ARM does not have any direct memory instructions - if you look how LDR and STR are encoded, you will find that the addresses are formed by adding or subtracting numbers from the PC (called relative addressing).
So for LDR and STR I have implemented what ARM call "register offset addressing" where you write [Rn+<label>] and the address used is formed by adding the contents of the register to the label. In the default word mode the register is incremented by one to access the next word. In byte mode you need to add 4 (which is the same as ARM).
If you just say [Rn] then the address used is the contents of the register. This is true "indirect addressing" but you have no way in the AQA instruction set of knowing the real memory addresses. In the simulator this can be done.
It is also possible to use [Rn+xxx] where xxx is a positive number and the number of words to add to the register contents (not bytes). Note that the byte/word mode selection affects the use of the register contents during execution but not the interpretation of the offset xxx by the assembler.
Extension - Comments.
Comments are useful so anything after a '/' is taken as a comment. In the examples, I have used '//' to follow the style of many higher level languages.
Building your own ARM instructions is not easy and with modern computers self-modifying code is regarded as poor practice. However in this simulator and with some instructions there are simple changes you can make. An important thing to remember is that ARM does not have direct addressing and so all the AQA direct instructions are actually implemented as an offset from "PC+8" (i.e. the instruction after the next one).
For Branch instructions you can add or subtract the number of words to change the destination of the branch (i.e. bytes/4).
For LDR and STR instructions you can add or subtract a number of bytes (i.e. words*4) to a forward referencing instruction providing both the original and modified addresses are greater than PC+8 and the final address is within the range of the simulator. (Other cases are more complex.)
For the non-shift #immediate instructions, if the immediate value is between 0 and 255 you can add to or subtract from the instruction providing the immediate result is also between 0 and 255.
Just because it is possible does not mean you have to do it. Please use the indirect addressing extension instead.
MAR and MBR
As some of you may know, I don't agree with MAR and MBR because it is very hard to find anything that exactly implements them in any modern CPU design I have ever seen. However they are in your syllabus so I thought I would give in and put them where they might actually be. For LDR/STR you may notice a slight stutter in some modes while the simulator shows you the instruction fetch in MAR/MBR followed by the values used by the LDR or STR. In all other cases (if you single step) the instruction fetch is the only memory reference.
The MAR always shows the address in denary and the MBR always shows the data in hexadecimal. (I know this is not particularly consistent but a signed MBR would need three more digits and binary one 24 more digits.)
"SELECT" allows you to choose one of a few example programs to assemble and, if you wish, alter and/or run.
There are some options under the "OPTIONS" drop down. "Clr memory" clears memory and the next four ("signed", "unsigned", "hex" and "binary") select the mode to show the contents of memory and the registers. Signed means two's complement signed and hex is short for hexadecimal (base 16 with a=10, b=11, c=12, d=13, e=14 and f =15). Binary only shows the memory in binary. The data blobs normally follow the display mode (except binary which would be too big) but the addressing blobs (red) are normally in denary.
The next four options control the speed of execution. "def normal" is the default speed (7.5) showing the moving data blobs. Once running you can change the speed from 0 (def slow) to 12.5 and still see the moving blobs. "def execute" is the slowest speed (15) to execute without the delay of moving blobs and "def fast" (24) is the fastest the simulator will go and still show all progress on the screen. You can increase the speed up to 24.9 while running at the expense of not refreshing the screen after each instruction.
The next two options control the memory addressing mode (byte/word). The last two control whether the Assembly Language window shows you the line number in the program or the word address in memory.
The Status Register holds the result of the last CMP operation - negative (N), zero (Z), carry (C) and overflow (V). Overflow is for a 2's complement compare whereas Carry means no overflow for the compare seen as an unsigned operation.
The Z flag is tested by the BEQ and BNE instructions. BLT tests (N set and V clear) OR (N clear and V set) and BGT tests Z clear AND (N and V both set or both clear). AQA don't give you instructions to use or test C. (C is important for doing extended and/or unsigned arithmetic - for example 64 bits or more on a 32 bit computer or 32 bits as unsigned.)
In the AQA instruction set, CMP is the only instruction that sets the status bits.
This is animated by moving blobs. As I expected the size of some of the data blobs with 32 bit numbers is sometimes an issue and there seem to be browsers that do not scale text and graphics at the same rate.
This was produced in co-operation with who has produced a set of course notes culminating in a snake game which you can play on the simulator. V0.07 has a number of extra features designed to make the operation of the game possible and the programming task slightly simpler. The SELECT option "New I/O" is a example of using the new features.
Video Memory. The output area can also be addressed as a 32 column by 24 row array, starting at address 256 in the top left and ending at address 1023 in the bottom right. The STR instruction is used to write a 24 bit HTML Colour Code to the pixel address. (So, for example, 0 is black and 0xff0000 is red.) Note that the top 8 bits in the register are ignored and the 24 bit pattern can be read using LDR.
Additional Input Device Codes. INP Rd,4 reads the Key Code of the last key pressed on the keyboard into the register d. If no key has been pressed a zero is returned. (Device Code 5 clears the character on read so you can tell if a new key has been pressed.) The Key Code for a (or A) is 65 and for 1 is 49. Beware that using any key with a Browser function may behave unpredictably.
Additional Input Device Code. INP Rd,8 reads a random 32 bit pattern into the register d.
Additional Output Device code. OUT Rd,8 uses the contents of the register d as the address of a null terminated ASCII string in memory which is written to the output area. Note the characters need to be in little-endian order.
Numeric <memory ref>. In previous versions of this simulator <memory ref> can only be a label because my original understanding was that the addressing of memory was hidden (particularly because of the bytes/words conflict). However AQA have set questions that use numeric <memory ref> and so that is now permitted. (Note this does not apply to branch which still requires a label.)
Extended Immediate Range for MOV. AQA do not specify the allowed range of immediate operands. The simulator implements the range allowed by ARM - a simplified version of which is a byte in any of the four positions in a word. It is hard to explain to students why, for example, MOV R1,#768 is valid but MOV R1,#767 is not allowed. To avoid this unnecessary complication and to make HTML Colour Codes usable as immediate operand values, I have implemented an extended immediate range for the MOV instruction. Any 26 bit positive value can be used (only with MOV). AFAIK this is the only departure from valid ARM op-codes. (Assuming you had a handler for SPI used for HALT, INP and OUT. The extended MOV uses other SPI and co-processor op-codes and so is unlikely to be affected by any extension to the AQA instruction set.)
I am grateful to Daniel Stone at Reading School for pointing out that with Chrome or Safari on MacOS, the memory and register cells display incorrectly. The root cause of this is that, in those configurations,   is considerably wider than the space taken up by a number leading to padding pushing numbers beyond the assigned field. (Firefox on MacOS is better but not perfect.)
The best solution I can find (given the way the Simulator is structured) is to use   for padding. In all the tests I have seen this makes no difference to the displays of any browser on Windows OS (which all display correctly) but does fix Chrome and Safari on MacOS. Firefox on MacOS seems to have   bigger and   smaller that the correct number space - so I feel smaller is safer; it is however noticable. There are no other changes between V0.07 and V0.08.
© 2017-8 Peter L Higginson (plh256 at hotmail.com)
I am debugging this using Chrome so if you get any problems if would help me if you could check whether they appear in Chrome as well. Please feel free to send me any comments whatsoever.
The LMC simulator is at www.peterhigginson.co.uk/LMC and the RISC simulator is at www.peterhigginson.co.uk/RISC.
Disclaimer - I am not associated with AQA in any way and this simulator is based on their public documentation.