Skip to main content

OS

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

The definitive 8086 register reference: all 14 registers explained with size, purpose, aliases, mandatory roles, and typical instructions. Includes a bit-by-bit FLAGS table, addressing mode summary, and a "which register for which task" decision guide that maps every common programming task to the correct register — with links to worked assembly examples throughout.

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

A complete guide to handling the 8086 external timer interrupt INT 08h. Covers the interrupt vector table, how the CPU dispatches interrupts, saving and restoring the original BIOS handler, writing a far ISR that increments a tick counter, chains correctly, and exits safely — with a common-mistakes table.

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

A complete guide to 8086 assembly stack operations. Covers how the stack works (SP, SS, LIFO), what PUSH and POP do to memory, how CALL saves a return address and RET retrieves it, and a fully annotated working program that demonstrates all four instructions together.

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.

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.

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]

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.

Implementing Round Robin Scheduling Algorithm in C++

In this post, we implement the Round Robin (RR) CPU scheduling algorithm in C++. Round Robin is the most widely used algorithm in time-sharing systems because it gives every process a fair, equal slice of CPU time. Each process gets to run for a fixed duration called the time quantum, after which it is preempted and moved to the back of the ready queue. What is Round Robin Scheduling? All processes are arranged in a circular queue. The CPU runs each process for at most Q (time quantum) units. If the process finishes within Q units, it leaves the queue. If it doesn’t finish, it is interrupted and placed at the back to wait for its next turn. This cycle repeats until all processes complete. Time Quantum (Q) — The maximum CPU time a process gets per round. A smaller Q improves response time but increases context-switch overhead. A very large Q degrades to FCFS behavior. Waiting Time (WT) — Total time the process spent waiting across all rounds, not including its own execution time. Turnaround Time (TT): TT = WT + TotalBurstTime

Implementing the Producer-Consumer Algorithm in C++

In this post, we implement a simulation of the Producer-Consumer Problem in C++ using a circular bounded buffer. This is one of the most classic synchronization problems in operating systems, modeling the coordination between a process that generates data (producer) and a process that consumes data (consumer) through a shared buffer of fixed capacity. What is the Producer-Consumer Problem? The Producer-Consumer problem (also called the Bounded Buffer problem) defines these rules: The producer generates items and places them into the buffer — but only if space is available. If the buffer is full, the producer must wait (sleep). The consumer retrieves and processes items from the buffer — but only if items exist. If the buffer is empty, the consumer must wait (sleep). Only one entity should access the buffer at a time (mutual exclusion). This simulation runs 20 steps. At each step, a random number determines whether the producer or consumer acts. A circular buffer is used so the array can be reused efficiently without shifting elements.

Implementation of Priority Scheduling Algorithm in C++

In this post, we implement the Priority Scheduling CPU scheduling algorithm in C++. Each process is assigned a priority number, and the process with the highest priority is scheduled first. This algorithm is used in real-time and embedded systems where certain tasks are more urgent than others and must preempt routine work. What is Priority Scheduling? In this implementation, a higher priority number means higher urgency (e.g., priority 5 executes before priority 1). The algorithm is non-preemptive — once a process starts, it runs to completion. All processes are assumed to arrive at time 0. The algorithm works by sorting processes in descending order of priority and then computing scheduling metrics in that order: Waiting Time (WT): WT[i] = WT[i-1] + BurstTime[i-1] (since all arrive at time 0) Turnaround Time (TT): TT[i] = WT[i] + BurstTime[i] The main concern with priority scheduling is starvation — low-priority processes may never get the CPU. The solution is aging: gradually increasing a process’s priority the longer it waits.