Close
0%
0%

tinygames

Trying to fit video game(s) into 1kB without blowing up a TV.

Similar projects worth following
I have a primitive version of Centipede running on an Attiny84 with minimal support components. The picture shown here is a real analog B+W television. No digital flat panels, no hidden processors, the 1kb in the microcontroller and that's it!

The following features are implemented:

- Bugs can move either left or right.
- A bug that runs into the screen edges or a mushroom move down.
- Player can move left or right, and shoot.
- Shooting a mushroom removes it from the screen.
- Shooting a bug turns it into a mushroom (sometimes). This also spawns a new bug at the top of the screen.


The game plays forever, with no scoring or game-over condition. It can easily be added, but not in the 1kB limit. The microcontroller has 8kB of memory, so there is room for later enhancement. This version is more like an interactive screensaver. However, if you don't play, the mushrooms will eventually burn holes in your TV!

Video Output Hardware

The video output is achieved using just two outputs: One for sync and one for color. These two ports are tied together with a resistor divided and form a crude DAC. This is based off of the Arduino TVout library http://playground.arduino.cc/Main/TVout


Controller Input

The controller input is an Atari 2600 Joystick. The pinout is provided here. http://www.hardwarebook.info/Atari_2600_Joystick I only use Left, Right and Fire for this project.

Video Output Software

I found the TVout software to be very hard to understand. I took 1 small part of it: the wait_until inline-assembly routine. The rest was discarded.

I took some liberties to what constitutes a NTSC signal. After trying to implement most of the spec, I settled on something that ended up being the same thing Atari and other consoles did: A 'hack' called 240P. The screen is not interfaced as usual, and there are no 'odd' and 'even' scanlines. Only 240 lines are drawn every frame, and there are 240 lines on the screen that are black and never drawn at all. If you want some deep details on how full NTSC is supposed to work, visit this page: http://www.ntsc-tv.com/ntsc-index-02.htm For info on how I stumbled upon 240P instead, I have a project log just on screen syncing, so I won't repeat it here.


Racing the Beam

To generate the video signal, the technique used here is called racing the beam. This is exactly how the Atari 2600 generates an image. There is no framebuffer. As the electron beam is scanning the TV, the software must pull the output high, to produce white, or pull the output low, to produce black, at the exact instant the electroncs are shot into the screen glass. This means timing must be perfect. Fortunately for me, I don't have to do much cycle-counting at all thanks to Interrupts.

The Atari 2600 had NO interrupts. The programmer must carefully work everything out during the blanking period and output the signal on-time. Using the Attiny timer interrupt, I can split the program into two threads. Thread 1 is the 'main' thread running game logic. This reads the joystick, and calculates where everything should be on the screen. Thread 2 is the interrupt handler: Every so often, the interrupt fires and the interrupt handler outputs 1 scanline of video. One thing gained by this project is a tremendous amount of respect for the Atari 2600 programmers, who had a much worse CPU and no interrupts. Somehow Pitfall happened.

Video Frame Structure

To represent the gamestate to draw, there are some variables defined:

  • playerx: The player x-position
  • bulletx, bullety: The position of the bullet the player has fired
  • mushrooms[]: sorted char array specifying positions of mushrooms on the screen
  • bugs[]: sorted char array specifying positions of bugs
  • SCR_COLS (constant) : number of grid-columns in the display.
  • scrpos: The screen position the electron beam is point at
  • drawpos: A pointer into the mushrooms or bugs array, noting which one is expected to be hit by the electron beam next.
  • line: The current scanline number. (line mod 8) is the row to use for a sprite. Hint: (line mod 8) is the same as (line & 7), so no division/remainer is necessary.

The position of an object is just a number representing a point on a grid. The top-left position is 0, the one to the right is 1, all the way up to (SCR_COLS-1). Position SCR_COLS starts the second row, 2*SCR_COLS is the 3rd row. It looks like this:


00 01 02 03 04 05 06 07 08

09 10 11 12 13 14 15 16 17 18

19 20 21 22 23 24 25 26 27

...

A typical mushrooms array might look like { 5, 12, 17, 23, ENDOFLINE}; This would mean to draw a mushroom in positions 5, 12, 17 and 23. ENDOFLINE is the value 255, which serves as a terminator of the array.

Suppose we only want to draw mushrooms. For a given scanline, we can look thru the mushroom array for mushrooms that would appear on our row. We can display a horizontal 'slice' of pixels for a mushroom, or keep the display black, for every position on this row. The mushroom array is kept sorted so that after...

Read more »

sizeproof.png

How I figured out it's 1KB.

Portable Network Graphics (PNG) - 48.83 kB - 01/03/2017 at 05:15

Preview

SMALL.hex

The hex file.

hex - 2.84 kB - 01/03/2017 at 05:15

Download

SMALL.atsln

Atmel Studio Solution

atsln - 886.00 bytes - 01/03/2017 at 05:14

Download

SMALL.c

The Source

plain - 14.85 kB - 01/03/2017 at 05:14

Download

SMALL.cproj

Atmel Studio Project

cproj - 5.43 kB - 01/03/2017 at 05:14

Download

View all 7 files

  • 1 × Atmel Attint 84
  • 1 × 10.7 Mhz Crystal Oscillator (What I happend to have sitting around)
  • 1 × 330 Ohm resistor
  • 1 × 1k resistor
  • 1 × 470 ohm resistor

View all 12 components

  • Finished in 1024 Bytes

    Mars01/03/2017 at 05:36 0 comments

    I got it finished, with exactly 1024 bytes:

    Here is a youtube video of it running:


  • Video Update

    Mars01/01/2017 at 22:12 0 comments


    I thought I would share what is working so far.

    Only a few days left, and I'm almost out of memory.

  • Ancient Computer Problems

    Mars12/30/2016 at 22:03 1 comment

    I depend on my old K6-2 linux machine to program by Attiny chips through its parallel port. The old AT (not-X) power supply has failed. I borrowed the power supply out of my trusty DOS machine. My workspace is quite the mess at the moment.

    But everything is back in working order!

    I suppose I should buy a proper USB AVR programmer. But, that's no fun :-)

  • Smaller Code

    Mars12/20/2016 at 06:48 0 comments

    Everything I was doing before in 904 bytes I can now do in 674 bytes. I refactored the mushroom and bug drawing code to substantially overlap.

    Now I need bugs and mushrooms to interact.

  • Multi Mushroom and Bugs

    Mars12/16/2016 at 06:40 0 comments

    I just updated the main project picture. I'm drawing multiple mushrooms and the nameless trademarked bugs.

    900 bytes used. It's getting tight.

    The video loop is using an old Atari 2600 trick: The even frames show mushrooms and the odd frames show bugs.

    Now I need to squish the game logic of the bugs moving around the mushrooms, and shooting them down, in very tiny space. I suspect I'll have to rewrite the video driver loop to be smaller somehow.

    Here is a pic of the whole hardware setup.

  • One Mushroom

    Mars12/06/2016 at 18:43 0 comments

    After a bunch of fiddling around, I got 1 mushroom to appear on the screen. I have a display loop that can display or not display a muchroom in each screen tile. If you notice the vertical timing markers, the mushroom space takes more time than the no-mushroom space. I am planning to add in delays in the right places to make all spaces take the same time, but I dont want to tune any of this yet until everything is mostly working. Next is a working multi-mushroom list, instead of a single mushroom position variable.

    I think you can guess what game this is.

    508 bytes used.

  • Clean Sync

    Mars12/05/2016 at 02:15 0 comments

    I spent some time online researching how NTSC video signals work. There are a lot of details, and after attempting to implement the full odd/even interlaced signal complete with equalization pulses and 1/2 scanlines, I ended up with a picture that was slightly unstable on my LCD monitor. I got an old B+W portable TV, and that refused to synchronize to my signal at all. Oh well.


    After some more searching, I found this page: http://www.rickard.gunee.com/projects/video/pic/howto.php Rickard's approach started by experimenting with Horizontal syncing only. This didn't seems like something at would work on my LCD monitor: its want a full frame before it displays. Using the old CRT TV, I hacked my code down to horizontal sync only. And it displayed a rolling picture, that was horizontally synchronized! If I messed with the v-hold setting on the TV, I could get a very slow roll. All I need it a little vertical synchronization.

    I decided to implement the only part of vertical sync that looked unique: The vertical serrations. During the vertical serrations, two things happen: A) The sync signal rate is double (2 sync periods per line) and B) The signal is inverted: Normally low instead of normally high. This made the B+W TV sync just fine.

    But I don't have a 1/2 scanline anywhere, and I was sure the picture wasn't being properly interfaced odd/even. After a little more research, I discovered this is what the Atari 2600 does: Every frame is even lines only. It turns out many game console do this, and it's referred to as '240p'. If you search for this you will find a lot of new TVs won't even show anything on this mode.

    Stella (Atari) programming Guide: http://atarihq.com/danb/files/stella.pdf "A typical frame will consists of 3 vertical sync (VSYNC) lines*, 37 vertical blank (VBLANK) lines, 192 TV picture lines, and 30 overscan lines. Atari’s research has shown that this pattern will
    work on all types of TV sets."

    After getting this working, I found the TV image was a little jittery in the horizontal direction. I guessed that this was the 8Mhz internal oscillator. I had a piece of e-waste with a 10.752 Mhz crystal oscillator in it, so a salvaged it and updated all the constants in the code to run at 10.8 Mhz. Now the picture is solid.

    I am using 350 bytes to produce the above image. Let's see if I can squash a game or two into the rest of the space.

    I noticed someone else is attempting full-color space invaders. I better try something different :-)

View all 7 project logs

  • 1
    Step 1

    Obtain the proper parts. This may be assembled on a breadboard as I have done. You will also need a few special items:

    • Suitable TV: CRT preferred, older LCDs will probably work. If an Atari 2600 works on your TV, this should too.
    • Atari 2600 joystick: If you can't get one, you just need 3 pushbuttons.
    • AVR Programmer: This is NOT Arduino, and there is no serial bootloader. You need a proper USB or a bitbang programmer. I used the 'bsd' parallel port programmer using AVRDUDE, hanging off the back of an old Linux machine.

    If you wish you may modify or recompile the code. I used Atmel Studio 6.1

  • 2
    Step 2

    Assemble the schematic.

  • 3
    Step 3

    Set the AVR fuses. A factory-fresh AVR is set to use the internal 8Mhz oscillator. I was able to generate a video signal at 8Mhz, but the code I've provided has the wrong timings for 8Mhz, and the picture is wobbly due to jitter.

    The correct fuse values are:

    lfuse: 0xFF

    hfuse: 0xDF

    This will set it up for an external clock. You must have an external clock connected, or you will no longer be able to program the AVR, and the MCU will be bricked until you do connect an external clock.

    This is the avrdude command to set the fuses:

    sudo avrdude -c bsd -p t84 -u -U lfuse:w:0xff:m

    sudo avrdude -c bsd -p t84 -u -U lfuse:w:0xdf:m

    Change -c parameter to use a different programmer if necessary.

View all 5 instructions

Enjoy this project?

Share

Discussions

Grzegorz Pietrusiak wrote 01/17/2017 at 19:33 point

Congratulations!!!

  Are you sure? yes | no

MortenW wrote 01/13/2017 at 22:24 point

Congratulations man, you made it right up there with the top ones.

  Are you sure? yes | no

Mars wrote 01/13/2017 at 23:50 point

Thanks! Your's was really good too. I thought you would have had me beat because you had color, and VGA is even more sensitive to timing than NTSC. You also took it on in assembly language, I'm very impressed with your work.

  Are you sure? yes | no

Ted Yapo wrote 01/04/2017 at 20:48 point

You had me at "centipede"

Nice job!

  Are you sure? yes | no

MortenW wrote 01/04/2017 at 19:38 point

good job completing this on time and within size limit. Marble maze here we come :-)

  Are you sure? yes | no

Mars wrote 01/04/2017 at 20:58 point

Nooo! Not the marble maze! I want the bulbdial clock.

  Are you sure? yes | no

ActualDragon wrote 01/03/2017 at 19:17 point

never played centipede before, more into COD Ghosts XD

  Are you sure? yes | no

Hacker404 wrote 12/21/2016 at 05:39 point

I love it! 

  Are you sure? yes | no

coletonn wrote 12/20/2016 at 18:34 point

This is really cool! Hope you win somethin!

  Are you sure? yes | no

MortenW wrote 12/06/2016 at 18:50 point

Centipede ?

  Are you sure? yes | no

Similar Projects

Does this project spark your interest?

Become a member to follow this project and never miss any updates