Pdp 8 Emulator in python, terza parte.

Introduzione

In questo articolo analizzeremo i metodi statici che sono presenti nella classe pdp8. Prima però vorrei parlare di qualcosa di più generale: se avete seguito i commenti del precedente post, potete aver notato come alcune soluzioni, se pur funzionanti, possano essere più facili o difficili, oppure meno performanti di altre.

Quando iniziate un progetto, dovete tener bene in mente dei punti fondamentali :

  • Fare un’attenta analisi del problema da risolvere (problem setting)
  • Cercare di modellizzare il problema (visualizzandolo logicamente, magari con strutture a blocchi. Questa parte dipende molto dalla natura del problema)
  • Focalizzare i reali obbiettivi del problema (eliminando eventuali dettagli inutili)

Pensate poi che le specifiche di un progetto, qualsiasi esso sia, presenteranno delle problematiche scomponibili in sottoproblemi da risolvere sempre tenendo a mente gli stessi punti.

Fatto questo potete avere delle varie opzioni di risoluzione (problem solving) delle quali stavamo parlando nei commenti del precedente articolo. Se iniziata una soluzione si nota che la cosa è troppo complicata e prolissa, sarà opportuno ricominciare da capo (se necessario, altrimenti, se si procede per step, basta tornare a quello immediatamente precedente).

Ottimizzare il lavoro per rendere il progetto maneggevole è tutto un altro paio di maniche e dipende molto dalle scelte iniziali fatte. Per esempio in questo caso, riconvertire il core per l’utilizzo degli interi comporterebbe una riscrittura quasi completa del codice, ma in termini di prestazioni e di leggibilità ne guadagnerebbe molto.

Anche la soluzione che utilizzare una classe con comportamenti prestabiliti inerenti alle esigenze del progetto non può essere adottata se non riscrivendo gran parte del codice. Queste soluzioni, purtroppo, sono venute fuori dopo la stesura di una versione “completa” del programma e per questo difficili da adattare.

Per non incorrere in queste problematiche, come già detto in precedenza, occorre FOCALIZZARE gli obbiettivi. Nel mio caso, quando ho iniziato a scrivere questo programma, erano due:

  • Portabilità : programma multipiattaforma per gli OS più diffusi al momento
  • Emulazione corretta del funzionamento del calcolatore didattico pdp8

Raggiunti questi obbiettivi, non mi sono preoccupato moltissimo della forma e delle ottimizzazioni, ma bensì delle cose necessarie all’uso didattico. Ora che le ultime modifiche al programma sono state apportate, non ci dovrebbero più essere cambiamenti per quanto riguarda il funzionamento, se non per migliorare l’esecuzione (velocità) e correggere qualche bug.

Le questioni appena sollevate sono di particolare importanza per qualsiasi programma, videogioco o problema che dovete risolvere ed implementare. Come mio primo progetto, sono molto contento di condividere con voi queste idee perché credo siano basilari per la formazione di un qualsiasi programmatore.

Ora torniamo al pdp8 ed ai suoi metodi statici :

Codice

    @staticmethod
    def purge(lista):
        """
        Rimuove dalla lista caratteri indesiderati
        """
        vuoti = lista.count('')
        spazi = lista.count(' ')
        tab = lista.count('\t')
        newline = lista.count('\n')

        for x in range(0,vuoti):
            lista.remove('')
        for x in range(0,spazi):
            lista.remove(' ')
        for x in range(0,tab):
            lista.remove('\t')
        for x in range(0,newline):
            lista.remove('\n')
        for x in range(0,len(lista)):
            lista[x] = lista[x].lstrip()

    @staticmethod
    def purgestr(stringa):
        """
        Rimuove dalla stringa caratteri indesiderati
        """
        stringa = stringa.strip('\t')
        stringa = stringa.strip(' ')
        stringa = stringa.strip('')
        stringa = stringa.strip('\n')
        stringa = stringa.strip('\r')
        return stringa

    @staticmethod
    def range(i):
        """
        Converte i nell'intervallo di rappresentabilit? degli interi.
        """
        temp = i%65536
        if i>32767:
            return (temp-65536)
        else:
            return temp

    @staticmethod
    def elws(opcode):
        """
        Elimina gli spazi bianchi alla fine dei comandi e
        ritorna la stringa corretta
        """
        temp = ''
        whites = True
        for x in range(0,len(opcode)):
            if whites is True and opcode[-1-x]==' ':
                pass
            else:
                temp += opcode[-1-x]
                whites = False
        return temp[::-1]

    @staticmethod
    def binario(x):
        """
        Coverte un numero intero in una stringa binaria e
        ritorna una stringa binaria senza '0b' in testa
        """
        if x<0:
            temp = bin((2**16)+x)
        else:
            temp = bin(x)
        return temp[2:]

    @staticmethod
    def extendpositive(x,fine = 16):
        """
        Estende il campo dei numeri positivi se rappresentati con meno di 16
        cifre
        """
        if len(x)            num = fine-len(x)
            temp = ''
            for y in range(0,num):
                temp+= '0'
            x = temp+x
        return x

    @staticmethod
    def extendlabel(x):
        """
        Estende il campo dei degli indirizzi di memoria dei label
        """
        if len(x)<12:
            num = 12-len(x)
            temp = ''
            for y in range(0,num):
                temp+= '0'
            x = temp+x
        return x

    @staticmethod
    def esadecimale(x):
        """
        Converte un numero intero in una stringa esadecimale e
        ritorna una stringa esadecimale senza '0x' in testa
        """
        temp = hex(x)
        return temp[2:]

    @staticmethod
    def hex2dec(x):
        """
        Coverte un numero esadecimale in un numero intero
        """
        return int(x,16)

    @staticmethod
    def bin2dec(x):
        """
        Converte un numero binario in un numero intero
        """
        return int(x,2)

    @staticmethod
    def strand(a,b):
        """
        Ritorna l'and tra i caratteri binari a e b
        """
        if a == '1' and b == '1':
            return ('1')
        else :
            return ('0')

    @staticmethod
    def dec2char(x):
        """
        Ritorna il carattere ascii del numero decimale passato
        """
        return chr(x)

    @staticmethod
    def char2dec(x):
        """
        Ritorna il numero decimale del carattere ascii passato
        """
        return ord(x)

Analisi

Queste funzioni non hanno niente di speciale da spiegare, se non il motivo per il quale sono state create:

  • La prima funzione ci consente di eliminare caratteri superflui da una lista. In particolare viene utilizzata dopo una split per cancellare quello che non serve, come spaziature iniziali, caratteri di fine carrello ecc… Da notare che si opera direttamente sulla lista, quindi non si restituisce nulla.
  • La seconda funzione effettua le stesse rimozioni della prima ma su una stringa generica. Infatti restituisce la stringa stessa una volta ripulita. Queste due funzioni sono state molto utili per quanto riguarda la lettura (o parsing) del file assembly che viene caricato. Magari sono un pò eccessive come soluzioni, quindi sarà un piacere leggere qualche valida alternativa, se qualcuno ne ha.
  • Tra le funzioni di pulizia delle stringhe rientra anche elws, che elimina esclusivamente gli spazi bianchi dalla fine della stringa e la ritorna.
  • La funzione range converte l’intero passato in complemento a 2 su 16 bit. Quindi l’intervallo di rappresentabilità va da -32768 a 32767.
  • Binario ed esadecimale ritornano una stringa in base due o sedici senza 0b o 0x in testa, che non sono necessari nella visualizzazione dei dati (si elimina anche l’eventuale segno negativo). In binario si distinguono anche valori negativi, mentre per esadecimale non è necessario in quanto viene utilizzato solo per gli indirizzi, che sono solo positivi.
  • Extendpositive e extendlabel hanno lo stesso compito, estendere la stringa su 16 e 12 cifre rispettivamente. Come potete notare, extendlabel è praticamente inutile, visto che, quando si estendono i positivi, si può indicare il numero di bit (o cifre) che si dovranno avere in extendpositive, quindi si può anche passare 12. Nelle prossime versioni eliminerò questo metodo che era stato utilizzato solo ad inizio progetto perché, nel parsing del file assembly, i labels hanno la priorità rispetto alla conversione del codice, in quanto rappresentano veri e propri indirizzi di memeria e quindi sono stati affrontati per primi.
  • strand è la funzione che esercita l’AND tra due caratteri, necessaria perché fa parte delle istruzioni della ALU. ADD invece ha bisogno solamente che le stringhe vengano riconvertite in interi per operare. Questi meccanismi sono necessari per la scelta iniziale da me fatta, ovvero di utilizzare le stringhe. Come potete vedere però, non ci sono altre istruzioni da implementare perché il processore è molto essenziale sotto questo punto di vista.

Conclusioni

Con questo abbiamo visualizzato i metodi che contornano il pdp8. Essi sono indispensabili per il funzionamento della macchina che poggia le sue basi sulle stringhe.

Tornando al discorso delle possibili implementazioni, potete pensare che, se si utilizzavano solo gli interi, queste funzioni erano comunque necessarie per la rappresentazione della macchina (nell’interfaccia utente). Magari se si organizzavano degli oggetti per rappresentare i registri, questi metodi potevano fare parte dei registri stessi per essere utilizzati quando necessario (si poteva creare anche un “dato generale” con questi metodi).

Nei prossimi articoli continuerò ad illustrarvi il codice fino ad arrivare all’interfaccia grafica. Spero che, come nei primi post, i vostri commenti portino nuove e brillanti idee per migliorare questo programma.

PS: Ho aggiunto la possibilità di inserire dei break all’interno del codice ed il controllo sintattico del file assembly. Inoltre la macchina ora non esegue più secondo i cicli (fetch …), ma l’esecuzione è scandita dal clock (a quattro tempi in questo caso) e quindi si simula l’esecuzione di ogni singola micro-istruzione. Quest’ultima modifica, che è la più corposa, ha esclusivamenti fini didattici e non sarebbe necessaria per un semplice simulatore/emulatore; però non badate troppo alle istruzioni di input ed output perché sono state simulate correttamente solo nel funzionamento e non nelle micro-istruzioni.

Press ESC to close