diff options
Diffstat (limited to '')
4 files changed, 848 insertions, 917 deletions
diff --git a/2020/info/27.md b/2020/info/27.md index bc19e6c2..f2df923a 100644 --- a/2020/info/27.md +++ b/2020/info/27.md @@ -1,8 +1,9 @@ # State of Retro Gaming in Emacs Vasilij "wasamasa" Schneidermann -[[!template id=vid src="https://mirror.csclub.uwaterloo.ca/emacsconf/2020/emacsconf-2020--27-state-of-retro-gaming-in-emacs-chip8--vasilij-wasamasa-schneidermann.webm"]] -[Download compressed .webm video (7.2M)](https://mirror.csclub.uwaterloo.ca/emacsconf/2020/smaller/emacsconf-2020--27-state-of-retro-gaming-in-emacs-chip8--vasilij-wasamasa-schneidermann--vp9-q56-video-original-audio.webm) +[[!template vidid=mainVideo id=vid src="https://mirror.csclub.uwaterloo.ca/emacsconf/2020/emacsconf-2020--27-state-of-retro-gaming-in-emacs-chip8--vasilij-wasamasa-schneidermann.webm"subtitles="/2020/subtitles/emacsconf-2020--27-state-of-retro-gaming-in-emacs-chip8--vasilij-wasamasa-schneidermann.vtt"]] +[Download compressed .webm video (7.2M)](https://mirror.csclub.uwaterloo.ca/emacsconf/2020/smaller/emacsconf-2020--27-state-of-retro-gaming-in-emacs-chip8--vasilij-wasamasa-schneidermann--vp9-q56-video-original-audio.webm) +[View transcript](#transcript) - [chip8 demo - alien, .webm video, 720p, 2M](https://mirror.csclub.uwaterloo.ca/emacsconf/2020/emacsconf-2020--27-state-of-retro-gaming-in-emacs-chip8-demo-alien--vasilij-wasamasa-schneidermann.webm) - [chip8 demo - brix, .webm video, 720p, 1M](https://mirror.csclub.uwaterloo.ca/emacsconf/2020/emacsconf-2020--27-state-of-retro-gaming-in-emacs-chip8-demo-brix--vasilij-wasamasa-schneidermann.webm) @@ -71,3 +72,216 @@ constraints. - Repository available at <https://depp.brause.cc/chip8.el/>. - Blog post available at <https://emacsninja.com/posts/smooth-video-game-emulation-in-emacs.html>. + +<a name="transcript"></a> +# Transcript + +[[!template new="1" text="Hello everyone and welcome to my talk, "The State of Retro Gaming and Emacs."" start="00:00:00.880" video="mainVideo" id=subtitle]] +[[!template text="First of all, a little bit about myself." start="00:00:06.960" video="mainVideo" id=subtitle]] +[[!template text="My name is Vasilij Schneidermann. I'm 28 years old." start="00:00:08.639" video="mainVideo" id=subtitle]] +[[!template text="I work as a cyber security consultant at msg systems," start="00:00:12.000" video="mainVideo" id=subtitle]] +[[!template text="and test other people's web applications" start="00:00:14.719" video="mainVideo" id=subtitle]] +[[!template text="and review the source code for security problems." start="00:00:17.359" video="mainVideo" id=subtitle]] +[[!template text="You can reach me by email." start="00:00:20.160" video="mainVideo" id=subtitle]] +[[!template text="I have my own self-hosted git repositories," start="00:00:22.080" video="mainVideo" id=subtitle]] +[[!template text="and I have a blog where you can occasionally find new posts by me" start="00:00:25.039" video="mainVideo" id=subtitle]] +[[!template text="on all kinds of things, not just Emacs things." start="00:00:28.160" video="mainVideo" id=subtitle]] +[[!template new="1" text="The motivation about this one..." start="00:00:32.160" video="mainVideo" id=subtitle]] +[[!template text="I found that Emacs is the ultimate procrastination machine," start="00:00:34.559" video="mainVideo" id=subtitle]] +[[!template text="and there are lots of fun demonstrations." start="00:00:37.600" video="mainVideo" id=subtitle]] +[[!template text="I'll go over a few of them." start="00:00:39.600" video="mainVideo" id=subtitle]] +[[!template text="For example, someone made a thing to order salad for himself online," start="00:00:41.200" video="mainVideo" id=subtitle]] +[[!template text="so he doesn't have to walk over to the shop." start="00:00:45.840" video="mainVideo" id=subtitle]] +[[!template text="There's plenty of IRC bots. There's some game things." start="00:00:48.239" video="mainVideo" id=subtitle]] +[[!template text="There's an emulator for the Z-machine which you can use to play zork." start="00:00:51.760" video="mainVideo" id=subtitle]] +[[!template new="1" text="And so I asked myself, at this point," start="00:00:55.600" video="mainVideo" id=subtitle]] +[[!template text="can you actually emulate retro games at 60fps?" start="00:00:57.440" video="mainVideo" id=subtitle]] +[[!template text="I looked around a bit and found some projects," start="00:00:59.920" video="mainVideo" id=subtitle]] +[[!template text="but none that were actually able to do it at 60fps." start="00:01:02.079" video="mainVideo" id=subtitle]] +[[!template text="So I set out to do my own one," start="00:01:06.159" video="mainVideo" id=subtitle]] +[[!template text="and looked out for a console" start="00:01:08.000" video="mainVideo" id=subtitle]] +[[!template text="that you can actually emulate at that speed," start="00:01:09.200" video="mainVideo" id=subtitle]] +[[!template text="using Emacs with its very, very limited rendering." start="00:01:11.119" video="mainVideo" id=subtitle]] +[[!template new="1" text="And here's the project, chip8.el." start="00:01:16.320" video="mainVideo" id=subtitle]] +[[!template text="It's pretty much finished." start="00:01:19.200" video="mainVideo" id=subtitle]] +[[!template text="It clocks into under 1000 source lines of code." start="00:01:20.560" video="mainVideo" id=subtitle]] +[[!template text="It supports the superchip 8 extensions." start="00:01:24.000" video="mainVideo" id=subtitle]] +[[!template text="It runs at full speed." start="00:01:26.159" video="mainVideo" id=subtitle]] +[[!template text="All games behave okay, as far as I'm concerned," start="00:01:27.280" video="mainVideo" id=subtitle]] +[[!template text="and yeah, I'm pretty happy with it." start="00:01:29.600" video="mainVideo" id=subtitle]] +[[!template text="It's very much the hello world of emulation," start="00:01:31.680" video="mainVideo" id=subtitle]] +[[!template text="and I might, maybe, do some other emulation projects in the future." start="00:01:34.479" video="mainVideo" id=subtitle]] +[[!template new="1" text="Now, for the section which is the longest:" start="00:01:40.880" video="mainVideo" id=subtitle]] +[[!template text="bunch of fun facts about chip8.el" start="00:01:43.360" video="mainVideo" id=subtitle]] +[[!template text="which I've learned during this project." start="00:01:45.439" video="mainVideo" id=subtitle]] +[[!template text="So what the hell is chip8 anyway?" start="00:01:49.200" video="mainVideo" id=subtitle]] +[[!template text="First of all, unlike many other emulation game things," start="00:01:51.759" video="mainVideo" id=subtitle]] +[[!template text="it's not a console, but a VM." start="00:01:54.960" video="mainVideo" id=subtitle]] +[[!template text="It was designed for easy porting of home computer games." start="00:01:56.799" video="mainVideo" id=subtitle]] +[[!template text="It wasn't terribly successful," start="00:02:00.000" video="mainVideo" id=subtitle]] +[[!template text="but there's still a small community of enthusiasts writing games for it," start="00:02:02.320" video="mainVideo" id=subtitle]] +[[!template text="and there are even a few demos." start="00:02:05.439" video="mainVideo" id=subtitle]] +[[!template text="This VM has system specs." start="00:02:09.119" video="mainVideo" id=subtitle]] +[[!template text="It has a very, very simple 8-bit cpu with 16 registers," start="00:02:11.039" video="mainVideo" id=subtitle]] +[[!template text="and 36 fixed-size instructions." start="00:02:14.720" video="mainVideo" id=subtitle]] +[[!template text="You have a whole 4 kilobyte of RAM." start="00:02:17.280" video="mainVideo" id=subtitle]] +[[!template text="You have a stack with 16 return addresses." start="00:02:19.680" video="mainVideo" id=subtitle]] +[[!template text="The resolution is 64 by 32 black/white pixels." start="00:02:22.080" video="mainVideo" id=subtitle]] +[[!template new="1" text="Rendering is done by drawing sprites." start="00:02:25.760" video="mainVideo" id=subtitle]] +[[!template text="These are drawn in XOR mode," start="00:02:28.000" video="mainVideo" id=subtitle]] +[[!template text="meaning that if you draw a sprite and set a bit," start="00:02:29.200" video="mainVideo" id=subtitle]] +[[!template text="it just flips over from black to white or white to black." start="00:02:31.840" video="mainVideo" id=subtitle]] +[[!template text="For sound, you have a monotone buzzer that can just beep at one frequency." start="00:02:35.040" video="mainVideo" id=subtitle]] +[[!template text="Most unusually, there's a hexadecimal keypad as input," start="00:02:39.360" video="mainVideo" id=subtitle]] +[[!template text="so the keys are basically zero to nine and a to f." start="00:02:43.120" video="mainVideo" id=subtitle]] +[[!template text="So how does this whole thing work?" start="00:02:48.480" video="mainVideo" id=subtitle]] +[[!template text="It runs at an unspecified speed." start="00:02:50.720" video="mainVideo" id=subtitle]] +[[!template text="You'll probably have to do some fine-tuning" start="00:02:52.400" video="mainVideo" id=subtitle]] +[[!template text="to find the speed you're happy with." start="00:02:53.040" video="mainVideo" id=subtitle]] +[[!template new="1" text="Sound and delay timers exist." start="00:02:56.080" video="mainVideo" id=subtitle]] +[[!template text="They count down at 60fps down to 0." start="00:02:58.080" video="mainVideo" id=subtitle]] +[[!template text="This is done so that you can play a sound at some specific time." start="00:03:01.120" video="mainVideo" id=subtitle]] +[[!template text="The game itself is loaded with a fixed offset into RAM." start="00:03:05.120" video="mainVideo" id=subtitle]] +[[!template text="The program counter is set to exactly that offset," start="00:03:07.840" video="mainVideo" id=subtitle]] +[[!template text="and from there it enters the game loop" start="00:03:10.480" video="mainVideo" id=subtitle]] +[[!template text="where it decodes an instruction," start="00:03:11.920" video="mainVideo" id=subtitle]] +[[!template text="executes it for the side effects," start="00:03:13.280" video="mainVideo" id=subtitle]] +[[!template text="and just loops and does this ad infinitum." start="00:03:14.800" video="mainVideo" id=subtitle]] +[[!template new="1" text="So the game loop was the first thing where we ran into problems." start="00:03:19.599" video="mainVideo" id=subtitle]] +[[!template text="The usual game approach is to do stuff," start="00:03:22.720" video="mainVideo" id=subtitle]] +[[!template text="figure out how long to wait," start="00:03:25.120" video="mainVideo" id=subtitle]] +[[!template text="wait for exactly that much, and repeat." start="00:03:26.640" video="mainVideo" id=subtitle]] +[[!template text="This doesn't work well in Emacs at all, because, well," start="00:03:29.280" video="mainVideo" id=subtitle]] +[[!template text="user input, basically." start="00:03:31.680" video="mainVideo" id=subtitle]] +[[!template text="Emacs is designed to just do whatever it needs to do" start="00:03:34.959" video="mainVideo" id=subtitle]] +[[!template text="whenever you enter user input" start="00:03:37.760" video="mainVideo" id=subtitle]] +[[!template text="instead of doing things at one specific time." start="00:03:39.040" video="mainVideo" id=subtitle]] +[[!template text="If you try to do interruptable sleep, well, you get unpredictable behavior." start="00:03:42.319" video="mainVideo" id=subtitle]] +[[!template text="For example, it can be the timer doesn't run at all at the next time" start="00:03:46.640" video="mainVideo" id=subtitle]] +[[!template text="because you've accidentally cancelled it." start="00:03:50.480" video="mainVideo" id=subtitle]] +[[!template text="If you do uninterruptable sleep, it freezes instead ," start="00:03:52.560" video="mainVideo" id=subtitle]] +[[!template text="which isn't what we want either." start="00:03:55.120" video="mainVideo" id=subtitle]] +[[!template new="1" text="So I went for timers, which forced me to do inversion of control," start="00:03:56.720" video="mainVideo" id=subtitle]] +[[!template text="meaning that I have to write code in the style" start="00:04:00.560" video="mainVideo" id=subtitle]] +[[!template text="where it just calls timer," start="00:04:02.560" video="mainVideo" id=subtitle]] +[[!template text="and this allows this input to happen" start="00:04:04.879" video="mainVideo" id=subtitle]] +[[!template text="and for things to progress at roughly the speed I want to." start="00:04:06.560" video="mainVideo" id=subtitle]] +[[!template text="So there's the timer function which is called at 60fps" start="00:04:11.040" video="mainVideo" id=subtitle]] +[[!template text="and I have to be very careful to not do too much in it." start="00:04:14.159" video="mainVideo" id=subtitle]] +[[!template text="And, say, this function executes CPU cycles," start="00:04:17.359" video="mainVideo" id=subtitle]] +[[!template text="decrement the sound/delay registers, and redraw the screen." start="00:04:21.305" video="mainVideo" id=subtitle]] +[[!template new="1" text="So to map this whole system to Emacs Lisp," start="00:04:26.479" video="mainVideo" id=subtitle]] +[[!template text="I've used just integers and vectors" start="00:04:28.800" video="mainVideo" id=subtitle]] +[[!template text="which contain even more integers." start="00:04:31.199" video="mainVideo" id=subtitle]] +[[!template text="This is used for the RAM, registers," start="00:04:33.120" video="mainVideo" id=subtitle]] +[[!template text="return stack, key state, screen," start="00:04:35.040" video="mainVideo" id=subtitle]] +[[!template text="and so on and so forth." start="00:04:37.040" video="mainVideo" id=subtitle]] +[[!template text="Basically, what you would do if you were writing C." start="00:04:38.508" video="mainVideo" id=subtitle]] +[[!template text="All of this is stored in global variables." start="00:04:41.520" video="mainVideo" id=subtitle]] +[[!template text="I'm not using any lists at all." start="00:04:43.360" video="mainVideo" id=subtitle]] +[[!template text="As a side effect, there's no consing going on at all." start="00:04:45.600" video="mainVideo" id=subtitle]] +[[!template text="There are no extra objects created" start="00:04:48.400" video="mainVideo" id=subtitle]] +[[!template text="which would trigger garbage collection processes." start="00:04:50.080" video="mainVideo" id=subtitle]] +[[!template text="Getting this right was rather tricky, actually," start="00:04:53.199" video="mainVideo" id=subtitle]] +[[!template text="and there were some hidden garbage collection problems" start="00:04:55.600" video="mainVideo" id=subtitle]] +[[!template text="which I had to resolve over time." start="00:04:58.240" video="mainVideo" id=subtitle]] +[[!template new="1" text="So, decoding instructions." start="00:05:01.759" video="mainVideo" id=subtitle]] +[[!template text="For this, you have to know that all instructions are two bytes long," start="00:05:03.759" video="mainVideo" id=subtitle]] +[[!template text="and the arguments are encoded inside them." start="00:05:06.800" video="mainVideo" id=subtitle]] +[[!template text="For example, the jump to address instruction" start="00:05:08.880" video="mainVideo" id=subtitle]] +[[!template text="is encoded as one and three hex digits." start="00:05:11.440" video="mainVideo" id=subtitle]] +[[!template text="The type is extracted masking with #xF000" start="00:05:15.120" video="mainVideo" id=subtitle]] +[[!template text="and then shifting it by 12 bits." start="00:05:18.400" video="mainVideo" id=subtitle]] +[[!template text="Mask means you perform the binary AND." start="00:05:20.400" video="mainVideo" id=subtitle]] +[[!template text="You can do the same with the argument by masking with #0xFFF and no shift." start="00:05:23.520" video="mainVideo" id=subtitle]] +[[!template text="If you do this long enough, you'll find common patterns." start="00:05:28.400" video="mainVideo" id=subtitle]] +[[!template text="For example, addresses are always encoded like this" start="00:05:30.560" video="mainVideo" id=subtitle]] +[[!template text="using the last three nibbles." start="00:05:32.639" video="mainVideo" id=subtitle]] +[[!template text="In the code, you'll find a big cond" start="00:05:34.880" video="mainVideo" id=subtitle]] +[[!template text="which dispatches on the type and executes it for the side effects." start="00:05:36.160" video="mainVideo" id=subtitle]] +[[!template new="1" text="For testing, I've initially just executed the ROM until I've hit C-g," start="00:05:41.440" video="mainVideo" id=subtitle]] +[[!template text="and then use the debug command to render the screen to a buffer." start="00:05:45.919" video="mainVideo" id=subtitle]] +[[!template text="Later on, I found tiny ROMs that just display a static test screen," start="00:05:49.039" video="mainVideo" id=subtitle]] +[[!template text="for example, logo, and looked whether it looked right." start="00:05:53.199" video="mainVideo" id=subtitle]] +[[!template text="I added instructions as needed" start="00:05:57.280" video="mainVideo" id=subtitle]] +[[!template text="and went through more and more and more ROMs." start="00:05:58.800" video="mainVideo" id=subtitle]] +[[!template text="And later I wrote a unit test suite as a safety net." start="00:06:00.720" video="mainVideo" id=subtitle]] +[[!template text="This unit test suite, it just sets up an empty emulator state," start="00:06:04.000" video="mainVideo" id=subtitle]] +[[!template text="executes some instructions," start="00:06:07.840" video="mainVideo" id=subtitle]] +[[!template text="and then looks whether the expected side effects have happened." start="00:06:09.199" video="mainVideo" id=subtitle]] +[[!template new="1" text="For debugging, I usually use edebug, but this was super ineffective," start="00:06:14.880" video="mainVideo" id=subtitle]] +[[!template text="because, well, you don't really want to step through big cons" start="00:06:18.319" video="mainVideo" id=subtitle]] +[[!template text="doing side effects for every single cycle," start="00:06:21.600" video="mainVideo" id=subtitle]] +[[!template text="when it can take like 100 cycles for things to happen." start="00:06:23.680" video="mainVideo" id=subtitle]] +[[!template text="Therefore I've set up logging." start="00:06:26.880" video="mainVideo" id=subtitle]] +[[!template text="Whenever I logged something and couldn't figure out the error," start="00:06:29.680" video="mainVideo" id=subtitle]] +[[!template text="I compared my log output with the instrumented version of another emulator," start="00:06:32.639" video="mainVideo" id=subtitle]] +[[!template text="and if the logs diverge, then I have figured out where the bug lies" start="00:06:37.039" video="mainVideo" id=subtitle]] +[[!template text="and could look deeper into it." start="00:06:40.479" video="mainVideo" id=subtitle]] +[[!template text="Future project idea might be a chip 8 debugger," start="00:06:42.720" video="mainVideo" id=subtitle]] +[[!template text="but I doubt I'll ever go into it." start="00:06:44.960" video="mainVideo" id=subtitle]] +[[!template new="1" text="For analysis, I initially wrote a disassembler," start="00:06:49.440" video="mainVideo" id=subtitle]] +[[!template text="which is a very simple thing but super tedious," start="00:06:51.759" video="mainVideo" id=subtitle]] +[[!template text="especially if you wanted to add advanced functionality," start="00:06:54.400" video="mainVideo" id=subtitle]] +[[!template text="for example, analysis or thinking of what part is data," start="00:06:56.639" video="mainVideo" id=subtitle]] +[[!template text="what part is code." start="00:06:58.720" video="mainVideo" id=subtitle]] +[[!template text="I had this great idea for using the radare 2 framework" start="00:07:00.000" video="mainVideo" id=subtitle]] +[[!template text="and adding analysis and disassembly plug-in for it." start="00:07:03.360" video="mainVideo" id=subtitle]] +[[!template text="So I looked into this. Found, okay," start="00:07:06.479" video="mainVideo" id=subtitle]] +[[!template text="you can write plugins in C" start="00:07:08.400" video="mainVideo" id=subtitle]] +[[!template text="but also in Python, so I wrote one in Python," start="00:07:10.319" video="mainVideo" id=subtitle]] +[[!template text="and then discovered there's actually an existing one in core," start="00:07:12.639" video="mainVideo" id=subtitle]] +[[!template text="which you have to enable explicitly by passing an extra argument." start="00:07:14.720" video="mainVideo" id=subtitle]] +[[!template text="I've tried it and found it's not exactly as good as my own one," start="00:07:18.400" video="mainVideo" id=subtitle]] +[[!template text="so I improved this one and submitted pull requests" start="00:07:21.680" video="mainVideo" id=subtitle]] +[[!template text="until it was at the same level." start="00:07:24.160" video="mainVideo" id=subtitle]] +[[!template new="1" text="Rendering was the trickiest part of this whole thing," start="00:07:28.080" video="mainVideo" id=subtitle]] +[[!template text="because, well, I decided against using a library." start="00:07:30.720" video="mainVideo" id=subtitle]] +[[!template text="Not like there would have been any usable library for this." start="00:07:34.319" video="mainVideo" id=subtitle]] +[[!template text="My usual approach of creating SVG files was too expensive." start="00:07:37.120" video="mainVideo" id=subtitle]] +[[!template text="It just created too much garbage and took too long time." start="00:07:40.880" video="mainVideo" id=subtitle]] +[[!template text="I then tried creating mutating strings." start="00:07:45.120" video="mainVideo" id=subtitle]] +[[!template text="This was either too expensive, just like SVGs, or too complicated." start="00:07:47.360" video="mainVideo" id=subtitle]] +[[!template text="I tried changing SVG tiles, which created gaps between the lines." start="00:07:52.479" video="mainVideo" id=subtitle]] +[[!template text="Then I tried to create an xpm file which was backed by a bool vector" start="00:07:57.280" video="mainVideo" id=subtitle]] +[[!template text="and mutating this bool vector," start="00:08:00.720" video="mainVideo" id=subtitle]] +[[!template text="but the image caching effect" start="00:08:02.400" video="mainVideo" id=subtitle]] +[[!template text="made it just every nth frame to appear," start="00:08:04.000" video="mainVideo" id=subtitle]] +[[!template text="which wasn't good either." start="00:08:06.479" video="mainVideo" id=subtitle]] +[[!template text="Then I had the idea to just use plain text" start="00:08:08.879" video="mainVideo" id=subtitle]] +[[!template text="and paint the individual characters" start="00:08:11.440" video="mainVideo" id=subtitle]] +[[!template text="with a different background color." start="00:08:13.120" video="mainVideo" id=subtitle]] +[[!template text="This had perfect, perfect performance." start="00:08:14.800" video="mainVideo" id=subtitle]] +[[!template text="There were many optimization attempts until I got there," start="00:08:17.120" video="mainVideo" id=subtitle]] +[[!template text="and it was very, very stressful." start="00:08:19.280" video="mainVideo" id=subtitle]] +[[!template text="I wasn't sure whether I would ever get to accept the performance at all." start="00:08:21.199" video="mainVideo" id=subtitle]] +[[!template new="1" text="For sound, you only need to do a single beep," start="00:08:26.160" video="mainVideo" id=subtitle]] +[[!template text="so technically, it shouldn't be difficult to emulate it." start="00:08:28.560" video="mainVideo" id=subtitle]] +[[!template text="However, doing this is hard because" start="00:08:31.280" video="mainVideo" id=subtitle]] +[[!template text="Emacs officially only supports synchronous playback of sounds." start="00:08:33.039" video="mainVideo" id=subtitle]] +[[!template text="But there's also Emacs process, which you can launch in asynchronous way." start="00:08:37.200" video="mainVideo" id=subtitle]] +[[!template text="So I looked into it and found that mplayer has a slave mode" start="00:08:41.360" video="mainVideo" id=subtitle]] +[[!template text="and mpv supports listing on the fifo for commands." start="00:08:44.720" video="mainVideo" id=subtitle]] +[[!template text="So I've created a pipe, started a paused MPV in loop mode," start="00:08:48.640" video="mainVideo" id=subtitle]] +[[!template text="and always send in pause and unpause command to the FIFO," start="00:08:53.760" video="mainVideo" id=subtitle]] +[[!template text="and that way I could control" start="00:08:56.560" video="mainVideo" id=subtitle]] +[[!template text="when to start beeping and stop beeping." start="00:08:58.000" video="mainVideo" id=subtitle]] +[[!template new="1" text="So yeah, that's it so far." start="00:09:02.640" video="mainVideo" id=subtitle]] +[[!template text="It was a very educational experience." start="00:09:04.160" video="mainVideo" id=subtitle]] +[[!template text="I have tried out a bunch of games which were," start="00:09:07.200" video="mainVideo" id=subtitle]] +[[!template text="well, I almost say the worst ports of classic games I've ever tried." start="00:09:10.320" video="mainVideo" id=subtitle]] +[[!template text="It wasn't terribly fun to play them," start="00:09:14.320" video="mainVideo" id=subtitle]] +[[!template text="but was fun to improve the emulator" start="00:09:15.680" video="mainVideo" id=subtitle]] +[[!template text="until, well, things worked good enough." start="00:09:18.555" video="mainVideo" id=subtitle]] +[[!template text="I've learned a lot about how computers work at this level," start="00:09:21.760" video="mainVideo" id=subtitle]] +[[!template text="so, maybe, maybe I'll in the future make another emulator," start="00:09:25.120" video="mainVideo" id=subtitle]] +[[!template text="but I'm not sure whether anything more advanced, like an Intel 8080 emulator," start="00:09:28.880" video="mainVideo" id=subtitle]] +[[!template text="will actually run in Emacs fast enough," start="00:09:34.000" video="mainVideo" id=subtitle]] +[[!template text="but it's still an interesting idea," start="00:09:36.560" video="mainVideo" id=subtitle]] +[[!template text="because then you could actually have an OS inside Emacs" start="00:09:37.839" video="mainVideo" id=subtitle]] +[[!template text="and fulfill that one specific meme." start="00:09:40.800" video="mainVideo" id=subtitle]] +[[!template text="But if I try to do most serious stuff," start="00:09:43.120" video="mainVideo" id=subtitle]] +[[!template text="I'll probably use Chicken Scheme," start="00:09:45.440" video="mainVideo" id=subtitle]] +[[!template text="which is my preferred language for serious projects," start="00:09:47.040" video="mainVideo" id=subtitle]] +[[!template text="and write a NES game emulator." start="00:09:49.920" video="mainVideo" id=subtitle]] +[[!template text="And that's it. Thank you." start="00:09:53.279" video="mainVideo" id=subtitle]] diff --git a/2020/organizers-notebook.org b/2020/organizers-notebook.org index b8b5f6d1..33a6118a 100644 --- a/2020/organizers-notebook.org +++ b/2020/organizers-notebook.org @@ -55,8 +55,8 @@ I modified the =subed= package to work with VTT files. The modified version is a - [X] sachac [[./subtitles/emacsconf-2020--25-traverse-complex-json-structures-with-live-feedback-counsel-jq--zen-monk-alain-m-lafon-autogen.vtt]] - [X] sachac [[./subtitles/emacsconf-2020--26-emacs-as-a-highschooler-how-it-changed-my-life--pierce-wang-autogen.vtt]] - [ ] [[./subtitles/emacsconf-2020--26-emacs-as-a-highschooler-how-it-changed-my-life--questions--pierce-wang-autogen.vtt]] -- [ ] sachac [[./subtitles/emacsconf-2020--27-state-of-retro-gaming-in-emacs-chip8--vasilij-wasamasa-schneidermann-autogen.vtt]] -- [ ] [[./subtitles/emacsconf-2020--28-welcome-to-the-dungeon--erik-elmshauser-corwin-brust-autogen.vtt]] +- [X] sachac [[./subtitles/emacsconf-2020--27-state-of-retro-gaming-in-emacs-chip8--vasilij-wasamasa-schneidermann-autogen.vtt]] +- [ ] sachac [[./subtitles/emacsconf-2020--28-welcome-to-the-dungeon--erik-elmshauser-corwin-brust-autogen.vtt]] - [ ] [[./subtitles/emacsconf-2020--30-a-tour-of-vterm--gabriele-bozzola-sbozzolo-autogen.vtt]] - [ ] [[./subtitles/emacsconf-2020--30-a-tour-of-vterm--questions--gabriele-bozzola-sbozzolo-autogen.vtt]] - [ ] [[./subtitles/emacsconf-2020--31-lakota-language-and-emacs--grant-shangreaux-autogen.vtt]] diff --git a/2020/subtitles/emacsconf-2020--27-state-of-retro-gaming-in-emacs-chip8--vasilij-wasamasa-schneidermann-autogen.vtt b/2020/subtitles/emacsconf-2020--27-state-of-retro-gaming-in-emacs-chip8--vasilij-wasamasa-schneidermann-autogen.vtt deleted file mode 100644 index 1d41f1e5..00000000 --- a/2020/subtitles/emacsconf-2020--27-state-of-retro-gaming-in-emacs-chip8--vasilij-wasamasa-schneidermann-autogen.vtt +++ /dev/null @@ -1,913 +0,0 @@ -WEBVTT - -00:00:00.880 --> 00:00:02.879 -hello everyone and welcome to my talk - -00:00:02.879 --> 00:00:04.520 -state of retrogaming and Emacs - -00:00:04.520 --> 00:00:06.960 -[Music] - -00:00:06.960 --> 00:00:08.639 -first of all a little bit about myself - -00:00:08.639 --> 00:00:12.000 -my name is neilman I'm 28 years old - -00:00:12.000 --> 00:00:13.599 -I work as a cyber security consultant - -00:00:13.599 --> 00:00:15.200 -msg systems and - -00:00:15.200 --> 00:00:17.440 -test other people's web applications and - -00:00:17.440 --> 00:00:19.359 -review the source code for security - -00:00:19.359 --> 00:00:20.160 -problems - -00:00:20.160 --> 00:00:22.960 -you can reach me by email I have my own - -00:00:22.960 --> 00:00:25.039 -self-hosted git repositories - -00:00:25.039 --> 00:00:26.480 -and I have a blog where you can - -00:00:26.480 --> 00:00:28.480 -occasionally find new posts by me on all - -00:00:28.480 --> 00:00:32.160 -kinds of things not just emix things - -00:00:32.160 --> 00:00:34.800 -so but the motivation about this one I - -00:00:34.800 --> 00:00:36.480 -found that Emacs is the ultimate - -00:00:36.480 --> 00:00:38.079 -procrastination machine and there are - -00:00:38.079 --> 00:00:40.160 -lots of fun demonstrations I'll go over - -00:00:40.160 --> 00:00:41.200 -a few of them - -00:00:41.200 --> 00:00:44.160 -for example someone made a thing to - -00:00:44.160 --> 00:00:46.079 -order sell it for himself online so it - -00:00:46.079 --> 00:00:48.239 -doesn't have to walk over to the shop - -00:00:48.239 --> 00:00:50.879 -there's plenty rc bots there's some game - -00:00:50.879 --> 00:00:51.760 -things - -00:00:51.760 --> 00:00:53.520 -there's an emulator for the z machine - -00:00:53.520 --> 00:00:55.600 -which you can use to play zorg - -00:00:55.600 --> 00:00:57.600 -and so I asked myself at this point can - -00:00:57.600 --> 00:00:59.039 -you actually emulate retro games at - -00:00:59.039 --> 00:01:01.039 -60fps and it looked around a bit - -00:01:01.039 --> 00:01:02.800 -and found some projects but none that - -00:01:02.800 --> 00:01:04.479 -were actually able to - -00:01:04.479 --> 00:01:07.360 -do it at 60fps so I set out to do my own - -00:01:07.360 --> 00:01:08.000 -one - -00:01:08.000 --> 00:01:09.439 -and looked out for a console that you - -00:01:09.439 --> 00:01:11.119 -can actually emulate at that speed - -00:01:11.119 --> 00:01:13.439 -using emax with its very very limited - -00:01:13.439 --> 00:01:14.690 -rendering - -00:01:14.690 --> 00:01:16.320 -[Music] - -00:01:16.320 --> 00:01:19.520 -and here's the project chip8.el it's - -00:01:19.520 --> 00:01:20.560 -pretty much finished - -00:01:20.560 --> 00:01:22.880 -it clocks into under 1000 sourced lines - -00:01:22.880 --> 00:01:24.000 -of code - -00:01:24.000 --> 00:01:26.159 -it supports the superchip 8 extensions - -00:01:26.159 --> 00:01:28.080 -it runs at full speed all games behave - -00:01:28.080 --> 00:01:30.159 -okay as far as I'm concerned and - -00:01:30.159 --> 00:01:32.320 -yeah I'm pretty happy with it it's very - -00:01:32.320 --> 00:01:34.479 -much the hell world of emulation - -00:01:34.479 --> 00:01:37.040 -and I might maybe do some other - -00:01:37.040 --> 00:01:40.880 -emulation projects in the future - -00:01:40.880 --> 00:01:43.360 -now for the section which is the longest - -00:01:43.360 --> 00:01:45.439 -bunch of fun facts about ship a dot el - -00:01:45.439 --> 00:01:46.320 -which I've learned - -00:01:46.320 --> 00:01:49.759 -during this project so - -00:01:49.759 --> 00:01:52.240 -what the hell is debate anyway first of - -00:01:52.240 --> 00:01:54.640 -all unlike many other emulation game - -00:01:54.640 --> 00:01:56.799 -things it's not a console but a vm - -00:01:56.799 --> 00:01:58.560 -it was designed for easy parting of home - -00:01:58.560 --> 00:02:00.000 -computer games - -00:02:00.000 --> 00:02:02.560 -it wasn't terribly successful and but - -00:02:02.560 --> 00:02:03.680 -there's still a small community of - -00:02:03.680 --> 00:02:04.320 -enthusiasts - -00:02:04.320 --> 00:02:06.079 -writing games for it and there are even - -00:02:06.079 --> 00:02:09.119 -a few demos - -00:02:09.119 --> 00:02:11.920 -this vm has system specs it has a very - -00:02:11.920 --> 00:02:14.720 -very simple 8-bit cpu with 16 registers - -00:02:14.720 --> 00:02:14.959 -and - -00:02:14.959 --> 00:02:17.760 -36 fixed size instructions you have a - -00:02:17.760 --> 00:02:18.160 -whole - -00:02:18.160 --> 00:02:20.560 -4 kilobyte of ram you have a stack with - -00:02:20.560 --> 00:02:22.080 -16 return addresses - -00:02:22.080 --> 00:02:24.480 -the resolution is 64 by 32 black white - -00:02:24.480 --> 00:02:25.760 -pixels - -00:02:25.760 --> 00:02:28.000 -rendering is done by drawing sprites - -00:02:28.000 --> 00:02:29.440 -these are drawn in excel mode meaning - -00:02:29.440 --> 00:02:30.160 -that if you - -00:02:30.160 --> 00:02:32.239 -draw a sprite and set a bit it just - -00:02:32.239 --> 00:02:33.840 -flips over from black to white or white - -00:02:33.840 --> 00:02:35.040 -to black - -00:02:35.040 --> 00:02:36.560 -first one you have a modern buzz that - -00:02:36.560 --> 00:02:38.239 -can just beep at one - -00:02:38.239 --> 00:02:40.640 -frequency and most unusually there's a - -00:02:40.640 --> 00:02:43.120 -hexadecimal keypad as input - -00:02:43.120 --> 00:02:45.360 -so the keys are basically zero to nine - -00:02:45.360 --> 00:02:48.480 -and a to f - -00:02:48.480 --> 00:02:50.879 -so how does this whole thing work it - -00:02:50.879 --> 00:02:52.400 -runs an unspecified speed - -00:02:52.400 --> 00:02:53.599 -you'll probably have to do some fine - -00:02:53.599 --> 00:02:54.879 -tune you find the speed you're happy - -00:02:54.879 --> 00:02:56.080 -with - -00:02:56.080 --> 00:02:58.560 -sound and delay timers exist they count - -00:02:58.560 --> 00:03:01.120 -down at 60fps down to zero - -00:03:01.120 --> 00:03:02.879 -this is done so that you can play a - -00:03:02.879 --> 00:03:05.120 -sound at some specific time - -00:03:05.120 --> 00:03:06.640 -the game itself is loaded with a fixed - -00:03:06.640 --> 00:03:08.640 -offset into ram the program account is - -00:03:08.640 --> 00:03:10.480 -set to exactly that offset - -00:03:10.480 --> 00:03:11.920 -and from there it enters the game loop - -00:03:11.920 --> 00:03:13.840 -where decodes and instruction executes - -00:03:13.840 --> 00:03:15.519 -it for the side effects and just - -00:03:15.519 --> 00:03:18.130 -loops and does this at infinitum - -00:03:18.130 --> 00:03:19.599 -[Music] - -00:03:19.599 --> 00:03:21.920 -so the game was the first thing where - -00:03:21.920 --> 00:03:23.920 -into problems the usual game approach is - -00:03:23.920 --> 00:03:24.239 -to - -00:03:24.239 --> 00:03:26.640 -do stuff figure out how long to eight - -00:03:26.640 --> 00:03:28.239 -wait for exactly that much and - -00:03:28.239 --> 00:03:30.640 -repeat this doesn't work well in imax at - -00:03:30.640 --> 00:03:31.680 -all because well - -00:03:31.680 --> 00:03:34.959 -user input basically and - -00:03:34.959 --> 00:03:37.280 -Emacs is designed to just do whatever it - -00:03:37.280 --> 00:03:39.040 -needs to do whenever you enter use input - -00:03:39.040 --> 00:03:40.080 -instead of - -00:03:40.080 --> 00:03:42.799 -doing things at one specific time if you - -00:03:42.799 --> 00:03:43.440 -try to do - -00:03:43.440 --> 00:03:45.040 -interruptable sleep well you get - -00:03:45.040 --> 00:03:46.640 -unpredictable behavior - -00:03:46.640 --> 00:03:49.440 -for example can be the timer doesn't run - -00:03:49.440 --> 00:03:50.959 -at all at next time because you've - -00:03:50.959 --> 00:03:52.560 -accidentally cancelled it - -00:03:52.560 --> 00:03:54.400 -if you do uninterruptable sleep it's - -00:03:54.400 --> 00:03:55.760 -freezes instead which isn't what you - -00:03:55.760 --> 00:03:56.720 -want either - -00:03:56.720 --> 00:03:59.360 -so I went for timers which forced me to - -00:03:59.360 --> 00:04:00.560 -do inversion of control - -00:04:00.560 --> 00:04:02.159 -meaning that I have to write code in the - -00:04:02.159 --> 00:04:04.080 -style where it's just call it - -00:04:04.080 --> 00:04:06.159 -time and this allows this input to - -00:04:06.159 --> 00:04:07.200 -happen and to - -00:04:07.200 --> 00:04:09.120 -for things to progress at roughly the - -00:04:09.120 --> 00:04:11.040 -speed I want to - -00:04:11.040 --> 00:04:12.879 -so there's the skydiver function which - -00:04:12.879 --> 00:04:14.159 -is called a 60fps - -00:04:14.159 --> 00:04:16.000 -and I have to be very careful to not do - -00:04:16.000 --> 00:04:17.359 -too much in it - -00:04:17.359 --> 00:04:20.479 -and say this function execute cpu cycles - -00:04:20.479 --> 00:04:22.960 -decrypt the sound delay registers and - -00:04:22.960 --> 00:04:23.680 -redraw - -00:04:23.680 --> 00:04:27.759 -the screen so to map this whole system - -00:04:27.759 --> 00:04:28.800 -to mx lisp - -00:04:28.800 --> 00:04:31.199 -I've used just integers and vectors - -00:04:31.199 --> 00:04:33.120 -which contain even more integers - -00:04:33.120 --> 00:04:35.040 -this is used for the ram registers - -00:04:35.040 --> 00:04:37.759 -return stack key state screen and so on - -00:04:37.759 --> 00:04:39.120 -and so forth basically - -00:04:39.120 --> 00:04:41.520 -what you would do if you were writing c - -00:04:41.520 --> 00:04:42.800 -all of this is stored in global - -00:04:42.800 --> 00:04:44.560 -variables I'm not using any - -00:04:44.560 --> 00:04:46.479 -lists at all and as a side effect - -00:04:46.479 --> 00:04:48.400 -there's no constant going on at all - -00:04:48.400 --> 00:04:50.320 -there are no extra objects created which - -00:04:50.320 --> 00:04:51.120 -would trigger - -00:04:51.120 --> 00:04:53.919 -garbage collection pulses this getting - -00:04:53.919 --> 00:04:55.840 -this red was rather tricky actually and - -00:04:55.840 --> 00:04:56.720 -there were some - -00:04:56.720 --> 00:04:58.560 -in garbage collection problems which I - -00:04:58.560 --> 00:05:01.759 -had to resolve over time - -00:05:01.759 --> 00:05:04.320 -so the coding instructions for this you - -00:05:04.320 --> 00:05:05.520 -have to know that all instructions are - -00:05:05.520 --> 00:05:06.800 -two bytes long - -00:05:06.800 --> 00:05:08.880 -and the arguments encoded inside them - -00:05:08.880 --> 00:05:10.240 -for example the jump to address - -00:05:10.240 --> 00:05:11.440 -instruction - -00:05:11.440 --> 00:05:15.120 -is encoded as one and three hex digits - -00:05:15.120 --> 00:05:18.400 -the type is extracted masking with f000 - -00:05:18.400 --> 00:05:20.800 -and then shifting it by 12 bits mask - -00:05:20.800 --> 00:05:21.680 -means the hd - -00:05:21.680 --> 00:05:24.000 -performance binary end you can do the - -00:05:24.000 --> 00:05:25.440 -same with the argument basement with - -00:05:25.440 --> 00:05:26.639 -zero fff - -00:05:26.639 --> 00:05:29.520 -and no shift if you do this long enough - -00:05:29.520 --> 00:05:31.039 -you'll find common patterns for example - -00:05:31.039 --> 00:05:32.639 -addresses are always encoded like this - -00:05:32.639 --> 00:05:35.280 -using the last three nibbles in the code - -00:05:35.280 --> 00:05:36.880 -you'll find a big count which dispatches - -00:05:36.880 --> 00:05:38.400 -on the type and executes it for the side - -00:05:38.400 --> 00:05:40.070 -effects - -00:05:40.070 --> 00:05:41.440 -[Music] - -00:05:41.440 --> 00:05:43.440 -for testing I've initially just accused - -00:05:43.440 --> 00:05:45.919 -the rom until I fit ctrl g - -00:05:45.919 --> 00:05:47.280 -and then use the debug command to run - -00:05:47.280 --> 00:05:49.039 -the screen to a buffer - -00:05:49.039 --> 00:05:51.360 -later on I found tiny roms that just - -00:05:51.360 --> 00:05:52.320 -display a static - -00:05:52.320 --> 00:05:55.680 -test screen for example logo and looked - -00:05:55.680 --> 00:05:57.280 -whether it looked right - -00:05:57.280 --> 00:05:59.199 -I added instructions as needed and went - -00:05:59.199 --> 00:06:00.960 -through more and more and more roms and - -00:06:00.960 --> 00:06:03.360 -later I wrote in unit test suite as a - -00:06:03.360 --> 00:06:05.199 -safety net and this unit test suite it - -00:06:05.199 --> 00:06:06.000 -just - -00:06:06.000 --> 00:06:08.400 -sets up an empty emulator state executes - -00:06:08.400 --> 00:06:10.080 -some instructions and then looks whether - -00:06:10.080 --> 00:06:10.400 -the - -00:06:10.400 --> 00:06:14.880 -expected side effects have happened - -00:06:14.880 --> 00:06:17.120 -for debugging I usually use e-debug but - -00:06:17.120 --> 00:06:19.120 -this was super ineffective because well - -00:06:19.120 --> 00:06:20.880 -you don't really want to step through - -00:06:20.880 --> 00:06:22.960 -big cons doing side effects for every - -00:06:22.960 --> 00:06:24.960 -single cycle when it can take like 100 - -00:06:24.960 --> 00:06:26.880 -cycles for things to happen - -00:06:26.880 --> 00:06:29.680 -therefore I've set up logging and - -00:06:29.680 --> 00:06:31.360 -whenever I locked something - -00:06:31.360 --> 00:06:32.720 -and couldn't figure out the error I - -00:06:32.720 --> 00:06:33.919 -compared my lock output with - -00:06:33.919 --> 00:06:37.039 -instrumented version of another emulator - -00:06:37.039 --> 00:06:39.199 -and if the locks diverge then I have - -00:06:39.199 --> 00:06:40.880 -figured out where the bug lies and could - -00:06:40.880 --> 00:06:42.720 -look deeper into it - -00:06:42.720 --> 00:06:44.479 -future project idea might be a chip 8 - -00:06:44.479 --> 00:06:46.639 -debugger but I doubt I'll ever - -00:06:46.639 --> 00:06:50.720 -go into it for analysis I initially - -00:06:50.720 --> 00:06:52.639 -wrote a disassembler which is a very - -00:06:52.639 --> 00:06:54.400 -simple thing but super tedious - -00:06:54.400 --> 00:06:56.160 -especially if you wanted to add advanced - -00:06:56.160 --> 00:06:57.599 -functionality for example analysis or - -00:06:57.599 --> 00:06:59.120 -thinking of what part is data what had - -00:06:59.120 --> 00:07:00.000 -this code - -00:07:00.000 --> 00:07:01.840 -and I had this great idea for using the - -00:07:01.840 --> 00:07:03.360 -radari 2 framework - -00:07:03.360 --> 00:07:05.120 -and adding analysis and disassembly - -00:07:05.120 --> 00:07:06.479 -plug-in for it - -00:07:06.479 --> 00:07:08.400 -so I looked into this found okay here - -00:07:08.400 --> 00:07:10.319 -where you can write plugins in c - -00:07:10.319 --> 00:07:12.160 -but also in python so I wrote one in - -00:07:12.160 --> 00:07:13.520 -python and then the scout there's - -00:07:13.520 --> 00:07:15.039 -actually existing one in core which you - -00:07:15.039 --> 00:07:17.440 -have to enable explicitly by passing its - -00:07:17.440 --> 00:07:19.599 -argument so I've tried it and found it's - -00:07:19.599 --> 00:07:21.840 -not exactly as good as my own one so - -00:07:21.840 --> 00:07:23.680 -improved this one and submitted pull - -00:07:23.680 --> 00:07:26.610 -requests until it was at the same level - -00:07:26.610 --> 00:07:28.080 -[Music] - -00:07:28.080 --> 00:07:30.160 -rendering was the trickiest part of this - -00:07:30.160 --> 00:07:31.360 -whole thing because - -00:07:31.360 --> 00:07:34.319 -well I decided against using a library - -00:07:34.319 --> 00:07:35.759 -not like there would have been any - -00:07:35.759 --> 00:07:37.120 -usable library for this - -00:07:37.120 --> 00:07:39.599 -my usual approach of accelerating svg - -00:07:39.599 --> 00:07:41.680 -file was too expensive it just created - -00:07:41.680 --> 00:07:45.120 -too much garbage and took too long time - -00:07:45.120 --> 00:07:47.360 -I then tried creating mutating strings - -00:07:47.360 --> 00:07:49.599 -this was either either too expensive - -00:07:49.599 --> 00:07:52.639 -just like svgs or too complicated I - -00:07:52.639 --> 00:07:55.039 -tried changing svg tiles which created - -00:07:55.039 --> 00:07:57.280 -gaps between the lines - -00:07:57.280 --> 00:07:59.520 -then I tried to create an xpm file which - -00:07:59.520 --> 00:08:00.720 -was backed by a bull vector - -00:08:00.720 --> 00:08:02.400 -administrating this bull vector - -00:08:02.400 --> 00:08:04.400 -but the image caching effect made it - -00:08:04.400 --> 00:08:06.879 -just every nth frame to appear which - -00:08:06.879 --> 00:08:10.000 -wasn't good either then I had the idea - -00:08:10.000 --> 00:08:11.440 -to just use plain text - -00:08:11.440 --> 00:08:13.280 -and paint the individual characters with - -00:08:13.280 --> 00:08:14.800 -a different background color this - -00:08:14.800 --> 00:08:17.120 -this had perfect perfect performance - -00:08:17.120 --> 00:08:18.479 -there were many optimization attempts - -00:08:18.479 --> 00:08:20.000 -until I got there and it was - -00:08:20.000 --> 00:08:21.840 -very very stressful I wasn't sure - -00:08:21.840 --> 00:08:23.199 -whether I would ever get to accept the - -00:08:23.199 --> 00:08:26.160 -performance at all - -00:08:26.160 --> 00:08:28.560 -for sound you only need to a single beep - -00:08:28.560 --> 00:08:30.319 -so technically it shouldn't be difficult - -00:08:30.319 --> 00:08:31.280 -to emulate it - -00:08:31.280 --> 00:08:33.519 -however doing this is hard because Emacs - -00:08:33.519 --> 00:08:34.880 -officially only supports synchronous - -00:08:34.880 --> 00:08:37.200 -playback of sounds - -00:08:37.200 --> 00:08:39.039 -but there's also emax process which you - -00:08:39.039 --> 00:08:41.360 -can launch in asynchronous way - -00:08:41.360 --> 00:08:43.519 -so I looked into it and found that - -00:08:43.519 --> 00:08:45.279 -employee has a slave mode and mpv - -00:08:45.279 --> 00:08:46.640 -supports listing on the - -00:08:46.640 --> 00:08:50.880 -fifo for commands so I've created a pipe - -00:08:50.880 --> 00:08:54.000 -started a past mpv in loop mode and - -00:08:54.000 --> 00:08:55.519 -always send in pause and pause command - -00:08:55.519 --> 00:08:58.000 -to the fifo and that way I could control - -00:08:58.000 --> 00:09:02.640 -when to start beeping and stop beeping - -00:09:02.640 --> 00:09:05.760 -so yeah that's it so far was a very - -00:09:05.760 --> 00:09:07.200 -educational experience - -00:09:07.200 --> 00:09:09.279 -I have tried out a bunch of games which - -00:09:09.279 --> 00:09:10.320 -were - -00:09:10.320 --> 00:09:12.640 -well I almost say the worst ports of - -00:09:12.640 --> 00:09:14.320 -classic games I've ever tried - -00:09:14.320 --> 00:09:15.839 -it wasn't terribly fun to play them but - -00:09:15.839 --> 00:09:18.320 -was fun to improve the emulator until - -00:09:18.320 --> 00:09:21.760 -well things worked good enough - -00:09:21.760 --> 00:09:23.279 -and I've learned a lot about how - -00:09:23.279 --> 00:09:25.120 -computers work at this level - -00:09:25.120 --> 00:09:27.760 -so maybe maybe I'll in the future make - -00:09:27.760 --> 00:09:28.880 -another emulator - -00:09:28.880 --> 00:09:31.920 -but uh I'm not sure whether anything - -00:09:31.920 --> 00:09:34.000 -more advanced like intel 8080 emulator - -00:09:34.000 --> 00:09:36.560 -will actually run mix fast enough - -00:09:36.560 --> 00:09:37.839 -but it's still an interesting idea - -00:09:37.839 --> 00:09:39.200 -because then you could actually have an - -00:09:39.200 --> 00:09:39.600 -os - -00:09:39.600 --> 00:09:41.680 -inside Emacs and fulfill that one - -00:09:41.680 --> 00:09:43.120 -specific meme - -00:09:43.120 --> 00:09:45.440 -but if I try to do most serious stuff - -00:09:45.440 --> 00:09:47.279 -I'll probably use chicken scheme which - -00:09:47.279 --> 00:09:48.000 -is my - -00:09:48.000 --> 00:09:49.920 -preferred language for serious projects - -00:09:49.920 --> 00:09:53.279 -and writing neso gamer emulator - -00:09:53.279 --> 00:09:57.839 -and that's it thank you diff --git a/2020/subtitles/emacsconf-2020--27-state-of-retro-gaming-in-emacs-chip8--vasilij-wasamasa-schneidermann.vtt b/2020/subtitles/emacsconf-2020--27-state-of-retro-gaming-in-emacs-chip8--vasilij-wasamasa-schneidermann.vtt new file mode 100644 index 00000000..3d655630 --- /dev/null +++ b/2020/subtitles/emacsconf-2020--27-state-of-retro-gaming-in-emacs-chip8--vasilij-wasamasa-schneidermann.vtt @@ -0,0 +1,630 @@ +WEBVTT + +00:00:00.880 --> 00:00:04.520 +Hello everyone and welcome to my talk, "The State of Retro Gaming and Emacs." + +00:00:06.960 --> 00:00:08.639 +First of all, a little bit about myself. + +00:00:08.639 --> 00:00:12.000 +My name is Vasilij Schneidermann. I'm 28 years old. + +00:00:12.000 --> 00:00:14.719 +I work as a cyber security consultant at msg systems, + +00:00:14.719 --> 00:00:17.359 +and test other people's web applications + +00:00:17.359 --> 00:00:20.160 +and review the source code for security problems. + +00:00:20.160 --> 00:00:22.080 +You can reach me by email. + +00:00:22.080 --> 00:00:25.039 +I have my own self-hosted git repositories, + +00:00:25.039 --> 00:00:28.160 +and I have a blog where you can occasionally find new posts by me + +00:00:28.160 --> 00:00:32.160 +on all kinds of things, not just Emacs things. + +00:00:32.160 --> 00:00:34.559 +The motivation about this one... + +00:00:34.559 --> 00:00:37.600 +I found that Emacs is the ultimate procrastination machine, + +00:00:37.600 --> 00:00:39.600 +and there are lots of fun demonstrations. + +00:00:39.600 --> 00:00:41.200 +I'll go over a few of them. + +00:00:41.200 --> 00:00:45.840 +For example, someone made a thing to order salad for himself online, + +00:00:45.840 --> 00:00:48.239 +so he doesn't have to walk over to the shop. + +00:00:48.239 --> 00:00:51.760 +There's plenty of IRC bots. There's some game things. + +00:00:51.760 --> 00:00:55.600 +There's an emulator for the Z-machine +which you can use to play zork. + +00:00:55.600 --> 00:00:57.440 +And so I asked myself, at this point, + +00:00:57.440 --> 00:00:59.920 +can you actually emulate retro games at 60fps? + +00:00:59.920 --> 00:01:02.079 +I looked around a bit and found some projects, + +00:01:02.079 --> 00:01:06.159 +but none that were actually able to do it at 60fps. + +00:01:06.159 --> 00:01:08.000 +So I set out to do my own one, + +00:01:08.000 --> 00:01:09.200 +and looked out for a console + +00:01:09.200 --> 00:01:11.119 +that you can actually emulate at that speed, + +00:01:11.119 --> 00:01:14.690 +using Emacs with its very, very limited rendering. + +00:01:16.320 --> 00:01:19.200 +And here's the project, chip8.el. + +00:01:19.200 --> 00:01:20.560 +It's pretty much finished. + +00:01:20.560 --> 00:01:24.000 +It clocks into under 1000 source lines of code. + +00:01:24.000 --> 00:01:26.159 +It supports the superchip 8 extensions. + +00:01:26.159 --> 00:01:27.280 +It runs at full speed. + +00:01:27.280 --> 00:01:29.600 +All games behave okay, as far as I'm concerned, + +00:01:29.600 --> 00:01:31.680 +and yeah, I'm pretty happy with it. + +00:01:31.680 --> 00:01:34.479 +It's very much the hello world of emulation, + +00:01:34.479 --> 00:01:40.880 +and I might, maybe, do some other emulation projects in the future. + +00:01:40.880 --> 00:01:43.360 +Now, for the section which is the longest: + +00:01:43.360 --> 00:01:45.439 +bunch of fun facts about chip8.el + +00:01:45.439 --> 00:01:49.200 +which I've learned during this project. + +00:01:49.200 --> 00:01:51.759 +So what the hell is chip8 anyway? + +00:01:51.759 --> 00:01:54.960 +First of all, unlike many other emulation game things, + +00:01:54.960 --> 00:01:56.799 +it's not a console, but a VM. + +00:01:56.799 --> 00:02:00.000 +It was designed for easy porting of home +computer games. + +00:02:00.000 --> 00:02:02.320 +It wasn't terribly successful, + +00:02:02.320 --> 00:02:05.439 +but there's still a small community of enthusiasts writing games for it, + +00:02:05.439 --> 00:02:09.119 +and there are even a few demos. + +00:02:09.119 --> 00:02:11.039 +This VM has system specs. + +00:02:11.039 --> 00:02:14.720 +It has a very, very simple 8-bit cpu with 16 registers, + +00:02:14.720 --> 00:02:17.280 +and 36 fixed-size instructions. + +00:02:17.280 --> 00:02:19.680 +You have a whole 4 kilobyte of RAM. + +00:02:19.680 --> 00:02:22.080 +You have a stack with 16 return addresses. + +00:02:22.080 --> 00:02:25.760 +The resolution is 64 by 32 black/white pixels. + +00:02:25.760 --> 00:02:28.000 +Rendering is done by drawing sprites. + +00:02:28.000 --> 00:02:29.200 +These are drawn in XOR mode, + +00:02:29.200 --> 00:02:31.840 +meaning that if you draw a sprite and set a bit, + +00:02:31.840 --> 00:02:35.040 +it just flips over from black to white or white to black. + +00:02:35.040 --> 00:02:39.360 +For sound, you have a monotone buzzer that can just beep at one frequency. + +00:02:39.360 --> 00:02:43.120 +Most unusually, there's a hexadecimal keypad as input, + +00:02:43.120 --> 00:02:48.480 +so the keys are basically zero to nine and a to f. + +00:02:48.480 --> 00:02:50.720 +So how does this whole thing work? + +00:02:50.720 --> 00:02:52.400 +It runs at an unspecified speed. + +00:02:52.400 --> 00:02:53.040 +You'll probably have to do some fine-tuning + +00:02:53.040 --> 00:02:56.080 +to find the speed you're happy with. + +00:02:56.080 --> 00:02:58.080 +Sound and delay timers exist. + +00:02:58.080 --> 00:03:01.120 +They count down at 60fps down to 0. + +00:03:01.120 --> 00:03:05.120 +This is done so that you can play a sound at some specific time. + +00:03:05.120 --> 00:03:07.840 +The game itself is loaded with a fixed offset into RAM. + +00:03:07.840 --> 00:03:10.480 +The program counter is set to exactly that offset, + +00:03:10.480 --> 00:03:11.920 +and from there it enters the game loop + +00:03:11.920 --> 00:03:13.280 +where it decodes an instruction, + +00:03:13.280 --> 00:03:14.800 +executes it for the side effects, + +00:03:14.800 --> 00:03:18.130 +and just loops and does this ad infinitum. + +00:03:19.599 --> 00:03:22.720 +So the game loop was the first thing where we ran into problems. + +00:03:22.720 --> 00:03:25.120 +The usual game approach is to do stuff, + +00:03:25.120 --> 00:03:26.640 +figure out how long to wait, + +00:03:26.640 --> 00:03:29.280 +wait for exactly that much, and repeat. + +00:03:29.280 --> 00:03:31.680 +This doesn't work well in Emacs at all, because, well, + +00:03:31.680 --> 00:03:34.959 +user input, basically. + +00:03:34.959 --> 00:03:37.760 +Emacs is designed to just do whatever it needs to do + +00:03:37.760 --> 00:03:39.040 +whenever you enter user input + +00:03:39.040 --> 00:03:42.319 +instead of doing things at one specific time. + +00:03:42.319 --> 00:03:46.640 +If you try to do interruptable sleep, well, you get unpredictable behavior. + +00:03:46.640 --> 00:03:50.480 +For example, it can be the timer doesn't run at all at the next time + +00:03:50.480 --> 00:03:52.560 +because you've accidentally cancelled it. + +00:03:52.560 --> 00:03:55.120 +If you do uninterruptable sleep, it freezes instead , + +00:03:55.120 --> 00:03:56.720 +which isn't what we want either. + +00:03:56.720 --> 00:04:00.560 +So I went for timers, which forced me to do inversion of control, + +00:04:00.560 --> 00:04:02.560 +meaning that I have to write code in the style + +00:04:02.560 --> 00:04:04.879 +where it just calls timer, + +00:04:04.879 --> 00:04:06.560 +and this allows this input to happen + +00:04:06.560 --> 00:04:11.040 +and for things to progress at roughly the speed I want to. + +00:04:11.040 --> 00:04:14.159 +So there's the timer function which is called at 60fps + +00:04:14.159 --> 00:04:17.359 +and I have to be very careful to not do too much in it. + +00:04:17.359 --> 00:04:21.305 +And, say, this function executes CPU cycles, + +00:04:21.305 --> 00:04:26.479 +decrement the sound/delay registers, and redraw the screen. + +00:04:26.479 --> 00:04:28.800 +So to map this whole system to Emacs Lisp, + +00:04:28.800 --> 00:04:31.199 +I've used just integers and vectors + +00:04:31.199 --> 00:04:33.120 +which contain even more integers. + +00:04:33.120 --> 00:04:35.040 +This is used for the RAM, registers, + +00:04:35.040 --> 00:04:37.040 +return stack, key state, screen, + +00:04:37.040 --> 00:04:38.508 +and so on and so forth. + +00:04:38.508 --> 00:04:41.520 +Basically, what you would do if you were writing C. + +00:04:41.520 --> 00:04:43.360 +All of this is stored in global variables. + +00:04:43.360 --> 00:04:45.600 +I'm not using any lists at all. + +00:04:45.600 --> 00:04:48.400 +As a side effect, there's no consing going on at all. + +00:04:48.400 --> 00:04:50.080 +There are no extra objects created + +00:04:50.080 --> 00:04:53.199 +which would trigger garbage collection processes. + +00:04:53.199 --> 00:04:55.600 +Getting this right was rather tricky, actually, + +00:04:55.600 --> 00:04:58.240 +and there were some hidden garbage collection problems + +00:04:58.240 --> 00:05:01.759 +which I had to resolve over time. + +00:05:01.759 --> 00:05:03.759 +So, decoding instructions. + +00:05:03.759 --> 00:05:06.800 +For this, you have to know that all instructions are two bytes long, + +00:05:06.800 --> 00:05:08.880 +and the arguments are encoded inside them. + +00:05:08.880 --> 00:05:11.440 +For example, the jump to address instruction + +00:05:11.440 --> 00:05:15.120 +is encoded as one and three hex digits. + +00:05:15.120 --> 00:05:18.400 +The type is extracted masking with #xF000 + +00:05:18.400 --> 00:05:20.400 +and then shifting it by 12 bits. + +00:05:20.400 --> 00:05:23.520 +Mask means you perform the binary AND. + +00:05:23.520 --> 00:05:28.400 +You can do the same with the argument by masking with #0xFFF and no shift. + +00:05:28.400 --> 00:05:30.560 +If you do this long enough, you'll find common patterns. + +00:05:30.560 --> 00:05:32.639 +For example, addresses are always encoded like this + +00:05:32.639 --> 00:05:34.880 +using the last three nibbles. + +00:05:34.880 --> 00:05:36.160 +In the code, you'll find a big cond + +00:05:36.160 --> 00:05:40.070 +which dispatches on the type and executes it for the side effects. + +00:05:41.440 --> 00:05:45.919 +For testing, I've initially just executed the ROM until I've hit C-g, + +00:05:45.919 --> 00:05:49.039 +and then use the debug command to render the screen to a buffer. + +00:05:49.039 --> 00:05:53.199 +Later on, I found tiny ROMs that just display a static test screen, + +00:05:53.199 --> 00:05:57.280 +for example, logo, and looked whether it looked right. + +00:05:57.280 --> 00:05:58.800 +I added instructions as needed + +00:05:58.800 --> 00:06:00.720 +and went through more and more and more ROMs. + +00:06:00.720 --> 00:06:04.000 +And later I wrote a unit test suite as a safety net. + +00:06:04.000 --> 00:06:07.840 +This unit test suite, it just sets up an empty emulator state, + +00:06:07.840 --> 00:06:09.199 +executes some instructions, + +00:06:09.199 --> 00:06:14.880 +and then looks whether the expected side effects have happened. + +00:06:14.880 --> 00:06:18.319 +For debugging, I usually use edebug, but this was super ineffective, + +00:06:18.319 --> 00:06:21.600 +because, well, you don't really want to step through big cons + +00:06:21.600 --> 00:06:23.680 +doing side effects for every single cycle, + +00:06:23.680 --> 00:06:26.880 +when it can take like 100 cycles for things to happen. + +00:06:26.880 --> 00:06:29.680 +Therefore I've set up logging. + +00:06:29.680 --> 00:06:32.639 +Whenever I logged something and couldn't figure out the error, + +00:06:32.639 --> 00:06:37.039 +I compared my log output with the instrumented version of another emulator, + +00:06:37.039 --> 00:06:40.479 +and if the logs diverge, then I have figured out where the bug lies + +00:06:40.479 --> 00:06:42.720 +and could look deeper into it. + +00:06:42.720 --> 00:06:44.960 +Future project idea might be a chip 8 debugger, + +00:06:44.960 --> 00:06:49.440 +but I doubt I'll ever go into it. + +00:06:49.440 --> 00:06:51.759 +For analysis, I initially wrote a disassembler, + +00:06:51.759 --> 00:06:54.400 +which is a very simple thing but super tedious, + +00:06:54.400 --> 00:06:56.639 +especially if you wanted to add advanced functionality, + +00:06:56.639 --> 00:06:58.720 +for example, analysis or thinking of what part is data, + +00:06:58.720 --> 00:07:00.000 +what part is code. + +00:07:00.000 --> 00:07:03.360 +I had this great idea for using the radare 2 framework + +00:07:03.360 --> 00:07:06.479 +and adding analysis and disassembly plug-in for it. + +00:07:06.479 --> 00:07:08.400 +So I looked into this. Found, okay, + +00:07:08.400 --> 00:07:10.319 +you can write plugins in C + +00:07:10.319 --> 00:07:12.639 +but also in Python, so I wrote one in Python, + +00:07:12.639 --> 00:07:14.720 +and then discovered there's actually an existing one in core, + +00:07:14.720 --> 00:07:18.400 +which you have to enable explicitly by passing an extra argument. + +00:07:18.400 --> 00:07:21.680 +I've tried it and found it's not exactly as good as my own one, + +00:07:21.680 --> 00:07:24.160 +so I improved this one and submitted pull requests + +00:07:24.160 --> 00:07:26.610 +until it was at the same level. + +00:07:28.080 --> 00:07:30.720 +Rendering was the trickiest part of this whole thing, + +00:07:30.720 --> 00:07:34.319 +because, well, I decided against using a library. + +00:07:34.319 --> 00:07:37.120 +Not like there would have been any usable library for this. + +00:07:37.120 --> 00:07:40.880 +My usual approach of creating SVG files was too expensive. + +00:07:40.880 --> 00:07:45.120 +It just created too much garbage and took too long time. + +00:07:45.120 --> 00:07:47.360 +I then tried creating mutating strings. + +00:07:47.360 --> 00:07:52.479 +This was either too expensive, just like SVGs, or too complicated. + +00:07:52.479 --> 00:07:57.280 +I tried changing SVG tiles, which created gaps between the lines. + +00:07:57.280 --> 00:08:00.720 +Then I tried to create an xpm file which was backed by a bool vector + +00:08:00.720 --> 00:08:02.400 +and mutating this bool vector, + +00:08:02.400 --> 00:08:04.000 +but the image caching effect + +00:08:04.000 --> 00:08:06.479 +made it just every nth frame to appear, + +00:08:06.479 --> 00:08:08.879 +which wasn't good either. + +00:08:08.879 --> 00:08:11.440 +Then I had the idea to just use plain text + +00:08:11.440 --> 00:08:13.120 +and paint the individual characters + +00:08:13.120 --> 00:08:14.800 +with a different background color. + +00:08:14.800 --> 00:08:17.120 +This had perfect, perfect performance. + +00:08:17.120 --> 00:08:19.280 +There were many optimization attempts until I got there, + +00:08:19.280 --> 00:08:21.199 +and it was very, very stressful. + +00:08:21.199 --> 00:08:26.160 +I wasn't sure whether I would ever get to accept the performance at all. + +00:08:26.160 --> 00:08:28.560 +For sound you only need to do a single beep, + +00:08:28.560 --> 00:08:31.280 +so technically, it shouldn't be difficult to emulate it. + +00:08:31.280 --> 00:08:33.039 +However, doing this is hard because + +00:08:33.039 --> 00:08:37.200 +Emacs officially only supports synchronous playback of sounds. + +00:08:37.200 --> 00:08:41.360 +But there's also Emacs process, which you can launch in asynchronous way. + +00:08:41.360 --> 00:08:44.720 +So I looked into it and found that mplayer has a slave mode + +00:08:44.720 --> 00:08:48.640 +and mpv supports listing on the fifo for commands. + +00:08:48.640 --> 00:08:53.760 +So I've created a pipe, started a paused MPV in loop mode, + +00:08:53.760 --> 00:08:56.560 +and always send in pause and unpause command to the FIFO, + +00:08:56.560 --> 00:08:58.000 +and that way I could control + +00:08:58.000 --> 00:09:02.640 +when to start beeping and stop beeping. + +00:09:02.640 --> 00:09:04.160 +So yeah, that's it so far. + +00:09:04.160 --> 00:09:07.200 +It was a very educational experience. + +00:09:07.200 --> 00:09:10.320 +I have tried out a bunch of games which were, + +00:09:10.320 --> 00:09:14.320 +well, I almost say the worst ports of classic games I've ever tried. + +00:09:14.320 --> 00:09:15.680 +It wasn't terribly fun to play them, + +00:09:15.680 --> 00:09:18.555 +but was fun to improve the emulator + +00:09:18.555 --> 00:09:21.760 +until, well, things worked good enough. + +00:09:21.760 --> 00:09:25.120 +I've learned a lot about how computers work at this level, + +00:09:25.120 --> 00:09:28.880 +so, maybe, maybe I'll in the future make another emulator, + +00:09:28.880 --> 00:09:34.000 +but I'm not sure whether anything more advanced, like an Intel 8080 emulator, + +00:09:34.000 --> 00:09:36.560 +will actually run in Emacs fast enough, + +00:09:36.560 --> 00:09:37.839 +but it's still an interesting idea, + +00:09:37.839 --> 00:09:40.800 +because then you could actually have an OS inside Emacs + +00:09:40.800 --> 00:09:43.120 +and fulfill that one specific meme. + +00:09:43.120 --> 00:09:45.440 +But if I try to do most serious stuff, + +00:09:45.440 --> 00:09:47.040 +I'll probably use Chicken Scheme, + +00:09:47.040 --> 00:09:49.920 +which is my preferred language for serious projects, + +00:09:49.920 --> 00:09:53.279 +and write a NES game emulator. + +00:09:53.279 --> 00:09:57.839 +And that's it. Thank you. |