2012-04-05 9 views
8

Sono nuovo nel racket/schema, quindi ho deciso di imparare implementando un emulatore per il DCPU-16, un semplice processore a 16 bit.Qual è l'idioma di Racket/Scheme appropriato per questo codice?

La mia domanda è quindi: qual è un modo migliore per implementare la mia soluzione?

Questa è la soluzione che ho hackerato per controllare i registri della cpu. Il punto principale era consentire alle funzioni che modificano un registro di essere concatenate. Ad esempio:

; Increment value stored in register r-id 
; returns the updated register 
; 
; Reg - the register structure 
; (reg-inc Reg 'SP) 
(define (reg-inc reg r-id) 
    (reg-write reg r-id (+ (reg-read reg r-id) 1))) 

; chain them together 
;(reg-inc (reg-inc Reg 'SP) 
;   'PC) 
; 
; returns structure with both 'SP and 'PC incremented 

Il testo completo della soluzione del mio registro è riportato di seguito. My full program è anche su github. C'è così tanto ripetuto logica, so che ci deve essere un modo più semplice:

(struct registers (A B C X Y Z I J SP PC O Pa Pb Paadr Pbadr CLK) 
    #:transparent) 

(define Reg (registers 0 0 0 0 0 0 0 0 #x10000 0 0 0 0 0 0 0)) 

(define (reg-name n) 
    (case n 
    [(0) 'A] 
    [(1) 'B] 
    [(2) 'C] 
    [(3) 'X] 
    [(4) 'Y] 
    [(5) 'Z] 
    [(6) 'I] 
    [(7) 'J] 
    [(8) 'SP] 
    [(9) 'PC] 
    [(10) 'O] 
    [(11) 'Pa] 
    [(12) 'Pb] 
    [(13) 'Paadr] 
    [(14) 'Pbadr] 
    [(15) 'CLK] 
    [else (error "Invalid register")])) 

(define (reg-id s) 
    (cond 
    [(eq? 'A s) 0] 
    [(eq? 'B s) 1] 
    [(eq? 'C s) 2] 
    [(eq? 'X s) 3] 
    [(eq? 'Y s) 4] 
    [(eq? 'Z s) 5] 
    [(eq? 'I s) 6] 
    [(eq? 'J s) 7] 
    [(eq? 'SP s) 8] 
    [(eq? 'PC s) 9] 
    [(eq? 'O s) 10] 
    [(eq? 'Pa s) 11] 
    [(eq? 'Pb s) 12] 
    [(eq? 'Paadr s) 13] 
    [(eq? 'Pbadr s) 14] 
    [(eq? 'CLK s) 15])) 

(define (reg-read reg r) 
    (if (symbol? r) 
     (reg-read reg (reg-id r)) 
     (case r 
     [(0) (registers-A reg)] 
     [(1) (registers-B reg)] 
     [(2) (registers-C reg)] 
     [(3) (registers-X reg)] 
     [(4) (registers-Y reg)] 
     [(5) (registers-Z reg)] 
     [(6) (registers-I reg)] 
     [(7) (registers-J reg)] 
     [(8) (registers-SP reg)] 
     [(9) (registers-PC reg)] 
     [(10) (registers-O reg)] 
     [(11) (registers-Pa reg)] 
     [(12) (registers-Pb reg)] 
     [(13) (registers-Paadr reg)] 
     [(14) (registers-Pbadr reg)] 
     [(15) (registers-CLK reg)] 
     [else (error "Invalid register")]))) 

(define (reg-write reg r val) 
    (if (symbol? r) 
     (reg-write reg (reg-id r) val) 
     (let ([mask-val (bitwise-and val #xffff)]) 
     (case r 
      [(0) (struct-copy registers reg [A mask-val])] 
      [(1) (struct-copy registers reg [B mask-val])] 
      [(2) (struct-copy registers reg [C mask-val])] 
      [(3) (struct-copy registers reg [X mask-val])] 
      [(4) (struct-copy registers reg [Y mask-val])] 
      [(5) (struct-copy registers reg [Z mask-val])] 
      [(6) (struct-copy registers reg [I mask-val])] 
      [(7) (struct-copy registers reg [J mask-val])] 
      [(8) (struct-copy registers reg [SP mask-val])] 
      [(9) (struct-copy registers reg [PC mask-val])] 
      [(10) (struct-copy registers reg [O mask-val])] 
      [(11) (struct-copy registers reg [Pa mask-val])] 
      [(12) (struct-copy registers reg [Pb mask-val])] 
      [(13) (struct-copy registers reg [Paadr mask-val])] 
      [(14) (struct-copy registers reg [Pbadr mask-val])] 
      [(15) (struct-copy registers reg [CLK mask-val])] 
      [else (error "Invalid register")])))) 

Aggiornamento:

Grazie alla sugestions di oobviat ho refactoring utilizzando gli elenchi. L'unica parte difficile era aggiornare un valore nell'elenco. Ho scritto una procedura per la mappa che aggiornare il registro desiderato e lasciare gli altri con il loro valore originale:

;; a-list of registers and initial values 
(define (build-reg) 
    '((A . 0) (B . 0)  (C . 0)  (X . 0) 
    (Y . 0) (Z . 0)  (I . 0)  (J . 0) 
    (SP . 0) (PC . 0) (O . 0)  (Pa . 0) 
    (Pb . 0) (Paadr . 0) (Pbadr . 0) (CLK . 0))) 

(define *REF-REG* (build-reg)) ; used to determine structure 

(define (reg-name n) 
    (if (symbol? n) 
     n 
     (car (list-ref *REF-REG* n)))) 

(define (reg-id s) 
    (- (length *REF-REG*) 
    (length (memf (lambda (arg) 
        (eq? s (car arg))) 
        *REF-REG*)))) 

(define (reg-write reg r val) 
    (let ([r-name (reg-name r)]) 
    (define (reg-write-helper entry) 
     (if (eq? r-name 
       (car entry)) 
      (cons r-name val) 
      entry)) 
    (map reg-write-helper reg))) 

(define (reg-read reg r) 
    (cdr (assoc (reg-name r) reg))) 
+0

Le strutture non hanno procedure di introspezione? Sono sicuro che lo faccia. – leppie

+0

@leppie, le strutture non hanno i loro nomi nelle informazioni di introspezione. –

risposta

2

Questo non è stato scritto in Racket, quindi potrebbe non funzionare per voi come è .. se si getta errori prova a specificare il tipo di codice R5RS nella parte superiore del file. Per semplicità, farei qualcosa di simile usando un a-list piuttosto che struct.

;; a-list of registers and initial values 
(define *reg* 
    '((A . 0) (B . 0) (C . 0) (X . 0) (Y . 0) (Z . 0) 
    (I . 0) (J . 0) (SP . #X10000) (PC . 0) (O . 0) 
    (Pa . 0) (Pb . 0) (Paadr . 0) (Pbadr . 0) (CLK . 0))) 

(define (reg-write register val) 
    (set-cdr! (assoc register *reg*) val) ;write new value to register 
    val) ; return newly written value 

(define (reg-read register) 
    (cdr (assoc register *reg*))) 

(define (reg-inc register) 
    (reg-write register (+ 1 (reg-read register)))) 

;; to do many operations 
;; input: a list of registers 
;; EX: '(a b x) 
(define (do-incs registers) 
    (if (null? registers) 
     'done  ; return something when the incs are done 
     (begin  ; lets you evaluate multiple expressions since `if` doesn't   
     (reg-inc (car registers)) 
     (do-incs (cdr registers))))) 

sto supponendo che Racket è dotato di un simile assoc che restituisce la lista corretta dalla A-list. Inoltre, notare che *reg* è definito come variabile globale in questo caso, in modo che possiamo definirlo solo una volta, quindi utilizzare set-cdr! per scrivere valori su di esso.

infine, questo potrebbe fare cose strane al tuo registro SP. Il mio schema lo vede come 65536 .. se non è giusto, potresti dover aggiungere uno if a reg-write e reg-read per assicurarti di ottenere i valori corretti.

<EDIT> Quindi, ho letto un po 'sulle procedure Racket e questo codice quasi certamente non verrà eseguito nel normale Racket perché apparentemente hanno coppie sia mutabili che non mutabili. Le modifiche che dovrai eseguire se vuoi eseguire questa operazione in Racket e non in R5RS sono le seguenti:

Piuttosto che usare solo un elenco quotato, probabilmente dovrai compilare l'elenco dei registri con i costruttori di elenchi/coppie modificabili. (define *reg* (mlist (mcons 'A 0) (mcons 'B 0) ...).

Invece di utilizzare set-cdr! la versione Racket è set-mcdr! e funziona solo su coppie mutabili. </EDIT>

+0

[Lo fa.] (Http://docs.racket-lang.org/reference/pairs.html# (def._ ((lib._racket/private/list..rkt) ._assoc))) – Taymon

+0

Sì. immaginavo fosse un'ipotesi abbastanza sicura. – oobivat

+0

Volevo scrivere questo senza l'uso del set, per costringermi a pensare in modo diverso. La tua soluzione di lista ha ispirato la mia nuova implementazione. –

Problemi correlati