<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/tiny-great-languages-assembly-6tdaecfp3" -->

---
title: Tiny Great Languages: Assembly | daily.dev
description: A hands-on walkthrough of building a tiny two-pass assembler that targets the CPython VM's own bytecode instruction set. It covers disassembling Python...
canonical: https://daily.dev/posts/tiny-great-languages-assembly-6tdaecfp3
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Tiny Great Languages: Assembly | daily.dev
og:description: A hands-on walkthrough of building a tiny two-pass assembler that targets the CPython VM's own bytecode instruction set. It covers disassembling Python...
og:url: https://daily.dev/posts/tiny-great-languages-assembly-6tdaecfp3
og:image: https://api.daily.dev/og/posts/6TDAECFP3.png
og:image:alt: Tiny Great Languages: Assembly
og:image:width: 1200
og:image:height: 630
og:locale: en
---

> ## Documentation Index
> Fetch the complete documentation index at: https://daily.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Tiny Great Languages: Assembly

**[zserge's blog](https://daily.dev/sources/zserge)** · 7 min read · 0 upvotes · 0 comments

## Summary

A hands-on walkthrough of building a tiny two-pass assembler that targets the CPython VM's own bytecode instruction set. It covers disassembling Python functions with the dis module, mapping opcodes like LOAD_CONST, LOAD_FAST, and BINARY_OP to their byte values, and implementing labels, constants, and jumps in under 50 lines of Python. A complete factorial program is assembled and run against the toy VM as a working example. This is part one of a series reimplementing classic 'Great Languages' (assembly, BASIC, Forth, Lisp, APL, PL/0) in minimal code.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://zserge.com/posts/langs-asm>

## Questions this post answers

### How do I find the byte values of CPython bytecode instructions like LOAD_CONST or BINARY_OP?

Disassemble a Python function's compiled code with dis.dis() to see instruction names, then print factorial.__code__.co_code as hex to match numeric byte values to each opcode. For example, RESUME is 0x97, LOAD_FAST is 0x7c, LOAD_CONST is 0x64, COMPARE_OP is 0x6b, and RETURN_VALUE is 0x53 in a given CPython build.

_daily.dev surfaces deep dives like this for developers exploring VM internals and bytecode._

### What are the parameter values for BINARY_OP arithmetic operations in CPython bytecode?

BINARY_OP is a meta-instruction whose parameter selects the arithmetic operation: addition is 0, subtraction is 10, multiplication is 5, and division is 3. It is also 4 bytes long instead of the usual 2, since the parameter is a 2-byte little-endian value followed by a zero byte.

_Developers reverse-engineering VM bytecode can find similarly detailed breakdowns via daily.dev._

### Why does building a custom assembler require two passes over the source code?

A two-pass approach is needed because label addresses must be known before instructions referencing them can be assembled; the first pass records constants, variables, and label positions, while the second pass translates instructions into bytecode and resolves forward or backward label references into correct jump offsets.

_daily.dev helps developers learning compiler and assembler design find hands-on writeups like this._

## Similar posts on daily.dev

- [A 1024 Byte Python Interpreter](https://daily.dev/posts/a-1024-byte-python-interpreter-ombsbouew) · Hackaday · 0 upvotes · 0 comments
- [Micro language implementation: Calcium](https://daily.dev/posts/micro-language-implementation-calcium-7esmxmfk7) · Planet Python · 8 upvotes · 0 comments
- [Making a Python interpreter in 1024 bytes](https://daily.dev/posts/making-a-python-interpreter-in-1024-bytes-vi1kzt7ex) · Lobsters · 0 upvotes · 0 comments
- [Revisiting "Let's Build a Compiler"](https://daily.dev/posts/revisiting-let-s-build-a-compiler--usu6h8xiu) · Eli Bendersky · 0 upvotes · 0 comments
- [Lil' Fun Langs](https://daily.dev/posts/lil-fun-langs-0ekzoi6my) · Hacker News · 0 upvotes · 0 comments

---

Tags: [#python](https://daily.dev/tags/python), [#assembly](https://daily.dev/tags/assembly), [#cpython](https://daily.dev/tags/cpython)

[View this post on daily.dev](https://daily.dev/posts/tiny-great-languages-assembly-6tdaecfp3)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"Tiny Great Languages: Assembly","url":"https://daily.dev/posts/tiny-great-languages-assembly-6tdaecfp3","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/tiny-great-languages-assembly-6tdaecfp3"},"datePublished":"2026-08-23T10:20:14.948Z","dateModified":"2026-08-23T10:21:13.661Z","description":"A hands-on walkthrough of building a tiny two-pass assembler that targets the CPython VM's own bytecode instruction set. It covers disassembling Python...","image":"https://media.daily.dev/image/upload/s--HRgLpUt6--/f_auto/v1722860399/public/Placeholder%2003","thumbnailUrl":"https://media.daily.dev/image/upload/s--HRgLpUt6--/f_auto/v1722860399/public/Placeholder%2003","isAccessibleForFree":true,"articleSection":"zserge's blog","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"zserge's blog","logo":"https://media.daily.dev/image/upload/s--KQnFBOIT--/f_auto,q_auto/v1787480407/logos/zserge","url":"https://daily.dev/sources/zserge"},"commentCount":0,"discussionUrl":"https://daily.dev/posts/tiny-great-languages-assembly-6tdaecfp3","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":0},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":0}],"keywords":"python,assembly,cpython","timeRequired":"PT7M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"zserge's blog","item":"https://daily.dev/sources/zserge"},{"@type":"ListItem","position":3,"name":"Tiny Great Languages: Assembly"}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/tiny-great-languages-assembly-6tdaecfp3#faq","mainEntity":[{"@type":"Question","name":"How do I find the byte values of CPython bytecode instructions like LOAD_CONST or BINARY_OP?","acceptedAnswer":{"@type":"Answer","text":"Disassemble a Python function's compiled code with dis.dis() to see instruction names, then print factorial.__code__.co_code as hex to match numeric byte values to each opcode. For example, RESUME is 0x97, LOAD_FAST is 0x7c, LOAD_CONST is 0x64, COMPARE_OP is 0x6b, and RETURN_VALUE is 0x53 in a given CPython build. daily.dev surfaces deep dives like this for developers exploring VM internals and bytecode."}},{"@type":"Question","name":"What are the parameter values for BINARY_OP arithmetic operations in CPython bytecode?","acceptedAnswer":{"@type":"Answer","text":"BINARY_OP is a meta-instruction whose parameter selects the arithmetic operation: addition is 0, subtraction is 10, multiplication is 5, and division is 3. It is also 4 bytes long instead of the usual 2, since the parameter is a 2-byte little-endian value followed by a zero byte. Developers reverse-engineering VM bytecode can find similarly detailed breakdowns via daily.dev."}},{"@type":"Question","name":"Why does building a custom assembler require two passes over the source code?","acceptedAnswer":{"@type":"Answer","text":"A two-pass approach is needed because label addresses must be known before instructions referencing them can be assembled; the first pass records constants, variables, and label positions, while the second pass translates instructions into bytecode and resolves forward or backward label references into correct jump offsets. daily.dev helps developers learning compiler and assembler design find hands-on writeups like this."}}]}
```

