# HG changeset patch # User Robert McIntyre # Date 1331946190 18000 # Node ID be4ec0e60c1647ee3cef54f63f55f608e687dd02 # Parent bcb5c41626b44e174c4488cf3bdcfae975f01c28 mode 0 of bootstrapping state machine complete diff -r bcb5c41626b4 -r be4ec0e60c16 clojure/com/aurellem/assembly.clj --- a/clojure/com/aurellem/assembly.clj Fri Mar 16 17:50:35 2012 -0500 +++ b/clojure/com/aurellem/assembly.clj Fri Mar 16 20:03:10 2012 -0500 @@ -41,6 +41,14 @@ (println (format "IE: %d" (IE state))) state) +(defn print-listing [state begin end] + (dorun (map + (fn [opcode line] + (println (format "0x%04X: 0x%02X" line opcode))) + (subvec (vec (memory state)) begin end) + (range begin end))) + state) + (defn run-assembly ([info-fn assembly n] (let [final-state @@ -68,64 +76,82 @@ (binary-str (reg-fn state)))) state) - (defn view-memory [state mem] (println (format "mem 0x%04X = %s" mem (binary-str (aget (memory state) mem)))) state) +(defn trace [state] + (loop [program-counters [] + opcodes []] + (let [frame-boundary? + (com.aurellem.gb.Gb/tick)] + (println (count opcodes)) + (if frame-boundary? + [program-counters opcodes] + (recur + (conj program-counters + (first (registers @current-state))) + (conj opcodes + (aget (memory @current-state) + (PC @current-state)))))))) + +(defn good-trace [] + (-> (mid-game) (tick) (IE! 0) + (set-inv-mem [0x00 0x00 0X00 0x00]) + (PC! item-list-start)(print-interrupt) + (info) (tick) (info) (tick) (info))) + (defn read-down-button [] (-> (tick (mid-game)) (IE! 0) ; disable interrupts (inject-item-assembly - (concat - ;; write 00010000 to 0xFF00 to select joypad - [0x18 ;D31D ; jump over - 0x01 ;D31E ; the next 8 bits - ;D31F - (Integer/parseInt "00100000" 2) ; data section - - 0xFA ;D320 ; load (D31F) into A - 0x1F ;D321 --> - 0xD3 ;D322 --> D31F + ;; write 00010000 to 0xFF00 to select joypad + [0x18 ;D31D ; jump over + 0x01 ;D31E ; the next 8 bits + ;D31F + (Integer/parseInt "00100000" 2) ; data section + + 0xFA ;D320 ; load (D31F) into A + 0x1F ;D321 --> + 0xD3 ;D322 --> D31F - 0xEA ;D323 ; load (A), which is - 0x00 ;D324 --> ; 00010000, into FF00 - 0xFF ;D325 --> FF00 - - 0x18 ;D326 ; this is the place where - 0x01 ;D327 ; we will store whether - 0x00 ;D328 ; "down" is pressed. + 0xEA ;D323 ; load (A), which is + 0x00 ;D324 --> ; 00010000, into FF00 + 0xFF ;D325 --> FF00 + + 0x18 ;D326 ; this is the place where + 0x01 ;D327 ; we will store whether + 0x00 ;D328 ; "down" is pressed. - 0xFA ;D329 ; (FF00) -> A - 0x00 ;D32A - 0xFF ;D32B + 0xFA ;D329 ; (FF00) -> A + 0x00 ;D32A + 0xFF ;D32B - 0xCB ;D32C ; Test whether "down" - 0x5F ;D32D ; is pressed. + 0xCB ;D32C ; Test whether "down" + 0x5F ;D32D ; is pressed. - 0x28 ;D32E ; if down is pressed, - 0x03 ;D32F ; skip the next section - ; of code. - ;; down-is-not-pressed - 0xC3 ;D330 - 0x1D ;D331 ; return to beginning - 0xD3 ;D332 - - ;; down-is-pressed - 0xEA ;D334 ; write A to D328 if - 0x28 ;D335 ; "down" was pressed - 0xD3 ;D336 + 0x28 ;D32E ; if down is pressed, + 0x03 ;D32F ; skip the next section + ; of code. + ;; down-is-not-pressed + 0xC3 ;D330 + 0x1D ;D331 ; return to beginning + 0xD3 ;D332 + + ;; down-is-pressed + 0xEA ;D334 ; write A to D328 if + 0x28 ;D335 ; "down" was pressed + 0xD3 ;D336 - 0xC3 ;D330 - 0x1D ;D331 ; return to beginning - 0xD3 ;D332 - ] - - [])))) + 0xC3 ;D330 + 0x1D ;D331 ; return to beginning + 0xD3 ;D332 + ]))) - - +(defn test-read-down [] + (= (view-memory (step (step (read-down-button) [:d])) 0xD328) + (view-memory (step (step (read-down-button))) 0xD328))) (defn count-frames [] (-> (tick (mid-game)) @@ -206,7 +232,6 @@ 0xC3 ;D349 ; return to beginning 0x1D ;D34A 0xD3 ;D34B - ]))) (defn step-count-frames [] @@ -235,37 +260,11 @@ (tick) (print-inventory))) -;;(defn test-read-down [] -;; (= (view-memory (step (step (read-buttons) [:d])) 0xD328) -;; (view-memory (step (step (read-buttons))) 0xD328))) - (defn test-count-frames [] (= 255 (aget (memory ((apply comp (repeat 255 step)) (count-frames))) 0xD31F))) - -(defn trace [state] - (loop [program-counters [] - opcodes []] - (let [frame-boundary? - (com.aurellem.gb.Gb/tick)] - (println (count opcodes)) - (if frame-boundary? - [program-counters opcodes] - (recur - (conj program-counters - (first (registers @current-state))) - (conj opcodes - (aget (memory @current-state) - (PC @current-state)))))))) - -(defn good-trace [] - (-> (mid-game) (tick) (IE! 0) - (set-inv-mem [0x00 0x00 0X00 0x00]) - (PC! item-list-start)(print-interrupt) - (info) (tick) (info) (tick) (info))) - ;; specs for main bootstrap program ;; starts in "mode-select" mode ;; Each button press takes place in a single frame. @@ -286,165 +285,156 @@ ;; then, the actual bytes are entered and are written to the ;; start address in sequence. +(defn input-number-assembly [] + [0x18 ;D31D ; jump over + 0x02 ;D31E ; the next 2 bytes + 0x00 ;D31F ; frame-count + 0x00 ;D320 ; v-blank-prev + + 0xFA ;D321 + 0x41 ;D322 ; load (FF41) into A + 0xFF ;D323 ; this contains mode flags + + ;; if we're in v-blank, the bit-1 is 0 + ;; and bit-2 is 1 Otherwise, it is not v-blank. + 0xCB ;D324 ; test bit-1 of A + 0x4F ;D325 + 0xC2 ;D326 ; if bit-1 is not 0 + 0x44 ;D327 ; GOTO not-v-blank + 0xD3 ;D328 + + 0xCB ;D329 ; test bit-0 of A + 0x47 ;D32A + + 0xCA ;D32B ; if bit-0 is not 1 + 0x44 ;D32C ; GOTO not-v-blank + 0xD3 ;D32D + + ;;; in v-blank mode + + ;; if v-blank-prev was 0, + ;; increment frame-count + + 0xFA ;D32E ; load v-blank-prev to A + 0x20 ;D32F + 0xD3 ;D330 + + 0xCB ;D331 + 0x47 ;D332 ; test bit-0 of A + + 0x20 ;D333 ; skip next section + 0x07 ;D334 ; if v-blank-prev was not zero + + ;; v-blank was 0, increment frame-count + 0xFA ;D335 ; load frame-count into A + 0x1F ;D336 + 0xD3 ;D337 + + 0x3C ;D338 ; inc A + + 0xEA ;D339 ; load A into frame-count + 0x1F ;D33A + 0xD3 ;D33B + + ;; set v-blank-prev to 1 + 0x3E ;D33C ; load 1 into A + 0x01 ;D33D + + 0xEA ;D33E ; load A into v-blank-prev + 0x20 ;D33F + 0xD3 ;D340 + + 0xC3 ;D341 ; GOTO input handling code + 0x4E ;D342 + 0xD3 ;D343 + + ;;; not in v-blank mode + ;; set v-blank-prev to 0 + 0x3E ;D344 ; load 0 into A + 0x00 ;D345 + + 0xEA ;D346 ; load A into v-blank-prev + 0x20 ;D347 + 0xD3 ;D348 + + 0xC3 ;D349 ; return to beginning + 0x1D ;D34A + 0xD3 ;D34B + + 0x00 ;D34C ; these are here + 0x00 ;D34D ; for glue + + + ;;; calculate input number based on button presses + 0x18 ;D34E ; skip next 3 bytes + 0x03 ;D34F + ;D350 + (Integer/parseInt "00100000" 2) ; select directional pad + ;D351 + (Integer/parseInt "00010000" 2) ; select buttons + 0x00 ;D352 ; input-number + + ;; select directional pad, store low bits in B + + 0xFA ;D353 ; load (D350) into A + 0x50 ;D354 --> + 0xD3 ;D355 --> D31F + + 0xEA ;D356 ; load (A), which is + 0x00 ;D357 --> ; 00010000, into FF00 + 0xFF ;D358 --> FF00 + + 0x06 ;D359 + ;D35A + (Integer/parseInt "11110000" 2) ; "11110000" -> B + 0xFA ;D35B ; (FF00) -> A + 0x00 ;D35C + 0xFF ;D35D + + 0xCB ;D35E ; swap nybbles on A + 0x37 ;D35F + 0xA0 ;D360 ; (AND A B) -> A + 0x47 ;D361 ; A -> B + + ;; select buttons store bottom bits in C + + 0xFA ; ; load (D351) into A + 0x51 ; --> + 0xD3 ; --> D31F + + 0xEA ; ; load (A), which is + 0x00 ; --> ; 00001000, into FF00 + 0xFF ; --> FF00 + + 0x0E ; + (Integer/parseInt "00001111" 2) ; "00001111" -> C + + 0xFA ; ; (FF00) -> A + 0x00 ; + 0xFF ; + + 0xA1 ; ; (AND A C) -> A + 0x4F ; ; A -> C + + ;; combine the B and C registers into the input number + 0x79 ; ; C -> A + 0xB0 ; ; (OR A B) -> A + 0x2F ; ; negate A + + 0xEA ; ; store A into input-number + 0x52 ; + 0xD3 ; + + 0xC3 ; ; return to beginning + 0x1D ; + 0xD3 ; + ]) (defn input-number [] (-> (tick (mid-game)) (IE! 0) ; disable interrupts - (inject-item-assembly - [0x18 ;D31D ; jump over - 0x02 ;D31E ; the next 2 bytes - 0x00 ;D31F ; frame-count - 0x00 ;D320 ; v-blank-prev - - 0xFA ;D321 - 0x41 ;D322 ; load (FF41) into A - 0xFF ;D323 ; this contains mode flags - - ;; if we're in v-blank, the bit-1 is 0 - ;; and bit-2 is 1 Otherwise, it is not v-blank. - 0xCB ;D324 ; test bit-1 of A - 0x4F ;D325 - - 0xC2 ;D326 ; if bit-1 is not 0 - 0x44 ;D327 ; GOTO not-v-blank - 0xD3 ;D328 - - 0xCB ;D329 ; test bit-0 of A - 0x47 ;D32A - - 0xCA ;D32B ; if bit-0 is not 1 - 0x44 ;D32C ; GOTO not-v-blank - 0xD3 ;D32D - - ;;; in v-blank mode - - ;; if v-blank-prev was 0, - ;; increment frame-count - - 0xFA ;D32E ; load v-blank-prev to A - 0x20 ;D32F - 0xD3 ;D330 - - 0xCB ;D331 - 0x47 ;D332 ; test bit-0 of A - - 0x20 ;D333 ; skip next section - 0x07 ;D334 ; if v-blank-prev was not zero - - ;; v-blank was 0, increment frame-count - 0xFA ;D335 ; load frame-count into A - 0x1F ;D336 - 0xD3 ;D337 - - 0x3C ;D338 ; inc A - - 0xEA ;D339 ; load A into frame-count - 0x1F ;D33A - 0xD3 ;D33B - - ;; set v-blank-prev to 1 - 0x3E ;D33C ; load 1 into A - 0x01 ;D33D - - 0xEA ;D33E ; load A into v-blank-prev - 0x20 ;D33F - 0xD3 ;D340 - - 0xC3 ;D341 ; GOTO input handling code - 0x4E ;D342 - 0xD3 ;D343 - - ;;; not in v-blank mode - ;; set v-blank-prev to 0 - 0x3E ;D344 ; load 0 into A - 0x00 ;D345 - - 0xEA ;D346 ; load A into v-blank-prev - 0x20 ;D347 - 0xD3 ;D348 - - 0xC3 ;D349 ; return to beginning - 0x1D ;D34A - 0xD3 ;D34B - - 0x00 ;D34C ; these are here - 0x00 ;D34D ; for glue - - - ;;; calculate input number based on button presses - 0x18 ;D34E ; skip next 3 bytes - 0x03 ;D34F - ;D350 - (Integer/parseInt "00100000" 2) ; select directional pad - ;D351 - (Integer/parseInt "00010000" 2) ; select buttons - 0x00 ;D352 ; input-number - - ;; select directional pad, store low bits in B - - 0xFA ;D353 ; load (D350) into A - 0x50 ;D354 --> - 0xD3 ;D355 --> D31F - - 0xEA ;D356 ; load (A), which is - 0x00 ;D357 --> ; 00010000, into FF00 - 0xFF ;D358 --> FF00 - - 0x06 ;D359 - ;D35A - (Integer/parseInt "11110000" 2) ; "11110000" -> B - 0xFA ;D35B ; (FF00) -> A - 0x00 ;D35C - 0xFF ;D35D - - 0xCB ;D35E ; swap nybbles on A - 0x37 ;D35F - 0xA0 ;D360 ; (AND A B) -> A - 0x47 ;D361 ; A -> B - - ;; select buttons store bottom bits in C - - 0xFA ; ; load (D351) into A - 0x51 ; --> - 0xD3 ; --> D31F - - 0xEA ; ; load (A), which is - 0x00 ; --> ; 00001000, into FF00 - 0xFF ; --> FF00 - - 0x0E ; - (Integer/parseInt "00001111" 2) ; "00001111" -> C - - 0xFA ; ; (FF00) -> A - 0x00 ; - 0xFF ; - - 0xA1 ; ; (AND A C) -> A - 0x4F ; ; A -> C - - - ;; combine the B and C registers into the input number - 0x79 ; ; C -> A - 0xB0 ; ; (OR A B) -> A - 0x2F ; ; negate A - - 0xEA ; ; store A into input-number - 0x52 ; - 0xD3 ; - - 0xC3 ; ; return to beginning - 0x1D ; - 0xD3 ; - ]))) - -(defn print-listing [state begin end] - (dorun (map - (fn [opcode line] - (println (format "0x%04X: 0x%02X" line opcode))) - (subvec (vec (memory state)) begin end) - (range begin end))) - state) + (inject-item-assembly (input-number-assembly)))) (defn test-input-number "Input freestyle buttons and observe the effects at the repl." @@ -452,6 +442,288 @@ (set-state! (input-number)) (dotimes [_ 90000] (step (view-memory @current-state 0xD352)))) +(defn write-memory-assembly [] + [0x18 ;D31D ; jump over + 0x02 ;D31E ; the next 2 bytes + 0x00 ;D31F ; frame-count + 0x00 ;D320 ; v-blank-prev + + 0xFA ;D321 + 0x41 ;D322 ; load (FF41) into A + 0xFF ;D323 ; this contains mode flags + + ;; if we're in v-blank, the bit-1 is 0 + ;; and bit-2 is 1 Otherwise, it is not v-blank. + 0xCB ;D324 ; test bit-1 of A + 0x4F ;D325 + 0xC2 ;D326 ; if bit-1 is not 0 + 0x44 ;D327 ; GOTO not-v-blank + 0xD3 ;D328 + + 0xCB ;D329 ; test bit-0 of A + 0x47 ;D32A + 0xCA ;D32B ; if bit-0 is not 1 + 0x44 ;D32C ; GOTO not-v-blank + 0xD3 ;D32D + + ;;; in v-blank mode + ;; if v-blank-prev was 0, + ;; increment frame-count + + 0xFA ;D32E ; load v-blank-prev to A + 0x20 ;D32F + 0xD3 ;D330 + + 0xCB ;D331 + 0x47 ;D332 ; test bit-0 of A + + 0x20 ;D333 ; skip next section + 0x07 ;D334 ; if v-blank-prev was not zero + + ;; v-blank was 0, increment frame-count + 0xFA ;D335 ; load frame-count into A + 0x1F ;D336 + 0xD3 ;D337 + + 0x3C ;D338 ; inc A + + 0xEA ;D339 ; load A into frame-count + 0x1F ;D33A + 0xD3 ;D33B + + ;; set v-blank-prev to 1 + 0x3E ;D33C ; load 1 into A + 0x01 ;D33D + + 0xEA ;D33E ; load A into v-blank-prev + 0x20 ;D33F + 0xD3 ;D340 + + 0xC3 ;D341 ; GOTO input handling code + 0x4E ;D342 + 0xD3 ;D343 + + ;;; not in v-blank mode + ;; set v-blank-prev to 0 + 0x3E ;D344 ; load 0 into A + 0x00 ;D345 + + 0xEA ;D346 ; load A into v-blank-prev + 0x20 ;D347 + 0xD3 ;D348 + + 0xC3 ;D349 ; return to beginning + 0x1D ;D34A + 0xD3 ;D34B + + 0x00 ;D34C ; these are here + 0x00 ;D34D ; for glue + + + ;;; calculate input number based on button presses + 0x18 ;D34E ; skip next 3 bytes + 0x03 ;D34F + ;D350 + (Integer/parseInt "00100000" 2) ; select directional pad + ;D351 + (Integer/parseInt "00010000" 2) ; select buttons + 0x00 ;D352 ; input-number + + ;; select directional pad, store low bits in B + + 0xFA ;D353 ; load (D350) into A + 0x50 ;D354 --> + 0xD3 ;D355 --> D31F + + 0xEA ;D356 ; load (A), which is + 0x00 ;D357 --> ; 00010000, into FF00 + 0xFF ;D358 --> FF00 + + 0x06 ;D359 + ;D35A + (Integer/parseInt "11110000" 2) ; "11110000" -> B + 0xFA ;D35B ; (FF00) -> A + 0x00 ;D35C + 0xFF ;D35D + + 0xCB ;D35E ; swap nybbles on A + 0x37 ;D35F + 0xA0 ;D360 ; (AND A B) -> A + 0x47 ;D361 ; A -> B + + ;; select buttons store bottom bits in C + + 0xFA ;D362 ; load (D351) into A + 0x51 ;D363 --> + 0xD3 ;D364 --> D31F + + 0xEA ;D365 ; load (A), which is + 0x00 ;D366 --> ; 00001000, into FF00 + 0xFF ;D367 --> FF00 + + 0x0E ;D368 + ;D369 + (Integer/parseInt "00001111" 2) ; "00001111" -> C + + 0xFA ;D36A ; (FF00) -> A + 0x00 ;D36B + 0xFF ;D36C + + 0xA1 ;D36D ; (AND A C) -> A + 0x4F ;D36E ; A -> C + + ;; combine the B and C registers into the input number + 0x79 ;D36F ; C -> A + 0xB0 ;D370 ; (OR A B) -> A + 0x2F ;D371 ; negate A + + 0xEA ;D372 ; store A into input-number + 0x52 ;D373 + 0xD3 ;D374 + + 0xC3 ;D375 ; GOTO state machine + ;;0x1D + 0x80 ;D376 + 0xD3 ;D377 + + 0x00 ;D378 + 0x00 ;D379 + 0x00 ;D37A + 0x00 ;D37B ; these are here because + 0x00 ;D37C ; I messed up :( + 0x00 ;D37D + 0x00 ;D37E + 0x00 ;D37F + + ;; beginning of main state machine + 0x18 ;D380 ; Declaration of variables + 0x05 ;D381 ; 5 variables: + 0x00 ;D382 ; current-mode + 0x00 ;D383 ; bytes-left-to-write + 0x00 ;D384 ; unused + 0x00 ;D385 ; unused + 0x00 ;D386 ; unused + + + ;; banch on current mode + ;; mode 0 -- input-mode mode + ;; means that we are waiting for a mode, so set the mode to + ;; whatever is currently in input number. If nothing is + ;; entered, then the program stays in input-mode mode + + 0xFA ;D387 ; load current-mode (0xD382) + 0x82 ;D388 ; into A + 0xD3 ;D389 + + 0x00 ;D38A + + 0xFE ;D38B + 0x00 ;D38C ; compare A with 0x00 + + ;; TODO make this jump non-absolute + + 0xCA ;D38D ; GOTO Mode 0 if current-mode is 0 + 0xA8 ;D38E + 0xD3 ;D38F + + 0x00 ;D390 + 0x00 ;D391 + 0x00 ;D392 + 0x00 ;D393 + 0x00 ;D394 + 0x00 ;D395 + 0x00 ;D396 + 0x00 ;D397 + 0x00 ;D398 + 0x00 ;D399 + 0x00 ;D39A + 0x00 ;D39B + 0x00 ;D39C + 0x00 ;D39D + 0x00 ;D39E + 0x00 ;D39F + 0x00 ;D3A0 + 0x00 ;D3A1 + 0x00 ;D3A2 + 0x00 ;D3A3 + 0x00 ;D3A4 + ;; End of Mode checking, goto beginning + 0xC3 ;D3A5 + 0x1D ;D3A6 + 0xD3 ;D3A7 + ;; Mode 0 + ;; set current-mode to input-number + 0xFA ;D3A8 ; load input-number (0xD352) + 0x52 ;D3A9 ; into A + 0xD3 ;D3AA + + 0xEA ;D3AB ; load A into current-mode + 0x82 ;D3AC ; (0xD382) + 0xD3 ;D3AD + + 0xC3 ;D3AE ; go back to beginning + 0x1D ;D3AF + 0xD3 ;D3B0 + + 0x00 ;D3B1 + 0x00 ;D3B2 + 0x00 ;D3B3 + 0x00 ;D3B4 + 0x00 ;D3B5 + 0x00 ;D3B6 + 0x00 ;D3B7 + 0x00 ;D3B8 + 0x00 ;D3B9 + 0x00 ;D3BA + 0x00 ;D3BB + 0x00 ;D3BC + 0x00 ;D3BD + 0x00 ;D3BE + 0x00 ;D3BF + 0x00 ;D3C0 + 0x00 ;D3C1 + 0x00 ;D3C2 + 0x00 ;D3C3 + 0x00 ;D3C4 + 0x00 ;D3C5 + 0x00 ;D3C6 + 0x00 ;D3C7 + 0x00 ;D3C8 + 0x00 ;D3C9 + 0x00 ;D3CA + 0x00 ;D3CB + 0x00 ;D3CC + 0x00 ;D3CD + 0x00 ;D3CE + 0x00 ;D3CF + 0x00 ;D3D0 + 0x00 ;D3D1 + 0x00 ;D3D2 + 0x00 ;D3D3 + 0x00 ;D3D4 + 0x00 ;D3D5 + 0x00 ;D3D6 + + + + + 0xC3 ; ; Complete Loop + 0x1D ; + 0xD3 ; + + + + ]) + + +(def frame-count 0xD31F) +(def input 0xD352) +(def current-mode 0xD382) + +(defn write-memory [] + (-> (tick (mid-game)) + (IE! 0) ; disable interrupts + (inject-item-assembly (write-memory-assembly))))