sunnuntai 1. marraskuuta 2026

NES9500: a NES emulator for the Nokia 9500 Communicator, from scratch

The Nokia 9500 has been my main phone since the 9210 started shutting itself down at random. It has a 640×200 screen, a full keyboard and a 150 MHz ARM9 running Symbian OS 7.0s with Series 80 v2.0 on top. In other words, it's a pocket computer that badly needed a NES emulator.

There wasn't a good one, so I made one: NES9500. The code was written by Claude (Anthropic's AI). I built it on my Windows 2000 VM with the S80 v2.0 SDK, tested it on the phone, sent back logs and screenshots, and complained. A week later, it plays Super Mario Bros. 3, Blaster Master and Battletoads at full speed with sound, and the whole thing fits in a 42 KB SIS file.

This post is about how it works. It gets geeky.

A core from scratch

NES9500 isn't a port. It doesn't contain code from FCEUX, Nestopia, InfoNES or anything else; every line was written for this phone. The SDK's compiler is GCC 2.95, so everything is C89, with declarations at the top of each block like it's 1999. That's fitting, since it nearly is.

FileLinesWhat it does
cpu.c4126502 interpreter: one big switch over all 256 opcodes, unofficial ones included
ppu.c637Scanline renderer using loopy's scroll model, writing straight into the framebuffer, already scaled
apu.c539Two pulses, triangle, noise and DMC, non-linear mixing, 22050 Hz mono
nes.c569Memory map, iNES loader, mappers, frame loop, save states
nesmain.c1253ROM list, menus, settings, key config, frame pacing
epocsys.cpp971Native Symbian layer: framebuffer, keys, menus, files
epocaudio.cpp345Audio thread feeding CMdaAudioOutputStream

The native layer comes from my earlier Doom9500 and Quake9500 ports. It skips the Symbian UI framework entirely: it grabs the screen's raw memory address through the HAL (RGB565, 1280 bytes per line), puts its own window group in front so the system keeps quiet, and draws there directly. Menus are drawn with the system font straight into the framebuffer, so the emulator carries no font data at all.

Supported mappers: 0 (NROM), 1 (MMC1), 2 (UxROM), 3 (CNROM), 4 (MMC3), 7 (AxROM), 11 (Color Dreams), 66 (GxROM) and 71 (Camerica). That covers SMB 1–3, both Zeldas, Metroid, Mega Man 1–6, Castlevania 1–2, Contra, Kirby, Battletoads, Duck Tales and a few hundred others.


SMB3 uses MMC3, the mapper with a scanline counter that games use for split screens, like the status bar at the bottom here.

Squeezing 240 lines into 200

The NES draws 256×240, and a TV shows roughly lines 8–231. The 9500 has 200 rows, which doesn't fit. There are two options in the menu:

  • Fit all 224 (the default) drops every ninth line. You see the whole picture, slightly squashed. A precomputed table maps each NES line to a screen row or to −1, and lines that map to −1 aren't drawn at all.
  • Crop to 200 shows the middle 200 lines 1:1. Text is sharper, but status bars at the edges get cut.

Left: fit. Right: crop. Look at the WORLD 1 text in the status bar: crop keeps it sharp but cuts off the bottom of the bar.

Horizontally there are four sizes: 1× (256 px, the true shape when combined with "fit"), 1.5× (384, the default), 2× (512) and full width (640). Scaling happens while each line is written out, so there's no second pass.

The same frame at 1×, 1.5×, 2× and full width.

Whatever the picture doesn't cover gets a wallpaper: background.bmp in the NES folder, or <game>.bmp next to a ROM to give that game its own. The decoder handles uncompressed 1/4/8/24/32-bit BMPs, so whatever Paint saves works. A 5-pixel black border keeps the game picture from bleeding into the wallpaper.

My wallpaper: a living room with a CRT, a lamp and too many VHS tapes. At 1.5× the game covers the TV and the shelves frame it.

The speed hunt

The first build ran straight away and played Blaster Master with sound, which genuinely surprised me. It wasn't fast enough, though, and we went through three rounds of optimisation, each one driven by numbers from the phone's log file.

Round 1 wrote two pixels per 32-bit store, built a per-line sprite list only when OAM changes instead of scanning all 64 sprites on every line, and folded the NMI into the IRQ word so the CPU checks one variable per instruction instead of two. Result: 58–62 fps emulated, about 36 frames drawn per second.

Round 2 went after the CPU. Every opcode fetch now goes through a cached window onto the current ROM bank:

#define FETCH(a) ({ \
    const u32 fa_ = (a); \
    u32 fv_; \
    if (fa_ - pcwin < 0x2000) \
        fv_ = pcbase[fa_]; \
    else if (fa_ >= 0x8000) \
    { \
        pcwin = fa_ & 0xe000; \
        pcbase = cart.prgbank[(fa_ >> 13) & 3] - pcwin; \
        fv_ = pcbase[fa_]; \
    } \
    else \
        fv_ = nes_read(fa_); \
    fv_; })

The unsigned subtraction folds both bounds checks into one compare, and pcbase is pre-offset so the hit path is a single load. It's a GCC statement expression, which GCC 2.95 already supported. Round 2 also added a log line that turned out to be the most useful thing in the project:

Frame time: drawn 21-29 ms, skipped 0.2-4 ms

A frame where the picture is drawn costs about 25 ms. A frame where it's skipped costs a couple. So the 6502 is cheap; the time goes into drawing. The CPU work had been aimed at the wrong target all along.

Round 3 therefore rebuilt the drawing. The PPU no longer draws a line whenever the CPU crosses a scanline. It catches up lazily: nes_frame() only records how far the beam has got, and lines are drawn in a batch when something visible is about to change (a write to a PPU register, a mapper bank switch, OAM DMA) or at the end of the frame:

#define PPU_SYNC() do { if (ppu_done <= ppu_now && ppu_done < 240) ppu_sync(); } while (0)

Most games touch the PPU only during vblank, so most frames are drawn in one tight loop that doesn't fight the CPU emulation for the 9500's small cache. Each line is built in a cached buffer and then blasted to screen memory four words at a time, because the 9500's screen memory is slow to write and much prefers burst stores:

"1:\n\t"
"ldmia %1!, {r4, r5, r6, r7}\n\t"
"stmia %0!, {r4, r5, r6, r7}\n\t"
"subs %2, %2, #1\n\t"
"bne 1b\n\t"

Games always run at the correct 60 Hz (or 50 for PAL), and automatic frameskip decides how many frames are actually drawn. Blaster Master typically draws 30–45 frames per second and Battletoads 23–41, and it feels smooth. The emulator sleeps only when it's a whole 16 ms system tick ahead, since Symbian's timer can't do anything finer.

A Symbian bug found along the way

The input code polls a window server event request (iEventStatus) instead of blocking on it. It turns out that if you check the status without calling User::WaitForRequest, the completion signal stays on the thread's semaphore. They pile up, and every later wait in the program spins straight through them. NES9500 now consumes the signal properly.

Sound

The APU runs in step with the CPU and writes 22050 Hz mono samples into a ring buffer. A separate audio thread pulls from the ring into CMdaAudioOutputStream. The first test logged 1 underflow in 4617 chunks. The channels are mixed with the NES's non-linear lookup tables, and there's a DC filter and a low-pass, so it sounds like a NES and not like five square waves added together.

The volume has 8 steps that halve at the quiet end: 1, 2, 4, 8, 16, 32, 48 and 64 out of 64, roughly 6 dB apart. The original four steps bottomed out at 25%, and at 25% the 9500's speaker makes it clear to everyone on the bus that you're playing Mario.

PAL, NTSC and Finnish ROM dumps

I'm in Finland, so a lot of my carts are PAL. PAL runs 312 lines per frame, 106.5625 CPU cycles per line, and has its own APU period tables. NES9500 picks the region from the iNES header or from the file name: (E), (Europe), (Scandinavia) and so on mean PAL. You can override it in the menu.

Save states the lazy way

Everything the emulated machine knows lives in one struct, NesState, with no pointers in it: CPU, PPU, APU, mapper registers, RAM, SRAM and CHR RAM. A save state is a memcpy. After loading one, mapper_sync() rebuilds the bank pointers from the mapper registers. Chr+S saves, Chr+L loads, and a small square flashes in the corner (green for saved, blue for loaded, red for no state). Chr+F toggles fast forward.

Keys, turbo, and 16 sprites per line

The Communicator keyboard has plenty of keys. The defaults are WASD, the arrow keys or the rocker for the D-pad, Z/K/Ctrl/rocker press for A, X/L for B, Enter or Space for Start, and Tab or Shift for Select. I and O are turbo A and B, with fast, medium and slow speeds (30, 15 or 10 presses per second).

Every button can be remapped from the menu. The emulator keeps a 256-entry table from hardware scan code to pad bits, and turbo uses bits 0x100 and 0x200, above the eight real NES buttons, so they get masked off before the game sees them. Remapping waits for the key to be released before returning, so the key you just assigned doesn't fire into the game. The table is saved in nes9500.cfg as plain scancode=bits pairs.

A real NES can show 8 sprites per line, and games flicker sprites to get around that. NES9500 shows up to 16 by default, which removes most of the flicker. "Sprites per line: 8" restores the real behaviour for the few games that deliberately hide things behind the limit.

The feature that didn't make it

The 9500's screen is wide, and a NES nametable holds more picture than the 256 pixels you see. So we tried a "wide view" that drew 64 extra background pixels on each side from the offscreen part of the nametable.

It failed for a fundamental reason: games write each new column of scenery into the nametable just before it scrolls into view. The area beyond the visible edge isn't the upcoming level; it's leftovers. In Blaster Master the extra columns flickered and showed scenery from random places. After a couple of fixes that made it only somewhat less bad, I removed the whole thing.



A real screenshot from the phone: Blaster Master, taken while chasing the wide-view bug. The other images in this post come from the same emulator core running on a PC with the phone's 640×200 layout, because it's easier to get the timing right with scripted input.

The launcher and the icon

The emulator itself is a plain .exe. To get an icon on the Desk, there's a tiny launcher app. It draws a black screen, starts nes9500.exe from a CIdle callback right after startup, and exits. If the emulator can't be found, it stays open with an error and Start/Exit buttons.

Something worth knowing if you make S80 v2 apps: the AIF Builder offers 24×24, 32×32 and 48×48 icons, but the 9500 actually uses 64×50 and 25×20. I checked the AIF files of the built-in apps: 33 have a 25×20 icon and 24 have a 64×50 one. You can skip the AIF Builder entirely and put the icons on the AIF line in the .mmp:

AIF NES9500.aif . NES9500Aif.rss c16,1 appicon_64x50.bmp appicon_64x50_mask.bmp appicon_25x20.bmp appicon_25x20_mask.bmp

In the masks, black means icon and white means transparent. I drew the icon with green corner pixels to mark where it should be transparent, and a small Python script turns exactly that green into the mask. The black tile stays, with rounded corners.

What's missing

  • MMC2/MMC4 (Punch-Out!!, Fire Emblem)
  • MMC5 (Castlevania III)
  • The Konami VRC mappers and the Famicom Disk System
  • ARM assembly for the 6502 core, if I ever want more speed. Honestly, it's already fine.

Games go in D:\NES\ on the memory card (or C:\NES\). Battery saves and save states sit next to each ROM, and nes9500log.txt records what the emulator thinks of your phone, including the 6502 program counter if something crashes.

Not bad for a 2004 phone with a 150 MHz CPU. Next up: Punch-Out!!.



torstai 3. marraskuuta 2016

How-to: a physical keyboard for Huawei P8 Lite


Original post in Finnish HERE.

As you may have noticed, I am so tired of the fact that the phone models with a physical keyboard are non-existent. Well, now that we, the power users, just have to deal with it, I decided to make my own keyboard. It's not very expensive (about $25 currently) and I really recommend making this if you're that guy who likes to hear and feel the nice clicks. It's going to be a big, ugly monster if you ask a common slab user, but for us it's actually not bad, even though I'm the one saying this :D

I chose P8 Lite because it has the camera in the corner so that the cover doesn't block the view at all. I have a Samsung Galaxy S5, but the problem for that model is that the camera lens is in the middle so it's not a good option if you like to do photographing.

What you need:
- Dremel or similar small hobby drill
- glue
- a screwdriver

What you'll need to buy:
- an iPhone 6 keyboard cover - $19.62
- Huawei P8 Lite's cover x 2 - $5.50 total
(optional: keyboard stickers for your local layout, I use Scandinavian)


So let's begin. Tear the sticker off from the cover.


Remove the screws and then the phone holder part. In the image below the keyboard part is already fitted to the P8 Lite cover.


Now you can see that the cover is a wee bit taller than the keyboard part, so now it's time to use the Dremel to saw a few millimeter off.




Now fit the iPhone's holder part to the P8 Lite's holder part (not the one you just used the Dremel on) and drill holes to the same places that the iPhone's holder has. Also, remove the aluminium backplate from the P8 Lite's cover.


Clean the drilled holes a bit and modify them so that the screws sink in there nicely. If the screws you removed are too short, there are some longer screws in the keyboard part which you can take out and use. You probably will have to saw them a bit after.


Now take some glue and put a few drops of it in the lower P8 Lite cover.


Then you just fit the keyboard to the lower cover and there you go!


You can use this method for lots of other phones as well.

maanantai 21. maaliskuuta 2016

WhatsApp ending support for S60

It's a sad year. WhatsApp has announced that they will drop the support for marginal operating systems after 2016. Marginal OS's include S60, of course. I've been using WhatsApp on my Nokia E90 for a while now and while it's not fast, it functions well and there's nothing wrong with it.

Now I think it's time to move on... I decided to get myself a cover with a keyboard for my Samsung Galaxy S3. Actually two of them.

Image (c) dhgate.com


Image (c) tinydeal.com


And on top of that I also got myself A Motorola Droid 4 for just 60 €. We'll see how these gadgets manage to do the job in the hands of a long time Communicator user.

Image (c) Androidcentral.com

Too bad the Nordic keyboard layout is missing from all of today's devices, but I think I can draw Ä and Ö characters on the keyboard with a white marker.

BTW. Check out this FB page. It lists comments and yearnings about QWERTY phones.

https://www.facebook.com/qwertyless/

perjantai 8. tammikuuta 2016

Physical QWERTY phones are wanted!

I just found the Concept-phones.com website which lists imaginary phones found all over the web from different people. Once I found the site, I browsed it enthusiastically, and one of the best devices there is Omar Piela's Lumia Communicator concept. The satellite phone function aside, it's a very nice concept. I went and praised the device on the website and on YouTube.

There's also an iPhone Communicator, which is also quite nice.

Also the Nokia E7 form factor has been an inspiration for at least one QWERTY phone:

Maybe the time for Communicators will come once again: it's clear that people still want them and there is a huge demand of such devices. A lot of business users and the majority of previous power users are fed up with the current devices. Check the comments section of this article, for example.

"I would gladly fork out a nice hunk of change for a high spec android cell with a brilliantly made QWERTY keyboard. As someone who travels frequently and has family and friends scattered all over the world, texting is by far, much cheaper than calling. Hence, I won't buy an android cell if it doesn't have a physical keyboard. Not only is is more accurate but I can type touch with a physical keyboard. This is very handy on the go."
"MAKE A HIGH QUALITY QWERTY KEYBOARD PHONE I'M TIRED OF FULL TOUCH SCREEN PHONES- LG G2 owner."
"With 9 out of the top selling 10 Android apps being games, it seems that those of us who want to use our phones as tools for increasing our efficiency are a minority. Most people use their phones as time wasting devices optimized for playing "irate poultry" and upload pictures of high school drunks to Instagram.
I suspect that's the reason for the lack of qwerty-phones."
The bolded part is the problem. Manufacturers don't care about the power users any more. Phones are made for the masses, the irate poultry that doesn't do anything useful with their phones.

There is no company that makes quality phones for power users any more. It's a sad fact, but hopefully it will change one day.

Blackberry makes QWERTY devices, but the keyboards are ridiculously small and cumbersome to use. Why can't they make a full QWERTY clamshell like the old Nokia 9500, for example?

Meanwhile, all we can do is to keep on believing and use our old QWERTY phones. I'm currently using Nokia E90. It's a nice device, although S60 isn't as good as the old S80. My current dream would be Nokia E90's form factor, and inside there would be Ubuntu Mobile or Android. Like this.

Ubuntu on a Nokia E90 Communicator. That would be nice.

sunnuntai 26. heinäkuuta 2015

Commander - a new communicator

Five years of silence is broken now.

Symbian is dead, but I still miss communicators, they were the best devices ever. That's why I've been designing my own dream phone, a new communicator which uses Android based UI. Its name is



Commander.







Commander's UI mechanics are exactly the same as the most powerful phone - Nokia 9210 Communicator - had.

Check it out and please let me know what you think!


http://symbioosi.net/commander/index.html



keskiviikko 20. tammikuuta 2010

Come and establish a company in Finland... OR NOT!

I've been thinking and weighing some facts about establishing a company here in Finland. Currently the deal goes like this:

- if you make profit, 26% of it goes to taxes (government and municipality)

- you have to carry all the risks on your shoulders

- if you don't make enough profit, neither the government nor the municipality is there to help or support you, you'll have to make it on your own

- you still have to pay the place's rent, the water + eletricity + internet bill and all other expenses

- you'll also have to LIVE with the money you have earned, which sometimes may be negative


Now the government (more specifically the Green League's representative Anni Sinnemäki) is planning on adding the taxes so that instead of paying the good ol' 26%, you'd have to give 47% away of your earnings. And this new 21% must be given from the money you pay yourself to be able to LIVE. And it is taken only from the companies that aren't listed in the stock (i.e. small ones).

So, if you are planning on establishing a company in Finland, I very strongly recommend not to. This is nothing but a tax and bureaucracy paradise. The government has been recommending to establish a company for a long time, but this time it clearly shows that some of them want to add the difficulty of running a company to new levels.

There are going to be lots of quit enterpreneurs if the law passes through and takes effect. If you make profit in Finland, the government and the municipality want some half of it, but if you fail running it, you'll be punished by months without money (normally you'd get the joblessness support ~400 € / month minimum) after declaring bankrupcy. And you'd still have those unpaid expenses in your account. No wonder Finnish people commit suicides so often ;)

maanantai 26. lokakuuta 2009

My-Symbian.com-vetoomus / My-Symbian.com petition

ceroberts75 My-Symbian.com-foorumilla teki vetoomuksen sivuston puolesta Nokialle luettavaksi, eli kaikki jotka ovat sivuston puolesta, kannattaa kirjoittaa koska Nokian touhussa ei mielestäni ole mitään järkeä kohdella sivustoa kuin roskaa, kaiken sen jälkeen mitä ylläpitäjä on tehnyt Nokian ja Symbianin hyväksi. Kirjoitin itsekin vetoomuksen, mutta kommentistani taisi tulla liian pitkä, koska se ei näy allekirjoituksissa.


http://www.ipetitions.com/petition/my-symbian/index.html


ceroberts75 on the My-Symbian.com forum created a petition on behalf of My-Symbian.com to be read by Nokia, so everyone who is on the site's side, I ask you to sign the petition because there's really no sense in Nokia's way in which it treats the site and the administrator. Especially after what the site has done for Nokia and Symbian - promoted every Symbian device out there, but Nokia just ignores it. I signed the petition but my comment was probably to large so it doesn't show in the signatures. But here it is.


"This is truly the best Symbian site there is, and I really can't understand why Nokia treats the site like cr*p. Michal is always the last one who gets the review phones (or not at all), like the N900, which were given to 300 people at the Maemo Summit, but there's no unit left for him although he's been constantly asking Nokia for months if he could get one. 

My-Symbian has been on the net before Symbian was even born, and has ALWAYS promoted Nokia's products from the GEOS Communicator times, and has brought thousands of new customers for Nokia and has kept a thousand more as Nokia users. Somehow Nokia doesn't see it and leaves the site out of link lists (like in the case of Nokia Conversations), and the Maemo Marketing Manager doesn't even respond to his e-mails regarding a better N900 review phone than the one Michal had to obtain unofficially, with broken/ missing radio chip!?

Nokia acts very immaturely towards My-Symbian and Michal, and I'd like to hear a reason for this."



http://www.ipetitions.com/petition/my-symbian/index.html