一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

腳本之家,腳本語言編程技術及教程分享平臺!
分類導航

Python|VBS|Ruby|Lua|perl|VBA|Golang|PowerShell|Erlang|autoit|Dos|bat|

服務器之家 - 腳本之家 - Python - 使用Python編寫一個模仿CPU工作的程序

使用Python編寫一個模仿CPU工作的程序

2020-06-08 10:00Python教程網 Python

這篇文章主要介紹了使用Python編寫一個模仿CPU工作的程序,包括簡單的內存和輸入輸出的實現,本文中的例子需要一定的Python編程基礎,是深入Python的實踐,需要的朋友可以參考下

今天早上早些時候,在我的Planet Python源中,我讀到了一篇有趣的文章"開發CARDIAC:紙板計算機(Developing upwards: CARDIAC: The Cardboard Computer)",它是關于名為Cardiac的紙板計算機的.我的一些追隨者和讀者應該知道,我有一個名為簡單CPU(simple-cpu)的項目,過去的數月我一直工作于此,并且已經發布了源代碼.我真的應該給這個項目提供一個合適的許可證,這樣,其他人可能更感興趣,并在他們自己的項目中使用.不管怎樣,但愿在這發布之后,我可以完成這件事.

在讀完了這篇文章以及它鏈接的頁面后,我受到了一些啟發,決定為它編寫我自己的模擬器,因為我有編寫字節碼引擎的經驗.我計劃著跟隨這篇文章繼續往前,先寫一篇關于匯編器的文章,接下來是關于編譯器的文章.這樣,通過這些文章,你基本上可以學到,如何用Python為Cardiac創建編譯工具集. 在簡單CPU(simple-cpu)項目中,我已經編寫了一個完整的可工作的匯編器.在內置的游戲中,已經有了可工作的編譯器的最初步驟.我也選擇Cardiac作為一個驗證機器是因為它絕對的簡單.不需要復雜的記憶,每個操作碼只接受單一的參數,所以它是絕好的學習工具.此外,所有的數據參數都是相同的,不需要檢測程序是需要一個寄存器,字符串或者還是內存地址.實際上,只有一個寄存器,累加器.因此,讓我們開始吧!我們將基于類來創建,這樣包含范圍.如果你想嘗試的話,你可以簡單通過子類來增加新的操作碼.首先,我們將集中于初始化例程.這個CPU非常簡單,所以我們只需要初始化下面的內容: CPU寄存器, 操作碼, 內存空間, 讀卡器/輸入, 和 打印/tty/輸出.
 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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 reset(self):
  """  This method 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 fancy method will automatically build a list of our 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 that looks  a bit messy. This keeps things clean and simple with a nice one-to-one  call-map.   """
  self.__opcodes = {}
  classes = [self.__class__] #: This holds all 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 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):
  """  This method resets the Cardiac's memory space to all blank strings, as per Cardiac specs.  """
  self.mem = ['' for i in range(0,100)]
  self.mem[0] = '001' #: The Cardiac bootstrap operation.
 def init_reader(self):
  """  This method initializes the input reader.  """
  self.reader = [] #: This variable can be accessed after initializing the class to provide input data.
 def init_output(self):
  """  This method initializes the output deck/paper/printer/teletype/etc...  """
  self.output = []

 

但愿我寫的注釋能讓你們看明白代碼的各部分功能.  也許你已經發現這段代碼處理指令集的方法(method)跟 simple-cpu 項目有所不同. 由于它能讓開發者根據自己的需求輕松的擴展類庫, 我打算在后續的項目中繼續使用這種處理方式. 隨著我對各部分功能原理的深入理解, 項目也在不斷的發展變化. 其實吧,  做這樣一個項目真的能讓人學到不少東西.  對于精通計算機的人來說 ,  CPU 的工作原理啦, 指令集是怎么處理的啦, 都不是問題啦 .  關鍵是, 能夠按照自己的想法去實現這樣一個 CPU 仿真器, 真的很好玩. 根據自己想象中的樣子, 親手打造出這樣一臺仿真器, 然后看著它屁顛屁顛的運行著, 那叫一個有成就感.


接下來, 我們講下工具函數(utility functions), 這些函數在很多地方都會用到, 而且允許在子類(subclasses)中重寫:
 
   

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def read_deck(self, fname):
 """  將指令讀到 reader 中.  """
 self.reader = [s.rstrip('\n') for s in open(fname, 'r').readlines()]
 self.reader.reverse()
def fetch(self):
 """  根據指令指針(program pointer) 從內存中讀出指令, 然后將指令指針加1.  """
 self.ir = int(self.mem[self.pc])
 self.pc +=1
def get_memint(self, data):
 """  由于我們是以字符串形式(*string* based)保存內存數據的, 要仿真 Cardiac, 就要將字符串轉化成整數. 如果是其他存儲形式的內存, 如 mmap, 可以根據需要重寫本函數.  """
 return int(self.mem[data])
def pad(self, data, length=3):
 """  本函數的功能是像 Cardiac 那樣, 在數字的前面補0.  """
 orig = int(data)
 padding = '0'*length
 data = '%s%s' % (padding, abs(data))
 if orig < 0:
  return '-'+data[-length:]
 return data[-length:]

本文后面我會另外給大家一段能結合 Mixin classes 使用的代碼, 靈活性(pluggable)更強些.  最后就剩下這個處理指令集的方法了:
 
   

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
def process(self):
  """  本函數只處理一條指令. 默認情況下, 從循環代碼(running loop)中調用, 你也可以自己寫代碼, 以單步調試的方式調用它, 或者使用 time.sleep() 降低執行的速度. 如果想用 TK/GTK/Qt/curses 做的前端界面(frontend), 在另外一個線程中操作, 也可以調用本函數.  """
  self.fetch()
  opcode, data = int(math.floor(self.ir / 100)), self.ir % 100
  self.__opcodes[opcode](data)
 def opcode_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):
  """ 加法指令 """
  self.acc += self.get_memint(data)
 def opcode_3(self, data):
  """ 測試累加器內容指令 """
  if self.acc < 0:
   self.pc = data
 def opcode_4(self, data):
  """ 位移指令 """
  x,y = int(math.floor(data / 10)), int(data % 10)
  for i in range(0,x):
   self.acc = (self.acc * 10) % 10000
  for i in range(0,y):
   self.acc = int(math.floor(self.acc / 10))
 def opcode_5(self, data):
  """ 輸出指令 """
  self.output.append(self.mem[data])
 def opcode_6(self, data):
  """ 存儲指令 """
  self.mem[data] = self.pad(self.acc)
 def opcode_7(self, data):
  """ 減法指令 """
  self.acc -= self.get_memint(data)
 def opcode_8(self, data):
  """ 無條件跳轉指令 """
  self.pc = data
 def opcode_9(self, data):
  """ 終止, 復位指令 """
  self.reset()
 def run(self, pc=None):
  """ 這段代碼一直執行到遇到 終止/復位 指令為止. """
  if pc:
   self.pc = pc
  self.running = True
  while self.running:
   self.process()
  print "Output:\n%s" % '\n'.join(self.output)
  self.init_output()if __name__ == '__main__':
 c = Cardiac()
 c.read_deck('deck1.txt')
 try:
  c.run()
 except:
  print "IR: %s\nPC: %s\nOutput: %s\n" % (c.ir, c.pc, '\n'.join(c.output))
  raise


這段是上面提到的, 能在 Mixin 中使用的代碼, 我重構過后, 代碼如下 :
 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
class Memory(object):
 """ 本類實現仿真器的虛擬內存空間的各種功能 """
 def init_mem(self):
  """  用空白字符串清除 Cardiac 系統內存中的所有數據  """
  self.mem = ['' for i in range(0,100)]
  self.mem[0] = '001' #: 啟動 Cardiac 系統.
 def get_memint(self, data):
  """  由于我們是以字符串形式(*string* based)保存內存數據的, 要仿真 Cardiac, 就要將字符串轉化成整數. 如果是其他存儲形式的內存, 如 mmap, 可以根據需要重寫本函數.  """
  return int(self.mem[data])
 def pad(self, data, length=3):
  """  在數字前面補0  """
  orig = int(data)
  padding = '0'*length
  data = '%s%s' % (padding, abs(data))
  if orig < 0:
   return '-'+data[-length:]
  return data[-length:]
class IO(object):
 """ 本類實現仿真器的 I/O 功能. To enable alternate methods of input and output, swap this. """
 def init_reader(self):
  """  初始化 reader.  """
  self.reader = [] #: 此變量在類初始化后, 可以用來讀取輸入的數據.
 def init_output(self):
  """  初始化諸如: deck/paper/printer/teletype/ 之類的輸出功能...  """
  self.output = []
 def read_deck(self, fname):
  """  將指令讀到 reader 中.  """
  self.reader = [s.rstrip('\n') for s in open(fname, 'r').readlines()]
  self.reader.reverse()
 def format_output(self):
  """  格式化虛擬 I/O 設備的輸出(output)  """
  return '\n'.join(self.output)
 def get_input(self):
  """  獲取 IO 的輸入(input), 也就是說用 reader 讀取數據, 代替原來的 raw_input() .  """
  try:
   return self.reader.pop()
  except IndexError:
   # 如果 reader 遇到文件結束標志(EOF) 就用 raw_input() 代替 reader.
   return raw_input('INP: ')[:3]
 def stdout(self, data):
  self.output.append(data)
class CPU(object):
 """ 本類模擬 cardiac CPU. """
 def __init__(self):
  self.init_cpu()
  self.reset()
  try:
   self.init_mem()
  except AttributeError:
   raise NotImplementedError('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):
  """  用默認值重置 CPU 的寄存器  """
  self.pc = 0 #: 指令指針
  self.ir = 0 #: 指令寄存器
  self.acc = 0 #: 累加器
  self.running = False #: 仿真器的運行狀態?
 def init_cpu(self):
  """  本函數自動在哈希表中創建指令集. 這樣我們就可以使用 case/select 方式調用指令, 同時保持代碼簡潔. 當然, 在 process() 中使用 getattr 然后用 try/except 捕捉異常也是可以的, 但是代碼看起來就沒那么簡潔了.  """
  self.__opcodes = {}
  classes = [self.__class__] #: 獲取全部類, 包含基類.
  while classes:
   cls = classes.pop() # 把堆棧中的類彈出來
   if cls.__bases__: # 判斷有沒有基類
    classes = classes + list(cls.__bases__)
   for name in dir(cls): # 遍歷名稱.
    if name[:7] == 'opcode_': # 只需要把指令讀出來即可     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 fetch(self):
  """  根據指令指針(program pointer) 從內存中讀取指令, 然后指令指針加 1.  """
  self.ir = self.get_memint(self.pc)
  self.pc +=1
 def process(self):
  """  處理當前指令, 只處理一條. 默認情況下是在循環代碼中調用(running loop), 也可以自己寫代碼, 以單步調試方式調用, 或者利用 time.sleep() 降低執行速度. 在 TK/GTK/Qt/curses 做的界面的線程中調用本函數也是可以的.  """
  self.fetch()
  opcode, data = int(math.floor(self.ir / 100)), self.ir % 100
  self.__opcodes[opcode](data)
 def opcode_0(self, data):
  """ 輸入指令 """
  self.mem[data] = self.get_input()
 def opcode_1(self, data):
  """ 清除累加器指令 """
  self.acc = self.get_memint(data)
 def opcode_2(self, data):
  """ 加法指令 """
  self.acc += self.get_memint(data)
 def opcode_3(self, data):
  """ 測試累加器內容指令 """
  if self.acc < 0:
   self.pc = data
 def opcode_4(self, data):
  """ 位移指令 """
  x,y = int(math.floor(data / 10)), int(data % 10)
  for i in range(0,x):
   self.acc = (self.acc * 10) % 10000
  for i in range(0,y):
   self.acc = int(math.floor(self.acc / 10))
 def opcode_5(self, data):
  """ 輸出指令 """
  self.stdout(self.mem[data])
 def opcode_6(self, data):
  """ 存儲指令 """
  self.mem[data] = self.pad(self.acc)
 def opcode_7(self, data):
  """ 減法指令 """
  self.acc -= self.get_memint(data)
 def opcode_8(self, data):
  """ 無條件跳轉指令 """
  self.pc = data
 def opcode_9(self, data):
  """ 停止/復位指令"""
  self.reset()
 def run(self, pc=None):
  """ 這段代碼會一直運行, 直到遇到 halt/reset 指令才停止. """
  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 __name__ == '__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

大家可以從 Developing Upwards: CARDIAC: The Cardboard Computer 中找到本文使用的 deck1.txt .

希望本文能啟發大家, 怎么去設計基于類的模塊, 插拔性強(pluggable)的 Paython 代碼, 以及如何開發 CPU 仿真器.   至于本文 CPU 用到的匯編編譯器(assembler) , 會在下一篇文章中教大家.

這段是上面提到的, 能在 Mixin 中使用的代碼, 我重構過后, 代碼如下 :

 

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
class Memory(object):
 """ 本類實現仿真器的虛擬內存空間的各種功能 """
 def init_mem(self):
  """  用空白字符串清除 Cardiac 系統內存中的所有數據  """
  self.mem = ['' for i in range(0,100)]
  self.mem[0] = '001' #: 啟動 Cardiac 系統.
 def get_memint(self, data):
  """  由于我們是以字符串形式(*string* based)保存內存數據的, 要仿真 Cardiac, 就要將字符串轉化成整數. 如果是其他存儲形式的內存, 如 mmap, 可以根據需要重寫本函數.  """
  return int(self.mem[data])
 def pad(self, data, length=3):
  """  在數字前面補0  """
  orig = int(data)
  padding = '0'*length
  data = '%s%s' % (padding, abs(data))
  if orig < 0:
   return '-'+data[-length:]
  return data[-length:]
class IO(object):
 """ 本類實現仿真器的 I/O 功能. To enable alternate methods of input and output, swap this. """
 def init_reader(self):
  """  初始化 reader.  """
  self.reader = [] #: 此變量在類初始化后, 可以用來讀取輸入的數據.
 def init_output(self):
  """  初始化諸如: deck/paper/printer/teletype/ 之類的輸出功能...  """
  self.output = []
 def read_deck(self, fname):
  """  將指令讀到 reader 中.  """
  self.reader = [s.rstrip('\n') for s in open(fname, 'r').readlines()]
  self.reader.reverse()
 def format_output(self):
  """  格式化虛擬 I/O 設備的輸出(output)  """
  return '\n'.join(self.output)
 def get_input(self):
  """  獲取 IO 的輸入(input), 也就是說用 reader 讀取數據, 代替原來的 raw_input() .  """
  try:
   return self.reader.pop()
  except IndexError:
   # 如果 reader 遇到文件結束標志(EOF) 就用 raw_input() 代替 reader.
   return raw_input('INP: ')[:3]
 def stdout(self, data):
  self.output.append(data)
class CPU(object):
 """ 本類模擬 cardiac CPU. """
 def __init__(self):
  self.init_cpu()
  self.reset()
  try:
   self.init_mem()
  except AttributeError:
   raise NotImplementedError('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):
  """  用默認值重置 CPU 的寄存器  """
  self.pc = 0 #: 指令指針
  self.ir = 0 #: 指令寄存器
  self.acc = 0 #: 累加器
  self.running = False #: 仿真器的運行狀態?
 def init_cpu(self):
  """  本函數自動在哈希表中創建指令集. 這樣我們就可以使用 case/select 方式調用指令, 同時保持代碼簡潔. 當然, 在 process() 中使用 getattr 然后用 try/except 捕捉異常也是可以的, 但是代碼看起來就沒那么簡潔了.  """
  self.__opcodes = {}
  classes = [self.__class__] #: 獲取全部類, 包含基類.
  while classes:
   cls = classes.pop() # 把堆棧中的類彈出來
   if cls.__bases__: # 判斷有沒有基類
    classes = classes + list(cls.__bases__)
   for name in dir(cls): # 遍歷名稱.
    if name[:7] == 'opcode_': # 只需要把指令讀出來即可     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 fetch(self):
  """  根據指令指針(program pointer) 從內存中讀取指令, 然后指令指針加 1.  """
  self.ir = self.get_memint(self.pc)
  self.pc +=1
 def process(self):
  """  處理當前指令, 只處理一條. 默認情況下是在循環代碼中調用(running loop), 也可以自己寫代碼, 以單步調試方式調用, 或者利用 time.sleep() 降低執行速度. 在 TK/GTK/Qt/curses 做的界面的線程中調用本函數也是可以的.  """
  self.fetch()
  opcode, data = int(math.floor(self.ir / 100)), self.ir % 100
  self.__opcodes[opcode](data)
 def opcode_0(self, data):
  """ 輸入指令 """
  self.mem[data] = self.get_input()
 def opcode_1(self, data):
  """ 清除累加器指令 """
  self.acc = self.get_memint(data)
 def opcode_2(self, data):
  """ 加法指令 """
  self.acc += self.get_memint(data)
 def opcode_3(self, data):
  """ 測試累加器內容指令 """
  if self.acc < 0:
   self.pc = data
 def opcode_4(self, data):
  """ 位移指令 """
  x,y = int(math.floor(data / 10)), int(data % 10)
  for i in range(0,x):
   self.acc = (self.acc * 10) % 10000
  for i in range(0,y):
   self.acc = int(math.floor(self.acc / 10))
 def opcode_5(self, data):
  """ 輸出指令 """
  self.stdout(self.mem[data])
 def opcode_6(self, data):
  """ 存儲指令 """
  self.mem[data] = self.pad(self.acc)
 def opcode_7(self, data):
  """ 減法指令 """
  self.acc -= self.get_memint(data)
 def opcode_8(self, data):
  """ 無條件跳轉指令 """
  self.pc = data
 def opcode_9(self, data):
  """ 停止/復位指令"""
  self.reset()
 def run(self, pc=None):
  """ 這段代碼會一直運行, 直到遇到 halt/reset 指令才停止. """
  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 __name__ == '__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

大家可以從Developing Upwards: CARDIAC: The Cardboard Computer 中找到本文使用的 deck1.txt 的代碼, 我用的是 從 1 計數到 10 的那個例子 .

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 成人影院观看 | 毛片亚洲毛片亚洲毛片 | 黄网在线观看免费网站台湾swag | 亚洲视频一 | 日本视频在线观看播放 | 国产伦码精品一区二区三区 | 女人和男人搞基 | 色在线亚洲视频www 色欲麻豆国产福利精品 | 久久青青草原综合伊人 | 手机在线免费观看高清 | 国产高清好大好夹受不了了 | 美妇在线| 国产精品亚洲专区一区 | 情人我吃糖果小说 | 色综合天天综合网国产人 | 午夜久久久久久亚洲国产精品 | 亚洲卡一卡2卡三卡4卡无卡三 | 国产午夜精品久久理论片小说 | 免费二级毛片免费完整视频 | 美女视频ww8888网网 | 色综合久久综精品 | 插美女b| 欧美日韩精品免费一区二区三区 | 久草草在线视视频 | bl双性受乖调教改造身体 | 亚洲成在人线视频 | 国产精品高清在线 | 超级乱淫伦短篇小说做车 | 青青青久在线视频免费观看 | 息与子中文字幕完整在线 | 亚洲 日韩 在线 国产 视频 | 亚洲无限观看 | 国产在线观看色 | 任你操视频在线观看 | 免费观看伦理片 | 天选之王漫画顾长歌免费阅读 | 日本美女动态图片 | 国产农村一级特黄α真人毛片 | 好吊色青青青国产综合在线观看 | 免费av在线看 | 99热成人精品热久久669 |