rlm@550: ;;;; "Advanced Choreography" -- this is the final video for this project. rlm@550: rlm@550: (ns com.aurellem.run.adv-choreo rlm@550: (:use (com.aurellem.gb saves gb-driver util constants rlm@550: items vbm characters money rlm@550: rlm-assembly)) rlm@550: (:use (com.aurellem.run util music title save-corruption rlm@550: bootstrap-0 bootstrap-1 image rlm@550: ram-display final-cut basic-choreo)) rlm@550: (:require clojure.string) rlm@553: (:import java.awt.image.BufferedImage) rlm@553: (:import (javax.imageio ImageWriteParam IIOImage ImageIO)) rlm@550: (:import [com.aurellem.gb.gb_driver SaveState]) rlm@550: (:import java.io.File)) rlm@550: rlm@550: rlm@550: rlm@550: ;; Use the gameboy's screen to display the new programming rlm@550: ;; instead of a side window. This will make it look much rlm@550: ;; cooler and create a terminal-like effect as the game is rlm@550: ;; being reprogramed. To do this, use a fixed data entry rlm@550: ;; region in ram, and run a program that translates this rlm@550: ;; region into the screen. Every time this data entry region rlm@550: ;; is full, run a program that copies the data to the rlm@550: ;; appropriate region in memory. This will cost ~15 seconds rlm@550: ;; at the beginning to set up, and then should have minimal rlm@550: ;; overhead (~5%) for the rest of the data transfer, but rlm@550: ;; will have a good psychological effect for the viewer rlm@550: ;; since he can see that something is actually happening in rlm@550: ;; the game. rlm@550: rlm@550: rlm@551: ;; Symbol size and type. rlm@551: rlm@551: ;; use fonts from zophar's domain: rlm@551: ;; http://www.zophar.net/utilities/fonts/8x8-font-archive.html rlm@551: rlm@551: ;; Green font on black background for matrix look. rlm@551: rlm@551: rlm@551: (defn program-data [base-address] rlm@551: (let [image-program rlm@551: (display-image-kernel rlm@551: base-address rlm@554: rlm@554: ;;pinkie-pie-mark rlm@554: test-image-color rlm@554: rlm@554: ) rlm@554: rlm@551: rlm@551: music-base-address (+ (count image-program) base-address) rlm@551: rlm@551: initial-music-data rlm@551: (midi-bytes pony-csv 0 0 0 0) rlm@551: rlm@551: data-lengths rlm@551: (map (comp count :data) rlm@551: [(:kernel initial-music-data) rlm@551: (:voice-1 initial-music-data) rlm@551: (:voice-2 initial-music-data)]);; noise not needed rlm@551: addresses rlm@551: (map (partial + music-base-address) (reductions + 0 data-lengths)) rlm@551: rlm@551: final-music-data rlm@551: (apply (partial midi-bytes pony-csv) addresses) rlm@551: rlm@551: music-program rlm@551: (concat rlm@551: (:data (:kernel final-music-data)) rlm@551: (:data (:voice-1 final-music-data)) rlm@551: (:data (:voice-2 final-music-data)) rlm@551: (:data (:noise final-music-data)))] rlm@551: rlm@551: (concat rlm@551: image-program ;; image program falls through to music program rlm@554: rlm@554: (infinite-loop) rlm@554: ;;music-program rlm@554: rlm@554: ))) rlm@551: rlm@551: rlm@551: rlm@553: rlm@553: (def glyphs rlm@553: "The sixteen 8x8 glyphs which make up the \"terminal\" font." rlm@553: (mapv #(ImageIO/read rlm@553: (File. user-home (str "proj/vba-clojure/font/" % ".png"))) rlm@553: ["0" "1" "2" "3" "4" "5" "6" "7" "8" "9" "A" "B" "C" "D" "E" "F"])) rlm@553: rlm@554: (defn glyph-init-program rlm@553: [start-address] rlm@553: (let [zero-glyph (image->gb-image (glyphs 0)) rlm@553: rlm@553: ;; write same pallet information to all pallettes rlm@553: A (flatten rlm@554: [(write-byte LCD-control-register 0x00);; disable LCD protection rlm@553: (set-palettes bg-palette-select bg-palette-data rlm@553: (repeat 8 (first (:palettes zero-glyph)))) rlm@553: (select-LCD-bank 0) rlm@553: (write-byte SCX-register 0) rlm@553: (write-byte SCY-register 0)]) rlm@553: B (flatten rlm@553: [(write-data rlm@553: (+ start-address (count A)) rlm@553: character-data-address rlm@553: (flatten rlm@553: (map (comp gb-tile->bytes first :tiles image->gb-image) rlm@554: glyphs))) rlm@553: rlm@554: rlm@554: (write-byte rlm@554: LCD-control-register rlm@554: (Integer/parseInt rlm@554: (str rlm@554: "1" ;; LCDC on/off rlm@554: "0" ;; Window code area rlm@554: "0" ;; Windowing on? rlm@554: "1" ;; BG tile base (1 = 0x8000) rlm@554: "0" ;; BG-1 or BG-2 ? rlm@554: "0" ;; OBJ-block composition rlm@554: "0" ;; OBJ-on flag rlm@554: "1") ;; no-effect rlm@554: 2))])] rlm@554: (concat A B ))) rlm@553: rlm@553: (defn glyph-display-program rlm@553: [start-address rlm@553: delay-count rlm@553: total-glyph-count] rlm@554: [0xC5 rlm@554: 0xD5 rlm@554: 0xE5 rlm@554: 0xF5 rlm@554: rlm@554: rlm@554: rlm@554: 0xF1 rlm@554: 0xE1 rlm@554: 0xD1 rlm@554: 0xC1 rlm@554: rlm@554: ]) rlm@553: rlm@553: rlm@553: (defn glyph-bootstrap-program rlm@553: [start-address delay-count total-glyph-count] rlm@553: (let [init [0xAF 0x4F 0x47] ;; 0->A; 0->C; 0->B rlm@554: header (concat (frame-metronome) (read-user-input)) rlm@553: rlm@553: glyph-display (glyph-display-program rlm@553: (+ (count init) (count header) rlm@553: start-address) rlm@553: 0 0) ;; ONLY FOR TESTING rlm@553: rlm@553: state-machine-start-address rlm@553: (+ start-address (count init) (count header) (count glyph-display)) rlm@553: state-machine rlm@553: (bootstrap-state-machine state-machine-start-address) rlm@553: rlm@553: return-to-header rlm@553: (flatten rlm@553: [0x18 rlm@553: (->signed-8-bit rlm@553: (- (count init) rlm@553: 2 ;; this command length rlm@553: 3 ;; I have no idea why we need a 3 here rlm@553: ;; need to investigate. rlm@553: (count glyph-display) rlm@553: (count header) rlm@553: (count state-machine)))])] rlm@553: rlm@553: (concat init glyph-display header state-machine return-to-header))) rlm@553: rlm@551: (def main-program-base-address 0xC000) rlm@551: rlm@554: (defn begin-glyph-bootstrap rlm@554: ([] (begin-glyph-bootstrap (launch-main-bootstrap-program))) rlm@554: ([script] rlm@554: (let [glyph-init (glyph-init-program relocated-bootstrap-start) rlm@554: main-glyph-start (+ relocated-bootstrap-start rlm@554: (count glyph-init)) rlm@554: glyph-program (glyph-bootstrap-program rlm@554: main-glyph-start 0 0)] rlm@554: (->> script rlm@554: (do-nothing 2) rlm@554: ;; begin glyph program rlm@554: (write-RAM 0xFF1A [0 0 0]) ;; silence remnant music rlm@554: rlm@554: (write-RAM rlm@554: relocated-bootstrap-start rlm@554: (concat glyph-init glyph-program)) rlm@554: (transfer-control relocated-bootstrap-start) rlm@555: (do-nothing 1) rlm@553: rlm@554: )))) rlm@553: rlm@551: (defn write-all-program-data rlm@554: ([] (write-all-program-data (begin-glyph-bootstrap))) rlm@551: ([script] rlm@551: (let [base-address main-program-base-address] rlm@551: (->> script rlm@551: (write-RAM base-address (program-data base-address)))))) rlm@551: rlm@551: (defn activate-program rlm@551: ([] (activate-program (write-all-program-data))) rlm@551: ([script] rlm@551: (->> script rlm@551: (transfer-control main-program-base-address) rlm@554: ;;(do-nothing 1800) rlm@554: (do-nothing 50) rlm@554: ))) rlm@552: rlm@552: rlm@552: ;; possible screen writing programs rlm@552: rlm@552: ;; (program needs to stop executing at some point) rlm@552: ;; maybe have total length counter or something? rlm@552: rlm@552: ;; automatic counter that reads from program-start and clears the rlm@552: ;; screen every 360 (* 18 20) gliphs rlm@552: rlm@552: ;; advantages -- very simple and low bandwidth rlm@552: ;; disadvantages -- hard to align counter rlm@552: rlm@552: ;; implementation -- refactor main-bootstrap-program to provide a rlm@552: ;; state-machine code-section which can be recombined into another rlm@552: ;; program.