B Language Reference

B is a typeless systems programming language created by Ken Thompson and Dennis Ritchie at Bell Labs around 1969. It is the direct predecessor of C. In B, all data is a 16-bit machine word — there are no type declarations. A value can be an integer, a character, a memory address, or a boolean, all in the same variable.

Table of Contents

1. Philosophy2. Memory Model
3. Variables4. Expressions & Operators
5. Control Flow6. Functions
7. Arrays & Strings8. Preprocessor
9. Inline Functions10. Calling Convention
11. B vs C12. Complete Examples

1. Philosophy

B was designed for systems programming on the PDP-11, a 16-bit minicomputer. Its core principles are:

2. Memory Model

All values in B are 16-bit words (2 bytes). There is no concept of char or byte as a first-class type — characters are simply small integers (0-255) stored in a word.

ConceptB RepresentationExample
Integer16-bit signed word (-32768 to 32767)42, -1, 0xFFFF
Character8-bit value in low byte of a word'A' = 65, '\n' = 10
Memory address16-bit unsigned word0x0100, buf
Boolean0 = false, non-zero = trueConditional expressions
ArrayConsecutive words in memoryarr[10] = 20 bytes
StringArray of character values, null-terminated"hello"

Word addressing: Array indices are word-based. arr[1] accesses the second word, which is 2 bytes after arr[0].

3. Variables

3.1 Global Variables

Declared at the top level (outside any function). Placed in the DATA or BSS section. Initialized to zero. Visible to all functions in the same file.

counter;           /* single word, initialized to 0 */
buffer[256];        /* array of 256 words (512 bytes) */
flag, index;        /* multiple globals */

3.2 Local Variables

Declared with auto inside a function. Allocated on the stack frame when the function is called. Not preserved between calls (no recursion-safe locals in the original B, but this compiler does support reentrant functions via stack allocation). Initial value is undefined — always initialize before use.

auto i, j, k;       /* three local words */
auto buf[10];        /* local array of 10 words (20 bytes) */
auto x = 42;         /* initialization (compiler extension) */

3.3 External Symbols

Declared with extrn. Used for functions or variables defined in other modules or libraries. Must appear before the first use.

extrn printf;        /* external function */
extrn putchar;       /* external function */
extrn global_var;    /* external variable */

3.4 Parameters

Function parameters are declared in the function definition header. They behave like local variables initialized by the caller. The compiler accesses them via the frame pointer at positive offsets (ix+N on Z80, bp+N on 8086).

add(a, b) {
    return a + b;    /* a and b are parameters */
}

4. Expressions & Operators

4.1 Operator Precedence Table

PrecAssocOperatorsDescription
1L→R() [] .Function call, array index, member access
2R→L! ~ - ++ --Logical NOT, bitwise NOT, unary minus, increment, decrement
3L→R* / %Multiply, divide, modulo
4L→R+ -Add, subtract
5L→R<< >>Left shift, right shift
6L→R< > <= >=Relational comparison
7L→R== !=Equality, inequality
8L→R&Bitwise AND
9L→R^Bitwise XOR
10L→R|Bitwise OR
11L→R&&Logical AND (short-circuit)
12L→R||Logical OR (short-circuit)
13R→L= += -= *= /= %= &= |= ^=Assignment and compound assignment

4.2 Arithmetic

auto a, b, c;
a = 10;
b = 3;
c = a + b;      /* 13 */
c = a - b;      /* 7  */
c = a * b;      /* 30 */
c = a / b;      /* 3  (integer division, truncates toward 0) */
c = a % b;      /* 1  (remainder)   */
c = -a;         /* -10 (unary minus) */
c = a + b * 2;  /* 16 (operator precedence) */

4.3 Bitwise Operations

auto a, b;
a = 0xFF;       /* 11111111 */
b = 0x0F;       /* 00001111 */

a & b;           /* 0x0F = 00001111  (AND) */
a | b;           /* 0xFF = 11111111  (OR)  */
a ^ b;           /* 0xF0 = 11110000  (XOR) */
~a;              /* 0xFF00             (NOT, 16-bit) */
a << 2;         /* 0x03FC             (shift left) */
b >> 1;         /* 0x0007             (shift right) */

4.4 Comparison Operators

auto x, y;
x = 10; y = 3;

x < y;     /* 0 (false) */
x > y;     /* 1 (true)  */
x <= 10;   /* 1 (true)  */
y >= 3;    /* 1 (true)  */
x == 10;   /* 1 (true)  */
x != y;    /* 1 (true)  */

Comparisons return 1 for true and 0 for false. These are ordinary integer values that can be used in arithmetic.

4.5 Logical Operators (Short-Circuit)

auto a, b;
a = 10; b = 0;

a && b;    /* 0: a is true, b is false, result false */
a || b;    /* 1: a is true, short-circuits, result true  */
!a;        /* 0: logical NOT of non-zero is 0           */
!b;        /* 1: logical NOT of zero is 1               */

Short-circuit: In a && b, if a is 0, b is never evaluated. In a || b, if a is non-zero, b is never evaluated.

4.6 Increment and Decrement

auto i, j;
i = 5;
j = ++i;    /* i=6, j=6 (pre-increment) */
j = i++;    /* j=6, i=7 (post-increment) */
j = --i;    /* i=6, j=6 (pre-decrement) */
j = i--;    /* j=6, i=5 (post-decrement) */

Note: Prefix and postfix operators return values (rvalues), not lvalues. The increment/decrement applies to the storage location.

5. Control Flow

5.1 if / else

if (x > 0)
    putchar('+');
else if (x < 0)
    putchar('-');
else
    putchar('0');

/* Multi-statement blocks require braces */
if (x) {
    putchar('T');
    x = 0;
} else {
    putchar('F');
    x = 1;
}

5.2 while Loop

auto i;
i = 0;
while (i < 10) {
    putchar('0' + i);
    i = i + 1;
}

5.3 for Loop

auto i;
for (i = 0; i < 10; i = i + 1)
    putchar('0' + i);

/* Multiple expressions in for */
auto i, j;
for (i = 0, j = 9; i < j; i = i + 1, j = j - 1)
    putchar('.');

5.4 do / while

auto n;
n = 0;
do {
    putchar('0' + n);
    n = n + 1;
} while (n < 5);

5.5 break

break exits the innermost while, for, or do loop immediately:

auto i;
i = 0;
while (1) {
    if (i >= 10) break;
    putchar('0' + i);
    i = i + 1;
}

5.6 return

return expr; exits the current function and provides the return value to the caller. The return value is placed in HL (Z80/8080/8085) or AX (8086).

abs(n) {
    if (n < 0) return -n;
    return n;
}

6. Functions

6.1 Definition

name(param1, param2, ...) {
    declarations
    statements
}

6.2 Calling Functions

result = add(10, 20);
putchar('A');
printf("value = %d", 42);    /* printf from runtime library */

6.3 Nested Calls

auto r;
r = add(mul(2, 3), div(10, 2));  /* r = 6 + 5 = 11 */
printf("deep = %d", foo(bar(baz(1, 2), 3), 4));

6.4 Recursion

fib(n) {
    if (n < 2) return n;
    return fib(n - 1) + fib(n - 2);
}

B functions are fully reentrant and support recursion. Each call allocates a new stack frame.

6.5 Variable Arguments

B allows calling a function with fewer arguments than declared. Extra parameters will contain garbage values. This is used by printf:

printf(fmt, a, b, c, d, e, f, g, h) { ... }

printf("Hello");             /* only 1 arg used */
printf("%d %d", 10, 20);    /* 3 args used */

Stack layout for printf("x=%d y=%d", 42, 99):

Higher addresses:
    [param 8: h]     (garbage — not passed)
    [param 7: g]     (garbage)
    [param 6: f]     (garbage)
    [param 5: e]     (garbage)
    [param 4: d]     (garbage)
    [param 3: c]     (garbage)
    [param 2: b]     99   ← pushed 3rd
    [param 1: a]     42   ← pushed 2nd
    [param 0: fmt]   ptr  ← pushed 1st
    [return addr]
    [saved frame ptr]      ← frame pointer (ix/bp/bc)
Lower addresses (SP):

7. Arrays & Strings

7.1 Global Arrays

buf[256];           /* 256 words (512 bytes) in BSS */
arr[10];            /* 10 words */

main() {
    buf[0] = 65;    /* 'A' */
    buf[1] = 66;    /* 'B' */
    putchar(buf[0]); /* prints 'A' */
}

7.2 Local Arrays

main() {
    auto arr[10];   /* 10 words on stack frame */
    auto i;

    i = 0;
    while (i < 10) {
        arr[i] = i * 2;
        i = i + 1;
    }
}

7.3 Array Indexing

Arrays are word-indexed. arr[i] accesses the word at address arr + i * 2 (since each word is 2 bytes). The compiler multiplies the index by 2 automatically.

arr[0]    /* first word,  at address arr + 0 */
arr[1]    /* second word, at address arr + 2 */
arr[2]    /* third word,  at address arr + 4 */

7.4 Strings

B has a string literal type. String literals are stored in the DATA section as a sequence of bytes (one per character) followed by a null word (dw 0). Strings are accessed via peekb() for byte-by-byte reading.

extrn printf;
main() {
    printf("Hello world!");    /* string passed by address */
}

/* String storage (generated by compiler):
   .L1:  db "Hello world!"
         dw 0
*/

Manual string processing with peekb:

print_str(s) {
    auto i, ch;
    i = 0;
    ch = peekb(s + i);          /* read byte at s[i] */
    while (ch != 0) {
        putchar(ch);
        i = i + 1;
        ch = peekb(s + i);
    }
}

8. Preprocessor

The B compiler includes a C-compatible preprocessor that runs before lexing. Directives start with # at the beginning of a line.

8.1 #define

#define BUFSIZE 256
#define MAX(a, b) ((a) > (b) ? (a) : (b))

buf[BUFSIZE];
x = MAX(10, 20);

8.2 Conditional Compilation

#define DEBUG 1

#ifdef DEBUG
    printf("Debug mode\n");
#endif

#ifndef RELEASE
    printf("Development build\n");
#endif

8.3 #include

#include "myheader.h"    /* local file */
#include <stdlib.b>       /* include path */

9. Inline Functions (peek/poke)

The compiler recognizes four special function names and generates inline code instead of a function call. These provide direct memory access without the overhead of a call/return.

FunctionOperationZ80 Code8086 Code
peekb(addr) Read byte from memory
ld a, [hl]  ; or equivalent
mov bx, ax
xor ah, ah
mov al, [bx]
pokeb(addr, val) Write byte to memory
ld [hl], e  ; or equivalent
mov [bx], al
peekw(addr) Read 16-bit word from memory Implemented as *(addr) dereference
pokew(addr, val) Write 16-bit word to memory Implemented as *(addr) = val store
auto ch, addr;
addr = 0x8000;
pokeb(addr, 0x41);      /* write 'A' to memory location 0x8000 */
ch = peekb(addr);       /* read back → ch = 0x41 */

10. Inline Assembly

The asm() statement allows embedding raw assembly instructions directly in B code:

asm("ld a, 0");      /* Z80 */
asm("mov ax, 0");    /* 8086 */
asm("hlt");          /* halt CPU */

11. Calling Convention

AspectConvention
Parameter passingStack, right-to-left. Rightmost argument pushed first, at lowest address.
Evaluation orderRight-to-left (two-pass technique, no reversal step needed)
Stack cleanupCaller cleans. After call: add sp, N*2 where N is the argument count.
Return valueHL register pair (Z80/8080/8085), AX register (8086)
Frame pointerIX (Z80), BP (8086), BC (8080/8085)
Scratch registersAll registers except frame pointer and stack pointer may be modified by callee

For a detailed explanation of stack frames and register usage, see the Calling Convention page.

12. B vs C

FeatureBC
Type systemTypeless (one word type)Static types (int, char, long, struct)
Variable declarationauto x; (inside functions)int x; (anywhere)
Storage classesauto, extrnauto, static, extern, register
StructuresNot supportedstruct { ... }
PointersImplicit (word = address)Explicit (int *p)
Array/pointer equivalenceLimitedFull decay semantics
Ternary operator?: not availablea ? b : c
switch/caseNot availableFull support
Function prototypesNot neededRecommended
Standard libraryMinimal (printf, putchar, string fns)Full stdlib

13. Complete Examples

13.1 Hello World

extrn printf;

main() {
    printf("Hello, World!");
}

13.2 Character Output with putchar

extrn putchar;

main() {
    auto i;
    i = 65;              /* 'A' */
    while (i <= 90) {     /* 'A' through 'Z' */
        putchar(i);
        i = i + 1;
    }
    putchar('\n');
}

13.3 Factorial (Recursive)

fact(n) {
    if (n <= 1) return 1;
    return n * fact(n - 1);
}

13.4 String Length

strlen(s) {
    auto i;
    i = 0;
    while (peekb(s + i) != 0)
        i = i + 1;
    return i;
}

13.5 Simple Calculator

extrn putchar;
extrn printf;

calc(a, b, op) {
    if (op == '+') return a + b;
    if (op == '-') return a - b;
    if (op == '*') return a * b;
    if (op == '/') return a / b;
    return 0;
}

main() {
    auto r;
    r = calc(10, 3, '+');
    printf("10 + 3 = %d\n", r);
    r = calc(10, 3, '*');
    printf("10 * 3 = %d\n", r);
}

13.6 Array Fill and Sum

extrn printf;
arr[20];

main() {
    auto i, sum;
    i = 0;
    while (i < 20) {
        arr[i] = i * 3;
        i = i + 1;
    }

    sum = 0;
    i = 0;
    while (i < 20) {
        sum = sum + arr[i];
        i = i + 1;
    }
    printf("Sum of 0..19 * 3 = %d\n", sum);
}

13.7 Bitwise Flags

extrn printf;

#define FLAG_READ   1
#define FLAG_WRITE  2
#define FLAG_EXEC   4

main() {
    auto flags;
    flags = FLAG_READ | FLAG_WRITE;   /* 3 */

    if (flags & FLAG_READ)  printf("Readable\n");
    if (flags & FLAG_WRITE) printf("Writable\n");
    if (flags & FLAG_EXEC)  printf("Executable\n");

    flags = flags | FLAG_EXEC;        /* add exec */
    printf("New flags = %d\n", flags); /* 7 */
}

[B Compiler] · [Calling Convention] · [Samples] © 2025-2026 HC SDK