DEV Community

Cover image for Creating a code style guide
Andrew Owen
Andrew Owen

Posted on

Creating a code style guide

This article originally appeared on my personal dev blog: Byte High, No Limit.

I've written previously about the importance of a style guide for written content. The same is true for code. Arguably, it's more important because code is much harder to understand. When it comes to code style, you'll never find two developers who agree on everything. So my advice would be to get the input of everyone who touches the code and come to a consensus on what the style should be. And remember that it's a guide, not a rigid set of rules.

Because I spent nearly 15 years working as a technical writer, most of the coding I've done in my life has been for my own amusement. And that means I've been free to do what I like. But in that time I've seen some bad habits that I've tried to avoid. In particular, I've dealt a lot with Z80 assembly language source code. Most of it is horrible. My gripes include:

  • Single monolithic assembly files.
  • Written in CAPITALS.
  • Needless decoration on comments.
  • Too few comments.
  • Meaningless labels
  • Full of magic numbers
  • Pointless gaps between instructions and parameters
  • Enough whitespace to fill the Albert Hall
  • No API docs.
  • Numbers written as hex that should be written as decimal.
  • Bitwise instructions written as hex that should be written as binary.
  • Undocumented pseudo-op codes.

My own code style is essentially a reaction against this. I was also influenced by a document published by Nikos Drakos and Ken Clowes at the Computer Based Learning Unit of the University of Leeds, and the fact that I used to write SDK documentation for C++ developers. I will now attempt to formally document it. At some point in the future, I should extend it. Where information is missing, I defer to the C++ Core Guidelines.

Introduction

This document describes coding standards for Z80 assembly language programs. The following plug-ins are recommended for Visual Studio Code:

API

API calls should use a vector table so that relocation of the target code will not break existing applications. For example:

org $04c4;

    ;;
    ; open a file for appending if it exists
    ; @param IX - pointer to ASCIIZ file path
    ; @throws sets carry flag on error
    ;;
    SEFileAppend:
        jp v_open_w_append;                 // $00
Enter fullscreen mode Exit fullscreen mode

Characters

When comparing ASCII characters, use the character, not the code point. For example:

        cp '(';                             // opening parenthesis?
Enter fullscreen mode Exit fullscreen mode

Comments

In Z80, the semicolon ( ; ) denotes that a comment follows. However, it is most commonly used as a line terminator. Modern integrated development environments are set up to expect this. Therefore, every line should end with a semicolon. Comments should be preceded with a double slash ( // ) for visibility. Standalone comments should be written as:

    ; // this is a standalone comment
Enter fullscreen mode Exit fullscreen mode

Inline comments should start at the tenth tab stop (column 40), for example:

        di;                                 // interrupts off
Enter fullscreen mode Exit fullscreen mode

Comments should be as short as possible to convey what the code is doing. Where an instruction can only have one meaning, it is not necessary to comment the code.

Use capitals for register names in comments. For example:

        ld d, e;                            // E to D
Enter fullscreen mode Exit fullscreen mode

Data

So far as possible, data should be stored separately from code.

Headers

Use a definitions file to declare:

  • constants
  • macros
  • index register offsets

Index registers

If you are using a large range of index registers, for example as a set of variables, define the offset with the name of the variable and a leading underscore. For example: _kstate.

Line length

Lines should be no more than 80 characters in length for display on a standard terminal. In-line comments should occur at the 40 character point so that they will appear on the next line on a 40-column display.

Macros

Use macros sparingly. Different assemblers use different syntax, and converting them can be time-consuming. They work best for pseudo op codes.

Numbers

Define meaningful names for constants and variables. Avoid magic numbers.

Use decimal wherever possible.

Always use binary for bitwise operations (AND, OR, XOR). For example, and %00001111.

Write hexadecimal with lower case letters and a leading dollar sign. For example, $801c.

Pseudo op codes

Never use pseudo op codes. Support is inconsistent between assemblers. If you must, define a pseudo-op code as a macro.

Public documentation

Use the asmdoc Perl script to generate API docs for public routines.

    ;;
    ; Short description.
    ; @author Name of contributing author.
    ; @deprecated Version deprecated in and replacement module.
    ; @param HL - Register contents.
    ; @return Result in register, such as <code>HL</code>.
    ; @see <a href="www.example.com">External reference</a>.
    ; @since Version in which module was introduced.
    ; @throws Error number and description handled by RST 8 routine.
    ; @version Version in which the module was last udpated. 
    ;;
Enter fullscreen mode Exit fullscreen mode

Routines

A routine is somewhat analogous to a function in C. A public routine is preceded by an asmdoc definition. Routine and subroutine names are terminated by a colon. Instructions in the routine or subroutine are indented by one tab. For example:

     joystick:
        in a, (stick);                      // read joystick
        ld (jstate), a;                     // store in system variable
        ret;                                // end of subroutine
Enter fullscreen mode Exit fullscreen mode

Self-modifying code

Self-modifying code should be avoided with two exceptions:

  • When it is critical for speed of operation.
  • When it is critical for code compactness.

Source

Break source code into sets of related modules and include those modules in the main assembly file. Modules should start with a special comment. For example:

    ;;
    ;   // --- ARITHMETIC ROUTINES -------------------------------------------------
    ;;
    :
Enter fullscreen mode Exit fullscreen mode

Strings

User lowercase wherever possible. Use camelCase in preference to underscores ( _ ). Reserve CAPITALS for substitutions in comments. For example:

        xor a;                              // LD A, 0
Enter fullscreen mode Exit fullscreen mode

Whitespace

Uses tabs (one tab is four characters). This speeds up search and compile times.

Image: Detail from Rodnay Zaks’ “Programming the Z80” (Sybex)

Top comments (0)