blob: 1d41f1e52de08db8c8f75e20a2f46bd5beea1511 (
plain) (
tree)
|
|
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
|