├── .gitattributes ├── .gitignore ├── .gitmodules ├── .travis.yml ├── Makefile.am ├── README.md ├── bootstrap ├── build.config ├── carts ├── Makefile.am ├── dickwave.p8 ├── dickwave.p8.png ├── launcher.p8 ├── rgbplasma.p8 ├── rgbplasma.p8.png ├── rulez.p8 ├── rulez.p8.png ├── shmup.p8 ├── tunnel.p8 ├── tunnel.p8.png ├── tut.p8 ├── tut.p8.png ├── zepto.p8 ├── zepto8.p8.png ├── zepton.p8 └── zepton.p8.png ├── configure.ac ├── doc ├── PICO-8-language.md ├── ports.md ├── z8tool.md └── zepto8.md ├── src ├── .gitignore ├── Makefile.am ├── analyzer.h ├── bindings │ ├── js.h │ └── lua.h ├── bios.cpp ├── bios.h ├── compress.cpp ├── compress.h ├── config_app.h ├── data │ ├── blank.png │ ├── jingle.p8 │ ├── makefont.p8 │ ├── zepto8.sfd │ └── zepto8.ttf ├── dither.cpp ├── dither.h ├── dummy.cpp ├── filter.cpp ├── filter.h ├── ide │ ├── ide.cpp │ ├── ide.h │ ├── memory-editor.cpp │ ├── memory-editor.h │ ├── text-editor.cpp │ └── text-editor.h ├── libquickjs.vcxproj ├── libretro-zepto8.vcxproj ├── libretro.cpp ├── libz8lua.vcxproj ├── libzepto8.vcxproj ├── libzepto8.vcxproj.filters ├── minify.cpp ├── minify.h ├── pico8 │ ├── api.cpp │ ├── ast.cpp │ ├── bios.p8 │ ├── cart.cpp │ ├── cart.h │ ├── code.cpp │ ├── gfx.cpp │ ├── grammar.h │ ├── memory.h │ ├── parser.cpp │ ├── pico8.h │ ├── private.cpp │ ├── render.cpp │ ├── sfx.cpp │ ├── vm.cpp │ └── vm.h ├── player.cpp ├── player.h ├── raccoon │ ├── api.cpp │ ├── font.h │ ├── memory.h │ ├── vm.cpp │ └── vm.h ├── splore.cpp ├── splore.h ├── synth.cpp ├── synth.h ├── telnet.h ├── template.html ├── textfile.cpp ├── textfile.h ├── unz8 ├── unz8.p8 ├── vm.cpp ├── z8dev.cpp ├── z8dev.vcxproj ├── z8dev.vcxproj.filters ├── z8lua.vcxproj ├── z8lua.vcxproj.filters ├── z8tool.cpp ├── z8tool.vcxproj ├── z8tool.vcxproj.filters ├── zepto8.cpp ├── zepto8.h ├── zepto8.vcxproj └── zlib │ ├── deflate.c │ ├── deflate.h │ ├── gz8.h │ ├── trees.c │ ├── trees.h │ ├── zconf.h │ ├── zlib.h │ └── zutil.h ├── t ├── Makefile.am ├── math-old.p8 ├── math.p8 ├── print.p8 ├── syntax-depicofier.lua ├── syntax.p8 └── tables.p8 ├── utils └── Makefile.am └── zepto8.sln /.gitattributes: -------------------------------------------------------------------------------- 1 | 2 | # Everything is text unless it isn’t. 3 | * text=auto 4 | 5 | # Source files are whatever the OS prefers but should be 6 | # normalised in the repository. 7 | *.c text 8 | *.cpp text 9 | *.cc text 10 | *.h text 11 | *.hh text 12 | *.lolfx text 13 | *.cs text 14 | 15 | # Vim doesn't like CRLF, even on Windows. 16 | # Same for autoconf/automake 17 | *.vim text eol=lf 18 | *.m4 text eol=lf 19 | *.ac text eol=lf 20 | *.in text eol=lf 21 | 22 | # This is Windows-specific and should always have CRLF, or 23 | # Visual Studio may misbehave. 24 | *.sln text eol=crlf 25 | *.csproj text eol=crlf 26 | *.vcxproj text eol=crlf 27 | *.resx text eol=crlf 28 | 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Our binaries 2 | zepto8 3 | z8dev 4 | z8lua 5 | z8tool 6 | # Autotools cruft 7 | *.o 8 | *.lo 9 | *.a 10 | *.la 11 | *.exe 12 | *.so 13 | *.elf 14 | *.self 15 | *.nexe 16 | *.userprefs 17 | *.usertasks 18 | *.pidb 19 | .auto 20 | .libs 21 | .deps 22 | .dirstamp 23 | .*.androiddir 24 | .*.androidstamp 25 | Makefile 26 | Makefile.in 27 | aclocal.m4 28 | autom4te.cache 29 | config.h.in 30 | config.h 31 | config.log 32 | config.status 33 | configure 34 | libtool 35 | stamp-* 36 | *-stamp 37 | test-suite.log 38 | # Emscripten cruft 39 | *.html 40 | *.html.mem 41 | *.js 42 | *.wasm 43 | *.wasm.map 44 | # Personal stuff 45 | patch-*.diff 46 | # Debugging cruft 47 | core 48 | !core/ 49 | core.* 50 | vgcore.* 51 | callgrind.out.* 52 | perf.data* 53 | *.gcda 54 | *.gcno 55 | # Editor cruft 56 | .*.swp 57 | *~ 58 | .ycm_extra_conf.pyc 59 | # Visual Studio cruft 60 | *.vcxproj.user 61 | *.csproj.user 62 | binaries/*Debug 63 | binaries/*Release 64 | .vs 65 | *.VC.VC.opendb 66 | *.VC.db 67 | *.sdf 68 | *.suo 69 | *.opensdf 70 | # ReSharper cruft 71 | _ReSharper.* 72 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lolengine"] 2 | path = src/3rdparty/lolengine 3 | url = https://github.com/lolengine/lolengine 4 | branch = master 5 | update = merge 6 | 7 | [submodule "wiki"] 8 | path = wiki 9 | url = https://github.com/samhocevar/zepto8.wiki 10 | branch = master 11 | update = merge 12 | 13 | [submodule "z8lua"] 14 | path = src/3rdparty/z8lua 15 | url = https://github.com/samhocevar/z8lua 16 | branch = zepto8 17 | update = merge 18 | upstream = https://github.com/lua/lua 19 | 20 | [submodule "imgui-club"] 21 | path = src/3rdparty/imgui-club 22 | url = https://github.com/samhocevar/fork-imgui-club 23 | branch = master 24 | update = merge 25 | upstream = https://github.com/ocornut/imgui_club 26 | 27 | [submodule "zep"] 28 | path = src/3rdparty/zep 29 | url = https://github.com/samhocevar/fork-zep 30 | branch = zepto8/experiments 31 | update = merge 32 | upstream = https://github.com/cmaughan/zep 33 | 34 | [submodule "quickjs"] 35 | path = src/3rdparty/quickjs 36 | url = https://github.com/samhocevar/fork-quickjs 37 | branch = zepto8 38 | update = merge 39 | upstream = https://github.com/horhof/quickjs 40 | 41 | [submodule "libretro-common"] 42 | path = src/3rdparty/libretro-common 43 | url = https://github.com/libretro/libretro-common 44 | branch = master 45 | update = merge 46 | 47 | [submodule "lodepng"] 48 | path = src/3rdparty/lodepng 49 | url = https://github.com/lvandeve/lodepng 50 | branch = master 51 | update = merge 52 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: trusty 3 | language: c++ 4 | env: VERBOSE=1 5 | 6 | addons: 7 | apt: 8 | packages: 9 | - build-essential 10 | - automake 11 | - autoconf 12 | - libtool 13 | - pkg-config 14 | - libsdl2-dev 15 | - libsdl2-image-dev 16 | - libsdl2-mixer-dev 17 | - libglew-dev 18 | # this can fix clang compilation 19 | - clang-3.8 20 | - libc++-dev 21 | 22 | before_install: 23 | - if [ "$CC" = "clang" ]; then export CC="clang-3.8"; fi 24 | - if [ "$CXX" = "clang++" ]; then export CXX="clang++-3.8 -stdlib=libc++"; fi 25 | - ./bootstrap 26 | 27 | os: 28 | - linux 29 | 30 | compiler: 31 | - gcc 32 | - clang 33 | 34 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | 2 | include $(top_srcdir)/src/3rdparty/lolengine/build/autotools/common.am 3 | 4 | ACLOCAL_AMFLAGS = -I src/3rdparty/lolengine/build/autotools/m4 5 | EXTRA_DIST += bootstrap 6 | 7 | SUBDIRS = src/3rdparty/lolengine src 8 | DIST_SUBDIRS = $(SUBDIRS) t utils carts 9 | 10 | test: check 11 | 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # zepto8 2 | Fantasy fantasy console emulator emulator 3 | 4 | This is all experimental. 5 | 6 | ## Documentation 7 | 8 | - [`zepto8`](doc/zepto8.md), the PICO-8 emulator 9 | - [`z8tool`](doc/z8tool.md), a utility toolkit 10 | - `z8dev` is an experimental IDE and does not work yet 11 | - the [PICO-8 language](doc/PICO-8-language.md) 12 | - [zepto8 ports to other platforms](doc/ports.md) 13 | 14 | ## See also 15 | 16 | - [`z8lua`](https://github.com/samhocevar/z8lua) 17 | - the [PICO-8](https://www.lexaloffle.com/pico-8.php) fantasy console 18 | -------------------------------------------------------------------------------- /bootstrap: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Clean up 4 | if [ -d .git/modules/lol ]; then 5 | echo "Warning: found old submodule directory .git/modules/lol" 6 | exit 1 7 | fi 8 | 9 | # Check that the repository is properly set up 10 | if [ ! -x "./src/3rdparty/lolengine/bootstrap" ]; then 11 | cat << EOF 12 | Error: cannot execute src/3rdparty/lolengine/bootstrap 13 | 14 | Did you configure the Lol Engine submodule? The following may help: 15 | 16 | git submodule update --init --recursive 17 | 18 | EOF 19 | exit 1 20 | fi 21 | 22 | # Bootstrap this project first, using the Lol Engine script 23 | ./src/3rdparty/lolengine/bootstrap 24 | 25 | # Then bootstrap Lol Engine itself 26 | (cd src/3rdparty/lolengine && ./bootstrap) 27 | 28 | -------------------------------------------------------------------------------- /build.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | no 7 | yes 8 | yes 9 | yes 10 | yes 11 | 12 | no 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /carts/Makefile.am: -------------------------------------------------------------------------------- 1 | 2 | include $(top_srcdir)/src/3rdparty/lolengine/build/autotools/common.am 3 | 4 | EXTRA_DIST += zepto8.p8.png \ 5 | tunnel.p8.png \ 6 | rulez.p8 rulez.p8.png \ 7 | tut.p8 tut.p8.png 8 | 9 | -------------------------------------------------------------------------------- /carts/dickwave.p8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samhocevar/zepto8/b2929a4fe31e5765ef66d7794edd11086b9638b4/carts/dickwave.p8.png -------------------------------------------------------------------------------- /carts/launcher.p8: -------------------------------------------------------------------------------- 1 | pico-8 cartridge // http://www.pico-8.com 2 | version 41 3 | __lua__ 4 | 5 | -------------- 6 | -----WIDE----- 7 | -------------- 8 | 9 | poke(0x5f36,1) 10 | 11 | _cam_x=0 12 | _cam_y=0 13 | 14 | _realcamera=camera 15 | function camera(_cx,_cy) 16 | _cam_x=_cx and _cx or 0 17 | _cam_y=_cy and _cy or 0 18 | _realcamera(_cam_x-64,_cam_y) 19 | end 20 | 21 | function _widedraw(_f,...) 22 | _realcamera(_cam_x-64,_cam_y) 23 | _f(...) 24 | _map_display(1) 25 | _realcamera(_cam_x+64,_cam_y) 26 | _f(...) 27 | _realcamera(_cam_x,_cam_y) 28 | _map_display(0) 29 | end 30 | 31 | _realcls=cls function cls(...)_widedraw(_realcls,...)end 32 | _realspr=spr function spr(...)_widedraw(_realspr,...)end 33 | _realsspr=sspr function sspr(...)_widedraw(_realsspr,...)end 34 | _realmap=map function map(...)_widedraw(_realmap,...)end 35 | _realrect=rect function rect(...)_widedraw(_realrect,...)end 36 | _realrectfill=rectfill function rectfill(...)_widedraw(_realrectfill,...)end 37 | _realcircfill=circfill function circfill(...)_widedraw(_realcircfill,...)end 38 | _realcirc=circ function circ(...)_widedraw(_realcirc,...)end 39 | _realprint=print function print(...)_widedraw(_realprint,...)end 40 | _realpset=pset function pset(...)_widedraw(_realpset,...)end 41 | _realline=line function line(...)_widedraw(_realline,...)end 42 | -------------- 43 | 44 | cartdata("z8_launcher_label") 45 | 46 | piledir={} 47 | curdir="" 48 | curlist={} 49 | meta_loaded=false 50 | 51 | function find(t,si,c) 52 | for i=si,#t do 53 | if t[i]==c then 54 | return i 55 | end 56 | end 57 | return -1 58 | end 59 | 60 | function find_end(t,si,c) 61 | for i=#t-si,1,-1 do 62 | if t[i]==c then 63 | return i 64 | end 65 | end 66 | return -1 67 | end 68 | 69 | function uplist() 70 | curdir="/" 71 | for i=1,#piledir do 72 | curdir..=piledir[i].."/" 73 | end 74 | curlist=dir(curdir) 75 | add(curlist,"../",1) 76 | sel=2 77 | end 78 | 79 | function get_start_dir() 80 | curlist={} 81 | local start=stat(124) 82 | local st=2 83 | while st<#start do 84 | local v=find(start,st,"/") 85 | if v<0 then 86 | v=#start+1 87 | end 88 | add(piledir,sub(start,st,v-1)) 89 | st=v+1 90 | end 91 | end 92 | 93 | get_start_dir() 94 | uplist() 95 | 96 | function load_meta() 97 | if not meta_loaded then 98 | memset(0,0,0x2000) 99 | if #curlist>0 and sel<=#curlist then 100 | local dd=curlist[sel] 101 | extcmd("z8_load_metadata "..curdir..dd) 102 | end 103 | meta_loaded = true 104 | end 105 | end 106 | 107 | function back_folder() 108 | if #piledir>0 and piledir[#piledir]!=".." then 109 | deli(piledir,#piledir) 110 | end 111 | end 112 | 113 | function _update() 114 | if btnp(2) then sel-=1 meta_loaded=false end 115 | if btnp(3) then sel+=1 meta_loaded=false end 116 | if #curlist>0 then 117 | sel=(sel-1)%#curlist+1 118 | else 119 | sel=1 120 | end 121 | if btnp(5) then 122 | meta_loaded=false 123 | back_folder() 124 | uplist() 125 | end 126 | if btnp(4) then 127 | if #curlist>0 and sel<=#curlist then 128 | meta_loaded=false 129 | local dd=curlist[sel] 130 | local isdir=dd[#dd]=="/" 131 | if isdir then 132 | if dd=="../" then 133 | back_folder() 134 | else 135 | add(piledir,sub(dd,1,#dd-1)) 136 | end 137 | uplist() 138 | else 139 | load(curdir..dd,"back to launcher") 140 | end 141 | end 142 | end 143 | end 144 | 145 | function bprint(t,x,y,c) 146 | print(t,x-1,y,1) 147 | print(t,x+1,y,1) 148 | print(t,x,y-1,1) 149 | print(t,x,y+1,1) 150 | print(t,x,y,c) 151 | end 152 | 153 | function _draw() 154 | load_meta() 155 | 156 | camera() 157 | cls(1) 158 | print(curdir,65,1,7) 159 | local base=min(max(sel-10,1),max(1,#curlist-18)) 160 | for i=base,min(base+19,#curlist) do 161 | local dd=curlist[i] 162 | local isdir=dd[#dd]=="/" 163 | local c=isdir and 4 or 13 164 | if(i==sel) c=isdir and 8 or 12 165 | print(dd,65,(i-base+1)*6+1,c) 166 | end 167 | 168 | camera(64,0) 169 | rectfill(0,0,127,127,0) 170 | spr(0,0,0,16,16) 171 | bprint(stat(130),4,4,7) 172 | bprint(stat(131),4,10,7) 173 | end 174 | __gfx__ 175 | 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 176 | 00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 177 | 00700700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 178 | 00077000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 179 | 00077000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 180 | 00700700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 181 | -------------------------------------------------------------------------------- /carts/rgbplasma.p8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samhocevar/zepto8/b2929a4fe31e5765ef66d7794edd11086b9638b4/carts/rgbplasma.p8.png -------------------------------------------------------------------------------- /carts/rulez.p8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samhocevar/zepto8/b2929a4fe31e5765ef66d7794edd11086b9638b4/carts/rulez.p8.png -------------------------------------------------------------------------------- /carts/shmup.p8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samhocevar/zepto8/b2929a4fe31e5765ef66d7794edd11086b9638b4/carts/shmup.p8 -------------------------------------------------------------------------------- /carts/tunnel.p8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samhocevar/zepto8/b2929a4fe31e5765ef66d7794edd11086b9638b4/carts/tunnel.p8.png -------------------------------------------------------------------------------- /carts/tut.p8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samhocevar/zepto8/b2929a4fe31e5765ef66d7794edd11086b9638b4/carts/tut.p8.png -------------------------------------------------------------------------------- /carts/zepto8.p8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samhocevar/zepto8/b2929a4fe31e5765ef66d7794edd11086b9638b4/carts/zepto8.p8.png -------------------------------------------------------------------------------- /carts/zepton.p8: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samhocevar/zepto8/b2929a4fe31e5765ef66d7794edd11086b9638b4/carts/zepton.p8 -------------------------------------------------------------------------------- /carts/zepton.p8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samhocevar/zepto8/b2929a4fe31e5765ef66d7794edd11086b9638b4/carts/zepton.p8.png -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | dnl 2 | dnl Configure script for a Lol Engine project 3 | dnl 4 | 5 | AC_INIT([zepto8], [0.0]) 6 | 7 | dnl 8 | dnl Standard autoconf setup and tools requirements 9 | dnl 10 | 11 | AC_PREREQ(2.69) 12 | AC_CONFIG_AUX_DIR(.auto) 13 | AC_CANONICAL_TARGET 14 | AM_INIT_AUTOMAKE([subdir-objects no-define tar-ustar silent-rules]) 15 | 16 | AC_PROG_CXX 17 | LT_INIT 18 | LT_LANG([C++]) 19 | 20 | AC_CONFIG_HEADERS([config.h]) 21 | 22 | AC_CONFIG_FILES( 23 | [Makefile 24 | src/Makefile 25 | t/Makefile 26 | utils/Makefile 27 | carts/Makefile 28 | ]) 29 | 30 | AC_CHECK_HEADERS(sys/select.h) 31 | 32 | ac_cv_have_readline=no 33 | AC_CHECK_LIB(readline, rl_callback_handler_install, [ac_cv_have_readline=yes]) 34 | AM_CONDITIONAL(HAVE_READLINE, test "${ac_cv_have_readline}" != "no") 35 | 36 | dnl 37 | dnl Inherit all Lol Engine checks 38 | dnl 39 | 40 | LOL_AC_SUBPROJECT(src/3rdparty/lolengine) 41 | 42 | dnl 43 | dnl Perform the actual commands 44 | dnl 45 | 46 | AC_OUTPUT 47 | 48 | -------------------------------------------------------------------------------- /doc/PICO-8-language.md: -------------------------------------------------------------------------------- 1 | ## Syntax 2 | 3 | PICO-8 uses Lua 5.2, however through various feature extensions its syntax has become closer to Lua 5.3. 4 | 5 | The following PICO-8 syntax is adapted from [the official Lua 5.3 manual](https://www.lua.org/manual/5.3/manual.html#9). 6 | 7 | ```ebnf 8 | chunk ::= block 9 | 10 | block ::= {stat} [retstat] 11 | 12 | stat ::= ";" | 13 | varlist "=" explist | 14 | var compop exp | 15 | functioncall | 16 | "?" [explist] | 17 | label | 18 | "break" | 19 | "goto" Name | 20 | "do" block "end" | 21 | "while" exp "do" block "end" | 22 | "while" "(" exp ")" block | 23 | "repeat" block "until" exp | 24 | "if" exp "then" block {"elseif" exp "then" block} ["else" block] "end" | 25 | "if" "(" exp ")" block ["else" block] | 26 | "for" Name "=" exp "," exp ["," exp] "do" block "end" | 27 | "for" namelist "in" explist "do" block "end" | 28 | "function" funcname funcbody | 29 | "local" "function" Name funcbody | 30 | "local" namelist ["=" explist] 31 | 32 | retstat ::= "return" [explist] [";"] 33 | 34 | label ::= "::" Name "::" 35 | 36 | funcname ::= Name {"." Name} [":" Name] 37 | 38 | varlist ::= var {"," var} 39 | 40 | var ::= Name | prefixexp "[" exp "]" | prefixexp "." Name 41 | 42 | namelist ::= Name {"," Name} 43 | 44 | explist ::= exp {"," exp} 45 | 46 | exp ::= "nil" | "false" | "true" | Numeral | LiteralString | "..." | functiondef | 47 | prefixexp | tableconstructor | exp binop exp | unop exp 48 | 49 | prefixexp ::= var | functioncall | "(" exp ")" 50 | 51 | functioncall ::= prefixexp args | prefixexp ":" Name args 52 | 53 | args ::= "(" [explist] ")" | tableconstructor | LiteralString 54 | 55 | functiondef ::= "function" funcbody 56 | 57 | funcbody ::= "(" [parlist] ")" block "end" 58 | 59 | parlist ::= namelist ["," "..."] | "..." 60 | 61 | tableconstructor ::= "{" [fieldlist] "}" 62 | 63 | fieldlist ::= field {fieldsep field} [fieldsep] 64 | 65 | field ::= "[" exp "]" "=" exp | Name "=" exp | exp 66 | 67 | fieldsep ::= "," | ";" 68 | 69 | binop ::= "+" | "-" | "*" | "/" | "^" | "%" | "\" | "^^" | 70 | "&" | "|" | ">>" | ">>>" | "<<" | ">><" | "<<>" | ".." | 71 | "<" | "<=" | ">" | ">=" | "==" | "~=" | "and" | "or" 72 | 73 | compop ::= "+=" | "-=" | "*=" | "/=" | "^=" | "%=" | "\=" | "^^=" | 74 | "&=" | "|=" | ">>=" | ">>>=" | "<<=" | ">><=" | "<<>=" | "..=" 75 | 76 | unop ::= "-" | "not" | "#" | "~" | "@" | "%” | "$" 77 | ``` 78 | -------------------------------------------------------------------------------- /doc/ports.md: -------------------------------------------------------------------------------- 1 | # zepto8 ports to other platforms 2 | 3 | ## OpenDingux, GCW0, RG350, … 4 | 5 | ### Build toolchain 6 | 7 | First you need to get a recent enough toolchain (GCC 8.x or clang) and its dependencies. I have had 8 | success with [rg350-buildroot](https://github.com/tonyjih/RG350_buildroot): 9 | 10 | ```sh 11 | sudo apt install python unzip bc gcc-multilib subversion mercurial rsync libncurses-dev 12 | ``` 13 | 14 | ```sh 15 | git clone https://github.com/tonyjih/RG350_buildroot ~/rg350 16 | cd ~/rg350 17 | make rg350_defconfig BR2_EXTERNAL=board/opendingux 18 | make menuconfig 19 | ``` 20 | 21 | Make sure a recent version of GCC is selected: _Toolchain_ → _GCC Compiler Version_ → _gcc 9.x_ 22 | then exit the configuration utility. 23 | 24 | Next, build the toolchain and the SDL libraries: 25 | 26 | ```sh 27 | export BR2_JLEVEL=0 28 | make toolchain 29 | make sdl2 sdl2_image sdl2_mixer 30 | ``` 31 | 32 | For some reason the toolchain needs to be patched in the following way: 33 | 34 | ```sh 35 | echo 'namespace std { using ::cbrt, ::round, ::exp2; }' \ 36 | >> ~/rg350/output/host/usr/mipsel-gcw0-linux-uclibc/include/c++/9.2.0/cmath 37 | ``` 38 | 39 | ### Build zepto8 40 | 41 | Assuming `rg350` is located in `${HOME}`: 42 | 43 | ```sh 44 | ./bootstrap 45 | 46 | BUILDROOT="${HOME}/rg350" 47 | HOSTCONF="mipsel-gcw0-linux-uclibc" 48 | TOOLCHAIN="${BUILDROOT}/output/host/usr/bin" 49 | SYSROOT="${BUILDROOT}/output/host/usr/${HOSTCONF}/sysroot" 50 | PKG_CONFIG_LIBDIR="${SYSROOT}/usr/lib/pkgconfig:${SYSROOT}/usr/share/pkgconfig" 51 | PKG_CONFIG_SYSROOT_DIR="$SYSROOT" 52 | ./configure --host="${HOSTCONF}" --with-sysroot="${SYSROOT}" \ 53 | CC="${TOOLCHAIN}/${HOSTCONF}-gcc" CXX="${TOOLCHAIN}/${HOSTCONF}-g++" \ 54 | CPPFLAGS="-I${SYSROOT}/usr/include" 55 | 56 | make -j 57 | ``` 58 | 59 | -------------------------------------------------------------------------------- /doc/z8tool.md: -------------------------------------------------------------------------------- 1 | # z8tool 2 | 3 | **z8tool** is a multi-purpose tool for working with PICO-8 cartridges. 4 | 5 | Usage: `z8tool ` 6 | 7 | ## `z8tool stats` 8 | 9 | Outputs statistics about a cart. 10 | 11 | Usage: 12 | 13 | z8tool stats 14 | 15 | Where `` is any cartridge in P8 (`.p8`), PNG (`.p8.png`), JavaScript (`.js`) or binary format (`.bin`). 16 | 17 | Example: 18 | 19 | ``` 20 | % z8tool stats celeste.p8 21 | file_name: celeste.p8 22 | token_count: 5921 [8192] 23 | code_size: 27484 [65535] 24 | compressed_code_size: 7903 [15616] 25 | 26 | % 27 | ``` 28 | 29 | ## `z8tool listlua` 30 | 31 | Extract code from a cart, in text format. 32 | 33 | Usage: 34 | 35 | z8tool listlua 36 | 37 | Where `` is any cartridge in P8 (`.p8`), PNG (`.p8.png`), JavaScript (`.js`) or binary format (`.bin`). 38 | 39 | ## `z8tool printast` 40 | 41 | Not fully implemented yet. 42 | 43 | ## `z8tool convert` 44 | 45 | Convert carts beetween formats: P8 (`.p8`), PNG (`.p8.png`), JavaScript (`.js`) or binary format (`.bin`). 46 | 47 | Usage: 48 | 49 | z8tool convert 50 | 51 | Examples: 52 | 53 | % z8tool convert celeste.p8.png celeste.p8 54 | % z8tool convert celeste.p8 other_celeste.p8.png 55 | % 56 | 57 | ## `z8tool run` 58 | 59 | Run a cart in the terminal. 60 | 61 | Usage: 62 | 63 | z8tool run [--telnet] [--headless] 64 | 65 | - `--telnet` emit telnet server commands, for use with socat 66 | - `--headless` run without displaying anything 67 | 68 | ## `z8tool dither` 69 | 70 | Not fully implemented yet. 71 | 72 | ## `z8tool compress` 73 | 74 | Not fully implemented yet. 75 | 76 | ## `z8tool test` 77 | 78 | Run the internal test suite. Not fully implemented yet. 79 | 80 | -------------------------------------------------------------------------------- /doc/zepto8.md: -------------------------------------------------------------------------------- 1 | # zepto8 2 | 3 | **zepto8** is a PICO-8 emulator. 4 | 5 | Usage: `zepto8 [] []` 6 | 7 | Plays a PICO-8 cartridge or run the emulator without a cart. 8 | 9 | You can also run the launcher cart to easily browse other carts in the same folder. 10 | Usage: `zepto8 -width 1280 -height 720 "launcher.p8"` 11 | 12 | - 13 | -------------------------------------------------------------------------------- /src/.gitignore: -------------------------------------------------------------------------------- 1 | # Autotools cruft 2 | 3rdparty/z8lua/.deps 3 | 3rdparty/z8lua/.dirstamp 4 | -------------------------------------------------------------------------------- /src/analyzer.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2016—2020 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #pragma once 14 | 15 | #include 16 | 17 | // The analyzer class 18 | // —————————————————— 19 | // This class used to parse and rewrite the PICO-8 code and transcribe it to 20 | // regular Lua code. Now that we use z8lua instead of Lua, this is no longer 21 | // required, and the fix() function just adds some backwards compatibility 22 | // glue code to the source. 23 | 24 | namespace z8 25 | { 26 | 27 | class analyzer 28 | { 29 | public: 30 | std::string fix(std::string const &str); 31 | 32 | int m_disable_crlf = 0; 33 | }; 34 | 35 | } 36 | 37 | -------------------------------------------------------------------------------- /src/bindings/js.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2016—2020 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #if HAVE_CONFIG_H 14 | # include "config.h" 15 | #endif 16 | 17 | #include 18 | 19 | extern "C" { 20 | #include "3rdparty/quickjs/quickjs.h" 21 | } 22 | 23 | #include "zepto8.h" 24 | 25 | namespace z8::bindings 26 | { 27 | 28 | // 29 | // Convert a standard type to a JSValue 30 | // 31 | 32 | static JSValue js_box(JSContext *ctx, bool x) { return JS_NewBool(ctx, (int)x); } 33 | static JSValue js_box(JSContext *ctx, int x) { return JS_NewInt32(ctx, x); } 34 | static JSValue js_box(JSContext *ctx, double x) { return JS_NewFloat64(ctx, x); } 35 | static JSValue js_box(JSContext* ctx, std::string s) { return JS_NewStringLen(ctx, s.c_str(), s.length()); } 36 | 37 | // 38 | // Convert a JSValue to a standard type 39 | // 40 | 41 | template static void js_unbox(JSContext *ctx, T &, JSValueConst jsval); 42 | 43 | template<> void js_unbox(JSContext *ctx, int &arg, JSValueConst jsval) { JS_ToInt32(ctx, &arg, jsval); } 44 | template<> void js_unbox(JSContext *ctx, double &arg, JSValueConst jsval) { JS_ToFloat64(ctx, &arg, jsval); } 45 | template<> void js_unbox(JSContext *ctx, std::string &str, JSValueConst jsval) 46 | { 47 | char const *data = JS_ToCString(ctx, jsval); 48 | str = std::string(data); 49 | JS_FreeCString(ctx, data); 50 | } 51 | 52 | // Unboxing to std::optional always unboxes 53 | template void js_unbox(JSContext *ctx, std::optional &arg, JSValueConst jsval) 54 | { 55 | js_unbox(ctx, *(arg = T()), jsval); 56 | } 57 | 58 | template static T js_unbox(JSContext *ctx, JSValueConst jsval) 59 | { 60 | T ret; js_unbox(ctx, ret, jsval); return ret; 61 | } 62 | 63 | // 64 | // JavaScript binding mechanism 65 | // 66 | 67 | class js 68 | { 69 | public: 70 | template 71 | static void init(JSContext *ctx, T *that) 72 | { 73 | JS_SetContextOpaque(ctx, that); 74 | 75 | // FIXME: this should not be static, store it in "that" instead! 76 | static auto lib = typename T::template exported_api().data; 77 | 78 | // Add functions to global scope 79 | auto global_obj = JS_GetGlobalObject(ctx); 80 | JS_SetPropertyFunctionList(ctx, global_obj, lib.data(), (int)lib.size()); 81 | JS_FreeValue(ctx, global_obj); 82 | } 83 | 84 | // Helper to dispatch C++ functions to JavaScript C bindings 85 | template struct bind 86 | { 87 | static JSValue wrap(JSContext *ctx, JSValueConst, 88 | int argc, JSValueConst *argv) 89 | { 90 | return dispatch(ctx, argc, argv, FN, make_seq(FN)); 91 | } 92 | 93 | size_t size = make_seq(FN).size(); 94 | 95 | // Create an index sequence from a member function’s signature 96 | template 97 | static constexpr auto make_seq(R (T::*)(A...)) 98 | { 99 | return std::index_sequence_for(); 100 | } 101 | }; 102 | 103 | struct bind_desc : JSCFunctionListEntry 104 | { 105 | template 106 | bind_desc(char const *str, bind b) 107 | : JSCFunctionListEntry({ str, 108 | JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE, JS_DEF_CFUNC, 0, 109 | { { (uint8_t)b.size, JS_CFUNC_generic, { &b.wrap } } } }) 110 | {} 111 | }; 112 | 113 | private: 114 | template 115 | static JSValue dispatch(JSContext *ctx, int argc, JSValueConst *argv, 116 | R (T::*f)(A...), std::index_sequence) 117 | { 118 | // Retrieve “this” from the JS context. 119 | T *that = (T *)JS_GetContextOpaque(ctx); 120 | 121 | // Call the API function with the loaded arguments. Some specialization 122 | // is needed when the wrapped function returns void. 123 | if constexpr (std::is_same::value) 124 | return (that->*f)(((int)IS < argc ? js_unbox(ctx, argv[IS]) : A())...), JS_UNDEFINED; 125 | else 126 | return js_box(ctx, (that->*f)(((int)IS < argc ? js_unbox(ctx, argv[IS]) : A())...)); 127 | } 128 | }; 129 | 130 | } // namespace z8::bindings 131 | 132 | -------------------------------------------------------------------------------- /src/bindings/lua.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2016—2020 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #if HAVE_CONFIG_H 14 | # include "config.h" 15 | #endif 16 | 17 | #include 18 | #include 19 | 20 | #include "3rdparty/z8lua/lua.h" 21 | #include "3rdparty/z8lua/lauxlib.h" 22 | #include "3rdparty/z8lua/lualib.h" 23 | 24 | namespace z8::bindings 25 | { 26 | 27 | // 28 | // Push a standard type to the Lua stack 29 | // 30 | 31 | template static int lua_push(lua_State *l, T const &); 32 | 33 | template<> int lua_push(lua_State *l, bool const &x) { lua_pushboolean(l, x); return 1; } 34 | template<> int lua_push(lua_State *l, uint8_t const &x) { lua_pushnumber(l, x); return 1; } 35 | template<> int lua_push(lua_State *l, int16_t const &x) { lua_pushnumber(l, x); return 1; } 36 | template<> int lua_push(lua_State *l, fix32 const &x) { lua_pushnumber(l, x); return 1; } 37 | template<> int lua_push(lua_State *l, std::string const &s) { lua_pushlstring(l, s.c_str(), (int)s.size()); return 1; } 38 | template<> int lua_push(lua_State *l, std::nullptr_t const &) { lua_pushnil(l); return 1; } 39 | 40 | // Boxing an std::variant pushes the active alternative 41 | template int lua_push(lua_State *l, std::variant const &x) 42 | { 43 | return std::visit([l](auto&& arg) -> int { return lua_push(l, arg); }, x); 44 | } 45 | 46 | // Boxing an std::optional returns 0 or 1 depending on whether there is an object 47 | template int lua_push(lua_State *l, std::optional const &x) 48 | { 49 | return x ? lua_push(l, *x) : 0; 50 | } 51 | 52 | // Boxing an std::tuple pushes each value 53 | template int lua_push(lua_State *l, std::tuple const &t) 54 | { 55 | std::apply([l](auto&&... x){ ((lua_push(l, x)), ...); }, t); 56 | return (int)sizeof...(T); 57 | } 58 | 59 | // Boxing an std::vector pushes each value 60 | template int lua_push(lua_State *l, std::vector const &v) 61 | { 62 | for (auto &x : v) 63 | lua_push(l, x); 64 | return (int)v.size(); 65 | } 66 | 67 | // 68 | // Get a standard type from the Lua stack 69 | // 70 | 71 | template static T lua_get(lua_State *l, int i); 72 | template static void lua_get(lua_State *l, int i, T &); 73 | 74 | template<> void lua_get(lua_State *l, int i, fix32 &arg) { arg = lua_tonumber(l, i); } 75 | template<> void lua_get(lua_State *l, int i, bool &arg) { arg = (bool)lua_toboolean(l, i); } 76 | template<> void lua_get(lua_State *l, int i, uint8_t &arg) { arg = (uint8_t)lua_tonumber(l, i); } 77 | template<> void lua_get(lua_State *l, int i, int16_t &arg) { arg = (int16_t)lua_tonumber(l, i); } 78 | 79 | // Unboxing to std::string ensures that inline \0s are preserved 80 | template<> void lua_get(lua_State *l, int n, std::string &arg) 81 | { 82 | if (lua_isstring(l, n)) 83 | { 84 | size_t len; 85 | char const *s = lua_tolstring(l, n, &len); 86 | arg.assign(s, len); 87 | } 88 | } 89 | 90 | // Unboxing to std::optional checks for lua_isnone() first 91 | template void lua_get(lua_State *l, int i, std::optional &arg) 92 | { 93 | if (!lua_isnone(l, i)) 94 | arg = lua_get(l, i); 95 | } 96 | 97 | // Unboxing to std::vector unboxes the whole stack 98 | template void lua_get(lua_State *l, int i, std::vector &arg) 99 | { 100 | while (!lua_isnone(l, i)) 101 | arg.push_back(lua_get(l, i++)); 102 | } 103 | 104 | template static T lua_get(lua_State *l, int i) 105 | { 106 | T ret; lua_get(l, i, ret); return ret; 107 | } 108 | 109 | // 110 | // Lua binding mechanism 111 | // 112 | 113 | class lua 114 | { 115 | public: 116 | template 117 | static void init(lua_State *l, T *that) 118 | { 119 | // Store a pointer to the caller in the global state 120 | #if HAVE_LUA_GETEXTRASPACE 121 | *static_cast(lua_getextraspace(l)) = that; 122 | #else 123 | lua_pushlightuserdata(l, that); 124 | lua_setglobal(l, "\x01"); 125 | #endif 126 | 127 | auto lib = typename T::template exported_api().data; 128 | lib.push_back({}); 129 | 130 | lua_pushglobaltable(l); 131 | luaL_setfuncs(l, lib.data(), 0); 132 | } 133 | 134 | // Helper to dispatch C++ functions to Lua C bindings 135 | template struct bind 136 | { 137 | static int wrap(lua_State *l) 138 | { 139 | return dispatch(l, FN, make_seq(FN)); 140 | } 141 | 142 | // Create an index sequence from a member function’s signature 143 | template 144 | static constexpr auto make_seq(R (T::*)(A...)) 145 | { 146 | return std::index_sequence_for(); 147 | } 148 | }; 149 | 150 | struct bind_desc : luaL_Reg 151 | { 152 | bind_desc() 153 | : luaL_Reg({ nullptr, nullptr }) 154 | {} 155 | 156 | template 157 | bind_desc(char const *str, bind b) 158 | : luaL_Reg({ str, &b.wrap }) 159 | {} 160 | }; 161 | 162 | private: 163 | // Call a T::* member function with arguments pulled from the Lua stack, 164 | // and push the result to the Lua stack. 165 | template 166 | static inline int dispatch(lua_State *l, R (T::*f)(A...), 167 | std::index_sequence) 168 | { 169 | // Retrieve “this” from the Lua state. 170 | #if HAVE_LUA_GETEXTRASPACE 171 | T *that = *static_cast(lua_getextraspace(l)); 172 | #else 173 | lua_getglobal(l, "\x01"); 174 | T *that = (T *)lua_touserdata(l, -1); 175 | lua_remove(l, -1); 176 | #endif 177 | 178 | // Store this for API functions that we don’t know yet how to wrap 179 | that->m_sandbox_lua = l; 180 | 181 | // Call the API function with the loaded arguments. Some specialization 182 | // is needed when the wrapped function returns void. 183 | if constexpr (std::is_same::value) 184 | return (that->*f)(lua_get(l, IS + 1)...), 0; 185 | else 186 | return lua_push(l, (that->*f)(lua_get(l, IS + 1)...)); 187 | } 188 | }; 189 | 190 | } // namespace z8::bindings 191 | 192 | -------------------------------------------------------------------------------- /src/bios.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2016—2020 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #if HAVE_CONFIG_H 14 | # include "config.h" 15 | #endif 16 | 17 | #include // lol::msg 18 | 19 | #include "bios.h" 20 | 21 | namespace z8::pico8 22 | { 23 | 24 | bios::bios() 25 | { 26 | char const *filename = "pico8/bios.p8"; 27 | 28 | // Initialize BIOS 29 | if (!m_cart.load(filename)) 30 | lol::msg::error("unable to load BIOS file %s\n", filename); 31 | } 32 | 33 | } // namespace z8 34 | 35 | -------------------------------------------------------------------------------- /src/bios.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2016—2020 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #pragma once 14 | 15 | #include "pico8/cart.h" 16 | 17 | // The bios class 18 | // —————————————— 19 | // The actual ZEPTO-8 BIOS: contains the font and the startup code, loaded 20 | // from a regular .p8 cartridge file. 21 | 22 | namespace z8::pico8 23 | { 24 | 25 | class bios 26 | { 27 | public: 28 | bios(); 29 | 30 | std::string const &get_code() const 31 | { 32 | return m_cart.get_code(); 33 | } 34 | 35 | uint8_t get_spixel(int16_t x, int16_t y) const 36 | { 37 | if (x < 0 || x >= 128 || y < 0 || y >= 128) 38 | return 0; 39 | 40 | return m_cart.get_rom().gfx.get(x, y); 41 | } 42 | 43 | private: 44 | cart m_cart; 45 | }; 46 | 47 | } // namespace z8 48 | 49 | -------------------------------------------------------------------------------- /src/compress.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2017—2020 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #if HAVE_CONFIG_H 14 | # include "config.h" 15 | #endif 16 | 17 | #include "compress.h" 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | extern "C" { 28 | #define z_errmsg z_errmsg_gz8 // avoid conflicts with the real zlib 29 | #define register /**/ 30 | #define adler32_gz8(...) 0 // not implemented 31 | #include "gz8.h" 32 | #include "zutil.h" 33 | #include "zlib/deflate.c" 34 | #include "zlib/trees.c" 35 | #include "zlib.h" 36 | extern z_const char * const z_errmsg[] = {}; 37 | #undef compress 38 | } 39 | 40 | namespace z8 41 | { 42 | 43 | std::string encode49(std::vector const &v) 44 | { 45 | char chr = '#'; 46 | int const n = 28; // bits in a chunk 47 | int const p = 49; // alphabet size 48 | 49 | std::string ret; 50 | 51 | // Read all the data 52 | for (size_t pos = 0; pos < v.size() * 8; pos += n) 53 | { 54 | // Read a group of n bits 55 | uint64_t val = 0; 56 | for (size_t i = pos / 8; i <= (pos + n - 1) / 8 && i < v.size(); ++i) 57 | val |= (uint64_t)v[i] << ((i - pos / 8) * 8); 58 | val = (val >> (pos % 8)) & (((uint64_t)1 << n) - 1); 59 | 60 | // Convert those n bits to a string 61 | for (uint64_t k = p; k < 2 << n; k *= p) 62 | { 63 | ret += char(chr + val % p); 64 | val /= p; 65 | } 66 | } 67 | 68 | // Remove trailing zeroes 69 | while (ret.size() && ret.back() == chr) 70 | ret.erase(ret.end() - 1); 71 | 72 | return '"' + ret + '"'; 73 | } 74 | 75 | std::vector compress(std::vector &input) 76 | { 77 | // Prepare a vector twice as big... we don't really care. 78 | std::vector output(input.size() * 2 + 10); 79 | 80 | z_stream zs = {}; 81 | zs.zalloc = [](void *, unsigned int n, unsigned int m) -> void * { return new char[n * m]; }; 82 | zs.zfree = [](void *, void *p) -> void { delete[] (char *)p; }; 83 | zs.next_in = input.data(); 84 | zs.next_out = output.data(); 85 | zs.avail_in = (uInt)input.size(); 86 | zs.avail_out = (uInt)output.size(); 87 | 88 | deflateInit(&zs, Z_BEST_COMPRESSION); 89 | deflate(&zs, Z_FINISH); 90 | // Strip first 2 bytes (deflate header) and last 4 bytes (checksum) 91 | output = std::vector(output.begin() + 2, output.begin() + zs.total_out - 4); 92 | deflateEnd(&zs); 93 | 94 | return output; 95 | } 96 | 97 | } // namespace z8 98 | 99 | -------------------------------------------------------------------------------- /src/compress.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2017—2018 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #pragma once 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | namespace z8 20 | { 21 | 22 | std::vector compress(std::vector &input); 23 | std::string encode49(std::vector const &v); 24 | 25 | } // namespace z8 26 | 27 | -------------------------------------------------------------------------------- /src/config_app.h: -------------------------------------------------------------------------------- 1 | // Nothing here — but this file is required by the Zep headers. 2 | -------------------------------------------------------------------------------- /src/data/blank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samhocevar/zepto8/b2929a4fe31e5765ef66d7794edd11086b9638b4/src/data/blank.png -------------------------------------------------------------------------------- /src/data/jingle.p8: -------------------------------------------------------------------------------- 1 | pico-8 cartridge // http://www.pico-8.com 2 | version 16 3 | __lua__ 4 | 5 | __sfx__ 6 | 000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 7 | 010200001a5001a5001a5001a50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 8 | 001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 9 | 001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 10 | 001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 11 | 001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 12 | 001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 13 | 001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 14 | 001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 15 | 001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 16 | 001000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 17 | 011000001353013500135501555015550185501855017550175501a5501a5501c5501854018530005000050000000000000000000000000000000000000000000000000000000000000000000000000000000000 18 | 011000002074020750187001e7501c750187002075020750187001e7501c7501c7401c73000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 19 | 01100000197401c7501c750197502375023750207501c7501c7401800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 20 | 011000001a5301c5401e5501e5501555000500155500050015550175501a5501a5401a53000700007000070000700000000000000000000000000000000000000000000000000000000000000000000000000000 21 | 011000001d550005001a5501755000500135500050018550185501855018550000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 22 | 0110000010574105721057210572105720b5720b5720b5720b5720b5720b5720b5751a0001a0001a0001a00000000000000000000000000000000000000000000000000000000000000000000000000000000000 23 | 0110000010574125721157214572135721657215572185721757219572185721b5721a5721a5721a5721a5751a0021a0021a0051f0001f0000000000000000000000000000000000000000000000000000000000 24 | 011000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 25 | -------------------------------------------------------------------------------- /src/data/makefont.p8: -------------------------------------------------------------------------------- 1 | pico-8 cartridge // http://www.pico-8.com 2 | version 18 3 | __lua__ 4 | 5 | local mul = 200 6 | local glyphs = 0 7 | 8 | local fillc = 0 9 | local bg = 1 10 | local fg = 7 11 | 12 | -- override printh 13 | old_printh, buffer, printh = printh, "", function(s) 14 | buffer = buffer..s..'\n' 15 | end 16 | 17 | function _init() 18 | -- will be done at the end 19 | old_printh(buffer, 'font.sfd', true) 20 | cursor(0, 64) 21 | color(7) 22 | print('font written to file.') 23 | end 24 | 25 | fe0f = { 26 | { 11015, '002b07' }, 27 | { 11013, '002b05' }, 28 | { '127358', '01f17e' }, 29 | { 10145, '0027a1' }, 30 | { 11014, '002b06' }, 31 | } 32 | 33 | function fe0f_id(n) 34 | for i=1,#fe0f do if fe0f[i][1]==n then return i end end return 0 35 | end 36 | 37 | function codepoint(n) 38 | local lut = { 39 | 9646, 9632, 9633, 8281, 8280, 8214, 9664, 9654, 40 | 12300, 12301, 165, 8226, 12289, 12290, 12443, 12444, 41 | 9675, 42 | 9608, 9618, '128049', 11015, 9617, 10045, 9679, 9829, 43 | 9737, '50883', 8962, 11013, '128528', 9834, '127358', 9670, 44 | 8230, 10145, 9733, 10711, 11014, 711, 8743, 10062, 45 | 9636, 9637, 12354, 12356, 12358, 12360, 12362, 12363, 46 | 12365, 12367, 12369, 12371, 12373, 12375, 12377, 12379, 47 | 12381, 12383, 12385, 12388, 12390, 12392, 12394, 12395, 48 | 12396, 12397, 12398, 12399, 12402, 12405, 12408, 12411, 49 | 12414, 12415, 12416, 12417, 12418, 12420, 12422, 12424, 50 | 12425, 12426, 12427, 12428, 12429, 12431, 12434, 12435, 51 | 12387, 12419, 12421, 12423, 12450, 12452, 12454, 12456, 52 | 12458, 12459, 12461, 12463, 12465, 12467, 12469, 12471, 53 | 12473, 12475, 12477, 12479, 12481, 12484, 12486, 12488, 54 | 12490, 12491, 12492, 12493, 12494, 12495, 12498, 12501, 55 | 12504, 12507, 12510, 12511, 12512, 12513, 12514, 12516, 56 | 12518, 12520, 12521, 12522, 12523, 12524, 12525, 12527, 57 | 12530, 12531, 12483, 12515, 12517, 12519, 9692, 9693 58 | } 59 | return n<32 and lut[n-15] or n>126 and lut[n-110] or n 60 | end 61 | 62 | -- helper tables for path tracing 63 | local dx = { 1, 0, -1, 0, 1, 1, -1, -1, 1, 1, 0, 0 } 64 | local dy = { 0, 1, 0, -1, -1, 1, 1, -1, 0, 1, 1, 0 } 65 | 66 | -- flood fill 67 | function ffill(x,y,c) 68 | local c0 = pget(x,y) 69 | if c0==fillc or (c and c!=c0) then return end 70 | pset(x,y,fillc) 71 | for i=1,4 do ffill(x+dx[i],y+dy[i],c0) end 72 | end 73 | 74 | -- find bottom-left non-transparent char 75 | function finddot() 76 | for dx=0,8 do 77 | for dy=7,0,-1 do 78 | if pget(dx,dy)!=fillc then 79 | return dx,dy 80 | end 81 | end 82 | end 83 | end 84 | 85 | -- build envelope for current char 86 | function trace(sx,sy) 87 | local path, visited = { {sx,sy+1} }, {} 88 | local x,y,d = sx,sy,3 89 | repeat 90 | add(visited, {x,y}) 91 | add(visited, {x+dy[d+1],y-dx[d+1]}) 92 | local c = pget(x,y) 93 | local x1,y1 = x+dx[d+1],y+dy[d+1] 94 | local x2,y2 = x+dx[d+5],y+dy[d+5] 95 | local x3,y3 = x+dx[d+9],y+dy[d+9] 96 | if pget(x1,y1) != c then 97 | add(path,{x3,y3}) 98 | d = (d+1)%4 -- turn right 99 | elseif pget(x2,y2) == c then 100 | add(path,{x3,y3}) 101 | x,y,d = x2,y2,(d+3)%4 -- turn left 102 | else 103 | x,y = x1,y1 104 | end 105 | until x==sx and y==sy and d==3 106 | return path,visited 107 | end 108 | 109 | function output(path,rev) 110 | for i=1,#path do 111 | local p = path[rev and #path-i+1 or i] 112 | printh((i==1 and '' or ' ').. 113 | (p[1]*mul-mul).." ".. 114 | (6*mul-p[2]*mul).." ".. 115 | (i==1 and 'm' or 'l').." 1,".. 116 | ((i-1)%(#path-1))..",-1") 117 | end 118 | end 119 | 120 | function dochar(n) 121 | -- swap uppercase and lowercase 122 | local ch = chr(band(n,0xdf)>=0x41 and band(n,0xdf)<=0x5a and bxor(n,0x20) or n) 123 | local cpt = codepoint(n) 124 | local w = 4 + 4*flr(n/128) 125 | local h = 6 126 | doglyph(ch, 'g'..cpt, cpt, cpt, w, h) 127 | end 128 | 129 | dict = {} 130 | 131 | function doglyph(ch, name, enc1, enc2, w, h, ref, extra) 132 | printh('StartChar: '..name) 133 | printh('Encoding: '..enc1..' '..enc2..' '..glyphs) 134 | if extra then printh(extra) end 135 | printh('Width: '..(w*mul)) 136 | printh('VWidth: '..(6*mul)) 137 | printh('GlyphClass: 2\nFlags: W\nLayerCount: 2') 138 | dict[enc2] = glyphs 139 | glyphs += 1 140 | col = extra and 'ffddff' or ref and 'ffddbb' or w<=4 and 'ccffcc' or 'ccffff' 141 | 142 | if ref then 143 | printh('Fore\nRefer: '..(ref!=-1 and dict[ref] or 0)..' '..ref..' N 1 0 0 1 0 0 2') 144 | elseif ch then 145 | printh('Fore\nSplineSet') 146 | rectfill(0,0,8,6,bg) 147 | if #ch>0 then 148 | print(ch,1,1,fg) 149 | else 150 | fillp(0x5a5a.8) 151 | rect(1,1,7,5,fg) 152 | fillp() 153 | end 154 | ffill(0,0) 155 | while true do 156 | local sx,sy = finddot() 157 | if not sx then break end 158 | path,visited = trace(sx,sy) 159 | output(path,pget(sx,sy)!=fg) 160 | for _,p in pairs(visited) do 161 | ffill(p[1],p[2]) 162 | end 163 | end 164 | printh('EndSplineSet') 165 | end 166 | 167 | if fe0f_id(enc2)>0 then printh('Substitution2: "lut1-1" tmp'..fe0f_id(enc2)) end 168 | printh('Colour: '..col) 169 | printh('EndChar\n') 170 | end 171 | 172 | printh('SplineFontDB: 3.0') 173 | printh('FontName: Zepto-8\nFullName: ZEPTO-8\nFamilyName: Zepto-8\nWeight: Book') 174 | printh('Copyright: Sam Hocevar\n') 175 | printh('Version: 1.0\n') 176 | printh('ItalicAngle: 0') 177 | printh('UnderlinePosition: -200\nUnderlineWidth: 200') 178 | printh('Ascent: 1000\nDescent: 0') 179 | printh('LayerCount: 2\nLayer: 0 1 "Back" 1\nLayer: 1 1 "Fore" 0\n') 180 | printh('OS2Version: 2\nOS2_WeightWidthSlopeOnly: 0\nOS2_UseTypoMetrics: 0') 181 | printh('PfmFamily: 81') 182 | printh('TTFWeight: 400\nTTFWidth: 5') 183 | printh('LineGap: 0\nVLineGap: 0\nPanose: 0 0 4 0 0 0 0 0 0 0\n') 184 | printh('OS2TypoAscent: 1000\nOS2TypoAOffset: 0\nOS2TypoDescent: -200\nOS2TypoDOffset: 0\nOS2TypoLinegap: 0') 185 | printh('OS2WinAscent: 1000\nOS2WinAOffset: 0\nOS2WinDescent: 200\nOS2WinDOffset: 0') 186 | printh('HheadAscent: 1000\nHheadAOffset: 0\nHheadDescent: -200\nHheadDOffset: 0') 187 | printh('OS2SubXSize: 400\nOS2SubYSize: 400\nOS2SubXOff: 0\nOS2SubYOff: -200') 188 | printh('OS2SupXSize: 400\nOS2SupYSize: 400\nOS2SupXOff: 0\nOS2SupYOff: 400') 189 | printh('OS2StrikeYSize: 40\nOS2StrikeYPos: 160') 190 | printh('OS2CapHeight: 1000\nOS2XHeight: 800') 191 | printh([[Lookup: 1 0 0 "lut1" {"lut1-1"} [' RQD' ('DFLT' <'dflt'> 'hang' <'dflt'> 'kana' <'dflt'> 'latn' <'dflt'>)] ]]) 192 | printh('GaspTable: 1 65535 2 0') 193 | printh('Encoding: UnicodeFull') 194 | printh('DisplaySize: -48') 195 | printh('AntiAlias: 1') 196 | printh('FitToEm: 0') 197 | 198 | printh('BeginChars: 1114112 '..(256 - 16 + 3 + 2 * #fe0f)) 199 | 200 | -- the undefined glyph 201 | doglyph('', '.notdef', '65536', -1, 8, 6) -- undefined glyph 202 | 203 | -- all printable chars 204 | for n=16,255 do dochar(n) end 205 | 206 | -- handle the fact that we want u+2b07 to be an undefined glyph, 207 | -- but u+2b07 u+fe0f to be defined. we use a substitution table 208 | -- and the variation selector feature to achieve that. 209 | doglyph(nil, 'fe0f', '65039', '65039', 0, 6) -- u+fe0f, zero-width 210 | for i=1,#fe0f do 211 | doglyph(nil, 'tmp'..i, '655'..(36+i*2), -1, 4, 6, -1) 212 | doglyph(nil, 'var'..i, '655'..(37+i*2), -1, 8, 6, fe0f[i][1], 'AltUni2: '..fe0f[i][2]..'.00fe0f.0') 213 | end 214 | 215 | printh('EndChars\nEndSplineFont') 216 | 217 | -------------------------------------------------------------------------------- /src/data/zepto8.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/samhocevar/zepto8/b2929a4fe31e5765ef66d7794edd11086b9638b4/src/data/zepto8.ttf -------------------------------------------------------------------------------- /src/dither.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2017—2020 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #pragma once 14 | 15 | #include 16 | 17 | namespace z8 18 | { 19 | 20 | void dither(std::string const &src, std::string const &out, std::string const &palette, 21 | bool hicolor, bool error_diffusion); 22 | 23 | } // namespace z8 24 | 25 | -------------------------------------------------------------------------------- /src/dummy.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2016—2017 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #if HAVE_CONFIG_H 14 | # include "config.h" 15 | #endif 16 | 17 | int dummy = 0; 18 | 19 | -------------------------------------------------------------------------------- /src/filter.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2017–2024 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #include "filter.h" 14 | 15 | #include // lol::rand 16 | #include // std::fabs, std::fmod, std::sin 17 | 18 | namespace z8 19 | { 20 | 21 | filter::filter(type t, float freq, float q, float gain) 22 | { 23 | init(t, freq, q, gain); 24 | } 25 | 26 | void filter::init(type t, float freq, float q, float gain) 27 | { 28 | // Formulae are from https://www.w3.org/TR/audio-eq-cookbook/ 29 | float w0 = lol::F_TAU * freq / 22050; 30 | float cosw0 = std::cos(w0); 31 | float a = std::pow(10.0f, gain / 40); 32 | float alpha; 33 | if (t == type::lowshelf || t == type::highshelf) 34 | alpha = (std::sin(w0) / 2) * std::sqrt((a + 1 / a) * (1 / q - 1) + 2); 35 | else 36 | alpha = std::sin(w0) / (2 * q); 37 | 38 | float a0, a1, a2, b0, b1, b2; 39 | switch (t) 40 | { 41 | case type::lpf: 42 | a0 = 1 + alpha; 43 | a1 = -2 * cosw0; 44 | a2 = 1 - alpha; 45 | b0 = (1 - cosw0) / 2; 46 | b1 = (1 - cosw0); 47 | b2 = (1 - cosw0) / 2; 48 | break; 49 | case type::hpf: 50 | a0 = 1 + alpha; 51 | a1 = -2 * cosw0; 52 | a2 = 1 - alpha; 53 | b0 = (1 + cosw0) / 2; 54 | b1 = -(1 + cosw0); 55 | b2 = (1 + cosw0) / 2; 56 | break; 57 | case type::lowshelf: 58 | { 59 | float sqra = 2 * std::sqrt(a) * alpha; 60 | a0 = (a + 1) + (a - 1) * cosw0 + sqra; 61 | a1 = -2 * ((a - 1) + (a + 1) * cosw0); 62 | a2 = (a + 1) + (a - 1) * cosw0 - sqra; 63 | b0 = a * ((a + 1) - (a - 1) * cosw0 + sqra); 64 | b1 = 2 * a * ((a - 1) - (a + 1) * cosw0); 65 | b2 = a * ((a + 1) - (a - 1) * cosw0 - sqra); 66 | break; 67 | } 68 | case type::highshelf: 69 | { 70 | float sqra = 2 * std::sqrt(a) * alpha; 71 | a0 = (a + 1) - (a - 1) * cosw0 + sqra; 72 | a1 = 2 * ((a - 1) - (a + 1) * cosw0); 73 | a2 = (a + 1) - (a - 1) * cosw0 - sqra; 74 | b0 = a * ((a + 1) + (a - 1) * cosw0 + sqra); 75 | b1 = -2 * a * ((a - 1) + (a + 1) * cosw0); 76 | b2 = a * ((a + 1) + (a - 1) * cosw0 - sqra); 77 | break; 78 | } 79 | } 80 | c1 = b0 / a0; 81 | c2 = b1 / a0; 82 | c3 = b2 / a0; 83 | c4 = a1 / a0; 84 | c5 = a2 / a0; 85 | } 86 | 87 | float filter::run(float input) 88 | { 89 | float output = c1 * input + c2 * linput + c3 * llinput - c4 * loutput - c5 * lloutput; 90 | llinput = linput; 91 | linput = input; 92 | lloutput = loutput; 93 | loutput = output; 94 | 95 | return output; 96 | } 97 | 98 | } // namespace z8 99 | -------------------------------------------------------------------------------- /src/filter.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2016—2020 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #pragma once 14 | 15 | #include "zepto8.h" 16 | 17 | namespace z8 18 | { 19 | 20 | // 21 | // Biquad filter 22 | // 23 | 24 | class filter 25 | { 26 | public: 27 | enum class type 28 | { 29 | lpf, hpf, lowshelf, highshelf 30 | }; 31 | 32 | filter(type t, float freq, float q, float gain); 33 | 34 | void init(type t, float freq, float q, float gain); 35 | float run(float input); 36 | 37 | float c1, c2, c3, c4, c5; 38 | float linput = 0; 39 | float llinput = 0; 40 | float loutput = 0; 41 | float lloutput = 0; 42 | }; 43 | 44 | } // namespace z8 45 | 46 | -------------------------------------------------------------------------------- /src/ide/ide.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2016—2019 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #pragma once 14 | 15 | #include // lol::WorldEntity 16 | 17 | #include "zepto8.h" 18 | #include "player.h" 19 | #include "ide/text-editor.h" 20 | #include "ide/memory-editor.h" 21 | 22 | namespace z8 23 | { 24 | 25 | class ide : public lol::WorldEntity 26 | { 27 | public: 28 | ide(); 29 | virtual ~ide(); 30 | 31 | virtual bool init_draw() override; 32 | virtual void tick_game(float seconds) override; 33 | virtual void tick_draw(float seconds, lol::Scene &scene) override; 34 | virtual bool release_draw() override; 35 | 36 | void load(std::string const &name); 37 | 38 | private: 39 | void apply_scale(); 40 | 41 | void render_app(); 42 | void render_menu(); 43 | void render_toolbar(); 44 | void render_windows(); 45 | 46 | bool m_commands[5] = { false }; 47 | 48 | struct 49 | { 50 | bool player = true; 51 | bool code = true; 52 | bool maps = false; 53 | bool music = false; 54 | bool sprites = false; 55 | bool ram = true; 56 | bool rom = true; 57 | bool palette = true; 58 | } 59 | m_show; 60 | 61 | struct 62 | { 63 | ImGuiID root; 64 | ImGuiID main; 65 | ImGuiID bottom, bottom_left, bottom_right; 66 | } 67 | m_dock; 68 | 69 | int m_scale = 2; 70 | player *m_player; // FIXME: this reference should disappear because player is a lol::entity 71 | std::unique_ptr m_text_editor; 72 | std::unique_ptr m_ram_editor, m_rom_editor; 73 | 74 | std::shared_ptr m_screen, m_sprites; 75 | 76 | std::shared_ptr m_vm; 77 | std::map m_fonts; 78 | }; 79 | 80 | } // namespace z8 81 | 82 | -------------------------------------------------------------------------------- /src/ide/memory-editor.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2016—2020 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #if HAVE_CONFIG_H 14 | # include "config.h" 15 | #endif 16 | 17 | #include "zepto8.h" 18 | #include "memory-editor.h" 19 | #include "pico8/pico8.h" 20 | 21 | namespace z8 22 | { 23 | 24 | memory_editor::memory_editor() 25 | : m_area({ nullptr, 0 }) 26 | { 27 | m_editor.OptShowAscii = false; 28 | m_editor.OptUpperCaseHex = false; 29 | m_editor.OptShowOptions = false; 30 | } 31 | 32 | memory_editor::~memory_editor() 33 | { 34 | } 35 | 36 | void memory_editor::attach(std::tuple area) 37 | { 38 | m_area = area; 39 | } 40 | 41 | void memory_editor::render() 42 | { 43 | m_editor.DrawContents(std::get<0>(m_area), std::get<1>(m_area)); 44 | } 45 | 46 | } // namespace z8 47 | 48 | -------------------------------------------------------------------------------- /src/ide/memory-editor.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2016—2020 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #pragma once 14 | 15 | #include // for the ImGui headers and much more stuff 16 | #include "3rdparty/imgui-club/imgui_memory_editor/imgui_memory_editor.h" 17 | 18 | namespace z8 19 | { 20 | 21 | class memory_editor 22 | { 23 | public: 24 | memory_editor(); 25 | ~memory_editor(); 26 | 27 | void attach(std::tuple area); 28 | void render(); 29 | 30 | private: 31 | std::tuple m_area; 32 | MemoryEditor m_editor; 33 | }; 34 | 35 | } // namespace z8 36 | 37 | -------------------------------------------------------------------------------- /src/ide/text-editor.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2016—2020 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #if HAVE_CONFIG_H 14 | # include "config.h" 15 | #endif 16 | 17 | #include // std::string 18 | #include // std::map 19 | #include // std::shared_ptr 20 | #include // std::function 21 | 22 | #include "zepto8.h" 23 | #include "ide/text-editor.h" 24 | #include "pico8/pico8.h" 25 | 26 | #include // for the ImGui headers and much more stuff 27 | 28 | #include "zep.h" 29 | #include "zep/mode_vim.h" 30 | #include "zep/mode_standard.h" 31 | #include "zep/filesystem.h" 32 | 33 | #include "zep/imgui/editor_imgui.h" 34 | 35 | #define BUFFER_NAME "code.p8" 36 | 37 | namespace z8 38 | { 39 | 40 | #define TEST_TEXT \ 41 | "-- " \ 42 | "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\n" \ 43 | "-- !\"#$%&'()*+,-./0123456789:;<=>?\n" \ 44 | "-- @ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_\n" \ 45 | "-- `abcdefghijklmnopqrstuvwxyz{|}~\x7f\n" \ 46 | "-- \x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\n" \ 47 | "-- \x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\n" \ 48 | "-- \xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\n" \ 49 | "-- \xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\n" \ 50 | "-- \xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\n" \ 51 | "-- \xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\n" \ 52 | "-- \xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\n" \ 53 | "-- \xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff\n" 54 | 55 | class zep_filesystem : public Zep::IZepFileSystem 56 | { 57 | public: 58 | virtual std::string Read(const Zep::ZepPath& filePath) override { return ""; } 59 | virtual bool Write(const Zep::ZepPath& filePath, const void* pData, size_t size) override { return true; } 60 | virtual Zep::ZepPath GetSearchRoot(const Zep::ZepPath& start, bool& foundGit) const override { return start; } 61 | virtual Zep::ZepPath GetConfigPath() const override { return ""; } 62 | virtual const Zep::ZepPath& GetWorkingDirectory() const override { return m_cwd; } 63 | virtual void SetWorkingDirectory(const Zep::ZepPath& path) override { } 64 | virtual void SetFlags(uint32_t flags) override { } 65 | virtual bool IsDirectory(const Zep::ZepPath& path) const override { return false; } 66 | virtual bool IsReadOnly(const Zep::ZepPath& path) const override { return false; } 67 | virtual bool Exists(const Zep::ZepPath& path) const override { return path == BUFFER_NAME; } 68 | virtual bool MakeDirectories(const Zep::ZepPath& path) override { return false; } 69 | 70 | // A callback API for scaning 71 | virtual void ScanDirectory(const Zep::ZepPath& path, std::function fnScan) const override 72 | { 73 | } 74 | 75 | // Equivalent means 'the same file' 76 | virtual bool Equivalent(const Zep::ZepPath& path1, const Zep::ZepPath& path2) const override 77 | { 78 | return path1 == path2; 79 | } 80 | 81 | virtual Zep::ZepPath Canonical(const Zep::ZepPath& path) const override { return path; } 82 | 83 | private: 84 | Zep::ZepPath m_cwd = ""; 85 | }; 86 | 87 | class editor_impl : public Zep::ZepEditor_ImGui 88 | { 89 | public: 90 | editor_impl() 91 | : Zep::ZepEditor_ImGui(Zep::ZepPath(), Zep::NVec2f(1.f), Zep::ZepEditorFlags::DisableThreads, new zep_filesystem()) 92 | {} 93 | 94 | virtual ~editor_impl() 95 | {} 96 | 97 | Zep::ZepBuffer *m_buffer; 98 | }; 99 | 100 | class zep_theme : public Zep::ZepTheme 101 | { 102 | public: 103 | zep_theme() 104 | { 105 | add_color(Zep::ThemeColor::Text, pico8::palette::light_gray); 106 | add_color(Zep::ThemeColor::Text, pico8::palette::light_gray); 107 | add_color(Zep::ThemeColor::Normal, pico8::palette::light_gray); 108 | add_color(Zep::ThemeColor::Parenthesis, pico8::palette::light_gray); 109 | add_color(Zep::ThemeColor::Background, pico8::palette::dark_blue); 110 | add_color(Zep::ThemeColor::LineNumber, pico8::palette::orange); 111 | add_color(Zep::ThemeColor::CursorNormal, pico8::palette::red); 112 | add_color(Zep::ThemeColor::Comment, pico8::palette::indigo); 113 | add_color(Zep::ThemeColor::Keyword, pico8::palette::pink); 114 | add_color(Zep::ThemeColor::Identifier, pico8::palette::green); 115 | add_color(Zep::ThemeColor::Number, pico8::palette::blue); 116 | add_color(Zep::ThemeColor::String, pico8::palette::blue); 117 | } 118 | 119 | virtual Zep::NVec4f const &GetColor(Zep::ThemeColor themeColor) const 120 | { 121 | auto it = m_palette.find(themeColor); 122 | if (it != m_palette.end()) 123 | return it->second; 124 | return Zep::ZepTheme::GetColor(themeColor); 125 | } 126 | 127 | //virtual Zep::NVec4f GetComplement(const Zep::NVec4f& col) const 128 | //virtual Zep::NVec4f GetUniqueColor(uint32_t id) const 129 | 130 | private: 131 | void add_color(Zep::ThemeColor tid, int cid) 132 | { 133 | auto col = pico8::palette::get(cid); 134 | m_palette[tid] = Zep::NVec4f(col.r, col.g, col.b, col.a); 135 | } 136 | 137 | std::map m_palette; 138 | }; 139 | 140 | text_editor::text_editor() 141 | : m_impl(std::make_unique()) 142 | { 143 | //m_impl->SetMode(Zep::ZepMode_Standard::StaticName()); 144 | m_impl->RegisterSyntaxFactory({".p8"}, Zep::SyntaxProvider { "p8", Zep::tSyntaxFactory([](Zep::ZepBuffer* pBuffer) 145 | { 146 | return std::make_shared(*pBuffer, pico8::api::keywords, pico8::api::functions); 147 | })}); 148 | 149 | m_impl->m_buffer = m_impl->InitWithText(BUFFER_NAME, ""); 150 | m_impl->m_buffer->SetTheme(std::static_pointer_cast(std::make_shared())); 151 | } 152 | 153 | text_editor::~text_editor() 154 | { 155 | } 156 | 157 | void text_editor::attach(std::shared_ptr vm) 158 | { 159 | m_vm = vm; 160 | m_impl->m_buffer->SetText(m_vm->get_code()); 161 | } 162 | 163 | void text_editor::render() 164 | { 165 | ImGui::PushStyleColor(ImGuiCol_ChildBg, pico8::palette::get(5)); 166 | 167 | // Needs to be done in render() because it uses the current ImGui font size 168 | if (m_fontsize != ImGui::GetFontSize()) 169 | { 170 | m_fontsize = ImGui::GetFontSize(); 171 | m_impl->GetDisplay().GetFont(Zep::ZepTextType::Text).InvalidateCharCache(); 172 | } 173 | 174 | if (ImGui::IsWindowFocused()) 175 | { 176 | ImGuiIO& io = ImGui::GetIO(); 177 | io.WantCaptureKeyboard = true; 178 | io.WantTextInput = true; 179 | m_impl->HandleInput(); 180 | } 181 | 182 | m_impl->UpdateWindowState(); 183 | m_impl->SetDisplayRegion(Zep::toNVec2f(ImGui::GetCursorScreenPos()), 184 | Zep::toNVec2f(ImGui::GetContentRegionAvail()) + Zep::toNVec2f(ImGui::GetCursorScreenPos())); 185 | m_impl->Display(); 186 | 187 | ImGui::PopStyleColor(1); 188 | } 189 | 190 | } // namespace z8 191 | 192 | -------------------------------------------------------------------------------- /src/ide/text-editor.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2016—2020 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #pragma once 14 | 15 | #include // std::shared_ptr 16 | 17 | namespace z8 18 | { 19 | 20 | class text_editor 21 | { 22 | public: 23 | text_editor(); 24 | ~text_editor(); 25 | 26 | void attach(std::shared_ptr vm); 27 | void render(); 28 | 29 | private: 30 | std::unique_ptr m_impl; 31 | std::shared_ptr m_vm; 32 | float m_fontsize = 0.f; 33 | }; 34 | 35 | } // namespace z8 36 | 37 | -------------------------------------------------------------------------------- /src/libquickjs.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(SolutionDir)src\3rdparty\lolengine\ 5 | 6 | 7 | 8 | Debug 9 | NX64 10 | 11 | 12 | Debug 13 | ORBIS 14 | 15 | 16 | Debug 17 | Win32 18 | 19 | 20 | Debug 21 | x64 22 | 23 | 24 | Release 25 | NX64 26 | 27 | 28 | Release 29 | ORBIS 30 | 31 | 32 | Release 33 | Win32 34 | 35 | 36 | Release 37 | x64 38 | 39 | 40 | 41 | {5168dfab-3b54-4caa-8654-da760bc53b74} 42 | StaticLibrary 43 | Win32Proj 44 | 45 | 46 | 47 | MultiByte 48 | 49 | 50 | true 51 | 52 | 53 | false 54 | true 55 | 56 | 57 | 58 | .;%(AdditionalIncludeDirectories) 59 | CONFIG_VERSION="2019-08-18";%(PreprocessorDefinitions) 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /src/libretro-zepto8.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(SolutionDir)src\3rdparty\lolengine\ 5 | 6 | 7 | 8 | Debug 9 | NX64 10 | 11 | 12 | Debug 13 | ORBIS 14 | 15 | 16 | Debug 17 | Win32 18 | 19 | 20 | Debug 21 | x64 22 | 23 | 24 | Release 25 | NX64 26 | 27 | 28 | Release 29 | ORBIS 30 | 31 | 32 | Release 33 | Win32 34 | 35 | 36 | Release 37 | x64 38 | 39 | 40 | 41 | {57423C2D-5F70-4D60-AE8C-F5FBE39B1464} 42 | DynamicLibrary 43 | Win32Proj 44 | 8.1 45 | 46 | 47 | 48 | .dll 49 | 50 | 51 | 52 | .;3rdparty/libretro-common/include;%(AdditionalIncludeDirectories) 53 | PACKAGE_VERSION="0.0";%(PreprocessorDefinitions) 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | {5168dfab-3b54-4caa-8654-da760bc53b74} 63 | 64 | 65 | {141365e7-f8f3-4d95-b8db-1a093f92f436} 66 | 67 | 68 | {9ae29931-c72e-43df-805b-27e4ddfbb582} 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /src/libretro.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2016—2020 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #if HAVE_CONFIG_H 14 | # include "config.h" 15 | #endif 16 | 17 | #include // lol::msg 18 | #include // lol::array2d 19 | #include // lol::ends_with 20 | #include // std::array 21 | #include // std::memset 22 | #include // std::shared_ptr 23 | #include // std::vector 24 | 25 | #include "zepto8.h" 26 | #include "pico8/vm.h" 27 | #include "pico8/pico8.h" 28 | #include "raccoon/vm.h" 29 | 30 | #include "libretro.h" 31 | 32 | #define EXPORT extern "C" RETRO_API 33 | 34 | // Callbacks provided by retroarch 35 | static retro_video_refresh_t video_cb; 36 | static retro_audio_sample_t audio_cb; 37 | static retro_audio_sample_batch_t audio_batch_cb; 38 | static retro_environment_t enviro_cb; 39 | static retro_input_poll_t input_poll_cb; 40 | static retro_input_state_t input_state_cb; 41 | 42 | // Global core state 43 | static bool is_raccoon; 44 | static std::shared_ptr vm; 45 | static lol::array2d fb32; 46 | static lol::array2d fb16; 47 | 48 | EXPORT void retro_set_environment(retro_environment_t cb) 49 | { 50 | enviro_cb = cb; 51 | // We can run without a cartridge 52 | bool no_rom = true; 53 | enviro_cb(RETRO_ENVIRONMENT_SET_SUPPORT_NO_GAME, &no_rom); 54 | // Looks good to me 55 | char const *system_dir; 56 | enviro_cb(RETRO_ENVIRONMENT_GET_SYSTEM_DIRECTORY, &system_dir); 57 | } 58 | 59 | EXPORT void retro_set_video_refresh(retro_video_refresh_t cb) { video_cb = cb; } 60 | EXPORT void retro_set_audio_sample(retro_audio_sample_t cb) { audio_cb = cb; } 61 | EXPORT void retro_set_audio_sample_batch(retro_audio_sample_batch_t cb) { audio_batch_cb = cb; } 62 | EXPORT void retro_set_input_poll(retro_input_poll_t cb) { input_poll_cb = cb; } 63 | EXPORT void retro_set_input_state(retro_input_state_t cb) { input_state_cb = cb; } 64 | 65 | EXPORT void retro_init() 66 | { 67 | // Allocate framebuffer and VM 68 | vm = std::make_shared(); 69 | fb32.resize(lol::ivec2(128, 128)); 70 | fb16.resize(lol::ivec2(128, 128)); 71 | } 72 | 73 | EXPORT void retro_deinit() 74 | { 75 | fb32.clear(); 76 | fb16.clear(); 77 | } 78 | 79 | EXPORT unsigned retro_api_version() 80 | { 81 | return RETRO_API_VERSION; 82 | } 83 | 84 | EXPORT void retro_get_system_info(struct retro_system_info *info) 85 | { 86 | memset(info, 0, sizeof(*info)); 87 | info->library_name = "zepto8"; 88 | info->library_version = PACKAGE_VERSION; 89 | info->valid_extensions = "p8|p8.png|rcn.json"; 90 | info->need_fullpath = true; // we load our own carts for now 91 | } 92 | 93 | EXPORT void retro_get_system_av_info(struct retro_system_av_info *info) 94 | { 95 | memset(info, 0, sizeof(*info)); 96 | info->geometry.base_width = 128; 97 | info->geometry.base_height = 128; 98 | info->geometry.max_width = 128; 99 | info->geometry.max_height = 128; 100 | info->geometry.aspect_ratio = 1.f; 101 | info->timing.fps = 60.f; 102 | info->timing.sample_rate = 44100.f; 103 | 104 | retro_pixel_format pf = RETRO_PIXEL_FORMAT_RGB565; 105 | enviro_cb(RETRO_ENVIRONMENT_SET_PIXEL_FORMAT, &pf); 106 | } 107 | 108 | EXPORT void retro_set_controller_port_device(unsigned port, unsigned device) 109 | { 110 | } 111 | 112 | EXPORT void retro_reset() 113 | { 114 | } 115 | 116 | static std::array buttons 117 | { 118 | RETRO_DEVICE_ID_JOYPAD_LEFT, 119 | RETRO_DEVICE_ID_JOYPAD_RIGHT, 120 | RETRO_DEVICE_ID_JOYPAD_UP, 121 | RETRO_DEVICE_ID_JOYPAD_DOWN, 122 | RETRO_DEVICE_ID_JOYPAD_A, 123 | RETRO_DEVICE_ID_JOYPAD_B, 124 | RETRO_DEVICE_ID_JOYPAD_START, 125 | }; 126 | 127 | EXPORT void retro_run() 128 | { 129 | // Update input 130 | input_poll_cb(); 131 | for (int n = 0; n < 8; ++n) 132 | for (int k = 0; k < 7; ++k) 133 | vm->button(n, k, input_state_cb(n, RETRO_DEVICE_JOYPAD, 0, buttons[k])); 134 | 135 | // Step VM 136 | vm->step(1.f / 60); 137 | 138 | // Render video, convert to RGB565, send back to frontend 139 | vm->render(fb32.data()); 140 | for (int y = 0; y < 128; ++y) 141 | for (int x = 0; x < 128; ++x) 142 | fb16(x, y) = uint16_t(lol::dot(lol::ivec3(fb32(x, y).rgb) / lol::ivec3(8, 4, 8), 143 | lol::ivec3(2048, 32, 1))); 144 | video_cb(fb16.data(), 128, 128, 2 * 128); 145 | 146 | // Render audio 147 | } 148 | 149 | EXPORT size_t retro_serialize_size() 150 | { 151 | return 0; 152 | } 153 | 154 | EXPORT bool retro_serialize(void *data, size_t size) 155 | { 156 | return false; 157 | } 158 | 159 | EXPORT bool retro_unserialize(const void *data, size_t size) 160 | { 161 | return false; 162 | } 163 | 164 | EXPORT void retro_cheat_reset() 165 | { 166 | } 167 | 168 | EXPORT void retro_cheat_set(unsigned index, bool enabled, const char *code) 169 | { 170 | } 171 | 172 | EXPORT bool retro_load_game(struct retro_game_info const *info) 173 | { 174 | is_raccoon = lol::ends_with(info->path, ".rcn.json"); 175 | is_raccoon ? vm = std::make_shared() 176 | : vm = std::make_shared(); 177 | vm->load(info->path); 178 | vm->run(); 179 | return true; 180 | } 181 | 182 | EXPORT bool retro_load_game_special(unsigned game_type, 183 | const struct retro_game_info *info, size_t num_info) 184 | { 185 | return false; 186 | } 187 | 188 | EXPORT void retro_unload_game() 189 | { 190 | } 191 | 192 | EXPORT unsigned retro_get_region() 193 | { 194 | return 0; 195 | } 196 | 197 | EXPORT void *retro_get_memory_data(unsigned id) 198 | { 199 | return nullptr; 200 | } 201 | 202 | EXPORT size_t retro_get_memory_size(unsigned id) 203 | { 204 | return 0; 205 | } 206 | 207 | -------------------------------------------------------------------------------- /src/libz8lua.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(SolutionDir)src\3rdparty\lolengine\ 5 | 6 | 7 | 8 | Debug 9 | NX64 10 | 11 | 12 | Debug 13 | ORBIS 14 | 15 | 16 | Debug 17 | Win32 18 | 19 | 20 | Debug 21 | x64 22 | 23 | 24 | Release 25 | NX64 26 | 27 | 28 | Release 29 | ORBIS 30 | 31 | 32 | Release 33 | Win32 34 | 35 | 36 | Release 37 | x64 38 | 39 | 40 | 41 | {141365e7-f8f3-4d95-b8db-1a093f92f436} 42 | StaticLibrary 43 | Win32Proj 44 | 45 | 46 | 47 | MultiByte 48 | 49 | 50 | true 51 | 52 | 53 | false 54 | true 55 | 56 | 57 | 58 | .;%(AdditionalIncludeDirectories) 59 | _LIB;%(PreprocessorDefinitions) 60 | CompileAsCpp 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | -------------------------------------------------------------------------------- /src/libzepto8.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | $(SolutionDir)src\3rdparty\lolengine\ 5 | 6 | 7 | 8 | Debug 9 | NX64 10 | 11 | 12 | Debug 13 | ORBIS 14 | 15 | 16 | Debug 17 | Win32 18 | 19 | 20 | Debug 21 | x64 22 | 23 | 24 | Release 25 | NX64 26 | 27 | 28 | Release 29 | ORBIS 30 | 31 | 32 | Release 33 | Win32 34 | 35 | 36 | Release 37 | x64 38 | 39 | 40 | 41 | {9ae29931-c72e-43df-805b-27e4ddfbb582} 42 | StaticLibrary 43 | Win32Proj 44 | 45 | 46 | 47 | MultiByte 48 | 49 | 50 | true 51 | 52 | 53 | false 54 | true 55 | 56 | 57 | 58 | .;%(AdditionalIncludeDirectories) 59 | _LIB;%(PreprocessorDefinitions) 60 | /bigobj %(AdditionalOptions) 61 | /bigobj %(AdditionalOptions) 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /src/libzepto8.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | pico8 7 | 8 | 9 | pico8 10 | 11 | 12 | pico8 13 | 14 | 15 | pico8 16 | 17 | 18 | pico8 19 | 20 | 21 | pico8 22 | 23 | 24 | pico8 25 | 26 | 27 | pico8 28 | 29 | 30 | pico8 31 | 32 | 33 | pico8 34 | 35 | 36 | raccoon 37 | 38 | 39 | raccoon 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | pico8 50 | 51 | 52 | pico8 53 | 54 | 55 | pico8 56 | 57 | 58 | pico8 59 | 60 | 61 | pico8 62 | 63 | 64 | raccoon 65 | 66 | 67 | raccoon 68 | 69 | 70 | 71 | 72 | raccoon 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | {df3ce4b7-459e-4b34-bf57-a283e4098876} 84 | 85 | 86 | {6f37686e-5de3-410d-941c-d5aafbdb59c1} 87 | 88 | 89 | {50d0600e-9314-4f24-a578-53301f642348} 90 | 91 | 92 | {d45e4565-b73a-4010-8b11-d8ee39404049} 93 | 94 | 95 | 96 | 97 | data 98 | 99 | 100 | 101 | 102 | pico8 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /src/minify.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2016—2018 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #if HAVE_CONFIG_H 14 | # include "config.h" 15 | #endif 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "minify.h" 25 | 26 | namespace z8 27 | { 28 | 29 | std::string minify(std::string const &input) 30 | { 31 | std::regex re_spaces(" +"); 32 | 33 | // Split input into lines 34 | std::vector lines; 35 | std::regex re_crlf("\r*\n"); 36 | std::copy(std::sregex_token_iterator(input.begin(), input.end(), re_crlf, -1), 37 | std::sregex_token_iterator(), 38 | std::back_inserter(lines)); 39 | 40 | // Skip first three lines (they’re the PICO-8 cart header) 41 | lines.erase(lines.begin(), lines.begin() + 3); 42 | 43 | std::vector> replaces; 44 | std::string result; 45 | 46 | // Process each line 47 | for (auto & line : lines) 48 | { 49 | // Parse special comments indicating possible replacements 50 | static std::regex re_replaces("^(.*--.*replaces: |.*)"); 51 | static std::regex re_replace_pair(" *([^ ]+) *([^ ]+) *"); 52 | std::vector replaces_list; 53 | auto tmp = std::regex_replace(line, re_replaces, ""); 54 | std::copy(std::sregex_token_iterator(tmp.begin(), tmp.end(), re_spaces, -1), 55 | std::sregex_token_iterator(), 56 | std::back_inserter(replaces_list)); 57 | for (size_t i = 0; i + 1 < replaces_list.size(); i += 2) 58 | replaces.push_back(std::make_tuple(replaces_list[i], replaces_list[i + 1])); 59 | 60 | // Strip comments; if comment starts with "debug" then whole line is stripped 61 | static std::regex re_comment("(^.*-- *debug| *--).*"); 62 | line = std::regex_replace(line, re_comment, ""); 63 | 64 | // Ignore empty lines 65 | static std::regex re_empty("^ *$"); 66 | if (std::regex_match(line, re_empty)) 67 | continue; 68 | 69 | // Protect lines that contain +=, -= etc. 70 | static std::regex re_compound(".*[-+*/%]=.*"); 71 | line = std::regex_replace(line, re_compound, "X $0 X"); 72 | 73 | result += result.empty() ? line : " " + line; 74 | } 75 | 76 | // Rename all variables according to our rules 77 | for (auto & rep : replaces) 78 | result = std::regex_replace(result, std::regex("([^a-z_])(" + std::get<0>(rep) + ")([^a-z0-9_])"), "$1" + std::get<1>(rep) + "$3"); 79 | 80 | // Strip multiple spaces 81 | result = std::regex_replace(result, re_spaces, " "); 82 | 83 | // Exploit Lua parsing rules 84 | result = std::regex_replace(result, std::regex("0 ([g-wyz])"), "0$1"); 85 | result = std::regex_replace(result, std::regex("([1-9]) ([g-z])"), "$1$2"); 86 | 87 | // Unprotect lines protected with X 88 | result = std::regex_replace(result, std::regex(" *X[ X]*"), " "); 89 | 90 | // Remove spaces before and after symbols 91 | result = std::regex_replace(result, std::regex(" *([[\\]<>(){}#+*%^/=:!~,-]) *"), "$1"); 92 | 93 | return result; 94 | } 95 | 96 | } // namespace z8 97 | 98 | -------------------------------------------------------------------------------- /src/minify.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2017—2018 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #pragma once 14 | 15 | namespace z8 16 | { 17 | 18 | std::string minify(std::string const &input); 19 | 20 | } // namespace z8 21 | 22 | -------------------------------------------------------------------------------- /src/pico8/api.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2016—2020 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #if HAVE_CONFIG_H 14 | # include "config.h" 15 | #endif 16 | 17 | #include // std::unordered_set 18 | #include // std::string 19 | 20 | #include "pico8/pico8.h" 21 | 22 | namespace z8::pico8 23 | { 24 | 25 | // This is the list from Lua’s luaX_tokens 26 | std::unordered_set api::keywords = 27 | { 28 | "and", "break", "do", "else", "elseif", 29 | "end", "false", "for", "function", "goto", "if", 30 | "in", "local", "nil", "not", "or", "repeat", 31 | "return", "then", "true", "until", "while", 32 | }; 33 | 34 | // The official API. Anything not in this list may still exist in the Lua 35 | // runtime, but will be hidden from user cartridges. 36 | std::unordered_set api::functions = 37 | { 38 | // Implemented in Lua (mostly baselib) 39 | "assert", "getmetatable", "next", "ipairs", "pairs", "rawequal", 40 | "rawlen", "rawget", "rawset", "setmetatable", "type", "pack", "unpack", 41 | // Present in Lua but actually implemented by ZEPTO-8 42 | "load", "print", 43 | // Implemented in pico8lib (from z8lua) 44 | "max", "min", "mid", "ceil", "flr", "cos", "sin", "atan2", "sqrt", 45 | "abs", "sgn", "band", "bor", "bxor", "bnot", "shl", "shr", "lshr", 46 | "rotl", "rotr", "tostr", "tonum", "srand", "rnd", "ord", "chr", 47 | "split", 48 | // Implemented in the ZEPTO-8 VM 49 | "run", "reload", "cstore", "dget", "dset", "peek", "peek2", "peek4", 50 | "poke", "poke2", "poke4", "memcpy", "memset", "stat", "printh", "extcmd", 51 | "btn", "btnp", "cursor", "camera", "circ", "circfill", 52 | "clip", "cls", "color", "fillp", "fget", "fset", "line", "map", "mget", 53 | "mset", "oval", "ovalfill", "pal", "palt", "pget", "pset", "rect", "rectfill", 54 | "serial", "sget", "sset", "spr", "sspr", "music", "sfx", "time", "tline", "_map_display", 55 | // Implemented in the ZEPTO-8 BIOS 56 | "cocreate", "coresume", "costatus", "yield", "trace", "stop", 57 | "count", "add", "sub", "foreach", "all", "del", "deli", "t", "dget", 58 | "dset", "cartdata", "load", "save", "info", "abort", "folder", 59 | "resume", "reboot", "dir", "ls", "flip", "holdframe", "_update_buttons", "mapdraw", "menuitem", "_set_mainloop_exists", 60 | "_set_fps", "serial" 61 | }; 62 | 63 | } // namespace z8::pico8 64 | 65 | -------------------------------------------------------------------------------- /src/pico8/ast.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2016—2020 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #if HAVE_CONFIG_H 14 | # include "config.h" 15 | #endif 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include // std::string 23 | #include // std::setw 24 | 25 | #include "pico8.h" 26 | #define WITH_PICO8 1 27 | #include "grammar.h" 28 | 29 | using namespace tao; 30 | 31 | namespace z8::pico8 32 | { 33 | 34 | // 35 | // Parse tree support 36 | // 37 | 38 | struct transform_expr : pegtl::parse_tree::apply< transform_expr > 39 | { 40 | template< typename Node, typename... States > 41 | static void transform( std::unique_ptr< Node >& n, States&&... ) 42 | { 43 | // Collapse if only one child 44 | if (n->children.size() == 1) 45 | { 46 | n = std::move(n->children.back()); 47 | return; 48 | } 49 | } 50 | }; 51 | 52 | using store_selector = pegtl::parse_tree::store_content::on< 53 | // others 54 | variable, 55 | keyword, 56 | name, 57 | numeral, 58 | bracket_expr, 59 | literal_string, 60 | unary_operators, 61 | compop, 62 | operators_nine, 63 | operators_eight, 64 | operators_six, 65 | operators_two 66 | >; 67 | 68 | using transform_selector = transform_expr::on< 69 | expr_eleven, 70 | expr_nine, 71 | expr_eight, 72 | expr_seven, 73 | expr_six, 74 | expr_five, 75 | expr_four, 76 | expr_three, 77 | expr_two, 78 | expr_one, 79 | expression 80 | >; 81 | 82 | using remove_selector = pegtl::parse_tree::remove_content::on< 83 | // statement := 84 | assignments, 85 | assignment_variable_list, 86 | expr_list_must, 87 | compound_statement, 88 | // compop, 89 | short_print, 90 | #if 0 91 | if_do_statement, 92 | #endif 93 | short_if_statement, 94 | short_while_statement, 95 | function_call, 96 | label_statement, 97 | key_break, 98 | goto_statement, 99 | do_statement, 100 | while_statement, 101 | repeat_statement, 102 | if_statement, 103 | for_statement, 104 | function_definition, 105 | local_statement 106 | >; 107 | 108 | // The global AST selector 109 | template 110 | struct ast_selector : pegtl::parse_tree::selector< Rule, 111 | store_selector, transform_selector, remove_selector > {}; 112 | 113 | // Special rule: discard a whole subtree 114 | template< typename... Rule > 115 | struct ast_selector< silent_at< Rule... > > : std::true_type 116 | { 117 | template< typename Node, typename... States > 118 | static void transform( std::unique_ptr< Node >& n, States&&... ) 119 | { 120 | n.reset(); 121 | } 122 | }; 123 | 124 | struct chunk {}; 125 | 126 | template<> 127 | struct ast_selector< grammar > : std::true_type 128 | { 129 | template< typename Node, typename... States > 130 | static void transform( std::unique_ptr< Node >& n, States&&... ) 131 | { 132 | for (auto &c : n->children) 133 | c->template set_type(); 134 | } 135 | }; 136 | 137 | std::string code::ast(std::string const &s) 138 | { 139 | pegtl::string_input<> in(s, "p8"); 140 | try 141 | { 142 | const auto root = pegtl::parse_tree::parse(in, parse_state{}); 143 | pegtl::parse_tree::print_dot(std::cout, *root); 144 | } 145 | catch (pegtl::parse_error const &e) 146 | { 147 | auto const pos = e.positions().front(); 148 | std::cerr << e.what() << '\n' 149 | << in.line_at(pos) << '\n' 150 | << std::setw(pos.byte_in_line) << '^' << '\n'; 151 | } 152 | catch (std::exception const &e) 153 | { 154 | std::cerr << e.what() << '\n'; 155 | } 156 | return ""; 157 | } 158 | 159 | } // namespace z8::pico8 160 | -------------------------------------------------------------------------------- /src/pico8/cart.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2016—2020 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #pragma once 14 | 15 | #include // std::vector 16 | #include // std::string 17 | #include // std::filesystem 18 | #include "pico8/pico8.h" 19 | #include "pico8/memory.h" 20 | 21 | // The cart class 22 | // —————————————— 23 | // Represents a PICO-8 cartridge. Can load and unpack .p8 and .p8.png files, but also .lua 24 | // and .js (from a PICO-8 web export). The VM can then load their content into memory. 25 | 26 | namespace z8::pico8 27 | { 28 | 29 | class cart 30 | { 31 | public: 32 | cart() 33 | {} 34 | 35 | bool load(std::string const &filename); 36 | 37 | memory const &get_rom() const 38 | { 39 | return m_rom; 40 | } 41 | 42 | memory &get_rom() 43 | { 44 | return m_rom; 45 | } 46 | 47 | std::vector &get_label() 48 | { 49 | return m_label; 50 | } 51 | 52 | std::string const &get_code() const 53 | { 54 | return m_code; 55 | } 56 | 57 | std::string const& get_filename() const 58 | { 59 | return m_filename; 60 | } 61 | 62 | std::string const& get_title() const 63 | { 64 | return m_title; 65 | } 66 | 67 | std::string const& get_author() const 68 | { 69 | return m_author; 70 | } 71 | 72 | 73 | bool has_file_changed() const 74 | { 75 | #if !__NX__ && !__SCE__ 76 | if (!m_file_time_init) return false; 77 | return m_file_time != std::filesystem::last_write_time(m_filename); 78 | #else 79 | return false; 80 | #endif 81 | } 82 | 83 | std::vector get_compressed_code() const; 84 | std::vector get_bin() const; 85 | bool save(std::string const& filename) const; 86 | void set_from_ram(memory const& ram, int in_dst, int in_src, int in_size); 87 | 88 | std::string preprocess_code() const; 89 | 90 | private: 91 | bool load_png(std::string const &filename); 92 | bool load_p8(std::string const &filename); 93 | bool load_lua(std::string const &filename); 94 | bool load_js(std::string const &filename); 95 | 96 | bool save_p8(std::string const& filename) const; 97 | bool save_png(std::string const& filename) const; 98 | 99 | void set_bin(std::vector const &data); 100 | void init_rom(); 101 | void init_title(); 102 | void init_filename(std::string filename); 103 | 104 | memory m_rom; 105 | std::vector m_label; 106 | std::string m_code, m_lua; 107 | std::string m_filename; 108 | std::string m_title; 109 | std::string m_author; 110 | int m_version; 111 | 112 | #if !__NX__ && !__SCE__ 113 | bool m_file_time_init = false; 114 | std::filesystem::file_time_type m_file_time; 115 | #endif 116 | }; 117 | 118 | } // namespace z8::pico8 119 | 120 | -------------------------------------------------------------------------------- /src/pico8/parser.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2016—2020 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #if HAVE_CONFIG_H 14 | # include "config.h" 15 | #endif 16 | 17 | #include 18 | #include 19 | #include 20 | 21 | #include // std::string 22 | #include // std::setw 23 | 24 | #include "pico8.h" 25 | #define WITH_PICO8 1 26 | #include "grammar.h" 27 | 28 | using namespace tao; 29 | 30 | namespace z8::pico8 31 | { 32 | 33 | template static int token_counter = 0; 34 | 35 | // Most keywords cost 1 token 36 | template<> int token_counter = 1; 37 | template<> int token_counter = 1; 38 | template<> int token_counter = 1; 39 | template<> int token_counter = 1; 40 | template<> int token_counter = 1; 41 | template<> int token_counter = 0; // “end” is free 42 | template<> int token_counter = 1; 43 | template<> int token_counter = 1; 44 | template<> int token_counter = 1; 45 | template<> int token_counter = 1; 46 | template<> int token_counter = 1; 47 | template<> int token_counter = 1; 48 | template<> int token_counter = 0; // “local” is free 49 | template<> int token_counter = 1; 50 | template<> int token_counter = 0; // “not” already appears in unary_operators 51 | template<> int token_counter = 1; 52 | template<> int token_counter = 1; 53 | template<> int token_counter = 1; 54 | template<> int token_counter = 1; 55 | template<> int token_counter = 1; 56 | template<> int token_counter = 1; 57 | template<> int token_counter = 1; 58 | 59 | // Most terminals cost 1 token 60 | template<> int token_counter = 1; 61 | template<> int token_counter = 1; 62 | template<> int token_counter = 1; 63 | template<> int token_counter = 0; 64 | template<> int token_counter = 1; 65 | template<> int token_counter = 1; // the “{}” 66 | template<> int token_counter = 2; // the “[]” and “=” in “[x]=1” 67 | template<> int token_counter = 1; // the “=” in “x=1” 68 | template<> int token_counter = 1; // the “()” 69 | template<> int token_counter = 1; // the “()” 70 | template<> int token_counter = 1; // the “()” 71 | template<> int token_counter = 1; // the “[]” in “a[b]” 72 | template<> int token_counter = 0; // the “.” in “a.b” is free 73 | template<> int token_counter = 0; // the “:” in “a:b()” is free 74 | template<> int token_counter = 1; // the “?” in “?12,3,5” 75 | template<> int token_counter = 1; // the “=” in “for x=1,2 do end” 76 | template<> int token_counter = 0; // no additional cost 77 | template<> int token_counter = 1; // the “=” in “a,b,c = 2,3,4” 78 | 79 | template<> int token_counter = 1; // “^” 80 | template<> int token_counter = 1; // “/” “\” etc. 81 | template<> int token_counter = 1; // “-” “+” 82 | template<> int token_counter = 1; // “..” 83 | template<> int token_counter = 1; // “<<” “>>” etc. 84 | template<> int token_counter = 1; // “&” 85 | template<> int token_counter = 1; // “^^” 86 | template<> int token_counter = 1; // “|” 87 | template<> int token_counter = 1; // “==” “<=” etc. 88 | 89 | template<> int token_counter = -1; // “-2” counts as one, “- 2” counts as two 90 | template<> int token_counter = 1; 91 | template<> int token_counter = 1; 92 | 93 | template struct tokenise_action 94 | { 95 | template 96 | static void apply(const Input &, parse_state &ps) 97 | { 98 | //if (token_counter) 99 | // printf(" +%d: %s\n", token_counter, std::string(in.string()).c_str()); 100 | ps.tokens += token_counter; 101 | } 102 | }; 103 | 104 | int code::count_tokens(std::string const &s) 105 | { 106 | pegtl::string_input<> in(s, "p8"); 107 | try 108 | { 109 | parse_state ps; 110 | pegtl::parse(in, ps); 111 | return ps.tokens; 112 | } 113 | catch (pegtl::parse_error const &e) 114 | { 115 | auto const pos = e.positions().front(); 116 | std::cerr << e.what() << '\n' 117 | << in.line_at(pos) << '\n' 118 | << std::setw(pos.byte_in_line) << '^' << '\n'; 119 | } 120 | catch (std::exception const &e) 121 | { 122 | lol::msg::error("parse error: %s\n", e.what()); 123 | } 124 | return -1; 125 | } 126 | 127 | } // namespace z8::pico8 128 | -------------------------------------------------------------------------------- /src/pico8/pico8.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2016—2020 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #pragma once 14 | 15 | #include // std::map 16 | #include // std::unordered_set 17 | #include // std::string_view 18 | #include // std::regex 19 | #include // FLT_MAX 20 | #include // lol::vec4 21 | 22 | // The PICO-8 definitions 23 | // —————————————————————— 24 | 25 | namespace z8::pico8 26 | { 27 | 28 | enum 29 | { 30 | PICO8_VERSION = 25, 31 | }; 32 | 33 | enum 34 | { 35 | LABEL_WIDTH = 128, 36 | LABEL_HEIGHT = 128, 37 | LABEL_X = 16, 38 | LABEL_Y = 24, 39 | }; 40 | 41 | struct api 42 | { 43 | static std::unordered_set functions; 44 | static std::unordered_set keywords; 45 | }; 46 | 47 | struct charset 48 | { 49 | // Convert between UTF-8 strings and 8-bit PICO-8 strings 50 | static std::string utf8_to_pico8(std::string const &str); 51 | static std::string pico8_to_utf8(std::string const &str); 52 | 53 | // Map 8-bit PICO-8 characters to UTF-32 codepoints 54 | static std::u32string_view to_utf32[256]; 55 | 56 | // Map 8-bit PICO-8 characters to UTF-8 string views 57 | static std::string_view to_utf8[256]; 58 | 59 | private: 60 | static std::regex static_init(); 61 | static std::regex utf8_regex; 62 | }; 63 | 64 | struct code 65 | { 66 | enum class format 67 | { 68 | best, pxa, pxa_fast, old, store, 69 | }; 70 | 71 | static std::string decompress(uint8_t const *input); 72 | static std::vector compress(std::string const &input, 73 | format fmt = format::pxa); 74 | 75 | static int count_tokens(std::string const &s); 76 | static std::string ast(std::string const &s); 77 | }; 78 | 79 | struct palette 80 | { 81 | enum 82 | { 83 | black = 0, 84 | dark_blue, 85 | dark_purple, 86 | dark_green, 87 | brown, 88 | dark_gray, 89 | light_gray, 90 | white, 91 | red, 92 | orange, 93 | yellow, 94 | green, 95 | blue, 96 | indigo, 97 | pink, 98 | peach, 99 | }; 100 | 101 | /* Get the nth palette element as a vector of floats in 0…1 */ 102 | static lol::vec4 get(int n) 103 | { 104 | return lol::vec4(get8(n)) / 255.f; 105 | } 106 | 107 | /* Get the nth palette element as a vector of uint8_ts in 0…255 */ 108 | static lol::u8vec4 get8(int n) 109 | { 110 | static lol::u8vec4 const pal[] = 111 | { 112 | lol::u8vec4(0x00, 0x00, 0x00, 0xff), // black 113 | lol::u8vec4(0x1d, 0x2b, 0x53, 0xff), // dark_blue 114 | lol::u8vec4(0x7e, 0x25, 0x53, 0xff), // dark_purple 115 | lol::u8vec4(0x00, 0x87, 0x51, 0xff), // dark_green 116 | lol::u8vec4(0xab, 0x52, 0x36, 0xff), // brown 117 | lol::u8vec4(0x5f, 0x57, 0x4f, 0xff), // dark_gray 118 | lol::u8vec4(0xc2, 0xc3, 0xc7, 0xff), // light_gray 119 | lol::u8vec4(0xff, 0xf1, 0xe8, 0xff), // white 120 | lol::u8vec4(0xff, 0x00, 0x4d, 0xff), // red 121 | lol::u8vec4(0xff, 0xa3, 0x00, 0xff), // orange 122 | lol::u8vec4(0xff, 0xec, 0x27, 0xff), // yellow 123 | lol::u8vec4(0x00, 0xe4, 0x36, 0xff), // green 124 | lol::u8vec4(0x29, 0xad, 0xff, 0xff), // blue 125 | lol::u8vec4(0x83, 0x76, 0x9c, 0xff), // indigo 126 | lol::u8vec4(0xff, 0x77, 0xa8, 0xff), // pink 127 | lol::u8vec4(0xff, 0xcc, 0xaa, 0xff), // peach 128 | 129 | lol::u8vec4(0x29, 0x18, 0x14, 0xff), 130 | lol::u8vec4(0x11, 0x1d, 0x35, 0xff), 131 | lol::u8vec4(0x42, 0x21, 0x36, 0xff), 132 | lol::u8vec4(0x12, 0x53, 0x59, 0xff), 133 | lol::u8vec4(0x74, 0x2f, 0x29, 0xff), 134 | lol::u8vec4(0x49, 0x33, 0x3b, 0xff), 135 | lol::u8vec4(0xa2, 0x88, 0x79, 0xff), 136 | lol::u8vec4(0xf3, 0xef, 0x7d, 0xff), 137 | lol::u8vec4(0xbe, 0x12, 0x50, 0xff), 138 | lol::u8vec4(0xff, 0x6c, 0x24, 0xff), 139 | lol::u8vec4(0xa8, 0xe7, 0x2e, 0xff), 140 | lol::u8vec4(0x00, 0xb5, 0x43, 0xff), 141 | lol::u8vec4(0x06, 0x5a, 0xb5, 0xff), 142 | lol::u8vec4(0x75, 0x46, 0x65, 0xff), 143 | lol::u8vec4(0xff, 0x6e, 0x59, 0xff), 144 | lol::u8vec4(0xff, 0x9d, 0x81, 0xff), 145 | }; 146 | 147 | return pal[n & 0x1f]; 148 | } 149 | 150 | /* Find the closest palette element to c (a vector of floats in 0…1) */ 151 | static int best(lol::vec4 c, int count = 16) 152 | { 153 | int ret = 0; 154 | float dist = FLT_MAX; 155 | for (int n = 0; n < count; ++n) 156 | { 157 | lol::vec3 delta = c.rgb - get(n).rgb; 158 | float newdist = lol::sqlength(delta); 159 | if (newdist < dist) 160 | { 161 | dist = newdist; 162 | ret = n; 163 | } 164 | } 165 | return ret; 166 | } 167 | 168 | /* Find the closest palette element to c (a vector of uint8_ts in 0…255) */ 169 | static int best(lol::u8vec4 c, int count = 16) 170 | { 171 | return best(lol::vec4(c) / 255.f, count); 172 | } 173 | }; 174 | 175 | } // namespace z8::pico8 176 | 177 | -------------------------------------------------------------------------------- /src/pico8/render.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2016—2020 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #if HAVE_CONFIG_H 14 | # include "config.h" 15 | #endif 16 | 17 | #include "pico8/vm.h" 18 | #include "pico8/pico8.h" 19 | 20 | #include // lol::u8vec4 21 | 22 | namespace z8::pico8 23 | { 24 | 25 | void vm::private_end_render() 26 | { 27 | if (m_in_pause) return; 28 | 29 | memcpy(&m_front_buffer, &get_current_screen(), sizeof(m_front_buffer)); 30 | m_front_draw_state = m_ram.draw_state; 31 | m_front_hw_state = m_ram.hw_state; 32 | } 33 | 34 | void vm::render(lol::u8vec4 *screen) const 35 | { 36 | // Cannot use a 256-value LUT because data access will be 37 | // very random due to rotation, flip, stretch etc. 38 | lol::u8vec4 lut[128 + 16]; 39 | for (int c = 0; c < 16; ++c) 40 | { 41 | lut[c] = palette::get8(c); 42 | lut[128 + c] = palette::get8(16 + c); 43 | } 44 | 45 | for (int y = 0; y < 128; ++y) 46 | { 47 | for (int x = 0; x < 128; ++x) 48 | *screen++ = lut[pixel(x, y, get_front_screen())]; 49 | if (m_multiscreens_x > 1) 50 | { 51 | for (int sx = 1; sx < m_multiscreens_x; ++sx) 52 | for (int x = 0; x < 128; ++x) 53 | *screen++ = lut[pixel(x, y, *m_multiscreens[sx - 1])]; 54 | } 55 | } 56 | if (m_multiscreens_y > 1) 57 | { 58 | for (int sy = 1; sy < m_multiscreens_y; ++sy) 59 | for (int y = 0; y < 128; ++y) 60 | { 61 | for (int sx = 0; sx < m_multiscreens_x; ++sx) 62 | for (int x = 0; x < 128; ++x) 63 | *screen++ = lut[pixel(x, y, *m_multiscreens[sx + sy * m_multiscreens_x - 1])]; 64 | } 65 | } 66 | } 67 | 68 | 69 | // Hardware pixel accessor 70 | uint8_t vm::pixel(int x, int y, u4mat2<128, 128> const& screen) const 71 | { 72 | // TODO: cache all state 73 | auto &draw_state = m_front_draw_state; 74 | auto &hw_state = m_front_hw_state; 75 | 76 | // Get screen mode 77 | uint8_t const& mode = draw_state.screen_mode; 78 | 79 | // Apply screen mode (rotation, mirror, flip…) 80 | if ((mode & 0xbc) == 0x84) 81 | { 82 | // Rotation modes (0x84 to 0x87) 83 | if (mode & 1) 84 | std::swap(x, y); 85 | x = mode & 2 ? 127 - x : x; 86 | y = ((mode + 1) & 2) ? 127 - y : y; 87 | } 88 | else 89 | { 90 | // Other modes 91 | x = (mode & 0xbd) == 0x05 ? std::min(x, 127 - x) // mirror 92 | : (mode & 0xbd) == 0x01 ? x / 2 // stretch 93 | : (mode & 0xbd) == 0x81 ? 127 - x : x; // flip 94 | y = (mode & 0xbe) == 0x06 ? std::min(y, 127 - y) // mirror 95 | : (mode & 0xbe) == 0x02 ? y / 2 // stretch 96 | : (mode & 0xbe) == 0x82 ? 127 - y : y; // flip 97 | } 98 | 99 | int c = screen.get(x, y); 100 | 101 | // Apply raster mode 102 | if (hw_state.raster.mode == 0x10) 103 | { 104 | // Raster mode: alternate palette 105 | if (hw_state.raster.bits[y]) 106 | return hw_state.raster.palette[c]; 107 | } 108 | else if ((hw_state.raster.mode & 0x30) == 0x30) 109 | { 110 | // Raster mode: gradient 111 | if ((hw_state.raster.mode & 0x0f) == c) 112 | { 113 | int c2 = (y / 8 + (hw_state.raster.bits[y] ? 1 : 0)) % 16; 114 | return hw_state.raster.palette[c2]; 115 | } 116 | } 117 | 118 | // Apply screen palette 119 | return draw_state.screen_palette[c]; 120 | } 121 | 122 | int vm::get_ansi_color(uint8_t c) const 123 | { 124 | static int const ansi_palette[] = 125 | { 126 | 16, // 000000 → 000000 127 | 17, // 1d2b53 → 00005f 128 | 89, // 7e2553 → 87005f 129 | 29, // 008751 → 00875f 130 | 131, // ab5236 → ab5236 131 | 240, // 5f574f → 5f5f5f 132 | 251, // c2c3c7 → c6c6c6 133 | 230, // fff1e8 → ffffdf 134 | 197, // ff004d → ff005f 135 | 214, // ffa300 → ffaf00 136 | 220, // ffec27 → ffdf00 137 | 47, // 00e436 → 00ff5f 138 | 39, // 29adff → 00afff 139 | 103, // 83769c → 8787af 140 | 211, // ff77a8 → f787af 141 | 223, // ffccaa → ffdfaf 142 | }; 143 | 144 | // FIXME: support the extended palette! 145 | return ansi_palette[m_front_draw_state.screen_palette[c & 0xf] & 0xf]; 146 | } 147 | 148 | } // namespace z8::pico8 149 | 150 | -------------------------------------------------------------------------------- /src/player.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2016—2020 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #pragma once 14 | 15 | #include // lol::input 16 | #include // std::map 17 | #include // std::vector 18 | #include // std::shared_ptr 19 | 20 | #include "zepto8.h" 21 | #include "pico8/cart.h" 22 | 23 | // The player class 24 | // ———————————————— 25 | // This is a high-level Lol Engine entity that runs the ZEPTO-8 VM. 26 | 27 | namespace z8 28 | { 29 | 30 | class player : public lol::WorldEntity 31 | { 32 | public: 33 | player(bool is_embedded = false, bool is_raccoon = false); 34 | virtual ~player(); 35 | 36 | virtual void tick_game(float seconds) override; 37 | virtual void tick_draw(float seconds, lol::Scene &scene) override; 38 | 39 | void load(std::string const &name); 40 | void run(); 41 | 42 | std::shared_ptr get_vm() { return m_vm; } 43 | 44 | // HACK: if get_texture() is called, rendering is disabled (this 45 | // is so that we do not overwrite the IDE screen) 46 | lol::Texture *get_texture(); 47 | lol::Texture *get_font_texture(); 48 | 49 | private: 50 | std::shared_ptr m_vm; 51 | 52 | std::map m_input_map; 53 | std::vector m_screen; 54 | 55 | // Video 56 | bool m_embedded = false; 57 | lol::ivec2 m_win_size; 58 | lol::ivec2 m_screen_pos; 59 | float m_scale; 60 | 61 | // Audio 62 | int m_stream; 63 | 64 | lol::Camera *m_scenecam; 65 | lol::TileSet *m_tile; 66 | #if 0 67 | lol::TileSet *m_font_tile; 68 | #endif 69 | }; 70 | 71 | } // namespace z8 72 | 73 | -------------------------------------------------------------------------------- /src/raccoon/font.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2016—2020 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #pragma once 14 | 15 | // Font dump code: 16 | // 17 | // palset(1,255,255,255); 18 | // var t = [], s = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.,!()-"; 19 | // for (c in s) 20 | // { 21 | // cls(); 22 | // print(s[c]==","?1:0,0,s[c],1); 23 | // p = 0; 24 | // for (x = 0; x < 4; ++x) 25 | // for (y = 0; y < 8; ++y) 26 | // p += (pget(x,y) << y) << (x * 8); 27 | // t[c] = p; 28 | // } 29 | // 30 | // cls(); 31 | // for (c in s) print(c % 4 * 32, flr(c / 4) * 7, t[c].toString(16),1); 32 | // 33 | 34 | namespace z8::raccoon 35 | { 36 | 37 | uint32_t font_data[] = 38 | { 39 | // 0x20…0x2f: !(),-. 40 | 0, 0x17, 0, 0, 0, 0, 0, 0, 0x211e, 0x1e21, 0, 0, 0x1020, 0x80808, 0x10, 0, 41 | // 0x30…0x3f: 0…9 42 | 0x1f111f, 0x101f12, 0x17151d, 0x1f1511, 0x1f0407, 0x1d1517, 0x1d151f, 43 | 0x1f0101, 0x1f151f, 0x1f1517, 0, 0, 0, 0, 0, 0, 44 | // 0x40…0x5f: A…Z 45 | 0, 46 | 0x1e051e, 0xa151f, 0x11110e, 0xe111f, 0x11151f, 0x1051f, 0x19110e, 47 | 0x1f041f, 0x111f11, 0x11f11, 0x1b041f, 0x10101f, 0x1f021f, 0x1e011f, 48 | 0xe110e, 0x2051f, 0x16190e, 0x1a051f, 0x91512, 0x11f01, 0xf100f, 49 | 0xf180f, 0x1f081f, 0x1b041b, 0x71c07, 0x131519, 50 | 0, 0, 0, 0, 0, 51 | // 0x60…0x7f: a…z 52 | 0, 53 | 0x1c1418, 0x8141f, 0x141408, 0x1f1408, 0x140c, 0x51e, 0x385408, 54 | 0x18041f, 0x1d, 0x3d40, 0x14081f, 0x1f, 0x1c041c, 0x18041c, 55 | 0x81408, 0x8147c, 0x7c1408, 0x418, 0x41c10, 0x120f, 0xc100c, 56 | 0xc180c, 0x1c101c, 0x140814, 0x3c500c, 0x101c04, 57 | 0, 0, 0, 0, 0, 58 | }; 59 | 60 | }; 61 | 62 | -------------------------------------------------------------------------------- /src/raccoon/memory.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2016—2020 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #pragma once 14 | 15 | #include // lol::u8vec3 16 | #include // assert 17 | #include // std::array 18 | 19 | #include "zepto8.h" 20 | 21 | // The Raccoon memory class 22 | // ———————————————————————— 23 | // These classes map 1-to-1 with the Raccoon memory layout and provide 24 | // handful accessors for higher level information. 25 | // For instance: 26 | // - "memory[x]" accesses the xth byte in memory 27 | // - "memory.palette[3].g" gets the green component of the 3rd palette colour 28 | 29 | namespace z8::raccoon 30 | { 31 | 32 | #pragma pack(push,1) 33 | struct snd_state 34 | { 35 | uint8_t index; 36 | uint8_t offset; 37 | uint8_t length; 38 | uint16_t time; 39 | }; 40 | #pragma pack(pop) 41 | 42 | #pragma pack(push,1) 43 | struct mus_state 44 | { 45 | uint8_t index : 7; 46 | uint8_t playing : 1; 47 | uint16_t time; 48 | uint16_t max_time; 49 | }; 50 | #pragma pack(pop) 51 | 52 | #pragma pack(push,1) 53 | struct pad_state 54 | { 55 | std::array buttons; 56 | std::array prev_buttons; 57 | std::array layouts; 58 | }; 59 | #pragma pack(pop) 60 | 61 | // Register: SPPPPPPP EEIIIIII OOTTTTTT __EEEVVV 62 | struct snd_reg 63 | { 64 | uint8_t period : 7; 65 | uint8_t on_off : 1; 66 | 67 | uint8_t instrument : 6; 68 | uint8_t envelope : 2; 69 | 70 | uint8_t pitch : 6; 71 | uint8_t offset : 2; 72 | 73 | uint8_t volume : 3; 74 | uint8_t effect : 3; 75 | uint8_t : 2; 76 | }; 77 | 78 | // Note: __TTTTTT __EEEVVV 79 | struct note 80 | { 81 | uint8_t pitch : 6; 82 | uint8_t : 2; 83 | 84 | uint8_t volume : 3; 85 | uint8_t effect : 3; 86 | uint8_t : 2; 87 | }; 88 | 89 | // Track: PPPPPPPPP EEIIIIII 64xNote 90 | struct track 91 | { 92 | uint8_t period : 8; 93 | 94 | uint8_t instrument : 6; 95 | uint8_t envelope : 2; 96 | 97 | struct note notes[32]; 98 | }; 99 | 100 | // Music: FUTTTTTT FUTTTTTT FUTTTTTTT _UTTTTTT 101 | struct node 102 | { 103 | uint8_t track : 6; 104 | uint8_t used : 1; 105 | uint8_t flag : 1; 106 | }; 107 | 108 | #pragma pack(push,1) 109 | struct memory 110 | { 111 | // Spritesheet data is 128x96 pixels, arranged in 192 8x8 sprites from 112 | // left to right, top to bottom. 113 | u4mat2<128, 96> sprites; 114 | 115 | // Map data is 128x64 tiles, where each tile is a byte-sized sprite index. 116 | uint8_t map[64][128]; 117 | 118 | // Palette data is 16 colors represented in 4 bytes each: 3 bytes for the 119 | // red, green and blue channels, and a fourth byte with extra data. The 120 | // fourth byte's 4 lowest bits are used as an indirection index (to render 121 | // another color instead of this one), and its highest bit is used for 122 | // transparency. 123 | struct 124 | { 125 | lol::u8vec3 color; 126 | uint8_t index : 4; 127 | uint8_t : 3; 128 | uint8_t trans : 1; 129 | } 130 | palette[16]; 131 | 132 | // Sprite flags are 192 8bits bitfields. 133 | uint8_t flags[192]; 134 | 135 | // Sound data is 64 tracks. 136 | struct track sound[64]; 137 | 138 | // Music data is 64 32bit nodes that connect tracks together via their indices. 139 | struct node music[64][4]; 140 | 141 | uint8_t ____[0x0580]; 142 | union 143 | { 144 | uint8_t end_of_rom; 145 | uint8_t _____[0x0fc7]; 146 | }; 147 | 148 | lol::i16vec2 camera; 149 | struct snd_state soundstate[4]; 150 | struct mus_state musicstate; 151 | struct snd_reg soundreg[4]; 152 | struct pad_state gamepad; 153 | u4mat2<128, 128> screen; 154 | 155 | inline uint8_t &operator[](int n) 156 | { 157 | assert(n >= 0 && n < (int)sizeof(memory)); 158 | return ((uint8_t *)this)[n]; 159 | } 160 | 161 | inline uint8_t const &operator[](int n) const 162 | { 163 | assert(n >= 0 && n < (int)sizeof(memory)); 164 | return ((uint8_t const *)this)[n]; 165 | } 166 | }; 167 | #pragma pack(pop) 168 | 169 | // Check all section offsets and sizes 170 | #define static_check_section(name, offset, size) \ 171 | static_assert(offsetof(memory, name) == offset, \ 172 | "z8::raccoon::memory::"#name" should have offset "#offset); \ 173 | static_assert(sizeof(memory::name) == size, \ 174 | "z8::raccoon::memory::"#name" should have size "#size); 175 | 176 | static_check_section(sprites, 0x0000, 0x1800); 177 | static_check_section(map, 0x1800, 0x2000); 178 | static_check_section(palette, 0x3800, 0x40); 179 | static_check_section(flags, 0x3840, 0xc0); 180 | static_check_section(sound, 0x3900, 0x1080); 181 | static_check_section(music, 0x4980, 0x100); 182 | 183 | static_check_section(end_of_rom, 0x5000, 0x1); 184 | 185 | static_check_section(camera, 0x5fc7, 0x4); 186 | static_check_section(soundstate, 0x5fcb, 0x14); 187 | static_check_section(musicstate, 0x5fdf, 0x05); 188 | static_check_section(soundreg, 0x5fe4, 0x10); 189 | static_check_section(gamepad, 0x5ff4, 0xc); 190 | static_check_section(screen, 0x6000, 0x2000); 191 | #undef static_check_section 192 | 193 | // Final sanity check 194 | static_assert(sizeof(memory) == 0x8000, "z8::raccoon::memory should have size 0x8000"); 195 | 196 | } // namespace z8::raccoon 197 | 198 | -------------------------------------------------------------------------------- /src/raccoon/vm.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2016–2024 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #pragma once 14 | 15 | #include // std::optional 16 | #include // lol::ivec2 17 | 18 | #include "zepto8.h" 19 | #include "player.h" 20 | #include "raccoon/memory.h" 21 | 22 | namespace z8::raccoon 23 | { 24 | 25 | class vm : public z8::vm_base 26 | { 27 | friend class z8::player; 28 | 29 | public: 30 | vm(); 31 | virtual ~vm(); 32 | 33 | virtual void load(std::string const &file) override; 34 | virtual void run() override; 35 | virtual void reset() override; 36 | virtual bool step(float seconds) override; 37 | virtual float getTime() override { return 1.0f; }; 38 | 39 | virtual void render(lol::u8vec4 *screen) const override; 40 | 41 | virtual std::string const &get_code() const override; 42 | virtual u4mat2<128, 128> const &get_front_screen() const override; 43 | virtual lol::ivec2 get_screen_resolution() const override; 44 | virtual int get_ansi_color(uint8_t c) const override; 45 | 46 | virtual void get_audio(void* inbuffer, size_t in_bytes) override; 47 | 48 | virtual void button(int player, int index, int state) override; 49 | virtual void mouse(lol::ivec2 coords, lol::ivec2 relative, int buttons, int scroll) override; 50 | virtual void text(char ch) override; 51 | virtual void sixaxis(lol::vec3 angle) override {}; 52 | virtual void axis(int player, float valueX, float valueY) override {}; 53 | 54 | virtual std::tuple ram() override; 55 | virtual std::tuple rom() override; 56 | 57 | virtual bool is_running() override { return true; }; 58 | virtual void request_exit() override {}; 59 | 60 | virtual int get_filter_index() override { return 0; } 61 | virtual int get_fullscreen() override { return 0; } 62 | virtual void set_config_dir(std::string new_path_config_dir) override {}; 63 | virtual void use_default_carts_dir() override {}; 64 | virtual void set_fullscreen(int value, bool save = true, bool runCallback = false) override {}; 65 | 66 | virtual void add_extcmd(std::string const &, std::function) override {}; 67 | virtual void add_stat(int16_t, std::function) override {}; 68 | 69 | private: 70 | void js_wrap(); 71 | 72 | private: 73 | void api_debug(std::string s); 74 | 75 | int api_read(int p); 76 | void api_write(int p, int x); 77 | void api_palset(int n, int r, int g, int b); 78 | int api_fget(int n, std::optional f); 79 | void api_fset(int n, int f, std::optional v); 80 | int api_mget(int x, int y); 81 | void api_mset(int x, int y, int n); 82 | int api_pget(int x, int y); 83 | void api_pset(int x, int y, int c); 84 | 85 | void api_palm(int c0, int c1); 86 | void api_palt(int c, int v); 87 | bool api_btn(int i, std::optional p); 88 | bool api_btnp(int i, std::optional p); 89 | std::string api_btns(int i, std::optional p); 90 | void api_cls(std::optional c); 91 | void api_cam(int x, int y); 92 | void api_map(int celx, int cely, int sx, int sy, int celw, int celh); 93 | void api_line(int x0, int y0, int x1, int y1, int c); 94 | void api_circ(int x, int y, int r, int c); 95 | void api_circfill(int x, int y, int r, int c); 96 | void api_rect(int x, int y, int w, int h, int c); 97 | void api_rectfill(int x, int y, int w, int h, int c); 98 | void api_spr(int n, int x, int y, 99 | std::optional w, std::optional h, 100 | std::optional fx, std::optional fy); 101 | void api_print(int x, int y, std::string str, int c); 102 | double api_rnd(std::optional x); 103 | double api_mid(double x, double y, double z); 104 | void api_mus(int n); 105 | void api_sfx(int a, int b, int c, int d); 106 | 107 | public: 108 | // The API we export to extension languages 109 | template struct exported_api 110 | { 111 | template using bind = typename T::template bind; 112 | 113 | std::vector data = 114 | { 115 | { "debug", bind<&vm::api_debug>() }, 116 | 117 | { "read", bind<&vm::api_read>() }, 118 | { "write", bind<&vm::api_write>() }, 119 | 120 | { "palset", bind<&vm::api_palset>() }, 121 | { "fget", bind<&vm::api_fget>() }, 122 | { "fset", bind<&vm::api_fset>() }, 123 | { "mget", bind<&vm::api_mget>() }, 124 | { "mset", bind<&vm::api_mset>() }, 125 | 126 | { "cls", bind<&vm::api_cls>() }, 127 | { "cam", bind<&vm::api_cam>() }, 128 | { "map", bind<&vm::api_map>() }, 129 | { "palm", bind<&vm::api_palm>() }, 130 | { "palt", bind<&vm::api_palt>() }, 131 | { "pget", bind<&vm::api_pget>() }, 132 | { "pset", bind<&vm::api_pset>() }, 133 | { "spr", bind<&vm::api_spr>() }, 134 | { "line", bind<&vm::api_line>() }, 135 | { "circ", bind<&vm::api_circ>() }, 136 | { "circfill", bind<&vm::api_circfill>() }, 137 | { "rect", bind<&vm::api_rect>() }, 138 | { "rectfill", bind<&vm::api_rectfill>() }, 139 | { "print", bind<&vm::api_print>() }, 140 | 141 | { "rnd", bind<&vm::api_rnd>() }, 142 | { "mid", bind<&vm::api_mid>() }, 143 | { "mus", bind<&vm::api_mus>() }, 144 | { "sfx", bind<&vm::api_sfx>() }, 145 | { "btn", bind<&vm::api_btn>() }, 146 | { "btnp", bind<&vm::api_btnp>() }, 147 | { "btns", bind<&vm::api_btns>() }, 148 | }; 149 | }; 150 | 151 | private: 152 | struct JSRuntime *m_rt; 153 | struct JSContext *m_ctx; 154 | 155 | std::string m_code; 156 | std::string m_name, m_link, m_host; 157 | int32_t m_version = -1; 158 | 159 | memory m_rom; 160 | memory m_ram; 161 | }; 162 | 163 | } // namespace z8::raccoon 164 | -------------------------------------------------------------------------------- /src/splore.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2017—2020 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #if HAVE_CONFIG_H 14 | # include "config.h" 15 | #endif 16 | 17 | #include // lol::old_image 18 | 19 | #include 20 | 21 | #include "zepto8.h" 22 | #include "splore.h" 23 | 24 | namespace z8 25 | { 26 | 27 | using lol::PixelFormat; 28 | 29 | bool splore::dump(std::string const &filename) 30 | { 31 | // Open cartridge as PNG image 32 | lol::old_image img; 33 | img.load(filename); 34 | lol::ivec2 size = img.size(); 35 | 36 | if (size != lol::ivec2(8 * 128, 4 * (128 + 8))) 37 | return false; 38 | 39 | auto const &pixels = img.lock2d(); 40 | 41 | for (int cart = 0; cart < 32; ++cart) 42 | { 43 | std::string info_lines[3]; 44 | 45 | for (int line = 0; line < 3; ++line) 46 | { 47 | lol::u8vec4 const *sol = &pixels[cart % 8 * 128][cart / 8 * (128 + 8) + 128 + line]; 48 | for (int x = 0; x < 128; ++x) 49 | { 50 | auto p = sol[x]; 51 | if (p.r == 0) 52 | break; 53 | info_lines[line] += (char)p.r; 54 | } 55 | 56 | printf("%d: %s\n", cart, info_lines[line].c_str()); 57 | } 58 | } 59 | 60 | img.unlock2d(pixels); 61 | 62 | return true; 63 | } 64 | 65 | } // namespace z8 66 | 67 | -------------------------------------------------------------------------------- /src/splore.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2017—2020 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #pragma once 14 | 15 | // The splore class 16 | // ———————————————— 17 | // This will be a Splore implementation to access BBS games. 18 | 19 | namespace z8 20 | { 21 | 22 | class splore 23 | { 24 | public: 25 | splore() 26 | {} 27 | 28 | bool dump(std::string const &filename); 29 | }; 30 | 31 | } // namespace z8 32 | 33 | -------------------------------------------------------------------------------- /src/synth.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2017—2020 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #if HAVE_CONFIG_H 14 | # include "config.h" 15 | #endif 16 | 17 | #include "synth.h" 18 | 19 | #include // lol::rand 20 | #include // std::fabs, std::fmod 21 | 22 | namespace z8 23 | { 24 | 25 | float synth::waveform(pico8::state::synth_param ¶ms) 26 | { 27 | using std::fabs, std::fmod; 28 | 29 | float advance = params.phi; 30 | float t = fmod(advance, 1.f); 31 | float ret = 0.f; 32 | 33 | bool noiz = params.filters & 0x2; 34 | bool buzz = params.filters & 0x4; 35 | 36 | // Multipliers were measured from PICO-8 WAV exports. Waveforms are 37 | // inferred from those exports by guessing what the original formulas 38 | // could be. 39 | switch (params.instrument) 40 | { 41 | case INST_TRIANGLE: 42 | ret = 1.0f - fabs(4.f * t - 2.0f); 43 | if (buzz) { 44 | // seems to be averaged with a tilted saw 45 | static float const a = 0.875f; 46 | float bret = t < a ? 2.f * t / a - 1.f 47 | : 2.f * (1.f - t) / (1.f - a) - 1.f; 48 | ret = ret * 0.75f + bret * 0.25f; 49 | } 50 | return ret * 0.5f; 51 | case INST_TILTED_SAW: 52 | { 53 | float a = buzz ? 0.975f : 0.875f; 54 | ret = t < a ? 2.f * t / a - 1.f 55 | : 2.f * (1.f - t) / (1.f - a) - 1.f; 56 | return ret * 0.5f; 57 | } 58 | case INST_SAW: 59 | ret = (t < 0.5f ? t : t - 1.f); 60 | // slight offset looping on 2x period 61 | if (buzz) ret = ret * 0.83f - (fabs(fmod(advance, 2.f) - 1.0f) < 0.5 ? 0.085f : 0.0f); 62 | return 0.653f * ret; 63 | case INST_SQUARE: 64 | return t < (buzz ? 0.4f : 0.5f) ? 0.25f : -0.25f; 65 | case INST_PULSE: 66 | return t < (buzz ? 0.255f : 0.316f) ? 0.25f : -0.25f; 67 | case INST_ORGAN: 68 | ret = t < 0.5f ? 3.f - fabs(24.f * t - 6.f) 69 | : 1.f - fabs(16.f * t - 12.f); 70 | if (buzz) 71 | { 72 | // add a cut on the first of the two triangles 73 | ret = t < 0.5f ? ret * 2.0f + 3.0f : ret; 74 | ret = (t < 0.5f && ret>-1.875f) ? ret * 0.2f - 1.0f : ret + 0.5f; 75 | } 76 | return ret / 9.f; 77 | case INST_NOISE: 78 | { 79 | //const float tscale = 22050 / key_to_freq(63); 80 | const float tscale = 8.858923f; 81 | float scale = (advance - params.last_advance) * tscale; 82 | float new_sample = (params.last_sample + scale * lol::rand(-1.f, 1.f)) / (1.0f + scale); 83 | 84 | float factor = 1.0f - params.key / 63.0f; 85 | ret = new_sample * 1.5f * (1.0f + factor * factor); 86 | 87 | if (noiz) 88 | { 89 | // sound a bit like a saw tooth but not quite 90 | ret *= 2.0f*(t < 0.5f ? t : t - 1.f); 91 | } 92 | 93 | params.last_advance = advance; 94 | params.last_sample = new_sample; 95 | 96 | return ret; 97 | } 98 | case INST_PHASER: 99 | { // sum of two triangle waves with a slightly different frequency 100 | // the ratio between the frequency seems to be around 110 for c2 101 | // but it is 97 for c0 and 127 for c5, not sure how to adjust that 102 | ret = 2.f - fabs(8.f * t - 4.f); 103 | ret += 1.f - fabs(4.f * fmod(advance * 109.f/110.f, 1.f) - 2.f); 104 | if (buzz) 105 | { 106 | // original triangle has freq 1, 3, 5, 7, 9 ... 107 | // add waves at 2, 6, 10, 14 108 | ret += 0.25f - fabs(1.f * fmod(advance * 2.0f + 0.5f, 1.f) - 0.5f); 109 | // add waves at 4, 12, 20, 28 110 | ret += 0.125f - fabs(0.5f * fmod(advance * 4.0f, 1.f) - 0.25f); 111 | } 112 | return ret / 6.f; 113 | } 114 | } 115 | 116 | return 0.0f; 117 | } 118 | 119 | } // namespace z8 120 | 121 | -------------------------------------------------------------------------------- /src/synth.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2016—2020 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #pragma once 14 | 15 | #include "pico8/vm.h" 16 | 17 | namespace z8 18 | { 19 | 20 | // 21 | // A waveform generator 22 | // 23 | 24 | class synth 25 | { 26 | public: 27 | enum 28 | { 29 | INST_TRIANGLE = 0, // Triangle signal 30 | INST_TILTED_SAW = 1, // Slanted triangle 31 | INST_SAW = 2, // Sawtooth 32 | INST_SQUARE = 3, // Square signal 33 | INST_PULSE = 4, // Asymmetric square signal 34 | INST_ORGAN = 5, // Some triangle stuff again 35 | INST_NOISE = 6, 36 | INST_PHASER = 7, 37 | }; 38 | 39 | static float waveform(pico8::state::synth_param ¶ms); 40 | }; 41 | 42 | } // namespace z8 43 | 44 | -------------------------------------------------------------------------------- /src/telnet.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2016—2020 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #pragma once 14 | 15 | #include // lol::msg 16 | #include // lol::ends_with 17 | #include // lol::timer 18 | #include // lol::ivec2 19 | #include // std::vector 20 | 21 | #if HAVE_UNISTD_H 22 | # include 23 | #endif 24 | #if HAVE_SYS_SELECT_H 25 | # include 26 | #endif 27 | 28 | #include "zepto8.h" 29 | #include "pico8/vm.h" 30 | #include "raccoon/vm.h" 31 | 32 | // The telnet class 33 | // ———————————————— 34 | // This is a high-level telnet server that runs a ZEPTO-8 VM. 35 | 36 | namespace z8 37 | { 38 | 39 | struct telnet 40 | { 41 | std::vector m_screen; 42 | lol::ivec2 m_term_size = lol::ivec2(128, 64); 43 | 44 | void run(std::string const &cart) 45 | { 46 | disable_echo(); 47 | 48 | std::unique_ptr vm; 49 | if (lol::ends_with(cart, ".rcn.json")) 50 | vm.reset((z8::vm_base *)new raccoon::vm()); 51 | else 52 | vm.reset((z8::vm_base *)new pico8::vm()); 53 | vm->load(cart); 54 | vm->run(); 55 | 56 | auto const &ram = vm->ram(); 57 | 58 | while (true) 59 | { 60 | lol::timer t; 61 | 62 | for (int i = 0; i < 16; ++i) 63 | vm->button(i, 0); 64 | 65 | for (;;) 66 | { 67 | int key = get_key(); 68 | if (key < 0) 69 | break; 70 | 71 | switch (key) 72 | { 73 | /* For now, Escape quits */ 74 | case 0x1b: return; 75 | 76 | case 0x144: vm->button(0, 1); break; // left 77 | case 0x143: vm->button(1, 1); break; // right 78 | case 0x141: vm->button(2, 1); break; // up 79 | case 0x142: vm->button(3, 1); break; // down 80 | case 'z': case 'Z': 81 | case 'c': case 'C': 82 | case 'n': case 'N': vm->button(4, 1); break; 83 | case 'x': case 'X': 84 | case 'v': case 'V': 85 | case 'm': case 'M': vm->button(5, 1); break; 86 | case '\r': case '\n': vm->button(6, 1); break; 87 | case 's': case 'S': vm->button(8, 1); break; 88 | case 'f': case 'F': vm->button(9, 1); break; 89 | case 'e': case 'E': vm->button(10, 1); break; 90 | case 'd': case 'D': vm->button(11, 1); break; 91 | case 'a': case 'A': vm->button(12, 1); break; 92 | case '\t': 93 | case 'q': case 'Q': vm->button(13, 1); break; 94 | default: 95 | lol::msg::info("Got unknown key %02x\n", key); 96 | break; 97 | } 98 | } 99 | 100 | vm->step(1.f / 60.f); 101 | 102 | vm->print_ansi(m_term_size, 103 | m_screen.empty() ? nullptr : m_screen.data()); 104 | 105 | // FIXME: PICO-8 specific 106 | m_screen.resize(0x2000); 107 | ::memcpy(m_screen.data(), std::get<0>(ram) + 0x6000, 0x2000); 108 | 109 | t.wait(1.f / 60.f); 110 | } 111 | } 112 | 113 | void disable_echo() 114 | { 115 | #if HAVE_UNISTD_H 116 | uint8_t const message[] = 117 | { 118 | 0xff, 0xfb, 0x03, // WILL suppress go ahead (no line buffering) 119 | 0xff, 0xfe, 0x22, // DONT linemode (no idea what it does) 120 | 0xff, 0xfb, 0x01, // WILL echo (actually disables local echo) 121 | 0xff, 0xfd, 0x1f, // DO NAWS (window size negociation) 122 | }; 123 | 124 | write(STDOUT_FILENO, message, sizeof(message)); 125 | #endif 126 | } 127 | 128 | int get_key() 129 | { 130 | #if HAVE_UNISTD_H 131 | static std::string seq; 132 | 133 | fd_set fds; 134 | FD_ZERO(&fds); 135 | FD_SET(STDIN_FILENO, &fds); 136 | 137 | struct timeval tv; 138 | tv.tv_sec = tv.tv_usec = 0; 139 | 140 | select(STDIN_FILENO + 1, &fds, NULL, NULL, &tv); 141 | 142 | if (!FD_ISSET(0, &fds)) 143 | return -1; 144 | 145 | char ch; 146 | if (read(STDIN_FILENO, &ch, 1) <= 0) 147 | exit(EXIT_SUCCESS); 148 | 149 | if (ch != '\x1b' && ch != '\xff' && seq.length() == 0) 150 | return ch; 151 | 152 | seq += ch; 153 | 154 | // TELNET commands 155 | if (seq[0] == '\xff') // telnet commands 156 | { 157 | if (seq[1] >= '\xfb' && seq[1] <= '\xfe') 158 | { 159 | if (seq[2] == 0) 160 | return -1; // wait for more data 161 | goto reset; 162 | } 163 | else if (seq[1] == '\xfa') // subnegociation 164 | { 165 | if (seq[2] == 0) 166 | return -1; // wait for more data 167 | if (seq[2] != '\x1f') 168 | goto reset; // can’t happen 169 | if (seq.length() < 9) 170 | return -1; // wait for more data 171 | m_term_size.x = (uint8_t)seq[3] * 256 + (uint8_t)seq[4]; 172 | m_term_size.y = (uint8_t)seq[5] * 256 + (uint8_t)seq[6]; 173 | printf("\x1b[2J"); // clear screen 174 | m_screen.clear(); 175 | goto reset; 176 | } 177 | else if (seq.length() >= 3) 178 | { 179 | goto reset; 180 | } 181 | 182 | return -1; 183 | } 184 | 185 | // Escape sequences 186 | if (seq[0] == '\x1b') 187 | { 188 | if (seq[1] == '\x5b') 189 | { 190 | if (seq[2] == 0) 191 | return -1; // wait for more data 192 | int ret = 0x100 + seq[2]; 193 | seq = ""; 194 | return ret; 195 | } 196 | else if (seq[1] == '\x1b') 197 | { 198 | seq = ""; 199 | return '\x1b'; 200 | } 201 | 202 | return -1; 203 | } 204 | 205 | reset: 206 | seq = ""; 207 | #endif 208 | return -1; 209 | } 210 | }; 211 | 212 | } // namespace z8 213 | 214 | -------------------------------------------------------------------------------- /src/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Emscripten-Generated Code 7 | 46 | 47 | 48 |
49 |
emscripten
50 |
Downloading...
51 |
52 | 53 |
54 |
55 | 56 |
57 |
58 |
59 | 60 | 66 |
67 | 68 | 69 | 72 | 73 | 150 | {{{ SCRIPT }}} 151 | 152 | 153 | -------------------------------------------------------------------------------- /src/textfile.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2017–2024 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #include "textfile.h" 14 | #include // lol::file 15 | #if __NX__ 16 | #include 17 | #include 18 | #elif !__SCE__ 19 | #include 20 | #endif 21 | #include // std::stringstream 22 | 23 | namespace z8 24 | { 25 | 26 | textfile::textfile() 27 | { 28 | 29 | } 30 | 31 | bool textfile::tick(bool force) 32 | { 33 | m_frames_since_last_save += 1; 34 | if (!force) 35 | { 36 | if (!m_is_dirty) return false; 37 | if (m_frames_since_last_save < m_min_frames_between_saves) return false; 38 | } 39 | m_frames_since_last_save = 0; 40 | m_is_dirty = false; 41 | 42 | return true; 43 | } 44 | 45 | bool textfile::read_save(std::string filepath, uint8_t* data) 46 | { 47 | // todo: verify cartdata is not empty 48 | std::string s; 49 | if (!lol::file::read(filepath, s)) 50 | return false; 51 | 52 | auto ss = std::stringstream(s); 53 | 54 | int j = 0; 55 | for (std::string line; std::getline(ss, line, '\n');) 56 | { 57 | if (j >= 8) break; 58 | for (int i = 0; i < 32; ++i) 59 | { 60 | std::string sub = line.substr(i * 2, 2); 61 | unsigned int x = std::stoul(sub, nullptr, 16); 62 | int gindex = i + j * 32; 63 | // pico 8 store the numbers in reverse order from ram 64 | int index = (gindex & ~0x3) + 3 - gindex % 4; 65 | data[index] = x & 0xff; 66 | } 67 | j++; 68 | } 69 | 70 | return true; 71 | } 72 | 73 | bool textfile::write_save(std::string filepath, uint8_t* data) 74 | { 75 | std::string content; 76 | for (int i = 0; i < 256; ++i) 77 | { 78 | char hex[3]; 79 | // pico 8 store the numbers in reverse order from ram 80 | int index = (i & ~0x3) + 3 - i % 4; 81 | std::snprintf(hex, sizeof(hex), "%02x", data[index]); 82 | content += hex; 83 | if (i % 32 == 31) 84 | { 85 | content += "\n"; 86 | } 87 | } 88 | 89 | // todo: verify cartdata is not empty 90 | if (!lol::file::write(filepath, content)) 91 | return false; 92 | 93 | return true; 94 | } 95 | 96 | } // namespace z8 97 | -------------------------------------------------------------------------------- /src/textfile.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2016—2024 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #pragma once 14 | 15 | #include "zepto8.h" 16 | 17 | namespace z8 18 | { 19 | 20 | // 21 | // Text File with a minimum delay between writes 22 | // 23 | 24 | class textfile 25 | { 26 | public: 27 | 28 | textfile(); 29 | 30 | bool tick(bool force); 31 | bool read_save(std::string filepath, uint8_t* data); 32 | bool write_save(std::string filepath, uint8_t* data); 33 | bool read_config(std::string filepath, uint8_t* data); 34 | bool write_config(std::string filepath, uint8_t* data); 35 | 36 | void set_dirty() { m_is_dirty = true; } 37 | 38 | private: 39 | bool m_is_dirty = false; 40 | int m_min_frames_between_saves = 640; 41 | int m_frames_since_last_save = 0; 42 | 43 | }; 44 | 45 | } // namespace z8 46 | 47 | -------------------------------------------------------------------------------- /src/unz8: -------------------------------------------------------------------------------- 1 | function unz8(s,y,x)local w=0local u=0local v local function f(i)u-=i w>>>=i end local function g(i)while u0then w+=peek(y)>>>16-u u+=8 y+=1 x-=1 elseif v then w+=v%1<>>=16 end end return(w<<32-i)>>>16-i end local function u(i)return g(i),f(i)end local function v(i)local j=g(i.j)f(i[j]%1*16)return i[j]\1 end local function g(i)local t={j=1}for j=1,288 do t.j=max(t.j,i[j])end local u=0 for l=1,18 do for j=1,288 do if l==i[j]then local z=0 for j=1,l do z+=(u>>>j-1&1)<j*32-16)+(t[k]or 0)w+=1/4 end for j=1,288 do if u(1)<1then if u(1)<1then return(t)end for i=1,u(16)do f(u(8))end else local k={}local q={}if u(1)<1then for j=1,288 do k[j]=8 end for j=145,280 do k[j]+=sgn(256-j)end for j=1,32 do q[j]=5 end else local l=257+u(5)local i=1+u(5)local t={}for j=-3,u(4)do t[j%19+1]=u(3)end local g=g(t)local function r(k,l)while#kj then local k=i\j-1i=(i%j+j<> 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #if HAVE_CONFIG_H 14 | # include "config.h" 15 | #endif 16 | 17 | #include // lol::ivec2 18 | #include // std::swap, std::min 19 | #include // memcmp() 20 | 21 | #include "zepto8.h" 22 | 23 | namespace z8 24 | { 25 | 26 | void vm_base::print_ansi(lol::ivec2 term_size, 27 | uint8_t const *prev_screen) const 28 | { 29 | using std::min; 30 | 31 | int palette[16]; 32 | for (int i = 0; i < 16; ++i) 33 | palette[i] = get_ansi_color(i); 34 | 35 | printf("\x1b[?25l"); // hide cursor 36 | 37 | auto &screen = get_front_screen(); 38 | 39 | for (int y = 0; y < 2 * min(64, term_size.y); y += 2) 40 | { 41 | if (prev_screen && !memcmp(&screen.data[y], 42 | &prev_screen[y * 64], 128)) 43 | continue; 44 | 45 | printf("\x1b[%d;1H", y / 2 + 1); 46 | 47 | int oldfg = -1, oldbg = -1; 48 | 49 | for (int x = 0; x < min(128, term_size.x); ++x) 50 | { 51 | uint8_t fg = screen.get(x, y); 52 | uint8_t bg = screen.get(x, y + 1); 53 | char const *glyph = "▀"; 54 | 55 | if (fg < bg) 56 | { 57 | std::swap(fg, bg); 58 | glyph = "▄"; 59 | } 60 | 61 | if (fg == oldfg) 62 | { 63 | if (bg != oldbg) 64 | printf("\x1b[48;5;%dm", palette[bg]); 65 | } 66 | else 67 | { 68 | if (bg == oldbg) 69 | printf("\x1b[38;5;%dm", palette[fg]); 70 | else 71 | printf("\x1b[38;5;%d;48;5;%dm", palette[fg], palette[bg]); 72 | } 73 | 74 | printf("%s", glyph); 75 | 76 | oldfg = fg; 77 | oldbg = bg; 78 | } 79 | 80 | printf("\x1b[0m\x1b[K"); // reset properties and clear to end of line 81 | } 82 | 83 | printf("\x1b[?25h"); // show cursor 84 | fflush(stdout); 85 | } 86 | 87 | } // namespace z8 88 | 89 | -------------------------------------------------------------------------------- /src/z8dev.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2016—2020 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #if HAVE_CONFIG_H 14 | # include "config.h" 15 | #endif 16 | 17 | #include // lol::Application 18 | #include // lol::cli 19 | #include // std::cout 20 | #include // std::optional 21 | 22 | #include "zepto8.h" 23 | #include "player.h" 24 | #include "ide/ide.h" 25 | 26 | int main(int argc, char **argv) 27 | { 28 | lol::sys::init(argc, argv); 29 | 30 | std::optional cart; 31 | 32 | lol::cli::app opts("zepto8"); 33 | opts.add_option("cart", cart, "cartridge"); 34 | opts.set_version_flag("-V,--version", PACKAGE_VERSION); 35 | 36 | CLI11_PARSE(opts, argc, argv); 37 | 38 | lol::ivec2 win_size(1280, 768); 39 | auto app = lol::app::init("zepto8 IDE", win_size, 60.0f); 40 | 41 | auto ide = new z8::ide(); 42 | 43 | if (cart) 44 | { 45 | ide->load(*cart); 46 | } 47 | 48 | app->run(); 49 | 50 | return EXIT_SUCCESS; 51 | } 52 | 53 | -------------------------------------------------------------------------------- /src/z8lua.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(SolutionDir)src\3rdparty\lolengine\ 5 | 6 | 7 | 8 | Debug 9 | NX64 10 | 11 | 12 | Debug 13 | ORBIS 14 | 15 | 16 | Debug 17 | Win32 18 | 19 | 20 | Debug 21 | x64 22 | 23 | 24 | Release 25 | NX64 26 | 27 | 28 | Release 29 | ORBIS 30 | 31 | 32 | Release 33 | Win32 34 | 35 | 36 | Release 37 | x64 38 | 39 | 40 | 41 | {dc847af8-ff9c-4f24-ae83-b86fc5c6777d} 42 | Application 43 | Win32Proj 44 | 45 | 46 | 47 | 48 | .;%(AdditionalIncludeDirectories) 49 | _LIB;LUA_USE_STRTODHEX;%(PreprocessorDefinitions) 50 | CompileAsCpp 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | {141365e7-f8f3-4d95-b8db-1a093f92f436} 59 | 60 | 61 | 62 | 63 | Console 64 | true 65 | true 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /src/z8lua.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/z8tool.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(SolutionDir)src\3rdparty\lolengine\ 5 | 6 | 7 | 8 | Debug 9 | NX64 10 | 11 | 12 | Debug 13 | ORBIS 14 | 15 | 16 | Debug 17 | Win32 18 | 19 | 20 | Debug 21 | x64 22 | 23 | 24 | Release 25 | NX64 26 | 27 | 28 | Release 29 | ORBIS 30 | 31 | 32 | Release 33 | Win32 34 | 35 | 36 | Release 37 | x64 38 | 39 | 40 | 41 | {cc700045-8e2c-4516-9159-43fc4ffd4783} 42 | Application 43 | Win32Proj 44 | 45 | 46 | 47 | 48 | .;zlib;%(AdditionalIncludeDirectories) 49 | PACKAGE_VERSION="0.0";GZ8;Z_SOLO;NO_GZIP;HAVE_MEMCPY;local=;%(PreprocessorDefinitions) 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | {5168dfab-3b54-4caa-8654-da760bc53b74} 77 | 78 | 79 | {141365e7-f8f3-4d95-b8db-1a093f92f436} 80 | 81 | 82 | {9ae29931-c72e-43df-805b-27e4ddfbb582} 83 | 84 | 85 | 86 | -------------------------------------------------------------------------------- /src/z8tool.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | zlib 17 | 18 | 19 | zlib 20 | 21 | 22 | zlib 23 | 24 | 25 | zlib 26 | 27 | 28 | zlib 29 | 30 | 31 | zlib 32 | 33 | 34 | zlib 35 | 36 | 37 | 38 | 39 | {4072514c-b5f4-4201-a799-31c2812abc3b} 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/zepto8.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2016—2020 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #if HAVE_CONFIG_H 14 | # include "config.h" 15 | #endif 16 | 17 | #include // lol::Application 18 | #include // lol::cli 19 | #include // lol:ends_with 20 | #include // std::cout 21 | 22 | #include "zepto8.h" 23 | #include "player.h" 24 | #include "raccoon/vm.h" 25 | 26 | int main(int argc, char **argv) 27 | { 28 | lol::sys::init(argc, argv); 29 | 30 | std::optional cart; 31 | lol::ivec2 win_size(144 * 4, 144 * 4); 32 | 33 | lol::cli::app opts("zepto8"); 34 | opts.set_version_flag("-V,--version", PACKAGE_VERSION); 35 | opts.add_option("cart", cart, "Load a cartridge")->type_name(""); 36 | 37 | opts.add_option("-width", win_size.x, "Set the window width")->type_name(""); 38 | opts.add_option("-height", win_size.y, "Set the window height")->type_name(""); 39 | // -window n 40 | // -volume n 41 | // -joystick n 42 | // -pixel_perfect n 43 | // -preblit_scale n 44 | // -draw_rect x,y,w,h 45 | opts.add_option("-run", cart, "Load and run a cartridge")->type_name(""); 46 | // -x filename 47 | // -export param_str 48 | // -p param_str 49 | // -splore 50 | // -home path 51 | // -root_path path 52 | // -desktop path 53 | // -screenshot_scale n 54 | // -gif_scale n 55 | // -gif_len n 56 | // -gui_theme n 57 | // -timeout n 58 | // -software_blit n 59 | // -foreground_sleep_ms n 60 | // -background_sleep_ms n 61 | // -accept_future n 62 | 63 | CLI11_PARSE(opts, argc, argv); 64 | 65 | auto app = lol::app::init("zepto8", win_size, 60.0f); 66 | 67 | bool is_raccoon = cart && lol::ends_with(*cart, ".rcn.json"); 68 | 69 | z8::player *player = new z8::player(false, is_raccoon); 70 | 71 | if (cart) 72 | { 73 | player->load(*cart); 74 | player->run(); 75 | } 76 | 77 | app->run(); 78 | 79 | return EXIT_SUCCESS; 80 | } 81 | 82 | -------------------------------------------------------------------------------- /src/zepto8.h: -------------------------------------------------------------------------------- 1 | // 2 | // ZEPTO-8 — Fantasy console emulator 3 | // 4 | // Copyright © 2016–2024 Sam Hocevar 5 | // 6 | // This program is free software. It comes without any warranty, to 7 | // the extent permitted by applicable law. You can redistribute it 8 | // and/or modify it under the terms of the Do What the Fuck You Want 9 | // to Public License, Version 2, as published by the WTFPL Task Force. 10 | // See http://www.wtfpl.net/ for more details. 11 | // 12 | 13 | #pragma once 14 | 15 | #include // std::any 16 | #include // lol::ivec2 17 | #include // std::string 18 | #include // std::tuple 19 | #include // std::function 20 | #include // assert() 21 | #include 22 | #include // std::unique_ptr 23 | 24 | // The ZEPTO-8 types 25 | // ————————————————— 26 | // Various types and enums that describe ZEPTO-8. 27 | 28 | namespace z8 29 | { 30 | 31 | namespace pico8 32 | { 33 | class bios; // TODO: get rid of this 34 | } 35 | 36 | // 37 | // A simple 4-bit 2D array 38 | // 39 | 40 | template 41 | class u4mat2 42 | { 43 | public: 44 | inline uint8_t safe_get(int x, int y) const 45 | { 46 | return (x >= 0 && y >= 0 && x < W && y < H) ? get(x,y) : 0; 47 | } 48 | 49 | inline void safe_set(int x, int y, uint8_t c) 50 | { 51 | if (x >= 0 && y >= 0 && x < W && y < H) 52 | set(x, y, c); 53 | } 54 | 55 | inline uint8_t get(int x, int y) const 56 | { 57 | assert(x >= 0 && x < W && y >= 0 && y < H); 58 | uint8_t const p = data[y][x / 2]; 59 | return x & 1 ? p >> 4 : p & 0xf; 60 | } 61 | 62 | inline void set(int x, int y, uint8_t c) 63 | { 64 | assert(x >= 0 && x < W && y >= 0 && y < H); 65 | uint8_t &p = data[y][x / 2]; 66 | p = (p & (x & 1 ? 0x0f : 0xf0)) | (x & 1 ? c << 4 : c & 0x0f); 67 | } 68 | 69 | uint8_t data[H][W / 2]; 70 | }; 71 | 72 | // 73 | // The generic VM interface 74 | // 75 | 76 | class vm_base 77 | { 78 | friend class player; 79 | 80 | public: 81 | vm_base() = default; 82 | virtual ~vm_base() = default; 83 | 84 | virtual void load(std::string const &name) = 0; 85 | virtual void run() = 0; 86 | virtual void reset() = 0; 87 | virtual bool step(float seconds) = 0; 88 | virtual float getTime() = 0; 89 | 90 | // Rendering 91 | virtual void render(lol::u8vec4 *screen) const = 0; 92 | virtual u4mat2<128, 128> const &get_front_screen() const = 0; 93 | virtual lol::ivec2 get_screen_resolution() const = 0; 94 | 95 | virtual int get_ansi_color(uint8_t c) const = 0; 96 | // FIXME: get_ansi_color() should be get_rgb(), and render() 97 | // should be removed in favour of a generic function that 98 | // uses get_rgb() too. 99 | 100 | void print_ansi(lol::ivec2 term_size = lol::ivec2(128, 64), 101 | uint8_t const *prev_screen = nullptr) const; 102 | 103 | // Code 104 | virtual std::string const &get_code() const = 0; 105 | 106 | // Audio streaming 107 | virtual void get_audio(void* buffer, size_t frames) = 0; 108 | 109 | // IO 110 | virtual void button(int player, int index, int state) = 0; 111 | virtual void mouse(lol::ivec2 coords, lol::ivec2 relative, int buttons, int scroll) = 0; 112 | virtual void text(char ch) = 0; 113 | virtual void sixaxis(lol::vec3 angle) = 0; 114 | virtual void axis(int player, float valueX, float valueY) = 0; 115 | 116 | // Memory (TODO: switch to std::span one day…) 117 | virtual std::tuple ram() = 0; 118 | virtual std::tuple rom() = 0; 119 | 120 | virtual void request_exit() = 0; 121 | virtual bool is_running() = 0; 122 | 123 | virtual int get_filter_index() = 0; 124 | virtual int get_fullscreen() = 0; 125 | virtual void set_fullscreen(int value, bool save = true, bool runCallback = true) = 0; 126 | virtual void set_config_dir(std::string new_path_config_dir) = 0; 127 | virtual void use_default_carts_dir() = 0; 128 | 129 | std::function setfilter_callback; 130 | std::function getfiltername_callback; 131 | 132 | //full screen callbacks 133 | std::function setfullscreen_callback; 134 | std::function getfullscreen_callback; 135 | std::function pointerLock_callback; 136 | 137 | void registerSetFilterCallback(std::function setfilterListener) { 138 | setfilter_callback = std::move(setfilterListener); 139 | } 140 | void registerGetFilterNameCallback(std::function getfilternameListener) { 141 | getfiltername_callback = std::move(getfilternameListener); 142 | } 143 | // Register SET fullscreen Callback 144 | void registerSetFullscreenCallback(std::function setFullscreenListener) { 145 | setfullscreen_callback = std::move(setFullscreenListener); 146 | } 147 | // Register GET fullscreen Callback 148 | void registerGetFullscreenCallback(std::function getFullscreenListener) { 149 | getfullscreen_callback = std::move(getFullscreenListener); 150 | } 151 | void registerPointerLockCallback(std::function pointerLockListener) { 152 | pointerLock_callback = std::move(pointerLockListener); 153 | } 154 | 155 | // Extension commands 156 | virtual void add_extcmd(std::string const &, std::function) = 0; 157 | virtual void add_stat(int16_t, std::function) = 0; 158 | 159 | protected: 160 | std::unique_ptr m_bios; // TODO: get rid of this 161 | }; 162 | 163 | enum 164 | { 165 | SCREEN_WIDTH = 128, 166 | SCREEN_HEIGHT = 128, 167 | }; 168 | 169 | } // namespace z8 170 | -------------------------------------------------------------------------------- /src/zepto8.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | $(SolutionDir)src\3rdparty\lolengine\ 5 | 6 | 7 | 8 | Debug 9 | NX64 10 | 11 | 12 | Debug 13 | ORBIS 14 | 15 | 16 | Debug 17 | Win32 18 | 19 | 20 | Debug 21 | x64 22 | 23 | 24 | Release 25 | NX64 26 | 27 | 28 | Release 29 | ORBIS 30 | 31 | 32 | Release 33 | Win32 34 | 35 | 36 | Release 37 | x64 38 | 39 | 40 | 41 | {948C0C9A-2C25-41D9-BF1B-92B14A8C061B} 42 | Application 43 | Win32Proj 44 | 45 | 46 | 47 | 48 | .;%(AdditionalIncludeDirectories) 49 | PACKAGE_VERSION="0.0";%(PreprocessorDefinitions) 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | {5168dfab-3b54-4caa-8654-da760bc53b74} 64 | 65 | 66 | {141365e7-f8f3-4d95-b8db-1a093f92f436} 67 | 68 | 69 | {9ae29931-c72e-43df-805b-27e4ddfbb582} 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /src/zlib/gz8.h: -------------------------------------------------------------------------------- 1 | // cpp -DZEXPORT=ZEXPORT < zlib.h | sed -ne 's/.*ZEXPORTV*A* *\([^ (]*\).*/#define \1 \1_gz8/p' 2 | #define zlibVersion zlibVersion_gz8 3 | #define deflate deflate_gz8 4 | #define deflateEnd deflateEnd_gz8 5 | #define inflate inflate_gz8 6 | #define inflateEnd inflateEnd_gz8 7 | #define deflateSetDictionary deflateSetDictionary_gz8 8 | #define deflateGetDictionary deflateGetDictionary_gz8 9 | #define deflateCopy deflateCopy_gz8 10 | #define deflateReset deflateReset_gz8 11 | #define deflateParams deflateParams_gz8 12 | #define deflateTune deflateTune_gz8 13 | #define deflateBound deflateBound_gz8 14 | #define deflatePending deflatePending_gz8 15 | #define deflatePrime deflatePrime_gz8 16 | #define deflateSetHeader deflateSetHeader_gz8 17 | #define inflateSetDictionary inflateSetDictionary_gz8 18 | #define inflateGetDictionary inflateGetDictionary_gz8 19 | #define inflateSync inflateSync_gz8 20 | #define inflateCopy inflateCopy_gz8 21 | #define inflateReset inflateReset_gz8 22 | #define inflateReset2 inflateReset2_gz8 23 | #define inflatePrime inflatePrime_gz8 24 | #define inflateMark inflateMark_gz8 25 | #define inflateGetHeader inflateGetHeader_gz8 26 | #define inflateBack inflateBack_gz8 27 | #define inflateBackEnd inflateBackEnd_gz8 28 | #define zlibCompileFlags zlibCompileFlags_gz8 29 | #define compress compress_gz8 30 | #define compress2 compress2_gz8 31 | #define compressBound compressBound_gz8 32 | #define uncompress uncompress_gz8 33 | #define uncompress2 uncompress2_gz8 34 | #define gzdopen gzdopen_gz8 35 | #define gzbuffer gzbuffer_gz8 36 | #define gzsetparams gzsetparams_gz8 37 | #define gzread gzread_gz8 38 | #define gzfread gzfread_gz8 39 | #define gzwrite gzwrite_gz8 40 | #define gzfwrite gzfwrite_gz8 41 | #define gzputs gzputs_gz8 42 | #define gzgets gzgets_gz8 43 | #define gzputc gzputc_gz8 44 | #define gzgetc gzgetc_gz8 45 | #define gzungetc gzungetc_gz8 46 | #define gzflush gzflush_gz8 47 | #define gzrewind gzrewind_gz8 48 | #define gzeof gzeof_gz8 49 | #define gzdirect gzdirect_gz8 50 | #define gzclose gzclose_gz8 51 | #define gzclose_r gzclose_r_gz8 52 | #define gzclose_w gzclose_w_gz8 53 | #define gzerror gzerror_gz8 54 | #define gzclearerr gzclearerr_gz8 55 | #define adler32 adler32_gz8 56 | #define adler32_z adler32_z_gz8 57 | #define crc32 crc32_gz8 58 | #define crc32_z crc32_z_gz8 59 | #define deflateInit_ deflateInit__gz8 60 | #define inflateInit_ inflateInit__gz8 61 | #define deflateInit2_ deflateInit2__gz8 62 | #define inflateInit2_ inflateInit2__gz8 63 | #define inflateBackInit_ inflateBackInit__gz8 64 | #define gzgetc_ gzgetc__gz8 65 | #define gzopen gzopen_gz8 66 | #define gzseek gzseek_gz8 67 | #define gztell gztell_gz8 68 | #define gzoffset gzoffset_gz8 69 | #define adler32_combine adler32_combine_gz8 70 | #define crc32_combine crc32_combine_gz8 71 | #define zError zError_gz8 72 | #define inflateSyncPoint inflateSyncPoint_gz8 73 | #define get_crc_table get_crc_table_gz8 74 | #define inflateUndermine inflateUndermine_gz8 75 | #define inflateValidate inflateValidate_gz8 76 | #define inflateCodesUsed inflateCodesUsed_gz8 77 | #define inflateResetKeep inflateResetKeep_gz8 78 | #define deflateResetKeep deflateResetKeep_gz8 79 | -------------------------------------------------------------------------------- /t/Makefile.am: -------------------------------------------------------------------------------- 1 | 2 | include $(top_srcdir)/src/3rdparty/lolengine/build/autotools/common.am 3 | 4 | EXTRA_DIST += \ 5 | math.p8 \ 6 | math-old.p8 \ 7 | print.p8 \ 8 | syntax.p8 \ 9 | $(NULL) 10 | 11 | -------------------------------------------------------------------------------- /t/math-old.p8: -------------------------------------------------------------------------------- 1 | pico-8 cartridge // http://www.pico-8.com 2 | version 8 3 | __lua__ 4 | -- mATHS tEST sUITE 5 | -- sAM hOCEVAR 6 | 7 | function tostr(x) 8 | return x == nil and "nil" or ""..x 9 | end 10 | 11 | function section(name) 12 | printh("") 13 | printh("##########################") 14 | printh("### "..name) 15 | printh("") 16 | end 17 | 18 | 19 | section("atan2") 20 | 21 | -- TODO: we want -32768 instead of -32767 below, but it causes PICO-8 to crash 22 | values = { nil, -32767, -1, 0, 1, 32767 } 23 | printh("atan2 = "..atan2()) 24 | for i = 1,#values do 25 | printh("atan2 "..tostr(x).." = "..atan2(x)) 26 | for j = 1,#values do 27 | x, y = values[i], values[j] 28 | printh("atan2 "..tostr(x).." "..tostr(y).." = "..atan2(x,y)) 29 | end 30 | end 31 | 32 | 33 | section("sin/cos") 34 | 35 | values = { -32768, -32767, -128, -127, -5, -4, -3, -2, -1, 0 } 36 | steps = { 0, 1/4, 2/4, 3/4 } 37 | printh("sin = "..sin()) 38 | printh("cos = "..cos()) 39 | printh("sin nil = "..sin(nil)) 40 | printh("cos nil = "..cos(nil)) 41 | for i = 1,#values do 42 | for j = 1,#steps do 43 | x = values[i] + steps[j] 44 | printh("sin "..tostr(x).." = "..sin(x)) 45 | printh("cos "..tostr(x).." = "..cos(x)) 46 | if (x ~= 0) and (x >= -32767) then 47 | printh("sin "..tostr(-x).." = "..sin(-x)) 48 | printh("cos "..tostr(-x).." = "..cos(-x)) 49 | end 50 | end 51 | end 52 | 53 | 54 | section("boolean ops") 55 | 56 | for i = 0,256 do 57 | -- use flr() to make sure the wrap around at 32768 happens 58 | x = flr(i * 257) 59 | for j = 1,256 do 60 | y = flr(j * 257) 61 | printh("band "..tostr(x).." "..tostr(y).." = "..band(x, y)) 62 | printh("bor "..tostr(x).." "..tostr(y).." = "..bor(x, y)) 63 | printh("bxor "..tostr(x).." "..tostr(y).." = "..bxor(x, y)) 64 | end 65 | -- bnot(x) can’t be represented exactly so we compute this value instead 66 | z = flr((bnot(x) - x) * 256 * 256) 67 | printh("bnot "..tostr(x).." = "..tostr(flr(-x)).." + "..tostr(z).." / 65536") 68 | end 69 | 70 | -------------------------------------------------------------------------------- /t/math.p8: -------------------------------------------------------------------------------- 1 | pico-8 cartridge // http://www.pico-8.com 2 | version 8 3 | __lua__ 4 | -- zepto-8 conformance tests 5 | -- for lua syntax extensions 6 | 7 | -- small test framework 8 | do local ctx, fail, total = "", 0, 0 9 | function fixture(name) 10 | ctx = name 11 | a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z = 12 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 13 | end 14 | function test_equal(x, y) 15 | total = total + 1 16 | if x ~= y then 17 | print(ctx.." failed: '"..x.."' != '"..y.."'") 18 | fail = fail + 1 19 | end 20 | end 21 | function summary() print("\n"..total.." tests - "..(total - fail).." passed, "..fail.." failed.") end 22 | end 23 | 24 | -- 25 | -- t1. check that shl works properly 26 | -- 27 | 28 | fixture "t1.01" 29 | x = shl(1, 1) 30 | test_equal(x, 2) 31 | 32 | fixture "t1.02" 33 | x = shl(1, 0) 34 | test_equal(x, 1) 35 | 36 | fixture "t1.03" 37 | x = shl(1, 14) 38 | test_equal(x, 16384) 39 | 40 | fixture "t1.04" 41 | x = shl(1, 15) 42 | test_equal(x, -32768) 43 | 44 | fixture "t1.05" 45 | x = shl(1, 16) 46 | test_equal(x, 0) 47 | 48 | fixture "t1.06" 49 | x = shl(1.125, 1) 50 | test_equal(x, 2.25) 51 | 52 | fixture "t1.07" 53 | x = shl(16384, 1) 54 | test_equal(x, -32768) 55 | 56 | fixture "t1.08" 57 | x = shl(-32768, 1) 58 | test_equal(x, 0) 59 | 60 | fixture "t1.09" 61 | x = shl(-32767.5, 1) 62 | test_equal(x, 1) 63 | 64 | fixture "t1.10" 65 | x = shl(-1, 1) 66 | test_equal(x, -2) 67 | 68 | fixture "t1.11" 69 | x = shl(-1, 15) 70 | test_equal(x, -32768) 71 | 72 | fixture "t1.12" 73 | x = shl(-1, 16) 74 | test_equal(x, 0) 75 | 76 | fixture "t1.13" 77 | x = shl(-1, 32) 78 | test_equal(x, -1) 79 | 80 | fixture "t1.14" 81 | x = shl(-1, 33) 82 | test_equal(x, -2) 83 | 84 | -- 85 | -- t2. check that shr works properly 86 | -- 87 | 88 | fixture "t2.01" 89 | x = shr(2, 1) 90 | test_equal(x, 1) 91 | 92 | fixture "t2.02" 93 | x = shr(-2, 1) 94 | test_equal(x, -1) 95 | 96 | -- 97 | -- print report 98 | -- 99 | 100 | summary() 101 | 102 | -------------------------------------------------------------------------------- /t/print.p8: -------------------------------------------------------------------------------- 1 | pico-8 cartridge // http://www.pico-8.com 2 | version 8 3 | __lua__ 4 | -- zepto-8 conformance tests 5 | -- for print() 6 | 7 | -- small test framework 8 | do local sec, sn, ctx, cn = "", 0, "", 0 9 | local fail, total, idx = 0, 0, 0 10 | function section(name) 11 | sec = name 12 | sn += 1 13 | end 14 | function fixture(name) 15 | ctx = name 16 | cn += 1 17 | idx = 0 18 | a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z = 19 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 20 | end 21 | function test_equal(x, y) 22 | total += 1 23 | idx += 1 24 | if x ~= y then 25 | printh('section '..sec..':') 26 | printh(ctx.." #"..idx.." failed: '"..tostr(x).."' != '"..tostr(y).."'") 27 | fail = fail + 1 28 | end 29 | end 30 | function summary() 31 | printh("\n"..total.." tests - "..(total - fail).." passed, "..fail.." failed.") 32 | end 33 | end 34 | 35 | -- small crc for the screen 36 | function crc() 37 | local x = 0 38 | for i=0x6000,0x7fff,4 do 39 | local p=peek4(i) 40 | x+=p*0xedb8.4e73+rotl(p,16)*0xbe36.7d8f+bxor(rotl(x,7),0xdead.beef) 41 | end 42 | return x 43 | end 44 | 45 | -- 46 | -- t1. check that print() changes the screen 47 | -- 48 | 49 | fixture "t1.01" 50 | cls() a=crc() 51 | print('x') 52 | test_equal(crc() == a, false) 53 | 54 | -- 55 | -- t2. print() for printable types 56 | -- 57 | 58 | fixture "t2.01" 59 | cls() print('false') a=crc() cls() 60 | print(false) 61 | test_equal(crc(), a) 62 | 63 | fixture "t2.02" 64 | cls() print('true') a=crc() cls() 65 | print(true) 66 | test_equal(crc(), a) 67 | 68 | fixture "t2.03" 69 | cls() print('1') a=crc() cls() 70 | print(1) 71 | test_equal(crc(), a) 72 | 73 | -- 74 | -- t3. print() for non-printable types 75 | -- 76 | 77 | fixture "t3.01" 78 | cls() print('[nil]') a=crc() cls() 79 | print(nil) 80 | test_equal(crc(), a) 81 | 82 | fixture "t3.02" 83 | cls() print('[table]') a=crc() cls() 84 | print({}) 85 | test_equal(crc(), a) 86 | 87 | fixture "t3.03" 88 | cls() print('[function]') a=crc() cls() 89 | print(cos) 90 | test_equal(crc(), a) 91 | 92 | fixture "t3.04" 93 | cls() print('[thread]') a=crc() cls() 94 | print(cocreate(cos)) 95 | test_equal(crc(), a) 96 | 97 | -- 98 | -- print report 99 | -- 100 | 101 | summary() 102 | -------------------------------------------------------------------------------- /t/syntax-depicofier.lua: -------------------------------------------------------------------------------- 1 | -- This file is taken from the Depicofier test suite. 2 | -- https://github.com/Enichan/Depicofier/blob/master/Depicofier/testsource.txt 3 | 4 | print("hello world") -- hi! 5 | 6 | --[[ 7 | ?"this is a comment" 8 | ]] 9 | 10 | ---[[ 11 | ?"this is not a comment" 12 | --]] 13 | 14 | aString = "this is a multi-line\ 15 | ?'this is inside a string'\ 16 | single-line string" 17 | 18 | someGlob = { 19 | value = 3 20 | } -- this is a comment 21 | 22 | if (someGlob != nil) someGlob.value *= 8 else print("someGlob undefined") // elseif end 23 | 24 | ?"Print shorthand", 1, 3 -- a comment here would normally mess things up 25 | 26 | binnum = 0b10001.11 27 | 28 | function add(a, b) 29 | someGlob["value"] += 1 * 3 30 | c -= d // another alt comment style 31 | e *= f 32 | g /= h 33 | i %= 20 34 | 35 | -- this is a normal lua operator and will give a warning unless processed in unified mode 36 | j = k << 4 37 | 38 | return a 39 | end 40 | -------------------------------------------------------------------------------- /t/syntax.p8: -------------------------------------------------------------------------------- 1 | pico-8 cartridge // http://www.pico-8.com 2 | version 8 3 | __lua__ 4 | -- zepto-8 conformance tests 5 | -- for lua syntax extensions 6 | 7 | -- small test framework 8 | do local sec, sn, ctx, cn = "", 0, "", 0 9 | local fail, total = 0, 0 10 | function section(name) 11 | sec = name 12 | sn += 1 13 | end 14 | function fixture(name) 15 | ctx = name 16 | cn += 1 17 | a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z = 18 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 19 | end 20 | function test_equal(x, y) 21 | total = total + 1 22 | if x ~= y then 23 | print(ctx.." failed: '"..x.."' != '"..y.."'") 24 | fail = fail + 1 25 | end 26 | end 27 | function summary() 28 | print("\n"..total.." tests - "..(total - fail).." passed, "..fail.." failed.") 29 | end 30 | end 31 | 32 | -- 33 | -- t1. check that != works properly 34 | -- 35 | 36 | fixture "t1.01" 37 | test_equal(1 != 1, false) 38 | 39 | fixture "t1.02" 40 | test_equal(1 != 2, true) 41 | 42 | fixture "t1.03" 43 | test_equal((1 != 2) != (3 != 4), false) 44 | 45 | fixture "t1.04" 46 | test_equal("!=", "!".."=") 47 | 48 | -- 49 | -- t2. check several variations of the += syntactic sugar 50 | -- 51 | 52 | fixture "t2.01" 53 | x += 1 54 | test_equal(x, 1) 55 | 56 | fixture "t2.02" 57 | x +=1 58 | test_equal(x, 1) 59 | 60 | fixture "t2.03" 61 | x+= 1 62 | test_equal(x, 1) 63 | 64 | fixture "t2.04" 65 | x+=1 x+=1 66 | test_equal(x, 2) 67 | 68 | fixture "t2.05" 69 | x+=1;x+=1 70 | test_equal(x, 2) 71 | 72 | fixture "t2.06" 73 | x+=(1)x+=1 74 | test_equal(x, 2) 75 | 76 | -- this is not pretty but should be legal because lua parses numbers 77 | -- until the last legal digit/character 78 | fixture "t2.07" 79 | x+=1x+=1 80 | test_equal(x, 2) 81 | 82 | fixture "t2.08" 83 | x+=1-- intrusive comment 84 | test_equal(x, 1) 85 | 86 | -- XXX: PICO-8 does not support multiline reassignments, so we 87 | -- disabled it in zepto8, too, but it could be re-added. 88 | --fixture "t2.09" 89 | --x-- more 90 | --+=-- intrusive 91 | --1-- comments 92 | --test_equal(x, 1) 93 | 94 | -- nested reassignments; will confuse most regex-based methods that 95 | -- attempt to convert pico-8 code to standard lua 96 | fixture "t2.10" 97 | x+=(function(x)x+=1 return x end)(2) 98 | test_equal(x, 3) 99 | 100 | fixture "t2.11" -- nested reassignments and ugliness! 101 | x+=1x+=(function(x)x+=1x+=x return x end)(1) 102 | test_equal(x, 5) 103 | 104 | fixture "t2.12" 105 | do a=1 local a+=2 x=a end 106 | test_equal(x, 3) 107 | 108 | 109 | -- 110 | -- t3. check several variations of if/then or if without then 111 | -- 112 | 113 | fixture "t3.01" 114 | if (x == 0) x = 1 115 | test_equal(x, 1) 116 | 117 | -- XXX: disabled (PICO-8 does not support this) 118 | --fixture "t3.02" 119 | --if (x == 0) x = 1 if (x == 1) x = 2 120 | --test_equal(x, 2) 121 | 122 | fixture "t3.03" 123 | for i=0,9 do if (i != 0) x = i end 124 | test_equal(x, 9) 125 | 126 | -- XXX: disabled (PICO-8 does not support this) 127 | --fixture "t3.04" 128 | --for i=0,9 do if (i > 5) for j=0,9 do if (j > 5) x = i + j end end 129 | --test_equal(x, 18) 130 | 131 | fixture "t3.05" 132 | if ((x == 0) 133 | ) then x = 1 end 134 | test_equal(x, 1) 135 | 136 | fixture "t3.06" 137 | if ((x != 1) and 138 | (x != 2)) then x = 1 end 139 | test_equal(x, 1) 140 | 141 | fixture "t3.07" 142 | function f() 143 | if (x != 0) return 144 | x = 1 145 | end 146 | f() 147 | test_equal(x, 1) 148 | 149 | fixture "t3.08" 150 | if (true) or (true) then 151 | x = 1 152 | end 153 | test_equal(x, 1) 154 | 155 | fixture "t3.09" 156 | if (x == 1) x = 0 else x = 1 157 | test_equal(x, 1) 158 | 159 | fixture "t3.10" 160 | if (x == 0) x = 1-- intrusive comment 161 | test_equal(x, 1) 162 | 163 | fixture "t3.11" 164 | if (x == 0) x = 1// intrusive comment 165 | test_equal(x, 1) 166 | 167 | -- 168 | -- t4. check that C++ comments work properly 169 | -- 170 | 171 | fixture "t4.01" 172 | x = 4 // 2 173 | test_equal(x, 4) 174 | 175 | fixture "t4.02" 176 | x += 4//2 177 | test_equal(x, 4) 178 | 179 | -- 180 | -- t5. check that short prints are supported 181 | 182 | fixture "t5.01" 183 | ?"" 184 | test_equal(true, true) 185 | 186 | fixture "t5.02" 187 | ?""-- intrusive comment 188 | test_equal(true, true) 189 | 190 | fixture "t5.03" 191 | ?""// intrusive comment 192 | test_equal(true, true) 193 | 194 | -- 195 | -- print report 196 | -- 197 | 198 | summary() 199 | 200 | -------------------------------------------------------------------------------- /t/tables.p8: -------------------------------------------------------------------------------- 1 | pico-8 cartridge // http://www.pico-8.com 2 | version 8 3 | __lua__ 4 | -- zepto-8 conformance tests 5 | -- for lua syntax extensions 6 | 7 | -- small test framework 8 | do local sec, sn, ctx, cn = "", 0, "", 0 9 | local fail, total, idx = 0, 0, 0 10 | function section(name) 11 | sec = name 12 | sn += 1 13 | end 14 | function fixture(name) 15 | ctx = name 16 | cn += 1 17 | idx = 0 18 | a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u,v,w,x,y,z = 19 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 20 | end 21 | function test_equal(x, y) 22 | total += 1 23 | idx += 1 24 | if x ~= y then 25 | printh('section '..sec..':') 26 | printh(ctx.." #"..idx.." failed: '"..tostr(x).."' != '"..tostr(y).."'") 27 | fail = fail + 1 28 | end 29 | end 30 | function summary() 31 | printh("\n"..total.." tests - "..(total - fail).." passed, "..fail.." failed.") 32 | end 33 | end 34 | 35 | -- 36 | -- Check that # works properly 37 | -- 38 | 39 | section 'operator #' 40 | 41 | fixture 'empty' 42 | t={} 43 | test_equal(#t,0) 44 | 45 | fixture 'nil in table' 46 | t={nil} 47 | test_equal(#t,0) 48 | 49 | fixture 'one' 50 | t={1} 51 | test_equal(#t,1) 52 | 53 | -- 54 | -- Check that count() works properly 55 | -- 56 | 57 | section 'count()' 58 | 59 | fixture 'empty' 60 | t={} 61 | test_equal(count(t),0) 62 | 63 | fixture 'empty string' 64 | t="" 65 | test_equal(count(t),0) 66 | 67 | fixture 'nil in table' 68 | t={nil} 69 | test_equal(count(t),0) 70 | 71 | fixture 'one' 72 | t={1} 73 | test_equal(count(t),1) 74 | 75 | fixture 'three' 76 | t={1,2,3} 77 | test_equal(count(t),3) 78 | 79 | fixture 'three minus one' 80 | t={1,2,3} 81 | t[3]=nil 82 | test_equal(count(t),2) 83 | 84 | fixture 'three plus one' 85 | t={1,2,3} 86 | t[4]=4 87 | test_equal(count(t),4) 88 | 89 | fixture 'three plus sparse' 90 | t={1,2,3} 91 | t[5]=5 92 | test_equal(count(t),3) 93 | 94 | fixture 'three with hole' 95 | t={1,2,3} 96 | t[2]=nil 97 | test_equal(count(t),2) 98 | 99 | -- 100 | -- Check that add() works properly 101 | -- 102 | 103 | section 'add()' 104 | 105 | fixture 'add to empty' 106 | t={} 107 | add(t,1) 108 | test_equal(t[1],1) 109 | 110 | fixture 'return value' 111 | x=add({},1) 112 | test_equal(x,1) 113 | 114 | fixture 'return nil' 115 | x=add(nil,1) 116 | test_equal(x,nil) 117 | 118 | fixture 'add to 1,2' 119 | t={1,2} 120 | add(t, 3) -- {1,2,3} 121 | test_equal(t[3],3) 122 | 123 | fixture 'add to *,2,3' 124 | t={[2]=2,[3]=3} 125 | add(t,4) -- {4,2,3} 126 | test_equal(t[1],4) 127 | 128 | fixture 'hole *,2,3' 129 | t={1,2,3} 130 | t[1]=nil 131 | add(t,4) -- {nil,2,3,4} 132 | test_equal(t[4],4) 133 | 134 | fixture 'hole 1,*,3' 135 | t={1,2,3} 136 | t[2]=nil 137 | add(t,4) -- {1,nil,3,4} 138 | test_equal(t[4],4) 139 | 140 | fixture 'hole 1,2,*' 141 | t={1,2,3} 142 | t[3]=nil 143 | add(t,4) -- {1,2,4} 144 | test_equal(t[3],4) 145 | 146 | fixture 'add at 1' 147 | t={1,2,3} 148 | add(t,4,1) 149 | test_equal(t[1],4) 150 | test_equal(t[2],1) 151 | 152 | fixture 'add at -10' 153 | t={1,2,3} 154 | add(t,4,-10) 155 | test_equal(t[1],4) 156 | test_equal(t[2],1) 157 | 158 | fixture 'add at #t' 159 | t={1,2,3} 160 | add(t,4,#t) 161 | test_equal(t[3],4) 162 | test_equal(t[4],3) 163 | 164 | fixture 'add at #t+1' 165 | t={1,2,3} 166 | add(t,4,#t+1) 167 | test_equal(t[3],3) 168 | test_equal(t[4],4) 169 | 170 | fixture 'add at #t+10' 171 | t={1,2,3} 172 | add(t,4,#t+10) 173 | test_equal(t[3],3) 174 | test_equal(t[4],4) 175 | 176 | -- 177 | -- Check that all() works properly 178 | -- 179 | 180 | section 'all()' 181 | 182 | fixture 'empty' 183 | t={} 184 | s='' 185 | for n in all(t) do s = s..tostr(n) end 186 | test_equal(s,'') 187 | 188 | fixture 'simple' 189 | t={1,2} 190 | s='' 191 | for n in all(t) do s = s..tostr(n) end 192 | test_equal(s,'12') 193 | 194 | fixture 'dupes' 195 | t={1,1,2,2} 196 | s='' 197 | for n in all(t) do s = s..tostr(n) end 198 | test_equal(s,'1122') 199 | 200 | fixture 'holes' 201 | t={1,nil,2,nil,nil,3} 202 | s='' 203 | for n in all(t) do s = s..tostr(n) end 204 | test_equal(s,'123') 205 | 206 | -- 207 | -- Check that foreach() works properly 208 | -- 209 | 210 | section 'foreach()' 211 | 212 | fixture 'simple' 213 | t={1,2,3} 214 | v={} 215 | foreach(t, function(x) add(v,x) end) 216 | test_equal(v[1],1) 217 | test_equal(v[2],2) 218 | test_equal(v[3],3) 219 | test_equal(v[4],nil) 220 | 221 | fixture 'inner add' 222 | t={1,2,3} 223 | v={} 224 | foreach(t, function(x) add(v,x) if(x==2) then add(t,4) end end) 225 | test_equal(v[1],1) 226 | test_equal(v[2],2) 227 | test_equal(v[3],3) 228 | test_equal(v[4],4) 229 | test_equal(v[5],nil) 230 | 231 | fixture 'inner del' 232 | t={1,2,3} 233 | v={} 234 | foreach(t, function(x) add(v,x) if(x==2) then del(t,3) end end) 235 | test_equal(v[1],1) 236 | test_equal(v[2],2) 237 | test_equal(v[3],nil) 238 | 239 | fixture 'inner del+add' 240 | t={1,2,3} 241 | v={} 242 | foreach(t, function(x) add(v,x) if(x==2) then del(t,3) add(t,4) end end) 243 | test_equal(v[1],1) 244 | test_equal(v[2],2) 245 | test_equal(v[3],4) 246 | test_equal(v[4],nil) 247 | 248 | fixture 'inner del+del+add' 249 | t={1,2,3} 250 | v={} 251 | foreach(t, function(x) add(v,x) if x==2 then del(t,2) del(t,3) add(t,4) end end) 252 | test_equal(v[1],1) 253 | test_equal(v[2],2) 254 | test_equal(v[3],4) 255 | test_equal(v[4],nil) 256 | 257 | fixture 'inner del+del+del+add' 258 | t={1,2,3} 259 | v={} 260 | foreach(t, function(x) add(v,x) if x==2 then del(t,1) del(t,2) del(t,3) add(t,4) end end) 261 | test_equal(v[1],1) 262 | test_equal(v[2],2) 263 | test_equal(v[3],nil) 264 | 265 | fixture 'nested foreach' 266 | t={1,2} 267 | v={} 268 | foreach(t, function(x) foreach(t, function(y) add(v,x*100+y) end) end) 269 | test_equal(v[1],101) 270 | test_equal(v[2],102) 271 | test_equal(v[3],201) 272 | test_equal(v[4],202) 273 | test_equal(v[5],nil) 274 | 275 | fixture 'nested foreach delete' 276 | t={1,2,3} 277 | v={} 278 | foreach(t, function(x) if x==2 then foreach(t, function(y) del(t,y) end) end add(v,x) end) 279 | test_equal(t[1],nil) 280 | test_equal(v[1],1) 281 | test_equal(v[2],2) 282 | test_equal(v[3],nil) 283 | 284 | -- 285 | -- print report 286 | -- 287 | 288 | summary() 289 | 290 | -------------------------------------------------------------------------------- /utils/Makefile.am: -------------------------------------------------------------------------------- 1 | 2 | include $(top_srcdir)/src/3rdparty/lolengine/build/autotools/common.am 3 | 4 | EXTRA_DIST += \ 5 | sysfont.p8 \ 6 | $(NULL) 7 | --------------------------------------------------------------------------------