Skip to content

Commit

Permalink
feat: move tetane-web into project
Browse files Browse the repository at this point in the history
  • Loading branch information
lukexor committed Oct 12, 2023
1 parent 2cf1a3a commit a49ff97
Show file tree
Hide file tree
Showing 20 changed files with 566 additions and 1 deletion.
1 change: 1 addition & 0 deletions web/public/tetanes-web/dist/607.bundle.js

Large diffs are not rendered by default.

Binary file not shown.
1 change: 1 addition & 0 deletions web/public/tetanes-web/dist/bundle.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Binary file added web/public/tetanes-web/emulogic.ttf
Binary file not shown.
303 changes: 303 additions & 0 deletions web/public/tetanes-web/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,303 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>TetaNES Web</title>
<style>
@font-face {
font-family: "Emulogic";
src: local("Emulogic"), url("emulogic.ttf") format("truetype");
}

body {
--color: #aaa;
--heading: #92340b;
--background: #0c141f;
background-color: var(--background);
max-width: 80%;
margin: auto;
margin-bottom: 100px;
color: var(--color);
font-family: "Courier New", Courier, monospace;
}

h1 {
margin: 40px 0;
}

h1,
h2 {
color: var(--heading);
font-family: Emulogic;
text-align: center;
}

p {
font-weight: 600;
max-width: 70ch;
margin: 15px 0;
}

table {
--color: #333;
border-collapse: separate;
border-color: var(--color);
border-spacing: 0;
border: 0.5px solid var(--color);
text-align: left;
width: 100%;
}

th {
color: var(--heading);
}

th,
td {
padding: 5px;
border: 0.5px solid var(--color);
}

td {
font-weight: bold;
}

a {
color: #ffe64d;
text-decoration: none;
}

a:hover {
color: #df740c;
text-decoration: underline;
}

canvas {
background: rgba(0.13, 0.13, 0.13, 1);
margin: 0 40px;
width: fit-content;
height: fit-content;
}

.load-rom {
width: 0.1px;
height: 0.1px;
opacity: 0;
overflow: hidden;
position: absolute;
z-index: -1;
}

button,
.load-rom-label {
background-color: #92340b; /* Rust color */
border: none;
color: white;
cursor: pointer;
display: inline-block;
text-decoration: none;
font-family: inherit;
font-size: 1em;
font-family: Emulogic;
}

#scale1,
#scale2,
#scale2 {
width: 3.5em;
}
button {
margin: 10px 0;
padding: 5px 8px;
}
button:hover,
label:hover {
background-color: #0b6992;
}

.load-rom-label {
margin-top: 20px;
margin-bottom: 10px;
width: 10em;
font-size: 1em;
padding: 10px 16px;
text-align: center;
}

button:focus,
button:hover,
.load-rom-label:focu,
.load-rom-label:hover {
color: #000;
background-color: #48bef1;
}

.load-rom-label:focus {
outline: 1px dotted #fff;
outline: -webkit-focus-ring-color auto 5px;
}

.load-rom-label * {
pointer-events: none;
}

#error {
color: red;
font-weight: bold;
text-align: center;
margin-bottom: 20px;
}

#wrapper {
display: flex;
justify-content: center;
flex-wrap: wrap;
margin-bottom: 20px;
}

#content {
max-width: 70ch;
margin: auto;
}

#controls {
display: flex;
flex-direction: column;
}

#homebrew-menu {
top: 40px;
left: 50%;
transform: translateX(-50%);
background-color: var(--background);
position: absolute;
width: 50%;
height: 500px;
border: 2px solid var(--heading);
display: flex;
flex-direction: column;
align-items: center;
}

#homebrew-list {
display: flex;
flex-direction: column;
}

#homebrew-close {
position: absolute;
right: 10px;
}

.hidden {
display: none !important;
}
</style>
</head>
<body>
<noscript
>This page contains WebAssembly and Javascript content, please enable
Javascript in your browser.</noscript
>

<h1>TetaNES</h1>

<div id="error"></div>
<div id="wrapper">
<canvas id="view" width="512" height="480"></canvas>
<canvas id="backView" class="hidden"></canvas>

<div id="controls">
<input
type="file"
id="load-rom"
name="load-rom"
class="load-rom"
accept=".nes"
/>
<label id="load-rom-label" for="load-rom" class="load-rom-label"
>Load ROM</label
>

<button id="load-homebrew">Homebrew ROMs</button>

<div id="scale">
<button id="scale1">1X</button>
<button id="scale2">2X</button>
<button id="scale3">3X</button>
</div>

<button id="toggle-audio">Unmute</button>
<button id="toggle-pause">Pause</button>

<div id="fps"></div>
</div>
</div>

<div id="content">
<p>
<em>TetaNES</em> is an emulator for the Nintendo Entertainment System
(NES) written in <a href="https://www.rust-lang.org/">Rust</a> and
<a href="https://webassembly.org/">WebAssembly</a>. The desktop version
is much more performant and fully featured, written using
<a href="https://www.libsdl.org/">SDL2</a>.
</p>

<p>
You can choose from a list of homebrew games, or load your own ROM which
uses the <a href="https://www.nesdev.org/wiki/INES">iNES</a> or
<a href="https://www.nesdev.org/wiki/NES_2.0">NES 2.0</a> format.
</p>

<p>
You can check out the code on
<a href="https://github.com/lukexor/tetanes">github</a>.
</p>

<h2>Controls</h2>
<table>
<tr>
<th>Action</th>
<th>Key</th>
</tr>
<tr>
<td>A Button</td>
<td>Z</td>
</tr>
<tr>
<td>B Button</td>
<td>X</td>
</tr>
<tr>
<td>A Button (Turbo)</td>
<td>A</td>
</tr>
<tr>
<td>B Button (Turbo)</td>
<td>S</td>
</tr>
<tr>
<td>Start Button</td>
<td>Return</td>
</tr>
<tr>
<td>Select Button</td>
<td>Right Shift</td>
</tr>
<tr>
<td>D-Pad</td>
<td>Arrow Keys</td>
</tr>
</table>
</div>

<div id="homebrew-menu" class="hidden">
<h2>Homebrew ROMS</h2>
<div id="homebrew-list"></div>
<button id="homebrew-close">X</button>
</div>

<script src="dist/bundle.js"></script>
</body>
</html>
Binary file added web/public/tetanes-web/roms/alter_ego.nes
Binary file not shown.
89 changes: 89 additions & 0 deletions web/public/tetanes-web/roms/alter_ego.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
Development notes

After completing Lawn Master, I had plan to try use C compiler to make a simple
NES game. From my previous experience with programming in C for micros (Genesis
and ZX Spectrum), and from things that thefox did, I knew it could be very
worthy in terms of development speed. The plan was to check it, and, in case of
success, prove that C is an actual option to develop NES games, not just a
theoretical possibility.

I wanted to make a project very fast, so I decided to not do an original game
this time, because design takes most of the time, and just make a port. I've
seen two new ZX Spectrum games by Denis Grachev, Join and Alter ego, at WoS when
they were released, and liked the combination of simplicity, playability, and
sort of retro appeal in them. I made a low-level library in 6502 assembly to use
in the project first. When main features of the library were implemented, I have
sent a mail to Denis, asking if he would allow to port Alter Ego. It took some
time, from June 8 to 17, to get the answer. Denis gave his permission, but I
already was busy with other project, and only has been able to start on the port
June 25.

Development process took about 10 days, the game was fully completed, but
without music, at July 5. This includes finishing the library, writing all the
game code from scratch, reverse-engineering levels format, and beating up both
the original game and port few times to test everything. The most difficult part
that was not expected by me initially was redesign of all the levels from
scratch. Initially I thought I can just convert them, edit a bit, and draw new
graphics, but in order to be able to use more colors and make better graphics I
had to completely redo all the levels to the NES attribute grid, only keeping
overall design of the original levels. In other words, none of the original data
get into the port, and there were some changes to make it more playable as well,
so it is actually more like a remake than a port. Levels and graphics redesign
took most of the time. I also made 5 graphics sets instead of 3 sets from the
original.

Code part was relatively easy both in assembly (low-level libary) and C (game),
except for few WTF bugs that took some time to figure out. There are about 1000
lines of assembly code for library, 1000 lines of FamiTone code (has been
adapted easiliy), and ~1500 lines of C code. Even total number of lines, 3500,
is significally less than amount of assembly code in my previous NES games that
were written in assembly, had ~5000 lines each (including FamiTone too), and
were much simpler gameplay and game logic wise.

As the game was a bit short on RAM, I've put FamiTone vars along with palette
buffer into the stack page. Despite being written in C, the game uses ~20 bytes
of the stack at most.

Other part of speeding up the development process was 'outsourcing' of the
music. I knew it is a risky decision, because any other person involved into a
project actually increases overall time, not decreases it, but I just tired from
making everything by myself all the time. It did increased time very
considerably - although I've negotiated about the music with kulor even before
starting any actual work on the game, by different reasons including personal
busyness and some misunderstanding, he only started 15th, ten days after the
game development itself was completed. This amount of music revealed a lot of
bugs and problems in FamiTone, not all of which were fixed, and data of one of
tracks was fixed by hand due to lack of time. Music was finished 22th, just in
time for DiHalt demoparty. Initially I planned to just release game, but because
the party date was now close, and there was a multiplatform game compo, I
decided to release the game there to get more publicity.

My conclucion regarding C usage on NES is that it is worthy indeed. It speeds up
and simplifies development process a lot because it greatly reduces amount of
code to be written and debugged, and the code is much more readable. However, to
use C you just have to know the system and 6502 very well, because debugging is
much more difficult - in case of the problems when C code does not work as
expected, you need to figure out what to do by examining of the generated
assembly code. So it is not easy way to program for NES, it actually requires
more knowledge than programming in assembly. Execution speed is, of course,
lower, but this wasn't an issue for this project, the size of the generated code
was more important actually - it is much larger than it could be if programmed
in assembly by hand.

Please note that the game is released as freeware, not Public Domain. There are
three authors involved. I personally grant you rights to do whatever you want
with things I created (code, sound effects, graphics), but rights to other
components (game concept, characters, title, music) are reserved to authors of
these components. I.e., if you want to port it somewhere else, you need to ask
Denis Grachev (and Kulor, if you need music) for permission.


Software used

CC65 - C compiler and assembler Notepad++ - for all the code and text works
FamiTracker - to make all the music and sound effects UnrealSpeccy - playing and
reversing the original version Borland Turbo Explorer - to make a level editor,
but it was only used to view levels FCEUX, VirtuaNES (profiler mod) - to test
everything, some others for compatibility tests NES Screen Tool - to design all
the graphics, screens, and levels Inkscape, Blender, GIMP, CutePDF Writer - to
make manual and label
Loading

0 comments on commit a49ff97

Please sign in to comment.