Racket/6502-emu
Från Täpp-Anders
Hoppa till navigeringHoppa till sök
#lang racket
;; ================================================
;; Enkel 6502-emulator skriven i Racket
;; ================================================
;; Detta är en grundläggande men fullt fungerande emulator för 6502-processorn.
;; Den hanterar minne (64 KB), register, stack, flaggor och ett grundläggande
;; set av instruktioner. Du kan lätt utöka den med fler opkoder.
;;
;; Instruktioner som stöds i denna version (kan utökas i cpu-step):
;; - $00 BRK (stoppar exekveringen)
;; - $A9 LDA #imm
;; - $8D STA abs
;; - $A2 LDX #imm
;; - $E8 INX
;; - $4C JMP abs
;; - $D0 BNE rel (enkel branch)
;;
;; NYTT: Funktioner för att skriva ut minne och flaggor har lagts till!
(struct cpu (a x y pc sp p mem) #:mutable #:transparent)
;; Flaggor (statusregister P)
(define CARRY #b00000001)
(define ZERO #b00000010)
(define INTERRUPT #b00000100)
(define DECIMAL #b00001000)
(define BREAK #b00010000)
(define OVERFLOW #b01000000)
(define NEGATIVE #b10000000)
;; Skapa en ny CPU (PC startar godtyckligt, SP pekar mot stack-sidan)
(define (make-cpu)
(cpu 0 0 0 #x8000 #xFF #b00100100 (make-vector 65536 0)))
;; Minnesåtkomst
(define (mem-read cpu addr)
(vector-ref (cpu-mem cpu) (bitwise-and addr #xFFFF)))
(define (mem-write cpu addr val)
(vector-set! (cpu-mem cpu) (bitwise-and addr #xFFFF) (bitwise-and val #xFF)))
;; Stack (6502-stack ligger på sidan 0x01)
(define (stack-push cpu val)
(mem-write cpu (cpu-sp cpu) val)
(set-cpu-sp! cpu (bitwise-and #xFF (- (cpu-sp cpu) 1))))
(define (stack-pop cpu)
(set-cpu-sp! cpu (bitwise-and #xFF (+ (cpu-sp cpu) 1)))
(mem-read cpu (cpu-sp cpu)))
;; Uppdatera N- och Z-flaggor (används av de flesta instruktioner)
(define (update-nz cpu val)
(set-flag cpu NEGATIVE (not (zero? (bitwise-and val #x80))))
(set-flag cpu ZERO (zero? (bitwise-and val #xFF))))
(define (set-flag cpu mask val)
(if val
(set-cpu-p! cpu (bitwise-ior (cpu-p cpu) mask))
(set-cpu-p! cpu (bitwise-and (cpu-p cpu) (bitwise-not mask)))))
(define (get-flag cpu mask)
(not (zero? (bitwise-and (cpu-p cpu) mask))))
;; Hjälpfunktioner för att hämta data och öka PC
(define (fetch-byte cpu)
(define addr (cpu-pc cpu))
(set-cpu-pc! cpu (add1 addr))
(mem-read cpu addr))
(define (fetch-word cpu)
(define lo (fetch-byte cpu))
(define hi (fetch-byte cpu))
(bitwise-ior lo (arithmetic-shift hi 8)))
;; Huvudfunktion: exekvera en instruktion
(define (cpu-step cpu)
(define opcode (fetch-byte cpu))
(case opcode
;; BRK – stoppar emulatorn
[(#x00)
(set-flag cpu BREAK #t)
'brk]
;; LDA #imm
[(#xA9)
(define val (fetch-byte cpu))
(set-cpu-a! cpu val)
(update-nz cpu val)]
;; STA abs
[(#x8D)
(define addr (fetch-word cpu))
(mem-write cpu addr (cpu-a cpu))]
;; LDX #imm
[(#xA2)
(define val (fetch-byte cpu))
(set-cpu-x! cpu val)
(update-nz cpu val)]
;; INX
[(#xE8)
(define new-x (bitwise-and #xFF (add1 (cpu-x cpu))))
(set-cpu-x! cpu new-x)
(update-nz cpu new-x)]
;; JMP abs
[(#x4C)
(define addr (fetch-word cpu))
(set-cpu-pc! cpu addr)]
;; BNE rel (branch if not equal / Z=0)
[(#xD0)
(define offset (fetch-byte cpu))
(when (not (get-flag cpu ZERO))
(set-cpu-pc! cpu (+ (cpu-pc cpu)
(if (> offset #x7F) (- offset #x100) offset))))]
[else
(error "Okänt opcode:" (format "~x" opcode))]))
;; Ladda ett program (lista av bytes) till minnet
(define (load-program cpu start-addr program-bytes)
(for ([b program-bytes]
[i (in-naturals)])
(mem-write cpu (+ start-addr i) b)))
;; Kör emulatorn tills BRK eller max-steg nås
(define (run-cpu cpu [max-steps 100000])
(let loop ([steps 0])
(cond
[(>= steps max-steps) 'timeout]
[else
(define result (cpu-step cpu))
(if (eq? result 'brk)
'halted-by-brk
(loop (add1 steps)))])))
;; ================================================
;; NYA HJÄLPFUNKTIONER: Skriv ut minne och flaggor
;; ================================================
;; Skriver ut aktuellt CPU-tillstånd (alla register + flaggor i läsbart format)
(define (print-cpu-state cpu)
(printf "=== CPU STATUS ===~n")
(printf "PC: $~x A: $~x X: $~x Y: $~x SP: $~x~n"
(cpu-pc cpu)
(cpu-a cpu)
(cpu-x cpu)
(cpu-y cpu)
(cpu-sp cpu))
(printf "P: $~x " (cpu-p cpu))
(printf "Flags: [N:~a V:~a - B:~a D:~a I:~a Z:~a C:~a]~n"
(if (get-flag cpu NEGATIVE) "1" "0")
(if (get-flag cpu OVERFLOW) "1" "0")
(if (get-flag cpu BREAK) "1" "0")
(if (get-flag cpu DECIMAL) "1" "0")
(if (get-flag cpu INTERRUPT)"1" "0")
(if (get-flag cpu ZERO) "1" "0")
(if (get-flag cpu CARRY) "1" "0"))
(newline))
;; Hex-dump av ett minnesområde (16 bytes per rad + ASCII-del)
;; Användning: (print-memory my-cpu #x0000 #x00FF) eller bara (print-memory my-cpu #x0600)
(define (print-memory cpu start [end #f])
(when (not end)
(set! end (+ start 255))) ; default: 256 bytes om inget slut anges
(set! start (bitwise-and start #xFFFF))
(set! end (bitwise-and end #xFFFF))
(printf "=== MEMORY DUMP $~4x – $~4x ===~n" start end)
(for ([addr (in-range start (add1 end) 16)])
(when (<= addr end)
(printf "$~4x: " addr)
;; Hex-delen
(for ([i (in-range 16)])
(define a (+ addr i))
(if (<= a end)
(printf "~2x " (mem-read cpu a))
(printf " ")))
;; ASCII-del
(printf " | ")
(for ([i (in-range 16)])
(define a (+ addr i))
(if (<= a end)
(let ([b (mem-read cpu a)])
(printf "~a" (if (and (>= b #x20) (<= b #x7E))
(integer->char b)
".")))
(printf " ")))
(newline)))
(newline))
;; ================================================
;; EXEMPEL: Ett litet program som demonstrerar emulatorn
;; ================================================
;; Programmet gör följande:
;; LDA #$05 ; A = 5
;; STA $0010 ; minne[0x0010] = 5
;; LDX #$0A ; X = 10
;; INX ; X = 11
;; JMP $0610 ; hoppa till en BRK
;; BRK
(define example-program
(list #xA9 #x05 ; LDA #5
#x8D #x10 #x00 ; STA $0010
#xA2 #x0A ; LDX #10
#xE8 ; INX
#x4C #x10 #x06 ; JMP $0610
#x00)) ; BRK (vid 0x0610)
;; Hur du kör exemplet (med de nya utskriftsfunktionerna):
(define my-cpu (make-cpu))
(load-program my-cpu #x0600 example-program)
(print-cpu-state my-cpu)
(set-cpu-pc! my-cpu #x0600)
(print-cpu-state my-cpu)
(run-cpu my-cpu)
(print-cpu-state my-cpu)
;; Flera opkoder kan läggas till för bättre emulering!