Mercurial > vba-clojure
view clojure/com/aurellem/run/music.clj @ 424:7bd806c4dbb6
changed assembly to handle mini-midi messages of different lengths.
author | Robert McIntyre <rlm@mit.edu> |
---|---|
date | Mon, 23 Apr 2012 04:45:55 -0500 |
parents | 971bd1774eab |
children | df4e03672b05 |
line wrap: on
line source
1 (ns com.aurellem.run.music2 (:use (com.aurellem.gb saves gb-driver util constants3 items vbm characters money4 rlm-assembly))5 (:use (com.aurellem.run util title save-corruption6 bootstrap-0 bootstrap-1))7 (:import [com.aurellem.gb.gb_driver SaveState]))10 (def music-base new-kernel)15 (defn store [n address]16 (flatten17 [0xF518 0xE520 0x3E21 n23 0x2124 (reverse (disect-bytes-2 address))26 0x7728 0xE129 0xF1]))31 (defn infinite-loop []32 [0x18 0xFE])36 (def divider-register 0xFF04)39 (defrecord Bit-Note [frequency volume duration duty])41 (defn clear-music-registers []42 (flatten43 [(store (Integer/parseInt "00000000" 2) 0xFF10)44 (store (Integer/parseInt "00000000" 2) 0xFF11)45 (store (Integer/parseInt "00000000" 2) 0xFF12)46 (store (Integer/parseInt "00000000" 2) 0xFF13)47 (store (Integer/parseInt "00000000" 2) 0xFF14)49 (store (Integer/parseInt "00000000" 2) 0xFF16) ;; pattern duty 00000050 (store (Integer/parseInt "00000000" 2) 0xFF17) ;; volume 000051 (store (Integer/parseInt "00000000" 2) 0xFF18) ;; frequency-low52 (store (Integer/parseInt "00000000" 2) 0xFF19) ;; 00000 frequency-high54 (store (Integer/parseInt "00000000" 2) 0xFF1A)55 (store (Integer/parseInt "00000000" 2) 0xFF1B)56 (store (Integer/parseInt "00000000" 2) 0xFF1C)57 (store (Integer/parseInt "00000000" 2) 0xFF1D)58 (store (Integer/parseInt "00000000" 2) 0xFF1E)60 (store (Integer/parseInt "00000000" 2) 0xFF20)61 (store (Integer/parseInt "00000000" 2) 0xFF21)62 (store (Integer/parseInt "00000000" 2) 0xFF22)63 (store (Integer/parseInt "00000000" 2) 0xFF23)]))66 ;; mini-midi syntax68 ;; codes69 ;; note-code == 0x0070 ;; change-duty-code = 0x0171 ;; silence-code = 0x0273 ;; silence format74 ;; 2 bytes75 ;; [silence-code (0x02)]76 ;; [duration-8-bits]78 ;; note data format79 ;; 4 bytes80 ;; [note-code (0x00)]81 ;; [volume-4-bits 0 frequency-high-3-bits]82 ;; [frequengy-low-8-bits]83 ;; [duration-8-bits]85 ;; change-duty-format86 ;; 2 bytes87 ;; [change-duty-code (0x01)]88 ;; [new-duty]90 (defn do-message91 "Read the message which starts at the current value of HL and do92 what it says. Duration is left in A, and HL is advanced93 appropraitely."94 []96 )103 (defn play-note104 "Play the note referenced by HL in the appropiate channel.105 Leaves desired-duration in A."106 []107 [0x2A ;; load volume/frequency-high info108 0xF5 ;; push A109 0xE6110 (Integer/parseInt "11110000" 2) ;; volume mask111 0xE0112 0x17 ;; set volume113 0xF1 ;; pop A114 0xE6115 (Integer/parseInt "00000111" 2) ;; frequency-high mask116 0xE0117 0x19 ;; set frequency-high119 0x2A ;; load frequency low-bits120 0xE0121 0x18 ;; set frequency-low-bits123 0x2A ;; load duration124 ])126 (defn music-step []127 (flatten128 [129 0xF5 ;; push A130 0xF0131 0x05 ;; load current ticks132 0xB8 ;; B holds previous sub-ticks, subtract it from A133 ;; if A-B caused a carry, then (B > A) is true, and134 ;; A = current-sub-tics, B = previous-sub-ticks, so135 ;; current-sub-ticks < previous-sub-ticks, which means that the136 ;; timer counter HAS overflowed.137 0x30 ;; increment C only if last result caused carry138 0x01139 0x0C141 0x47 ;; update sub-ticks (A->B)143 0xF1 ;; pop AF, now A contains desired-ticks145 0xB9 ;; compare with current ticks147 ;; if desired-ticks = current ticks148 ;; go to next note ; set current set ticks to 0.150 0x20151 (+ (count (play-note)) 2)153 (play-note)155 0x0E156 0x00])) ;; 0->C (current-ticks)158 (defn music-kernel []159 (flatten160 [(clear-music-registers)162 0x21163 0x00164 0xD0 ;; set HL to 0xD000 == music-start165 0x0E166 0x00 ;; 0->C167 0x06168 0x00 ;; 0->B170 0x3E171 0x01172 0xE0173 0x06 ;; set TMA to 0175 0x3E176 (Integer/parseInt "00000110" 2)177 0xE0178 0x07 ;; set TAC to 65536 Hz and activate timer181 0xAF ;; initialiaze A to zero184 (music-step)185 0x18186 (->signed-8-bit (+ (- (count (music-step)))187 -2))]))189 (def one-note190 [0xA0 0x00 0xFF])192 (def many-notes193 (flatten (repeat 10 one-note)))195 (def increasing-notes196 [0xA0 0x00 0x55197 0xA1 0x00 0x55198 0xA2 0x00 0x55199 0xA3 0x00 0x55200 0xA4 0x00 0x55201 0xA5 0x00 0x55202 0xA6 0x00 0x55203 0xA6 0x55 0xFF204 0xA6 0x55 0xFF205 0xA6 0x55 0xFF206 0x00 0x00 0xFF207 ])209 (defn frequency-code->frequency210 [code]211 (assert (<= 0 code 2047))212 (/ 131072 (- 2048 code)))214 (defn clamp [x low high]215 (cond (> x high) high216 (< x low) low217 true x))219 (defn frequency->frequency-code220 [frequency]221 (clamp222 (Math/round223 (float224 (/ (- (* 2048 frequency) 131072) frequency)))225 0x00 2048))227 (defn note-codes [frequency volume duration]228 (assert (<= 0 volume 0xF))229 (assert (<= 0 duration 0xFF))230 (let [frequency-code231 (frequency->frequency-code frequency)232 volume&high-frequency233 (+ (bit-shift-left volume 4)234 (bit-shift-right frequency-code 8))235 low-frequency236 (bit-and 0xFF frequency-code)]237 [volume&high-frequency238 low-frequency239 duration]))241 (def C4 (partial note-codes 261.63))242 (def D4 (partial note-codes 293.66))243 (def E4 (partial note-codes 329.63))244 (def F4 (partial note-codes 349.23))245 (def G4 (partial note-codes 392))246 (def A4 (partial note-codes 440))247 (def B4 (partial note-codes 493.88))248 (def C5 (partial note-codes 523.3))250 (def scale251 (flatten252 [(C4 0xF 0x40)253 (D4 0xF 0x40)254 (E4 0xF 0x40)255 (F4 0xF 0x40)256 (G4 0xF 0x40)257 (A4 0xF 0x40)258 (B4 0xF 0x40)259 (C5 0xF 0x40)]))261 (defn play-music [music-bytes]262 (let [program-target 0xC000263 music-target 0xD000]264 (-> (set-memory-range (second (music-base))265 program-target (music-kernel))266 (set-memory-range music-target music-bytes)267 (PC! program-target))))270 (defn test-note [music-bytes]271 (-> (set-memory-range (second (music-base))272 0xC000 (concat (clear-music-registers)273 (play-note)274 (infinite-loop)))275 (set-memory-range 0xD000 music-bytes)276 (PC! 0xC000)277 (HL! 0xD000)278 ))281 (defn run-program282 ([program]283 (let [target 0xC000]284 (-> (set-memory-range (second (music-base))285 target program)286 (PC! target)))))288 (defn trippy []289 (run-moves (play-music many-notes ) (repeat 8000 [])))291 (defn test-timer []292 (flatten293 [0x3E294 0x01295 0xE0296 0x06 ;; set TMA to 0298 0x3E299 (Integer/parseInt "00000100" 2)300 0xE0301 0x07 ;; set TAC to 16384 Hz and activate timer303 (repeat304 500305 [0xF0306 0x05])]))