KarolS / KarolS/millfork

Compiler assuming C clear when it isn't?

Open
#135 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Scala
Stars
279
Forks
21
PR merge metrics
No merged PRs in 30d

Description

Millfork has been great for developing my homebrew NES game, and so far it's been totally stable, but either I messed something up or the compiler is getting a bit overeager with an optimization. In my collision detection routine, I have this code:
```c
level_idx = (lo(player_x >> 4) & $1f) | (word(player_y & $f0) << 1)
if (hi(vel_x) & $80 == 0) {
level_idx += 1
}
if (solidity[level_shadow[level_idx]] || (player_y & $0f != 0 && solidity[level_shadow[level_idx+32]])) {
k += 1
}
```
Using optimization level 8 (same thing happens with 4), I get this assembly:
```
;
;line:89:main.mfk
; level_idx = (lo(player_x >> 4) & $1f) | (word(player_y & $f0) << 1)
LDA player_x
STA __reg
LDA player_x + 1
LSR
ROR __reg
LSR
ROR __reg
LSR
LDA __reg
ROR
ALR #$3E
STA __reg
TXA
PHA
LDA __reg
PHA
LDA player_y
AND #$F0
PHA
ROL
ROL
AND #1
STA __reg + 1
PLA
ASL
STA __reg
PLA
ORA __reg
STA main$level_idx
PLA
ORA __reg + 1
STA main$level_idx + 1
;
;line:90:main.mfk
; if (hi(vel_x) & $80 == 0) {
BIT vel_x + 1
BMI .fi__00221
;
;line:91:main.mfk
; level_idx += 1
INC main$level_idx
BNE .in__00220
INC main$level_idx + 1
.in__00220:
;
;line:90:main.mfk
; if (hi(vel_x) & $80 == 0) {
.fi__00221:
;
;line:93:main.mfk
; if (solidity[level_shadow[level_idx]] || (player_y & $0f != 0 && solidity[level_shadow[level_idx+32]])) {
LDY main$level_idx
LDA main$level_idx + 1
ADC #3 ; THIS IS WHERE THE BUG IS
STA __reg + 1
STX __reg
LDA (__reg), Y
TAY
LDA solidity.array, Y
BNE .or__00223
LDA player_y
AND #$F
BEQ .fi__00222
LDY main$level_idx
LDA main$level_idx + 1
CLC
ADC #3
STA __reg + 1
LDA #$20
STA __reg
LDA (__reg), Y
TAY
LDA solidity.array, Y
BEQ .fi__00222
.or__00223:
;
;line:94:main.mfk
; k += 1
INX
;
;line:93:main.mfk
; if (solidity[level_shadow[level_idx]] || (player_y & $0f != 0 && solidity[level_shadow[level_idx+32]])) {
.fi__00222:
```
`level_shadow` is located at `$0300`, so the address computation optimization makes sense, but that `ADC` is causing a bug when carry is set from the most recent `ASL`. When I add a `CLC` explicitly, or use optimization level 2, the bug disappears. Did I forget something or did the optimizer?

Contributor guide

Open the contributing guide

Research direction

Reproduce the shown collision-detection case with optimization level 8, then compare its generated assembly with optimization level 2 and with the explicit CLC variant. Focus on the level_shadow address computation at $0300 and the ADC shown after the preceding ASL; done means the optimized assembly preserves carry semantics and the reported case no longer misbehaves.

Written by the indexing model from the issue text.

Assessment

Tech stack
scala
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.