My Rambling Thoughts

What-if 80386 in an alternate world

Following our alt-286, we will enter 32-bit with our alt-386. :clap:

We can finally extend all registers to 32-bit and get a linear 4 GB memory space. No more messing with 24-bit address registers!

Or is it?

32-bit computing has a good run. It started in the late 1970s and lasted to early 2010s. Even today, 32-bit suffices for productivity apps. 64-bit became more common as the need to process >2 GB data arose: video editing, games, database, big data models, 3D modeling, machine learning, scientific simulations, virtual machines and so on. Wow, that's a long list!

What they want is >2 GB data, not necessarily 64-bit processing.

I use 2 GB limit because a 32-bit OS typically did not expect a single program to use >2 GB of memory. It was a lot of memory in the early 1990s.

Extending to 40 bits gives 5 byte address, which is awkwardly sized. So, let's use 48 bits — this makes the address 6 bytes. 48-bit allows us to address 256 TB of memory. This is future-proof — famous last words. :-D

So now we are back to asymmetric data register (32-bit) and address register (48-bit) again. Good thing is that we are already used to dealing with it. :lol:

Variable displacements

The 8086 supports 8-bit and 16-bit displacements. The 80386 supports 8-bit and 32-bit displacements in 32-bit mode. Should we support just two, or should we support more?

||8-bit|16-bit|24-bit|32-bit|48-bit|
|---|+--|+--|+--|+--|+--|
|Cond jump|✓|✓|–|–|–|
|Direct jump|✓|✓|✓|–|–|
|Direct call|–|.|✓|✓|–|
|Stack vars|✓|✓|–|–|–|
|Struct members|✓|✓|–|–|–|
|Array indexing|✓|✓|–|–|–|
|Memory addr|–|.|✓|✓|–|
|IP-relative|–|✓|✓|.|–|
|Constants|✓|✓|–|✓|–|
|Data pointers|–|.|✓|✓|✓|
|Function pointers|–|.|✓|✓|.|

Key: ✓ = useful, frequently used, '.' = nice to have, but not really needed, – = not needed

We do not want 48-bit immediate displacement, it makes the instruction too long. We may not want 32-bit as well. x86 has it, but RISC don't, and they do just fine.

We will use IP-rel addressing to load 32-bit and bigger immediates values.

Memory models

Does it make sense to bring back memory models? :lol:

  • Small: code/data up to 64 kB
  • Compact: code/data up to 16 MB
  • Flat: code/data up to 4 GB
  • Large: code/data > 4 GB

Memory model determines the default pointer size — how much code/data is addressable.

Code and data are separate, so it can be small code with flat data.

Most programs should fit in compact code and flat data. If the program handles data in streaming form, compact data is sufficient most of the time.

Stats from GHS '25

DoS (Department of Statistics) just released the General Household Survey 2025 report. It immediately caused a hooha in local forums when ST reported "about 1 in 7 S'pore families earn at least $30k a month". There was widespread disbelief.

How could there be so many, so high?

Many posters on HWZ's EDMW also expressed their shock. This is ironic given that they often used $20k/month pay as a benchmark.

One common explanation is that there is no real increase. Households now have more contributors because adult children stay with their parents due to high house prices.

Some say those who are in disbelief have been left behind... :sweat:

First, let's look at the Monthly Household Market Income table:

|Year|< 3k|< 6k|< 9k|< 12k|< 15k|< 20k|< 25k|< 30k|< 35k|35k+|
|---|---|---|---|---|---|---|---|---|---|---|
|2020|19.7%|15.6%|14.3%|12.3%|9.5%|11.0%|6.4%|3.8%|2.4%|5.0%|
|2025|14.6%|12.2%|10.9%|10.7%|10.0%|12.9%|9.1%|6.2%|4.4%|9.0%|
|2020 cum|0.0%|19.7%|35.3%|49.6%|61.9%|71.4%|82.4%|88.8%|92.6%|95.0%|
|2025 cum|0.0%|14.6%|26.8%|37.7%|48.4%|58.4%|71.3%|80.4%|86.6%|91.0%|

This is the first time income above $20k is broken down. In 2020, we only knew 17.6% earn above $20k. Even then, $20k was too low a bar. It is now raised to $35k — and almost 10% are above it.

$20k used to be 70th to 80th percentile (upper middle-income). It is now only 60th to 70th percentile (slightly above middle income).

Related stats:

||2020|2025|
|---|---|---|
|Avg|$12,396|$16,159|
|Avg per member|$4,111|$5,579|
|Avg employment income|$3,494|$4,439|
|Median|$9,099|$12,446|
|Median per member|$2,952|$4,160|

The average income has gone up significantly and 80% of income is from employment.

The average household size according to the above table is 2.90 (avg) and 2.99 (median). I don't know why they are different, or why they are different from the average household size (3.1).

Market income includes employer CPF, 1/12 of annual bonus and non-employment income, e.g. investments, rental, CPF payout. Some people say this is why the figures are on the high side.

For a single-income at $30k, the employer CPF contribution is only an extra $1.36k. For dual-income at $15k each (anything above $8k, actually), the employer CPF contribution is $2.72k.

If we assume a conservative 2-months bonus, it is $3.33k at $20k. (Using estimated employment income.)

Let's assume a more conservative non-employment income of 15% — $4.5k from a mix of investments and rental.

After adjusting all the factors, we get a figure of ~$21k monthly pay for $30k market income.

Individual income

GHS does not cover individual income, but DoS has an annual Gross Monthly Income From Employment report.
|Year|Total|1.5k+|2k+|3k+|6k+|9k+|12k+|15k+|20k+|22.5k+|
|---|---|---|---|---|---|---|---|---|
|2020|1,945.8k|9.7%|17.5%|34.1%|70.1%|84.2%|91.4%|94.3%|96.9%|97.8%|
|2024|2,067.2k|3.6%|9.4%|23.8%|61.3%|78.4%|88.1%|91.9%|95.6%|96.7%|
|2025|2,100.4k|2.1%|9.1%|22.0%|59.2%|77.1%|87.2%|91.3%|95.3%|96.6%|

It includes 1/12 of annual bonus, but excludes employer CPF. It tops out at $22.5k — top 10% of EP holders. Candidates earning over this are exempted from standard EP point system.

In this table, if you earn between $20k and $22.5k, you are in the 95th to 97th percentile.

From 2020 to 2022, it's like lower-paying jobs just... disappeared.

There is another source of info: Taxable Individuals by Assessed Income Group from IRAS. It has only up to 2024, and it goes by tax residents. Data for 2024:

||> 0|> 20k|> 25k|> 30k|> 40k|> 50k|> 60k|> 80k|> 100k|> 150k|> 200k|> 300k|> 400k|> 500k|> 1mil|
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|Number|74.9k|69.4k|125.7k|362.0k|295.0k|222.2k|169.6k|139.8k|213.1k|313.5k|150.9k|128.0k|50.9k|30.3k|9.2k|
|Avg pay|$0|$1.8k|$2.1k|$2.7k|$3.4k|$4.2k|$5.8k|$6.9k|$9.4k|$13.2k|$18.6k|$26.4k|$34.2k|$50.8k|$156.8k|
|%-tile|0.0%|3.2%|6.1%|11.4%|26.6%|39.0%|55.4%|61.3%|70.3%|83.4%|89.8%|95.2%|97.3%|98.3%|99.6%|

Avg pay is total assessable income in the band divided by the number of people in that band divided by 13 (w/ AWS).

How to read the table: if your assessable income is $200k - $300k (avg monthly pay of $18.6k to $26.4k), you are in the 90th to 95th percentile.

There is no figure for non-tax paying residents. I took the number of residents who earned less than $1.5k in 2024 — 74.9k. It is not accurate because 'residents' is not the same as 'tax residents', but it is much better than 0. This allows us to get income percentile for total employed population.

I blame high COE on easy loan and commerical use. I'm not so sure anymore after looking at this table. There are just too many high earners. There are more people earning $150k to $200k (313.5k) except for the $30k to $40k band (362.0k).

These people can own a car comfortably — car price should not exceed annual pay, remember?

Household size

||2020|2025|
|---|---|---|
|1 person|16.0%|16.7%|
|2 persons|22.6%|23.8%|
|3 persons|20.4%|22.6%|
|4 persons|20.1%|19.9%|
|5 persons|11.9%|10.9%|
|6+|9.0%|6.1%|
|Avg size|3.2|3.1|

To get average size of 3.2 in 2020, the 6+ avg is 6.x. In 2025, the 6+ avg size is 7.x.

The way I interpret this is, there are fewer 6-member households, leaving behind 7-members and above.

By living arrangement:

||2020|2025|
|---|---|---|
|Married w/ children|50.4%|47.6%|
|Married w/o children|16.8%|19.1%|
|Lone parent|9.3%|6.8%|
|One person|16.0%|16.7%|
|Others|7.5%|9.8%|

'Others' here can mean: grandparents w/ grandchildren, siblings-only, mixed relatives (e.g. uncle w/ nephew), divorced person living with widowed parent, elderly living only with domestic worker, unrelated roommates. The category is very broad!

The definition of lone-parent is very specific and may not be what you think. The household must:

  • have no married couple
  • at least one never-married / widowed / divorced / separated person
  • living with his/her child(ren), who must be below 16 y.o. or not married

This table is not very useful, it is more useful when broken down by age group.

||Total|< 35|< 50|< 65|65+|
|---|---|---|---|---|---|
|Married w/ children|47.6%|34.0%|56.7%|55.2%|31.6%|
|Married w/o children|19.1%|27.4%|14.8%|12.5%|29.8%|
|Lone parent|6.8%|6.4%|5.3%|9.8%|5.0%|
|One person|16.7%|18.2%|14.5%|13.9%|22.3%|
|Others|9.8%|14.0%|8.7%|8.7%|11.3%|

For 2020:

||Total|< 35|< 50|< 65|65+|
|---|---|---|---|---|---|
|Married w/ children|50.4%|39.0%|60.3%|56.4%|30.2%|
|Married w/o children|16.8%|25.0%|11.3%|11.4%|30.6%|
|Lone parent|9.3%|6.5%|8.6%|11.9%|7.4%|
|One person|16.0%|19.2%|14.2%|13.6%|21.6%|
|Others|7.5%|10.3%|5.7%|6.8%|10.2%|

Can we tell if households now have extra contributors? Not directly, but indirect evidence says no. If only there is a number of contributors per household figure...

Mode of transport to work

||2020|Median time|2025|Median time|
|---|---|---|---|---|
|Public transport|57.7%|50 mins|60.1%|60 mins|
|Taxi/PHV|3.1%|30 mins|2.5%|30 mins|
|Car|21.1%|30 mins|21.2%|30 mins|
|Motorcycle|3.4%|30 mins|2.9%|30 mins|
|No transport|9.8%|8 mins|9.7%|5 mins|

(Does not add up to 100% cos I omitted a few categories.)

I find surprising is that only 60.1% of the people take public transport to work. A full 20% drive! Wow, that's a lot.

There is breakdown by type of property, which I think is more revealing.

||1 - 2-rm|3-rm|4-rm|5-rm n Ex|Condo n Apart|Landed|Others|
|---|---|---|---|---|---|---|---|
|Public transport|70.2%|69.9%|65.1%|59.5%|48.9%|32.6%|46.5%|
|Taxi/PHV|1.6%|2.1%|2.2%|2.1%|4.1%|3.0%|4.2%|
|Car|4.6%|7.8%|14.8%|23.2%|36.6%|51.4%|14.4%|
|Motorcycle|3.5%|3.5%|4.1%|2.9%|0.7%|0.4%|0.0%|
|No transport|14.9%|12.2%|9.7%|8.6%|8.1%|11.1%|31.4%|

'Others' is where the surveyed person stays with someone else without charge.

This must be used together with the following table:

||1 - 2-rm|3-rm|4-rm|5-rm n Ex|Condo n Apart|Landed|Others|
|---|---|---|---|---|---|---|---|
|%-age|7.3%|16.6%|31.2%|22.1%|17.9%|4.7%|0.2%|
|Household size|1.9|2.4|3.2|3.5|3.1|4.1|--|

51.4% of Landed residents drive, but they are only 4.7% of the total population.

For travelling time, median is not enough. We need at least 20th and 90th percentile. We also need breakdown by area.

More logic puzzles


Murdle Book 2 [2023]

I regret getting Book 2. One book is enough.

Mine is a South Asia edition. I bought it for S$24.37. Really got ripped off.


Murdoku Vol 2 [2026]

OTOH, I don't mind Murdoku Vol 2 cos it is easier and in color. I'm shallow! :-P

This is hot off the press!

There are 5 chapters, each themed around a historical setting, with theme-specific rules to add variety. Each chapter has 16 puzzles, except one with 14; they range from easy to expert. There are two more puzzles at the end to round up to 80.

Due to this format, the difficulty ramps up very fast. Only the first two puzzles are easy, followed by medium, then hard, and the last two are expert — there are ten expert level puzzles altogether.

What-if 80286 in an alternate world

Following our alt-8086, let's extend it to 80286. What will 80286 be like if it did not have segmentation?

This is very ironic, because 80286 is the quintessential 16-bit segmented processor. Its memory management is based on segmentation in Protected Mode. It would have worked very well if segments were not limited to 16-bit — 80286 uses 24-bit address, it should allow 24-bit segments.

The answer to our question is simple. Our alt-80286 will use paging for memory management in Protected Mode — brought forward by one generation.

If we use 16 kB pages, the virtual address will look like this:

|Bits|#bits|Purpose|
|---|---|+--|
|14 - 23|10|Page Table|
|0 - 13|14|Offset|

We only need 10 bits for the Page Table, so a Page Table entry can be just 16 bits (6 bits for flags), but it is a little tight — we need 10 bits for flags, ideally — so let's use 32 bits, and we can use the same format when we move to 32-bit.

We only need 1,024 entries, so there is only one Page Table.

If we use all 4,096 entries, we can support an address space of 64 MB — needs 26-bit address bus.

That's it, we are done.

Protected Mode

For our alt-286, Real mode means paging is off. Protected mode means paging is on.

The CPU starts in Supervisor mode with paging off. The OS may set up page tables and enable paging, entering Protected mode. It then enters User mode using IRET.

Interrupts

An interrupt has its own stack, page table and PCID, different from the kernel. ISRs are not fully trusted code!

Interrupt Vector entry:

|Ofs|#bytes|Purpose|
|---|---|+--|
|0|2|Flags|
|2|2|PCID|
|4|4|Page Table addr|
|8|4|Stack addr|
|12|4|Call Stack addr|
|16|4|Entry point|

Flags:

  • Save registers? This is for convenience
  • Run in supervisor mode?
  • Disable interrupt?

Each Interrupt Vector is 20 bytes. Let's make it 32 bytes for future expansion.

An interrupt will always use its Page Table and stack. It will never use the process's stack.

The process's context is saved in its Context memory. This includes the return address.

The process's context address and SP are pushed to the ISR stack. User-mode SP is intended for a system call to access its parameters that are pushed on the process's stack. An ISR won't be able to use it — its Page Table will not have user memory.

Interrupt handling in x86 Protected Mode is notoriously slow. We want interrupts to be fast. I don't know if I count correctly, but we should be able to do it in < 30 clock cycles.

System calls

A system call is made using software interrupt instruction. There is no call gate, jump gate or task gate. There is no TSS (Task State Segment).

There is no need to use a dedicated SYSCALL instruction as interrupts have very little overhead compared to INT on x86.

System calls use kernel page table and stack — by definition.

Optimizing save/restore

Let's say alt-80286 has alt-80287 integrated. This means an additional 8 48-bit registers to save — a total of 48 bytes, or 24 cycles.

It is tempting to save and restore them lazily, as Intel did. FP instructions are trapped in user mode. When they are used, the OS then saves the values for the previous process and restores the values for the current process.

But this leads to LazyFP attack which uses speculative read to avoid tripping the trap. The data can then be teased out of the registers by using cache side-channel read.

A solution is to trap even for speculative read.

An alternative solution is to assign a dirty bit to each optional register. It is cleared if the register is zero. All registers are saved and restored on inter-process context switch, but clean ones are skipped, saving cycles:

  • Save current process: write dirty flags, write dirty registers, skip clean ones
  • Restore new process: read dirty flags, read dirty registers, clean ones are zeroed out

Moral of the story: always zero out registers after use!

Murdle for Juniors


Murdle Jr (Book 1) [2024]

Junior edition, easier for kids below 13 years old to solve. Has 40 puzzles.

It is divided into 5 sections. Each section has 8 puzzles. For the first four sections, there are two 3x3 (2 variables), two 4x4, two 3x3x3 (3 variables) and two 4x4x4 puzzles. The last section has four 4x4 (2 variables), one 3x3x3 (3 variables), one 4x4x4 and two 3x3x3 (4 variables). The last four puzzles are adult standard.


Murdle Jr: Sleuths on the Loose [2025]

This is a mystery novel. Don't buy this book if you are looking for puzzles.

It has only one 4x4x4 logic puzzle (3 variables) that is filled up gradually as the story progresses. You are given a chance to solve the puzzle at the end of the book before the reveal.

It has a bonus 3x3 puzzle (2 variables) at the end.

I knew it was a storybook, but I thought it had puzzles sprinkled throughout. I was mistaken.

I noticed my copy is for South Asia region and is not meant for export. Its MSRP is 499 Indian Rupee, which is S$6.82! I bought it for S$15.46. Its MSRP in UK is 7.99 pounds, or S$13.65.

(I checked Amazon India and it sells for 342 Ruppee, or S$4.68, there. Too bad they don't deliver to Singapore — I estimate shipping to be around S$5.)

It turns out my Murdle Book 1 is also a South Asia edition, 599 Indian Rupee (S$8.19). I bought it for S$20.38. I feel cheated. :lol: Its MSRP in UK is 14.99 pounds, or S$25.62.

It is not surprising that the same book sells much cheaper in South Asia — 1/3 to 1/2 UK MSRP. But the publisher should have at least make it harder to grey import by using a different book cover.