Home

Awesome

NPM CI CodeFactor Visual Studio Marketplace

Z80 Assembly meter in Visual Studio Code

The Z80 Assembly meter extension for Visual Studio Code meters clock cycles and bytecode size from Z80 assembly source code.

This extension meters timing in Z80 clock periods, referred to as T (time) cycles.

Index

Features

Select Z80 assembly source code to view clock cycles, mnemonic of the instruction, and/or bytecode size in the status bar. Click on either to copy the clock cycles and the bytecode size information to the clipboard.

Z80 Assembly meter <br>Theme: Dark- (Visual Studio) (Flat UI)

If there is no selection, the current line will be used.

As the MSX standard requires so-called M1 wait cycles, this extension also meters M1 wait cycles for Z80 timing calculations on MSX. For a good explanation on how to do Z80 timing calculations on MSX, please read Wait States from Grauw MSX Assembly Page.

In Amstrad CPC architecture, all instruction timings are stretched so that they are all multiples of a microsecond (1 µs), which is approximatively equivalent to the duration of a NOP instruction. This extension can meter duration in "number of NOPs" for timing calculations on Amstrad CPC.

ZX Spectrum Next Extended Z80 Instruction Set is supported.

Getting started

This extension can be installed standalone, but does not contribute any problem matcher, symbol provider, definition provider, or completion proproser for Z80 assembly.

Therefore, it is recommended to install this extension alongside other Z80-related extensions, such as:

Main settings

Status bar settings

These settings allow to fine-tune the information to be shown in the status bar item and its appearance.

<details> <summary>Status bar alignment settings</summary> </details> <details> <summary>Status bar information settings</summary> </details> <details> <summary>Status bar behaviour settings</summary> </details>

Assembler syntax settings

The main syntax of the assembler can be used to best suit one particular assembler. These settings allow to fine-tune the particular features (such as syntax of the labels, or support for fake instructions).

<details> <br>

As a summary, these are the default values of the assembler syntax settings, based on the value of the main z80-asm-meter.syntax setting:

Assembler syntax featureDefault valuedefaultglasspasmosjasmsjasmplusspasm-ngtniasm
labelColonOptionaldisabled--enabled-enabled-enabled
repeatnone---bracketsdot--
lineSeparatordisabled-----backslashpipe
fakeInstructionsdisabled----enabled--
registerListInstructionsdisabled----enabled--
negativeConditionsdisabled-enabled-----
dupEdupdisabled----enabled--
reptEndrdisabled----enabled--
reptEndmdisabled-enabled-----
</details>

Parser settings

These settings allow to fine-tune the source code parsing and metering.

<details> </details>

Advanced usage

Total timing calculations

When the selection covers several lines and encompasses a single subroutine, there are more than one way to calculate the total timing:

Total timing calculations

<details>

These are the three total timing calculation available:

  1. default: The default total timing calculation mode is the raw addition of the timings of the individual instructions.

  2. execution flow: When the selection is a single subroutine (i.e.: there are no unconditional JP, JR or RET instructions in the selection), the execution flow total timing calculation measures the timing of the execution flow through the selection (i.e.: to the next, non selected, instruction) by considering any DJNZ or conditional JP, JR or RET instruction as not taken.

  3. execution flow to the selected exit point: When the selection is a single subroutine and the selection ends at an exit point (a conditional or unconditional JP, JR or RET instruction) or calls a subroutine (a conditional or unconditional CALL), the execution flow to the selected exit point total timing calculation mode measures the timing of the execution flow to the selected exit point, by considering the last instruction as taken if it is a conditional instruction.

</details> <details> <summary>Total timing calculations settings</summary> </details> <details> <summary>Execution flow total timing calculation: settings</summary> </details> <details> <summary>Total timing calculation of the execution flow to the selected exit point: settings</summary> </details>

Inlay hints (experimental)

This extension can provide inlay hints (additional information about source code that is rendered inline). Particularly, it can show timing of the execution flow of subroutines (up to the first unconditional exit point), and timing of the execution flow up to conditional exit points.

Inlay hints

<details> <summary>Inlay hints settings</summary> </details>

Timing hints

Timing hints can be used to modify the timing of a particular instruction. The primary use case is to declare the timing of the subroutine being invoked by CALL or JP instructions.

For example:

Timing hints

<details>

A timing hint follows the pattern: [z80=27] or [msx=32/22] with the key being:

The timing can be either a single value or a pair of values separated by slash (/). This is convenient for taking into account different execution paths within the called routine:

In the example shown above:

Negative timings are supported. This may seem unintuitive, but serves very particular use cases including, but not limited to:

</details> <details> <summary>Timing hints settings</summary> </details>

User-defined macros

Macro definitions are not read from actual source code. They must provided in user settings in order to be detected and properly metered.

<details>

Macro definitions can be added to either user settings (settings.json) or workspace settings (.vscode/settings.json).

As most of the macro definition fields are optional, this extension uses a best-effort to meter a macro with the provided information. But, generally speaking, there are three ways to define a macro:

  1. Macro definition with instructions. Macro will be metered by aggregating the metrics of the instructions. For example:

    "z80-asm-meter.macros": [
        {
            "name": "ADD_HL_A",
            "instructions": [
                "ADD A, L",
                "LD L, A",
                "JR NC, zz",
                "INC H"
            ]
        }
    ]
    
  2. Macro definition with timing and size. Macro will be metered using the provided timing and/or size. For example:

    "z80-asm-meter.macros": [
        {
            "name": "ADD_HL_A",
            "z80": "24/19", // (note that there is no cpc timing,
            "msx": "28/23", //    so this macro won't be metered
            "size": 5       //    if platform=cpc)
        }
    ]
    
  3. Macro definition with both instructions and timing and/or size. Provided timing and/or size will override the metrics of the instructions. For example:

    "z80-asm-meter.macros": [
        {
            "name": "ADD_HL_A",
            "instructions": [
                "ADD A, L", "LD L, A", "JR NC, zz", "INC H"
            ],
            "msx": "23" // (overrides actual timing for platform=msx)
        }
    ]
    
</details> <details> <summary>Macros settings</summary> </details>

Migration to version 5.x

The Z80 Assembly meter extension started as a simple extension. To support different platforms, assembler syntaxes, macros, fake instructions, repetition blocks, and different total timing calculations, the extension grew and its configuration became cumbersome: some settings affected too many things, some other settings were unintuitive and caused undesired behaviour, etc.

Starting from version 5.0.0, the settings are properly grouped, are more fine grained, their default values make more sense for the majority of the users, and there are more customization options.

If you are migrating from any version prior to 5.x to version 5.x, some of your existing Z80 Assembly meter settings may have been moved or renamed, or may have changed its default value. Please update your settings accordingly by following the deprecation messages.

<details> <summary>Deprecated settings</summary>

Please find the deprecated settings, the last version where the setting was available, and the replacement setting or settings in the following table:

VersionDeprecated settingReplacement setting(s)
v4.3.0z80-asm-meter.viewInstructionz80-asm-meter.statusBar.showInstruction
v4.3.0z80-asm-meter.timing.modez80-asm-meter.statusBar.totalTimings
v4.3.0z80-asm-meter.viewBytesz80-asm-meter.statusBar.showBytes
v4.3.0z80-asm-meter.debouncez80-asm-meter.statusBar.debounce
v4.3.0z80-asm-meter.syntax.labelz80-asm-meter.syntax.label.colonOptional
v4.3.0z80-asm-meter.directivesAsInstructionsz80-asm-meter.parser.directives.defsAsInstructions
v4.3.0z80-asm-meter.timing.thresholdz80-asm-meter.timing.executionFlow.threshold<br>z80-asm-meter.timing.atExit.threshold
v4.3.0z80-asm-meter.timing.hintsz80-asm-meter.timing.hints.enabled
v5.1.0z80-asm-meter.statusBar.compactSizez80-asm-meter.statusBar.sizeSuffix
v5.1.0z80-asm-meter.timing.atExit.enabledz80-asm-meter.timing.atExit.retEnabled<br>z80-asm-meter.timing.atExit.jumpEnabled<br>z80-asm-meter.timing.atExit.callEnabled
v5.1.0z80-asm-meter.timing.atExit.iconz80-asm-meter.timing.atExit.jumpIcon<br>z80-asm-meter.timing.atExit.callIcon
v5.3.0z80-asm-meter.syntax.label.colonOptionalz80-asm-meter.syntaxFeature.labelColonOptional
v5.3.0z80-asm-meter.syntax.repeatz80-asm-meter.syntaxFeature.repeat
v5.3.0z80-asm-meter.syntax.lineSeparatorz80-asm-meter.syntaxFeature.lineSeparator
v5.3.0z80-asm-meter.syntax.enable.fakeInstructionsz80-asm-meter.syntaxFeature.fakeInstructions
v5.3.0z80-asm-meter.syntax.enable.registerListInstructionsz80-asm-meter.syntaxFeature.registerListInstructions
v5.3.0z80-asm-meter.syntax.enable.negativeConditionsz80-asm-meter.syntaxFeature.negativeConditions
v5.3.0z80-asm-meter.syntax.enable.dupEdupz80-asm-meter.syntaxFeature.dupEdup
v5.3.0z80-asm-meter.syntax.enable.reptEndrz80-asm-meter.syntaxFeature.reptEndr
v5.3.0z80-asm-meter.syntax.enable.reptEndmz80-asm-meter.syntaxFeature.reptEndm
</details>

Performance and efficiency

Users are encouraged to update to the newer versions for a more performant and efficient source code metering.

Roughly speaking, version 5.5.0 onwards the extension is about 15× to 20× faster than previous versions.

<details>

Early versions of this extension avoided metering more than once the same selection, and also prevented the metering to be triggered too frequently (debouncing).

From version 5.4.0 onwards, development has focused on performance improvements: now it uses a "Least Recently Used" (LRU) cache for previously metered selections, expensive RegExps have been replaced by lighter alternatives, and, since version 5.5.0, another internal LRU cache for instructions improves the performance when metering large source code blocks.

The following table compares the time took to meter 11 179 lines of source code (the fully annotated disassembly of King's Valley (© Konami 1985) by Manuel Pazos) using VS Code 1.92.1, Windows 10, AMD Ryzen 3 2200U:

Versionz80-asm-meter.parser.instructionsCacheSizeTime
5.3.5(not available)4 149 ms
5.4.0(not available)3 795 ms
5.5.0<br>5.5.1100 (default value)~398 ms
5.5.2100 (default value)242 ms
5.6.0 (preview)100 (default value)252 ms
5.6.1 (preview)100 (default value)230 ms
5.6.2 (preview)<br>5.6.3200 (new default value)~207 ms
5.6.4250 (new default value)165 ms
</details>

F.A.Q.

Q: The status bar does not display any information. I don't get clock cycles and bytecode size!

Double check the z80-asm-meter.languageIds setting in your settings.

Q: My macros are not recognized.

Macro definitions are not read from actual source code, but from user settings. Double check the z80-asm-meter.macros setting.

Q: How can I get clock cycles and bytecode size for in-lined assembly in my C files?

Double check the z80-asm-meter.languageIds setting in your settings. It has to include c:

"z80-asm-meter.languageIds": [ "c" ]

Q: I've added "z80-asm-meter.languageIds": [ "c", "s", "asm" ], but I only get clock cycles for in-lined assembly; now I don't get clock cycles in my assembly files!

The z80-asm-meter.languageIds setting uses language IDs, not extensions. Check the language ID of your assembly files and replace "s" and "asm" with that extension ID. Or use the default language IDs, then add "c":

"z80-asm-meter.languageIds": [ "asm-collection", "pasmo", "z80", "z80-asm", "z80-macroasm", "zeus-asm", "c" ]

Q: <some feature> stopped working after updating the extension.

Double check your settings for any deprecated setting that needs to be replaced.

Q: But that is your fault! You should support the deprecated settings for a few versions!

VS Code API support for deprecated settings does conflict with default values. I did my best to keep the extension backwards compatible, but it ended up being a hard-to-debug mess that failed most of the times.

From version 5.0.0 onwards I'll keep the deprecated setting section updated.

Q: The extension is too confusing; there are too many things in the status bar now.

There are new features, such as total timing calculations and timing hints, that are now enabled by default. The default values should be appropriate for the majority of the users, but if you are uncomfortable with the new features, or find them confusing, you can still disable them.

The shortest way to disable the new features is:

"z80-asm-meter.statusBar.totalTimings": "default",
"z80-asm-meter.timing.hints.enabled": "disabled"

Q: The extension does not follow the VS Code UX Guidelines given for the Status Bar.

The main issue is that, when using the default configuration, the extension uses more than one icon.

It is possible to use text instead of icons via configuration settings:

"z80-asm-meter.statusBar.timingsIcon": "$(watch) Ts ",
"z80-asm-meter.statusBar.sizeIcon": ", Sz ",
"z80-asm-meter.timing.executionFlow.icon": "↓", // UNICODE Downwards Arrow (U+2193)
"z80-asm-meter.timing.atExit.retIcon": "←",     // UNICODE Leftwards Arrow (U+2190)
"z80-asm-meter.timing.atExit.jumpIcon": "→",    // UNICODE Rightwards Arrow (U+2192)
"z80-asm-meter.timing.atExit.callIcon": "→",    // UNICODE Rightwards Arrow (U+2192)

Or, when showing the processed instruction in the status bar:

"z80-asm-meter.statusBar.showInstruction": true,
"z80-asm-meter.statusBar.instructionIcon": "$(watch)",
"z80-asm-meter.statusBar.timingsIcon": "Ts ",
"z80-asm-meter.statusBar.sizeIcon": ", Sz ",
"z80-asm-meter.timing.executionFlow.icon": "↓", // UNICODE Downwards Arrow (U+2193)
"z80-asm-meter.timing.atExit.retIcon": "←",     // UNICODE Leftwards Arrow (U+2190)
"z80-asm-meter.timing.atExit.jumpIcon": "→",    // UNICODE Rightwards Arrow (U+2192)
"z80-asm-meter.timing.atExit.callIcon": "→",    // UNICODE Rightwards Arrow (U+2192)

Credits

Coded by theNestruo (Néstor Sancho).