简体中文 / [English]


NJU ICS PA (Building Qemu-Lite from Scratch) — A (Fake) Speedrun Report & Reflection

 

This article is currently an experimental machine translation and may contain errors. If anything is unclear, please refer to the original Chinese version. I am continuously working to improve the translation.

Among freshmen in the Computer Science Department at Nanjing University, there’s a mysterious legend that’s been passed down for generations. Its name? ICS PA.

In reality, it’s a challenging yet well-designed course project for the sophomore-level course Computer System Fundamentals.

The goal is to build a “lite version” of QEMU — called Nemu — implementing core components such as ALU, FPU, x86 instruction set, protected mode, memory cache / paging / segmentation, and peripheral I/O. Then run some simple programs on it. (Of course, many complexities are simplified — don’t expect to boot a modern OS…)

The required materials and guidance are bundled here: https://pan.lyc8503.net/Public/%E5%8D%97%E5%A4%A7/PA/ICS.rar

Also available on GitHub: https://github.com/ics-nju-wl/icspa-public-guide

At first glance, it seemed like a massive workload — especially considering how complicated the x86 instruction set can be…

But when a classmate asked me to give it a try, I happily accepted this side quest, and speedran through the first playthrough.

Here’s my lab report game completion log.

Reflections

TL;DR: Truly a high-quality assignment. Anyone interested in the x86 instruction set or computer architecture should definitely give it a go.

I started working on this PA on the evening of March 14, 2022, and found it instantly fascinating. The more I coded, the more addicted I got (lol). Thanks to the pandemic shifting classes online, I had plenty of uninterrupted time to focus. I pushed through and finished by early morning on March 19 — completing my first full run of the PA. Four days to finish a semester-long project.

The actual workload wasn’t as heavy as expected. The PA provides a solid framework and clear guidance, complete with test cases for each part. Most of the work is essentially “fill-in-the-blank” coding + repeatedly checking the 80386 Manual. This significantly reduces debugging time and tedious boilerplate labor.

Computer history isn’t long, but it has evolved rapidly. Tech stacks come and go at breakneck speed. Yet the Turing machine model that underpins the entire system remains unchanged. Getting a hands-on peek into low-level computer principles — retracing the steps of pioneers — and seeing how old-school hardware/software still echoes in today’s systems is a surprisingly fun and enlightening experience.

Well-designed assignments like this one can accelerate learning and skill development. Unfortunately, such assignments are rare. Too often, lectures are just PPT readings followed by random homework problems. I wonder — in this age of abundant online resources, are the teachers and TAs who still design comprehensive projects the more dedicated ones?

Completing the PA taught me a few things:

Unit testing is crucial, especially in C. Writing test cases might be boring, but skipping them when building your own OS leads to one tiny bug snowballing into a chain of bizarre bugs — then spending a whole day debugging a single issue. Likewise, use assert liberally to catch errors early.

I gained a deeper understanding of the x86 instruction set. Let’s be honest — I’d never have read the 80386 manual cover to cover on my own. But coding? That’s fun.

Self-learning, research skills, proper tool usage, etc. I already had those — otherwise, how could I finish so fast? (At most, this was just extra practice.)

Pitfalls I Fell Into

PA0 (Setting up the environment can kill 90% of your enthusiasm for learning something new)

The host requires a 32-bit machine. I casually installed Debian 11 i686 (Flag), only to find out CLion and VS Code won’t run on 32-bit systems… CLion’s Remote Host feature requires CMake, but the project uses Makefile… The PA officially recommends vim, but I’m not used to it. Sublime Text works, but the VNC experience is terrible.

After some painful deliberation, I settled on mounting the remote disk locally and using CLion’s Lightedit Mode to write code.

PA1-1 & PA1-2 (Registers / ALU)

No issues — just read the docs and code along. Smooth sailing.

PA1-3 (FPU)

We were not allowed to convert to float and compute directly. Got stuck for an hour on normalized floating-point numbers because of that implicit leading ‘1’.

PA2-1 (Instruction Set)

First, you need a custom objdump for debugging, but the provided binary was incompatible…

Thankfully, the TAs provided helpful macros and a solid framework. Otherwise, we’d be copy-pasting like crazy. As the PA document says: This method of massive code duplication is fast and easy to understand, but leads to bloated code. Worse, if we discover a tiny bug in the original add_r2rm_b function after copying it everywhere, we’d have to do it all over again. Clearly, this approach is Stupid with little Simple — violating the KISS principle.

Using the framework still had its traps — like forgetting to call operand_read, copy-pasting without changing the right parts, or getting stuck on special-case instructions due to not reading the docs carefully…

Overall, PA2 involves implementing many instructions and is the most tedious part. But seeing it finally run is incredibly satisfying.

PA2-2 (ELF Loader)

The PDF gives clear instructions — passed with ease. PS: Finally introduced the kernel!

PA3-1 (Memory L1 Cache)

Wrote everything, all test cases passed. (Flag)

PA3-2 (Protected Mode)

Since we’re working in flat mode and not simulating privilege levels, even if there’s a bug, I wouldn’t know. Passed test cases for nothing.

PA3-3 (Memory Paging)

Now karma came back to bite me…

This part wasn’t hard in theory, but first — because I used Debian 11 (gcc-10) (Flag reclaimed) — strange things happened. With the -Ttext=0xc0030000 compilation flag, objdump confirmed that the bss section was indeed above the code segment. But during runtime, global variables were getting addresses like 0x0005xxxx. Then the TA-provided kernel crashed. Spent three grueling hours debugging before finding the root cause… Tried to reproduce with my own program, but couldn’t — possibly due to something in the Makefile.

Then, my L1 Cache implementation was flawed (Flag reclaimed again). Worse, the PA3-1 test cases weren’t strong enough, so it still passed. This caused the kernel to panic when calling mm_malloc. But the TAs didn’t provide mm_malloc‘s source code — impossible to debug.

Another three hours of painstaking elimination later, I finally traced it back to the L1 Cache — a write-through misjudgment turned cache misses into false cache hits…

Eventually, I passed.

PA4-1 & PA4-2 (Interrupts / I/O / Memory Mapping)

Most of the hardware simulation code was already provided by the TAs. Just implement a few commands according to the manual, and you’re basically done.

Mission Complete! ✿✿ヽ(°▽°)ノ✿

Some Rants

  • I used to complain that QEMU without KVM is too slow — until I wrote my own emulator and realized mine is billions of times slower. (
  • Although everyone says this PA is super hard, compared to building everything from scratch by reading manuals and coding blindly, it’s basically hand-holding. Like a full-on babysitter.
  • The x86 instruction set is insanely complicated… Hats off to Intel engineers. (Odd focus, I know.) But that still doesn’t excuse being stuck at 14nm+++++++++++.
  • I originally wanted to port my own OS onto this emulator, but too many features are missing — couldn’t get it to run. So I ghosted it. If I had managed it, bugs would’ve doubled — chasing one bug from user program → kernel → CPU? That’s a week-long nightmare.
  • The most annoying part was PA2, but the longest debugging session was in PA3 (memory-related). Suddenly reminded me of weird instability issues when building PCs — probably memory-related too. There should be a memtest in the test suite.

This article is licensed under the CC BY-NC-SA 4.0 license.

Author: lyc8503, Article link: https://blog.lyc8503.net/en/post/nju-ics-pa-season1/
If this article was helpful or interesting to you, consider buy me a coffee¬_¬
Feel free to comment in English below o/