Category Archives: OS

The Complete 8086 Register Reference: AX, BX, CX, DX, Segment, Index & Pointer Registers Explained

The first assembly exercise most students write touches four registers: AX, CX, DX, and BX, usually in that order. By the second week, they’ve discovered that two of those registers have hidden obligations — MUL silently writes into DX, LOOP quietly owns CX — and the bugs that follow take an hour to diagnose. I wrote this reference after going through exactly that experience in a microprocessor architecture course in my third year, and again later when I was building a small 8086 emulator in C and needed a reliable answer to “which registers are valid inside an effective address?” — because getting that wrong produces an assembler error with no intuitive explanation.

The Intel 8086 has exactly 14 programmer-visible registers, each with a fixed width and — in many cases — aliases for 8-bit access. This reference was built from the Intel 8086 Microprocessor Datasheet (order number 231455), the 8086 Family User’s Manual (1979), and hands-on testing in DOSBox-X and EMU8086. Where behaviour differs between emulators and real hardware (FLAGS edge cases, segment override interactions), I’ve noted it explicitly.

Continue reading The Complete 8086 Register Reference: AX, BX, CX, DX, Segment, Index & Pointer Registers Explained

8086 Assembly: Handling the External Timer Interrupt (INT 08h)

The 8086 processor does not poll a hardware timer on each clock tick — it reacts to one. The system timer fires INT 08h approximately 18.2 times per second, and the CPU responds by suspending whatever it is currently executing, saving its state, and running the interrupt service routine (ISR) stored at the corresponding vector. Understanding how to intercept this interrupt, execute custom logic, and then chain back to the original BIOS handler is a foundational skill in low-level 8086 programming.

This post builds a working INT 08h handler step by step. The ISR increments a counter in memory on every timer tick, displays a visible marker, and correctly chains to the original BIOS routine before returning — the safe and production-correct pattern for timer-interrupt programming.

Continue reading 8086 Assembly: Handling the External Timer Interrupt (INT 08h)

8086 Assembly: PUSH, POP, CALL, and RET – Stack Operations Explained

The stack is the workhorse of 8086 assembly. Every subroutine call, every register save-and-restore, and every return address depends on it. Understanding PUSH, POP, CALL, and RET at the instruction level — not just the high-level idea — is what separates someone who can read assembly from someone who can actually write and debug it.

In this post we build a working program that calls two subroutines, preserves registers across them, and stores results back in memory. Every line is annotated, the stack state is traced step-by-step, and the TASM/MASM debug output is shown so you can verify the execution yourself.

Continue reading 8086 Assembly: PUSH, POP, CALL, and RET – Stack Operations Explained

Multithreading Example in Java

In this post, we implement a basic Multithreading example in Java. Multithreading allows multiple threads to execute concurrently within a single program, enabling tasks to run in parallel rather than one after another. Java has built-in support for multithreading through the Thread class and the Runnable interface.

What is Multithreading?

A thread is the smallest unit of execution within a process. When a program creates multiple threads, the operating system’s thread scheduler interleaves their execution — giving each thread a slice of CPU time in turn. This makes it appear as though they are running simultaneously (and on multi-core systems, they actually can be).

In this example, we create two threads by extending the Thread class and overriding its run() method. Each thread prints a label 4 times, pausing 500 ms between prints. Both threads run concurrently, so their output interleaves.

  • start() — Tells the JVM to create a new OS-level thread and invoke run() on it. Calling run() directly would execute it on the current thread, not a new one.
  • Thread.sleep(ms) — Pauses the current thread for the given number of milliseconds, allowing other threads to execute.
  • InterruptedException — Must be caught when calling sleep(). It fires if another thread interrupts this one while it is sleeping.

Continue reading Multithreading Example in Java

Illustrating Working of FIFO Page Replacement Algorithm in C++

In this post, we implement the FIFO (First In, First Out) Page Replacement Algorithm in C++. When the OS needs to load a new page into memory but all frames are occupied, FIFO evicts the page that has been in memory the longest — the one that arrived first. It is one of the simplest page replacement strategies and serves as a baseline for comparing more sophisticated algorithms.

What is FIFO Page Replacement?

Physical memory is divided into frames. When a process references a page not currently in a frame (a page fault), it must be loaded. If all frames are full, an existing page must be evicted. FIFO chooses the oldest resident page for eviction, regardless of how frequently it has been used.

  • Page Hit — Referenced page is already in a frame. No disk I/O needed.
  • Page Fault (Miss) — Referenced page is not in any frame. Must load from disk.
  • FIFO Queue — Tracks the order in which pages were loaded. Front = oldest; back = newest.

A notable weakness of FIFO is Bélady’s Anomaly — adding more frames can sometimes cause more page faults, counter-intuitively.

Continue reading Illustrating Working of FIFO Page Replacement Algorithm in C++

Implementing Banker’s Algorithm in C++

In this post, we implement the Banker’s Algorithm in C++ — a classic deadlock avoidance mechanism in operating systems proposed by Edsger Dijkstra. The algorithm is named after the analogy of a bank that never lends money in a way that could prevent it from satisfying all customers’ future needs.

What is the Banker’s Algorithm?

Before granting any resource request, the OS runs the Banker’s Algorithm to check whether doing so keeps the system in a safe state. A safe state is one where a safe sequence exists — an ordering of all processes such that each can eventually complete using currently available resources plus the resources released by earlier processes in the sequence.

If no safe sequence exists, the state is unsafe, meaning deadlock is possible. The OS denies the request in that case.

Three data structures are needed:

  • Available[] — Current free units of each resource type.
  • Allocated[][] — Resources currently held by each process.
  • Maximum[][] — The maximum resources each process may ever request.
  • Need[][] — Remaining resources a process may still request: Need[i][j] = Maximum[i][j] - Allocated[i][j]
Continue reading Implementing Banker’s Algorithm in C++

Implementing SJF (Shortest Job First) Scheduling Algorithm in C++

In this post, we implement the Shortest Job First (SJF) CPU scheduling algorithm in C++. SJF always selects the process with the smallest burst time from the ready queue and executes it next. This greedy approach provably minimizes the average waiting time, making SJF theoretically optimal among all non-preemptive algorithms when all jobs are available simultaneously.

What is SJF Scheduling?

In SJF, the scheduler sorts all ready processes by their burst (execution) time in ascending order and then runs them in that sequence. The shortest job finishes earliest, releasing the CPU quickly for others and keeping the average wait low.

It is non-preemptive — once started, a process runs to completion. The preemptive variant is called Shortest Remaining Time First (SRTF).

  • Waiting Time (WT): WT[i] = WT[i-1] + BurstTime[i-1] - (ArrivalTime[i] - ArrivalTime[i-1])
  • Turnaround Time (TT): TT[i] = WT[i] + BurstTime[i]

The key risk with SJF is starvation: if short jobs keep arriving, long jobs may never execute.

Continue reading Implementing SJF (Shortest Job First) Scheduling Algorithm in C++