2012-04-05 12 views
8

Jestem nowy w rakiecie/schemacie, więc postanowiłem dowiedzieć się poprzez implementację emulatora dla DCPU-16, prostego procesora 16-bitowego.Jaki jest odpowiedni idiom Rakieta/Schemat dla tego kodu?

Moje pytanie brzmi: jaki jest lepszy sposób wdrożenia mojego rozwiązania?

To jest rozwiązanie, które zhakowałem, aby kontrolować rejestry procesora. Głównym celem było umożliwienie funkcji, które modyfikują rejestr, aby były połączone razem. Na przykład:

; 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 

Pełny tekst mojego rozwiązania rejestru znajduje się poniżej. My full program jest również na github. Jest tyle powtarzane logiki, wiem, że musi być łatwiejszy sposób:

(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")])))) 

Aktualizacja:

Dzięki za sugestie oobviat Mam refactored za pomocą list. Jedyną trudną częścią było zaktualizowanie wartości na liście. Napisałem procedurę mapie, które zaktualizować żądany rejestr i pozostawić innym z ich pierwotnej wartości:

;; 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

Czy struktura nie ma procedur introspekcji? Jestem pewien, że tak. – leppie

+0

@leppie, struktury nie mają swoich nazw w informacjach introspekcji. –

Odpowiedz

2

ta nie została napisana w rakieta, więc to nie może działać dla Ciebie, jak to .. jeśli rzuca błędy spróbuj podać typ kodu R5RS u góry pliku. Dla uproszczenia chciałbym zrobić coś takiego przy użyciu listy a nie struktur.

;; 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))))) 

Jestem zakładając, że rakieta posiada wbudowany jak assoc zwracającej właściwą listę z a-listy. Zauważ też, że *reg* jest w tym przypadku zdefiniowana jako zmienna globalna, abyśmy mogli zdefiniować ją raz, a następnie użyć set-cdr!, aby zapisać do niej wartości.

w końcu może to zrobić dziwne rzeczy do rejestru SP. Mój schemat widzi to jako 65536 .. jeśli to nie jest właściwe, być może będziesz musiał dodać if do reg-write i reg-read, aby upewnić się, że masz tam odpowiednie wartości.

<EDIT> Więc przeczytać trochę o procedurach rakieta, a ten kod prawie na pewno nie będzie działać w normalnym Racket bo widocznie mają zarówno zmienny i non-zmienny par. Zmiany, które będziesz musiał wprowadzić, jeśli chcesz uruchomić to pod rakietą, a nie R5RS, są następujące:

Zamiast używać tylko cytowanej listy, prawdopodobnie będziesz musiał utworzyć listę rejestrów z wymiennymi konstruktorami list/par. (define *reg* (mlist (mcons 'A 0) (mcons 'B 0) ...).

Zamiast używać wersji set-cdr!, wersja rakietowa to set-mcdr! i działa tylko na parach zmiennych. </EDIT>

+0

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

+0

Tak. Uznałem, że to całkiem bezpieczne założenie. – oobivat

+0

Chciałem napisać to bez użycia zestawu, aby zmusić się do myślenia inaczej. Twoje rozwiązanie do listy zainspirowało moją nową implementację. –