Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics

Internet Janitor

A member registered Aug 02, 2018 · View creator page →

Creator of

Recent community posts

(1 edit)

You could calculate the index you need. This will require some consideration of the tile size:

Be careful of overflow. If you have lots of tiles, you might need to add to i multiple times. This technique also gets a bit fiddlier if you aren't using a power-of-two size, but it's still doable.

Another option is to just use pre-multiplied values as your tile IDs. If you don't have many tiles, this makes looking one up trivial!

In some situations, you could consider using self-modifying code to rewrite the address in an `i := NNN` or `i := long NNNN` instruction. This gets a bit fiddly, but if you have lots of tiles, or tiles that take up a lot of memory (like full-color tiles) it might be the most efficient option. There are some examples of these sorts of techniques in the FAQ, when discussing pointers.

Edit: Oh, and one other thing- while writing a loop like you did there does work, it would be more idiomatic to do something like:

Using == or != instead of < is more efficient when you can get away with it.

I'll definitely be interested to hear about what you come up with!

Lots of possible tradeoffs depending on exactly what your needs are.

In general, when speed is of the utmost importance, macros and unrolled loops are your friends. One nice thing about this on XO-Chip is that while some of them take up a lot of memory, none of the above routines include any branches, so they could be located at the very end of the low 4k of address space and only consume one byte of precious "code RAM".

(1 edit)

Thinking about it some more, I guess a simple merge isn't necessarily all that bad:

Takes up nearly a kilobyte and a half, though. Oof!

Or for a modest increase, you could composite an image under the control of a third masking buffer:

(3 edits)

Might work in some situations, but it gets pretty brutal.

Assuming a buffer with data packed in an appropriate representation, we can do a fast 64x32 pixel (lores) fullscreen draw by using a series of 8 sprite draws in 21 cycles:

If you fill the v-registers with zeroes and take advantage of chip-8's auto-incrementing behavior on save, you can clear the buffer in 21 cycles:

Bulk operations have to touch every byte, so the best case for inverting the pixels stored in the buffer is around 296 cycles, with aggressive loop unrolling:

Merging together multiple buffers is even nastier, since you'll need to manually ping-pong the index register from buffer to buffer. Making use of the flag registers (xo-chip has 16 of 'em) as a lookaside buffer could potentially help.

The main thing I've papered over here is the byte layout. Plotting a pixel at a given x/y position in the buffer is fairly complex for the above routines, since 16x16 sprites alternate columns each byte and the sprites themselves are laid out in another zigzag pattern. There are many arrangements possible that would help or hinder different parts of a sprite drawing routine, but I think the result will be rather complicated any way you slice it. Using 8x15 sprites instead of 16x16 could help, but then drawing will be somewhat slower. Everything's a tradeoff.

These complete examples are here:

Chip8 and Octo are a great way to dip your toes into assembly language. While Octo's syntax is a bit nonstandard, all the core concepts- bits and bytes, labels, registers, 8-bit arithmetic- will carry over to other assemblers and architectures in the future. I hope you have fun!

Octo doesn't have any video documentation, but there's a great deal of written material on the Octo Github page.

You might start with the Beginner's Guide, which doesn't assume any previous experience with programming or CHIP-8. You can also just try editing one of the example programs and clicking "run" to see what happens. Sometimes breaking a program can produce spectacularly interesting results!

As we get closer to October, it's time to start thinking about what kind of program you're going to write. Having a case of programmer's-block? Let's brainstorm! Are you considering:

  • A rewrite of an existing CHIP-8 game, adding features and flourishes?
  • A "demake" of a newer game, boiling it down to its essence to cram it into CHIP-8's creative constraints?
  • An adaptation of an "analog" game- a sport, a board game, a card game, or something from an escape room?
  • A demo, focusing on technical virtuosity, visuals, and audio instead of gameplay?
  • Something "useful", like a calculator, a simulation, or an educational program?
  • A narrative experience, perhaps inspired by a favorite work of literature, music, or film?

...or something else entirely?

The Octojam is all about writing software for the Chip-8 platform. You can use any tools you like to prepare a Chip-8 ROM, but Octo is a convenient tool for writing and testing such programs.

Octo can save a standalone .HTML document which bundles the Chip-8 ROM together with an emulator. You can then use this .HTML document on Itch to let others play your game directly in their web browser. Here's an example of a game I've published in the past using this method:

You can additionally distribute the bare .ch8 ROM as a download on your game's submission page, for use with other Chip-8 emulators.

Does that make more sense?

This thread is for short questions about Chip8 programming and the Octo assembly language. If you have a more involved question, feel free to start a separate thread.

Last year's thread can be found here.

Many common tricks and pitfalls are addressed in the Octo FAQ.

Cool stuff! It's wonderful to see programs that recreate the visual style of Hypercard.

I recently built my own 1-bit drawing program, and only later discovered Strike- maybe Ditherpaint could have some inspirational ideas? I use the same internal model of "patterns as colors", but allow users to edit the patterns and create sequences of those patterns to cycle through, as a limited form of animation.

(1 edit)

You seem to be unclear on what "pixels" are, so I will try to explain. I apologize in advance if this seems in any way condescending.

The Nokia 3310 has a display consisting of a uniform grid of squares. The grid has 84 columns and 48 rows. At any given time, each square can be either a dark or a light color. It is not possible to have "half" of one of these squares illuminated; all graphics (including text!) must be drawn using these pixels as the minimum element of granularity.

Your game displays many objects at varying sizes which are not even multiples of a pixel size which divides your display into an 84x48 resolution. Objects move smoothly at "sub-pixel" intervals. Text is often scaled or animated in fine gradations which are not multiples of pixels and use anti-aliasing to smooth their edges. The background border of levels uses very finely-spaced lines which in practice appear as a different color from the proscribed 2-color palette. Even the introductory sequence of the game uses additional colors (black and white) beyond the palette constraints.

I hope this has helped clarify why your game has received criticism with respect to its adherence to the jam's aesthetic constraints.

This was a very interesting concept for a narrative game. For the most part, puzzles were logical and easy to trace out.

I actually had the most trouble with the initial PIN. I assumed that the hint was suggesting some permutation of day/month/year for the birthday instead of just the four-digit year. Not sure how to prevent overthinking on that one. If you want to make it more forgiving perhaps you could make the game quietly accept a few alternative solutions?

While I get that the battery level mechanic was included to incorporate the jam theme, I felt it was mainly a source of frustration when trying to explore the contents of the phone thoroughly.

(1 edit)

Generally a nice-looking and well-executed game.

I did feel that the text speed of dialogue was excessively slow, and would have liked space to advance to the end of pages of dialog instead of skipping unfinished paragraphs entirely. Simple changes to this UI would have made the plot more enjoyable.

In gameplay, I rarely felt that there was any reason not to hold down fire continuously. A charged shot, a limit on projectiles in the air, an ammo limit, or any number of other mechanics could make firing something that benefitted from careful timing.

This is really great!

Graphics, gameplay, and the control scheme are all clear and simple. I can can easily tell what's going on, and the game strikes a nice balance and pace. This submission really is the total package, and I could see myself enjoying this on a real phone.

Controls are laid out reasonably, all the animations look nice, and everything appears to faithfully adhere to the jam restrictions.

That said, I am completely baffled by the gameplay. Choosing a square appears to show a different random animation every time. This doesn't behave like the normal "match-two" memory games I am familiar with, and the game does not clearly explain how these animations are connected to anything.

The core mechanic is potentially interesting, but as others have pointed out this isn't remotely attempting to conform to the creative constraints of the jam.

The mechanic is an interesting use of the theme, but the control scheme is really awkward. I feel like I need an extra hand to play this. Why not go for something like arrows for movement and Z/X to jump/switch?

The audio was interesting and atmospheric.

I didn't feel like there was much to do in the game, though. Movement is very slow, and the environment is barren. I don't get the sense that there's much to find as I plod around in a featureless maze.

I never had any difficulty finding enough batteries, so the time limit didn't produce any tension.

There's some great pixel art and animation here! Level design is reasonably varied and interesting.

One thing that mars the presentation a bit is that (at least for me) the web build has blurred the upscaled display instead of using nearest-neighbor.

(1 edit)

This game doesn't adhere at all to the jam's 84x48 pixel resolution constraints- pixel art is nonuniformly scaled and anti-aliased, objects are moved smoothly at "sub-pixels", and text and background graphics are much too detailed.

The control scheme is intuitive. Gameplay seems very repetitive and simple. If the enemies escalate in difficulty, the ramp-up is far too slow to be noticeable.

Sound would have been a nice addition.

I like the visuals in this game- the atmospheric particle effect is particularly nice. The player sprite and game tiles all read clearly.

The music gets grating rather quickly. It might have been better to focus on sound effects giving feedback from the player's movements instead of music.

I think you have a typo in the description of the controls on your jam page.

This game looks great- visuals read well, and there's a lot of subtle attention to detail!

I'm not as sold on the gameplay. Controls feel very sluggish and unresponsive; while this may be somewhat intentional to make the input "weighty", I didn't enjoy it very much. It seems like I lose when I fail to stack a block on a previous one, but there isn't much feedback to failure- it seems like the block slips, and then a few moments after, when I expect the next piece to fall, the game suddenly ends. Maybe there's an audio cue I'm missing with the web version?

I wasn't really clear on my objective or the controls. More explanation, either on the itch page or in the game itself, would help. Using B/N for dialogs and Arrows/Space for gameplay is a bit confusing; consider unifying the control scheme.

Good adherence to the jam constraints. Sound would be a nice addition.

This game suffers from lack of feedback and affordance. For example, while "pumping" a tree and collecting money from it, the player has no indication of how much money they have until they move to a different menu entirely. Before money started growing, it was unclear to me whether I was supposed to hammer a button or something to make the tree develop faster. I found the control scheme somewhat unnatural, as I needed to move my hand back and forth between the arrow keys and other portions of the keyboard to navigate menus.

For the most part, this game adheres to the necessary design constraints, but some animations (notably water drips and progress bars) are not pixel-aligned, breaking the illusion of an 84x48 screen.

Gameplay is varied, though the player's motion feels a bit sluggish to me. Reading the manual outside the game is essential, as I don't think I could have made heads or tails of what was happening without a primer.

The conceit of a display which is slowly obscured and must be periodically "refreshed" is an interesting mechanic based on the jam's theme.

Aesthetically, this game adheres to all the constraints of the jam well.

Requiring the use of a numpad makes this game far less accessible than it could otherwise be- I couldn't play at all on my laptop. I think this game would have benefited greatly from a simpler and more consistent control scheme. I also think the game would have benefitted from more explanatory text/legends integrated with the gameplay, instead of frontloading all the explanation and relying mostly on pictures when e.g. collecting items.

Graphics read fairly well, and everything about the game appears to correctly adhere to the jam's aesthetic constraints.

Controls feel reasonably good, pixel art reads well, and the audio sounds authentic for the 3310. Visually, though, adherence to the jam constraints is poor. Some sprites are drawn with alpha transparency and some graphics are anti-aliased, resulting in far more than 2 colors. Graphics are not drawn pixel-aligned, breaking the illusion of an 84x48 pixel display.

Thank you! I'm glad you had an enjoyable and engaging experience.

The 3330 might be more feasible; it has the same external appearance, rez, and so on as the 3310, but according to Wikipedia that model supported J2ME. Even limited to MIDP 1.0, Java ME is quite capable and accessible.

Actually transferring software to the phone is its own hairy challenge. I don't know if there's any sort of data transfer cable you can get for these phones, and afaik downloading games over WAP would mean you had to set up a little local cellular network...

Really cool stuff, Dean- thanks for sharing!

Do you plan to open-source your chip8 interpreter? I didn't see any mention of it on the Hackaday project page.

I wrote some example code for writing nokiajam-compatible games with Octo, my CHIP-8 IDE:

Here's a stream from Darzington/No Problem Games:


This uses XO-CHIP instructions, mostly because I need the "scroll-up" instruction that is conspicuously absent from SCHIP. The sound effects and use of a third color are side benefits. :)

The source is available as a download, if you're curious.

Sure- sounds like fun!

First thing I noticed when I assembled your game is that it's 3814 bytes. That means that it won't quite fit in the low 4kb of RAM. (remember: the first 512 bytes are reserved, and CHIP-8 programs always begin assembling at 0x200.) If you pop a compile-time assertion like the following:

:assert "overflowed code RAM" { HERE < 4096 }

Somwhere in your "game over" routine, you'll see that the assertion fails, which confirms the theory.

Instructions like "jump" (which is emitted as part of an "if...begin" or a "loop...again") take a 12-bit address, so they can only point somewhere in the low 4kb of memory. We need to make your program shorter, or at least ensure that the code fits in a smaller space. Fortunately, I see some easy opportunities to make this happen!

The simplest option might be to move some or all of your graphics data to the end of the program. This may require you to use "i := long NNNN" instead of "i := NNN" in some cases, since that data will now be located somewhere that needs a 16-bit address to access; Octo's assembler will complain when this is necessary.

Another option to explore is reworking that "text" macro. As written, it emits 3 CHIP-8 instructions (6 bytes) for every character printed. That adds up quickly! Even just special-casing spaces like this should save you a couple dozen bytes:

:stringmode text "ABCDEFGHIJKLMNOPQRSTUVWXYZ.,!?/" {
:calc addr { font + VALUE * 4 }
i := addr
sprite vx vy 4
vx += 4
:stringmode text " " { vx += 4 }

Does that make sense?

(1 edit)

When you've completed all the finishing touches on your submission(s) to the Octojam, please consider contributing your software to the CHIP-8 Archive. This repository gathers together dozens of modern CHIP-8, SCHIP, and XO-CHIP programs, including most of the entries from previous Octojams.

Everything in the archive is made available under a highly-permissive CC0 license: This allows future generations to learn from and remix all the great CHIP-8 software we've built over the years without falling into a murky gray area of copyright. The archive also stores all the metadata for entries, so future emulators can display games with the intended palettes and compatibility settings, and many entries include source code and other raw assets. The source directory can contain supplementary information about a program, including links to your itch pages, other online source repos, and postmortems.

Participation in the archive  is entirely optional: it's up to you!

First, the simplest case: you have a single byte in memory and you want to load it into a register:

: data  0xAB
i := data     # set i to the base address
load v0       # load from i into v0

Slightly more complex: you have an array of up to 256 bytes, and you want to load one by index into a register:

: data  1 1 2 3 5 8 11
i := data    # set i to the base address
i += v1      # add an offset (in this case, the value in v1)
load v0      # load data[v1] into v0

Same as before, but now we want 2 bytes:

i := data    # set i to the base address
i += v1      # add an offset. items are two bytes wide...
i += v1      # (we alternatively could have pre-doubled v1.)
load v1      # v0=data[2*v1], v1=data[(2*v1)+1]

Are those cases clear?

This came out fantastic; well done!

I also greatly enjoyed reading the postmortem on your GitHub repo.

Yeah; it's a browser limitation, I'm afraid. To start an audio context playing, you must kick it off as a direct result of a user input action. If the subsystem hasn't been set up yet, Octo will make an attempt in response to keyboard or touch input.

(1 edit)

Exactly; the argument is an AND mask.

Sometimes it's clearer to write the argument to `random` in binary (e.g. `0b1100`) instead of hex or decimal. Then just imagine that every 1 in that mask could wind up as either 1 or 0 in the result.