diff --git a/Makefile b/Makefile
index a9671f3..7f238b7 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
# Makefile for the nibbles lab.
# ---> Compile with gamke on BSD systems! <---
-all: nibbles_asm nibbles_asm_start
+all: nibbles_c nibbles_asm nibbles_asm_start
# Rule for compiling C source
.c.o:
@@ -25,7 +25,10 @@ nibbles_asm: main.o nibbles.o helpers.o
nibbles_asm_start: start.o nibbles.o helpers.o
gcc -m32 -lcurses -lc -nostdlib $^ -o $@
+# Documentation
+docs: cnibbles.c nibbles.S start.S
+ docco --languages=docco-languages.json $^
+
clean:
- rm -f *~
rm -f *.o
rm -f nibbles_c nibbles_asm nibbles_asm_start
diff --git a/cnibbles.c b/cnibbles.c
index d336e46..ae0b5ff 100644
--- a/cnibbles.c
+++ b/cnibbles.c
@@ -22,6 +22,8 @@ void spawnApple();
void tick();
void draw();
+// start_game
+// ==========
void start_game(int len, int n_apples)
{
if (startLength == 0)
@@ -36,8 +38,6 @@ void start_game(int len, int n_apples)
nib_init();
- //printf("Started with worm length %i and %i apples!\n", snakeLength, numApples);
-
// Initialize the snake array
// Set every cell to a time value greater than the snake length
for (unsigned int i = 0; i < WIDTH*HEIGHT; ++i) {
@@ -69,6 +69,8 @@ void start_game(int len, int n_apples)
}
}
+// spawnApple
+// ==========
void spawnApple()
{
unsigned int x = rand() % WIDTH;
@@ -77,26 +79,30 @@ void spawnApple()
x = rand() % WIDTH;
y = rand() % HEIGHT;
}
- //printf("Placing apple at (%i,%i)\n", x, y);
snake[x + y*WIDTH] = -1;
}
+// tick
+// ====
void tick()
{
currentTick++;
// Increment cell times
+ // --------------------
for (unsigned int i = 0; i < WIDTH*HEIGHT; ++i) {
int val = snake[i];
if (val < 0) {
// Apple lifetime
- //snake[i]--;
+ /* snake[i]--; */
} else if (val >= 0 && val <= snakeLength) {
// Other cells
snake[i]++;
}
}
+ // Input
+ // -----
int key = nib_poll_kbd();
if (key == 'w' && snakeDirY != 1) {
snakeDirX = 0;
@@ -116,8 +122,12 @@ void tick()
}
// Move snake
+ // ----------
snakeX = (snakeX + snakeDirX + WIDTH) % WIDTH;
snakeY = (snakeY + snakeDirY + HEIGHT) % HEIGHT;
+
+ // Collision
+ // ---------
// On collision with self
int val = snake[snakeX + snakeY*WIDTH];
if (val < snakeLength && val >= 0) {
@@ -131,45 +141,50 @@ void tick()
// Increase our length!
snakeLength++;
}
+
+ // Update world
+ // ------------
snake[snakeX + snakeY*WIDTH] = 0;
}
+// draw
+// ====
void draw()
{
char body[] = "NIBBLES";
for (int y = -1; y <= HEIGHT; ++y) {
for (int x = -1; x <= WIDTH; ++x) {
-
+ // Draw a border outside the playing field
if (x == -1 || y == -1 || x == WIDTH || y == HEIGHT) {
- if (x == -1 || y == -1) {
- unsigned int screenX = x + 1;
- unsigned int screenY = y + 1;
- nib_put_scr(screenX, screenY, '#');
- } else {
- unsigned int screenX = x + 1;
- unsigned int screenY = y + 1;
- nib_put_scr(screenX, screenY, '#');
- }
+ unsigned int screenX = x + 1;
+ unsigned int screenY = y + 1;
+ nib_put_scr(screenX, screenY, '#');
} else {
int snakeVal = snake[x + y*WIDTH];
unsigned int screenX = x + 1;
unsigned int screenY = y + 1;
-
+
+ // If the `DEBUG` flag is set, draw the raw ASCII-value for debugging purposes
if (DEBUG) {
nib_put_scr(screenX, screenY, '0' + snakeVal);
continue;
}
+ // If the value is greater than `0` and less than `snakeLength`, it's part of the snake
if (snakeVal >= 0 && snakeVal < snakeLength) {
if (x == snakeX && y == snakeY) {
+ // Draw the head
nib_put_scr(screenX, screenY, '@');
} else {
+ // Draw the body
nib_put_scr(screenX, screenY, body[(snakeVal - 1) % 7]);
}
} else if (snakeVal < 0) {
+ // If the value is less than `0`, it's an apple
nib_put_scr(screenX, screenY, 'o');
} else {
+ // Otherwise it's empty.
nib_put_scr(screenX, screenY, ' ');
}
}
diff --git a/docco-languages.json b/docco-languages.json
new file mode 100644
index 0000000..324a2d9
--- /dev/null
+++ b/docco-languages.json
@@ -0,0 +1,4 @@
+{
+ ".S": {"name": "x86asm", "symbol": "//"},
+ ".c": {"name": "c", "symbol": "//"}
+}
diff --git a/docs/cnibbles.html b/docs/cnibbles.html
new file mode 100644
index 0000000..1411deb
--- /dev/null
+++ b/docs/cnibbles.html
@@ -0,0 +1,693 @@
+
+
+
+
+ cnibbles.c
+
+
+
+
+
+
+
+
+
+
+ Jump To …
+ +
+
+
+
+
+
+
+
+
+
+
cnibbles.c
+
+
+
+
+
+
+
+
+ #include <stdio.h>
+#include <stdbool.h>
+#include "helpers.c"
+
+#define WIDTH 60
+#define HEIGHT 40
+#define DEBUG false
+
+unsigned int startLength = 0 ;
+unsigned int startApples = 0 ;
+
+unsigned int currentTick;
+int snake[WIDTH*HEIGHT];
+unsigned int snakeLength;
+unsigned int snakeX;
+unsigned int snakeY;
+int snakeDirX;
+int snakeDirY;
+unsigned int numApples;
+
+void spawnApple () ;
+void tick () ;
+void draw () ;
+
+
+
+
+
+
+
+
+
+
+
+
+
+ void start_game (int len, int n_apples)
+ {
+ if (startLength == 0 )
+ startLength = len;
+ if (startApples == 0 )
+ startApples = n_apples;
+
+
+
+
+
+
+
+
+
Initialize variables
+
+
+
+ currentTick = 0 ;
+ snakeLength = len;
+ numApples = n_apples;
+
+ nib_init();
+
+
+
+
+
+
+
+
+
Initialize the snake array
+Set every cell to a time value greater than the snake length
+
+
+
+ for (unsigned int i = 0 ; i < WIDTH*HEIGHT; ++i) {
+ snake[i] = snakeLength;
+ }
+
+
+
+
+
+
+
+
+
Place the snake head in the middle
+
+
+
+ snakeX = WIDTH / 2 ;
+ snakeY = HEIGHT / 2 ;
+ snakeDirX = 0 ;
+ snakeDirY = -1 ;
+
+
+
+
+
+
+
+
+
Place the initial snake segments, downwards or something
+
+
+
+ for (unsigned int i = 0 ; i < snakeLength; ++i) {
+ unsigned int x = snakeX % WIDTH;
+ unsigned int y = (snakeY + i) % HEIGHT;
+ snake[x + y*WIDTH] = i;
+ }
+
+
+
+
+
+
+
+ srand(time(0 ));
+ for (unsigned int i = 0 ; i < numApples; ++i) {
+ spawnApple();
+ }
+
+ while (true ) {
+ tick();
+ draw();
+ }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ void spawnApple ()
+ {
+ unsigned int x = rand() % WIDTH;
+ unsigned int y = rand() % HEIGHT;
+ while (snake[x + y*WIDTH] < snakeLength) {
+ x = rand() % WIDTH;
+ y = rand() % HEIGHT;
+ }
+ snake[x + y*WIDTH] = -1 ;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ void tick ()
+ {
+ currentTick++;
+
+
+
+
+
+
+
+
+
Increment cell times
+
+
+
+
+
+
+
+
+
+ for (unsigned int i = 0 ; i < WIDTH*HEIGHT; ++i) {
+ int val = snake[i];
+
+ if (val < 0 ) {
+
+
+
+
+
+
+
+
+ } else if (val >= 0 && val <= snakeLength) {
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ int key = nib_poll_kbd();
+ if (key == 'w' && snakeDirY != 1 ) {
+ snakeDirX = 0 ;
+ snakeDirY = -1 ;
+ }
+ if (key == 's' && snakeDirY != -1 ) {
+ snakeDirX = 0 ;
+ snakeDirY = 1 ;
+ }
+ if (key == 'a' && snakeDirX != 1 ) {
+ snakeDirX = -1 ;
+ snakeDirY = 0 ;
+ }
+ if (key == 'd' && snakeDirX != -1 ) {
+ snakeDirX = 1 ;
+ snakeDirY = 0 ;
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+ snakeX = (snakeX + snakeDirX + WIDTH) % WIDTH;
+ snakeY = (snakeY + snakeDirY + HEIGHT) % HEIGHT;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
On collision with self
+
+
+
+ int val = snake[snakeX + snakeY*WIDTH];
+ if (val < snakeLength && val >= 0 ) {
+
+
+
+
+
+
+
+ start_game(startLength, startApples);
+ }
+
+
+
+
+
+
+
+
+
ON collision with apple
+
+
+
+
+
+
+
+
+
+
+
+
+
Spawn a new apple somewhere
+
+
+
+
+
+
+
+
+
+
+
+
+
Increase our length!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ snake[snakeX + snakeY*WIDTH] = 0 ;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ void draw ()
+ {
+ char body[] = "NIBBLES" ;
+
+ for (int y = -1 ; y <= HEIGHT; ++y) {
+ for (int x = -1 ; x <= WIDTH; ++x) {
+
+
+
+
+
+
+
+
+
Draw a border outside the playing field
+
+
+
+ if (x == -1 || y == -1 || x == WIDTH || y == HEIGHT) {
+ unsigned int screenX = x + 1 ;
+ unsigned int screenY = y + 1 ;
+ nib_put_scr(screenX, screenY, '#' );
+ } else {
+ int snakeVal = snake[x + y*WIDTH];
+ unsigned int screenX = x + 1 ;
+ unsigned int screenY = y + 1 ;
+
+
+
+
+
+
+
+
+
If the DEBUG flag is set, draw the raw ASCII-value for debugging purposes
+
+
+
+ if (DEBUG) {
+ nib_put_scr(screenX, screenY, '0' + snakeVal);
+ continue ;
+ }
+
+
+
+
+
+
+
+
+
If the value is greater than 0 and less than snakeLength, it’s part of the snake
+
+
+
+ if (snakeVal >= 0 && snakeVal < snakeLength) {
+ if (x == snakeX && y == snakeY) {
+
+
+
+
+
+
+
+ nib_put_scr(screenX, screenY, '@' );
+ } else {
+
+
+
+
+
+
+
+ nib_put_scr(screenX, screenY, body[(snakeVal - 1 ) % 7 ]);
+ }
+ } else if (snakeVal < 0) {
+
+
+
+
+
+
+
+
+
If the value is less than 0, it’s an apple
+
+
+
+ nib_put_scr(screenX, screenY, 'o' );
+ } else {
+
+
+
+
+
+
+
+
+
Otherwise it’s empty.
+
+
+
+ nib_put_scr(screenX, screenY, ' ' );
+ }
+ }
+ }
+ }
+}
+
+
+
+
+
+
+
diff --git a/docs/docco.css b/docs/docco.css
new file mode 100644
index 0000000..a2899ac
--- /dev/null
+++ b/docs/docco.css
@@ -0,0 +1,506 @@
+/*--------------------- Typography ----------------------------*/
+
+@font-face {
+ font-family: 'aller-light';
+ src: url('public/fonts/aller-light.eot');
+ src: url('public/fonts/aller-light.eot?#iefix') format('embedded-opentype'),
+ url('public/fonts/aller-light.woff') format('woff'),
+ url('public/fonts/aller-light.ttf') format('truetype');
+ font-weight: normal;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: 'aller-bold';
+ src: url('public/fonts/aller-bold.eot');
+ src: url('public/fonts/aller-bold.eot?#iefix') format('embedded-opentype'),
+ url('public/fonts/aller-bold.woff') format('woff'),
+ url('public/fonts/aller-bold.ttf') format('truetype');
+ font-weight: normal;
+ font-style: normal;
+}
+
+@font-face {
+ font-family: 'novecento-bold';
+ src: url('public/fonts/novecento-bold.eot');
+ src: url('public/fonts/novecento-bold.eot?#iefix') format('embedded-opentype'),
+ url('public/fonts/novecento-bold.woff') format('woff'),
+ url('public/fonts/novecento-bold.ttf') format('truetype');
+ font-weight: normal;
+ font-style: normal;
+}
+
+/*--------------------- Layout ----------------------------*/
+html { height: 100%; }
+body {
+ font-family: "aller-light";
+ font-size: 14px;
+ line-height: 18px;
+ color: #30404f;
+ margin: 0; padding: 0;
+ height:100%;
+}
+#container { min-height: 100%; }
+
+a {
+ color: #000;
+}
+
+b, strong {
+ font-weight: normal;
+ font-family: "aller-bold";
+}
+
+p {
+ margin: 15px 0 0px;
+}
+ .annotation ul, .annotation ol {
+ margin: 25px 0;
+ }
+ .annotation ul li, .annotation ol li {
+ font-size: 14px;
+ line-height: 18px;
+ margin: 10px 0;
+ }
+
+h1, h2, h3, h4, h5, h6 {
+ color: #112233;
+ line-height: 1em;
+ font-weight: normal;
+ font-family: "novecento-bold";
+ text-transform: uppercase;
+ margin: 30px 0 15px 0;
+}
+
+h1 {
+ margin-top: 40px;
+}
+
+hr {
+ border: 0;
+ background: 1px #ddd;
+ height: 1px;
+ margin: 20px 0;
+}
+
+pre, tt, code {
+ font-size: 12px; line-height: 16px;
+ font-family: Menlo, Monaco, Consolas, "Lucida Console", monospace;
+ margin: 0; padding: 0;
+}
+ .annotation pre {
+ display: block;
+ margin: 0;
+ padding: 7px 10px;
+ background: #fcfcfc;
+ -moz-box-shadow: inset 0 0 10px rgba(0,0,0,0.1);
+ -webkit-box-shadow: inset 0 0 10px rgba(0,0,0,0.1);
+ box-shadow: inset 0 0 10px rgba(0,0,0,0.1);
+ overflow-x: auto;
+ }
+ .annotation pre code {
+ border: 0;
+ padding: 0;
+ background: transparent;
+ }
+
+
+blockquote {
+ border-left: 5px solid #ccc;
+ margin: 0;
+ padding: 1px 0 1px 1em;
+}
+ .sections blockquote p {
+ font-family: Menlo, Consolas, Monaco, monospace;
+ font-size: 12px; line-height: 16px;
+ color: #999;
+ margin: 10px 0 0;
+ white-space: pre-wrap;
+ }
+
+ul.sections {
+ list-style: none;
+ padding:0 0 5px 0;;
+ margin:0;
+}
+
+/*
+ Force border-box so that % widths fit the parent
+ container without overlap because of margin/padding.
+
+ More Info : http://www.quirksmode.org/css/box.html
+*/
+ul.sections > li > div {
+ -moz-box-sizing: border-box; /* firefox */
+ -ms-box-sizing: border-box; /* ie */
+ -webkit-box-sizing: border-box; /* webkit */
+ -khtml-box-sizing: border-box; /* konqueror */
+ box-sizing: border-box; /* css3 */
+}
+
+
+/*---------------------- Jump Page -----------------------------*/
+#jump_to, #jump_page {
+ margin: 0;
+ background: white;
+ -webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777;
+ -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px;
+ font: 16px Arial;
+ cursor: pointer;
+ text-align: right;
+ list-style: none;
+}
+
+#jump_to a {
+ text-decoration: none;
+}
+
+#jump_to a.large {
+ display: none;
+}
+#jump_to a.small {
+ font-size: 22px;
+ font-weight: bold;
+ color: #676767;
+}
+
+#jump_to, #jump_wrapper {
+ position: fixed;
+ right: 0; top: 0;
+ padding: 10px 15px;
+ margin:0;
+}
+
+#jump_wrapper {
+ display: none;
+ padding:0;
+}
+
+#jump_to:hover #jump_wrapper {
+ display: block;
+}
+
+#jump_page {
+ padding: 5px 0 3px;
+ margin: 0 0 25px 25px;
+}
+
+#jump_page .source {
+ display: block;
+ padding: 15px;
+ text-decoration: none;
+ border-top: 1px solid #eee;
+}
+
+#jump_page .source:hover {
+ background: #f5f5ff;
+}
+
+#jump_page .source:first-child {
+}
+
+/*---------------------- Low resolutions (> 320px) ---------------------*/
+@media only screen and (min-width: 320px) {
+ .pilwrap { display: none; }
+
+ ul.sections > li > div {
+ display: block;
+ padding:5px 10px 0 10px;
+ }
+
+ ul.sections > li > div.annotation ul, ul.sections > li > div.annotation ol {
+ padding-left: 30px;
+ }
+
+ ul.sections > li > div.content {
+ overflow-x:auto;
+ -webkit-box-shadow: inset 0 0 5px #e5e5ee;
+ box-shadow: inset 0 0 5px #e5e5ee;
+ border: 1px solid #dedede;
+ margin:5px 10px 5px 10px;
+ padding-bottom: 5px;
+ }
+
+ ul.sections > li > div.annotation pre {
+ margin: 7px 0 7px;
+ padding-left: 15px;
+ }
+
+ ul.sections > li > div.annotation p tt, .annotation code {
+ background: #f8f8ff;
+ border: 1px solid #dedede;
+ font-size: 12px;
+ padding: 0 0.2em;
+ }
+}
+
+/*---------------------- (> 481px) ---------------------*/
+@media only screen and (min-width: 481px) {
+ #container {
+ position: relative;
+ }
+ body {
+ background-color: #F5F5FF;
+ font-size: 15px;
+ line-height: 21px;
+ }
+ pre, tt, code {
+ line-height: 18px;
+ }
+ p, ul, ol {
+ margin: 0 0 15px;
+ }
+
+
+ #jump_to {
+ padding: 5px 10px;
+ }
+ #jump_wrapper {
+ padding: 0;
+ }
+ #jump_to, #jump_page {
+ font: 10px Arial;
+ text-transform: uppercase;
+ }
+ #jump_page .source {
+ padding: 5px 10px;
+ }
+ #jump_to a.large {
+ display: inline-block;
+ }
+ #jump_to a.small {
+ display: none;
+ }
+
+
+
+ #background {
+ position: absolute;
+ top: 0; bottom: 0;
+ width: 350px;
+ background: #fff;
+ border-right: 1px solid #e5e5ee;
+ z-index: -1;
+ }
+
+ ul.sections > li > div.annotation ul, ul.sections > li > div.annotation ol {
+ padding-left: 40px;
+ }
+
+ ul.sections > li {
+ white-space: nowrap;
+ }
+
+ ul.sections > li > div {
+ display: inline-block;
+ }
+
+ ul.sections > li > div.annotation {
+ max-width: 350px;
+ min-width: 350px;
+ min-height: 5px;
+ padding: 13px;
+ overflow-x: hidden;
+ white-space: normal;
+ vertical-align: top;
+ text-align: left;
+ }
+ ul.sections > li > div.annotation pre {
+ margin: 15px 0 15px;
+ padding-left: 15px;
+ }
+
+ ul.sections > li > div.content {
+ padding: 13px;
+ vertical-align: top;
+ border: none;
+ -webkit-box-shadow: none;
+ box-shadow: none;
+ }
+
+ .pilwrap {
+ position: relative;
+ display: inline;
+ }
+
+ .pilcrow {
+ font: 12px Arial;
+ text-decoration: none;
+ color: #454545;
+ position: absolute;
+ top: 3px; left: -20px;
+ padding: 1px 2px;
+ opacity: 0;
+ -webkit-transition: opacity 0.2s linear;
+ }
+ .for-h1 .pilcrow {
+ top: 47px;
+ }
+ .for-h2 .pilcrow, .for-h3 .pilcrow, .for-h4 .pilcrow {
+ top: 35px;
+ }
+
+ ul.sections > li > div.annotation:hover .pilcrow {
+ opacity: 1;
+ }
+}
+
+/*---------------------- (> 1025px) ---------------------*/
+@media only screen and (min-width: 1025px) {
+
+ body {
+ font-size: 16px;
+ line-height: 24px;
+ }
+
+ #background {
+ width: 525px;
+ }
+ ul.sections > li > div.annotation {
+ max-width: 525px;
+ min-width: 525px;
+ padding: 10px 25px 1px 50px;
+ }
+ ul.sections > li > div.content {
+ padding: 9px 15px 16px 25px;
+ }
+}
+
+/*---------------------- Syntax Highlighting -----------------------------*/
+
+td.linenos { background-color: #f0f0f0; padding-right: 10px; }
+span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; }
+/*
+
+github.com style (c) Vasily Polovnyov
+
+*/
+
+pre code {
+ display: block; padding: 0.5em;
+ color: #000;
+ background: #f8f8ff
+}
+
+pre .hljs-comment,
+pre .hljs-template_comment,
+pre .hljs-diff .hljs-header,
+pre .hljs-javadoc {
+ color: #408080;
+ font-style: italic
+}
+
+pre .hljs-keyword,
+pre .hljs-assignment,
+pre .hljs-literal,
+pre .hljs-css .hljs-rule .hljs-keyword,
+pre .hljs-winutils,
+pre .hljs-javascript .hljs-title,
+pre .hljs-lisp .hljs-title,
+pre .hljs-subst {
+ color: #954121;
+ /*font-weight: bold*/
+}
+
+pre .hljs-number,
+pre .hljs-hexcolor {
+ color: #40a070
+}
+
+pre .hljs-string,
+pre .hljs-tag .hljs-value,
+pre .hljs-phpdoc,
+pre .hljs-tex .hljs-formula {
+ color: #219161;
+}
+
+pre .hljs-title,
+pre .hljs-id {
+ color: #19469D;
+}
+pre .hljs-params {
+ color: #00F;
+}
+
+pre .hljs-javascript .hljs-title,
+pre .hljs-lisp .hljs-title,
+pre .hljs-subst {
+ font-weight: normal
+}
+
+pre .hljs-class .hljs-title,
+pre .hljs-haskell .hljs-label,
+pre .hljs-tex .hljs-command {
+ color: #458;
+ font-weight: bold
+}
+
+pre .hljs-tag,
+pre .hljs-tag .hljs-title,
+pre .hljs-rules .hljs-property,
+pre .hljs-django .hljs-tag .hljs-keyword {
+ color: #000080;
+ font-weight: normal
+}
+
+pre .hljs-attribute,
+pre .hljs-variable,
+pre .hljs-instancevar,
+pre .hljs-lisp .hljs-body {
+ color: #008080
+}
+
+pre .hljs-regexp {
+ color: #B68
+}
+
+pre .hljs-class {
+ color: #458;
+ font-weight: bold
+}
+
+pre .hljs-symbol,
+pre .hljs-ruby .hljs-symbol .hljs-string,
+pre .hljs-ruby .hljs-symbol .hljs-keyword,
+pre .hljs-ruby .hljs-symbol .hljs-keymethods,
+pre .hljs-lisp .hljs-keyword,
+pre .hljs-tex .hljs-special,
+pre .hljs-input_number {
+ color: #990073
+}
+
+pre .hljs-builtin,
+pre .hljs-constructor,
+pre .hljs-built_in,
+pre .hljs-lisp .hljs-title {
+ color: #0086b3
+}
+
+pre .hljs-preprocessor,
+pre .hljs-pi,
+pre .hljs-doctype,
+pre .hljs-shebang,
+pre .hljs-cdata {
+ color: #999;
+ font-weight: bold
+}
+
+pre .hljs-deletion {
+ background: #fdd
+}
+
+pre .hljs-addition {
+ background: #dfd
+}
+
+pre .hljs-diff .hljs-change {
+ background: #0086b3
+}
+
+pre .hljs-chunk {
+ color: #aaa
+}
+
+pre .hljs-tex .hljs-formula {
+ opacity: 0.5;
+}
diff --git a/docs/nibbles.html b/docs/nibbles.html
new file mode 100644
index 0000000..9e01661
--- /dev/null
+++ b/docs/nibbles.html
@@ -0,0 +1,1966 @@
+
+
+
+
+ nibbles.S
+
+
+
+
+
+
+
+
+
+
+ Jump To …
+ +
+
+
+
+
+
+
+
+
+
+
nibbles.S
+
+
+
+
+
+
+
+
+
+
by Simon Holmberg
+
This is a document generated by docco .
+My report is interleaved with the source code in the form of comments in
+markdown format.
+
+
+
+ #define WIDTH 60
+#define HEIGHT 40
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Array of longs representing the game field
+
world is an array of increasing time values. By having the snake head set
+the time value to 0 at its current position, the actual snake will be
+defined by the locations where the time value is lesser than the snake
+length. This makes the tail of the snake automatically “move” visually, and
+allows for a snake with an arbitrary length without complicated arrays of
+coordinates!
+
Apples are defined by negative time values which do not get incremented
+each frame. If we wanted to, we could instead decrease this value and
+compare it with a lifetime value, to conveniently make apples disappear
+after a set amount of time.
+
+
+
+ .align 4
+world: .space WIDTH*HEIGHT*4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Current length of the snake
+
+
+
+
+
+
+
+
+
+
+
+
+
Snake position (starts in the middle of the field)
+
+
+
+ snakePosX: .long WIDTH / 2
+snakePosY: .long HEIGHT / 2
+
+
+
+
+
+
+
+
+
Snake direction (start moving to the right)
+
+
+
+ snakeDirX: .long 1
+snakeDirY: .long 0
+
+
+
+
+
+
+
+
+
Number of apples
+
+
+
+
+
+
+
+
+
+
+
+
+
Game over message
+
+
+
+ sgameover: .string " Game over! Score: %i "
+sgameover_end:
+
+
+
+
+
+
+
+
+
Various utility strings mainly used for debugging
+
+
+
+ string1: .string "Started with snake length %i and %i apples!\n"
+string2: .string "Snake position: (%i, %i)\n"
+stringi: .string "%i\n"
+stringtest: .string "> %i %i %c\n"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
start_game(int len, int n_apples)
+
+
+
+ .global start_game
+start_game:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Save our function arguments for later
+
+
+
+
+
+
+
+
+
+
+
snakeLength = len
+
+
+
+ movl 4 (%esp ), %eax
+ movl %eax , snakeLength
+
+
+
+
+
+
+
+
+
numApples = n_apples
+
+
+
+ movl 8 (%esp ), %eax
+ movl %eax , numApples
+
+
+
+
+
+
+
+
+
Store all registers so we can restore their state before we return later
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Seed randomness by calling srand with a time value from time
+
+
+
+ pushl $0
+ call time
+ addl $4 , %esp
+ pushl %eax
+ call srand
+ addl $4 , %esp
+
+
+
+
+
+
+
+
+
Initialize the snake array by setting every cell to a time value greater
+than snakeLength
+
+
+
+ movl $world, %edi
+ movl snakeLength, %eax
+ movl $WIDTH*HEIGHT, %ebx
+ xorl %ecx , %ecx
+1 :
+ movl %eax , (%edi , %ecx , 4 )
+ incl %ecx
+ cmpl %ebx , %ecx
+ jl 1b
+
+
+
+
+
+
+
+
+
Spawn initial apples
+
+
+
+ movl numApples, %ecx
+1 :
+ pushl %ecx
+ call spawnApple
+ popl %ecx
+ loop 1b
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Sleep for a while using usleep so the snake doesn’t wiggle at the
+speed of light!
+
+
+
+ pushl $250 *1000
+ call usleep
+ addl $4 , %esp
+
+
+
+
+
+
+
+
+
Jump to tick, our updating function, which later chain-jumps to draw
+
+
+
+
+
+
+
+
+
+
+
+
+
Restore all our registers as they were before start_game was called
+and return
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
This is where we update the world.
+
+
+
+ tick:
+ movl $world, %edi
+
+
+
+
+
+
+
+
+
Increment cell times
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ movl snakeLength, %ebx
+ xorl %ecx , %ecx
+1 :
+ movl (%edi , %ecx , 4 ), %eax
+
+
+
+
+
+
+
+
+
If the value is negative it’s an apple we don’t want to disappear
+
+
+
+
+
+
+
+
+
+
+
+
+
No need to increment if it’s already greater than snakeLength
+
+
+
+
+
+
+
+
+
+
+
+
+
Increment the cell value
+
+
+
+ incl %eax
+ movl %eax , (%edi , %ecx , 4 )
+2 : incl %ecx // Loop increment
+ cmpl $WIDTH*HEIGHT, %ecx
+ jne 1b
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Call nib_poll_kbd and check down, up, left and right in succession
+
+
+
+
+
+
+
+
+
+
+
+ movl $258 , %ebx
+ cmpl %eax , %ebx
+ jne 1f
+ movl $0 , snakeDirX
+ movl $1 , snakeDirY
+
+
+
+
+
+
+
+ 1 : movl $259 , %ebx
+ cmpl %eax , %ebx
+ jne 1f
+ movl $0 , snakeDirX
+ movl $-1 , snakeDirY
+
+
+
+
+
+
+
+ 1 : movl $260 , %ebx
+ cmpl %eax , %ebx
+ jne 1f
+ movl $-1 , snakeDirX
+ movl $0 , snakeDirY
+
+
+
+
+
+
+
+ 1 : movl $261 , %ebx
+ cmpl %eax , %ebx
+ jne 1f
+ movl $1 , snakeDirX
+ movl $0 , snakeDirY
+1 :
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
snakePosX = (snakePosX + snakeDirY + WIDTH) % WIDTH
+
+
+
+ movl snakePosX, %eax
+ movl snakeDirX, %ebx
+ addl %ebx , %eax
+
+
+
+
+
+
+
+
+
Add width for modulo wrap-around
+
+
+
+ addl $WIDTH, %eax
+ xorl %edx , %edx
+ movl $WIDTH, %ecx
+ divl %ecx
+ mov %edx , snakePosX
+
+
+
+
+
+
+
+
+
snakePosY = (snakePosY + snakeDirY + HEIGHT) % HEIGHT
+
+
+
+ movl snakePosY, %eax
+ movl snakeDirY, %ebx
+ addl %ebx , %eax
+
+
+
+
+
+
+
+
+
Add height for modulo wrap-around
+
+
+
+ addl $HEIGHT, %eax
+ xorl %edx , %edx
+ movl $HEIGHT, %ecx
+ divl %ecx
+ mov %edx , snakePosY
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Get value at head position
+
+
+
+ movl snakePosY, %eax
+ movl $WIDTH, %edx
+ mull %edx
+ addl snakePosX, %eax
+ movl (%edi , %eax , 4 ), %edx
+
+
+
+
+
+
+
+
+
Collision with apple if negative
+
+
+
+
+
+
+
+
+
+
+
+
+
Compare value with snakeLength
+
+
+
+ movl snakeLength, %ebx
+ cmpl %ebx , %edx
+
+
+
+
+
+
+
+
+
End the game if the snake collides with itself, i.e. %edx < snakeLength
+
+
+
+
+
+
+
+
+
+
+
+
+
Spawn a new apple and extend the snake!
+
+
+
+ 1 : pushl %edi
+ pushl %eax
+ call spawnApple
+ popl %eax
+ popl %edi
+ incl snakeLength
+2 :
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Set time value to 0 at snake head position
+
i = x + y * WIDTH
+
%edi = %ebx + %ecx * $WIDTH
+
+
+
+
+
+
+
+
+
+
+
+
+
Proceed to draw
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Save loop registers
+
+
+
+ pushl %ecx
+ pushl %ebx
+ subl $1 , %ecx
+ subl $1 , %ebx
+
+
+
+
+
+
+
+
+
Call nib_put_scr depending of the value in a cell
+
+
+
+
+
+
+
+
+
+
+
i = x + y * WIDTH
+
%eax = %ebx + %ecx * $WIDTH
+
+
+
+ movl %ecx , %eax
+ movl $WIDTH, %edx
+ mull %edx
+ addl %ebx , %eax
+
+
+
+
+
+
+
+
+
Fetch the value of the cell
+
+
+
+ movl $world, %edi
+ movl (%edi , %eax , 4 ), %eax
+
+
+
+
+
+
+
+
+
Compare with the snake length
+
+
+
+
+
+
+
+
+
+
+
+
+
If the value is negative, it’s an apple
+
+
+
+
+
+
+
+
+
+
+
+
+
If it’s greater or equal to snakeLength, it’s empty
+
+
+
+ cmpl snakeLength, %eax
+ jge 3f
+
+
+
+
+
+
+
+
+
Else, it’s part of the snake
+
+
+
+ pushl $'@'
+ jmp 5f
+3 : pushl $' '
+ jmp 5f
+4 : pushl $'o'
+5 :
+
+
+
+
+
+
+
+
+
Push Y-coordinate
+
+
+
+ movl %ecx , %eax
+ pushl %eax
+
+
+
+
+
+
+
+
+
Push X-coordinate
+
+
+
+ movl %ebx , %eax
+ pushl %eax
+
+
+
+
+
+
+
+
+
nib_put_scr(int x, int y, int c)
+
+
+
+ call nib_put_scr
+ addl $4 *3 , %esp
+
+
+
+
+
+
+
+
+
Restore loop registers
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Jump back to the start of our game loop!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Called when the game ends. It displays a game over message
+and score, and quits after 3 seconds .
+
+
+
+
+
+
+
+
+
+
+
+
+
Calculate message position
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Calculate string length
+
+
+
+ movl $sgameover_end, %eax
+ subl $sgameover, %eax
+
+
+
+
+
+
+
+
+
Discout null terminator
+
+
+
+
+
+
+
+
+
+
+
+
+
Calculate X-pos in the middle of the screen
+
+
+
+ movl $2 , %ebx
+ xorl %edx , %edx
+ divl %ebx
+ negl %eax
+ addl $WIDTH/2 , %eax
+
+
+
+
+
+
+
+
+
Print game over message
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Push snakeLength as score
+
+
+
+
+
+
+
+
+
+
+
+
+
Push printf-string
+
+
+
+
+
+
+
+
+
+
+
+
+
Push X-coordinate
+
+
+
+
+
+
+
+
+
+
+
+
+
Push Y-coordinate
+
+
+
+
+
+
+
+
+
+
+
+
+
mvprintw(int y, int x, const char *fmt, ...)
+
+
+
+ call mvprintw
+ add $4 *3 , %esp
+
+
+
+
+
+
+
+
+
Call refresh to refresh the screen
+
+
+
+
+
+
+
+
+
+
+
+
+
Sleep for 3 seconds before calling nib_end
+
+
+
+ pushl $3000 *1000
+ call usleep
+ addl $4 , %esp
+ call nib_end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Spawns an apple in a random location in the world.
+
+
+
+
+
+
+
+
+
+
+
+
+
%ecx = rand() % HEIGHT
+
+
+
+ call rand
+ xorl %edx , %edx
+ movl $HEIGHT, %ebx
+ divl %ebx
+ movl %edx , %ecx
+
+
+
+
+
+
+
+
+
%ebx = rand() % WIDTH
+
+
+
+ pushl %ecx
+ call rand
+ xorl %edx , %edx
+ movl $WIDTH, %ebx
+ divl %ebx
+ movl %edx , %ebx
+ popl %ecx
+
+
+
+
+
+
+
+
+
i = x + y * WIDTH
+
%eax = %ebx + %ecx * $WIDTH
+
+
+
+ movl %ecx , %eax
+ movl $WIDTH, %edx
+ mull %edx
+ addl %ebx , %eax
+
+
+
+
+
+
+
+
+
Set the cell negative to represent an apple
+
+
+
+ movl $world, %ecx
+ movl $-1 , %edx
+ movl %edx , (%ecx , %eax , 4 )
+
+ popl %ebx
+ ret
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
This is basically the same as draw, except it draws the actual ASCII-value
+of the world for debugging purposes.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Save loop registers
+
+
+
+ pushl %ecx
+ pushl %ebx
+ subl $1 , %ecx
+ subl $1 , %ebx
+
+
+
+
+
+
+
+
+
Call nib_put_scr
+
+
+
+
+
+
+
+
+
+
+
i = x + y * WIDTH
+
%edi = %ebx + %ecx * $WIDTH
+
+
+
+ movl %ecx , %eax
+ movl $WIDTH, %edx
+ mull %edx
+ addl %ebx , %eax
+
+
+
+
+
+
+
+
+
Fetch the value of the cell
+
+
+
+ movl $world, %edi
+ movl (%edi , %eax , 4 ), %eax
+ pushl %eax
+
+
+
+
+
+
+
+ movl %ecx , %eax
+ pushl %eax
+
+
+
+
+
+
+
+ movl %ebx , %eax
+ pushl %eax
+
+ call nib_put_scr
+ addl $4 *3 , %esp
+
+
+
+
+
+
+
+
+
Restore loop registers
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Jump back to the start of our game loop!
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/public/fonts/aller-bold.eot b/docs/public/fonts/aller-bold.eot
new file mode 100644
index 0000000..1b32532
Binary files /dev/null and b/docs/public/fonts/aller-bold.eot differ
diff --git a/docs/public/fonts/aller-bold.ttf b/docs/public/fonts/aller-bold.ttf
new file mode 100644
index 0000000..dc4cc9c
Binary files /dev/null and b/docs/public/fonts/aller-bold.ttf differ
diff --git a/docs/public/fonts/aller-bold.woff b/docs/public/fonts/aller-bold.woff
new file mode 100644
index 0000000..fa16fd0
Binary files /dev/null and b/docs/public/fonts/aller-bold.woff differ
diff --git a/docs/public/fonts/aller-light.eot b/docs/public/fonts/aller-light.eot
new file mode 100644
index 0000000..40bd654
Binary files /dev/null and b/docs/public/fonts/aller-light.eot differ
diff --git a/docs/public/fonts/aller-light.ttf b/docs/public/fonts/aller-light.ttf
new file mode 100644
index 0000000..c2c7290
Binary files /dev/null and b/docs/public/fonts/aller-light.ttf differ
diff --git a/docs/public/fonts/aller-light.woff b/docs/public/fonts/aller-light.woff
new file mode 100644
index 0000000..81a09d1
Binary files /dev/null and b/docs/public/fonts/aller-light.woff differ
diff --git a/docs/public/fonts/novecento-bold.eot b/docs/public/fonts/novecento-bold.eot
new file mode 100644
index 0000000..98a9a7f
Binary files /dev/null and b/docs/public/fonts/novecento-bold.eot differ
diff --git a/docs/public/fonts/novecento-bold.ttf b/docs/public/fonts/novecento-bold.ttf
new file mode 100644
index 0000000..2af39b0
Binary files /dev/null and b/docs/public/fonts/novecento-bold.ttf differ
diff --git a/docs/public/fonts/novecento-bold.woff b/docs/public/fonts/novecento-bold.woff
new file mode 100644
index 0000000..de558b5
Binary files /dev/null and b/docs/public/fonts/novecento-bold.woff differ
diff --git a/docs/public/stylesheets/normalize.css b/docs/public/stylesheets/normalize.css
new file mode 100644
index 0000000..73abb76
--- /dev/null
+++ b/docs/public/stylesheets/normalize.css
@@ -0,0 +1,375 @@
+/*! normalize.css v2.0.1 | MIT License | git.io/normalize */
+
+/* ==========================================================================
+ HTML5 display definitions
+ ========================================================================== */
+
+/*
+ * Corrects `block` display not defined in IE 8/9.
+ */
+
+article,
+aside,
+details,
+figcaption,
+figure,
+footer,
+header,
+hgroup,
+nav,
+section,
+summary {
+ display: block;
+}
+
+/*
+ * Corrects `inline-block` display not defined in IE 8/9.
+ */
+
+audio,
+canvas,
+video {
+ display: inline-block;
+}
+
+/*
+ * Prevents modern browsers from displaying `audio` without controls.
+ * Remove excess height in iOS 5 devices.
+ */
+
+audio:not([controls]) {
+ display: none;
+ height: 0;
+}
+
+/*
+ * Addresses styling for `hidden` attribute not present in IE 8/9.
+ */
+
+[hidden] {
+ display: none;
+}
+
+/* ==========================================================================
+ Base
+ ========================================================================== */
+
+/*
+ * 1. Sets default font family to sans-serif.
+ * 2. Prevents iOS text size adjust after orientation change, without disabling
+ * user zoom.
+ */
+
+html {
+ font-family: sans-serif; /* 1 */
+ -webkit-text-size-adjust: 100%; /* 2 */
+ -ms-text-size-adjust: 100%; /* 2 */
+}
+
+/*
+ * Removes default margin.
+ */
+
+body {
+ margin: 0;
+}
+
+/* ==========================================================================
+ Links
+ ========================================================================== */
+
+/*
+ * Addresses `outline` inconsistency between Chrome and other browsers.
+ */
+
+a:focus {
+ outline: thin dotted;
+}
+
+/*
+ * Improves readability when focused and also mouse hovered in all browsers.
+ */
+
+a:active,
+a:hover {
+ outline: 0;
+}
+
+/* ==========================================================================
+ Typography
+ ========================================================================== */
+
+/*
+ * Addresses `h1` font sizes within `section` and `article` in Firefox 4+,
+ * Safari 5, and Chrome.
+ */
+
+h1 {
+ font-size: 2em;
+}
+
+/*
+ * Addresses styling not present in IE 8/9, Safari 5, and Chrome.
+ */
+
+abbr[title] {
+ border-bottom: 1px dotted;
+}
+
+/*
+ * Addresses style set to `bolder` in Firefox 4+, Safari 5, and Chrome.
+ */
+
+b,
+strong {
+ font-weight: bold;
+}
+
+/*
+ * Addresses styling not present in Safari 5 and Chrome.
+ */
+
+dfn {
+ font-style: italic;
+}
+
+/*
+ * Addresses styling not present in IE 8/9.
+ */
+
+mark {
+ background: #ff0;
+ color: #000;
+}
+
+
+/*
+ * Corrects font family set oddly in Safari 5 and Chrome.
+ */
+
+code,
+kbd,
+pre,
+samp {
+ font-family: monospace, serif;
+ font-size: 1em;
+}
+
+/*
+ * Improves readability of pre-formatted text in all browsers.
+ */
+
+pre {
+ white-space: pre;
+ white-space: pre-wrap;
+ word-wrap: break-word;
+}
+
+/*
+ * Sets consistent quote types.
+ */
+
+q {
+ quotes: "\201C" "\201D" "\2018" "\2019";
+}
+
+/*
+ * Addresses inconsistent and variable font size in all browsers.
+ */
+
+small {
+ font-size: 80%;
+}
+
+/*
+ * Prevents `sub` and `sup` affecting `line-height` in all browsers.
+ */
+
+sub,
+sup {
+ font-size: 75%;
+ line-height: 0;
+ position: relative;
+ vertical-align: baseline;
+}
+
+sup {
+ top: -0.5em;
+}
+
+sub {
+ bottom: -0.25em;
+}
+
+/* ==========================================================================
+ Embedded content
+ ========================================================================== */
+
+/*
+ * Removes border when inside `a` element in IE 8/9.
+ */
+
+img {
+ border: 0;
+}
+
+/*
+ * Corrects overflow displayed oddly in IE 9.
+ */
+
+svg:not(:root) {
+ overflow: hidden;
+}
+
+/* ==========================================================================
+ Figures
+ ========================================================================== */
+
+/*
+ * Addresses margin not present in IE 8/9 and Safari 5.
+ */
+
+figure {
+ margin: 0;
+}
+
+/* ==========================================================================
+ Forms
+ ========================================================================== */
+
+/*
+ * Define consistent border, margin, and padding.
+ */
+
+fieldset {
+ border: 1px solid #c0c0c0;
+ margin: 0 2px;
+ padding: 0.35em 0.625em 0.75em;
+}
+
+/*
+ * 1. Corrects color not being inherited in IE 8/9.
+ * 2. Remove padding so people aren't caught out if they zero out fieldsets.
+ */
+
+legend {
+ border: 0; /* 1 */
+ padding: 0; /* 2 */
+}
+
+/*
+ * 1. Corrects font family not being inherited in all browsers.
+ * 2. Corrects font size not being inherited in all browsers.
+ * 3. Addresses margins set differently in Firefox 4+, Safari 5, and Chrome
+ */
+
+button,
+input,
+select,
+textarea {
+ font-family: inherit; /* 1 */
+ font-size: 100%; /* 2 */
+ margin: 0; /* 3 */
+}
+
+/*
+ * Addresses Firefox 4+ setting `line-height` on `input` using `!important` in
+ * the UA stylesheet.
+ */
+
+button,
+input {
+ line-height: normal;
+}
+
+/*
+ * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
+ * and `video` controls.
+ * 2. Corrects inability to style clickable `input` types in iOS.
+ * 3. Improves usability and consistency of cursor style between image-type
+ * `input` and others.
+ */
+
+button,
+html input[type="button"], /* 1 */
+input[type="reset"],
+input[type="submit"] {
+ -webkit-appearance: button; /* 2 */
+ cursor: pointer; /* 3 */
+}
+
+/*
+ * Re-set default cursor for disabled elements.
+ */
+
+button[disabled],
+input[disabled] {
+ cursor: default;
+}
+
+/*
+ * 1. Addresses box sizing set to `content-box` in IE 8/9.
+ * 2. Removes excess padding in IE 8/9.
+ */
+
+input[type="checkbox"],
+input[type="radio"] {
+ box-sizing: border-box; /* 1 */
+ padding: 0; /* 2 */
+}
+
+/*
+ * 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome.
+ * 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome
+ * (include `-moz` to future-proof).
+ */
+
+input[type="search"] {
+ -webkit-appearance: textfield; /* 1 */
+ -moz-box-sizing: content-box;
+ -webkit-box-sizing: content-box; /* 2 */
+ box-sizing: content-box;
+}
+
+/*
+ * Removes inner padding and search cancel button in Safari 5 and Chrome
+ * on OS X.
+ */
+
+input[type="search"]::-webkit-search-cancel-button,
+input[type="search"]::-webkit-search-decoration {
+ -webkit-appearance: none;
+}
+
+/*
+ * Removes inner padding and border in Firefox 4+.
+ */
+
+button::-moz-focus-inner,
+input::-moz-focus-inner {
+ border: 0;
+ padding: 0;
+}
+
+/*
+ * 1. Removes default vertical scrollbar in IE 8/9.
+ * 2. Improves readability and alignment in all browsers.
+ */
+
+textarea {
+ overflow: auto; /* 1 */
+ vertical-align: top; /* 2 */
+}
+
+/* ==========================================================================
+ Tables
+ ========================================================================== */
+
+/*
+ * Remove most spacing between table cells.
+ */
+
+table {
+ border-collapse: collapse;
+ border-spacing: 0;
+}
\ No newline at end of file
diff --git a/docs/start.html b/docs/start.html
new file mode 100644
index 0000000..c922baf
--- /dev/null
+++ b/docs/start.html
@@ -0,0 +1,157 @@
+
+
+
+
+ start.S
+
+
+
+
+
+
+
+
+
+
+ Jump To …
+ +
+
+
+
+
+
+
+
+
+
+
start.S
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
The _start symbol. Calls start_game with “reasonable parameters”.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
start_game(int len, int n_apples)
+
+
+
+ call start_game
+ addl $4 *2 , %esp
+
+
+
+
+
+
+
+
+
Exit cleanly with an exit code of 0
+
+
+
+
+
+
+
+
+
+
+
diff --git a/nibbles.S b/nibbles.S
index 4e41431..75ab419 100644
--- a/nibbles.S
+++ b/nibbles.S
@@ -1,50 +1,80 @@
+// by Simon Holmberg
+//
+// This is a document generated by [docco](http://jashkenas.github.io/docco/).
+// My report is interleaved with the source code in the form of comments in
+// [markdown](http://daringfireball.net/projects/markdown/) format.
+//
+//
#define WIDTH 60
#define HEIGHT 40
+// .bss
+// ====
.bss
// Array of longs representing the game field
+//
+// `world` is an array of increasing time values. By having the snake head set
+// the time value to 0 at its current position, the actual **snake** will be
+// defined by the locations where the time value is lesser than the snake
+// length. This makes the tail of the snake automatically "move" visually, and
+// allows for a snake with an arbitrary length without complicated arrays of
+// coordinates!
+//
+// **Apples** are defined by negative time values which do not get incremented
+// each frame. If we wanted to, we could instead decrease this value and
+// compare it with a lifetime value, to conveniently make apples disappear
+// after a set amount of time.
.align 4
world: .space WIDTH*HEIGHT*4
+// .data
+// =====
.data
// Current length of the snake
snakeLength: .long 0
-// Snake position
+// Snake position (starts in the middle of the field)
snakePosX: .long WIDTH / 2
snakePosY: .long HEIGHT / 2
-// Snake direction
+// Snake direction (start moving to the right)
snakeDirX: .long 1
snakeDirY: .long 0
// Number of apples
numApples: .long 0
-// Optional score-keeping
-score: .long 0
-// Utility strings
-snibbles: .ascii "NIBBLES"
-snibbles_end:
+// Game over message
sgameover: .string " Game over! Score: %i "
sgameover_end:
+// Various utility strings mainly used for debugging
string1: .string "Started with snake length %i and %i apples!\n"
string2: .string "Snake position: (%i, %i)\n"
stringi: .string "%i\n"
stringtest: .string "> %i %i %c\n"
+// .text
+// =====
.text
+// start_game
+// ============
+// `start_game(int len, int n_apples)`
.global start_game
start_game:
- // snakeLength = len
+ // Initialization
+ // --------------
+
+ // Save our function arguments for later
+
+ // `snakeLength = len`
movl 4(%esp), %eax
movl %eax, snakeLength
- // numApples = n_apples
+ // `numApples = n_apples`
movl 8(%esp), %eax
movl %eax, numApples
- pushl %ebx
- pushl %edi
-
+ // Store all registers so we can restore their state before we return later
+ pusha
+
// Call nib_init
call nib_init
- // Seed randomness
+ // Seed randomness by calling `srand` with a time value from `time`
pushl $0
call time
addl $4, %esp
@@ -52,22 +82,8 @@ start_game:
call srand
addl $4, %esp
- // Print our arguments
- //pushl numApples
- //pushl snakeLength
- //pushl $string1
- //call printf
- //addl $4*3, %esp
-
- // Print worm position
- //pushl snakePosY
- //pushl snakePosX
- //pushl $string2
- //call printf
- //addl $4*3, %esp
-
- // Initialize the snake array
- // Set every cell to a time value greater than the snake length
+ // Initialize the snake array by setting every cell to a time value greater
+ // than `snakeLength`
movl $world, %edi
movl snakeLength, %eax
movl $WIDTH*HEIGHT, %ebx
@@ -86,25 +102,33 @@ start_game:
popl %ecx
loop 1b
- // Loop forever
+ // Game loop
+ // ---------
loop:
- // Sleep for a while
+ // Sleep for a while using `usleep` so the snake doesn't wiggle at the
+ // speed of light!
pushl $250*1000
- pushl %ecx
call usleep
addl $4, %esp
+ // Jump to `tick`, our updating function, which later chain-jumps to `draw`
jmp tick
- popl %edi
- popl %ebx
+ // Restore all our registers as they were before `start_game` was called
+ // and return
+ popa
ret
-// Update stuff
+// tick
+// ====
+// This is where we update the world.
tick:
movl $world, %edi
// Increment cell times
+ // --------------------
+
+ // Loop
movl snakeLength, %ebx
xorl %ecx, %ecx
1:
@@ -123,6 +147,9 @@ tick:
jne 1b
// Input
+ // -----
+
+ // Call nib_poll_kbd and check down, up, left and right in succession
call nib_poll_kbd
// Down
movl $258, %ebx
@@ -130,20 +157,20 @@ tick:
jne 1f
movl $0, snakeDirX
movl $1, snakeDirY
-1: // Up
- movl $259, %ebx
+ // Up
+1: movl $259, %ebx
cmpl %eax, %ebx
jne 1f
movl $0, snakeDirX
movl $-1, snakeDirY
-1: // Left
- movl $260, %ebx
+ // Left
+1: movl $260, %ebx
cmpl %eax, %ebx
jne 1f
movl $-1, snakeDirX
movl $0, snakeDirY
-1: // Right
- movl $261, %ebx
+ // Right
+1: movl $261, %ebx
cmpl %eax, %ebx
jne 1f
movl $1, snakeDirX
@@ -151,25 +178,32 @@ tick:
1:
// Move snake
- // snakePosX = (snakePosX + snakeDirY + WIDTH) % WIDTH
+ // ----------
+
+ // `snakePosX = (snakePosX + snakeDirY + WIDTH) % WIDTH`
movl snakePosX, %eax
movl snakeDirX, %ebx
addl %ebx, %eax
- addl $WIDTH, %eax // For modulo wrap-around
+ // Add width for modulo wrap-around
+ addl $WIDTH, %eax
xorl %edx, %edx
movl $WIDTH, %ecx
divl %ecx
mov %edx, snakePosX
- // snakePosY = (snakePosY + snakeDirY + HEIGHT) % HEIGHT
+ // `snakePosY = (snakePosY + snakeDirY + HEIGHT) % HEIGHT`
movl snakePosY, %eax
movl snakeDirY, %ebx
addl %ebx, %eax
- addl $HEIGHT, %eax // For modulo wrap-around
+ // Add height for modulo wrap-around
+ addl $HEIGHT, %eax
xorl %edx, %edx
movl $HEIGHT, %ecx
divl %ecx
mov %edx, snakePosY
+ // Collision
+ // ---------
+
// Get value at head position
movl snakePosY, %eax
movl $WIDTH, %edx
@@ -180,36 +214,42 @@ tick:
// Collision with apple if negative
test %edx, %edx
js 1f
- // No need to increment if it's already greater than snakeLength
+ // Compare value with `snakeLength`
movl snakeLength, %ebx
cmpl %ebx, %edx
- jg 2f
- // End the game when the snake collides with itself
+ // End the game if the snake collides with itself, i.e. `%edx < snakeLength`
+ jge 2f
jmp gameOver
-1: // Spawn a new apple
- pushl %edi
+ // Spawn a new apple and extend the snake!
+1: pushl %edi
pushl %eax
call spawnApple
popl %eax
popl %edi
- // Extend the snake!
incl snakeLength
2:
+ // Update world
+ // ------------
+
// Set time value to 0 at snake head position
- // i = x + y * WIDTH
- // %edi = %ebx + %ecx * $WIDTH
+ //
+ // `i = x + y * WIDTH`
+ //
+ // `%edi = %ebx + %ecx * $WIDTH`
movl $0, (%edi, %eax, 4)
+ // Proceed to `draw`
jmp draw
//jmp debug_draw
-// Draw stuff
+// draw
+// ====
draw:
- // Loop Y
+ // Loop on Y-axis
movl $HEIGHT, %ecx
1:
- // Loop X
+ // Loop on X-axis
movl $WIDTH, %ebx
2:
// Save loop registers
@@ -218,10 +258,11 @@ draw:
subl $1, %ecx
subl $1, %ebx
- // Call nib_put_scr
+ // Call `nib_put_scr` depending of the value in a cell
- // i = x + y * WIDTH
- // %eax = %ebx + %ecx * $WIDTH
+ // `i = x + y * WIDTH`
+ //
+ // `%eax = %ebx + %ecx * $WIDTH`
movl %ecx, %eax
movl $WIDTH, %edx
mull %edx
@@ -246,12 +287,15 @@ draw:
4: pushl $'o'
5:
- movl %ecx, %eax // Y
+ // Push Y-coordinate
+ movl %ecx, %eax
pushl %eax
- movl %ebx, %eax // X
+ // Push X-coordinate
+ movl %ebx, %eax
pushl %eax
+ // `nib_put_scr(int x, int y, int c)`
call nib_put_scr
addl $4*3, %esp
@@ -259,13 +303,101 @@ draw:
popl %ebx
popl %ecx
- decl %ebx // Decrement X
- jnz 2b // End of X loop
- loop 1b // End of Y loop
+ // Decrement X
+ decl %ebx
+ // End of X-loop
+ jnz 2b
+ // End of Y-loop
+ loop 1b
+ // Jump back to the start of our game loop!
jmp loop
-// Debug drawing
+// gameOver
+// ========
+// Called when the game ends. It displays a game over message
+// and score, and quits after **3 seconds**.
+gameOver:
+ // Calculate message position
+ // --------------------------
+
+ // Calculate string length
+ movl $sgameover_end, %eax
+ subl $sgameover, %eax
+ // Discout null terminator
+ subl $1, %eax
+
+ // Calculate X-pos in the middle of the screen
+ movl $2, %ebx
+ xorl %edx, %edx
+ divl %ebx
+ negl %eax
+ addl $WIDTH/2, %eax
+
+ // Print game over message
+ // -----------------------
+
+ // Push `snakeLength` as score
+ pushl snakeLength
+ // Push printf-string
+ pushl $sgameover
+ // Push X-coordinate
+ pushl %eax
+ // Push Y-coordinate
+ pushl $HEIGHT/2
+ // `mvprintw(int y, int x, const char *fmt, ...)`
+ call mvprintw
+ add $4*3, %esp
+ // Call `refresh` to refresh the screen
+ call refresh
+
+ // Sleep for 3 seconds before calling `nib_end`
+ pushl $3000*1000
+ call usleep
+ addl $4, %esp
+ call nib_end
+
+// spawnApple
+// ==========
+// Spawns an **apple** in a random location in the `world`.
+spawnApple:
+ pushl %ebx
+
+ // `%ecx = rand() % HEIGHT`
+ call rand
+ xorl %edx, %edx
+ movl $HEIGHT, %ebx
+ divl %ebx
+ movl %edx, %ecx
+ // `%ebx = rand() % WIDTH`
+ pushl %ecx
+ call rand
+ xorl %edx, %edx
+ movl $WIDTH, %ebx
+ divl %ebx
+ movl %edx, %ebx
+ popl %ecx
+
+ // `i = x + y * WIDTH`
+ //
+ // `%eax = %ebx + %ecx * $WIDTH`
+ movl %ecx, %eax
+ movl $WIDTH, %edx
+ mull %edx
+ addl %ebx, %eax
+
+ // Set the cell negative to represent an apple
+ movl $world, %ecx
+ movl $-1, %edx
+ movl %edx, (%ecx, %eax, 4)
+
+ popl %ebx
+ ret
+
+// debug_draw
+// =============
+// This is basically the same as `draw`, except it draws the actual ASCII-value
+// of the world for debugging purposes.
debug_draw:
// Loop Y
movl $HEIGHT, %ecx
@@ -281,8 +413,9 @@ debug_draw:
// Call nib_put_scr
- // i = x + y * WIDTH
- // %edi = %ebx + %ecx * $WIDTH
+ // `i = x + y * WIDTH`
+ //
+ // `%edi = %ebx + %ecx * $WIDTH`
movl %ecx, %eax
movl $WIDTH, %edx
mull %edx
@@ -292,10 +425,12 @@ debug_draw:
movl (%edi, %eax, 4), %eax
pushl %eax
- movl %ecx, %eax // Y
+ // Y coordinate
+ movl %ecx, %eax
pushl %eax
- movl %ebx, %eax // X
+ // X coordinate
+ movl %ebx, %eax
pushl %eax
call nib_put_scr
@@ -305,68 +440,14 @@ debug_draw:
popl %ebx
popl %ecx
- decl %ebx // Decrement X
- jnz 2b // End of X loop
- loop 1b // End of Y loop
+ // Decrement X
+ decl %ebx
+ // End of X-loop
+ jnz 2b
+ // End of Y-loop
+ loop 1b
+
+ // Jump back to the start of our game loop!
jmp loop
-gameOver:
- // Calculate string length
- movl $sgameover_end, %eax
- subl $sgameover, %eax
- subl $1, %eax // Discount null terminator
-
- // Calculate X-pos
- movl $2, %ebx
- xorl %edx, %edx
- divl %ebx
- negl %eax
- addl $WIDTH/2, %eax
-
- // Print game over message
- pushl snakeLength
- pushl $sgameover
- pushl %eax // X
- pushl $HEIGHT/2 // Y
- call mvprintw
- add $4*3, %esp
- call refresh
-
- pushl $3000*1000
- call usleep
- addl $4, %esp
- call nib_end
-
-spawnApple:
- pushl %ebx
-
- // %ecx = rand() % HEIGHT
- call rand
- xorl %edx, %edx
- movl $HEIGHT, %ebx
- divl %ebx
- movl %edx, %ecx
- // %ebx = rand() % WIDTH
- pushl %ecx
- call rand
- xorl %edx, %edx
- movl $WIDTH, %ebx
- divl %ebx
- movl %edx, %ebx
- popl %ecx
-
- // i = x + y * WIDTH
- // %eax = %ebx + %ecx * $WIDTH
- movl %ecx, %eax
- movl $WIDTH, %edx
- mull %edx
- addl %ebx, %eax
-
- // Set the cell negative to represent an apple
- movl $world, %ecx
- movl $-1, %edx
- movl %edx, (%ecx, %eax, 4)
-
- popl %ebx
- ret
diff --git a/nibbles_asm b/nibbles_asm
index 4475c38..22c6f52 100755
Binary files a/nibbles_asm and b/nibbles_asm differ
diff --git a/nibbles_asm_start b/nibbles_asm_start
index 79d8182..be9bb0e 100755
Binary files a/nibbles_asm_start and b/nibbles_asm_start differ
diff --git a/nibbles_c b/nibbles_c
index d2a5398..97fc96e 100755
Binary files a/nibbles_c and b/nibbles_c differ
diff --git a/start.S b/start.S
index 252d351..d5a7e2a 100644
--- a/start.S
+++ b/start.S
@@ -1,12 +1,17 @@
.text
+// _start
+// ======
+// The `_start` symbol. Calls `start_game` with "reasonable parameters".
.global _start
_start:
- # n_apples
+ // `n_apples`
pushl $10
- # len
+ // `len`
pushl $3
+ // `start_game(int len, int n_apples)`
call start_game
addl $4*2, %esp
-
- movl $0, %eax
- ret
+
+ // Exit cleanly with an exit code of 0
+ pushl $0
+ call _exit