• Home
  • Blog


Alternative Version





November 15, 2025

Second Reality++

  • by Jean-Sebastien Royer

C++ version of "Second Reality by Future Crew"



After seeing this post on X, I forked the project and started Second Reality++.

I've reworked Second Reality Win32 port from mixed C / C++ / Pascal / ASM into modern, cross-platform C++ targeting Windows (x86 & x64), Linux, and WebAssembly.

For this port, I also did an experience where I tried for the first time: Vide Coding . . .

If you would rather bypass the port detail, click the button below to visit the main page and grab executables, view the source code on GitHub, try the browser build, or watch YouTube demos for Windows, Web, and Ubuntu.

SecondReality++ Main Page

VIBE Coding - ChatGPT



I don't know why but at the beginning of this conversation, ChatGPT replied to me with the same level of coolness as John Connor in Terminator 2. I don't know if it was because of the code base which is from the DOS era, demoscene or graphics software rendering but it was interesting.

Also I know that I cannot send all the files and code in one shot because it will clearly get confused so I tried to be minimal by sending 1-2 files or some chunk of code.

It was expected... I did a lot of back and forth to be able to get the code working correctly with this work flow / process. I also simplified some parts of the code because ChatGPT got very confused and lost over time. Some translations took multiple days and I reworked a lot of parts.

My initial goal was to send the code to ChatGPT and it will be able to translate correctly. Some parts took 50 iterations, even 100 iterations and more but in the end, it worked.

One thing that pissed me off is the way it replied... It always wrote that now the translation, fixes or code are fine and work perfectly!

Here are the tiny list of "working" code reply that often didn't work:
- Why this should now match exactly
- Why this fixes the....
- What this nails (vs. the versions you tried)
- Below is a C version that matches the ASM byte-for-byte by reproducing:
- Why this fixes your mismatch
- Why this should finally match
- Why this should now match the ASM
- You're right—....
- Why this matches your ASM
- Why this fixes your crash
- You're right — let's stop the back-and-forth and give you a single, working, ...
- Here is a 100% C translation of the two routines, faithful to the original code.
- Here is a 100% C translation faithful to the original code.
- It exactly reproduces the flow.
- You’re right — let’s stop zig-zagging.
- You’re right — I broke it.
- ...


So I saw a lot of shuffling phrases above during the port of this project. A lot over and over and my trust level went close to an epsilon value...

I know the codebase is low level and sometimes it struggles a lot like asm macro where I need to update and replace manually.

Also, the way the codebase handles the stack: classic "push ebp / mov ebp", naked, multiple pops, ...
For that, I need to rewrite the enter/leave stack type functions.

Sometime, it will create 2 variables from the same register and threat them differently:
uint32_t EAX = 0; // fraction 32-bit
uint16_t AX = 0; // entier 16-bit (signe)

JS: no it's not that, look at ng_pass3. do you know that di register is a sub register of edi? inside the function ng_pass3, you created 2 variables edi and di...
ahh—good catch. you’re totally right: in the ASM, DI is the low 16 bits of EDI, so the code implicitly keeps “last written position” (di) in lockstep with the VRAM write pointer (edi). my C port split them into two independent variables, which breaks that invariant and can stall the whole river merge (you end up computing runs from a stale di while advancing edi).
surgical fix: make di = low16(edi)

JS: why you fetch 8 bits with bl register when it's ebx in the asm code ?
Good catch — that's a subtle but important detail.

JS: are you sure about the translation? It's far from the asm version.
You’re right to push back — my first pass missed several ASM details. I’ve rebuilt the translation carefully...

JS: _demomode have 3 parameters, not 2
You’re right—my bad. In this codebase _demomode takes 3 params: (ax, dx, ebx_minus2).

JS: no there is not REP MOVSD inside this code... I'm talking about polyixy, polyxy, clipxy2
You’re right—no rep movsd here. In this codepath they “pair-load” two 16-bit coords (x,y) as a single 32-bit dword and pass them around in EAX/EDX for speed.

JS: do you see the Self-modifying code inside the _rotate function?
Yes — your _rotate does use self-modifying code (SMC).


Sometime, it generated very strange code:

JS: What is this: `mov bl, byte bx ;` BL = BX low8
You're absolutely right — `mov bl, byte bx` doesn't exist! (NASM/MASM will reject it).
What I meant to write was simply:
`mov bl, bl ;` (obviously unnecessary)
… but actually, the goal was to copy the low byte of BX into BL.
But BL is already the low byte of BX! — so we don't need to do anything to extract it.

JS: What is this? `lea dx, [bx + byte [ebx + 6]]`
This is a bad instruction (and invalid in practice):
`lea dx, [bx + byte [ebx + 6]]`
And it replied with a wall of text explaining why it's wrong... the code it generated by itself...

JS: And where does `renderColumns` come from?
Good question — `renderColumns` doesn't come from your project.
It's an example function I introduced in a "clean/simplified" (non-ASM-faithful) version to explain the algorithm.
It doesn't exist in your source code and isn't used by the exact port.

JS: What's going on? It won't even compile; you've mixed code from the original file on top of your own code.
Let me start again properly. I rebuilt a single file from your JL_ASM.ASM without "mixing" anything...

This one was funny:
Perfect! Now I can see the actual data. The array starts with zeros and has small values (0, 1, 2, 3). This is completely different from what the W1DTA.BIN file showed in your document (which had 0xFF bytes). This is not the correct data! The W1DTA.BIN file you showed earlier starts with: ￿￿￿￿￿￿￿￿￿￿￿￿￿￾￾￾￾￾￾...
Which in hex should be:
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xFE, 0xFE...

The binary data was not converted correctly! When you converted the .BIN files to C arrays, something went wrong.
The UTF-8 encoding issues you see in the document (￿ and ￾ characters) are because those are high byte values (0xFF = 255, 0xFE = 254) being displayed as Unicode.
You need to re-convert the W1DTA.BIN and W2DTA.BIN files to C arrays properly. The binary files contain signed 16-bit height values.
Use a tool like xxd or write a small program to dump the binary data correctly:

JS: what are you talking about... UTF-8, FF, ... i sent you the original file before, the converted version and both started with 0. I tried you xxd and it's also start with 0...
unsigned char W1DTA_BIN[] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,

You're absolutely right - I apologize for the confusion. The W1DTA.BIN file you provided does indeed start with zeros. I was misreading the UTF-8 encoded text display in the document viewer, which was showing special characters. So the data IS correct - it starts with zeros and contains small height values (0, 1, 2, 3).

VIBE Coding - Claude



As the screenshot shows, I subscribed at 10:10 a.m. and canceled about 2.5 hours later. Claude's warnings / limits kept breaking my flow and killing velocity. I hit a "Limit reached" popup urging me to upgrade at 7x the price. Given that those two hours produced nothing useful.

The flow was: within 30-60 minutes of coding, I kept getting "Approaching 5-hour limit" pop-ups, then hard stops like "Session limit reached ∙ resets XX:XX XM", right in the middle of work. Even a single request with two files (C/ASM) triggered an "almost out of usage" warning, To top it off, pasting six short lines once threw a "We couldn't connect to Claude. Please check your network connection and try again."

This one is the worse:
Opus limit reached ∙ resets Wednesday 4:00 PM (WE ARE SUNDAYYYYYYYYYYYYYYYYYY !!!)

It's very different compare to ChatGpt. For ChatGPT, I got timeout like:
"Network connection lost. Attempting to reconnect…",
"Error in message stream",
"Something went wrong while generating the response. If this issue persists please contact us through our help center at help.openai.com.",
but was able to continue after that.


Claude got confused with AX and often produce 8 bits output instead of 16 bits (e.g. mov [edi],ax).

Both (Claude and ChatGPT) struggled to fetch 16 variables into 32 bits register and do some transformation.

Claude also generated incorrect assembly code, just like ChatGPT:
invalid effective address: lea eax, [bx+eax]
The lea eax, [bx+eax] instruction is invalid because you can't use 16-bit and 32-bit registers together in the same effective address calculation. Here's the fix:...

JS: you read 8 bits instead of 16 bits for w2dta and w1dta...
You're absolutely right! Let me fix those issues.

JS: what is that?
You're right to question that code - it's broken. I made several fundamental errors:
Wrong register usage - I mixed up edi/esi and used wrong addressing
Incorrect data extraction - I didn't actually parse the original patterns correctly
Broken loop logic - The pixel processing loops don't match the original behavior
Missing exact patterns - I approximated instead of extracting the real offset sequences
The original code has very specific, hardcoded pixel sampling patterns for each zoom level. To create a proper table-driven version, ...

Some interesting feedback I got from Claude:
"The assembly is clearly from a sophisticated voxel terrain engine (likely from a 1990s game), and the precise fixed-point mathematics and perspective calculations are too complex to accurately reverse-engineer from assembly alone without making errors. My conversion attempts have gotten the basic structure working (ray casting, heightmap sampling, vertical column rendering) but the specific mathematical relationships that create the smooth terrain effect are still incorrect. If you have access to the original source code or documentation for this renderer, that would be the most reliable path forward. Otherwise, you might consider using a modern voxel terrain rendering algorithm instead of trying to convert this specific assembly implementation."

The core issue is that the original assembly has very specific, interdependent behaviors that are extremely difficult to replicate in C without making subtle errors in the fixed-point arithmetic, carry flag handling, and register interactions.

The conversion to C is proving more complex than the assembly-to-assembly transformation because C doesn't have the same low-level control over carry flags and 16-bit register arithmetic that the original relies on.

Two weeks later I retried it with a tiny snippet that had sent ChatGPT into 20+ iterations chasing a precision bug (0.5 in fixed-point). ChatGPT hit a dead end, Claude found it in two steps.

Result



We now have builds for Windows (x64 & x86), Linux (Ubuntu), and WebAssembly (WASM) that run in mobile and desktop browsers.

To ship this project, I simplified a few parts and cleaned up / refactored the code. Both before and after using AI. One ~100-line assembly routine example took nearly 100 back-and-forth iterations to get working correctly with the AI. I expected AI to excel at repetitive tasks. Even so, you often have to tweak, simplify, and refactor to get quality (e.g. table translation).

The pre and post Vibe coding were fun and very interesting to do. I found that Vibe coding is an alias for "removing the joy of coding" in my opinion. This kind of process looks similar as spending 7:50 hours in useless meetings and code 5 minutes during a day.

It was a nice experience to do but I don't think I will use ChaptGPT or Claude again at this level.

If someone says Software Engineering is "dead" because of AI, they probably don't code, haven't coded in years, or have never worked on a large and complex system.

AI is great for kickstarts scripts, quick lookups (e.g. API docs), light data parsing but for low level or complex work, the back and forth can take longer than just coding it yourself. Maybe that changes over time.

Here are some info about this project that we could do to improve or clean-up:
- The code could be more C++ like replacing the old C casting by static_cast or other cast, unify primitive types like uint8_t versus unsigned char and reducing #ifdef.
- I also embedded the data binary instead of loading some files (e.g. music) in a bin2c type but we could use #embed if we upgrade the project at C++ 23.
- We could also split and create multiple files for graphics and audio (e.g. platforms) for better readability.
- The way that it copies texture from CPU to GPU could be improved (partial instead of full copy).
- The timing could be improved on Firefox for the web / WASM version.
- The renderers are in DirectX and OpenGL and I also added my Super Sampling called "jsss". Far from DLSS and others but I think it's nice with pixelize style.

Here is the main page of Second Reality++ if you want to download, launch in your web browsers, look at the code source or watch youtube videos about that:

SecondReality++ Main Page

Huge thanks to Gargaj / Conspiracy, whose Win32 port inspired this C++ port.

Thanks for reading,

JS.

PayPal, Venmo, or 'Buy me a coffee' options below if you want to make a donation.


Useful Links

  • Sitemap
  • RSS
  • Speedtest
  • Changelog
  • Privacy Policy
  • PageSpeed Insights

Email Newsletter

Click here to subscribe to the email newsletter

Contact Me

Don't hesitate to contact me if you have any questions, suggestions, or feature requests that you would like to see in a specific project. I'm always open to hearing about and helping out with proposals (contract / freelance / jobs)
© Copyrights, 2025 All Rights Reserved