The simulator has two parts, an assembler (built using PEGjs) which translates 8085 assembly code to machine code and a 8085 microprocessor simulator (written in C and compiled to JS using Emscripten) which executes machine code. This web application is a graphical interface for the simulator.
Sim8085 was originally developed by Debjit Biswas. It would not be possible to improve the quality and correctness of the emulator without the bugs reported by individuals. Big thanks, to all bug reporters and contributors.
GNUSim8085 is available in repository of most Linux distributions. If the latest version is not available then you can download source or binaries we provide. Please note that we do not provide binaries for all distributions.
A big thanks to sridhar and the gang maintaining the 8085, Its around 12 midnight now and am studying for my microprocessor semester practicals. But my hand writing is next to shit, So bad that even I cannot understand it. To make matters worse I had to study from a record which was written in a hurry. Anyway all my mnemonics, opcodes in my record are messed up, so needed a way to properly sync up the two and learn.
This is another quick post about an unfinished (but working) software which I wrote around 8 years ago. It is an Intel 8085 microprocessor simulator, with a text based interface. The objective was to simulate the microprocessor, along with a minimal interface which closely resembles the microprocessor kit with the 7-segment displays, hex keyboard and minimal debugging features.
In our undergraduate computer science degree, we had a few subjects on microprocessor architecture. One of the subjects focused on the Intel 8085 microprocessor architecture in great details, Intel 8086 architecture, interfacing, etc. Along with the detailed architecture, we also had to do some assembly code for 8085. It was fun because, we had to use a physical 8085 microprocessor kit with a hex keyboard and just those 7-segment displays.
To write a code in the kit, you need to scan through the memory and enter the values of the assembled code. Who assembles it? We had to do that manually. We would have a table of all the instructions and the hex value for the op-code. More essentially it is very important to know the precise operations for each instruction. What operation it performs, which registers are accessed, what memory locations are accessed and how does it change the flags.
Although we were required to use the physical 8085 microprocessor kit in the exams, but for practice, we used 8085 microprocessor simulators. There are quite a few 8085 microprocessor simulators available. One of them was provided with one of the text books. There were simulators with text interface, some with text and some with nice GUI interfaces. I used one of them, the GNUSim8085. You can find an in depth review of GNUSim8085, which I wrote for OpenSource For You long ago, also posted here: Reviewing the GNUSim8085 (v1.3.7).
The right hand side shows the flags and the register states and the present start address of the simulator, very useful for step execution and debugging. The left hand side shown the kind of 7-segment display which you will get in a basic simulator kit.
To execute a code, first enter your assembled code, then set the start address by pressing s and then press x to execute. The previous image shows a step execution scenario (press z). In this case the simulator disassembles the machine code and displays the presently being executed code and the next one.
In the test machine state file dirty_test, I wrote and assembled a bubble sort code in 8085 and loaded it into 0x4200 location. The input list for the list to be sorted is in 0x0000. The first number in the list indicates the length of the list, then the actual numbers to be sorted follows. The bubble sort code sorts the list at 0x0000o and stores the result list in 0x07d0.
Go through the README file, as it has a description and I also have uploaded a file with three demo codes which you can run immediately. Play with the features if you want, and if you are able to break anything, please comment.
As I was in the middle of the year, and I could not allow much time to be invested on this, I gave myself a good 14 days to get the first version running. When I finished this, I thought that although it works as planned, but as it was not complete, therefore I thought to post this in my blog once I implement the TODOs for the second iteration (which never happened). After a few years I put that in github.
Download it as zip or clone repository. Execute make, run from present directory ./dirty or do make install as root. Note you need to have ncurses-devel, else build will fail. For Fedora/RedHat/OpenSuSe/CentOS do a dnf install ncurses-devel.
The struct _instruction_set holds all the information related to the instructions. Each entry for the array of structures struct _instruction_set instruction_set indicates the description of an instruction, which is needed. The way struct _instruction_set instruction_set was initialised is key. The description of an instruction is ordered in the array of struct _instruction_set instruction_set in the order of their op-code values. This will enable us to use the op-code to directly fetch the related information about the instruction.
The first field ins holds the function pointer for instruction. The second field ins_str the instruction string, this is used to display the instruction being executed. The third and forth fields are the operand_1 and operand_2. If an operand is not present, this will be an empty string. In the case if the operand is an immediate number, then this is set as a blank space. The last field mem_opnd is essentially a combined field. The upper nibble of this byte indicates the number of memory operations required to access the first operand, where as the lower nibble of this byte indicates the number of memory operations required to access the second operand. This is kind of unnecessary, but this was useful as in our course, it was sometimes required to compute and write the exact number of memory accesses.
For example _mvi_e , "mvi" , "e" , " " , 0x01, //0x1e is indexed in the 0x1e location, which is the op-code for MVI E. The first field has the function pointer _mvi_e, which simulates the move immediate to register E instruction. The second field shows the name of the instruction mvi. The first operand is the register e, therefore requires 0 memory operations, thus the lower nibble of the mem_opnd is 0. The second operand is kept a blank space, indicating there will be an immediate numeric operand, and in this case the number of memory operations would require one clock cycle.
Next is the CPU state. It has three sections. Sets of registers, including the flags register. The system memory array, 64K long, which is the max addressable memory for the Intel 8085. The last section is to store some additional things to help the simulator, like storing the last location where the program counter was (will help to implement step back), and holding a pointer to the instruction set structure to hold which instruction is running, so we can use the structure to print on screen what is going on.
Now we need to implement the functions which simulates the instructions. This took a significantly long time to write correctly by referring to the manual and then unit testing each function to make absolutely sure they work. This is because, I found quite a number of well used simulator had bugs in the DAA instruction.
This is an example of an instruction, ADD C. This performs A = A + C, and then updates the flags. Initially I implemented the flag updates inside each function, then I realised in a case of bug or change, it will be extremely difficult to maintain it, therefore made a centralised tm_update_flags function which is responsible to perform all the updates.
One interesting thing is, not all operators has the same number of memory operands. Updating the program counter is done by the main engine which performs the execution. But for the program counter modification like in jump instructions the instruction functions are is still responsible to perform change. This could have been a bit more centralised, but this is what I decided that time instead of over-engineering the thing. Possibly putting the number of bytes the instruction reads in the instruction description struct would have been a better idea.
This just keeps on fetching the next instruction and execute it until the code is not finished yet. One must place HLT at the end to terminate the code, which returns the EXECUTE_END and stops the loop.
The rest of the code which does the fetch and execute works on an internal state struct _8085_machine_t *tm. This is an object which represents the machine state entirely and on which all the instruction will work on. This state machine can directly be stored disk. A critical point to be noted is that I should have serialised the structure while storing. The UI was made in the last moment (visible from the code :) ). As the structure padding is implementation defined, if you save the machine state in an executable in one compiler, and load from an executable compiled in another compiler, the padding can change and create problems loading the file. I have tested in gcc (GCC) 7.1.1 20170622 (Red Hat 7.1.1-3). The tm_fetch_instruction reads the next instruction from the memory and uses it as an index in the instruction set structure to take out the corresponding row. Then it loads operands depending on the nature of the instruction, adjusts the program counter. It also sets the tm->current_instruction to the instruction is read. After this, the simulator is ready to perform the execution. This is done with the tm_execute_instruction function. These two are called in a loop in the above execute function.
If you see the 8085.h file, it defines the interface using which one can hook the simulator to any UI. To test the interface, I made a very quick UI. At that point of time, I was pretty tired and wrote and tested th UI in 2 days. Both the code and the UI was dirty, therefore the name.
c80f0f1006