Use Python to write a program that mimics CPU work _python

Source: Internet
Author: User
Tags abs gtk numeric

Earlier this morning, in my planet Python source, I read an interesting article, "Developing cardiac: Cardboard computer (developing upwards:CARDIAC:The cardboard Computer)", It's about the cardboard computer called cardiac. Some of my followers and readers should know that I have a project called Simple CPU (SIMPLE-CPU), which I have been working on for the past few months and has released the source code. I really should give this project a proper license, so , others may be more interested in and used in their own projects. Anyway, hopefully after this release, I can finish this thing.

After reading this article and the pages it links to, I was inspired by I decided to write my own simulator for it because I had the experience of writing bytecode engines. I plan to follow this article and go ahead and write an article on the assembler, followed by an article on the compiler. So, through these articles, You can basically learn how to create a compilation toolset for cardiac in Python. In a simple CPU (SIMPLE-CPU) project, I have already written a complete working assembler. In the built-in game, there is already the initial step of a working compiler. I also choose cardiac as a verification machine because it is absolutely simple. No need for complex memories, Each opcode accepts only a single parameter, so it's a great learning tool. In addition, all data parameters are the same, no need to detect a program is a register, string or memory address. In fact, there is only one register, accumulator. So let's get started! We will create them based on the class, This includes the scope. If you want to try, you can simply add new opcode by subclass. First, we will focus on initializing routines. This CPU is very simple, so we only need to initialize the following: CPU registers, opcode, memory space, card reader/input, and print/tty/output.

Class Cardiac (object): "" "" "" This class is the cardiac "CPU". "" def __init__ (self): Self.init_cpu () Self.reset () Self.init_mem () Self.init_reader () self.init_output () def Rese  T (self): "" This is resets the CPU ' s registers to their defaults. "" "self.pc = 0 #: program Counter self.ir = 0 #: instruction Register SELF.ACC = 0 #: Accumulator self.running =
 False #: Are we running?  def init_cpu (self): "" This is fancy method would automatically build a list of we opcodes into a hash. This enables us to build a typical case/select system in Python and also keeps things more DRY. We could have also used the getattr during the process () method before, and wrapped it around a try/except block, but tha T looks a bit messy.   This is keeps things clean and simple with a nice one-to-one call-map.
  "" "Self.__opcodes = {} classes = [self.__class__] #: This holds the classes and base classes. While classes:cls = Classes.pop () # Pop the classes stack and Being if cls.__bases__: # Does this class have any base classes?
    Classes = classes + list (cls.__bases__) for name in Dir (CLS): # lets iterate through the names.
     If name[:7] = = ' Opcode_ ': # We only have want opcodes here. Try:opcode = Int (name[7:]) except Valueerror:raise nameerror (' opcodes must be numeric, invalid opcode: %s '% name[7:] Self.__opcodes.update ({opcode:getattr (self, ' opcode_%s '% OpCode)}) def init_mem (self): "" ""  Method resets the cardiac ' memory spaces to all blank strings, as per cardiac specs.
 "" "Self.mem = [' For I in Range (0,100)] self.mem[0] = ' 001 ' #: the Cardiac bootstrap.  def init_reader (self): "" "This method initializes the input reader.
 "" "Self.reader = [] #: This variable can is accessed after initializing ' class to provide input data.
  def init_output (self): "" "This method initializes the output deck/paper/printer/teletype/etc ..." "
 Self.output = []

I wish I could write a note that will let you see the various parts of the code. Perhaps you've found that this code handles the instruction set's method differently from the SIMPLE-CPU project. Because it allows developers to easily extend the class library based on their own needs, I intend to continue using this approach in subsequent projects. With my in-depth understanding of the functional principles of the various parts, the project is also constantly developing and changing.  Actually, to do such a project really can let people learn a lot of things.  For the computer-savvy people, the CPU working principle, the instruction set is how to deal with it, is not a problem. The point is, it's fun to be able to implement such a CPU emulator in your own mind. According to the way I imagined it, I created such an emulator, and then I watched it run, it is called a sense of achievement.


Next, we'll talk about tool functions (utility functions), which are used in many places and are allowed to be overridden in subclasses (subclasses):

 def read_deck (self, fname): ""  read the instructions in reader.
  "" " Self.reader = [S.rstrip (' \ n ') for s in open (fname, ' R '). ReadLines ()]
  Self.reader.reverse ()
 def fetch (self): ""  reads the instruction from memory according to the instruction pointer (program pointer) and then adds the instruction pointer to 1.
  "" self.ir = Int (self.mem[self.pc])
  self.pc +=1
 def get_memint (self, data): ""  because we are in a string form (* String* based) to save the memory data, to simulate cardiac, the string must be converted to an integer. If you have memory in other storage forms, such as mmap, you can override this function as needed.
  "" return int (Self.mem[data])
 def pad (self, data, length=3): "" "The function  of this function is to complement 0 in front of the number, as in cardiac." "
  orig = int (data)
  padding = ' 0 ' *length
  data = '%s%s '% (padding, ABS (data)
  if orig < 0:
   retur N '-' +data[-length:] return
  data[-length:]

Later in this article I will give you a piece of code that can be combined with mixin classes, flexibility (pluggable) stronger. Finally, this is the way to handle the instruction set:

def process (self): "" This function handles only one instruction. By default, calling from the Loop code (running loop), you can also write your own code, invoke it as a single step, or use Time.sleep () to slow down execution.  This function can also be called if you want to use the tk/gtk/qt/curses to do a front-end interface (frontend), or to operate in another thread. "" "self.fetch () opcode, data = Int (Math.floor (self.ir/100)), self.ir% Self.__opcodes[opcode] (data) def OpCo  De_0 (self, data): "" "" "" "" "Self.mem[data" = Self.reader.pop () def opcode_1 (self, data): "" "" "" "" "" "" "Self.acc  = Self.get_memint (data) def opcode_2 (self, data): "" "" Addition Instruction "" "SELF.ACC + + = self.get_memint (data) def opcode_3 (self, Data): "" "" "" "" Test accumulator Content Directive "" if SELF.ACC < 0:self.pc = Data def opcode_4 (self, data): "" "" "," "" "" "" X,y = Int ( 
   Math.floor (DATA/10)), int (data%) for I in Range (0,x): SELF.ACC = (SELF.ACC *)% 10000 to I in range (0,y): SELF.ACC = Int (Math.floor (SELF.ACC/10)) def opcode_5 (self, data): "" "Output Instruction" "Self.output.append (Self.mem[data
 ] def opcode_6 (self, data): "" Storage Instruction "" Self.mem[data] = Self.pad (SELF.ACC)def opcode_7 (self, data): "" "" "" "" "" "Self.acc-= Self.get_memint (data) def opcode_8 (self, data):" "" Unconditional Jump Instruction "" self.pc = Data def opcode_9 (self, data): "" "Terminate, reset Instruction" "" Self.reset () def run (self, Pc=none): "" "" "" "" "The code has been executed to meet the end Stop/reset instructions so far. ' "' If pc:self.pc = pc self.running = True while self.running:self.process () print" output:\n%s "% ' \ n '. Jo In (Self.output) self.init_output () if __name__ = = ' __main__ ': c = cardiac () c.read_deck (' Deck1.txt ') Try:c.run () E
 Xcept:print "IR:%S\NPC:%s\noutput:%s\n"% (c.ir, c.pc, ' \ n '. Join (c.output)) raise


This section is the code mentioned above that can be used in mixin, and after I refactor, the code is as follows:
 

Class Memory (object): "" This class implements the various functions of the emulator's virtual memory Space "" Def Init_mem (self): "" Clears all data in cardiac system memory with a blank string "" self.me
 m = [' For I in Range (0,100)] self.mem[0] = ' 001 ' #: Start the cardiac system. def get_memint (self, data): "" "because we are in a string form (*string* based) to save the memory data, to simulate cardiac, we will convert the string to an integer.  If it is memory in other forms of storage, such as Mmap, you can override this function as needed. "" "Return int (Self.mem[data]) def pad (self, data, length=3):" "" in front of the number 0 "" Orig = Int (data) padding = ' 0 ' *le Ngth data = '%s%s '% (padding, ABS (data)) if orig < 0:return '-' +data[-length:] return Data[-length:] class I O (object): "" This class implements the I/O functionality of the emulator. To enable alternate methods of the input and output, swap this.  "" "Def Init_reader (self):" "Initializes reader.
 "" "Self.reader = [] #: This variable can be used to read the input data after the class has been initialized.  def init_output (self): "" Initializes output features such as: deck/paper/printer/teletype/...
  "" "Self.output = [] def read_deck (self, fname):" "reads the instruction to reader." " Self.reader = [S.rstrip (' \ n ') for s in open (fname, ' R '). ReadLines ()] Self.rEader.reverse () def format_output (self): "format virtual I/O device Output" "" Return ' \ n '. Join (self.output) def GET_INP  UT (self): "" "to obtain IO input (input), that is, read the data with reader, instead of the original Raw_input ().
   "" "Try:return Self.reader.pop () except Indexerror: # If reader encounters a file end sign (EOF), replace reader with Raw_input (). Return Raw_input (' INP: ') [: 3] def stdout (self, data): Self.output.append (data) class CPU (object): "" "This class simulates the cardiac CPU . "" "Def __init__ (self): Self.init_cpu () Self.reset () Try:self.init_mem () except Attributeerror:raise Noti
  Mplementederror (' You need to mixin a memory-enabled class. ') Try:self.init_reader () self.init_output () except Attributeerror:raise notimplementederror (' You need to mixin
 A io-enabled class. ') def reset (self): "Reset CPU registers with defaults" "self.pc = 0 #: instruction pointer self.ir = 0 #: Instruction Register SELF.ACC = 0 #: Accumulator self.run
 ning = False #: Emulator's Running state? def init_cpu (self): "" This function automatically creates a set of instructions in a hash table. This way we can invoke the instruction using the Case/select method while keeping the code simple.  Of course, it's OK to use getattr in process () and catch exceptions with Try/except, but the code looks less concise.
  "" "Self.__opcodes = {} classes = [self.__class__] #: Gets all classes, contains base classes. While classes:cls = Classes.pop () # Bounces the class out of the stack if cls.__bases__: # To determine if there is a base class classes = classes + list (Cls.__bases_
    _) for name in Dir (CLS): # traversal name. If name[:7] = = ' Opcode_ ': # simply read the instructions to Try:opcode = Int (name[7:]) except Valueerror:raise  R (' opcodes must be numeric, invalid opcode:%s '% name[7:]) self.__opcodes.update ({opcode:getattr (self, ' opcode_%s '%  OpCode) def fetch (self): "" reads the instruction from memory according to the instruction pointer (program pointer), and then the instruction pointer adds 1. "" "self.ir = Self.get_memint (self.pc) self.pc +=1 def process (self):" "handles the current instruction, only one. By default, it is called in loop code (running loop), you can write your own code, call it in step mode, or use Time.sleep () to slow down execution.  It is also possible to call this function in the thread of the Tk/gtk/qt/curses interface. "" "self.fetch () opcode, data = Int (Math.floor (self.ir/100)), self.ir% Self.__opcodes[opcode] (data) def OpCo De_0 (self, data): "" Input directives """ Self.mem[data] = Self.get_input () def opcode_1 (self, data): "" "Clear accumulator instruction" "SELF.ACC = self.get_memint (data) de
  F opcode_2 (self, data): "" Addition Instruction "" "SELF.ACC + + + self.get_memint (data) def opcode_3 (self, data):" "" Test accumulator Content directive "" " If SELF.ACC < 0:self.pc = Data def opcode_4 (self, data): "" Offset Instruction "" "x,y = Int (Math.floor (DATA/10)), int ( Data% for I in range (0,x): SELF.ACC = (SELF.ACC *)% 10000 to I in range (0,y): SELF.ACC = Int (Math.floo  R (SELF.ACC/10) def opcode_5 (self, data): "" "Output Instruction" "" Self.stdout (Self.mem[data]) def opcode_6 (self, data): "" Storage directives "" "self.mem[data] = Self.pad (SELF.ACC) def opcode_7 (self, data):" "" "" "" "" "Self.acc-= Self.get_memint (da TA) def opcode_8 (self, data): "" Unconditional jump Instruction "" "SELF.PC = Data def opcode_9 (self, data):" "Stop/Reset Instruction" "" Self.reset
  () def run (self, Pc=none): "" "This code runs until the Halt/reset command is encountered." " If pc:self.pc = pc self.running = True while Self.running:seLf.process () print "output:\n%s"% Self.format_output () self.init_output () class cardiac (CPU, Memory, IO): Passif __n ame__ = = ' __main__ ': c = cardiac () c.read_deck (' Deck1.txt ') try:c.run () except:print "IR:%S\NPC:%s\noutput:%s
 \ n "% (c.ir, c.pc, C.format_output ()) Raise

You can find the deck1.txt used in this article from the developing Upwards:CARDIAC:The cardboard Computer.

I hope this article can inspire you, how to design a class-based module, plug-in strong (pluggable) Paython code, and how to develop the CPU emulator. The assembler compiler (assembler) used in this article will be taught in the next post.

This section is mentioned above, the code that can be used in mixin, after I refactor, the code is as follows:


Class Memory (object): "" This class implements the various functions of the emulator's virtual memory Space "" Def Init_mem (self): "" Clears all data in cardiac system memory with a blank string "" self.me
 m = [' For I in Range (0,100)] self.mem[0] = ' 001 ' #: Start the cardiac system. def get_memint (self, data): "" "because we are in a string form (*string* based) to save the memory data, to simulate cardiac, we will convert the string to an integer.  If it is memory in other forms of storage, such as Mmap, you can override this function as needed. "" "Return int (Self.mem[data]) def pad (self, data, length=3):" "" in front of the number 0 "" Orig = Int (data) padding = ' 0 ' *le Ngth data = '%s%s '% (padding, ABS (data)) if orig < 0:return '-' +data[-length:] return Data[-length:] class I O (object): "" This class implements the I/O functionality of the emulator. To enable alternate methods of the input and output, swap this.  "" "Def Init_reader (self):" "Initializes reader.
 "" "Self.reader = [] #: This variable can be used to read the input data after the class has been initialized.  def init_output (self): "" Initializes output features such as: deck/paper/printer/teletype/...
  "" "Self.output = [] def read_deck (self, fname):" "reads the instruction to reader." " Self.reader = [S.rstrip (' \ n ') for s in open (fname, ' R '). ReadLines ()] Self.rEader.reverse () def format_output (self): "format virtual I/O device Output" "" Return ' \ n '. Join (self.output) def GET_INP  UT (self): "" "to obtain IO input (input), that is, read the data with reader, instead of the original Raw_input ().
   "" "Try:return Self.reader.pop () except Indexerror: # If reader encounters a file end sign (EOF), replace reader with Raw_input (). Return Raw_input (' INP: ') [: 3] def stdout (self, data): Self.output.append (data) class CPU (object): "" "This class simulates the cardiac CPU . "" "Def __init__ (self): Self.init_cpu () Self.reset () Try:self.init_mem () except Attributeerror:raise Noti
  Mplementederror (' You need to mixin a memory-enabled class. ') Try:self.init_reader () self.init_output () except Attributeerror:raise notimplementederror (' You need to mixin
 A io-enabled class. ') def reset (self): "Reset CPU registers with defaults" "self.pc = 0 #: instruction pointer self.ir = 0 #: Instruction Register SELF.ACC = 0 #: Accumulator self.run
 ning = False #: Emulator's Running state? def init_cpu (self): "" This function automatically creates a set of instructions in a hash table. This way we can invoke the instruction using the Case/select method while keeping the code simple.  Of course, it's OK to use getattr in process () and catch exceptions with Try/except, but the code looks less concise.
  "" "Self.__opcodes = {} classes = [self.__class__] #: Gets all classes, contains base classes. While classes:cls = Classes.pop () # Bounces the class out of the stack if cls.__bases__: # To determine if there is a base class classes = classes + list (Cls.__bases_
    _) for name in Dir (CLS): # traversal name. If name[:7] = = ' Opcode_ ': # simply read the instructions to Try:opcode = Int (name[7:]) except Valueerror:raise  R (' opcodes must be numeric, invalid opcode:%s '% name[7:]) self.__opcodes.update ({opcode:getattr (self, ' opcode_%s '%  OpCode) def fetch (self): "" reads the instruction from memory according to the instruction pointer (program pointer), and then the instruction pointer adds 1. "" "self.ir = Self.get_memint (self.pc) self.pc +=1 def process (self):" "handles the current instruction, only one. By default, it is called in loop code (running loop), you can write your own code, call it in step mode, or use Time.sleep () to slow down execution.  It is also possible to call this function in the thread of the Tk/gtk/qt/curses interface. "" "self.fetch () opcode, data = Int (Math.floor (self.ir/100)), self.ir% Self.__opcodes[opcode] (data) def OpCo De_0 (self, data): "" Input directives """ Self.mem[data] = Self.get_input () def opcode_1 (self, data): "" "Clear accumulator instruction" "SELF.ACC = self.get_memint (data) de
  F opcode_2 (self, data): "" Addition Instruction "" "SELF.ACC + + + self.get_memint (data) def opcode_3 (self, data):" "" Test accumulator Content directive "" " If SELF.ACC < 0:self.pc = Data def opcode_4 (self, data): "" Offset Instruction "" "x,y = Int (Math.floor (DATA/10)), int ( Data% for I in range (0,x): SELF.ACC = (SELF.ACC *)% 10000 to I in range (0,y): SELF.ACC = Int (Math.floo  R (SELF.ACC/10) def opcode_5 (self, data): "" "Output Instruction" "" Self.stdout (Self.mem[data]) def opcode_6 (self, data): "" Storage directives "" "self.mem[data] = Self.pad (SELF.ACC) def opcode_7 (self, data):" "" "" "" "" "Self.acc-= Self.get_memint (da TA) def opcode_8 (self, data): "" Unconditional jump Instruction "" "SELF.PC = Data def opcode_9 (self, data):" "Stop/Reset Instruction" "" Self.reset
  () def run (self, Pc=none): "" "This code runs until the Halt/reset command is encountered." " If pc:self.pc = pc self.running = True while Self.running:seLf.process () print "output:\n%s"% Self.format_output () self.init_output () class cardiac (CPU, Memory, IO): Passif __n ame__ = = ' __main__ ': c = cardiac () c.read_deck (' Deck1.txt ') try:c.run () except:print "IR:%S\NPC:%s\noutput:%s
 \ n "% (c.ir, c.pc, C.format_output ()) Raise

You can find the Deck1.txt code used in this article from the developing Upwards:CARDIAC:The cardboard Computer, and I use the example from 1 to 10.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.