Pdp 8 Emulator in python, quinta parte.

Introduzione

In questa parte vedremo con svolge l’esecuzione il calcolatore. Dopo l’introduzione delle micro-istruzioni, l’esecuzione non è più stata legata solo alle variabili di controllo, ma anche da una terza che rappresenta il tempo di clock. Infatti l’esecuzione temporale del programma (per capirci, l’iterfaccia utente) è completamente scollegata da quella interna.

Il pdp8 è legato fondamentalmente da un clock a 4 tempi e questo condizionerà tutto il codice che si vedrà in questa parte. Non è una scelta personale, infatti il pdp8 eseguiva una microi-istruzione (alcune possono essere eseguite in “contemporane”, quindi possono esserci più micro-istruzioni per clock) per ciclo temporale (clock) e quindi il “tempo” deve scandire le varie parti che vengono man mano eseguite. Per comodità, non riporterò per filo e per segno quello che accade con ogni istruzione, ma mi concentrerò sull’analisi della parte più importante e dell’organizzazione.

C’è da puntualizzare che il tempo di chiamata (cioè con quanta frequenza vengono richiamate le funzioni di esecuzione) è dato sempre dalla GUI, quindi il “clock” interno del programma è solamente una semplice variabile per determinare certe condizioni (interne) e non corrisponde ad un intervallo temporale determinato (come invece era per il pdp8 vero e proprio).

Codice

1def step(self,root=None,codice=None):
2 
3        """
4 
5        Uno step equivale all'esecuzione dei seguenti cicli:
6 
7            - ciclo di fetch
8 
9            - ciclo di indirizzamento indiretto (se necessario)
10 
11            - ciclo di esecuzione
12 
13            - ciclo di interruzione (se necessario)
14 
15        """
16 
17        try:
18 
19            self.breaks = False
20 
21            if not self.F and not self.R:
22 
23                self.fetch()
24 
25            elif not self.F and self.R:
26 
27                self.indind()
28 
29            elif self.F and not self.R:
30 
31                self.execute()
32 
33            elif self.F and self.R:
34 
35                self.interrupt(root)
36 
37            if self.breaks:
38 
39                self.S = False
40 
41            if self.tempo <3:
42 
43                self.tempo += 1
44 
45            else:
46 
47                self.tempo = 0
48 
49        except Exception:
50 
51            showwarning("Errore", "Controllare il codice assembly!", parent = codice.master)
52 
53            self.S = False
54 
55def interrupt(self, root):
56 
57        """
58 
59        Input da tastiera ed output su video di caratteri ASCII (da 0 a 127)
60 
61        """
62 
63        if self.MBR == self.Opcodes['INP'] and self.Interrupt: # INP
64 
65            if self.tempo is 0:
66 
67                self.microistruzioni += '\n'
68 
69                self.microistruzioni += "INP --- \n"
70 
71                self.microistruzioni += "NOP \n"
72 
73            elif self.tempo is 1:
74 
75                self.microistruzioni += "NOP \n"
76 
77            elif self.tempo is 2:
78 
79                self.microistruzioni += "NOP \n"
80 
81            elif self.tempo is 3:
82 
83                temp = ''
84 
85                to = 128
86 
87                while len(temp) != 1 or to > 127 and temp == None:
88 
89                    if root != None:
90 
91                        temp = tkSimpleDialog.askstring("Inserisci","CHAR", parent = root)
92 
93                    else:
94 
95                        temp = str(input("Inserisci un carattere"))
96 
97                    if len(temp) == 1:
98 
99                        to = ord(temp)
100 
101                    elif temp == '':
102 
103                        to = ord(chr(13)) ## Ritorno carrello
104 
105                        break
106 
107                self.AC = self.binario(to).zfill(16)
108 
109                self.F = False
110 
111                self.R = False
112 
113                self.microistruzioni += "F<- 0 , R <- 0 \n"
114 
115                self.microistruzioni += "----- \n"
116 
117        elif self.MBR == self.Opcodes['OUT'] and self.Interrupt: # OUT
118 
119            if self.tempo is 0:
120 
121                self.microistruzioni += '\n'
122 
123                self.microistruzioni += "OUT --- \n"
124 
125                self.microistruzioni += "NOP \n"
126 
127            elif self.tempo is 1:
128 
129                self.microistruzioni += "NOP \n"
130 
131            elif self.tempo is 2:
132 
133                self.microistruzioni += "NOP \n"
134 
135            elif self.tempo is 3:
136 
137                temp = chr(int(self.AC,2))
138 
139                if temp == '\r':
140 
141                    temp = '\n'
142 
143                self.inout += temp
144 
145                self.F = False
146 
147                self.R = False
148 
149                self.microistruzioni += "F<- 0 , R <- 0 \n"
150 
151                self.microistruzioni += "----- \n"
152 
153        elif self.MBR == self.Opcodes['ION']:
154 
155            if self.tempo is 0:
156 
157                self.microistruzioni += '\n'
158 
159                self.microistruzioni += "ION --- \n"
160 
161                self.microistruzioni += "NOP \n"
162 
163            elif self.tempo is 1:
164 
165                self.microistruzioni += "NOP \n"
166 
167            elif self.tempo is 2:
168 
169                self.microistruzioni += "NOP \n"
170 
171            elif self.tempo is 3:
172 
173                self.Interrupt = True
174 
175                self.F = False
176 
177                self.R = False
178 
179                self.microistruzioni += "F<- 0 , R <- 0 \n"
180 
181                self.microistruzioni += "----- \n"
182 
183        elif self.MBR == self.Opcodes['IOF']:
184 
185            if self.tempo is 0:
186 
187                self.microistruzioni += '\n'
188 
189                self.microistruzioni += "IOF --- \n"
190 
191                self.microistruzioni += "NOP \n"
192 
193            elif self.tempo is 1:
194 
195                self.microistruzioni += "NOP \n"
196 
197            elif self.tempo is 2:
198 
199                self.microistruzioni += "NOP \n"
200 
201            elif self.tempo is 3:
202 
203                self.Interrupt = False
204 
205                self.F = False
206 
207                self.R = False
208 
209                self.microistruzioni += "F<- 0 , R <- 0 \n"
210 
211                self.microistruzioni += "----- \n"
212 
213        else:
214 
215            self.F = False
216 
217            self.R = False
218 
219def execute(self):
220 
221        """
222 
223        Esecuzione dell'operazione. Se non presente, F torna a 0 per
224 
225        eseguire il fetch dell'istruzione successiva.
226 
227        """
228 
229        if self.I == '1' and self.OPR == '111':
230 
231            self.R = True
232 
233        elif self.I == '0' and self.OPR == '111':
234 
235            if self.MBR == self.Opcodes['HLT']:
236 
237                self._HLT()
238 
239            elif self.MBR == self.Opcodes['CLA']:
240 
241                self._CLA()
242 
243            elif self.MBR == self.Opcodes['CLE']:
244 
245                self._CLE()
246 
247            elif self.MBR == self.Opcodes['CMA']:
248 
249                self._CMA()
250 
251            elif self.MBR == self.Opcodes['CME']:
252 
253                self._CME()
254 
255            elif self.MBR == self.Opcodes['CIR']:
256 
257                self._CIR()
258 
259            elif self.MBR == self.Opcodes['CIL']:
260 
261                self._CIL()
262 
263            elif self.MBR == self.Opcodes['INC']:
264 
265                self._INC()
266 
267            elif self.MBR == self.Opcodes['SPA']:
268 
269                self._SPA()
270 
271            elif self.MBR == self.Opcodes['SNA']:
272 
273                self._SNA()
274 
275            elif self.MBR == self.Opcodes['SZA']:
276 
277                self._SZA()
278 
279            elif self.MBR == self.Opcodes['SZE']:
280 
281                self._SZE()
282 
283        else:
284 
285            if self.OPR == self.Opcodes['AND']:
286 
287                self._AND()
288 
289            elif self.OPR == self.Opcodes['ADD']:
290 
291                self._ADD()
292 
293            elif self.OPR == self.Opcodes['LDA']:
294 
295                self._LDA()
296 
297            elif self.OPR == self.Opcodes['STA']:
298 
299                self._STA()
300 
301            elif self.OPR == self.Opcodes['BUN']:
302 
303                self._BUN()
304 
305            elif self.OPR == self.Opcodes['BSA']:
306 
307                self._BSA()
308 
309            elif self.OPR == self.Opcodes['ISZ']:
310 
311                self._ISZ()
312 
313            else:
314 
315                self.F = False
316 
317def indind(self):
318 
319        """
320 
321        Ciclo di indirizzamento indiretto
322 
323        """
324 
325        if self.tempo is 0:
326 
327            self.MAR = self.MBR[4:]
328 
329            self.microistruzioni += '\n'
330 
331            self.microistruzioni += "Indirizzamento indiretto : --- \n"
332 
333            self.microistruzioni += "MAR <- MBR(AD) \n"
334 
335        elif self.tempo is 1:
336 
337            self.MBR = self.RAM[self.MAR]
338 
339            self.microistruzioni += "MBR <- M \n"
340 
341        elif self.tempo is 2:
342 
343            self.microistruzioni += "NOP \n"
344 
345        elif self.tempo is 3:
346 
347            self.F = True
348 
349            self.R = False
350 
351            self.microistruzioni += "F <- 1 , R <- 0 \n"
352 
353            self.microistruzioni += "----- \n"
354 
355def fetch(self):
356 
357        """
358 
359        Ciclo di fetch
360 
361        """
362 
363        if self.tempo is 0:
364 
365            if self.BREAKP[self.PC] == True:
366 
367                self.breaks = True
368 
369            self.MAR = self.PC
370 
371            line = 1
372 
373            for x in sorted(self.RAM):
374 
375                if x == self.PC:
376 
377                    self.nextistr = line
378 
379                    break
380 
381                line += 1
382 
383            if int(self.PC,2) == self.START:
384 
385                self.previstr = self.nextistr-1
386 
387            self.microistruzioni += '\n'
388 
389            self.microistruzioni += "++++++++++++++++++++++++++++++++++++++++++++++++++++++\n"
390 
391            self.microistruzioni += "Fetch :  \n"
392 
393            self.microistruzioni += "MAR <- PC \n"
394 
395        elif self.tempo is 1:
396 
397            temp = int(self.PC,2)
398 
399            temp += 1
400 
401            self.PC = self.binario(temp).zfill(12)
402 
403            self.MBR = self.RAM[self.MAR]
404 
405            self.microistruzioni += "MBR <- M , PC <- PC+1 \n"
406 
407        elif self.tempo is 2:
408 
409            self.OPR = self.MBR[1:4]
410 
411            self.I = self.MBR[0]
412 
413            self.microistruzioni += "OPR <- MBR(OP) , I <- MBR(I) \n"
414 
415        elif self.tempo is 3:
416 
417            if self.I == '1' and self.OPR != '111':
418 
419                self.R = True
420 
421                self.microistruzioni += "R <- 1 \n"
422 
423            else :
424 
425                self.F = True
426 
427                self.microistruzioni += "F <- 1 \n"

– def step() :

Questa funzione determina tutta l’esecuzione del programma. Step incrementa la variabile “tempo” che rappresenta il clock interno del processore e richiama i vari cicli in base alle variabili di controllo F ed R. Si occuperò anche di bloccare l’esecuzione se presente un break e di uscire dall’esecuzione si si presentano degli errori. In questo caso si esce per qualsiasi errore che si presenta, ma in precedenza questo except veniva utilizzato per debuggare il programma e controllare la sua esecuzione.

Come potete immaginare, questa sarà la funzione che verrà richiamata dalla gui per eseguire l’emulazione. Questo ci permette di spostare l’attenzione al solo file pdp8 in caso di malfunzionamenti e di escludere eventuali problematiche derivate dall’interfaccia, visto che l’esecuzione è completamente staccata da quest’ultima.

Le altre funzioni (fetch, indind (indirizzamento indiretto), execute, interrupt) non dovrebbero richiedere particolari spiegazioni, la loro struttura è lineare e rispechia tutte le operazioni che effettuerebbe il calcolatore. L’unica cosa in più è che devono aggiornare le microistruzioni (una stringa) per stampare a video le operazioni eseguite.

Anche se non sono presenti le altre istruzioni, potete dedurre facilmente che queste saranno legate alla variabile tempo (come in fetch per esempio, oppure l’istruzione INP). Le uniche operazioni che possiamo calcolare esternamente con la GUI (e che vedremo nei prossimi articoli), sono i vari tipi di step disponibili. Uno step equivale all’esecuzione completa di un’istruzione, quindi di una riga di codice; un mini step comprende solo un ciclo dei quattro presenti, mentre un micro-step esegue solo una microi-istruzione (corrisponde ad un clock, quindi al variare di una singola unità di tempo). Queste operazioni sono facilmente controllabili dall’esterno, visto che toccano solamente la variabile temporale.

Prima di concludere vorrei fare presente che il ciclo di interrupt non è proprio veritiero, quindi non prendete per buono quello che accade a livello di micro-istruzioni, visto che non simulo nè le periferiche nè l’iterfaccia di collegamento (anche se per il pdp8 sarebbe proprio il processore che si occupa di colloquiare con le periferiche esterne). Le altre istruzioni che trovate nei file sorgente invece sono effettivamente quelle che eseguiva l’originale calcolatore (per quanto riguarda le micro-istruzioni per clock), naturalmente adattate con costrutti python tenendo sempre presente di gestire delle stringhe.

Conclusioni

Credo che questa parte sia molto semplice da comprendere, visto che non ci possono essere molte varianti, ma si deve solamente cercare di simulare l’esecuzione vera e propria del calcolatore. Mi dovete scusare se non ho speso molte parole per analizzare il codice, ma non ci sono scelte particolari od operazioni complesse che si eseguono e quindi preferisco ricevere qualche commento su parti magari più oscure per spiegarle (sempre che ce ne siano) che descrivere ancora più minuziosamente operazioni semplici come quelle che avete letto.

Nel prossimo articolo cominceremo a vedere l’interfaccia grafica, così da poter capire meglio come quest’ultima interagisce con il programma vero e proprio. Spero che sia utile vedere il funzionamento di tkinter, perché oltre ad essere integrata in python, rispecchia logicamente il funzionamento delle librerie più ricche come GTK e QT.

Press ESC to close