Sadale - Games, Music and Stuffs

Welcome to the website of an amateur research and development center! Started off with game development, Sadale had developed pretty much everything, including games, music and research works. In the future, more products will be developed (well, as if anyone would care about that).

This website is a blog that is mainly about the products that I've developed, documentation of solution to non-googleable problems that I've encountered and publication of previously non-googleable javascript web-tools.

All products mentioned in this website are my own work, unless stated otherwise.

Our core value: Development in Stability

Showcase - Games

Poopie Icon Poopie - Upgrade game about pooping on people. [Play on web] [Google Play] [Blogposts]

Fall in Love Icon Fall in Love - Puzzle game for Android. Successor of award-winning game. [Google Play] [Website]

Showcase - Music

Icon of Futile Attempt of Killing Mosquito Futile Attempt of Killing Mosquito (mi ken ala moli e pipi) - My first Toki Pona song with animation. [Youtube] [Soundcloud] [Blogpost with remix resources]

The Lyrics of This Song are Weird (呢首歌嘅歌詞好奇怪) - A Cantonese song with unusual lyrics. This is my first song ever released. [Youtube] [Lyrics] [Blogpost]

Axial Inclination - A song about the end of 2017. [Spotify] [iTunes] [KKBox] [Website (with lyrics)] [Blogpost]

Showcase - Other Stuffs

Online Middle-Square Method Generator - A web tool that I couldn't find elsewhere. [Link to the Tool]

animeVPS - A low-end, donation-based VPS service that I'm hosting. [Website]

Click here for the full list of products that I've worked on.

薩地魯 - 遊戲, 音樂, 仲有其他作品!

歡迎嚟到薩地魯嘅網站. 曾經係業餘遊戲開發者, 今日薩地魯利用佢嘅業餘時間, 乜都整餐懵. 包括遊戲, 音樂, 研究項目等等.

呢個網站主要用來blog低薩地魯開發嘅作品同埋係Google搵唔到嘅問題嘅解決方案. 作品列表請見上面嘅網頁介紹嘅英文版.

絕大部分嘅blogpost只有英文版. 當然, 都有D有廣東話版嘅.

核心文化: 穩定為先, 開發為本

jan Sate - ona li pali e musi pi ilo sona e kalama musi e ijo ante!

kama pona! ni li lipu pi jan Sate. tenpo pini la jan Sate li pali e musi pi ilo sona. tenpo ni la jan Sate li pali e ali.

lipu ni li jo e nimi mute pi pali pi jan Sate. pali pi jan Sate li lon sewi pi lipu ni kepeken toki Inli.

ona mute li lon kepeken toki Inli. taso lipu ni li jo e ona lili pi toki pona. o pilin nena ni tan ona pi toki pona!

jan Sate li wile ala e utala li wile kama sona e ali li wile pali e ali.

Website Description

Recent Blogposts

mi pana e kalama musi "mi ken ala moli e pipi" tawa jan ali | Release Announcement of Toki Pona Song "Futile Attempt of Killing Mosquito"

toki! ni li nimi mute mi pi nanpa wan pi toki pona lon lipu mi. tenpo suno ni la mi pana e kalama musi "mi ken ala moli e pipi" tawa jan ali a! tenpo suno luka tu wan la mi pali e kalama musi e sitelen tawa. tenpo pi mute wan la mi pali e sitelen tawa a!

tenpo pini pi tenpo sike ni la mi pali e kalama musi mute tan tenpo "Global Game Jam 2018". kalama musi wan pi ona mute li pona mute tawa mi. mi pona e kalama musi ni li pali e sitelen tawa pi kalama musi ni li pana e sitelen tawa ni tawa jan ali.

tenpo pini la mi pali e kalama musi. taso mi pana ala e ijo ni tawa jan ali. jan jo ala e ijo ni la ona li ken ala ante e kalama musi.

tenpo ni la mi pana e ijo ni pi kalama musi tawa jan ali a! sina ken ante e kalama musi ni.

Hey guys! Today I'm releasing a toki pona song "Futile Attempt of Killing Mosquito" along with an animation. I've spent 8 days and nights on this piece of music and its animation! It's also my first serious animation project in my life!

This music came from Global Game Jam 2018. I decided to be a musical guy for the event. So I've made quite a few pieces of music as a practice. That's because we're required to get the work done within 48 hours in the event. So I have to get familiar with the tools. A few months after the event, I found that I love one of those musical segment particularly. Then I elaborate this part and enhance it and I've made an animation for it. And here we have this musical animation.

Unlike my previous music, this time I'm providing source files of music publicly and direct download link to a few variants of its animation. That makes remixing and editing the music and animation much easier compared with my previous works!

sitelen tawa lon lipu Jutu | Animation on Youtube

o lukin e sitelen tawa ni pi kalama musi lon lipu Jutu!

Check out this musical animation on Youtube!

kalama musi lon lipu "SoundCloud" | Music on SoundCloud

[o kute e ni! | Click here to listen to it]

ante pi sitelen tawa en ante pi kalama musi | Derived Works of this Project

sina ante e sitelen tawa e kalama musi la o toki e mi. ona li pona la mi wile pana e lupa pi pali sina lon lipu ni a!

If you distribute a remix of any resources from this project, feel free to inform me. If it's a good one I'd be happy to put up a link to your work right in this blogpost!

sina wile ante e sitelen tawa e kalama musi e kalama uta pi kalama musi la o kepeken e ni! | Resources for Remix, Vocal Swap and Other Modifications

lipu lawa pi "CC BY 4.0" li lawa e sina la sina ken kama jo e ijo lon sewi lipu.

License of the resources above: CC BY 4.0. Please attribute to this blog post or If you do not want to do the attribution, please contact me and let me know what you're going to use these resource for. It's very likely that I'll grant you attribution exemption.

o ante e kalama uta! | Possible Modification with the Resources Above: Vocal Swap

sina wile ante e kalama uta la o kepeken e ijo "kalama musi" anu "kalama musi pi kalama uta". sina pini kalama uta la o kama jo e ijo "kalama musi weka kalama uta" kepeken ilo sona kalama. ilo sona kalama li ilo "Audacity" anu ijo ante. ijo "kalama musi weka kalama uta" en kalama uta sina li kalama musi pi kalama uta sina.

sina ken kalama uta e nimi mute pi kalama musi ni kepeken toki ante a!

If you want to do vocal swap, first, download the resource "Music" or "Vocal Assist". While playing any of these, sing along and record what you're singing. Then download "Instrumental Music" and align your recording to the Instrumental Music track with software like Audacity. Then you've got your voice into this piece of music.

With vocal swap, you can even sing this song in another language!

o ante e nimi toki! | Possible Modification with the Resources Above: Modifying Subtitles

sina wile ante e nimi toki la sina ken kama jo e ijo "sitelen tawa weka nimi Inli" e ijo "nimi pi toki Inli pi sitelen tawa". sina pini ante e ijo "nimi pi toki Inli pi sitelen tawa" kepeken ilo "Aegisub" la sina ken pali e sitelen tawa sin kepeken e ilo "FFMPEG" anu ilo ante. sina jo e sitelen tawa pi nimi pi toki ante a!

sina pana e ijo tan "Aegisub" tawa sitelen tawa lon Jutu la jan ali li ken lukin e nimi pi toki sina a!

If you want to modify the subtitles of the vid, download the resource "Animation without English Subtitles" and "SubStation Alpha English subtitles". Modify the resource "SubStation Alpha English subtitles" with software like Aegisub. With FFMPEG, you can combine the subtitles and the video. Then you get a video with subtitles in another language.

If you provide that subtitles file to the video hosted on Youtube above, other users can view your subtitles.

o ante e kalama musi! | Possible Modification with the Resources Above: Remixing the Music

nasin nanpa wan li ni: sina wile kepeken e ilo "LMMS". o kama jo e ijo "ijo pi kalama musi pi ilo "LMMS"". sina pini lukin e nimi mute lon ona la o kama jo e "kalama uta". tenpo ni la sina ken ante e kalama musi ni.

nasin nanpa tu li ni: sina kama jo e ijo "kalama musi" e ijo "kalama musi weka kalama uta" e ijo "kalama musi pi kalama uta". sina namako e kalama musi ni kepeken ilo sona kalama.

There're two ways to do remix. The first way is to use LMMS to remix the music. Download the resource "LMMS Music Project Source File". Follow the instruction in the file and download "Vocal (for LMMS)". Then you can modify the music right inside LMMS.

Another way is to use other audio tools. After downloading the resources "Music", "Instrumental Music" and "Vocal", you can do operation on the audio to remix it. For example, one can add some drum and stuffs to it, or add some effects to it like adding filters or reverb effect or pitch change or something like that. Just do whatever you want with them!

nimi mute pi kalama musi ni li toki seme? | What's the Language of the Lyrics of this Song?

nimi mute pi kalama musi ni li toki pona. :P

It's Toki Pona. It's a minimalist, constructed language invented by jan Sonja. The entire language is made of 123 words! It's rather easy to get fluent on it. I learned this language like two or three months ago. It took me like a month to learn it.

Long time ago I've attempted learning another constructed language Esperanto. I didn't have much success. Mainly because it really takes a bit of time to learn those vocabularies. I had to look up the dictionary from time to time. But Toki Pona's different. Once I learn all those 123 words, it isn't that difficult to understand any Toki Pona text and conversations. Even if I don't completely understand it, at least I'd have an idea on what it's about because I understand every single word. It's just that I don't understand the combination of words.

If you've nothing to do and interested in learning a useless language, Toki Pona is the language to go! Toki Pona community does exist. It's a bit small, tho. Still, it's rather fun to chat in a language that others couldn't understand. That's how do you talk shit about others without drawing any unwanted attention! :P

jan Sate li pali e sitelen tawa ni tan nasin seme? | Behind the Scenes

nimi mute li pona lili. sitelen lukin li pona mute. o lukin e sitelen ni:

A image's worth thousands of words. Take a look on this pic for an overview of how this vid were made:

An image showing the workflow of using the software for the creation of the animation

mi kepeken e ilo mute a! mi wile ala toki e ilo ali. mi toki e musi ona.

I'll not go thru all of the software above. I'll just cover the interesting parts.

kamala musi | Music

mi pali e kalama lili kepeken e ilo "Palette MCT". mi wile kepeken e ilo "Wine" lon ilo "Linux" tan ni: ilo "Linux" li ken ala kepeken e ilo "Palette MCT".

I made the melody and chord with Palette MCT. It's a great free tool for doing chord progression and designing melody. Since I lack knowledge on music theory about chord and stuffs, this tool is very helpful to me. Previously I did chord progression by trial and error. I adjusts each note tediously until I find the one that sounds right. With this software, it helps me to filter out those inappropriate chord immediately. Then I can focus on the one that sounds good.

Unfortunately, it doesn't support linux. I had to run it on wine. And it's the only non-FOSS tool I've used for this project.

Screenshot of part of the music composition software Palette MCT

As you see, there're those musical notes. The pink notes are non-chord notes. The black ones are the chord ones. And the colored rectangles with letters and numbers are the chord chosen for the measure.

This part was done before Global Game Jam 2018. Once again, this music is a enhancement of a piece of music made during my practice for the jam. :P

sitelen tawa | Animation

mi pali e sitelen kepeken ilo "Inkscape". ilo "Inkscape" pana e sitelen, tawa ilo "Synfig". mi pali e sitelen tawa kepeken e ilo "Synfig". mi kama sona e ilo ni kepeken tenpo suno wan kepeken sitelen tawa ni: [sitelen tawa "How to create animation in Synfig (3rd edition)"]. ona li pona mute.

I made the graphic with Inkscape. Then it's imported to Synfig and turned into animation. I learned its basic within a day using this video course: [How to create animation in Synfig (3rd edition)]. Synfig itself is free. But the video course isn't. Still, it's very affordable. If you're interested in learning it and cannot afford it, I'd be happy to buy a copy for you.

Screenshot of the animation software Synfig

ilo "Synfig" li pona. taso tenpo mute la mi pali e sitelen tawa kepeken ona. mi pini pali la ona li pali e sitelen tawa kepeken tenpo mute mute.

Synfig is a great piece of FOSS animation tool. It's easy to use. But it's rather time consuming to make animation with it. After making the animation, it also takes quite a bit of time to render, especially for video that uses the "Curve Warp Layer". It takes an hour and half to render an animation with like 500 frames on my Bay Trail Pentium laptop. The Curve Warp Layer was used to make the swinging arm animation in our video.

tenpo kama la seme li kama? | What's next?

mi pini pali e sitelen tawa ni la mi pali e ilo sona musi.

pali pi sitelen tawa li pona tawa mi. pali ni li musi. taso mi pali e ona kepeken tenpo mute mute. mi jo e tenpo sin la mi wile pali sitelen tawa sin.

The completion of this video marks the end of the break of my Portable Game Console Project. I'll get back to that project soon.

I enjoyed making this animation. The catch is that it takes far too much time to make one. I'll make another one in the future if I have the time.

Portable Game Console Project - Graphic System Implemented!

The graphic system of the portable game console project is implemented! In addition, the clock control and power saving stuffs are also implemented. This blog post focus on Graphic System. I'll save other stuffs for my next blog post.

Graphic System Design

  • 84*48 monochrome. Each byte is 8 pixel grouped vertically.
  • Capability of drawing bitmap, rectangle and text
    • Rectangle: Supports both filled rectangle and hollow rectangle
    • Text: Supports specification of width, height and word wrap
  • Drawing mechanism:
    1. First, The background bounding rectangle is drawn.
    2. Then, the item itself is drawn
      • When they're being drawn, four modes can be selected. They are:
        • NONE: pixel = originalPixelGroup
        • AND NOT: pixel = originalPixelGroup&(~newPixelGroup)
        • OR: pixel = originalPixelGroup|newPixelGroup
        • XOR: pixel = originalPixeGroupl^newPixelGroup
      • Example: Drawing opaque item would be (BACKGROUND_AND_NOT|FOREGROUND_OR)
      • Example: Drawing transparent item would be (BACKGROUND_NONE|FOREGROUND_OR)
  • DMA is used for transferring the image from buffer to the LCD display via SPI
    • This allows the transfer operation to be done while the microcontroller is in sleep mode, saving power consumption.
  • Double buffering support
    • However, each buffer takes 504 bytes. With a total of 4kB of RAM, it may not be wise to do double-buffering

API Calls Listing

  • extern void graphicClearDisplay(bool inverted);
  • extern void graphicDrawText(const struct GraphicText *text, int8_t x, int8_t y, uint8_t mode);
  • extern void graphicDrawImage(const struct GraphicImage *image, int8_t x, int8_t y, uint8_t mode); //Can also draw rectangle with a special pointer address
  • extern void graphicLoadImage(const void buf, struct GraphicImage image); //converts binary from game console bitmap format to the struct GraphicImage
  • extern void graphicSetDrawBuffer(void *buf);
  • extern void graphicSetDisplayMode(enum GraphicDisplayMode displayMode); //Set inverted display and stuffs
  • extern bool graphicDisplay(const void *buf);
  • extern bool graphicIsDisplayReady(void);
  • Structs:
    • GraphicImage: void *image; uint8_t width; uint8_t height;
    • GraphicText: char *text; uint16_t length; uint8_t width; uint8_t height;

Typical Procedure of Using Graphic System

Double buffering isn't used in this procedure:

  1. Wait for the completion of the previous DMA transfer process. Function: graphicIsDisplayReady()
  2. Set a graphical buffer to have stuffs to be drawn to. Function: graphicDrawText(), graphicDrawImage()
  3. Clear the buffer to remove previously drawn image
  4. Draw stuffs to the buffer
  5. Start the DMA transfer process to transfer the buffer to the LCD. Then optionally enter sleep mode to save power
    • Do not modify the buffer until the transfer is completed

Updated sample program

I've modified the sample program that I made for testing storage system, input system and synth system. Now that it's also capable for testing the new graphic system!

The updated sample program draws a lot of stuffs on the LCD, including a background image loaded from a resource in the ROM, a square, and system information like system tick, clock frequency and sleep mode.

In the updated sample program, the drawing mode of the square can be configured. Here's a few photo comparing some of the available the drawing modes:

Four photos showing the comparison of drawing modes

Implemented but Removed Features

Due to flash space constraint, some graphical features were removed. They include ellipse, triangle, line and dot drawing support. :(

We have 16kB of flash in total. Currently the bootloader and library takes 10kB, which is taking more space than the originally planned 8kB. It'd be even worse if these features were enabled. :(

Fortunately, it's still possible to draw those shapes by implementing the drawing function in the game itself.

Other Progress to be Blogged in the Future

I've already implemented the following features. However, these features deserve another blog post. I'll blog about them later.

  • Clock control and sleep mode
  • Removal of EXTIF in favor of using existing function calling convention

What's next?

I'll be working on the following stuffs:

  • Clean up on the code
  • Sound System: Check if additive synth is possible
  • Game selection menu
  • LCD contrast adjustment
  • Find out how to put code on the RAM.

That's it for now. I'll update you guys for any progress! :)

And I guess I'm going to take a short break from this project. I'm going to work on a piece of music!

Portable Game Console Project - Storage System Implemented!

Hey guys! The storage system of my portable game console is implemented!

Storage System Design

  • Each game is a ROM file stored in SD card
  • File system support: FAT16 and FAT32
  • FAT library used: Petit FAT File System Module
  • Up to 256 resources can be packed into a ROM
    • A python script were developed for packing the resources
  • Each resource can be flashed to the microcontroller
    • The first one is flashed upon the game is loaded
  • Each resource can be read by the game
    • It can be used to store static assets (like map of a level in the game)
  • Each resource can be rewritten by the game
    • Can be used for implementing game-saving feature
    • Yes, the save content is saved right inside the ROM.
  • Each resource is aligned to 512 bytes.
  • It isn't possible to seek to an offset of any resources. You have to read/write everything in the resource at once.

Why is the game save content located right inside the ROM?

It seems to be dumb to save the save content right inside the ROM file. I don't really have a choice because of the limitation of the FAT library. :(

My original idea was to have two files. One is the ROM file. Another is the save file. The save file is dynamically created by the game by using the API Calls.

However, when I tried to create the file by using the function pf_write() of the FAT library, it didn't work. Then I looked into the documentation and found out this restriction:

  • "Cannot create file. Only existing file can be written."

Alright. Then I had come up with three ideas. They're:

  • Use another FAT library
  • Pre-create a save file on the SD card
  • Write the save content right inside the ROM

Using another FAT library takes a lot of work. Pre-creating a save file on the SD card would bring quite a bit of nuisance to the player because the player would have to manually paste the save file to the SD card in addition of the ROM file. So it's apparently to me that storing the save file content right inside the ROM would be the solution to go.

Resource Alignment

Since the restriction of the file-writing function is "Cannot create file. Only existing file can be written", everything should be working after saving the content right inside the ROM, right?

It turned out that I was mistaken. When I tried writing something on the SD card, the offset of the content being written was wrong! And some of the content were corrupted by zeros. Why?

After reading the documentation again, I've found out why. The library has a function for reading from file, and another function for writing to file. The function for reading works with any file offset. However, the function for writing only works properly if the seek offset of the file is aligned to 512 bytes. Otherwise, the offset would be rounded down to the closest 512-bytes. In addition of that, the number of bytes written are padded with zeros to 512 bytes.

The solution? Simple! Just align the offset of each resources to 512 bytes and pad them.

ROM File Format

Here's the file format of the ROM of the game console.

Offset      Size    Description
0           3       Magic number
3           1       ROM file format version
4           32      Name of the game. NULL-terminated.
36          32      Author of the game. NULL-terminated.
68          256     Game Description. NULL-terminated.
324         700     Reserved
1024+8*N    4       Resource offset in file. N is from 0~255.
1024+8*N+4  4       Resource length. N is from 0~255. Can be zero.
3072        varies  The content of the resources. Each resources are aligned to 512 bytes.

ROM-packing Python Script

The python script takes a CSV input file and generate the ROM. The CSV file looks like this:

NAME,Name of the game goes here
DESCRIPTION,Description of the game goes here

It's rather obvious what does the CSV do except the last line. For the resource #254, it allocates a resource that contains 1024 bytes of zero. It's useful for allocating space for save file of the game.

API Calls

It's simple. There're only three functions.

  • void storageFlash(uint8_t resNum);
  • uint16_t storageRead(uint8_t resNum, void *buf, uint16_t len);
  • uint16_t storageWrite(uint8_t resNum, void *buf, uint16_t len);

It's rather intuitive. I guess I don't need to explain it here. :P

Sample program

To demonstrate that the storage system is working, I've developed a test program. The program is capable for playing three kind of sound with configurable duty cycle. The function of the buttons are shown below:

  • Up: Reduce duty cycle
  • Down: Increase duty cycle
  • Left: Select the previous sound
  • Right: Select the next sound
  • Button A: Play the sound
  • Button B: Save the sound

Click here and see how does the code of the program looks like. Please notice that this isn't a complete project and it's not buildable without extif.h, which I'm not releasing until the completion of the software.

After saving the content of the file, here's how does the content of the ROM looks like:

Content of ROM showing the bytes 0x55, 0x01 and 0x01

As you can see above, the first byte 0x55 is the magic number. It's used for informing the program that the save data is valid.

The second one is the sound ID. The value of 0x01 refers to the second sound.

The third one is duty cycle. A value of 0x01 refers to 50% duty cycle.

After relaunching the program, the sound and the duty cycle will be loaded. I've tested it. The sound was played correctly after I pressed the Button A. :)

What's next?

I'll be working on the following stuffs:

  • Graphic system
  • Power-saving, clock rate adjustment, etc.
  • Menu for game selection and stuffs

That's it for now. I'll update you guys for any progress! :)

Announcing the Commencement of Portable Game Console Project

Photo of the portable game console

If you know me personally, you'd probably heard of this portable game console. I have been working on this project for almost two months! I decided not to blog about this until now because I wasn't confident about the completion of this project. My main concern was the hardware design of my portable game console isn't going to work at all. Now that I've tested and proven that it's possible to do whatever I need to for this project. Therefore, it's now the right time to announce this project in this blog! :)

Introducing Portable Game Console Project! It is a minimalist, low-cost portable game console. It isn't an emulator of existing game console. It's a brand new game console system. It doesn't have a name yet. Until it's getting named, I'll just call it portable game console.


It's mostly a "because I can" project. I aim to learn stuffs from this project, including drawing PCB board, electronic design, a little bit mechanic design by drawing the case of the console, embedded programming, etc.

This is an open source project. There're already many existing game consoles. I don't think anyone would be interested in getting this one. Commercialization is out of the question. Instead, upon the completion of this project, I plan to do free giveaway of this console (and hopefully someone would be interested in developing games for this console). Waste all of the time! Lose all of the money! Hooray! :P


  • Open source (after the completion of this project)
  • Low-cost
  • Minimalist - single color LCD, 1-bit audio.
  • Open design - Anyone can build their own. The software will be made open source
  • Supports loading games from SD card
  • Cross platform compatibility of games - the API will be ported so that PC, and hopefully web browsers can run the games developed for this console


  • Microcontroller: STM32F030F4P6
  • Graphical interface: Nokia 5110 LCD. 48x84 monochrome
  • Audio playback device: Buzzer
  • Input: 6 push buttons. Left, right, up, down, A and B.
  • microSD card slot (may be replaced with SD card slot in the future. I haven't decided yet)

Development Flow

  1. [Done]Hardware design
  2. [Done]Purchase prototyping material
  3. [Done]Solder a prototype of the circuit on a perfboard
  4. [WIP]Developing the API
  5. Developing Games, mainly for testing the API
  6. Drawing PCB
  7. Making the container case for the game console. Probably by 3D printing
  8. Port the API to PC platforms and possibly browser (Optional)
  9. Small scale production and giveaway

Hardware Wiring

I haven't drawn a schematic diagram. So I'd just write down the wiring of STM32F030F4P6 to other hardware here. It's enough for anyone to reconstruct the circuit

  • BOOT0 -> GND
  • PF0 -> Button Up -> GND
  • PF1 -> Button Left -> GND
  • NRST -> Vcc
  • V -> Vcc
  • PA0 -> Button Right -> GND
  • PA1 -> Button Down -> GND
  • PA2 -> Button A -> GND
  • PA3 -> Button B -> GND
  • PA4 -> Current-limiting resistor -> Buzzer -> GND
  • PA5(SPI1_SCK) -> LCD_SCK and SD_SCK
  • PA7(SPI1_MOSI) -> LCD_Din and SD_MOSI
  • PB1 -> LCD_CE and -parallely-> transistor to invert the signal -> SD_SS
  • VSS -> GND
  • V -> Vcc
  • PA9 -> LCD_DC
  • PA10 -> LCD_RES
  • PA13(SYS_SWDIO) -> JTAG for programming and debugging
  • PA13(SYS_SWCLK) -> JTAG for programming and debugging

Here's how the connection looks like on the prototype hardware:

Photo of the back of the portable game console, showing the soldering trace on perfboard

Software Development Environment

I've never used most of these tools and software before this project.

  • Compiler: gcc ARM cross-compiler
  • IDE: SW4STM32
  • Code template generator: STM32CubeMX
  • Library: STM32F0 LL driver (uses less Flash memory compared with HAL driver)
  • USB-to-JTAG adapter
  • Hardware prototype of the portable game console

OS Design

STM32F030F4P6 is the microcontroller of this portable game console. It's a low-end ARM Cortex-M0 microcontroller.

It has 4kB of RAM and 16kB of Flash. In our design, the flash memory is split into two parts. The first part is the bootloader. The second part is the application (game). The bootloader contains the implementation of the API for accessing the LCD, sound system, buttons and SD card. The game is a playable that calls those API functions to access the hardware function of the game console.

The RAM is also split into two parts. A small part is exclusively used by the API. The other part is available for the application.

In addition to that, the bootloader will show a menu for the user to pick a game stored in the SD card. When the game is chosen, self-flashing will be performed to the application part of the flash memory. Then the BX instruction will be called to set the program counter to the starting point of the application, which will caused the game to be launched. There's, unfortunately, no protection of access. Although there's no API for that, it's technically possible for the application to, says, implement their own function to erase the SD card.

The bootloader will probably take 8kB of flash and very small amount of RAM (like 100 bytes).

Implemented API Calls

The API call can be made by using a software interrupt. There's a fixed-address, 8-bytes storage inside the RAM. The API can be accessed by the application by first manipulating the content of the RAM of that address, then trigger a software interrupt. The interrupt EXTI line 15 is used for software interrupt. This design is inspired by int 80h of unix kernel.

Keys Input System

Three functions are available:

  • void keysSetDebouncePeriod(uint8_t milliseconds);
  • uint8_t keysGetPressedState();
  • uint8_t keysGetJustChangedState();

keysSetDebouncePeriod() is used for setting the software debounce period. The longer it is, the slower the reaction time, and less noisy the response is.

For keysGetPressedState() and keysGetJustChangedState(), each bit represent the state of a button.

For keysGetPressedState(), pressed button would have its bit set to 1. The non-pressed one would be 0.

For keysGetJustChangedState(), pressed button that had changed would have its bit set to 1. Otherwise 0. For example, if the keysGetJustChangedState() of a button is 1, and the keysGetPressedState() is 0, that implies that the button was just released. After calling this function, the justChanged state of all buttons would reset to zero.

Sound Synth System

The sound system consists of two identical 1-bit synthesizer. One of them is for sound effect (foreground), another of them is for background music. Only one of them can output sound at one time. Depending on the configuration, it's possible to pause the background music while the sound effect is being played, or have the sound effect played during its effective duration, replacing the part of background music.

The sound synth has the following capability:

  • Playback duration (10 millisecond per step, range: 0-2.55 seconds)
  • Frequency (32-suboctave frequency scale)
    • Frequency sweep control: shape: saw, triangle, bipolar, noise; repeat: yes/no; fast sweep: 1x/256x
  • Duty cycle (Possible value: (1-1/(n^2)) * 100%. n is a value between 0 and 15. Example: n=2 would be 75%)
    • Duty sweep control: shape: saw, triangle, bipolar, noise; repeat: yes/no
  • Fast sweep mode: reduces the duration of each sweep step to 1/4 of both frequency and duty cycle.

In addition, a musical playback format is defined for this game console, which enables the application to play music by calling an API function.

Sound Synth System: Frequency

Piano uses a 12-suboctave scale, which means that each octave is divided by 12 notes.

However, in this game console, a 32-suboctave frequency scale is used instead. It means that each octave is divided by 32 notes. This game console is able to generate more fine frequency compared with piano, mainly useful for sounds effects. Since 32 isn't divisible by 12, some of the piano notes has to be substituted with a close enough frequency. Fortunately, this is not a problem because "Humans can notice a difference in pitch of about 5 to 6 cents" according to a paper by Beatus Dominik Loeffler. For a 32-suboctave system, the maximum error compared with piano note would be 64-suboctave, which is 1/64*100=1.5625 cent of error, which is far less than 5~6 cents. I've also personally listened to a piece of music played on the game console. It sounds good. So the frequency error isn't a problem. :)

During frequency sweeping, the 32-suboctave is further subdivided into 16 frequencies, forming 512-suboctaves. Each step of sweep would increase/decrease the frequency by a 512-suboctave until the final frequency is reached.

The frequency of the sound to be played is 2^(5+n/32) Hz, where n can be any value from 0 to 255.

Sound Synth System: Duty Cycle

Generation of tone requires vibration of audio signal. For a 1-bit synth, the duty cycle determines the duration of the signal to be LOW or HIGH. Let's take a 1000Hz sound wave as an example. It takes 1ms to complete a period. If the duty cycle is 50%, 0.5ms would be spent on LOW, and another 0.5ms would be spent on HIGH. If the duty cycle is 75%, the signal would be LOW for 0.25ms, and HIGH for 0.75ms.

Although this synth is 1-bit synth, it's possible to control the volume of the synth by using duty cycle. That's because the duty cycle affects the RMS value of the waveform. The closer the duty cycle to 50%, the louder the sound is.

The duty cycle can be adjusted (1-1/(n^2)) * 100%, where n can be any value from 0 to 15.

Unlike frequency, the duty cycle sweep does not have any subdivide mechanism.

Sound Synth System: Music Format

The format is simple. It's a mix of command and data. Data starts with a non-zero byte. Command starts with a byte of zero. It has 6 modes. They are STOP, DF, DCF, FULL, FULL->DF, FULL->DCF.

  • STOP: end of music. No more command to be accepted.
  • DF: Two bytes. duration, frequency. Use the previous values for other parameters
  • DCF: Three bytes. duration, initial frequency, final frequency. Use the previous values for other parameters
  • FULL: Seven bytes. duration, init freq, final freq, freq sweep period, init duty cycle(half byte), final duty cycle(half byte), duty sweep period, sweep control flags
  • FULL->DF: Same as FULL. However, after playing this FULL musical note, it'll get back to DF mode
  • FULL->DCF: Same as FULL. However, after playing this FULL musical note, it'll get back to DCF mode

To switch mode, set the first byte (duration) to 0, then set the second byte to the command ID. The command ID for STOP is 0, DF is 1 and so on. Here's an example of a piece of music:

uint8_t musicArray[] = {
0, 4,  //Switch to FULL->DF mode
20, 97, 97, 0, 0x11, 0, SYNTH_FREQ_SWEEP_TRIANGLE|SYNTH_DUTY_SWEEP_REPEAT_ENABLE, //Play a sound with full parameters specification and switch to DF mode
20,102, //Now that we're in DF (duration+frequency) mode. We play a sound in DF mode
65,105, //Play another sound in DF mode
20,97, //ditto
20,105, //ditto
45,102, //ditto
100,97, //ditto
0, 0 //Switch to STOP mode. It's like a NULL character for string.

Sound Synth System: API Functions

The following functions are available:

  • void synthPlayOne(bool foreground, struct SynthData synthData);
  • void synthPlayCommand(bool foreground, uint8_t *command);
  • void synthSetFgFreezeBg(bool freeze);
  • uint8_t synthGetStatus();

Sound Synth System: Demonstration

Here's a piece of recorded music generated with the game console by using the music format:

Unimplemented APIs:

  • [Research performed, but not implemented]SD card interfacing system
  • [Research performed, but not implemented]Graphical system
  • Misc (e.g. changing the clock rate, sleep, etc)


I'll continue be developing this game console. It'll probably be completed some time in 2019. I'll update you guys about any progress! :)

International Asynchronous Rock Paper Scissors Tournament 2018 - Site's up!

March 28, 2018, 12:28 p.m. Behind the Scenes Product Release RPS

Hey guys! It's that time of the year again! Our awesome Rock Paper Scissors Tournament will soon be commencing, and its website's ready! :-)

This is supposed to be an annual event. However, it didn't happen last year because the hoster failed to set up the site. Therefore, we're going to host this event on our own this year.

We'll be issuing certificate of participation of all of our participants! If you're looking for a degree or a job, certificates are proven to be helpful on building your portfolio. That's why we're issuing them! Put that in your resume and we promise that'd secure your job offer or degree offer!

This year we've also made the captcha easier than it used to be! We believe that it'll invite some bots to join our event. That'd make our tournament even more competitive than it was! :)

Event details:

  • Duration: 1st April 2018 00:00~23:59 UTC
  • Location: Internet
  • Participation Criteria: Anyone from any country
  • Website for the tournament:

Technical details

The front-end is almost the same as the old one. Semantic-UI were used. But we've changed the back-end from Django to Flask. Flask takes much less RAM compared with Django. And it takes much less efforts to develop stuffs using Flask, especially for trivial projects like this one. I guess Django is better suit for larger scale websites.

I guess I'll be opensouring this thing. But I'm too lazy to do so right now. Maybe later. :P