365 Days of Code - Day 064

Project Status

ProjectLanguageStatusDue DateLatest Update
Personal WebsiteHugoOngoingNoneThe site is live. Continuous improvements ongoing.
Laravel From ScratchLaravel (PHP)In-Progress2026-03-31Episode 8
PRMLaravel (PHP)In-Progress2026-03-31Working alongside other Laravel projects.
Client Website (J.L.)Laravel (PHP)In-Progress2026-03-31Working alongside other Laravel projects.
Project EulerCOngoingNoneWorking on P25. BigInt (AI gen) was a waste of time, need to rewrite
Practice JavaJavaPausedNoneInstalled, need to find a good project.
Practice PythonPythonPausedNoneInstalled, need to find a good project.
Learn GoGoPausedNoneInstalled, work on LDAP Injector from ippsec.
Learn RustRustHaven’t StartedNoneInstalled, will try network protocols after finishing in C and Zig.
Learn ElixirElixirHaven’t StartedNoneInstalled, need a good tutorial project.
Learn HaskellHaskellHaven’t StartedNoneInstalled, need a good tutorial project.
Learn ZigZigHaven’t StartedNoneInstalled, will try network protocols after finishing in C.
Linux+N/AIn-Progress2026-03-31Reading Chapter 4.
Cyber Quest 2026N/AIn-Progress2026-02-28Finished quiz 1 with 75%.
Operating SystemsN/AIn-Progress2026-03-31Reading Chapter 4: Abstraction
Grey-Hat HackingVariousIn-Progress2026-03-31Reading Chapter 8: Threat Hunting Lab
PHP Time TrackerPHPBeta FinishedNoneWorking on a basic level.
HTTP Status Code ReaderCComplete2026-02-18Complete.
ZSH Configurationbash/zshCompleteNoneSort of an ongoing process, but complete for now. Works good.
Network ProtocolsCIn-ProgressNoneWorking on V3, implementing IPv6.
Discinox WebsiteHTML, CSS, JSComplete2026-03-04The site is live.
DiroffTech WebsiteHTML, CSS, JSComplete2026-03-05The site is live. git-lfs needs to be initialized for images.
Automate BackupsbashComplete2026-03-08Backups done.
CodinGameCOngoingNoneCompleted GhostLegs, ASCII Art.

CodinGame - MiniCPU Instruction Decoder

Another day, another CodinGame challenge. This one has us performing a simple CPU emulation, reading in instructions, and manipulating registers.

A simple 8-bit CPU emulator must be debugged. The CPU has 4 registers (R0, R1, R2, R3) initialized to 0.

  • Instructions are encoded as hexadecimal bytes:
Ins.Reg.Reg. or Val.Description
01XVLoad value V into register RX
02XYAdd register RY to register RX
03XYSubtract register RY from register RX
04XYMultiply register RX by register RY
05XIncrement register RX by 1
06XDecrement register RX by 1
FFHalt execution
  • Where X and Y are register indices (0-3), and V is an immediate byte value.
  • All arithmetic wraps at 256 (8-bit unsigned). Overflow wraps to 0, underflow wraps to 255.
  • The first instruction is not guaranteed to be 01 (MOV). Programs may start with any instruction, including 05 (INC) or 06 (DEC).
  • The smallest valid program is just FF (HLT).
  • Execute the program and output the final register values.

Input

  • Line 1: A string of space-separated hexadecimal bytes program

Output

  • 4 lines: The decimal value of each register R0, R1, R2, R3, one value per line

Constraints

  • Program length ≤ 100 bytes
  • For the opcodes 02, 03 and 04, X ≠ Y
  • Program always ends with HLT (FF) as an opcode. Byte value FF may also appear as an immediate value V.

Example Input:

plaintext
01 00 0A 01 01 05 02 00 01 03 00 01 FF
1
01 00 0A 01 01 05 02 00 01 03 00 01 FF

Example Output:

plaintext
10
5
0
0
1
2
3
4
10
5
0
0

Initial Thoughts

This looks pretty simple on the surface. Store the state of the registers, create functions for each of the operations, and output the final state.

Code

I was able to essentially one-shot this code. It required some debugging to ensure I got the reads correct, but the problem was fairly easy overall. I will note that I had to fill out the entire switch statement for the program to operate as expected. In my naivite, I only created the first opcode, mov in the switch and started testing. Since I wasn’t consuming the other opcodes, I was getting garbage output and seg faults.

c
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

/**
 * Decode and execute the MiniCPU bytecode program.
 **/

void mov(uint8_t *r, uint8_t val)
{
    *r = val;
    return;
}

void add(uint8_t *r1, uint8_t *r2)
{
    *r1 += *r2;
    return;
}

void sub(uint8_t *r1, uint8_t *r2)
{
    *r1 -= *r2;
    return;
}

void mul(uint8_t *r1, uint8_t *r2)
{
    *r1 *= *r2;
    return;
}

void inc(uint8_t *r)
{
    *r += 1;
    return;
}

void dec(uint8_t *r)
{
    *r -= 1;
    return;
}

int main()
{
    // Space-separated hex bytes representing CPU instructions
    char program[501] = "";
    scanf("%[^\n]", program);

    uint8_t R0 = 0;
    uint8_t R1 = 0;
    uint8_t R2 = 0;
    uint8_t R3 = 0;
    uint8_t *arr[4] = {&R0, &R1, &R2, &R3};

    int pos = 0;
    int consumed;

    unsigned int byte;
    bool halted = false;
    while (!halted && sscanf(program + pos, "%x%n", &byte, &consumed) == 1) {
        uint8_t b = (uint8_t)byte;
        pos += consumed;
        fprintf(stderr, "%x\n", b);
        switch (b)
        {
            case 0x01:
            {
                fprintf(stderr, "mov\n");
                sscanf(program + pos, "%x%n", &byte, &consumed);
                uint8_t r = (uint8_t)byte;
                fprintf(stderr, "r: %d\n", r);
                pos += consumed;
                sscanf(program + pos, "%x%n", &byte, &consumed);
                uint8_t val = (uint8_t)byte;
                fprintf(stderr, "val: %d\n", val);
                pos += consumed;
                mov(arr[r], val);
                break;
            }
            case 0x02:
            {
                fprintf(stderr, "add\n");
                sscanf(program + pos, "%x%n", &byte, &consumed);
                uint8_t r1 = (uint8_t)byte;
                fprintf(stderr, "r1: %d\n", r1);
                pos += consumed;
                sscanf(program + pos, "%x%n", &byte, &consumed);
                uint8_t r2 = (uint8_t)byte;
                fprintf(stderr, "r2: %d\n", r2);
                pos += consumed;
                add(arr[r1], arr[r2]);
                break;
            }
            case 0x03:
            {
                fprintf(stderr, "sub\n");
                sscanf(program + pos, "%x%n", &byte, &consumed);
                uint8_t r1 = (uint8_t)byte;
                fprintf(stderr, "r1: %d\n", r1);
                pos += consumed;
                sscanf(program + pos, "%x%n", &byte, &consumed);
                uint8_t r2 = (uint8_t)byte;
                fprintf(stderr, "r2: %d\n", r2);
                pos += consumed;
                sub(arr[r1], arr[r2]);
                break;
            }
            case 0x04:
            {
                fprintf(stderr, "mul\n");
                sscanf(program + pos, "%x%n", &byte, &consumed);
                uint8_t r1 = (uint8_t)byte;
                fprintf(stderr, "r1: %d\n", r1);
                pos += consumed;
                sscanf(program + pos, "%x%n", &byte, &consumed);
                uint8_t r2 = (uint8_t)byte;
                fprintf(stderr, "r2: %d\n", r2);
                pos += consumed;
                mul(arr[r1], arr[r2]);
                break;
            }
            case 0x05:
            {
                fprintf(stderr, "inc\n");
                sscanf(program + pos, "%x%n", &byte, &consumed);
                uint8_t r = (uint8_t)byte;
                fprintf(stderr, "r1: %d\n", r);
                pos += consumed;
                inc(arr[r]);
                break;
            }
            case 0x06:
            {
                fprintf(stderr, "dec\n");
                sscanf(program + pos, "%x%n", &byte, &consumed);
                uint8_t r = (uint8_t)byte;
                fprintf(stderr, "r1: %d\n", r);
                pos += consumed;
                dec(arr[r]);
                break;
            }
            case 0xFF:
                fprintf(stderr, "halt\n");
                halted = true;
                break;
            default:
                fprintf(stderr, "unknown opcode\n");
                halted = true;
                break;
        }
    }

    // Print the final value of each register R0, R1, R2, R3, one value per line
    printf("%d\n%d\n%d\n%d\n", R0, R1, R2, R3);

    return 0;
}
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

/**
 * Decode and execute the MiniCPU bytecode program.
 **/

void mov(uint8_t *r, uint8_t val)
{
    *r = val;
    return;
}

void add(uint8_t *r1, uint8_t *r2)
{
    *r1 += *r2;
    return;
}

void sub(uint8_t *r1, uint8_t *r2)
{
    *r1 -= *r2;
    return;
}

void mul(uint8_t *r1, uint8_t *r2)
{
    *r1 *= *r2;
    return;
}

void inc(uint8_t *r)
{
    *r += 1;
    return;
}

void dec(uint8_t *r)
{
    *r -= 1;
    return;
}

int main()
{
    // Space-separated hex bytes representing CPU instructions
    char program[501] = "";
    scanf("%[^\n]", program);

    uint8_t R0 = 0;
    uint8_t R1 = 0;
    uint8_t R2 = 0;
    uint8_t R3 = 0;
    uint8_t *arr[4] = {&R0, &R1, &R2, &R3};

    int pos = 0;
    int consumed;

    unsigned int byte;
    bool halted = false;
    while (!halted && sscanf(program + pos, "%x%n", &byte, &consumed) == 1) {
        uint8_t b = (uint8_t)byte;
        pos += consumed;
        fprintf(stderr, "%x\n", b);
        switch (b)
        {
            case 0x01:
            {
                fprintf(stderr, "mov\n");
                sscanf(program + pos, "%x%n", &byte, &consumed);
                uint8_t r = (uint8_t)byte;
                fprintf(stderr, "r: %d\n", r);
                pos += consumed;
                sscanf(program + pos, "%x%n", &byte, &consumed);
                uint8_t val = (uint8_t)byte;
                fprintf(stderr, "val: %d\n", val);
                pos += consumed;
                mov(arr[r], val);
                break;
            }
            case 0x02:
            {
                fprintf(stderr, "add\n");
                sscanf(program + pos, "%x%n", &byte, &consumed);
                uint8_t r1 = (uint8_t)byte;
                fprintf(stderr, "r1: %d\n", r1);
                pos += consumed;
                sscanf(program + pos, "%x%n", &byte, &consumed);
                uint8_t r2 = (uint8_t)byte;
                fprintf(stderr, "r2: %d\n", r2);
                pos += consumed;
                add(arr[r1], arr[r2]);
                break;
            }
            case 0x03:
            {
                fprintf(stderr, "sub\n");
                sscanf(program + pos, "%x%n", &byte, &consumed);
                uint8_t r1 = (uint8_t)byte;
                fprintf(stderr, "r1: %d\n", r1);
                pos += consumed;
                sscanf(program + pos, "%x%n", &byte, &consumed);
                uint8_t r2 = (uint8_t)byte;
                fprintf(stderr, "r2: %d\n", r2);
                pos += consumed;
                sub(arr[r1], arr[r2]);
                break;
            }
            case 0x04:
            {
                fprintf(stderr, "mul\n");
                sscanf(program + pos, "%x%n", &byte, &consumed);
                uint8_t r1 = (uint8_t)byte;
                fprintf(stderr, "r1: %d\n", r1);
                pos += consumed;
                sscanf(program + pos, "%x%n", &byte, &consumed);
                uint8_t r2 = (uint8_t)byte;
                fprintf(stderr, "r2: %d\n", r2);
                pos += consumed;
                mul(arr[r1], arr[r2]);
                break;
            }
            case 0x05:
            {
                fprintf(stderr, "inc\n");
                sscanf(program + pos, "%x%n", &byte, &consumed);
                uint8_t r = (uint8_t)byte;
                fprintf(stderr, "r1: %d\n", r);
                pos += consumed;
                inc(arr[r]);
                break;
            }
            case 0x06:
            {
                fprintf(stderr, "dec\n");
                sscanf(program + pos, "%x%n", &byte, &consumed);
                uint8_t r = (uint8_t)byte;
                fprintf(stderr, "r1: %d\n", r);
                pos += consumed;
                dec(arr[r]);
                break;
            }
            case 0xFF:
                fprintf(stderr, "halt\n");
                halted = true;
                break;
            default:
                fprintf(stderr, "unknown opcode\n");
                halted = true;
                break;
        }
    }

    // Print the final value of each register R0, R1, R2, R3, one value per line
    printf("%d\n%d\n%d\n%d\n", R0, R1, R2, R3);

    return 0;
}

Tested… 100%.

Related content