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.
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
z80-asm-meter.languageIds
: Additional language IDs for which the extension is enabled (such as "c", to meter in-lined assembly).
Defaults to: ["asm-collection", "pasmo", "z80", "z80-asm", "z80-macroasm", "zeus-asm"]
.
z80-asm-meter.platform
: Controls the instruction set to use and the timing information to display:
z80
(default): Uses the default Z80 instruction set and shows default timing information.
msx
: For MSX developers. Uses the default Z80 instruction set and shows Z80+M1 timing information (MSX standard).
pc8000
: For NEC PC-8000 series developers. Uses the default Z80 instruction set and conveniently shows both default Z80 timing and Z80+M1 timing information.
cpc
: For Amstrad CPC developers. Uses the default Z80 instruction set and shows timing measured in number of NOPs.
z80n
: For ZX Spectrum Next developers. Includes the ZX Spectrum Next Extended Z80 instruction set and shows default timing information.
z80-asm-meter.syntax
: Adjusts the main syntax of the assembler.
The main syntax of the assembler changes the default value of particular features (such as syntax of the labels, or support for fake instructions) to best suit one particular assembler. See assembler syntax settings for details.
default
(default): Matches most assemblers.
glass
: Matches Glass Z80 assembler syntax.
pasmo
: Matches Pasmo assembler syntax.
sjasm
: Matches Sjasm assembler syntax.
sjasmplus
: Matches SjASMPlus assembler syntax.
spasm-ng
: Matches SPASM-ng assembler syntax.
tniasm
: Matches tniASM assembler syntax.
z80-asm-meter.expandSelectionToLine
: When enabled, expands the selection to cover entire lines, preventing partial line selections to be mistakenly parsed (e.g.: RET
instead of RET Z
).
Enabled by default.
Status bar settings
These settings allow to fine-tune the information to be shown in the status bar item and its appearance.
Status bar alignment settings
Status bar information settings
z80-asm-meter.statusBar.showInstruction
: Shows the processed instruction in the status bar. Useful to check if the extension is mistaking instructions.
Disabled by default.
z80-asm-meter.statusBar.instructionIcon
: The icon to identify the instruction in the status bar. Any product icon, Unicode character, or plaint text can be used.
Defaults to: $(code)
.
z80-asm-meter.statusBar.timingsIcon
: The icon to identify the timings in the status bar. Any product icon, Unicode character, or plaint text can be used.
Defaults to: $(watch)
.
z80-asm-meter.statusBar.totalTimings
: Shows total timing calculations in the status bar.
See Total timing calculations for details.
z80-asm-meter.statusBar.totalTimingsOrder
: Determines the order of the total timing calculations in the status bar when more than one total timing calculation is visible.
See Total timing calculations for details.
z80-asm-meter.statusBar.sizeIcon
: The icon to identify the size and the bytes in the status bar. Any product icon, Unicode character, or plaint text can be used.
Defaults to: $(file-binary)
.
z80-asm-meter.statusBar.sizeNumericFormat
: The numerical format of the size in bytes in the status bar.
decimal
(default): Size in bytes as a decimal number.
hexadecimal
: Size in bytes as an hexadecimal number.
both
: Size in bytes as both a decimal and an hexadecimal number.
z80-asm-meter.statusBar.sizeHexadecimalFormat
: The hexadecimal format for the hexadecimal size in bytes in the status bar.
hash
: Hexadecimal size in bytes in hash format (#b45d
).
motorola
(default): Hexadecimal size in bytes in Motorola format ($b45d
).
intel
: Hexadecimal size in bytes in Intel format (0b45dh
).
intelUppercase
: Hexadecimal size in bytes in Intel format, uppercase suffix (0b45dH
).
cStyle
: Hexadecimal size in bytes in C-style format (0xb45d
).
uppercaseHash
: Hexadecimal size in bytes in uppercase, hash format (#B45D
).
uppercaseMotorola
: Hexadecimal size in bytes in uppercase, Motorola format ($B45D
).
uppercaseIntel
: Hexadecimal size in bytes in uppercase, Intel format, lowercase suffix (0B45Dh
).
uppercaseIntelUppercase
: Hexadecimal size in bytes in uppercase, Intel format (0B45DH
).
uppercaseCStyle
: Hexadecimal size in bytes in uppercase, C-style format (0xB45D
).
z80-asm-meter.statusBar.sizeSuffix
: The suffix for the size in bytes in the status bar. Either a single string (such as " B"
), or a couple of strings separated by pipe (|) for singular and plural.
Defaults to " byte| bytes"
.
z80-asm-meter.statusBar.showBytes
: Shows the bytes (opcode) in the status bar.
Disabled by default.
Status bar behaviour settings
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).
z80-asm-meter.syntaxFeature.labelColonOptional
: Adjusts the label detection to match the syntax of the assembler.
When disabled, the labels must be followed by a colon (:) and can be indented. This behaviour matches most assemblers and coding styles.
When enabled, the trailing colon is optional, and the labels must not be indented. This behaviour matches some assemblers such as Pasmo and SjASMPlus.
Enabled by default when the main syntax of the assembler is set to pasmo
or sjasmplus
, disabled by default otherwise.
z80-asm-meter.syntaxFeature.repeat
: Enables support for parsing repeat count:
none
(default): Disables repeat count.
brackets
: The repeat count is specified within square brackets ([
and ]
) before the instruction. This behaviour partially matches the source format of Sjasm, but multiple repeat counts and iteration count are not supported. This is the default value when the main syntax of the assembler is set to sjasm
.
dot
: The repeat count is specified after a dot (.
) before the instruction. This behaviour partially matches the repeat pseudo-op of SjASMPlus, but multiple repeat counts and expressions are not supported. This is the default value when the main syntax of the assembler is set to sjasmplus
.
z80-asm-meter.syntaxFeature.lineSeparator
: Adjusts the line separator to match the syntax of the assembler:
disabled
(default): Does not allow multiple instructions on a single line.
backslash
: Use backslash (\
) to have more than one instruction on a line. This behaviour matches some assemblers such as SPASM-ng. This is the default value when the main syntax of the assembler is set to spasm-ng
.
colon
: Use colon (:
) to have more than one instruction on a line.
pipe
: Use pipe (|
) to have more than one instruction on a line. This behaviour matches some assemblers such as tniASM. This is the default value when the main syntax of the assembler is set to tniasm
.
z80-asm-meter.syntaxFeature.fakeInstructions
: Enables SjASMPlus fake instructions support.
Enabled by default when the main syntax of the assembler is set to sjasmplus
, disabled by default otherwise.
z80-asm-meter.syntaxFeature.registerListInstructions
: Enables SjASMPlus register list instructions support.
Enabled by default when the main syntax of the assembler is set to sjasmplus
, disabled by default otherwise.
z80-asm-meter.syntaxFeature.negativeConditions
: Enables Glass negative conditions) support.
Enabled by default when the main syntax of the assembler is set to glass
, disabled by default otherwise.
z80-asm-meter.syntaxFeature.dupEdup
: Enables DUP
/EDUP
repetition blocks.
Enabled by default when the main syntax of the assembler is set to sjasmplus
, disabled by default otherwise.
z80-asm-meter.syntaxFeature.reptEndr
: Enables REPT
/ENDR
repetition blocks.
Enabled by default when the main syntax of the assembler is set to sjasmplus
, disabled by default otherwise.
z80-asm-meter.syntaxFeature.reptEndm
: Enables REPT
/ENDM
repetition blocks.
Enabled by default when the main syntax of the assembler is set to glass
, disabled by default otherwise.
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 feature |
Default value |
default |
glass |
pasmo |
sjasm |
sjasmplus |
spasm-ng |
tniasm |
labelColonOptional |
disabled |
- |
- |
enabled |
- |
enabled |
- |
enabled |
repeat |
none |
- |
- |
- |
brackets |
dot |
- |
- |
lineSeparator |
disabled |
- |
- |
- |
- |
- |
backslash |
pipe |
fakeInstructions |
disabled |
- |
- |
- |
- |
enabled |
- |
- |
registerListInstructions |
disabled |
- |
- |
- |
- |
enabled |
- |
- |
negativeConditions |
disabled |
- |
enabled |
- |
- |
- |
- |
- |
dupEdup |
disabled |
- |
- |
- |
- |
enabled |
- |
- |
reptEndr |
disabled |
- |
- |
- |
- |
enabled |
- |
- |
reptEndm |
disabled |
- |
enabled |
- |
- |
- |
- |
- |
Parser settings
These settings allow to fine-tune the source code parsing and metering.
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:
These are the three total timing calculation available:
default: The default total timing calculation mode is the raw addition of the timings of the individual instructions.
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.
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.
Total timing calculations settings
z80-asm-meter.statusBar.totalTimings
: Shows total timing calculations in the status bar.
all
: Shows all the total timing calculation that apply to the selection.
combineAll
: Shows all the total timing calculation that apply to the selection, but combined to reduce the size of the status bar item.
smart
(default): Shows total timing calculation that are relevant to the selection.
combineSmart
: Shows total timing calculation that are relevant to the selection, but combined to reduce the size of the status bar item.
best
: Shows the total timing calculation that best fits the selection.
default
: Does not show any alternative total timing calculation.
z80-asm-meter.statusBar.totalTimingsOrder
: Determines the order of the total timing calculations in the status bar when more than one total timing calculation is visible.
retFlowJumpCall
(default): At exit point (returns) first. Execution flow next. Other exit points (jumps and calls) last. This order is the most visual, with returns ("going back") to the left, execution flow ("going down") next, and jumps and calls ("going elsewhere") to the right.
flowRetJumpCall
: Execution flow first. Any exit points (returns, jumps and calls) last.
retJumpCallFlow
: Any exit point (returns, jumps and calls) first, execution flow last. This order matches the timing convention of single instructions, where the conditions met (returns, jumps and calls) are to the left and the conditions not met (execution flow) are to the right.
Execution flow total timing calculation: settings
Total timing calculation of the execution flow to the selected exit point: settings
z80-asm-meter.timing.atExit.retEnabled
: Enables total timing calculation of the execution flow to the selected exit point when the selection ends with a RET
, RETI
or RETN
instruction.
Enabled by default.
z80-asm-meter.timing.atExit.jumpEnabled
: Enables total timing calculation of the execution flow to the selected exit point when the selection ends with a DJNZ
, JP
or JR
instruction.
Enabled by default.
z80-asm-meter.timing.atExit.callEnabled
: Enables total timing calculation of the execution flow to the selected exit point when the selection ends with a CALL
or RST
instruction.
Disabled by default.
z80-asm-meter.timing.atExit.threshold
: Minimum number of instructions to be selected for the calculation of total timing calculation of the execution flow to the selected exit point.
Defaults to: 2
(2 instructions).
z80-asm-meter.timing.atExit.requireConditional
: Requires at least one conditional instruction to enable total timing calculation of the execution flow to the selected exit point.
Enabled by default.
z80-asm-meter.timing.atExit.stopOnUnconditionalJump
: Disables total timing calculation of the execution flow to the selected exit point if an unconditional JP
, JR
or RET
instruction is found.
Enabled by default.
z80-asm-meter.timing.atExit.retIcon
: Total timing calculation of the execution flow to the selected exit point (RET
, RETI
or RETN
instruction) icon in the status bar. Any product icon, Unicode character, or plaint text can be used.
Defaults to: $(debug-step-back)
.
z80-asm-meter.timing.atExit.jumpIcon
: Total timing calculation of the execution flow to the selected exit point (DJNZ
, JP
or JR
instruction) icon in the status bar. Any product icon, Unicode character, or plaint text can be used.
Defaults to: $(debug-step-out)
.
z80-asm-meter.timing.atExit.callIcon
: Total timing calculation of the execution flow to the selected exit point (CALL
or RST
instruction) icon in the status bar. Any product icon, Unicode character, or plaint text can be used.
Defaults to: $(debug-step-into)
.
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 settings
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:
A timing hint follows the pattern: [z80=27]
or [msx=32/22]
with the key being:
z80
for Z80 timings,
msx
or m1
for Z80+M1 timings,
cpc
for number of NOPs timings, or
t
or ts
for the timing to be used regardless the platform. Specific platform timing hints will take precedence over t
or ts
generic timing hints.
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:
CALL ADD_HL_A ; [msx=32/22]
will be metered as 50/40 Z80+M1 clock cycles, as the result of adding 18(/18) + 32/22.
CALL Z, ADD_HL_A ; [msx=32/22]
will be metered as 50/11 Z80+M1 clock cycles, as the result of adding 18/11 + 32(/0). Please note the second timing hint (22) will be ignored in conditional operations.
In the example shown above:
Of the timing hints of .OFF_SCREEN (41/28 clock cycles), the 41 has been added to the timing of conditional JR
instruction when the condition is taken (13 clock cycles). Please note the second timing hint (28) has been ignored, as there is only one possible timing for the taken condition path.
The timing hint of COORDS_TO_OFFSET (144 clock cycles) has been added to the timing of the CALL
instruction (18 clock cycles).
Negative timings are supported. This may seem unintuitive, but serves very particular use cases including, but not limited to:
Adjust the timing of an instruction in self-modifying code to match the timing of the replacement instruction.
Adjust timings when part of the code is to be skipped:
; (...)
jr z, .else
pop hl ; [msx=-100] will abort caller
.else:
; (...)
ret ; [msx=100] remaining code in the caller
Timing hints settings
z80-asm-meter.timing.hints.enabled
: Enables timing hints, read from the line comment:
none
: Disables timing hints.
subroutine
(default): Subroutine timing hint will be added to CALL
, DJNZ
, JP
, JR
, RET
or RST
instructions only. If the timing hint is a pair of values, both will be added to the current source code block timings. If the instruction is conditional, the timing hint will be added to the taken branch timing only.
any
: Any timing hint found, regardless the instruction. This includes empty (i.e.: no actual source code) lines.
ignoreCommentedOut
: Any timing hint found, regardless the instruction. This includes empty (i.e.: no actual source code) lines, but ignores empty lines that look like commented out source code.
z80-asm-meter.timing.hints.regexps
: An array of regexp-based user-defined timing hints. The line comment will be matched agains the regular expression and the timing hint values will be read from this configuration object.
pattern
: The pattern of the regular expression to match against the line comment.
flags
: The string indicating the flags of the regular expression to match against the line comment. Optional.
z80
: Declares or overrides Z80 default timing hint. Optional.
msx
: Declares or overrides Z80+M1 timing hint (MSX standard). Optional.
m1
: Declares or overrides Z80+M1 timing hint. Optional.
cpc
: Declares or overrides timing hint measured in number of NOPs. Optional.
t
: Declares or overrides default timing hint. Optional.
ts
: Declares or overrides default timing hint. Optional.
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.
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:
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"
]
}
]
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)
}
]
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)
}
]
Macros settings
z80-asm-meter.macros
: An array of user-defined macros:
name
: The name of the macro; will be matched against the mnemonic of the source code.
z80
: Declares or overrides Z80 default macro timing. Optional.
msx
: Declares or overrides Z80+M1 macro timing information (MSX standard). Optional.
m1
: Declares or overrides Z80+M1 macro timing information. Optional.
cpc
: Declares or overrides macro timing measured in number of NOPs. Optional.
t
: Declares or overrides default macro timing. Optional.
ts
: Declares or overrides default macro timing. Optional.
size
: Declares or overrides macro byte count. Optional.
instructions
: The macro definition, as instructions. Optional.
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.
Deprecated settings
Please find the deprecated settings, the last version where the setting was available, and the replacement setting or settings in the following table:
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.
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:
Version |
z80-asm-meter.parser.instructionsCacheSize |
Time |
5.3.5 |
(not available) |
4 149 ms |
5.4.0 |
(not available) |
3 795 ms |
5.5.0 5.5.1 |
100 (previous default value) |
398 ms |
5.5.2 |
100 (previous default value) |
242 ms |
5.5.2 |
500 |
202 ms |
5.6.0 (preview) |
100 (previous default value) |
252 ms |
5.6.1 (preview) |
100 (previous default value) |
230 ms |
5.6.2 (preview) |
200 (new default value) |
207 ms |
F.A.Q.
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).