(local util (require :lib.util)) (local {: lo : hi : readjson} util) (local lume (require :lib.lume)) (local asm (require :asm.asm)) (local VM (require :asm.vm)) (local tiles (require :game.tiles)) (local files (require :game.files)) (local Prodos (require :asm.prodos)) (local actions (require :editor.actions)) (local prg (asm.new)) (local vm (VM.new prg {:org 0xc00})) (Prodos.install-words vm) (local org { :boot vm.code :code (prg:org 0x4000) }) (local mapw 20) (local maph 12) (local mon { :hexout :0xfdda :putchar :0xfded :bell :0xff3a }) (local style { :normal 0x80 :inverse 0x00 :flashing 0x40 }) (fn str-with-style [s stylebits] (-> [(string.byte s 1 -1)] (lume.map #(bit.bor (bit.band $1 0x3f) stylebits)) (-> (table.unpack) (string.char)))) (fn achar [c] (bit.bor (string.byte c) style.normal)) (fn astr [s ?style] (str-with-style s (or ?style style.normal))) (fn rot8l [n] ; clears carry (local block [:block [:clc]]) (for [_ 1 n] (table.insert block [:block [:asl :a] [:adc 0]])) block) ; core graphics words needed for booting (vm:def :hires [:sta :0xc050] [:sta :0xc057] [:sta :0xc052] [:sta :0xc054]) (vm:def :cleargfx (vm:push 0x4000) [:block :page [:dec vm.TOPH :x] [:lda 0] [:block :start [:sta [vm.TOP :x]] [:inc vm.TOP :x] [:bne :start]] [:lda vm.TOPH :x] [:cmp 0x20] [:bne :page]] (vm:drop)) ; a handful of debugging words (vm:def :. [:lda vm.TOPH :x] [:jsr mon.hexout] [:lda vm.TOP :x] [:jsr mon.hexout] [:lda (achar " ")] [:jsr mon.putchar] (vm:drop)) (vm:def :stacklen (vm:reserve) [:txa] [:lsr :a] [:sta vm.TOP :x] [:lda 0] [:sta vm.TOPH :x]) (vm:word :.s :stacklen (prg:parse-addr vm.TOP) :swap (vm:for :dup :get :. :inc :inc) :drop) ; input words (vm:def :last-key ; -- key (vm:reserve) [:lda :0xc000] [:and 0x7f] [:sta vm.TOP :x] [:lda 0] [:sta vm.TOPH :x]) (vm:def :read-key ; -- key|0 [:block (vm:reserve) [:lda :0xc000] [:bmi :key-pressed] [:lda 0] [:sta vm.TOP :x] [:sta vm.TOPH :x] (vm:ret) :key-pressed [:and 0x7f] [:sta vm.TOP :x] [:lda 0] [:sta vm.TOPH :x] [:sta :0xc010]]) ; "random" numbers ; this is used only for cosmetic purposes and short noise generation, so we can get away ; with just including a short table of random digits rather than implementing our own ; pseudorandom number generator (var randombytes "") (for [i 0 0x40] (set randombytes (.. randombytes (string.char (math.random 0 255))))) (vm.code:append :randombytes [:bytes randombytes]) (vm:var :irandom [:db 0]) (vm:word :rnd :irandom :bget :dup 1 :+ 0x3f :& :irandom :bset :lit :randombytes :+ :bget) ; 20x12 means full map is 240 bytes - we have an extra 16 bytes at the end for metadata (fn append-map [map org label] (org:append [:align 0x100] label [:bytes map.map] [:db (length map.objects)] [:dw (tiles.encode-yx map.player)] [:jmp (if (= (or map.tickword "") "") :next map.tickword)] [:jmp (if (= (or map.moveword "") "") :move-noop map.moveword)] [:jmp (if (= (or map.loadword "") "") :next map.loadword)])) (vm.code:append :map-ptr [:db 0] :map-page [:db 0]) (vm:word :map :lit :map-ptr :get) (vm:word :entity-count :map 240 :+ :bget) (vm:word :map-player-yx-ptr 241 :+) (vm:word :map-player-yx :map :map-player-yx-ptr :get) (vm:word :map-specific-tick :map 243 :+ :execute) (vm:word :map-specific-move :map 246 :+ :execute) (vm:word :map-specific-load :map 249 :+ :execute) (fn generate-entity-code [level vm prefix] (each [ientity entity (ipairs level.objects)] (when (not entity.advanced) (let [code []] (each [iaction action (ipairs (or entity.steps []))] (if action.condition (lume.push code (.. :cond- action.condition) (vm:when (actions.generate action vm iaction))) (lume.push code (actions.generate action vm iaction)))) (vm:word (.. prefix ientity) :drop (table.unpack code)))))) (fn deflevel [ilevel label] (local level prg) ; todo: (asm.new prg) - if we want to load levels as an overlay (local org level.vm.code) ; (level:org org.level.org) - if we want to give level data a stable loxation (local map (. files.game.levels ilevel)) (local entity (require :game.entity)) (append-map map org label) (entity.append-from-map map org label) (set level.vm.code org) (generate-entity-code map level.vm (.. label "-entity-word-")) level) (fn say-runon [portrait ...] (local result [:vm (.. :draw-portrait- portrait)]) (local lines [...]) (local ilineOffset (if (< (length lines) 4) 1 0)) (each [iline line (ipairs lines)] (table.insert result [:vm (vm:str line) (.. :draw-text (+ iline ilineOffset))])) result) (fn say [portrait ...] (local result (say-runon portrait ...)) (table.insert result :dismiss-dialog) result) (fn itile [label] (tiles.find-itile files.game.tiles label)) (set vm.code org.code) {: vm : prg : mapw : maph : mon : org : achar : astr : style : rot8l : deflevel : say : say-runon : itile}