Add __CYCLES__ variable to count cycles used by instructions (#2171)

This commit is contained in:
Rangi authored and GitHub committed 2026-09-27 19:59:32 -04:00
1 parent 6e2e2f1db7
commit 62a9d8d518
7 files changed
+192

No files matched your search

+1
View File
@@ -40,6 +40,7 @@ struct Section {
uint16_t alignOfs;
std::deque<Patch> patches;
std::vector<uint8_t> data;
uint32_t cyclesCounter; // `__CYCLES__` value; unsigned for well-defined overflow on increment
SectionTypeInfo const &typeInfo() const { return sectionTypeInfo[type]; }
+1
View File
@@ -83,6 +83,7 @@ Symbol *sym_RedefEqu(InternedStr symName, int32_t value);
Symbol *sym_AddVar(InternedStr symName, int32_t value);
int32_t sym_GetRSValue();
void sym_SetRSValue(int32_t value);
void sym_IncrementCYCLESValue(uint32_t delta);
// Find a symbol by exact name, bypassing expansion checks
Symbol *sym_FindExactSymbol(InternedStr symName);
// Find a symbol, possibly scoped, by name
+1
View File
@@ -1800,6 +1800,7 @@ The following symbols are defined by the assembler:
.It Dv . Ta Ic EQUS Ta The current global label scope
.It Dv .. Ta Ic EQUS Ta The current local label scope
.It Dv __SCOPE__ Ta Ic EQUS Ta The innermost current label scope level (empty, ".", or "..")
.It Dv __CYCLES__ Ta Ic = Ta Number of M-cycles used by instructions in the current section
.It Dv _RS Ta Ic = Ta _RS Counter
.It Dv _NARG Ta Ic EQU Ta Number of arguments passed to macro, updated by Ic SHIFT
.It Dv __ISO_8601_LOCAL__ Ta Ic EQUS Ta ISO 8601 timestamp (local)
+87
View File
@@ -1904,9 +1904,11 @@ sm83_adc:
SM83_ADC op_a_n {
sect_ConstByte(0xCE);
sect_RelByte($2, 1);
sym_IncrementCYCLESValue(2);
}
| SM83_ADC op_a_r {
sect_ConstByte(0x88 | $2);
sym_IncrementCYCLESValue($2 == REG_HL_IND ? 2 : 1);
}
;
@@ -1914,16 +1916,20 @@ sm83_add:
SM83_ADD op_a_n {
sect_ConstByte(0xC6);
sect_RelByte($2, 1);
sym_IncrementCYCLESValue(2);
}
| SM83_ADD op_a_r {
sect_ConstByte(0x80 | $2);
sym_IncrementCYCLESValue($2 == REG_HL_IND ? 2 : 1);
}
| SM83_ADD MODE_HL COMMA reg_ss {
sect_ConstByte(0x09 | ($4 << 4));
sym_IncrementCYCLESValue(2);
}
| SM83_ADD MODE_SP COMMA reloc_8bit_signed {
sect_ConstByte(0xE8);
sect_RelByte($4, 1);
sym_IncrementCYCLESValue(4);
}
;
@@ -1931,9 +1937,11 @@ sm83_and:
SM83_AND op_a_n {
sect_ConstByte(0xE6);
sect_RelByte($2, 1);
sym_IncrementCYCLESValue(2);
}
| SM83_AND op_a_r {
sect_ConstByte(0xA0 | $2);
sym_IncrementCYCLESValue($2 == REG_HL_IND ? 2 : 1);
}
;
@@ -1947,6 +1955,7 @@ sm83_bit:
} else {
sect_ConstByte(mask | ($2.value() << 3));
}
sym_IncrementCYCLESValue($4 == REG_HL_IND ? 3 : 2);
}
;
@@ -1954,16 +1963,19 @@ sm83_call:
SM83_CALL reloc_16bit {
sect_ConstByte(0xCD);
sect_RelWord($2, 1);
sym_IncrementCYCLESValue(6);
}
| SM83_CALL ccode_expr COMMA reloc_16bit {
sect_ConstByte(0xC4 | ($2 << 3));
sect_RelWord($4, 1);
sym_IncrementCYCLESValue(6);
}
;
sm83_ccf:
SM83_CCF {
sect_ConstByte(0x3F);
sym_IncrementCYCLESValue(1);
}
;
@@ -1971,60 +1983,72 @@ sm83_cp:
SM83_CP op_a_n {
sect_ConstByte(0xFE);
sect_RelByte($2, 1);
sym_IncrementCYCLESValue(2);
}
| SM83_CP op_a_r {
sect_ConstByte(0xB8 | $2);
sym_IncrementCYCLESValue($2 == REG_HL_IND ? 2 : 1);
}
;
sm83_cpl:
SM83_CPL {
sect_ConstByte(0x2F);
sym_IncrementCYCLESValue(1);
}
| SM83_CPL MODE_A {
sect_ConstByte(0x2F);
sym_IncrementCYCLESValue(1);
}
;
sm83_daa:
SM83_DAA {
sect_ConstByte(0x27);
sym_IncrementCYCLESValue(1);
}
;
sm83_dec:
SM83_DEC reg_r {
sect_ConstByte(0x05 | ($2 << 3));
sym_IncrementCYCLESValue($2 == REG_HL_IND ? 3 : 1);
}
| SM83_DEC reg_ss {
sect_ConstByte(0x0B | ($2 << 4));
sym_IncrementCYCLESValue(2);
}
;
sm83_di:
SM83_DI {
sect_ConstByte(0xF3);
sym_IncrementCYCLESValue(1);
}
;
sm83_ei:
SM83_EI {
sect_ConstByte(0xFB);
sym_IncrementCYCLESValue(1);
}
;
sm83_halt:
SM83_HALT {
sect_ConstByte(0x76);
sym_IncrementCYCLESValue(1);
}
;
sm83_inc:
SM83_INC reg_r {
sect_ConstByte(0x04 | ($2 << 3));
sym_IncrementCYCLESValue($2 == REG_HL_IND ? 3 : 1);
}
| SM83_INC reg_ss {
sect_ConstByte(0x03 | ($2 << 4));
sym_IncrementCYCLESValue(2);
}
;
@@ -2032,13 +2056,16 @@ sm83_jp:
SM83_JP reloc_16bit {
sect_ConstByte(0xC3);
sect_RelWord($2, 1);
sym_IncrementCYCLESValue(4);
}
| SM83_JP ccode_expr COMMA reloc_16bit {
sect_ConstByte(0xC2 | ($2 << 3));
sect_RelWord($4, 1);
sym_IncrementCYCLESValue(4);
}
| SM83_JP MODE_HL {
sect_ConstByte(0xE9);
sym_IncrementCYCLESValue(1);
}
;
@@ -2046,28 +2073,34 @@ sm83_jr:
SM83_JR reloc_16bit {
sect_ConstByte(0x18);
sect_PCRelByte($2, 1);
sym_IncrementCYCLESValue(3);
}
| SM83_JR ccode_expr COMMA reloc_16bit {
sect_ConstByte(0x20 | ($2 << 3));
sect_PCRelByte($4, 1);
sym_IncrementCYCLESValue(3);
}
;
sm83_ldi:
SM83_LDI LBRACK MODE_HL RBRACK COMMA MODE_A {
sect_ConstByte(0x02 | (2 << 4));
sym_IncrementCYCLESValue(2);
}
| SM83_LDI MODE_A COMMA LBRACK MODE_HL RBRACK {
sect_ConstByte(0x0A | (2 << 4));
sym_IncrementCYCLESValue(2);
}
;
sm83_ldd:
SM83_LDD LBRACK MODE_HL RBRACK COMMA MODE_A {
sect_ConstByte(0x02 | (3 << 4));
sym_IncrementCYCLESValue(2);
}
| SM83_LDD MODE_A COMMA LBRACK MODE_HL RBRACK {
sect_ConstByte(0x0A | (3 << 4));
sym_IncrementCYCLESValue(2);
}
;
@@ -2080,6 +2113,7 @@ sm83_ldh:
} else {
sect_ConstByte($4.value());
}
sym_IncrementCYCLESValue(3);
}
| SM83_LDH op_mem_ind COMMA MODE_A {
$2.addCheckHRAM();
@@ -2089,18 +2123,23 @@ sm83_ldh:
} else {
sect_ConstByte($2.value());
}
sym_IncrementCYCLESValue(3);
}
| SM83_LDH MODE_A COMMA c_ind {
sect_ConstByte(0xF2);
sym_IncrementCYCLESValue(2);
}
| SM83_LDH MODE_A COMMA ff00_c_ind {
sect_ConstByte(0xF2);
sym_IncrementCYCLESValue(2);
}
| SM83_LDH c_ind COMMA MODE_A {
sect_ConstByte(0xE2);
sym_IncrementCYCLESValue(2);
}
| SM83_LDH ff00_c_ind COMMA MODE_A {
sect_ConstByte(0xE2);
sym_IncrementCYCLESValue(2);
}
;
@@ -2130,10 +2169,12 @@ sm83_ld_hl:
SM83_LD MODE_HL COMMA MODE_SP op_sp_offset {
sect_ConstByte(0xF8);
sect_RelByte($5, 1);
sym_IncrementCYCLESValue(3);
}
| SM83_LD MODE_HL COMMA reloc_16bit {
sect_ConstByte(0x01 | (REG_HL << 4));
sect_RelWord($4, 1);
sym_IncrementCYCLESValue(3);
}
| SM83_LD MODE_HL COMMA reg_tt_no_af {
::error(
@@ -2148,6 +2189,7 @@ sm83_ld_hl:
sm83_ld_sp:
SM83_LD MODE_SP COMMA MODE_HL {
sect_ConstByte(0xF9);
sym_IncrementCYCLESValue(2);
}
| SM83_LD MODE_SP COMMA reg_bc_or_de {
::error("\"LD SP, %s\" is not a valid instruction", reg_tt_names[$4]);
@@ -2155,6 +2197,7 @@ sm83_ld_sp:
| SM83_LD MODE_SP COMMA reloc_16bit {
sect_ConstByte(0x01 | (REG_SP << 4));
sect_RelWord($4, 1);
sym_IncrementCYCLESValue(3);
}
;
@@ -2162,22 +2205,26 @@ sm83_ld_mem:
SM83_LD op_mem_ind COMMA MODE_SP {
sect_ConstByte(0x08);
sect_RelWord($2, 1);
sym_IncrementCYCLESValue(5);
}
| SM83_LD op_mem_ind COMMA MODE_A {
sect_ConstByte(0xEA);
sect_RelWord($2, 1);
sym_IncrementCYCLESValue(4);
}
;
sm83_ld_c_ind:
SM83_LD ff00_c_ind COMMA MODE_A {
sect_ConstByte(0xE2);
sym_IncrementCYCLESValue(2);
}
;
sm83_ld_rr:
SM83_LD reg_rr COMMA MODE_A {
sect_ConstByte(0x02 | ($2 << 4));
sym_IncrementCYCLESValue(2);
}
;
@@ -2185,12 +2232,14 @@ sm83_ld_r_no_a:
SM83_LD reg_r_no_a COMMA reloc_8bit {
sect_ConstByte(0x06 | ($2 << 3));
sect_RelByte($4, 1);
sym_IncrementCYCLESValue($2 == REG_HL_IND ? 3 : 2);
}
| SM83_LD reg_r_no_a COMMA reg_r {
if ($2 == REG_HL_IND && $4 == REG_HL_IND) {
::error("\"LD [HL], [HL]\" is not a valid instruction");
} else {
sect_ConstByte(0x40 | ($2 << 3) | $4);
sym_IncrementCYCLESValue($2 == REG_HL_IND ? 2 : 1);
}
}
;
@@ -2199,19 +2248,24 @@ sm83_ld_a:
SM83_LD reg_a COMMA reloc_8bit {
sect_ConstByte(0x06 | ($2 << 3));
sect_RelByte($4, 1);
sym_IncrementCYCLESValue(2);
}
| SM83_LD reg_a COMMA reg_r {
sect_ConstByte(0x40 | ($2 << 3) | $4);
sym_IncrementCYCLESValue($4 == REG_HL_IND ? 2 : 1);
}
| SM83_LD reg_a COMMA ff00_c_ind {
sect_ConstByte(0xF2);
sym_IncrementCYCLESValue(2);
}
| SM83_LD reg_a COMMA reg_rr {
sect_ConstByte(0x0A | ($4 << 4));
sym_IncrementCYCLESValue(2);
}
| SM83_LD reg_a COMMA op_mem_ind {
sect_ConstByte(0xFA);
sect_RelWord($4, 1);
sym_IncrementCYCLESValue(4);
}
;
@@ -2219,6 +2273,7 @@ sm83_ld_ss:
SM83_LD reg_bc_or_de COMMA reloc_16bit {
sect_ConstByte(0x01 | ($2 << 4));
sect_RelWord($4, 1);
sym_IncrementCYCLESValue(3);
}
| SM83_LD reg_bc_or_de COMMA reg_tt_no_af {
::error(
@@ -2238,6 +2293,7 @@ sm83_ld_ss:
sm83_nop:
SM83_NOP {
sect_ConstByte(0x00);
sym_IncrementCYCLESValue(1);
}
;
@@ -2245,21 +2301,25 @@ sm83_or:
SM83_OR op_a_n {
sect_ConstByte(0xF6);
sect_RelByte($2, 1);
sym_IncrementCYCLESValue(2);
}
| SM83_OR op_a_r {
sect_ConstByte(0xB0 | $2);
sym_IncrementCYCLESValue($2 == REG_HL_IND ? 2 : 1);
}
;
sm83_pop:
SM83_POP reg_tt {
sect_ConstByte(0xC1 | ($2 << 4));
sym_IncrementCYCLESValue(3);
}
;
sm83_push:
SM83_PUSH reg_tt {
sect_ConstByte(0xC5 | ($2 << 4));
sym_IncrementCYCLESValue(4);
}
;
@@ -2273,21 +2333,25 @@ sm83_res:
} else {
sect_ConstByte(mask | ($2.value() << 3));
}
sym_IncrementCYCLESValue($4 == REG_HL_IND ? 4 : 2);
}
;
sm83_ret:
SM83_RET {
sect_ConstByte(0xC9);
sym_IncrementCYCLESValue(4);
}
| SM83_RET ccode_expr {
sect_ConstByte(0xC0 | ($2 << 3));
sym_IncrementCYCLESValue(5);
}
;
sm83_reti:
SM83_RETI {
sect_ConstByte(0xD9);
sym_IncrementCYCLESValue(4);
}
;
@@ -2295,12 +2359,14 @@ sm83_rl:
SM83_RL reg_r {
sect_ConstByte(0xCB);
sect_ConstByte(0x10 | $2);
sym_IncrementCYCLESValue($2 == REG_HL_IND ? 4 : 2);
}
;
sm83_rla:
SM83_RLA {
sect_ConstByte(0x17);
sym_IncrementCYCLESValue(1);
}
;
@@ -2308,12 +2374,14 @@ sm83_rlc:
SM83_RLC reg_r {
sect_ConstByte(0xCB);
sect_ConstByte(0x00 | $2);
sym_IncrementCYCLESValue($2 == REG_HL_IND ? 4 : 2);
}
;
sm83_rlca:
SM83_RLCA {
sect_ConstByte(0x07);
sym_IncrementCYCLESValue(1);
}
;
@@ -2321,12 +2389,14 @@ sm83_rr:
SM83_RR reg_r {
sect_ConstByte(0xCB);
sect_ConstByte(0x18 | $2);
sym_IncrementCYCLESValue($2 == REG_HL_IND ? 4 : 2);
}
;
sm83_rra:
SM83_RRA {
sect_ConstByte(0x1F);
sym_IncrementCYCLESValue(1);
}
;
@@ -2334,12 +2404,14 @@ sm83_rrc:
SM83_RRC reg_r {
sect_ConstByte(0xCB);
sect_ConstByte(0x08 | $2);
sym_IncrementCYCLESValue($2 == REG_HL_IND ? 4 : 2);
}
;
sm83_rrca:
SM83_RRCA {
sect_ConstByte(0x0F);
sym_IncrementCYCLESValue(1);
}
;
@@ -2351,6 +2423,7 @@ sm83_rst:
} else {
sect_ConstByte(0xC7 | $2.value());
}
sym_IncrementCYCLESValue(4);
}
;
@@ -2358,15 +2431,18 @@ sm83_sbc:
SM83_SBC op_a_n {
sect_ConstByte(0xDE);
sect_RelByte($2, 1);
sym_IncrementCYCLESValue(2);
}
| SM83_SBC op_a_r {
sect_ConstByte(0x98 | $2);
sym_IncrementCYCLESValue($2 == REG_HL_IND ? 2 : 1);
}
;
sm83_scf:
SM83_SCF {
sect_ConstByte(0x37);
sym_IncrementCYCLESValue(1);
}
;
@@ -2380,6 +2456,7 @@ sm83_set:
} else {
sect_ConstByte(mask | ($2.value() << 3));
}
sym_IncrementCYCLESValue($4 == REG_HL_IND ? 4 : 2);
}
;
@@ -2387,6 +2464,7 @@ sm83_sla:
SM83_SLA reg_r {
sect_ConstByte(0xCB);
sect_ConstByte(0x20 | $2);
sym_IncrementCYCLESValue($2 == REG_HL_IND ? 4 : 2);
}
;
@@ -2394,6 +2472,7 @@ sm83_sra:
SM83_SRA reg_r {
sect_ConstByte(0xCB);
sect_ConstByte(0x28 | $2);
sym_IncrementCYCLESValue($2 == REG_HL_IND ? 4 : 2);
}
;
@@ -2401,6 +2480,7 @@ sm83_srl:
SM83_SRL reg_r {
sect_ConstByte(0xCB);
sect_ConstByte(0x38 | $2);
sym_IncrementCYCLESValue($2 == REG_HL_IND ? 4 : 2);
}
;
@@ -2408,10 +2488,12 @@ sm83_stop:
SM83_STOP {
sect_ConstByte(0x10);
sect_ConstByte(0x00);
sym_IncrementCYCLESValue(1);
}
| SM83_STOP reloc_8bit {
sect_ConstByte(0x10);
sect_RelByte($2, 1);
sym_IncrementCYCLESValue(1);
}
;
@@ -2419,9 +2501,11 @@ sm83_sub:
SM83_SUB op_a_n {
sect_ConstByte(0xD6);
sect_RelByte($2, 1);
sym_IncrementCYCLESValue(2);
}
| SM83_SUB op_a_r {
sect_ConstByte(0x90 | $2);
sym_IncrementCYCLESValue($2 == REG_HL_IND ? 2 : 1);
}
;
@@ -2429,6 +2513,7 @@ sm83_swap:
SM83_SWAP reg_r {
sect_ConstByte(0xCB);
sect_ConstByte(0x30 | $2);
sym_IncrementCYCLESValue($2 == REG_HL_IND ? 4 : 2);
}
;
@@ -2436,9 +2521,11 @@ sm83_xor:
SM83_XOR op_a_n {
sect_ConstByte(0xEE);
sect_RelByte($2, 1);
sym_IncrementCYCLESValue(2);
}
| SM83_XOR op_a_r {
sect_ConstByte(0xA8 | $2);
sym_IncrementCYCLESValue($2 == REG_HL_IND ? 2 : 1);
}
;
+2
View File
@@ -374,6 +374,7 @@ static Section *createSection(
sect.bank = bank;
sect.align = alignment;
sect.alignOfs = alignOffset;
sect.cyclesCounter = 0;
out_RegisterNode(sect.src);
@@ -399,6 +400,7 @@ static Section *createSectionFragmentLiteral(Section const &parent) {
sect.bank = parent.bank == 0 ? UINT32_MAX : parent.bank;
sect.align = 0;
sect.alignOfs = 0;
sect.cyclesCounter = 0;
out_RegisterNode(sect.src);
+37
View File
@@ -44,6 +44,7 @@ static Symbol *SCOPESymbol;
static Symbol *globalScopeSymbol;
static Symbol *localScopeSymbol;
static Symbol *RSSymbol;
static Symbol *CYCLESSymbol;
static InternedStr PCName;
static InternedStr globalScopeName;
@@ -108,6 +109,15 @@ static std::shared_ptr<std::string> localScopeCallback() {
return std::make_shared<std::string>(localScope->name.str());
}
static int32_t CYCLESCallback() {
Section *section = sect_GetSymbolSection();
if (!section) {
error("`__CYCLES__` has no value outside of a section");
return 0;
}
return static_cast<int32_t>(section->cyclesCounter);
}
static int32_t PCCallback() {
return sect_GetSymbolSection()->org + sect_GetSymbolOffset();
}
@@ -329,6 +339,10 @@ Symbol *sym_FindScopedValidSymbol(InternedStr symName) {
if (sym == SCOPESymbol && !sect_GetSymbolSection()) {
return nullptr;
}
// `__CYCLES__` has no value outside of a section
if (sym == CYCLESSymbol && !sect_GetSymbolSection()) {
return nullptr;
}
return sym;
}
@@ -387,6 +401,15 @@ void sym_SetRSValue(int32_t value) {
RSSymbol->data = value;
}
void sym_IncrementCYCLESValue(uint32_t delta) {
// Calling this with no active section is an assembly error,
// but it will already have been reported
// by the instruction emission.
if (Section *section = sect_GetSymbolSection(); section) {
section->cyclesCounter += delta;
}
}
uint32_t Symbol::getConstantValue() const {
if (isConstant()) {
return getValue();
@@ -524,6 +547,15 @@ Symbol *sym_AddVar(InternedStr symName, int32_t value) {
} else if (sym->isDefined() && sym->type != SYM_VAR) {
alreadyDefinedError(*sym, sym->type == SYM_LABEL ? "label" : "constant");
return sym;
} else if (sym == CYCLESSymbol) {
if (Section *section = sect_GetSymbolSection(); section) {
// This does not call `updateSymbolFilename` because `__CYCLES__` should stay as a
// built-in symbol, not registered for output in the object file.
section->cyclesCounter = static_cast<uint32_t>(value);
} else {
error("`__CYCLES__` has no value outside of a section");
}
return sym;
} else {
updateSymbolFilename(*sym);
}
@@ -729,6 +761,11 @@ void sym_Init(time_t now) {
RSSymbol = sym_AddVar(intern("_RS"), 0);
RSSymbol->isBuiltin = true;
CYCLESSymbol = &createSymbol(intern("__CYCLES__"));
CYCLESSymbol->type = SYM_VAR;
CYCLESSymbol->data = CYCLESCallback;
CYCLESSymbol->isBuiltin = true;
sym_AddString(
intern("__RGBDS_VERSION__"), std::make_shared<std::string>(get_package_version_string())
)
+63
View File
@@ -0,0 +1,63 @@
assert !def(__CYCLES__)
SECTION "test", ROM0
assert def(__CYCLES__)
assert __CYCLES__ == 0
nop
assert __CYCLES__ == 1
ld a, 42
assert __CYCLES__ == 3
dec a
assert __CYCLES__ == 4
db $c9
assert __CYCLES__ == 4 ; data does not increment __CYCLES__
PUSHS
assert !def(__CYCLES__)
SECTION FRAGMENT "fragmented", ROM0
assert def(__CYCLES__)
assert __CYCLES__ == 0
add [hl]
assert __CYCLES__ == 2
add b
assert __CYCLES__ == 3
DEF __CYCLES__ = 1
assert __CYCLES__ == 1
ccf
assert __CYCLES__ == 2
POPS
assert __CYCLES__ == 4
SECTION UNION "united", WRAM0
assert __CYCLES__ == 0
DEF __CYCLES__ = 42
assert __CYCLES__ == 42
SECTION FRAGMENT "fragmented", ROM0
assert __CYCLES__ == 2 ; this fragment continues from the previous one
SECTION UNION "united", WRAM0
assert __CYCLES__ == 42 ; this union continues from the previous one
SECTION "rom code", ROM0
assert __CYCLES__ == 0
ld hl, STARTOF("ram code")
assert __CYCLES__ == 3
LOAD "ram code", HRAM
assert __CYCLES__ == 0
ld hl, @
assert __CYCLES__ == 3
inc hl
assert __CYCLES__ == 5
ENDL
assert __CYCLES__ == 3
ENDSECTION
assert !def(__CYCLES)