THE UNIVERSITY OF TEESSIDE

SCHOOL OF COMPUTING AND MATHEMATICS

MIDDLESBROUGH

TS1 3BA

 

ARM SIMULATOR

 

BSc Informatics

April 2000

T.Magnussen

 

 

Supervisor           : Professor A.Clements

Second reader     : A.Siday


 

ABSTRACT

 

This project has produced a software simulation of a ARM processor. A user may run ARM assembly programs and view the results in a graphical interface.

 

The analysis phase of the project involved detailed studies of different ARM architectures and ARM assembly language. Most of the decisions about hardware components to include in the simulation and assembly instructions to support were made during this stage. This phase also involved identifying the requirements of the simulator.

 

The next stage was design, in which the GUI was sketched and sequence and class diagrams were made to model the system structure. Use case diagrams were employed to fulfil the GUI design.

 

The implementation phase involved turning the diagrams into code, following the design as closely as possible.

 

Testing was done for each separate implemented part, and a final test was done at the end of the implementation. Shortage of time made the testing somewhat less extensive than desired.

 

When all was over and done with, all documentation was refined and put together in a final report together with a conclusion.

 

The system is fully implemented and all the major goals were reached. Some recommendations for enhancements have been summarised.

 


ACKNOWLEDGMENTS

 

I would like to thank Professor Alan Clements for his support, technical input and guidance through the project period. Thanks to Jan Andersen for lending me the hardware books that helped me through some difficult times.

 

Endre Moen and Morten Gustavsen provided unavailable help in testing the system.

 


CONTENTS

 

ABSTRACT…................................................................................................................. i

 

ACKNOWLEDGMENTS................................................................................................. ii

 

CONTENTS……............................................................................................................ iii

 

CHAPTER 1 – INTRODUCTION................................................................................... 1

 

CHAPTER 2 – METHODOLOGY................................................................................... 2

 

2.1      Analysis methodologies........................................................................................ 2

2.2      Design methodologies.......................................................................................... 3

2.3      Implementation methodologies.............................................................................. 3

2.4      Testing methodologies.......................................................................................... 4

 

CHAPTER 3 – ARM HARDWARE ANALYSIS............................................................. 5

 

3.1      ARM architectures.............................................................................................. 5

3.2      Registers and register roles.................................................................................. 5

3.3      Condition code flags............................................................................................. 8

3.4      Control bits.......................................................................................................... 8

3.5      Memory formats.................................................................................................. 8

3.6      Simulator constraints............................................................................................ 8

 

CHAPTER 4 – ARM ASSEMBLY LANGUAGE ANALYSIS........................................ 10

 

4.1      The ARM Instruction Set................................................................................... 10

4.2      The selected instruction set................................................................................. 10

4.3      Conditional execution......................................................................................... 11

4.4      The ‘S’ bit......................................................................................................... 12

4.5      Shifts operations................................................................................................ 12

4.6      Register based shift counts................................................................................. 17

4.7      Immediate operand substitution........................................................................... 17

4.8      Negative operands............................................................................................. 17

4.9      Conclusion......................................................................................................... 18

 

CHAPTER 5 – REQUIREMENT ANALYSIS................................................................ 19

 

5.1      An example scenario.......................................................................................... 19

5.2      Specification of requirements.............................................................................. 19

 

CHAPTER 6 – GRAPHICAL USER INTERFACE DESIGN.......................................... 21

 

6.1      GUI Design issues............................................................................................. 21

6.2      Designing the user interface............................................................................... 22

 

CHAPTER 7 – SYSTEM DESIGN................................................................................. 25

 

7.1      Designing the instruction objects......................................................................... 25

7.2      Designing a ’compiler’....................................................................................... 27

7.3      Connecting the designs....................................................................................... 28

7.4      Storing instructions............................................................................................. 29

7.5      Designing a shared datamodel............................................................................. 29

7.6      Memory representation...................................................................................... 30

7.7      Designing a stack............................................................................................... 30

7.8      Sequence and class diagrams............................................................................. 31

 

CHAPTER 8 – IMPLEMENTATION............................................................................ 36

 

8.1      Implementing the GUI........................................................................................ 36

8.2      Implementing the instruction objects.................................................................... 37

8.3      Implementing the instruction reader..................................................................... 37

8.4      Executing instructions......................................................................................... 38

8.5      Conditional execution......................................................................................... 39

8.6      Shift support...................................................................................................... 39

8.7      Branch abilities.................................................................................................. 39

8.8      The Autorun feature.......................................................................................... 39

8.9      Other implementation issues............................................................................... 41

 

CHAPTER 9 – TESTING............................................................................................... 44

 

9.1      Testing large operand substitution........................................................................ 44

9.2      Testing shift routines.......................................................................................... 44

9.3      Testing conditional execution.............................................................................. 45

9.4      Testing negative operand substitution................................................................... 45

9.5      Testing the Simulator with real users................................................................... 45

9.6     Testing the Simulator on different platforms......................................................... 46

 

CHAPTER 10 – CONCLUSIONS.................................................................................. 47

 

10.1    Goals and achievements..................................................................................... 47

   10.2    Personal thoughts............................................................................................... 47

 

CHAPTER 11 – RECOMMENDATIONS...................................................................... 49

 

11.1    Completing the instruction set............................................................................. 49

11.2    Serialization....................................................................................................... 49

11.3    Upgrading the ’compiler’.................................................................................... 49

11.4    Improving the help system.................................................................................. 49

11.5    Converting the Application to an Applet............................................................... 49

11.6    Thumb support................................................................................................... 49

11.7    Other features................................................................................................... 49

 

CHAPTER 12 – BIBLIOGRAPHY................................................................................ 51

APPENDIX A – PROJECT SPECIFICATION............................................................... 53

APPENDIX B – PROJECT TIME SCHEDULE............................................................. 54

APPENDIX C – USER GUIDE...................................................................................... 55

APPENDIX D – TEST RESULTS………………………………………………………..….77

APPENDIX E – USE CASE AND UML DIAGRAMS................................................... 85

APPENDIX F – CHOICE OF DEVELOPMENT LANGUAGE.................................... 124

APPENDIX G – SOURCE CODE................................................................................ 127


CHAPTER 1 – INTRODUCTION

 

The first ARM processor was designed by Acorn Computer Ltd, Cambridge, United Kingdom in 1985. In 1990 Advanced RISC Machines (ARM) was founded and VLSI Technology became their first licensee. Today ARM licenses high-performance, low-cost, power-efficient RISC processors, peripherals, and system-chip designs to leading international electronics companies and have offices worldwide.

 

The objective of the project is to produce a simulation of a ARM processor. The idea of the project came from my supervisor, Professor Alan Clements.

 

This project has the following overall goals:

 

·         Produce a realistic, GUI based, simulation of a ARM processor.

·         The simulator should be able executing ARM assembly programs.

·         The simulator should display ARM component like registers and memory.

·         The simulator should display the effect each instruction has on those registers, in an easy and consistent matter.

 

The chapters in this report have been positioned in the following order, according to the project handbook recommendations:

 

Chapter 2 - Discusses the methodologies used throughout the project.

Chapter 3 - Describes the hardware analysis carried out on the ARM architecture.

Chapter 4 - Describes the assembly analysis carried out.

Chapter 5 - Describes the requirement analysis for the simulator.

Chapter 6 - Describes design of the graphical user interface.

Chapter 7 - Describes the system design phase.

Chapter 8 - Describes the implementation.

Chapter 9 - Describes testing and the approach used in this phase.

Chapter 10 - Overall conclusion of the project.

Chapter 11 - Further recommendations.

 

 

CHAPTER 2 – METHODOLOGY

 

This chapter describes the methodologies that were used throughout the project.

 

2.1       Analysis methodologies

 

The core analysis done for this project involved gathering relevant information about the ARM family of processors, studying assembly language and looking at existing systems. A requirement analysis was written to identify the main requirements of the system.

 

This project was developed with a mixture of methodologies. Most of the object-oriented analysis and design was done by creating diagrams that were of great help when implementing the code itself.

 

UML (Unified Modelling Language) is a standard for modelling object-oriented applications like Java, and it is the evolution of notations used in OOSE (Object Oriented Software Engineering), Booch, and OMT (Object Modelling Technique). Used together with “use case diagrams”, it covers the full lifecycle of an object-oriented project

 

The lifecycle for this project is the ’Mini-Waterfall model’, which is an iterative process. Every iteration consists of analysing, designing, coding and integrating a small part of the system. When an iteration is complete, a new iteration is started dealing with another part of the system.

 

 

 

 

 

 

 


Figure 2.1       The iterative mini waterfall model

 

 

2.2       Design methodologies

 

UML was employed to model the full system. Two UML diagrams were used in conjunction with use case.

 

Use case diagrams describes the interaction between the user and the system. A use case is a task the system should be able to deal with. Each use case defines a subtask of the system. This subtask can in it’s turn be described by another use case diagram, but eventually all use cases is described by a sequence diagram. Use case diagrams complement UML by adding the ability to describe the behaviour of the system from a users viewpoint.

 

Sequence diagrams isolates a use case event and describes the interaction between the system and the user for that particular task.

 

Class diagrams express the structure of a system, in terms of classes and their relationships. A class is described by its class name, it’s attributes and it’s methods.

 

All UML diagrams, whether or not included in the report, can be found in Appendix D.

 

Use case diagrams formed the base of the design phase. Each case within a use case diagram was further broken down and represented by sequence diagrams. Having described an operation, classes were designed or updated to handle that operation.

 

In addition some specific operations, like building the instructions and executing an instruction, were designed using flowchart diagrams to clarify the process.

 

To implement the simulator, decisions would have to be made about its appearance. Based on the use case diagrams, sketches were made. The sketches were mostly created with pen and paper, the same goes for some of the diagrams. All have been recreated in electronic format through the painting tool in Word and Rational Rose 2000.

 

A lot of figures have been created where a quite simple figure can explain as much as any UML- or flowchart- diagram. These figures are very simplified but they all describe the important aspects of what they are designed to represent.

 

Pseudo code was used when applicable but has not been used continuously throughout the project. It was adapted for certain complex methods, like the methods dealing with recognition and verification of instructions and assembly operands.

 

2.3       Implementation methodologies

 

Implementation was closely tied to the development of UML diagrams. All class diagrams created in the design phase were converted into code in the implementation phase.

 

This did of course not always work out the way it should. In these cases, the diagrams were scratched or changed, and the code was written over again or changed to adapt the changes in the diagrams. For complex problems the implementation was carried out based on pseudo code or flowchart diagrams created during design.

 

2.4       Testing methodologies

 

The testing methodologies used within the project comprise of Module, System and Boundary Value testing.

 

Module testing involves testing classes and all it’s methods after the implementation. This is done before the module is integrated with the full system.

 

System testing involves performing tests on the full system, ensuring that all the parts work seamlessly together. A system test is carried out each time a new module is integrated with the full system, to ensure that the modules work seamlessly together.

 

State Boundary testing involves testing critical parts of the system with boundary values, that is the maximum and minimum values the particular part is supposed to handle, and any other values that are known to produce special results.

 

All these methods will be used throughout the project to test each part independently and when integrated with the full system.

 

The final testing will be performed using System and State Boundary testing.

 
CHAPTER 3 – ARM HARDWARE ANALYSIS

 

The objective of the hardware analysis is to gain knowledge about the ARM architecture and to select the components needed to include in the simulator.

 

3.1       ARM architectures

 

There are four major versions of the ARM architecture.

 

The initial architecture was never used in a commercial product. Version 2 was the first to be released and extended the original architecture by adding two new instructions, coprocessor support and two banked registers. The address range was 26-bits and r15 held a 24-bit program counter along with 8-bits of status information. It had three privileged processing modes, Supervisor, IRQ and FIQ.

 

Version 3 of the architecture extended the address range to 32-bits. Register 15 was dedicated to the program counter and the status information was moved to a new, 11-bit, status register. Two more privileged modes were added Undefined and Abort. For each privileged mode a new status register was added, called the SPSR, to preserve the CPSR contents when switching modes.

 

Version 4 added halfword load and store instructions. It defined several new undefined instructions.

 

3.2       Registers and register roles

 

The ARM has a total of 37 registers distributed across six processing modes. 6 of these are status registers, one for each mode, and one is the program counter, which is shared across all modes. The remaining 30 are general-purpose registers.

 

Registers 0-12 are always free for general-purpose use. Registers 13 and 14, is available for general use but have specific roles.

 

Register 13 is also called the SP, or Stack Pointer, and is banked across all modes to provide a private Stack Pointer for each mode.

 

Register r14 is called the LR, or Link Register and is used as the subroutine return address link register. R14 is also banked across all modes.

 

Register r15 is used to hold the PC, or Program Counter. The program counter holds the address of the next instruction to be carried out. R15 is shared over all processor modes.

 

The CPSR is an 11-bit register and contains the condition code flags, current processor mode and interrupt enable flags. See figure 3.2 for details.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


Figure 3.1       The ARM register set

 

 

 

 

 

 


Figure 3.2       The CPSR register

 

3.3       Condition code flags

 

The N, Z, C and V bits are called the condition code flags. These are located in the upper bits of the CPSR and can be changed by arithmetic and logical operations in the processor. The condition code flags determine if an ARM instruction is to be executed or not.

 

 

Flag

 

Description

N

Negative

Set if the last operation generated a negative result.

Z

Zero

Set if the last operation generated a zero result.

C

Carry

Set if the last operation resulted in a carry out from the most significant bit position of the operand.

V

Overflow

Set if the last operation resulted in an arithmetic overflow. That is when an operation on a one or two’s complement number yields a result outside it’s permitted range.

 

Figure 3.3       The Condition Code flags

 

3.4              Control bits

 

The bottom 8-bits of the CPSR is occupied by the control bits, which include two interrupt disable bits, a state bit and five mode bits.

The I and F bit disables respectively the IRQ interrupt and the FIQ interrupt, when set.

Bit T indicates the processor mode. If this bit is set the processor is in Thumb mode. This bit is only used on thumb aware processors (processor capable of running 16-bit Thumb instructions). The last five bits indicates which mode the processor operates in.

 

3.5              Memory formats

 

The ARM view memory as a linear collection of bytes numbered upwards from zero. Since all ARM instructions are 32-bits wide, instructions are stored at 4 byte boundaries. The ARM can treat words in memory as being stored in either Big Endian or Little Endian formats.

 

Big Endian assumes that the most significant byte of a word is stored at the lowest numbered byte, and the least significant byte at the highest numbered byte.

 

Little Endian is the opposite of Big Endian with the most significant byte at the highest numbered byte and the least significant at the lowest numbered byte.

 

3.6              Simulator constraints

 

The ARM Simulator will operate in USER mode only. Architecture 3 and above will be used as model for the simulator and Little Endian will be used as the memory model.

The components that will be visually displayed within the simulator will be the user accessible registers including the CPSR. As an option the user should be able to view memory.

 


CHAPTER 4 – ARM ASSEMBLY LANGUAGE ANALYSIS

 

The objective of the assembly analysis is to gain knowledge about the ARM assembly language and to select an instruction set for the simulator. This chapter also discusses some compiler specific issues.

 

4.1       The ARM Instruction Set

 

The ARM provides instructions for data processing, branches, register transfers, coprocessor interaction, software interrupts and more. Implementing the full set of instructions is a far to extensive task, way beyond the scope of this project, so a subset of commonly used instructions will have to be defined.

 

The software interrupt instruction is used to change the processor into another mode. Since the ARM Simulator will only run in user mode, this instruction is excluded.

The coprocessor instructions are used to interact with other processors. They are quite complex and will be excluded along with the software interrupt instructions.

 

4.2       The selected instruction set

 

The simulator will include data processing (also referred to as ALU instructions), branch and simple register transfer (memory) instructions.

 

ALU instructions may be classified as either logical or arithmetic. The ARM logical operations comprise of AND, EOR, TST, TEQ, ORR, MOV, BIC and MVN. Logical operations perform a logical action on all corresponding bits of the operand(s) to produce the result.

 

The ARM arithmetic operations perform variations of addition and subtraction on the operands and comprise of ADC, ADD, CMN, CMP, RSB, RSC, SBC and SUB. Each operand is treated as a 32-bit integer.

 

The syntax for most ALU instructions is:

 

opcode{ condition }{ S } destination, operand1, operand2

 

Four of the ALU instruction is test instructions and consist of CMP, CMN, TEQ and TST. These operations produce no result other than setting the CPSR bits. Both operands are preserved. The S bit is always implied, however specifying the bit will not cause an error. The syntax for these instructions is:

 

opcode{ condition} operand1, operand2

 

The MOV and MVN ALU instructions move a register or constant into another register. The syntax for these instructions is:

 

opcode{ condition }{ S } destination, operand2

 

Branch and branch with link is required in order to use subroutines. Consequently the system must support labels. A branch forces the program to a new address. If a branch with link is specified, the address of the next instruction is stored in a dedicated register, before performing the branch. These instructions have the following syntax:

 

B{ L }{ condition } < expression >

 

Register transfer operations is used to load and store values from memory. LDR and STR either load or store a value from memory respectively. The ARM also has multiple register transfer operations, capable of loading or storing up to 16 registers with a single instruction.

These will not be supported. The single register transfer operations have the following syntax:

 

opcode{ condition }{ B }{ T } destination,< address >{!}

 

These instructions may be both pre- and post- indexed. Initially the simplest form of the memory instructions will be supported and the pre-indexed form will be given prioritised. This will be sufficient to perform simple memory transactions.

 

Please refer to the last section of the Appendix C for a full description of the instructions discussed in this chapter.

 

4.3       Conditional execution

 

All ARM instructions are conditionally executed. This means that the operation may or may not be carried out depending on the state of the status bits in the processor. If the condition is true the instruction is executed, if not the instruction will have no effect except from using one cycle. An instruction is conditionally executed by adding one of 16 possible, 2-character condition codes to the op-code. Every ARM instruction has a 4-bit field holding the condition under which it will be executed.

 

 

Mnemonic

Code

Condition

Meaning

EQ

0000

Z set

Equal

NE

0001

Z clear

Not equal

CS

0010

C set

Unsigned higher or same

CC

0011

C clear

Unsigned lower

MI

0100

N set

Negative

PL

0101

N clear

Positive or zero

VS

0110

V set

Overflow

VC

0111

V clear

No overflow

HI

1000

C set and Z clear

Unsigned higher

LS

1001

C clear or Z set

Unsigned lower or same

GE

1010

N and V the same

Greater or equal

LT

1011

N and V differ

Less than

GT

1100

Z clear, N and V the same

Greater than

LE

1101

Z set, N and V differ

Less than or equal

AL

1110

-

Always execute

NV

1111

-

Never

 

Figure 4.1       ARM Condition Codes

 

4.4       The ‘S’ bit

 

Instructions with the S bit set will update the processor status bits according to the result of the operation. Appending a ‘S’ character to the op-code of an instruction sets the S bit.

For the CMP, CMN, TST and TEQ instructions the S bit is always implied.

 

4.5       Shifts operations

 

Almost all ARM instructions are shift instruction, meaning that an instruction can also perform shifting. A shift is carried out by the Barrel shifter, which can perform any of the LSL, LSR, ASR, ROR and RRX shift operations. The shift value may either be a numeric value or a register. If a numeric value is specified it must be in the range 1 to 31. Register-based shift counts are described in section 4.6.

 

LSL - Logical Shift Left moves each bit by the specified amount to a more significant position. The least significant bits are filled with zeroes. High bits that are shifted outside the 32-bit result range are discarded. The last discarded bit becomes the shifter carry output, which may be applied to the C bit of the CPSR if the instruction involved is a logical operation with the S bit set.

 

 

 

 

 


Figure 4.2       Logical Shift Left.

 

LSR - Logical Shift Right moves each bit by the specified amount to a less significant position. The most significant bits are filled with zeroes. Low bits that are shifted outside the 32-bit result range are discarded. Carry is generated as in LSL.

 

 

 

 

 


Figure 4.3       Logical Shift Right

 

ASR - Arithmetic Shift Right performs almost the same operation as Logical Shift Right. The difference is that the high bits are filled with replicates of the most significant bit of the shifted register instead of zeroes. This means that a negative value is extended with ones and a positive value is extended with zeroes. Carry is generated as in LSR.

 

 

 

 

 

 


Figure 4.4       Arithmetic Shift Right

 

ROR – Rotate Right reuses the bits that ’fall-of’ on the least significant end by wrapping them around at the most significant position of the result. A Carry is generated in the same way as LSR.

 

 

 

 

 

 


Figure 4.5       Rotate Right

 

RRX – Rotate Right Extended shifts the contents one position to the right, inserting the previous Carry into the most significant bit and then setting the carry to the value of the bit that ‘falloff’ the least significant end.

 

 

 

 

 

 


Figure 4.6       Rotate Right Extended

 

4.6       Register based shift counts

 

If a register is specified as the shift amount, the amount held in the least significant byte is used. If the byte is in range 1 to 31 the result will be the same as a value specified shift. Values of 32 or more produces the results seen in figure 4.7.

 

 

Shift

Action

LSL by 32

Result zero, carry out equal to bit zero of shifted register

LSL by > 32

Result zero, carry out zero

LSR by 32

Result zero, carry out equal to bit 31 of shifted register

LSR by > 32

Result zero, carry out zero

ASR by >=32

Result filled with, and carry out equal to bit 31 of shifted register

ROR by 32

Result equal to shifted register, carry out equal to bit 31 of shifted register

ROR by > 32

Same result and carry out as ROR n-32

 

Figure 4.7       Shifts of 32 or more

4.7       Immediate operand substitution

 

The ARM has a 4-bit immediate operand rotate field that specifies a shift operation on the 8-bit immediate value. The immediate value is zero extended to 32 bits and then rotated right by twice the value in the rotate field.

 

In ARM assembly large constants may be specified directly and the compiler automatically convert (if possible) the constant into the appropriate set of values. The immediate operand substitution is a poorly documented feature in the ARM compiler. A large amount of time was used to figure out how this worked.

 

 

Hexadecimal range

Description

0 - 0xFF

No rotate

0x100 - 0x3FC

Steps of 4 by rotating right by 30 bits

0x400 - 0xFF0

Steps of 16 by rotating right by 28 bits

0x100 – 00x3FC0

Steps of 64 by rotating right by 26 bits

0x4000 – 0xFF00

Steps of 256 by rotating right by 24 bits

0x10000 – 0x3FC00

Steps of 1024 by rotating right by 22 bits

0x40000 – 0xFF000

Steps of 4096 by rotating right by 20 bits

0x100000 – 0x3FC000

Steps of 16384 by rotating right by 18 bits

0x400000 – 0xFF0000

Steps of 65536 by rotating right by 16 bits

… and so on

 

 

Figure 4.8       Large operand conversion

 

4.8       Negative operands

 

If a negative constant is specified as the 2nd operand, the instruction might be substituted with it’s opposite and the 1’s or 2’s complement of the constant is used. Figure 4.9 show the instructions that this rule applies to.

 

 

Instruction

Substituted with

New operand 2 value

ADC

SBC

1’s complement

ADD

SUB

2’s complement

AND

BIC

1’s complement

CMP

CMN

2’s complement

MOV

MVN

1’s complement

 

Figure 4.9       Substitution if second operand is a negative value

 

 

4.9       Conclusion

 

Data processing, branch and load/store instructions will be sufficient to produce a realistic simulation of the ARM. Ideally all 16 ALU instructions should be implemented, but considering the extent of the project, this may not be possible. Priority will be given to the ADD, SUB, CMP, CMN, MOV and MVN instructions.

 

Both branch and branch with link is necessary to provide the ability of accessing and returning from subroutines, hence it follows that the simulator must be able of handling labels. LDR and STR are necessary to provide a means of loading and storing values from memory.

 

The fact that all instructions are conditionally executed is an important aspect of the ARM therefore all the condition codes should be supported. In addition almost all ARM instructions are shift instruction so common shift operations should be supported.

 

Compiler specific issues such as large immediate operand substitution and instruction substitution need to be implemented. As the ARM the simulator should support decimal and hexadecimal values.

 

An extensive set of special cases will need to be considered during implementation.

 


CHAPTER 5 – REQUIREMENT ANALYSIS

 

The requirement analysis is a crucial part of the project. Failing to identify, or not understanding the requirements of an application, will not produce a satisfying result no matter how well the rest of the project is carried out.

 

5.1       An example scenario

 

The user should be able to load, modify and save assembly programs using a built in editor. When the simulator is in this state it is in EDIT mode.

 

A program has to be built in order to run a simulation. The simulator enters BUILD mode, locking all user controls, while reading, decoding and building the instructions. If an error occurs the simulator aborts the BUILD mode and returns to EDIT mode, re-enabling the appropriate user controls and giving the user an indication of what went wrong. If the BUILD mode completes successfully the simulator enters RUN mode, enabling the appropriate user controls.

 

The user may now step through the program one instruction at the time. The simulator carries out the instruction and displays the result. A set of instructions can be run using an autorun feature.

 

The program may be reset while running a simulation. A reset brings the simulation back to the starting point, with the initial values.

 

The user may select different views of the instructions, including assembly view, binary view and RTL (Register Transfer Language) view. Registers may be seen as binary, hexadecimal or decimal. When a selection is made the simulator displays the correct view.

 

The user may select to view memory. When the selection occurs, the simulator initialises the memory window and displays it. The memory window can be closed and opened by the user at any point while the simulator is in RUN mode.

 

The program can not be changed when the simulator is in RUN mode. The user must abort, leaving the simulator in EDIT mode in order to make changes to the program. The program will have to be rebuilt in order to RUN the simulation again.

The user should be able of controlling and storing main settings in the simulator through an options dialog. These settings should be loaded upon program start-up.

 

5.2       Specification of requirements

 

The following describes the requirement of the ARM Simulator based on the previous analysis:

 

The ARM Simulator should:

·         Have basic file handling capabilities like load and save.

·         Support a variety of ALU, branch and memory instructions discussed in section 4.9.

·         Support condition based execution and shifting.

·         Handle compiler specific features, such as Negative operand substitution and immediate operand substitution, as described in section 4.7 and 4.8.

·         Display the user mode register and the CPSR as described in section 3.7.

·         Display the effect each instruction has on those registers, in an easy and consistent matter.

·         Offer flexibility. No restrictions should be laid on the number of instructions or the size of a program.

·         Provide error messages to help the user find errors in the assembly programs.

 

The user should be able of:

·         Load, edit and store assembly programs

·         Using a variety of assembly instructions, including condition codes, different flags and several flavours of syntaxes.

·         Run an assembly program consisting of any supported instructions.

·         Step through the program one instruction at the time and visually see the effects

·         Easily reset the simulation, to run it again.

·         View memory contents, including program, user and stack memory.

·         View the values of the registers in different number formats, including binary, hexadecimal and decimal.

·         View the instructions as one of input, strict assembly, binary or Register Transfer Language.

·         Setting and storing basic options within the ARM Simulator, using an option dialog.

 

CHAPTER 6 – GRAPHICAL USER INTERFACE DESIGN

 

A lot of effort was put in the user interface in order to provide the user with the best thinkable premises of understanding and follow the simulation.

 

Use case design was employed in order to identify the requirements of the user interface. The use cases were derived from the requirement analysis, and describes the main actions the user may perform on the system.

 

6.1       GUI Design issues

 

The ARM Simulator will have to display a vast amount of information simultaneously. Considering that the 16 registers, the CPSR and memory may be shown concurrently the user has a lot of numbers to keep track of. A well-designed GUI can make a great difference when it comes to following the simulation.

 

When an instruction is executed, no more than 3 registers and maybe the CPSR get affected. The other 13 registers still contain the same value as before the operation. By highlighting the registers where changes actually took place, it will be easier for the user to follow the simulation.

 

Another feature that may clarify the simulation, is if the memory location pointed at by the program counter is highlighted, and similarly the next instruction in the instruction list. These are of course the same, the value in the memory location is the machine code equivalent of the instruction in the instruction list.

 

In designing and presenting data we can adopt the idea of the smallest effective difference. The idea is quite simple, but the effects are remarkable. When displaying data it is usual to have some sort of frame or table containing the data. Quite often a black frame is used. The idea is that the lighter that frame, the easier the eye catches the data, which is what we want to display. Consider figure 6.1. A set of frames is displayed, all with a different border. The heavy black border in the table at the left is quite disturbing. The eye is drawn to the frame instead of the text. At the right a light grey border is used, and the contents catches the eye rather than the frame.

 

 

 

Contents

 

Contents

 

Contents

 

Contents

Contents

 

Contents

 

Contents

 

Contents

Contents

 

Contents

 

Contents

 

Contents

 

Figure 6.1       Border effects (Tufte, [9] and [10])

 

In GUI design this relates to things like textboxes, comboboxes, lists, labels, grids, frames and so on. A textbox typically has a distinct 3D-border and is usually used for user input. Labels are often used to display static text or data, but have no border at all.

 

Having considered the different possibilities, a table was chosen to display the registers. The table component within Swing is considered the most heavy and difficult to use, but also the most powerful.

 

A means of displaying the instructions during a simulation was also needed. An instruction is static during runtime and cannot be changed by the user. A simple list was chosen. A list also provides the ability to select, or highlight, the next instruction to be executed as discussed earlier in this chapter.

 

The same approach was decided used when designing the memory dialog. A list displaying the memory address and the value contained at that address with the ability to highlight the address pointed at by the program counter.

 

As discussed in Section 5.1, the ARM Simulator will work in different modes. The edit mode is when the user is working with files, the build mode is a intermediate state where the system is building the program and the run mode is where the user can step through a program. Three solutions were possible and is listed below:

 

·         The main window could contain the editor and a separate window containing the simulation could be spawned when the user builds a program.

·         A MDI (Multiple Document Interface) solution could be used, where the editor and simulation is child windows within a main window.

·         The main window could contain the editor, and then replace the editor with the simulation components when running a simulation.

 

 

After some consideration the last approach was selected. This was done for several reasons. Using several windows is generally not considered to be good design. The memory component, will probably be a separate dialog, so the user will have to keep track of three windows. Using the last approach helps keeping the simulations consistent. If a change is to be made, the simulation has to be stopped and then rebuilt ensuring that the change has been included in the simulation.

 

6.2       Designing the user interface

 

It was decided that the user controls would consist of a menubar, containing all possible user actions, and a toolbar containing the most frequently used actions.

 

Figure 6.2 show the overall use case diagram for the simulator. It defines the main operations a user may perform on the system. The diagram was used as a base when creating the user interface in order to provide the required user controls.

 

 

 

 

 

 

 

 

 

 

 

 

 


Figure 6.2       Main use case for the ARM Simulator

 

 

The following actions were derived from the use case:

 

·         Open and save file

·         Cut, copy and paste

·         Edit options

·         Build, step, reset and stop simulation

·         The different instruction views

·         The different register views

·         View memory

 

The user interface was drawn on paper, and is reproduced in figure 6.3

 

 

 

 

 

 

 

 

 

 

 

 

 


Figure 6.3       The ARM Simulator in edit mode at the left and run mode at the right.

 

The use cases were further derived into sequence diagrams, which are described in the section 7.8 and in appendix D.


CHAPTER 7 – SYSTEM DESIGN

 

This chapter describes the main issues considered when designing the ARM Simulator system. It is assumed that the reader have basic knowledge of Object Orientated features like objects, interfaces, inheritance, polymorphism and the use of abstract classes and methods. If this is not the case please refer to [2], Chapters 6 and 7. These topics are also covered by most introductional books on Object Oriented languages.

 

The first sections discusses initial design issues, describing the approach taken to create a framework for the ARM Simulator.

 

The last section describes the final design of the ARM Simulator, mostly represented by UML diagrams, as it exists after finishing the implementation.

 

7.1       Designing the instruction objects

 

The instruction objects make up the core of the system. Within the ARM Simulator a instruction is responsible of executing itself, set results and tell the system what instruction to execute next. A great effort was put down in designing these objects and their connection with the main system.

 

Each instruction object had to be capable of  holding all it’s attributes. In addition each instruction should be capable of executing itself. The first problem encountered was how a set of instructions could be stored within a datastructure, and be easily accessed at a later time. If all the objects within a datastructure is of a different type, the object needs to be identified before it can be accessed, and all possible objects will need to be instantiated.

 

The solution was to use abstract methods and polymorphism. A 3-split model was designed, comprising of the following elements:

 

·         an interface defining all constants needed by any instruction object

·         a abstract superclass storing the opcode and condition code of a instruction, this class also defines several abstract methods that any deriving class will have to implement

·         the actual instruction object derived from the superclass.

 

Polymorphism is quite complex, so a further explanation may be appropriate. The design consist of a abstract superclass ArmInstruction. This is subclassed by ArmAddInstruction, ArmSubInstruction, ArmMovInstruction and so on. The superclass defines several abstract methods that each subclass has to implement. To invoke a method on a ArmAddInstruction object, a superclass reference to that object is used.

 

In practice this means that the ARM Simulator can always use a superclass reference to access any instruction object, and do not need to know the exact type of that object.

This approach result in an easy and transparent way of working with instructions. It allows any instruction object to be accessed via an reference to the superclass. Any method that is declared abstract may then be invoked on this reference, which will cause that method to execute in the referenced object.

 

Within a instruction object everything is stored numerically, except from some label values and the original instruction line as it was read from file. Matching numbers are much faster than matching strings, so a instruction should be converted to numeric values upon build. The full instruction set and constants is declared in an interface, ArmInstructionSet that is implemented by all instruction objects. Below is a extract of the ArmInstructionSet interface.

 

// Binary representation of elements

BINVALS[]={’0000’,’0001’,’0010’…… //0–15 ALU Instructions

          ’0000’,’0001’,’0010’……  //16–31 Condition codes

           ’0000’,’0001’,’0010’……  //32-49 Registers

           … more constants }

 

// Textual representation of elements

ASMVALS[]={’AND’,’EOR’,’SUB’……     //0–15 ALU Instructions

          ’EQ’,’NE’,’CS’,……       //16–31 Condition codes

           ’R0’,’R1’,’R2’……       //32-49 Registers            … more constants }

 

public static final int AND = 0;

public static final int EOR = 1;

public static final int SUB = 2;

………… more instructions

public static final int EQ = 16;

public static final int NE = 17;

public static final int CS = 18;

………… more condition codes

public static final int R0 = 32;

public static final int R1 = 33;

public static final int R2 = 34;

………… more registers

 
 

 

 

 

 

 

 

 

 

 

 

 

 


 

 

 

 

 

Consider an ArmSubInstruction object representing the following instruction :

 

SUBNE r0, r1, r2

 

Within the instruction object the opcode will be stored as 2, the condition code will be stored as 17 and the three registers will be stored as 32, 33 and 34. The arrays containing binary and assembly values are used both when identifying and verifying objects and when creating the machine code representation of an instruction. The constant SUB has the value 2, a look-up of position 2 in the array of binary values returns ’0010’ which is the machine code representation of the SUB opcode. In addition each element within an object has an identifier, which is used to determine what is expected from that element. A shiftvalue will get the constant identifier SHIFTVAL, so that the system can determine that this value is supposed to be a 5-bit integer.

 

7.2       Designing a ’compiler’

 

Having determined a design for the instruction objects, a means of dynamically creating instruction objects was needed.

 

After careful consideration the ’compiler’ part was designed as an independent object instead of integrated as methods within the main class. This approach add some complexity to the overall design, but simplifies the process of  adding support for new instructions or even replace the ’compiler’ if necessary.

 

Based on the result from the assembly analysis the ’compiler’ had to support the following:

 

·         ALU instructions with three operand syntax.

·         ALU instructions with two operand syntax.

·         Branch and branch with link syntax (meaning that labels must be supported).

·         Basic memory instruction syntax.

·         Flags, condition codes, shifts and special cases that may be used within any of the above syntaxes.

·         Compiler specific issues such as immediate operand substitution and negative operand substitution.

 

The first design of the ’compiler’ was built to support ALU instruction with two and three operand syntax. Due to it’s complexity some pseudo code was written to aid the general design. The pseudo code covers all types of instructions.

 

 

if instruction is of type alu then read_alu_instruction

else if instruction is of type branch then read_branch_instruction

else if instruction is of type memory then read_memory_instruction

else instruction is invalid

 

Figure 7.1       Pseudo code for determining the what type of syntax to expect

 

 

verify and convert opcode                                // any ALU opcode

verify and convert condition_code                     // any cc, optional

verify and convert condition_flag                      // the S flag, optional

verify and convert destination_register              // always a register

 

if alu instruction is three operand

  verify and convert operand_one_register         // always a register

 

verify and convert operand_two_value_one       // register or immediate

verify and convert operand_two_value_two      // null or a shift

verify and convert operand_two_value_three    // null, register or immediate

 

report error or create instruction object

 

Figure 7.2       Pseudo code for creating a ALU type instruction

 

Only the ALU code is included here, the branch and memory equivalents are almost identical. When applying this to implementation each line would typically refer to a method handling a specialised task.

 

 

7.3       Connecting the designs

 

The system design now consisted of a set of instruction objects, a object capable of reading, verifying and creating instructions and the main class containing  the GUI. The instruction reader object is what ties the GUI and instruction objects toghether. The only instruction object known by the Simulator is a reference to the superclass. The ’compiler’ knows of all possible instruction types and instanciates one object of each instruction.

 

 

 

 

 

 

 

 

 

 

 

 


Figure 7.3       Creating instructions

 

This design makes up the framework of the ARM Simulator. It provides scalability when it comes to adding new instructions and a transparent handling of instruction objects within the main class. A new instruction is supported by adding it’s keywords to the interface, upgrading the instruction reader to support the syntax if not already supported and create an instance of that instruction object within the instruction reader. The rest of the system remains unchanged and only relates to a reference to superclass ArmInstruction.

 

7.4       Storing instructions

 

When working with objects there is no limit on how many instances that can be created. The ARM Simulator need to save the instruction objects as they are being created. The system should not have any restrictions on the number of instruction. One approach could be to count the number of instructions prior to a build, in order to allocate a correctly sized array. This approach was rejected in favor of a vector, which is a dynamic datastructure. A vector is capable of holding any type of object.

7.5       Designing a shared datamodel

 

It was desired to have the whole system share the same datamodel. In Swing this is possible because of the Model View Controller architecture (Please refer to Appendix F for more information on Swing and MVC). A table, which is used in the Graphical User Interface to represent the registers, may use a custom datamodel. Such a datamodel simply defines the initial data and several methods used by the table to retrieve and set values.

 

The advantage of this approach is that the whole system access the same datamodel, guaranteeing that the datamodel is consistent. Passing the datamodel to a instruction upon execution allows the instruction object to modify the data using call by reference.

 

As within a real processor it was desired to have the program counter control execution. Due to the shared datamodel design this was a quite easy task. Each instruction object is responsible of updating the program counter so that it points to the next instruction to be executed.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


Figure 7.4       Main framework of the ARM Simulator. Program counter controls execution.

 

 

 

7.6       Memory representation

 

In order to view memory contents, and load and store from memory a memory model was designed. The allowable address range was decided to be 0x00000000 to 0x7FFFFFFC, which is the maximum positive addressing range a register can handle, leaving 536,870,911 effective memory addresses available. This is of course way more than the simulator will be able of handle, but it add flexibility to the memory handling.

 

As with the storage of instructions, a dynamic datastructure was desired to represent memory, so that scalability could be retained. A hastable was selected, using the memory address as the key and the memory value as the value. It was decided that only memory used by the program or accessed by the user should be stored. All memory addresses that are not in the hashtable are considered empty and are never displayed to the user.

 

The organisation of memory is quite simple. Program memory starts at zero and goes to whatever is necessary. The user has full access to any address in the range.

 

7.7       Designing a stack

 

In order for the simulator to handle nested subroutines with several subsequent returns, a means of storing more than one return address was needed. The problem was solved by designing a simulated stack. The stack is of course represented by a stack allowing an infinite number of branches with link. It was decided that the stack should be located at the top of memory, running top down.

 

 

 

 

 

 

 

 

 

 

 


Figure 7.5       Stack representation

This design requires a register to be reserved as stack pointer, making this register unavaliable to the user. It was desired to let the user decide what register to use as stackpointer and also have the ability to disable the stack simulation, leaving all registers free.

 

7.8 Sequence and class diagrams

 

Some sample diagrams have been included within the report. For the full set of diagrams, please refer to appendix E.

 

The sequence diagrams are described both visually and textual. These diagrams have a higher level of detail than the use case diagram, describing the actions carried out by both the user and the system. They do not however, describe in detail how the system behaves in order to, for example, read a file.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


Figure 7.6       Sequence diagram describing the file open action

The overall class diagram describes the main interaction of the classes within the ARM Simulator software.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


Figure 7.7       The ARM Simulator class diagram

 

Class diagrams express the structure of the system, in terms of classes and their relationships. A class is described by its class name, it’s attributes and it’s methods. Figure 7.8 describe the abstract class ArmInstruction, which is the superclass of all instruction, objects within the ARM Simulator.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


Figure 7.8       The ArmInstruction class in its final form

 

The ArmInstruction class holds the characteristics all ARM instructions have in common. Every instruction has an opcode and any instruction may be conditionally executed. The ArmInstruction class holds the opcode and condition code for any subclass. The methods displayed in Italics in figure 7.8 are abstract methods and must be implemented for each derived class of ArmInstruction from which objects are instantiated.

 

Figure 7.9 describes class ArmAddInstruction that is derived from ArmInstruction.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


Figure 7.9 – Class ArmAddInstruction

 

The ArmAddInstruction describes a typical instruction object. All instructions within the ARM simulator are designed in this way. The differences are within the ‘executeInstruction()’, ‘getAsmInstruction()’, ‘getBinInstruction()’ and ‘getRtlInstruction()’ methods. In addition, the variables vary slightly because of differences in syntax and variations in the number of parameters.

 

CHAPTER 8 – IMPLEMENTATION

 

This chapter describes some of the main issues carried out during implementation. High priority has been given in building the simulator as accurate as possible, all down to binary representation of the instructions. To achieve this a lot of effort has been put in functionality that is not directly visible to the user.

 

The system is implemented using the Java Software Development Kit (SDK) freely avaliable from SUN Microsystems. For more information about the choice of language and why the ARM Simulator is developed as an application and not an applet, please refer to appendix F.

 

It is assumed that the design chapter has been read prior to reading this chapter.

 

8.1       Implementing the GUI

 

The first part of the implementation involved creating the GUI, based on the design discussed in section 6.1 and 6.2.

 


 

 


Figure 8.1       The ARM Simulator in edit mode

 

 

The user interface was implemented right before christmas and offered some challenges due to the complexity of certain Swing components, like the Jtable used to display the registers. Having created the user interface the functionality of the editor was added. The editor offers simple file handling capabilities similar to the ’Notepad’ application on the Windows platform. Figure 8.1 show the the ARM Simulator in Edit mode.

 

8.2       Implementing the instruction objects

 

To start with, the classes necessary for representing an ADD instruction were written. The implementation was done based on the design described in section 7.1. At this point most of the ALU instruction constants were added to the interface, as well as all register constants and identifiers for the appropriate values. The first implementation was only capable of storing the values within the instruction object and returning a string representations of the instruction in four different formats. These comprised of the following:

 

·         The actual input, which is the line read from the users file.

·         Strict assembly, which is the result of reversing the internal storage of the instruction into assembly, appending default flags and using uppercase.

·         Binary, which is the exact machine code representation of the instruction.

·         Register Transfer Language.

 

The ’executeInstruction’ method was only defined at this stage and was first implemented after creating the instruction reader component and integrated it with the user interface.

 

8.3       Implementing the instruction reader

 

The instruction reader was written based on the design issues described in section 7.3. In order to create the instructions, the simulator needed a ’compiler’ to read, verify and create the instructions, handling any errors that might occur. Rather than handling the instruction creation within the GUI class, a custom object was designed and written.

 

When building the program the ArmSimulator class read one line at the time from the users file. This line is tokenised and each token is stored in a string array. A ’compiler’ object is created, passing the array as a parameter. The ‘compiler’ then identifies, verifies and creates the appropriate instruction object. If the operation terminated successfully, the instantiating class retrieves and stores the instruction object. This process is repeated until all code is processed.

 

The following pseudo code describes the instruction creation:

 

while( readline ) {

tokenize line

store tokens in array

            create a new compiler object with the array as parameter

            if compiler completed succesfully {

                        get instruction object and store it in a vector

            }

            else {

                        get and display error message

                        abort reading

            }

}

 

The ’compiler’ needed to support compiler specific issues like large immediate operand substitution and automatic instruction substitution (Please refer to sections 4.7 and 4.8 for more information about these subjects). Both the immediate operand substitution and negative operand substitution were created as independent classes, instantiated by the ’compiler’ only when needed.

 

8.4       Executing instructions

 

Each instruction object has a method ’executeInstruction()’ to carry out their operation.

Program execution is fully controlled by the program counter value, which is updated by each instruction as it executes. The built in ‘compiler’ creates a vector of instruction objects during build. The program counter is used to determine what instruction object in the vector that is waiting to be executed.

 

An instruction object is responsible of updating the registers according to the result of the operation. This is achieved by having each instruction update the register datamodel using call by reference. The register datamodel is directly connected to the table displaying the registers in the GUI, where custom-written renderers update each cell, using the appropriate colour.

 


 

3

 

 


Figure 8.2       The ARM Simulator running a simulation

 

Figure 8.2 shows a screenshot of the ARM Simulator running a simulation.

1.       Any register that was changed during last operation is rendered black

2.       The next instruction to be executed

3.       The hexadecimal value seen in the memorydisplay is the actual encoding of the instruction. 0xE285547F equals 11100010 10000101 01010100 01111111 in binary. If we decode this according to the ALU instruction format we get the actual instruction.

 

1110

00

1

0100

0

0101

0101

0100

01111111

AL

 

I

AND

S

R5

R5

4

127

 

Register 15, the program counter points to next instruction in memory to be carried out. The program counter is always updated and will always be rendered black.

 

8.5       Conditional execution

 

In order to support conditional execution a custom class was written. This class receives an instructions condition code and the current CPSR flags as parameters, and return true or false depending on the test result.

 

All instruction objects instantiate a ConditionHandler object, which decides whether execution is to be carried out or not. If the instruction is not to be executed, it does nothing but updating the program counter.

 

8.6       Shift support

 

In order to support shift instructions a new class was designed and implemented. The ArmBarrelShifter class is capable of perform LSL, LSR, ASR and ROR shifts. All instruction objects instantiates an ArmBarrelShifter object, which perform the required rotation and returns the result.

 

8.7       Branch abilities

 

The branch instruction was derived from the same superclass as the ALU instructions. Each branch object holds its target and, if a branch with link is specified, its return address. When a branch object executes, it writes the target address to the program counter. If a branch with link was specified the return address is written to the link register (r14).

 

The addresses are retrieved during program build. When a branch instruction is encountered, the simulator searches for a matching label. The address of that label is then stored as the target address, and the return address is set to the address of the branch instruction + 4, which points to the next word aligned instruction. A missing label causes the build to halt, and an error message to be displayed.

 

8.8       The Autorun feature

 

An autorun feature was implemented to offer the user the ability of running a set of instructions without having to step through each instruction. The user can either run to a label or run a user-defined number of instructions. The implementation of this feature required no new coding except from creating the dialog. Instructions are executed in the same manner as

described in section 8.5, but the display is not updated until the last instruction is executed.


 

 


Figure 8.3       The ARM Simulator Autorun feature

 

8.9       Other implementation issues

 

Several utility classes were written during implementation dealing with everything from number conversions to customizing user interface components.

 

A simple configuration utility allows the user to enable stack simulation and select register and instruction views. The setting is of course saved and loaded the next time the user starts the application.

 

 


 

 

 


Figure 8.4       The ARM Simulator options dialog

 

A small HTML based help system containing a lightweight browser was also included. The documentation within the help system is not complete.

 


 


Figure 8.5       The ARM Simulator help system


CHAPTER 9 – TESTING

 

This chapter describes the testing which was carried out on the ARM Simulator application. Due to shortage in time, not all functionality could be tested. The full system consists of 44 classes containing several hundred methods.

 

Three test methods have been used. ’Module testing’ refers to the testing of classes. This method has been carried out throughout the implementation, testing each new part before integrating it with the full system.

 

’State boundary testing’ refers to testing the system with boundary values. ’System testing’ refers to an overall systems test. These two have been used in the testing phase of the project.

 

All tests are performed with registers and processor flags initially set to zero. No testresults are included in the report. For the full set of results please refer to appendix D

 

9.1       Testing large operand substitution

 

If a value larger than 255 is specified as the second operand this must be substituted with a value that goes within 8-bits, but when right rotated with a doubled 4-bit value produces the original value.

 

The operand substitution is handled by class ArmRotateFinder. This class receives the large immediate value as a parameter and if the constant can be generated, the instantiating class can retrieve the immediate value and the rotate value. Considering that this is an independent object it can be tested independently. If it produces the correct result for one instruction, it will work with any instruction.

 

When it comes to executing the instruction, the reverse operation must be done. This is handled by class ArmImmediateRotator.

 

A small test program was created using values stretching from small to maximal. During the simulation both the substituted values and the results were checked for correctness.

 

All instructions produced the expected result and created valid substitution values for the large constants. No bugs were detected during this test.

9.2       Testing shift routines

 

Shifting is handled by class ArmBarrelShifter. This class is used only if the shift operation involves a register as the second operand. All shift operations are carried out by the class ArmBarrelShifter. This means that if this produces the correct result with an ADD instruction, it will also be correct for any other instruction. For more information on how the shift operations works, please refer to section 4.5 of the report.

 

Test programs were written to test each of the shifts, and the results were checked for correctness. No errors were encountered during these tests.

 

9.3       Testing conditional execution

 

Conditional execution is handled by class ArmConditionHandler. Each instruction instantiates this class upon execution, and either gets permission to execute or is denied execution.

 

A large test program was written and simulated, testing all the condition codes. Each instruction result was also checked for correctness.

 

A bug in the MOV instruction was detected. If the condition code caused the instruction not to be executed, the program counter did not update and the program halted. The same error was located in the MVN, CMP, CMN, TEQ and TST instructions. This error is fixed.

 

9.4       Testing negative operand substitution

 

Instruction substitution occurs when an instruction has a negative second operand. The substitution is performed during program build, before creating the instruction object. This operation is carried out by the class ArmNegativeSubstitution. Any instruction that detects a negative second operand uses this class to determine if it should be substituted and, if that is the case, to retrieve its substituted values. However, not all instructions are substituted.

 

For more information about instruction substitution please refer to section 4.8.

 

A test program was written and simulated. All instructions produced the expected result and created valid substitution values for the large constants. No bugs were detected during this test.

 

9.5       Testing the Simulator with real users

 

In order to identify any problems with the GUI and user interaction, real users were set to test the program. They were given a set of programs and asked to run them. No instructions were given on program usage, but the help system was available (although not finished). After running these programs they were asked to write their own programs and try them out. The users were encouraged to play around with the program as much as possible, and also to make any remarks or thoughts they had regarding the interface.

 

This test proved to be the most successful. Two bugs were found and a lot were learnt by observing the users while they used the program.

 

A common mistake was to try to edit the program while running. The users selected an instruction in the list and tried to edit it. This was repeated a couple of times before they realised that they had to stop the simulation. The users quickly learnt to stop the program before trying to edit it, but it raises the question if a listbox is a good choice for displaying the instructions.

 

In addition two bugs were detected. Specifying an out of range integer value in the autorun dialog caused the simulator to crash. The other ’bug’ appeared in the open and save dialogs. This did not happen every time but occurred a couple of times during the test. The users double-clicked on a folder name, causing the folder not to open, but allowing the user to change its name. The solution seems to be to double click the folder icon and not the folder name. The open and save dialogs are however part of the Java API, so this bug must be dedicated to Sun Microsystems.

 

The bug with the autorun feature is fixed.

 

9.6 Testing the simulator on different platforms

 

The software has been tested and run on the following platforms:

 

·         Windows95 using Java SDK 1.2.2

·         Windows NT 4.0 using Java SDK 1.2.2

·         Linux Redhat 5.2 using Java 1.2.Beta and Java 1.2.2

 

Unfortunately these are the only platforms the software has been tested on, but it works fine on all of these.

 


CHAPTER 10 – CONCLUSIONS

 

10.1     Goals and achievements

 

The aim of the project was to produce a simulation of an ARM processor, allowing the users to load and execute their own assembly programs, visually showing the results of the execution.

 

The following list summaries the main achievements of the software:

 

·         The system fully supports the requirements described in the project specification.

·         All data processing instructions are supported, supporting most syntaxes.

·         Branch and branch with link allows usage of subroutines.

·         Simple memory instructions are supported.

·         All condition codes can be used with any instruction.

·         A simulated stack enables the system to handle nested subroutines.

·         Memory view is supported.

·         A full-featured editor with file handling capabilities is built in.

·         The system is scalable. There are no restrictions on the number of instructions.

 

The system fully supports the requirements described in the project specification.

 

10.2     Personal thoughts

 

Having no pre-knowledge of systems architecture, processors or assembly language I was quite anxious about how the project would turn out. A large effort was put down during analysis to gain the necessary knowledge to get started. During the project I have gained a thorough understanding of the ARM processor and the ARM assembly language, as I had to look at these areas in detail.

 

In advance of the project I had never used UML. While carrying out the project I have learned how to model and design a system using UML diagrams.

 

I had some experience with the Java programming language, but had never used Swing as a graphical user interface. The project has helped me brush-up old knowledge, as well as I have had to learn new things. I have come to appreciate the Java language as powerful, but yet simple to learn and easy to use.

 

Some important lessons have been learned during the project. One is the importance of continuos documentation throughout the project. The documentation produced during the different phases of the project was of great help when it came to produce the final report.

 

I have also understood the importance of the ability to approach a new subject, isolating the important aspects and then filter out the information needed to gain a broad overview.

 

Another important aspect is the ability to carry out a project in a consistent matter, making the correct decisions and selecting the right methods for solving the different phases of the project.


CHAPTER 11 – RECOMMENDATIONS

 

11.1     Completing the instruction set

 

The full instruction set could be implemented. Currently the full set of data processing and branch instructions is implemented. Single register load and store is supported, but only with pre-indexing. By completing memory instructions and maybe adding multiply, co-processor and software interrupt instructions, the instruction set will be close to the actual ARM.

 

11.2     Serialization

 

Serialization is a powerful object-oriented feature, enabling objects to be saved and loaded preserving their current state. By implementing serialization support, instruction objects could be saved directly to file. This enables the user to store and load ready built simulations, sparing the user from having to rebuild a program each time. It would also introduce the possibility of changing programs during a simulation.

 

11.3     Upgrading the ’compiler’

 

The ARM Simulator does not have anything like a true compiler. It has a means of decoding, verify and create instruction objects. By implementing a truer compiler, performing true parsing, the program build process could be much more efficient in terms of speed and memory usage.

 

11.4     Improving the help system

 

A more sophisticated help system could be implemented. A package called Java Help for creating Java based help systems is actually available from SUN. The only drawback is that the full package is several megabytes. The help system as it exists now is not finished. Full documentation of the instruction set should be included, together with information on the ARM architecture and a complete users guide.

 

11.5     Converting the Application to an Applet

 

Converting the application to an applet would make the simulator accessible through a browser. Considering the security restrictions for Java Applets most file handling capabilities would be needed to be removed. Instead a means of loading URL’s could be included, enabling a user to type in a local or remote URL to retrieve a file.

 

11.6     Thumb support

 

The simulator could be upgraded to support the 16-bit Thumb instruction set, supported by some ARM processors. This is a lightweight instruction set, often containing only two operand instructions and restrictions in the conditional execution abilities.

 

11.7     Other features

 

The ARM can support both Big and Little Endian memory addressing. It might be an idea to support this feature. Floating point emulation could make a useful add-on. Breakpoint support would be a nice feature.


CHAPTER 12 – BIBLIOGRAPHY

 

The following books have been consulted during the project period:

 

 

[01]

VLSI Technology, Inc.

Acorn Risc Machine Data Manual. Prentice Hall, 1990. ISBN : 0-13-781618-9

 

 

 

[02]

Deitel & Deitel

Java How To Program. Second Edition. Prentice Hall, 1997. ISBN : 0-13-899394-7

 

 

 

[03]

M.Morris Mano

Computer Engineering, Hardware Design. Prentice Hall, 1998. ISBN : 0-13-162926-3

 

 

 

[04]

Alan Clements

68000 Family Assembly Language.

 

 

 

[05]

G.W Gorsline

Computer Organization : Hardware / Software. Prentice Hall 1986.

 

 

 

[06]

-

Graphic Java 2, Mastering the JFC, third edition. Sun Microsystems Press.

 

 

 

[07]

Pierre-Alain Muller

Instant UML, Wrox Press, 1997.

ISBN : 1-861000-878-1

 

 

 

[08]

Ivor Horton

Beginning Java 2. Wrox Press. ISBN : 1861002238

 

 

 

[09]

Edward R. Tufte

Visual Explanations. Graphics Press, 1997.

ISBN : 0961392126

 

 

 

[10]

Edward R. Tufte

Envisioning Information. Graphics Press, 1990.

ISBN: 0961392118

 

 

 

[11]

Jenny Preece

Human-Computer Interaction. Addison-Wesley, 1994.

ISBN : 0-201-62769-8

 

 

 

 

 

 

The following electronic documents have been consulted:

 

 

[01]

ARM Ltd.

ARM Software Development Toolkit Version 2.0, Reference Manual. Advanced RISC Machines Ltd, 1995. Document number : ARM DUI 0020D

 

 

 

[02]

ARM Ltd.

ARM Software Development Toolkit Version 2.0, Windows Toolkit Guide. Advanced RISC Machines Ltd, 1995. Document number : ARM DUI 0022B

 

 

 

[03]

ARM Ltd.

ARM 7 Data Sheet. Advanced RISC Machines Ltd, 1994. Document Number : ARM DDI 0020C

 

 

 

[04]

ARM Ltd.

ARM Software Development Toolkit Version 2.0, Programming Techniques. Advanced RISC Machines Ltd, 1995. Document number : ARM DUI 0021A

 

 

 

 

[05]

ARM Ltd.

ARM Instruction Set Quick Reference. Advanced RISC Machines Ltd.

Document number : ARM QRC-0001C

 

 

 

 

The following webpages have been consulted

 

[01]

-

ARM Ltd. Homepage

http://www.arm.com/

 

 

 

[02]

-

Sun Microsystems Java page

http://java.sun.com/

 

 

 

[03]

-

Alan Clements website on the SCM-intranet

 

 

 

 

Other references consulted:

 

[01]

M.Etchells

Assembly Language Simulator, 1997.

 

 

 

 


APPENDIX A – PROJECT SPECIFICATION

 

The objective of this project will be to design and develop an ARM processor simulator, capable of reading and executing ARM assembly instructions. The simulator will have a Graphical User Interface. It will visually show how the execution of the assembly instructions affects the memory, registers and other components of the ARM architecture. Users will be able to load and execute their own assembly programs.

 

The analysis phase of the project will be to examine the behaviour of the ARM system and see the effect different instructions have on the components that make up the system.

 

When the analysis-phase is finished, I will start the design phase. This includes everything from sketches of the GUI and designing the simulator down to appropriate detail, probably using UML or other tools. The code will be designed down to fine detail, including classes and methods. This will make the implementation quicker and easier.

 

Implementation will be done using an appropriate development language, probably JAVA.

 

The testing will first be unit-based, testing each part as I go along. Then I will choose an appropriate testing strategy for the whole system.

 

All software will be fully documented and be produced to common standards.


APPENDIX B – PROJECT TIME SCHEDULE

 

Week

Description

Comments

 

 

 

43

Project start

 

44

Background reading

 

45

Background reading

 

46

Background reading, Specification

 

47

Specification, Analysis

Specification by 23.11.1999

48

Analysis

 

49

Analysis, Design, GUI

HCI considerations

50

Analysis, Design, GUI

HCI considerations

51

Christmas

 

52

Christmas

 

01

Christmas

 

02

Start implementation

 

03

Implementation

Interim report by 18.01.2000

04

Implementation

 

05

Implementation

 

06

Implementation

 

07

Implementation

 

08

Testing

 

09

Testing

 

10

Documentation

 

11

Documentation

 

12

Documentation

 

13

Hand over

Final report by 28.03.2000

 

 

 

Documentation will be done continuously throughout the project.

 

Testing will be a continiuos process, testing each part as I go along. When the system is implemented a proper method will be selected to test the system as a whole.

 

 

 

 


APPENDIX C – USER GUIDE

 

The ARM Simulator user guide is divided into the following sections:

 

Chapter 1 – Introduction

Starting the ArmSimulator

A visual guide to the program

Quick start

Problems

 

Chapter 2 - Working with files

Loading, editing and saving files

Edit, copy and paste

 

Chapter 3 - Running a simulation

Starting a simulation

Executing the instructions

Running a batch of instructions

Reset a simulation

Stopping the simulation

Keyboard shortcuts

 

Chapter 4 - Other features

Memory viewer

Instruction and register views

Stack simulation

The options dialog

 

Chapter 5 - Assembler syntax

Valid instructions, conditions and shift operations

Using labels


Chapter 1 – Introduction

 

Starting the ArmSimulator

 

You will need to have the Java Runtime Environment installed on your machine. The version number should be at least 1.2.

 

On a Windows system you should be able to doubleclick the file called ArmSimulator.jar. If this does not work, open a command shell and switch to the directory where the ArmSimulator.jar file is resident. Type the following at the prompt :

 

path_to_java\bin\jawav –v ArmSimulator

 

If this does not work, you have an error in your java installation. Try to de- and then reinstall it.

 

On a Linux/Unix system you need to start the program from a command shell. Start a shell and type the following at the prompt :

 

path_to_java\bin\jawav –v ArmSimulator

 

Mac users should be able to drag and drop the ArmSimulator.jar file on the jawav tool in your java\bin directory. If this does not work, follow the instructions in your java documentation on how to set up a shortcut for a java program.


A visual guide to the program

 

The ArmSimulator work in two modes. One is the EDIT mode, where you load, store and edit your assembly programs. The other is RUN mode that actually run the assembly programs you worked with in EDIT mode. Figure 1.1 show the ARM Simulator in EDIT mode with a program loaded from file.


 

 

 

 


Figure 1.1 - The ARM Simulator in EDIT mode with a program loaded from file.

 

The toolbar functions are explained in figure 1.2. The leftmost 7 functions are avaliable only when working in EDIT mode. The next 4 functions are avaliable in RUN mode only, and provide the functionality needed to step through a program. The rightmost button, Help, is always avaliable.



 

 


Figure 1.2 – The toolbar functions


 

 

 


Figure 1.3 – The ARM Simulator in RUN mode running the file from figure 1.1

 

 

Quick Start

 

There are some example programs bundled with the ArmSimulator. Start the simulator, select File from the menu and then Open. Browse to the directory where ArmSimulator is installed. Select the programs directory, select a file and click Open.

The selected file should now appear in the ArmSimulator window. To run the program, select Program from the menu and then Build.

 

The simulator window now split, a list is seen on the left and a table showing the registers on the right. You can now start the simulation by selecting Program from the menu and then step.

 

The ArmSimulator will then execute the statement, the program counter (r15) will increase and focus will be moved in the list on the left.

 

To view memory select View from the menu and the Memory. A dialog box will appear.

 

To reset the simulation select Program and the Reset.

 


Chapter 2 - Working with files

 

The ArmSimulator has a fully featured editor. It enables you to load, edit and save files similar to the Windows Notepad utility.

 

Loading a file

 

To load a file, either select the folder icon on the toolbar or use the File menu. A open file dialog will appear. Select the file you want to open and either doubleclick it or click the open button. If the file is large the open process may take a while.

The selected file should appear in the ArmSimulator editor. The keyboard shortcut for this operation is Control + O.

 

Edit a file

 

Editing files is done as in any other editor. Text can be selected, deleted, copied and pasted.

 

Saving a file

 

To save a file, select the save icon from the toolbar or use the File menu. If the file already excists the saving process is done automatically. If you want to save the file using another file name, you can select File->Save as… which will launch the save dialog.

The keyboard shortcut for this operation is Control + S.

 

Creating a new file

 

To create a new file, simply select the new icon fro the toolbar or use the File menu. If a file is already open and a change has occured since last save operation, the system will prompt you if you want to save your changes.

The keyboard shortcut for this operation is Control + O.

 

Cut, Copy and Paste

 

These commands are avaliable from the toolbar or the edit menu. To cut or copy simply select a piece of text and use the desired command. To paste a piece of text, put the cursor in the position where you want the text and select paste.

 

The keyboard shortcuts for these operations are:

Cut                  Control + X

Copy                Control + C

Paste               Control + V

 

Chapter 3 - Running a simulation

 

Starting a simulation

 

To start a simulation, open the desired file or type the instructions right into the editor. Select Build from the toolbar or use the Program menu. If no errors occur, the program enters run mode.

If an error was encountered, an error message appears in the statusbar of the window. It tells you what type of error that was found and the linenumber of the instruction.

The keyboard shortcut for building the program is Alt + 5.

 

Executing the instructions

 

The ArmSimulator lets you step through the program one line at a time. This is done by selecting Step from the toolbar or using the Program menu. The simulator executes the instruction, updates the program counter, displays the changes and highlights the next instruction to be executed.

 

Reset a simulation

 

If you missed a step in simulation, you can reset it by selecting Reset from the toolbar or using the Program menu. A reset causes the simulation to og back to the entry point of your program.

 

Running a batch of instructions

 

The simulator has an autorun feature to run large chunks of code, or running to a specific label. Select Autorun from the Program menu. The Autorun dialog will appear (figure 3.1).

Select the desired action and click OK to start. The simulator will run until it reaches the criteria set, or if no such criteria can be fulfilled, until the program ends.

The simulation can now be continued from this point by stepping through the instructions.

 


 

 


Figure 3.1 – The ARM Simulator AutoRun Dialog

 

Stopping the simulation

 

To stop a simulation, select Stop from the toolbar or use the Program menu. Stopping a simulation causes the ArmSimulator to go back in edit mode.

 

Keyboard shortcuts

 

The keyboard shortcuts for these operations are:

Step                 Alt + 1

Autorun            Alt + 2

Build                Alt + 5

Reset               Alt + 8

Stop                 Alt + 9

 

 


Chapter 4 – Other features

 

Memory viewer

 


The ArmSimulator includes a memory viewer. To launch it, select Memory from the toolbar or use the view menu.

 

 

Figure 4.1 – The ARM Simulator memory dialog

 

At left you see the memory address of the instruction. The second hexadecimal value is the representation of the instruction in memory. The rightmost letter describes the ’owner’ of the memory address and can be any of P for program, U for user or S for stack.

 

Only memory addresses used by the program, the stack or accessed by the user is shown in the dialog. Any other memory address is considered empty and is therefore not necessary to show.

 

The memory dialog is updated in the same way as the instruction list in the ArmSimulator window. Each time the program counter is increased, the instruction pointed to is highlighted as shown in figure 4.1.

 

 

Instruction and register views

 

Another feature of the ArmSimulator is that both instructions and register values can be seen in different ways.

 

Instructions can be seen as:

 

Input                - the same as the isntruction read during program build

Strict assebly    - the way instructions are stored internally

Binary              - the binary representation of this instruction

Rtl                   - this instruction expressed in Register Transfer Language

 

Register values can be seen as:

 

Binary              - the binary representation of the value currently stored in a register

Hexadecimal    - the hexadecimal representation of the value currently stored in a register

Decimal           - the decimal representation of the value currently stored in a register

 

You can change these views during runtime. Select either Instructions or Registers from the View menu and select the desired view. These views can be set permanently in the ArmSimulator configuration dialog. Your setting will then be saved and loaded during startup. Please refer to the section called ‘The options dialog’ for instructions on how to do this.

 

Stack simulation

 

In order to handle nested subroutines the ArmSimulator provides a simulated stack. The stack is not enabled by default, so you will have to do this by selecting Options from the Edit menu.

Select the tab labeled ’Stack’, check the ’Activate Stack’ option and select a register to use as stack pointer.

 

When the stack is enabled BL (Branch with link), will save the return address to the stack. Return as usual with MOV PC, LR. This enables you to use nested subroutines in an easy and convinient matter.

 

The register selected as stack pointer is always read-only when stack is enabled. Any attempts to write to this register will cause an error. The stack is located at top of memory. You will see that the stack pointer contains large values. The stack values can bee seen in the memory dialog. The values are labeled with S.

 

The options dialog

 

The ArmSimulator provides a simple configuration tool where you can store your setting. Select Edit->Options to launch the dialog. The first tab contains the stack simulation setting. Please refer to the previous chapter for a more thorough explanation of this setting. The tab is shown in figure 4.2.

 

 


 

 


Figure 4.2 – The Options dialog with stack settings active

 

The second tab enables you to choose what instruction view to use as default. The third tab defines the register view. Select the desired view from the dropdown box.

 

Click Ok to save your changes, or cancel to abandon. The ArmSimulator will save your changes and load them the next time the program is started.

 


Chapter 5 - Assembler syntax

 

Valid instructions, conditions and shift operations

 

This chapter discusses these subjects in the displayed order:

 

·         Data processing instructions

·         Branch instructions

·         Memory instructions

·         Shifts

 


Data processing instructions

 

The data processing instruction set comprise of 16 different operations. The syntax within the ARM Simulator is described below.

 

 

(1) MOV, MVN - single operand instructions

 

opcode{ condition }{ S } Rd, Operand2

 

(2) CMP, CMN, TEQ, TST – logical instructions

 

opcode{ condition } Rn, Operand2

 

(3) AND, EOR, SUB, RSB, ADD, ADC, SBC, RSC, ORR, BIC – arithmetic instructions

 

opcode{ condition }{ S } Rd, Rn, Operand2

 

 

{ condition }                 is a optional two character condition code

{ S }                            set condition codes if present (implied for CMP, CMN, TEQ and TST)

Rd, Rn                         a register

Operand2                     is any of :

                        Rm

                        #expression1

Rm shift Rs

                        Rm shift #expression2

Rm                              a register

Rs                               a register containing a shift count in the range 1..32

shift                             any of LSL, LSR, ASR, ROR

#expression1                any signed expression shiftable into an 8-bi value

#expression2                any positive absolute shift count in the range 1..31

 

 


ADC – Arithmetic add with carry

 

ADC adds two 32-bit 2’s complement operands, storing the result into a designated register. If the carry bit was set prior to the instruction a value of +1 is added to the sum. Nothing is added if the carry bit was not set.

 

Operational function      :           Rd = Rn + Operand2 + Carry

Flags affected              :           N, Z, C, V

Syntax (3)                    :           ADC{ condition }{ S } Rd, Rn, Operand2

 

Examples :

            ADC                r0, r1, r3                       ; R0 = R1 + R3 + Carry (if any)

            ADC                r0, r1, #0xFF                ; R0 = R1 + 255 + Carry (if any)

            ADCS              r2, r11, #1024               ; R2 = R11 + 1024 + Carry (if any)

            ADC                r3, r4, r5, LSL #4          ; R2 = R4 + R5*16 + Carry (if any)

 

If a negative constant is specified as Operand2, the 1’s complement of it is used, and a SBC is substituted for the ADC.

 

ADD  - Arithmetic add

 

ADD adds two 32-bit 2’s complement signed numbers together and store the result into a designated register.

 

Operational function      :           Rd = Rn + Operand2

Flags affected              :           N, Z, C, V

Syntax(3)                     :           ADD{ condition }{ S } Rd, Rn, Operand2

 

Examples :

            ADD               r0, r1, r3                       ; R0 = R1 + R3

            ADDEQ          r0, r1, #0xCC               ; R0 = R1 + 204( If Z set)

            ADDS             r2, r11, #125, 28            ; R2 = R11 + 2000

            ADD               r3, r4, r5, LSR #4          ; R2 = R4 + R5 / 16

 

If a negative constant is specified as Operand2, the 2’s complement of it is used, and a SUB is substituted for the ADD.

 

AND – Logical AND

 

A logical AND operation is performed on two operands, and the 32-bit result is stored into a designated register.

 

Operational function      :           Rd = Rn AND Operand2

Flags affected              :           N, Z, C

Syntax(3)                     :           AND{ condition }{ S } Rd, Rn, Operand2

 

Examples :

            AND               r0, r2, r3, LSL #2          ; Mask via another register

            AND               r0, r1, #0xFFFFFF00     ; Same as BIC R0, R1, #0xFF

            AND               r4, r4, r3                       ;

 

If a negative constant is specified as Operand2, the 1’s complement of it is used, and a BIC is substituted for the AND.

 

BIC – Bit clear

 

Clear the bits in one operand indicated by the bits in the same position in the other operand. The result is stored into a designated register.

 

Operational function      :           Rd = Rn AND ( 1’s complement of Operand2 )

Flags affected              :           N, Z, C

Syntax(3)                     :           BIC{ condition }{ S } Rd, Rn, Operand2

 

Examples :

            BIC                 r1, r1, #1                      ; Clear least significant byte of R1

             

If a negative constant is specified as Operand2, the 1’s complement of it is used, and an AND is substituted for the BIC.

 

CMN – Set negative compare

 

Compare an operand against a 2’s complement negative value. Result is not stored.

 

Operational function      :           Rn + Operand2

Flags affected              :           N, Z, C, V

Syntax(2)                     :           CMN{ condition } Rn, Operand2

 

Examples :

            CMN               r0, r1, #-204                 ; Same as CMP R0, R1, #203

            CMN               r0, r2                            ;

             

If a negative constant is specified as Operand2, the 2’s complement of it is used, and a CMP is substituted for the CMN. The S suffix is always implied.

 

CMP – Arithmetic comparison

 

Compare a register against a value in another register or a constant. Result is not stored.

 

Operational function      :           Rn + Operand2

Flags affected              :           N, Z, C, V

Syntax(2)                     :           CMP{ condition } Rn, Operand2

 

Examples :

            CMN               r0, r1, #-204                 ; Same as CMP R0, R1, #203

            CMN               r0, r2                            ;

             

If a negative constant is specified as Operand2, the 2’s complement of it is used, and a CMN is substituted for the CMP. The S suffix is always implied.

 

EOR – Bitwise exclusive OR

 

A bitwise exclusive OR is performed between two operand words, and the 32-bit result is written to a designated register.

 

Operational function      :           Rd = (Rn AND NOT Operand2) OR (Operand2 AND NOT Rn)

Flags affected              :           N, Z, C

Syntax(3)                     :           EOR{ condition }{ S } Rd, Rn, Operand2

 

Examples :

 

MOV – Move register or constant

 

Move a 32-bit item from one register to another, or move an constant into a register. A constant must apply to the pattern of #expression1.

 

Operational function      :           Rd = Operand2

Flags affected              :           N, Z, C

Syntax(2)                     :           MOV{ condition }{ S } Rd, Operand2

 

Examples :

            MOV               PC, LR            ; Move contents of link register into program counter

            MOV               PC, R15           ; Same as above

            MOV               r1, #0x80000     ; Load a big constant

            MOV               r3, #-205          ; Same as MVN R3, 204

 

If a negative constant is specified as Operand2, the 2’s complement of it is used, and a MVN is substituted for the MOV.


MVN – Move complement of register

 

Load the 1’s complement of a register or register, into another register.

 

Operational function      :           Rd = 0xFFFFFFFF XOR Operand2

Flags affected               :           N, Z, C

Syntax(2)                     :           MVN{ condition }{ S } Rd, Operand2

 

Examples :

            MVN               r0, #40             ; Move contents of link register into program counter

            MVN               r1,#-201           ; Same as MOV R1, #200

            MVN               r1, r3, ASR #4  ; Shift register before operation

 

If a negative constant is specified as Operand2, the 2’s complement of it is used, and a MOV is substituted for the MVN.

 

ORR – Logical OR

 

A logical OR operation is performed on two operand words, and the 32-bit result is stored in a designated register.

 

Operational function      :           Rd = Rn OR Operand2

Flags affected              :           N, Z, C

Syntax(3)                     :           ORR{ condition }{ S } Rd, Rn, Operand2

 

Examples :

            ORR                r1, r1, r0                       ; R1 = R1 OR R0

            ORR                R0, R1, r3, ROR #4

 


RSB – Reverse operand subtract

 

Identical to the SUB instruction, except that the operand order is reversed. The result is stored into a designated register.

 

Operational function      :           Rd = Operand2 - Rn

Flags affected              :           N, Z, C, V

Syntax(3)                     :           RSB{ condition }{ S } Rd, Rn, Operand2

 

Examples :

            RSB                 r1, r1, #0xFF                ; Subtract R1 from a constant

            RSB                 r0, r1, r3, LSL #4

 

RSC – Reverse operand subtract with carry

 

The RSC instruction is identical to the SBC instruction, except that the operand order is reversed.

A value of +1 is added to the difference if the carry bit was set prior to the instruction. Nothing is added if the carry bit was clear.The result is stored in a designated register.

 

Operational function      :           Rd = Operand2 – Rn – 1 + Carry

Flags affected              :           N, Z, C, V

Syntax(3)                     :           RSB{ condition }{ S } Rd, Rn, Operand2

 

Examples :

            RSC                 r1, r1, #0xFF                ; R1 = 255 – R1 – 1 + Carry (if set)

            RSC                 r0, r1, r3, LSL #4          ; R0 = R3*16 – R1

 


SBC – Subtract with carry

 

SBC subtracts two 32-bit operands, storing the difference in a register. A value of +1 is subtracted from the difference if the carry bit was clear prior to the instruction. Nothing is subtracted if the carry bit was set.

 

Operational function      :           Rd = Rn – Operand2 – 1 + Carry

Flags affected              :           N, Z, C, V

Syntax(3)                     :           SBC{ condition }{ S } Rd, Rn, Operand2

 

Examples :

            SBC                 r1, r2, r3

            SBC                 r0, r1, r3, LSL #4

 

If a negative constant is specified as Operand2, the 1’s complement of it is used, and a ADC is substituted for the SBC.

 

SUB – Subtract

 

Subtract one 32-bit operand from another storing the result into a designated register.

 

Operational function      :           Rd = Rn - Operand2

Flags affected              :           N, Z, C, V

Syntax(3)                     :           SUB{ condition }{ S } Rd, Rn, Operand2

 

Examples :

            SUB                r0, r1, r3                       ; R0 = R1 - R3

            SUBEQ           r0, r1, #0xCC               ; R0 = R1 - 204( If Z set)

            SUBS              r2, r11, #125, 28            ; R2 = R11 - 2000

            SUB                r3, r4, r5, LSR #4          ; R2 = R4 - R5 / 16

 

If a negative constant is specified as Operand2, the 2’s complement of it is used, and a ADD is substituted for the SUB.

 


TEQ - Test Equivalence

 

Performs a bitwise Exclusive OR between the two operands, setting the Z flag if the result is zero. Result is not stored.

 

Operational function      :           Rn XOR Operand2

Flags affected              :           N, Z, C

Syntax(2)                     :           TEQ{ condition } Rn, Operand2

 

Examples :

            TEQ                r1, #32                         ; See if R1 contains 32

            TEQ                r0, r2                           

             

The S suffix is always implied.

 

TST – Test under Mask

 

Performs a bitwise AND between the two operands, setting the Z flag if the result is zero.

 

Operational function      :           Rn AND Operand2

Flags affected              :           N, Z, C

Syntax(2)                     :           TST{ condition } Rn, Operand2

 

Examples :

            TST                 r1, #32

            TST                 r0, r2                           

             

The S suffix is always implied.


Branch instructions

 

The syntax within the ARM Simulator is described below.

 

B{ L }{ condition } #expression

 

{ L }                is used to request the Branch with Link form of the instruction. If absent, R14 will not be affected by the instruction.

{ condition }     is a two-char condition code. If absent then AL (always) will be used.

#expression      is the destination (a label). The assembler calculates the offset.

 

If the stack simulation is enabled within the ARM Simulator a BL automatically makes use of the stack.

 

Examples :

 

            B         subone             ; Branch to ’subone’

            BLEQ  subone             ; Branch with link to ’subone’ if Z = 1

            MOV PC, LR              ; Return from subroutine

 

When writing subroutines labels are used to identify the function. In the ARM Simulator a label must be at the line preceding the start of the subroutine. An example is given beloew.

 

subone

 SUB R0, R0, #1

 ADD r0, r1, #2

 MOV PC, LR

 

Placing the label on the same line as the instruction will cause an error.


Load and store register instructions

 

Two memory instructions are supported by the ARM Simulator. Their syntax is described below.

 

LDR|STR { condition } Rd, #address

 

{ condition }                 is a two-char condition code. If absent then AL (ALways) will be used.

{ Rd }                          is a register

#address is any of :

            [Rn]

            [Rn, #expression]

            [Rn, Rm]

            [Rn, Rm shift #count]

 

Rn, Rm            is a register

#expression      is a value in the range 0..4095

shift                 is any of LSL, LSR, ASR or ROR

#count              is any positive constant in the range 1..31

 

STR - store from a register into memory

 

STR r0, [r1]                 ; Store value in R0 into address pointed to by R1

STR r0,[r1, r2]              ; Store value in R0 into address pointed at by R1 + R2

STR r0,[r1,#16]                        ; Store value in R0 into address pointed at by R1 + 16

STR r0,[r1, r2 ,LSL #2] ; Store value in R0 into address pointed at by R1 + R2*4

 

LDR - load from memory into a register

 

Same syntax as STR. The first register acts as the destination register, rather than the source register.

 

LD r0, [r1]                   ; Store value in address pointed to by R1 into R0.
Condition codes

 

Any of the 16 condition codes may be used with any of the instructions supported by the ARM Simulator. The condition codes are listed below.

 

Mnemonic

Code

Condition

Meaning

EQ

0000

Z set

Equal

NE

0001

Z clear

Not equal

CS

0010

C set

Unsigned higher or same

CC

0011

C clear

Unsigned lower

MI

0100

N set

Negative

PL

0101

N clear

Positive or zero

VS

0110

V set

Overflow

VC

0111

V clear

No overflow

HI

1000

C set and Z clear

Unsigned higher

LS

1001

C clear or Z set

Unsigned lower or same

GE

1010

N and V the same

Greater or equal

LT

1011

N and V differ

Less than

GT

1100

Z clear, N and V the same

Greater than

LE

1101

Z set, N and V differ

Less than or equal

AL

1110

-

Always execute

NV

1111

-

Never

 

Shifts

 

The shifts supported by the ARM Simulator are LSL, LSR, ASR and ROR.

 

LSL - Logical Shift Left moves each bit by the specified amount to a more significant position. The least significant bits are filled with zeroes. High bits that are shifted outside the 32-bit result range are discarded. The last discarded bit becomes the shifter carry output, which may be applied to the C bit of the CPSR if the instruction involved is a logical operation with the S bit set.

 

LSR - Logical Shift Right moves each bit by the specified amount to a less significant position. The most significant bits are filled with zeroes. Low bits that are shifted outside the 32-bit result range are discarded. Carry is generated as in LSL.

 

ASR - Arithmetic Shift Right performs almost the same operation as Logical Shift Right. The difference is that the high bits are filled with replicates of the most significant bit of the shifted register instead of zeroes. This means that a negative value is extended with ones and a positive value is extended with zeroes. Carry is generated as in LSR.

 

ROR – Rotate Right reuses the bits that ’fall-of’ on the least significant end by wrapping them around at the most significant position of the result. A Carry is generated in the same way as LSR.

 

Using labels

 

When writing subroutines labels are used to identify the function. In the ARM Simulator a label must be at the line preceding the start of the subroutine. An example is given beloew.

 

subone

 SUB R0, R0, #1

 ADD r0, r1, #2

 MOV PC, LR

 

Placing the label on the same line as the instruction will cause an error.


APPENDIX D – TEST RESULTS

 

Large operand substitution test results

 

Instruction

Result

Immediate value

Rotate value

Assembly value

ADD r0, r1, #89128960

OK

01010101 = 85

0110 = 6

85, 12

ADD r1, r0, #0xDF

OK

11011111 = 223

0000 = 0

223, 0

SUB r1, r0, #0x4000

OK

01000000 = 64

1100 = 12

64, 24

ADD r3, r4, #1024

OK

01000000 = 64

1110 = 14

64, 28

MOV r3, #71303168

OK

01000100 = 68

0011 = 3

68, 6

ADD r4, r5, #6720

OK

01101001 = 105

1101 = 13

105, 26

SUB r3, r4, #65,28

OK

01000001 = 65

1110 = 14

65, 28

 

Shift routine test results

 

LSL – Logical Shift Left

 

Instruction

Expected result

Decimal value

1. ADD r0, r0, #30

R0 = 0x0000001E

30

2. ADD r1, r1, r0, LSL #4

R1 = 0x000001E0

480

3. ADD r2, r2, r0, LSL #30

R2 = 0x80000000

-2147483648

4. ADD r3, r3, r0, LSL #31

R3 = 0x00000000

0

 

LSR – Logical Shift Right

 

Instruction

Expected result

Decimal value

1. ADD r0, r0, #120

R0 = 0x00000078

120

2. ADD r1, r1, r0, LSR #2

R1 = 0x0000001E

30

3. ADD r2, r2, r0, LSR #5

R2 = 0x00000003

3

4. ADD r3, r3, r0, LSR #10

R3 = 0x00000000

0

 

ASR – Arithmetic Shift Right

 

Instruction

Expected result

Decimal value

1. ADD r0, r0, #250

R0 = 0x000000FA

250

2. SUB r1, r1, #250

R1 = 0xFFFFFF06

-250

3. ADD r2, r2, r0 ASR #4

R2 = 0x0000000F

15

5. ADD r3, r3, r1 ASR #4

R3 = 0xFFFFFFF0

-16

 

ROR – Rotate Right

 

Instruction

Expected result

Decimal value

1. ADD r0, r0, #120

R0 = 0x00000078

120

2. ADD r1, r1, r0, ROR #4

R1 = 0x80000007

-2147483641

3. ADD r2, r2, r0, ROR #12

R2 = 0x07800000

125829120

 

RRX – Rotate Right Extended

Due to lack of time the RRX shift operation was not implemented.

 

Conclusion

The shift operations seem to work fine. No errors were encountered during these tests. The fact that shifts are handled by a special object, makes this part quite failsafe. Instructions only pass it the parameters and the result is returned.

 

Conditional execution test results

 

Instruction

Condition Code

Execute

N

Z

C

V

ADD r0,r0,#1

AL

Y

0

0

0

0

SUBNES r0,r0,#1

NE

Y

0

1

1

0

ADDEQ r0,r0,#1

EQ

Y

0

1

1

0

ADDNE r0,r0,#1

NE

N

0

1

1

0

RSBCSS r0,r0,#0

CS

Y

1

0

0

0

ADCSS r0,r1,r2

CS

N

1

0

0

0

ADCCCS r0,r0,#267386880

CC

Y

0

0

1

0

BICHI r1,r0,#10

HI

Y

0

0

1

0

MOVLS r0,#0xFF

LS

N

0

0

1

0

ADDGT r2,r2,#0xCE

GT

Y

0

0

1

0

RSCALS r2,r2,#0x10

AL

Y

1

0

0

0

ADDLTS r2,r2,#4096

LT

Y

0

0

1

0

EORMI r0,r1,#10

MI

N

0

0

1

0

SUBPLS r0,r4,#1

PL

Y

1

0

0

0

ADDS r0,r1,r0

AL

Y

0

0

1

0

MOVLES r5,#0x4000

LE

Y

0

0

1

0

ADD r4,r4,#127,8

AL

Y

0

0

1

0

ADD r5,r5,#127,8

AL

Y

0

0

1

0

ADDS r6,r4,r5

AL

Y

1

0

0

1

ADDGE r0,r7,#0

GE

Y

1

0

0

1

SUBVCS r0,r7,#0xFF

VC

N

1

0

0

1

SBCVS r0,r7,#10

VS

Y

1

0

0

1

MOVLE r0,#0

LE

N

1

0

0

1

MVNLTS r1,#0

LT

N

1

0

0

1

ORREQ r2,r3,r4

EQ

N

1

0

0

1

MOVGES r1,#0

GE

Y

0

1

0

1

MOVLE r0,#0

LE

Y

0

1

0

1

ADDVSS r0,r1,r2, LSL #4

VS

Y

0

0

0

0

 

 

 

 

 

 

 

 

 

 

Negative operand substitution test results

 

Instruction

Expected result

ADC r0,r0,#-5 

SBC r0,r0,#4

SBC r0,r0,#-1025

ADC r0,r0,#64,28

ADD r0,r0,#-10

SUB r0,r0,#10

SUB r0,r0,#-132

ADD r0,r0,#132

AND r0,r3,#-45

BIC r0,r3,#44

BIC r4,r5,#-4097

AND r4,r5,#64,26

CMP r10,#-25

CMN r10,#25

CMN r11,#-255

CMP r11,#255

MOV r7,#-12  

MVN r7,#11

MVN r6,#-98

MOV r6,#97

 


APPENDIX E – USE CASE AND UML DIAGRAMS

 

This section contains the use case and UML diagrams describing the ARM Simulator software.

 

The diagrams are displayed in the following sequence :

·         Use case diagrams

·         Sequence diagrams

·         Overall class diagram

·         Class diagrams of each class

 

The overall use case diagram for the application is displayed first, followed by the sequence diagram for each case.

 

The overall class diagram show how all the classes within the ARM Simulator are connected. Due to the limited space, only the class names are shown in this diagram. For the same reason the diagram had to become a mixture of a Rational Rose and a Word diagram.

 

The last section show detailed class diagrams of each class. Due to the fact that many classes has a identical set of variables and methods, one class has been chosen to represent all of these classes.

 

 

 

 

 

 

 

 


The above diagram gives a short introduction to the symbols and syntax if the diagrams. Blue items are variables, purple are methods. A lock means private, while a key means protected. If a ’$’ sign is appended the item is also static. A class name or method in italics indicates that the class or method is abstract. A circle defines an interface.
Main Use case for the ARM Simulator

 

The following diagram describes the main use cases:

 

 

 

 

 

 

 

 

 

 

 

 

 


 

 


Start Simulator sequence diagram

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 



Open File sequence diagram

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 



Save File sequence diagram

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 



Edit File sequence diagram

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 



Build program sequence diagram

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 



Run Program sequence diagram

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 



Change View sequence diagram

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 



View memory sequence diagram

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 



Edit Option sequence diagram

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 



Stop Program sequence diagram

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 



ArmSimulator class diagram

 

The following diagram shows the main connections between the classes within the ARM Simulator. A mixture of Rational Rose diagrams, and Word drawings had to be used to make it fit within the report.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 



ArmSimulator and ArmSimulatorFrame

 

The ArmSimulator class is the main class of the application. It instanciates a new ArmSimulatorFrame object which contains the full GUI and handles all interaction with the user. No GUI elements are shown in the ArmSimulatorFrame model, because of spacelimitations. The most important variables and methods for program execution is included.

 

 

 

 

 



ArmInstruction class

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 



ArmAddInstruction

 

The layout of the ArmAddInstruction class is identical to those sharing the same syntax. Therefore the ArmAndInstruction, ArmEorInstruction, ArmSubInstruction, ArmRsbInstruction, ArmAdcInstruction, ArmSbcInstruction, ArmRscInstruction, ArmOrrInstruction and ArmBicInstruction class diagrams are not included. Their variables and methods are identical to those of the ArmAddInstruction class.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


ArmMovInstruction

 

The ArmMovInstruction and ArmMvnInstruction class diagrams are identical when it comes to variables and methods, therfore only the ArmMovInstruction class diagram is included here.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 



ArmCmpInstruction

 

The ArmCmpInstruction class diagram is identical to those sharing the same syntax. These comprise of the ArmCmnInstruction, ArmTeqInstruction and ArmTstInstruction diagrams. Only the ArmCmpInstruction class diagram is included.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 



ArmBranchInstruction

 

The ArmBranchInstruction defines both B and BL. It has the following layout:

 



The ArmLdrInstruction and ArmStrInstruction

 

The class diagrams of the two is identical, therefore only the ArmLdrInstruction diagram is shown here.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 



ArmInstructionParser

 

The ArmInstructionParser class is responsible of creating all the instruction objects.


Utility classes

 


The rest of the classes shown in the main diagram is shown here.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 



APPENDIX F – CHOICE OF DEVELOPMENT LANGUAGE

 

The first section of this appendix discusses the software requirements. Section two discusses choice of development language for the project. Section three discusses the Swing user interface library and the Model View Controller architecture. Section four discusses the differences between Java applications and applets.

 

F.1 Software requirements

 

This chapter describes the process of selecting the programming language. When selecting a programming language several factors need to be considered. Before any specific language was considered a list of the required and wanted functionality was created.

 

Object Orientation provides reusability and easier scaling of projects. Its modular class hierarchy makes it easy to introduce new classes and functionality during development.

 

Graphic capabilities the simulator will of course offer a graphical user interface. Languages not supporting this are automatically discarded.

 

Portability is an important factor to keep in mind. If the application runs on several platforms instead of one, the number of users that can be reached

 

Speed for the developer speed may not matter that much, but for the end user that is actually going to use the product it does.

 

Ease of development one may discuss if this should be a factor. For a large scale project with several developers this may not matter much. Considering the time limit on the project, an easy and rapid development tool helps produce a better result.

 

These five criterias will help decide what language to choose.

 

F.2 Selecting a language

 

C++ offers object orientation, it has great speed, and it has good graphic capabilities. Several good window libraries are accessible as for example MFC, OWL or QT. None of these libraries are platform independent. Development is hard for unexperienced, and sometimes even experienced, Windows programmers.

 

Visual Basic can hardly be called object oriented, although it offers reusability and modularity through its modules. It has all the graphic capabilities needed but runs rather slow. Visual Basic programs run only on the Windows platform. Development is easy.

 

Delphi is a graphical development package from Borland, built on top of Pascal. It has all the graphic capabilities needed and the speed as well as development is intermediate. It is not platform independent.

 

Java is object orientated, it has all the graphic capabilities needed. It is supposed to be platform independent but runs rather slow. Development is easy.

 

Condsidering the criteras first set out, Java will be the language of choice.

 

F.3 Selecting a window toolkit

 

The Java Software Development Kit is bundled with a window library collectively known as JFC or Java Foundation Classes. This library actually contains two different packages, AWT and Swing. Although both AWT and Swing is commonly referred to as JFC, Sun recommends to use one of the two, and not mixing GUI components.

 

Swing was first introduced with the 1.2 release of the Development Kit. It defines nearly 40 graphical components, and is said to be 100% system independent.

 

Awt uses native peers to handle actions, in Swing this is solved fully in Java and a more complex framework is being used. This framework is called MVC – Model View Controller.

 

Model View Controller is a design pattern or framework originally developed by Prof. Trygve Reenskaug at Xerox PARC in 1978/79. It was developed to allow Smalltalk to conveniently support GUIs.

 

MVC is used extensively in Swing. Basically, the "model" contains the data, the "view" is the graphical representation, and the "controller" is responsible for the interaction between the other two.

 

As an example, think of visually editing a Tree component that represents a directory.

The display is the view. Selecting a file, and dragging it to another directory will move or copy the file. In order for the operation to happen, the controller must tell the model what just happened in the view.

 

In practice, inter-communication between the view and the controller is complex, so the two are bundled together in one category in Swing. The model (data) is separate though.

 

The MVC architecture makes most of the Swing components more difficult to use than the respective AWT ones, but they are also much more customizable.

 

F.4 Applications and applets

 

It was decided to implement the simulator as an application. Java applications have full access on a users system. The drawback is that the user must have the appropriate Java Runtime Environment installed which is a several megabyte download.

 

If the simulator should be implemented as an applet the software could run in a browser. However applets have strong security restrictions when it comes to accessing a users files. Considering that Swing will be used to create the GUI, the user would have to download and install the Java Plug-In which is, just as JRE, a several megabyte download. The user would also lose the ability of creating and saving assembly programs.


APPENDIX G – SOURCE CODE

 

Please note that not all source code has been included and that some of the listed files only contain extractions of the full source. Only that which was deemed to be of interest to the reader was included. The complete source code can be found on the accompanying CD-Rom.

 

The following source files are printed overleaf:

 

ArmSimulator.java                    - main class for the project

ArmSimulatorFrame.java          - GUI and user interaction

ArmInstructionSet.java             - static constants used by all instruction objects

ArmInstruction.java                  - the superclass of all instruction objects

ArmAddInstruction.java            - an example instruction object

 

ArmInstructionReader.java       - create the instructions from a user program


/*

 * @(#)ArmSimulator.java 1.00 2000/03/07

 *

 * Class ArmSimulator.

 *

 * Main class. This initializes the application and

 * launches ArmSimulatorFrame, which contains the GUI.

 *

 * Torkild Magnussen, BSc Informatics 1999/2000.

 * Email : torkild@magnussen.nu

 * Copyright (c) University of Teesside, Middlesbrough, UK.

 * All Rights Reserved.

 */

 

import java.awt.*;

 

public class ArmSimulator {

 

  private static ArmSimulatorFrame window;

  private static ArmSimulator application;

 

  public static void main(String[] args) {

 

    application = new ArmSimulator();

    application.init();

 

  }

 

  public void init() {

 

    window = new ArmSimulatorFrame("ArmSimulator - ");

 

    Toolkit tk = window.getToolkit();

    Dimension wndSize = tk.getScreenSize();

 

    window.setBounds(wndSize.width/6, wndSize.height/6,

      2*wndSize.width/3, 2*wndSize.height/3);

 

    window.setVisible(true);

 

  }

 

}


/*

 * @(#)ArmSimulatorFrame.java 1.00 2000/03/07

 *

 * Class ArmSimulatorFrame.

 *

 * This class hold the GUI,and handles all user interaction.

 *

 * Torkild Magnussen, BSc Informatics 1999/2000.

 * Email : torkild@magnussen.nu

 * Copyright (c) University of Teesside, Middlesbrough, UK.

 * All Rights Reserved.

 */

 

import javax.swing.*;

import javax.swing.event.*;

import javax.swing.table.*;

import java.awt.*;

import java.awt.event.*;

import java.io.*;

import java.lang.*;

import java.util.*;

 

public class ArmSimulatorFrame extends Jframe implements 

  ActionListener, ItemListener, DocumentListener, WindowListener {

 

  // DocumentViewer

  private ArmDocumentViewer helpView;

 

  // Memory viewer

  private ArmMemoryView extView;

 

  // Program options

  private ArmOptionsView optView;

 

  // Autorun dialog

  private ArmAutoRun autoRun;

 

  // Splashscreen and about box

  private ArmSplashScreen splashScreen;

 

  // Parsing and building instructions

  private StreamTokenizer instructionReader;

 

  private Reader textReader;

 

  private ArmInstructionReader instReader;

 

  private Vector program = new Vector(1);

 

  private Vector listReference = new Vector(1);

 

  private Hashtable labelvals = new Hashtable();

 

  private Vector labels = new Vector(1);

 

  private Stack linkvals = new Stack();

 

  private Hashtable memory;

 

  private boolean stackSimulationActive = false;

  private int currentStackRegister = 13;

  private int currentRegisterView = VIEW_REG_AS_BIN;

  private int currentInstructionView = VIEW_INST_AS_USR;

 

  private ArmInstruction reference;

  private ArmInstruction refHelper;

  private ArmBinaryToDecimal converter;

  private Enumeration enumParseAll;

  private Enumeration enumHelper;

 

  public ArmSimulatorFrame( String title ) {

 

    // Display a splash screen to monitor application loading

    createSplashScreen( 6 );

 

    // Set up initial variables and increase progress

    initApplication( title );

    readIniFile();

    splashScreen.setProgress( 1 );

 

    // Set up the menubar and increase progress

    setJMenuBar( createMenuBar() );

    splashScreen.setProgress( 2 );

 

    // Set up the toolbar and editor and increase progress

    contentPane.add( createToolBar(), BorderLayout.NORTH );

    contentPane.add( createEditor(), BorderLayout.CENTER );

    splashScreen.setProgress( 3 );

 

    // Set up the statusbar and increase progress

    contentPane.add( createStatusBar(), BorderLayout.SOUTH );

    createMemoryView();

    createOptionsView();

    splashScreen.setProgress( 4 );

 

    // Set up programspace and register table, increase progress

    createSimArea();

    splashScreen.setProgress( 5 );

 

    // Initialize help dialog and increase progress

    createHelpView();

    setEditMode();

    splashScreen.setProgress( 6 );

 

    // Pack all elements of the application

    pack();

 

    // Loading is done, so we remove the splashscreen

    splashScreen.dispose();

 

  }

 

 

  /******************************************************************

   Definining events for menu, toolbar, editor and document viewer.

   Toolbar button and menu item events are handled by ActionListener.

   Checkbox menu item events are handled by ItemListener.

   Events in the editor are handled by DocumentListener.

  *****************************************************************/

 

  // Trigger is something is inserted into document

  public void insertUpdate(DocumentEvent e) {

 

    // Set document changed flag

    if(!documentChanged) documentChanged = true;

 

  }

 

  // Trigger if something is deleted in the document

  public void removeUpdate(DocumentEvent e) {

 

    // Set document changed flag

    if(!documentChanged) documentChanged = true;

 

  }

 

  // Trigger if any change is detected in the document

  public void changedUpdate(DocumentEvent e) {

 

    // Set document changed flag

    if(!documentChanged) documentChanged = true;

 

  }

 

  // Triggered by menu selections or toolbar buttons

  public void actionPerformed(ActionEvent e) {

 

    // If this is either File->New or New from toolbar

    if((e.getSource() == tlbNew) || (e.getSource() == mnuNew)) {

 

      // Check if changes has occured since last save

      if(checkForSave()) {

 

            // Clear editor area

        editorText.setText("");

 

        // Set change flag

        documentChanged = false;

 

        // Set document title

        documentName = "Untitled";

 

        // No file assosciated with this document

        documentFile = null;

 

        // Update simulator title

        setTitle(simulatorTitle + documentName);

 

      }

 

    }

 

    // If this is either File->Open or Open from toolbar

    else if((e.getSource() == tlbOpen) ||

(e.getSource() == mnuOpen)) {

 

      // Check if changes has occured since last save

      if(checkForSave()) {

 

        // Get file from dialog

        File file = openDialog();

 

        // If file exists

        if( file != null ) {

 

          // Load another cursor to indicate that system is busy

          setWaitCursor();

 

          // If load was sucessfull set file variables

          if( openDocument(file) ) fileCompleted(file);

 

          // Set change flag

          documentChanged = false;

 

          // Update simulator title

          setTitle(simulatorTitle + documentName);

 

          // Load the default cursor when done

          setDefaultCursor();

 

        }

 

      }

 

    }

 

    // If this is either File->Save or Save from toolbar

    else if((e.getSource() == tlbSave) ||

(e.getSource() == mnuSave)) {

 

      // Call save operation, also deals with documents with no file

      saveOperation();

 

      // Set change flag

      documentChanged = false;

 

      // Update simulator title

      setTitle(simulatorTitle + documentName);

 

    }

 

    // If this is File->Save as

    else if(e.getSource() == mnuSaveas) {

 

      // If save was ok set change flag

      if(saveAsOperation()) documentChanged = false;

 

      // Update simulator title

      setTitle(simulatorTitle + documentName);

 

    }

 

    // If this is File->Exit

    else if(e.getSource() == mnuExit) {

 

      // Check if document changed

      if(checkForSave()) {

 

            // Dispose our frame

        dispose();

 

        // Explicit call system exit

 

        System.exit(0);

 

      }

 

    }

 

    // If this is eiter Edit->Cut or Cut from toolbar

    else if((e.getSource() == tlbCut) || (e.getSource() == mnuCut)) {

 

      // Cut any selected text and store to clipboard

      editorText.cut();

 

    }

 

    // If this is eiter Edit->Copy or Copy from toolbar

    else if((e.getSource() == tlbCopy) ||

(e.getSource() == mnuCopy)) {

 

      // Copy any selected text and store to clipboard

      editorText.copy();

 

    }

 

    // If this is eiter Edit->Paste or Paste from toolbar

    else if((e.getSource() == tlbPaste) ||

(e.getSource() == mnuPaste)) {

 

      // Past content of clipboard into current position

      editorText.paste();

 

    }

 

    // If this is eiter Edit->Options

    else if( e.getSource() == mnuOptions ) {

 

      // If an instance of options is running kill it

      if (optView != null) optView.dispose();

 

      // Re create object to make sure latest settings is applied

      optView = new ArmOptionsView(this, " ArmSimulator - Options");

 

      // Position the help dialog

      optView.setLocationRelativeTo(this);

 

      // Show a new instance of the dialog

      optView.show();

 

    }

 

    // If this is Program->Build or Build from toolbar

    else if((e.getSource() == tlbBuild) ||

(e.getSource() == mnuBuild)) {

 

      // Load another cursor to indicate that system is busy

      setWaitCursor();

 

      // Enter build mode ( disable certain controls ... )

      setBuildMode();

 

      // Try to build the program

      readtext();

 

      if( stackSimulationActive ) {

 

            registerValues[currentStackRegister] = sp;

 

      }

 

      // Update the registers so that they show the current view

      updateRegisters( currentRegisterView );

 

      // Update instruction list to show current view

      setInstructionView( currentInstructionView );

 

      // Current instruction should be the first

      current = 0;

 

      // Load the default cursor when done

      setDefaultCursor();

 

    }

 

    // If this is Program->Reset or Reset from toolbar

    else if((e.getSource() == tlbReset) ||

(e.getSource() == mnuReset)) {

 

      // Load another cursor to indicate that system is busy

      setWaitCursor();

 

      // Get an instance of a instruction and clear datamodel */

      reference = (ArmInstruction) program.elementAt(0);

 

      // Set clear mode

      reference.executeInstruction(registerValues, false,

linkvals, memory);

 

      if( stackSimulationActive ) {

 

            registerValues[currentStackRegister] = sp;

 

      }

 

      // Update the registers so that they show the current view

      updateRegisters( currentRegisterView );

 

      // Update instruction list to show current view

      setInstructionView( currentInstructionView );

 

      // Current instruction should be the first

      current = 0;

 

      // Clear the stack

      linkvals = new Stack();

 

      // Point to first instruction in program list

      programList.setSelectedIndex(current);

 

      // Reset view

      programList.ensureIndexIsVisible(current);

 

      // Make sure step button is enables

      tlbStep.setEnabled( true );

 

      // Make sure step menu is enabled

      mnuStep.setEnabled( true );

 

      // Make sure run is enabled

      mnuRun.setEnabled( true );

 

      // Load the default cursor when done

      setDefaultCursor();

 

    }

 

    // If this is Program->Stop or Stop from toolbar

    else if((e.getSource() == tlbStop) ||

(e.getSource() == mnuStop)) {

 

      // Remove the list and register table

      getContentPane().remove(dividerArea);

 

      // Add the editor

      getContentPane().add(editorArea, BorderLayout.CENTER);

 

      // Invalidate the frame to say that components have changed

      invalidate();

 

      // Validate the frame to show the list and register tables

      validate();

 

      // Explicit call to repaint() to make sure frame gets updated

      repaint();

 

      // Current instruction should be the first

      current = 0;

 

      // Clear the stack

      linkvals = new Stack();

 

      // Get an instance of a instruction and clear datamodel */

      reference = (ArmInstruction) program.elementAt(0);

 

      // Set clear mode

      reference.executeInstruction(registerValues, false,

linkvals, memory);

 

      // Update the registers

      updateRegisters( currentRegisterView );

 

      // Kill memory dialog

      //if( memoryView != null ) memoryView.dispose();

 

      // Kill stackdialog

      //if( stackView != null ) stackView.dispose();

 

      /* Kill memory dialog */

      if( extView != null ) extView.dispose();

 

      // Set edit mode ( enable and disable controls )

      setEditMode();

 

    }

 

    // If this is Program->Step or Step from toolbar

    else if((e.getSource() == tlbStep) ||

(e.getSource() == mnuStep)) {

 

      // Create an int representation of the program counter

converter = new    

  ArmBinaryToDecimal(registerValues[15].substring(0,35));

 

      // Get the in representation

      int pc = converter.getValue();

 

      // Convert program counter so that we can apply it to the list

      if( pc != 0 ) current = pc / 4;

 

int tracker =   

  ((Integer)listReference.elementAt(current*2+1)).intValue();

 

      // Try to process the next instruction

      try {

 

        // Get an instance of the next instruction

        reference = (ArmInstruction) program.elementAt( tracker );

 

        // Pass the datamodel using call by reference

        if( reference.executeInstruction(

            registerValues, true, linkvals, memory ) ) {

 

          // Set new register values using the current view

          updateRegisters( currentRegisterView );

 

        }

 

 

 

        // Create an int representation of the program counter

  converter = new          

    ArmBinaryToDecimal(registerValues[15].substring(0,35));

 

        // Get the in representation

        pc = converter.getValue();

 

        // Convert program counter so we can apply it to the list

        if( pc != 0 ) current = pc / 4;

 

tracker =  

  ((Integer)listReference.elementAt(current*2+1)).intValue();

 

        // Update list selection

        programList.setSelectedIndex(tracker);

 

        // Update listview

        programList.ensureIndexIsVisible( tracker );

 

        // Update stackview

        //if( stackView != null ) stackView.setData( linkvals );

 

if( extView != null ) extView.setMemory( memory, linkvals,     

  stackSimulationActive );

 

        // Update memory view

        if( extView != null ) extView.setSelected( current );

 

      }

 

      // Catch out of bounds

      catch( ArrayIndexOutOfBoundsException aiobe ) {

 

            statusBarText.setText(" End of program ");

            tlbStep.setEnabled( false );

            mnuStep.setEnabled( false );

            mnuRun.setEnabled( false );

 

      }

 

    }

 

    // If this is Program->AutoRun

    else if( e.getSource() == mnuRun ) {

 

 

      // If an instance of helpview is running, the kill it

      if (autoRun != null) autoRun.dispose();

 

      // Reinitialize  dialog

      autoRun = new ArmAutoRun(this, " ArmSimulator - AutoRun");

 

      // Position the help dialog

      autoRun.setLocationRelativeTo(this);

 

      // Show a new instance of the dialog

      autoRun.show();

 

 

    }

 

    // If this is View->Memory or Memory from toolbar

    else if( (e.getSource()==tlbMemory) ||

 (e.getSource() == mnuMemory) ) {

 

     if( extView != null ) extView.dispose();

 

      extView.setLocationRelativeTo( this );

 

      extView.setMemory( memory, linkvals, stackSimulationActive );

 

      extView.show();

 

    }

 

    // If this is Help->Contents or Contents from toolbar

    else if((e.getSource()==tlbContents) ||    

      (e.getSource()==mnuContents)) {

 

      // If an instance of helpview is running, the kill it

      if (helpView != null) helpView.dispose();

 

      // Position the help dialog

      helpView.setLocationRelativeTo(this);

 

      // Show a new instance of the dialog

      helpView.show();

 

    }

 

    // If this is Help->About or About from toolbar

    else if(e.getSource() == mnuAbout) {

 

      // If an instance is running, kill it

      if (splashScreen != null) splashScreen.dispose();

 

      // Hide the progress bar

      splashScreen.useProgress(false);

 

      // Show the about box

      splashScreen.show();

 

      // Request focus for our about box

      splashScreen.requestFocus();

 

    }

 

  }

 

 

  // If main frame gets activated

  public void windowActivated(WindowEvent e) {

 

    // Kill any instances of about box

    if (splashScreen != null) splashScreen.dispose();

 

  }

 

  // If window is closing

  public void windowClosing(WindowEvent e) {

 

    // Check if document has changed

    if( checkForSave() ) {

 

      // Dispose main frame

      dispose();

 

      // Explixit call to system exit

      System.exit(0);

 

    }

    else { } //Do nothing

 

 

  }

 

  // Do nothing, these must be declared

  public void windowClosed(WindowEvent e) { }

  public void windowDeactivated(WindowEvent e) { }

  public void windowDeiconified(WindowEvent e) { }

  public void windowIconified(WindowEvent e) { }

  public void windowOpened(WindowEvent e) { }

 

 

  // Handle checkbox menu items

  public void itemStateChanged( ItemEvent ec ) {

 

    // If this is View->ToolBar or ToolBar from toolbar

    if( ec.getSource() == mnuToolbar ) {

 

      // If toolbar is visible, then hide and vice versa

      toolBar.setVisible( ! toolBar.isVisible() );

 

    }

 

    // Else this must be the statusbar

    else {

 

      // If statusbar is visible, then hide and vice versa

      statusBar.setVisible( ! statusBar.isVisible() );

 

    }

 

    // Invalidate the frame to say that components have changed

    invalidate();

 

    // Validate the frame to show changes

    validate();

 

  }

 

 

 

 

  // Create a SplashScreen

  private void createSplashScreen(int num) {

 

    // Create a new instance of ArmSplashScreen

    splashScreen = new ArmSplashScreen(num);

 

    // Show the splashScreen

    splashScreen.show();

 

  }

 

  // Initialize application

  private void initApplication(String t) {

 

    // Apply a window listener

    this.addWindowListener(this);

 

    // Change default close operation

    this.setDefaultCloseOperation( DO_NOTHING_ON_CLOSE );

 

    // Set change flag

    documentChanged = false;

 

    // Set document name

    documentName = "Untitled";

 

    // Create a new instance of a JFileChooser object

    fileHandler = new JFileChooser();

 

    // Set title String

    simulatorTitle = t;

 

    // Set simulator title

    setTitle(simulatorTitle + documentName);

 

    // Get this frames content pane

    contentPane = getContentPane();

 

    // Apply BorderLayout to the content pane

    contentPane.setLayout(new BorderLayout());

 

  }

 

 

  // Create memory view

  private void createMemoryView() {

 

    // Create a new instance of an ArmStackView

    extView = new ArmMemoryView(this, " ArmSimulator - MemoryView");

 

  }

 

 // Create memory view

  private void createOptionsView() {

 

    // Create a new instance of an ArmStackView

    optView = new ArmOptionsView(this, " ArmSimulator - Options");

 

  }

 

 

  // Create help system

  private void createHelpView() {

 

    // Create a new instance of an ArmDocumentViewer

    helpView = new ArmDocumentViewer(

      this, " ArmSimulator - HelpViewer", "index.html");

 

  }

 

  /******************************************************************

   readText() reads user input and creates the instructions using

   the ArmInstructionParser help class.

  ******************************************************************/

 

  private void readtext() {

 

    // new instance of a StringReader with editor text as argument

    textReader = new StringReader(editorText.getText()+"\n");

 

    // new instance of StreamTokenizer with StringReader as argument

    instructionReader = new StreamTokenizer(textReader);

 

    // Tell StreamTokenizer that we want reports on EOL's        

    instructionReader.eolIsSignificant(true);

 

    // Set ';' as comment char

    instructionReader.commentChar(';');

 

    // Set spaces as whitespace chars, these will be ignored

    instructionReader.whitespaceChars(' ',' ');

 

    // Set square brackets as whitespace chars

    instructionReader.whitespaceChars('[','[');

 

    // Set square brackets as whitespace chars

    instructionReader.whitespaceChars(']',']');

 

    // Set ',' as whitespace chars, these will be ignored

    instructionReader.whitespaceChars(',',',');

 

    // Set '!' as regular char

    instructionReader.wordChars('!','!');

 

    // We need some helper variables

    int token = 0; int wc = 0;

 

    // String array to store values in

    String[] vals = new String[7];

 

    // We need to be able to abort reading

    boolean read = true;

 

    // Help variable to keep track of valid instructions

    int counter = 0;

 

    // Vector to store ArmInstruction objects in

    program = new Vector(1);

 

    // Vector to track listreferences

    listReference = new Vector(1);

 

    // Create an StringReader with a BufferedReader wrapper      

    BufferedReader in = new BufferedReader(new    

    StringReader(editorText.getText()+"\n"));