1
0
mirror of https://github.com/KarolS/millfork.git synced 2024-06-25 19:29:49 +00:00

Preliminary support for volatile variables

This commit is contained in:
Karol Stasiak 2018-12-16 21:07:04 +01:00
parent 5a99dc7293
commit 78afe3d5f5
23 changed files with 343 additions and 236 deletions

View File

@ -51,6 +51,9 @@ but you need to be careful with using absolute vs immediate addressing:
Any assembly opcode can be prefixed with `?`, which allows the optimizer change it or elide it if needed.
Opcodes without that prefix will be always compiled as written.
The '!' prefix marks the statement as volatile, which means it will be a subject to certain, but not all optimizations,
in order to preserve its semantics.
You can insert macros into assembly, by prefixing them with `+` and using the same syntax as in Millfork:
macro void run(byte x) {

View File

@ -70,6 +70,9 @@ but you need to be careful with using absolute vs immediate addressing:
Any assembly opcode can be prefixed with `?`, which allows the optimizer change it or elide it if needed.
Opcodes without that prefix will be always compiled as written.
The '!' prefix marks the statement as volatile, which means it will be a subject to certain, but not all optimizations,
in order to preserve its semantics.
You can insert macros into assembly, by prefixing them with `+` and using the same syntax as in Millfork:
macro void run(byte x) {

View File

@ -20,10 +20,14 @@ or a top level of a function (*local* variables).
Syntax:
`[segment(<segment>)] [<storage>] <type> <name> [@<address>] [= <initial_value>]`
`[segment(<segment>)] [volatile] [<storage>] <type> <name> [@<address>] [= <initial_value>]`
* `<segment>`: segment name; if absent, then defaults to `default`.
* `volatile` means that the variable is volatile.
The optimizer shouldn't remove or reorder accesses to volatile variables.
Volatile variables cannot be declared as `register` or `stack.
* `<storage>` can be only specified for local variables. It can be either `stack`, `static`, `register` or nothing.
`register` is only a hint for the optimizer.
See [the description of variable storage](../abi/variable-storage.md).

View File

@ -5,21 +5,21 @@
#endif
// CIA1
byte cia1_pra @$DC00
byte cia1_prb @$DC01
volatile byte cia1_pra @$DC00
volatile byte cia1_prb @$DC01
byte cia1_ddra @$DC02
byte cia1_ddrb @$DC03
byte cia2_pra @$DD00
byte cia2_prb @$DD01
volatile byte cia2_pra @$DD00
volatile byte cia2_prb @$DD01
byte cia2_ddra @$DD02
byte cia2_ddrb @$DD03
macro asm void cia_disable_irq() {
LDA #$7f
LDA $dc0d
LDA $dd0d
LDA $dc0d
LDA $dd0d
? LDA #$7f
! LDA $dc0d
! LDA $dd0d
! LDA $dc0d
! LDA $dd0d
}

View File

@ -24,5 +24,5 @@ byte sid_v3_cr @$D412
byte sid_v3_ad @$D413
byte sid_v3_sr @$D414
byte sid_paddle_x @$D419
byte sid_paddle_y @$D41A
volatile byte sid_paddle_x @$D419
volatile byte sid_paddle_y @$D41A

View File

@ -24,15 +24,15 @@ byte vic_spr7_x @$D00E
byte vic_spr7_y @$D00F
byte vic_spr_hi_x @$D010
byte vic_cr1 @$D011
byte vic_raster @$D012
volatile byte vic_raster @$D012
byte vic_lp_x @$D013
byte vic_lp_y @$D014
byte vic_spr_ena @$D015
byte vic_cr2 @$D016
byte vic_spr_exp_y @$D017
byte vic_mem @$D018
byte vic_irq @$D019
byte vic_irq_ena @$D01A
volatile byte vic_irq @$D019
volatile byte vic_irq_ena @$D01A
byte vic_spr_dp @$D01B
byte vic_spr_mcolor @$D01C
byte vic_spr_exp_x @$D01D

View File

@ -2,58 +2,58 @@
#warn nes_hardware module should be only used on NES/Famicom targets
#endif
byte ppu_ctrl @$2000
byte ppu_mask @$2001
byte ppu_status @$2002
byte oam_addr @$2003
byte oam_data @$2004
byte ppu_scroll @$2005
byte ppu_addr @$2006
byte ppu_data @$2007
byte oam_dma @$4014
volatile byte ppu_ctrl @$2000
volatile byte ppu_mask @$2001
volatile byte ppu_status @$2002
volatile byte oam_addr @$2003
volatile byte oam_data @$2004
volatile byte ppu_scroll @$2005
volatile byte ppu_addr @$2006
volatile byte ppu_data @$2007
volatile byte oam_dma @$4014
// TODO: better names
byte apu_pulse1_ctrl @$4000
byte apu_pulse1_sweep @$4001
word apu_pulse1_period @$4002
volatile byte apu_pulse1_ctrl @$4000
volatile byte apu_pulse1_sweep @$4001
volatile word apu_pulse1_period @$4002
byte apu_pulse2_ctrl @$4004
byte apu_pulse2_sweep @$4005
word apu_pulse2_period @$4006
volatile byte apu_pulse2_ctrl @$4004
volatile byte apu_pulse2_sweep @$4005
volatile word apu_pulse2_period @$4006
byte apu_triangle_unmute @$4008
word apu_triangle_period @$400A
volatile byte apu_triangle_unmute @$4008
volatile word apu_triangle_period @$400A
byte apu_noise_ctrl @$400C
byte apu_noise_period @$400E
volatile byte apu_noise_ctrl @$400C
volatile byte apu_noise_period @$400E
byte apu_dmc_ctrl @$4010
byte apu_dmc_load @$4011
byte apu_dmc_sample_addr @$4012
byte apu_dmc_sample_length @$4013
volatile byte apu_dmc_ctrl @$4010
volatile byte apu_dmc_load @$4011
volatile byte apu_dmc_sample_addr @$4012
volatile byte apu_dmc_sample_length @$4013
byte apu_status @$4015
byte apu_frame_counter @$4017
volatile byte apu_status @$4015
volatile byte apu_frame_counter @$4017
byte joypad1_ctrl @$4016
byte joypad2_ctrl @$4017
volatile byte joypad1_ctrl @$4016
volatile byte joypad2_ctrl @$4017
inline asm byte strobe_joypad() {
? LDA #1
STA joypad1_ctrl
LSR
STA joypad1_ctrl
! STA joypad1_ctrl
? LSR
! STA joypad1_ctrl
? RTS
}
inline asm byte read_joypad1() {
LDA joypad1_ctrl
! LDA joypad1_ctrl
? RTS
}
inline asm byte read_joypad2() {
LDA joypad2_ctrl
! LDA joypad2_ctrl
? RTS
}
@ -62,29 +62,29 @@ macro asm void simulate_reset() {
}
inline asm void ppu_set_addr(word ax) {
STX ppu_addr
STA ppu_addr
! STX ppu_addr
! STA ppu_addr
?RTS
}
inline asm byte read_ppu_status() {
LDA ppu_status
! LDA ppu_status
? RTS
}
inline asm void ppu_set_scroll(word a, word x) {
STA ppu_scroll
STX ppu_scroll
?RTS
! STA ppu_scroll
! STX ppu_scroll
? RTS
}
inline asm void ppu_write_data(byte a) {
STA ppu_data
?RTS
! STA ppu_data
? RTS
}
inline asm void ppu_oam_dma_write(byte a) {
STA oam_dma
?RTS
! STA oam_dma
? RTS
}

View File

@ -9,11 +9,11 @@ word reset_routine_addr @$FFFC
word irq_routine_addr @$FFFE
macro asm void poke(word const addr, byte a) {
STA addr
! STA addr
}
macro asm byte peek(word const addr) {
LDA addr
! LDA addr
}
macro asm void disable_irq() {
@ -33,7 +33,7 @@ asm byte hi_nibble_to_hex(byte a) {
}
asm byte lo_nibble_to_hex(byte a) {
AND #$F
! AND #$F
CLC
ADC #$30
CMP #$3A
@ -44,7 +44,7 @@ _lo_nibble_to_hex_lbl:
}
macro asm void panic() {
JSR _panic
? JSR _panic
}
array __constant8 = [8]

View File

@ -9,11 +9,11 @@
import i80_math
macro asm void poke(word const addr, byte a) {
LD (addr), A
! LD (addr), A
}
macro asm byte peek(word const addr) {
LD A, (addr)
! LD A, (addr)
}
macro asm void disable_irq() {
@ -51,5 +51,5 @@ _lo_nibble_to_hex_lbl:
}
macro asm void panic() {
CALL _panic
? CALL _panic
}

View File

@ -3,75 +3,75 @@
#warn vcs_hardware module should be only used on Atari 2600-compatible targets
#endif
byte VSYNC @$00
byte VBLANK @$01
byte WSYNC @$02
byte RSYNC @$03
byte NUSIZ0 @$04
byte NUSIZ1 @$05
byte COLUP0 @$06
byte COLUP1 @$07
byte COLUPF @$08
byte COLUBK @$09
byte CTRLPF @$0A
byte REFP0 @$0B
byte REFP1 @$0C
byte PF0 @$0D
byte PF1 @$0E
byte PF2 @$0F
byte RESP0 @$10
byte RESP1 @$11
byte RESM0 @$12
byte RESM1 @$13
byte RESBL @$14
byte AUDC0 @$15
byte AUDC1 @$16
byte AUDF0 @$17
byte AUDF1 @$18
byte AUDV0 @$19
byte AUDV1 @$1A
byte GRP0 @$1B
byte GRP1 @$1C
byte ENAM0 @$1D
byte ENAM1 @$1E
byte ENABL @$1F
byte HMP0 @$20
byte HMP1 @$21
byte HMM0 @$22
byte HMM1 @$23
byte HMBL @$24
byte VDELP0 @$25
byte VDELP1 @$26
byte VDELBL @$27
byte RESMP0 @$28
byte RESMP1 @$29
byte HMOVE @$2A
byte HMCLR @$2B
byte CXCLR @$2C
byte CXM0P @$30
byte CXM1P @$31
byte CXP0FB @$32
byte CXP1FB @$33
byte CXM0FB @$34
byte CXM1FB @$35
byte CXBLPF @$36
byte CXPPMM @$37
byte INPT0 @$38
byte INPT1 @$39
byte INPT2 @$3A
byte INPT3 @$3B
byte INPT4 @$3C
byte INPT5 @$3D
byte SWCHA @$0280
byte SWACNT @$0281
byte SWCHB @$0282
byte SWBCNT @$0283
byte INTIM @$0284
byte INSTAT @$0285
byte TIM1T @$0294
byte TIM8T @$0295
byte TIM64T @$0296
byte T1024T @$0297
volatile byte VSYNC @$00
volatile byte VBLANK @$01
volatile byte WSYNC @$02
volatile byte RSYNC @$03
volatile byte NUSIZ0 @$04
volatile byte NUSIZ1 @$05
volatile byte COLUP0 @$06
volatile byte COLUP1 @$07
volatile byte COLUPF @$08
volatile byte COLUBK @$09
volatile byte CTRLPF @$0A
volatile byte REFP0 @$0B
volatile byte REFP1 @$0C
volatile byte PF0 @$0D
volatile byte PF1 @$0E
volatile byte PF2 @$0F
volatile byte RESP0 @$10
volatile byte RESP1 @$11
volatile byte RESM0 @$12
volatile byte RESM1 @$13
volatile byte RESBL @$14
volatile byte AUDC0 @$15
volatile byte AUDC1 @$16
volatile byte AUDF0 @$17
volatile byte AUDF1 @$18
volatile byte AUDV0 @$19
volatile byte AUDV1 @$1A
volatile byte GRP0 @$1B
volatile byte GRP1 @$1C
volatile byte ENAM0 @$1D
volatile byte ENAM1 @$1E
volatile byte ENABL @$1F
volatile byte HMP0 @$20
volatile byte HMP1 @$21
volatile byte HMM0 @$22
volatile byte HMM1 @$23
volatile byte HMBL @$24
volatile byte VDELP0 @$25
volatile byte VDELP1 @$26
volatile byte VDELBL @$27
volatile byte RESMP0 @$28
volatile byte RESMP1 @$29
volatile byte HMOVE @$2A
volatile byte HMCLR @$2B
volatile byte CXCLR @$2C
volatile byte CXM0P @$30
volatile byte CXM1P @$31
volatile byte CXP0FB @$32
volatile byte CXP1FB @$33
volatile byte CXM0FB @$34
volatile byte CXM1FB @$35
volatile byte CXBLPF @$36
volatile byte CXPPMM @$37
volatile byte INPT0 @$38
volatile byte INPT1 @$39
volatile byte INPT2 @$3A
volatile byte INPT3 @$3B
volatile byte INPT4 @$3C
volatile byte INPT5 @$3D
volatile byte SWCHA @$0280
volatile byte SWACNT @$0281
volatile byte SWCHB @$0282
volatile byte SWBCNT @$0283
volatile byte INTIM @$0284
volatile byte INSTAT @$0285
volatile byte TIM1T @$0294
volatile byte TIM8T @$0295
volatile byte TIM64T @$0296
volatile byte T1024T @$0297
macro asm void wsync() {
@ -79,28 +79,28 @@ macro asm void wsync() {
}
macro asm void start_frame() {
LDA #0
STA VBLANK
LDA #2
STA VSYNC
STA WSYNC
STA WSYNC
STA WSYNC
LDA #0
STA VSYNC
LDX #37
? LDA #0
! STA VBLANK
? LDA #2
! STA VSYNC
! STA WSYNC
! STA WSYNC
! STA WSYNC
! LDA #0
! STA VSYNC
? LDX #37
start_frame_loop:
STA WSYNC
DEX
! STA WSYNC
? DEX
BNE start_frame_loop
}
macro asm void end_frame() {
LDA #%01000010
STA VBLANK
LDX #30
? LDA #%01000010
! STA VBLANK
? LDX #30
end_frame_loop:
STA WSYNC
DEX
! STA WSYNC
? DEX
BNE end_frame_loop
}

View File

@ -414,12 +414,13 @@ object AssemblyLine {
???
}
} else {
val elidability = if (variable.isVolatile) Elidability.Volatile else Elidability.Elidable
variable match {
case v@MemoryVariable(_, _, VariableAllocationMethod.Zeropage) =>
List(AssemblyLine.zeropage(opcode, v.toAddress + offset))
case v@RelativeVariable(_, _, _, true, None) =>
List(AssemblyLine.zeropage(opcode, v.toAddress + offset))
case v: VariableInMemory => List(AssemblyLine.absolute(opcode, v.toAddress + offset))
List(AssemblyLine.zeropage(opcode, v.toAddress + offset).copy(elidability = elidability))
case v@RelativeVariable(_, _, _, true, None, _) =>
List(AssemblyLine.zeropage(opcode, v.toAddress + offset).copy(elidability = elidability))
case v: VariableInMemory => List(AssemblyLine.absolute(opcode, v.toAddress + offset).copy(elidability = elidability))
case v: StackVariable =>
AssemblyLine.tsx(ctx) :+ AssemblyLine.dataStackX(ctx, opcode, v, offset)
}
@ -520,8 +521,11 @@ case class AssemblyLine(opcode: Opcode.Value, addrMode: AddrMode.Value, var para
def mergePos(s: Seq[Option[SourceLine]]): AssemblyLine = if (s.isEmpty) this else pos(SourceLine.merge(this.source, s))
@inline
def elidable: Boolean = elidability == Elidability.Elidable
@inline
def notFixed: Boolean = elidability != Elidability.Fixed
import AddrMode._
import OpcodeClasses._

View File

@ -198,16 +198,16 @@ object AlwaysGoodOptimizations {
HasOpcode(ANC) & HasAddrMode(Immediate) & DoesntMatterWhatItDoesWith(State.C)) ~~> { (code, ctx) =>
AssemblyLine.immediate(LDA, CompoundConstant(MathOperator.And, NumericConstant(ctx.get[Int](0), 1), ctx.get[Constant](1)).quickSimplify) :: Nil
},
(Elidable & HasA(0) & HasOpcodeIn(ORA, EOR)) ~~> (code => code.map(_.copy(opcode = LDA))),
(Elidable & HasA(0) & HasOpcode(ADC) &
(NotFixed & HasA(0) & HasOpcodeIn(ORA, EOR)) ~~> (code => code.map(_.copy(opcode = LDA))),
(NotFixed & HasA(0) & HasOpcode(ADC) &
HasClear(State.D) & HasClear(State.C) &
// C stays cleared!
DoesntMatterWhatItDoesWith(State.V)) ~~> (code => code.map(_.copy(opcode = LDA))),
(Elidable & HasA(0) & HasOpcode(ADC) &
(NotFixed & HasA(0) & HasOpcode(ADC) &
HasClear(State.C) &
// C stays cleared!
DoesntMatterWhatItDoesWith(State.N, State.Z, State.V)) ~~> (code => code.map(_.copy(opcode = LDA))),
(Elidable & HasA(0xff) & HasOpcode(AND)) ~~> (code => code.map(_.copy(opcode = LDA))),
(NotFixed & HasA(0xff) & HasOpcode(AND)) ~~> (code => code.map(_.copy(opcode = LDA))),
(Elidable & HasA(0) & HasOpcode(AND)) ~~> (code => List(AssemblyLine.immediate(LDA, 0))),
(Elidable & HasX(0) & HasOpcode(XAA)) ~~> (code => List(AssemblyLine.immediate(LDA, 0))),
(Elidable & HasImmediate(0) & HasOpcode(AND)) ~~> (code => List(AssemblyLine.immediate(LDA, 0))),
@ -455,6 +455,21 @@ object AlwaysGoodOptimizations {
(Elidable & HasOpcode(TAZ) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~
(Linear & Not(ConcernsIZ) & DoesntChangeMemoryAt(0, 1)).* ~
(Elidable & HasOpcode(TZA) & DoesntMatterWhatItDoesWith(State.IZ)) ~~> (code => code.head :: (code.drop(2).init :+ code.head.copy(opcode = LDA))),
(NotFixed & HasOpcodeIn(LDA, STA) & IsZeroPage & MatchAddrMode(0) & MatchParameter(1)) ~
(Elidable & HasOpcode(TAX) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~
(Elidable & Linear & Not(ConcernsX) & DoesNotConcernMemoryAt(0, 1)).* ~
(Elidable & HasOpcode(TXA) & DoesntMatterWhatItDoesWith(State.X)) ~~> (code => code.head :: (code.drop(2).init :+ code.head.copy(opcode = LDA))),
(NotFixed & HasOpcodeIn(LDA, STA) & IsZeroPage & MatchAddrMode(0) & MatchParameter(1)) ~
(Elidable & HasOpcode(TAY) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~
(Elidable & Linear & Not(ConcernsY) & DoesNotConcernMemoryAt(0, 1)).* ~
(Elidable & HasOpcode(TYA) & DoesntMatterWhatItDoesWith(State.Y)) ~~> (code => code.head :: (code.drop(2).init :+ code.head.copy(opcode = LDA))),
(NotFixed & HasOpcodeIn(LDA, STA) & IsZeroPage & MatchAddrMode(0) & MatchParameter(1)) ~
(Elidable & HasOpcode(TAZ) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~
(Elidable & Linear & Not(ConcernsIZ) & DoesNotConcernMemoryAt(0, 1)).* ~
(Elidable & HasOpcode(TZA) & DoesntMatterWhatItDoesWith(State.IZ)) ~~> (code => code.head :: (code.drop(2).init :+ code.head.copy(opcode = LDA))),
)
private def operationPairBuilder(op1: Opcode.Value, op2: Opcode.Value, middle: AssemblyLinePattern, discardToRemove: Option[Opcode.Value]) = {

View File

@ -16,13 +16,13 @@ object CmosOptimizations {
val ZeroStoreAsStz = new RuleBasedAssemblyOptimization("Zero store",
needsFlowInfo = FlowInfoRequirement.ForwardFlow,
(HasA(0) & HasZ(0) & HasOpcode(STA) & Elidable & HasAddrModeIn(StzAddrModes)) ~~> {code =>
(HasA(0) & HasZ(0) & HasOpcode(STA) & NotFixed & HasAddrModeIn(StzAddrModes)) ~~> {code =>
code.head.copy(opcode = STZ) :: Nil
},
(HasX(0) & HasZ(0) & HasOpcode(STX) & Elidable & HasAddrModeIn(StzAddrModes)) ~~> {code =>
(HasX(0) & HasZ(0) & HasOpcode(STX) & NotFixed & HasAddrModeIn(StzAddrModes)) ~~> {code =>
code.head.copy(opcode = STZ) :: Nil
},
(HasY(0) & HasZ(0) & HasOpcode(STY) & Elidable & HasAddrModeIn(StzAddrModes)) ~~> {code =>
(HasY(0) & HasZ(0) & HasOpcode(STY) & NotFixed & HasAddrModeIn(StzAddrModes)) ~~> {code =>
code.head.copy(opcode = STZ) :: Nil
},
)

View File

@ -86,17 +86,17 @@ object LaterOptimizations {
val DoubleLoadToTwoRegistersWhenOneWillBeTrashed = new RuleBasedAssemblyOptimization("Double load to two registers when one will be trashed",
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
(Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1) & Not(ConcernsX)) ~
(Elidable & HasOpcode(STA) & HasAddrModeIn(StxAddrModes) & DoesNotConcernMemoryAt(0, 1) & Not(ConcernsX)).+ ~
(NotFixed & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1) & Not(ConcernsX)) ~
(NotFixed & HasOpcode(STA) & HasAddrModeIn(StxAddrModes) & DoesNotConcernMemoryAt(0, 1) & Not(ConcernsX)).+ ~
(Elidable & (HasOpcode(TAX) | HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1) & Not(ConcernsX)) & DoesntMatterWhatItDoesWith(State.A)) ~~> (_.init.map(a2x)),
(Elidable & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1) & Not(ConcernsY)) ~
(Elidable & HasOpcode(STA) & HasAddrModeIn(StyAddrModes) & DoesNotConcernMemoryAt(0, 1) & Not(ConcernsY)).+ ~
(NotFixed & HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1) & Not(ConcernsY)) ~
(NotFixed & HasOpcode(STA) & HasAddrModeIn(StyAddrModes) & DoesNotConcernMemoryAt(0, 1) & Not(ConcernsY)).+ ~
(Elidable & (HasOpcode(TAY) | HasOpcode(LDA) & MatchAddrMode(0) & MatchParameter(1) & Not(ConcernsY)) & DoesntMatterWhatItDoesWith(State.A)) ~~> (_.init.map(a2y)),
(Elidable & HasOpcode(LDX) & MatchAddrMode(0) & MatchParameter(1)) ~
(Elidable & HasOpcode(STX) & HasAddrModeIn(StaAddrModes) & DoesNotConcernMemoryAt(0, 1)).+ ~
(NotFixed & HasOpcode(LDX) & MatchAddrMode(0) & MatchParameter(1)) ~
(NotFixed & HasOpcode(STX) & HasAddrModeIn(StaAddrModes) & DoesNotConcernMemoryAt(0, 1)).+ ~
(Elidable & (HasOpcode(TXA) | HasOpcode(LDX) & MatchAddrMode(0) & MatchParameter(1)) & DoesntMatterWhatItDoesWith(State.X)) ~~> (_.init.map(x2a)),
(Elidable & HasOpcode(LDY) & MatchAddrMode(0) & MatchParameter(1)) ~
(Elidable & HasOpcode(STY) & HasAddrModeIn(StaAddrModes) & DoesNotConcernMemoryAt(0, 1)).+ ~
(NotFixed & HasOpcode(LDY) & MatchAddrMode(0) & MatchParameter(1)) ~
(NotFixed & HasOpcode(STY) & HasAddrModeIn(StaAddrModes) & DoesNotConcernMemoryAt(0, 1)).+ ~
(Elidable & (HasOpcode(TYA) | HasOpcode(LDY) & MatchAddrMode(0) & MatchParameter(1)) & DoesntMatterWhatItDoesWith(State.Y)) ~~> (_.init.map(y2a)),
)

View File

@ -744,7 +744,12 @@ case class EitherPattern(l: AssemblyLinePattern, r: AssemblyLinePattern) extends
case object Elidable extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
line.elidability == Elidability.Elidable
line.elidable
}
case object NotFixed extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
line.notFixed
}
case object DebugMatching extends AssemblyPattern {

View File

@ -88,6 +88,10 @@ object ZLine {
import ZOpcode._
import ZRegister._
private def elidability(source: ThingInMemory): Elidability.Value = {
if (source.isVolatile) Elidability.Volatile else Elidability.Elidable
}
def label(label: String): ZLine = ZLine.label(Label(label))
def label(label: Label): ZLine = ZLine(LABEL, NoRegisters, label.toAddress)
@ -148,22 +152,50 @@ object ZLine {
def ldImm16(target: ZRegister.Value, source: Constant): ZLine = ZLine(LD_16, TwoRegisters(target, IMM_16), source)
def ldAbs8(target: ZRegister.Value, source: ThingInMemory): ZLine = ZLine(LD, TwoRegisters(target, MEM_ABS_8), source.toAddress)
def ldAbs8(target: ZRegister.Value, source: ThingInMemory): ZLine =
ZLine(LD, TwoRegisters(target, MEM_ABS_8), source.toAddress, elidability = elidability(source))
def ldAbs16(target: ZRegister.Value, source: ThingInMemory): ZLine = ZLine(LD_16, TwoRegisters(target, MEM_ABS_16), source.toAddress)
def ldAbs16(target: ZRegister.Value, source: ThingInMemory): ZLine =
ZLine(LD_16, TwoRegisters(target, MEM_ABS_16), source.toAddress, elidability = elidability(source))
def ldAbs8(target: ZRegister.Value, source: ThingInMemory, offset: Int): ZLine =
ZLine(LD, TwoRegisters(target, MEM_ABS_8), source.toAddress + offset, elidability = elidability(source))
def ldAbs16(target: ZRegister.Value, source: ThingInMemory, offset: Int): ZLine =
ZLine(LD_16, TwoRegisters(target, MEM_ABS_16), source.toAddress + offset, elidability = elidability(source))
def ldAbs8(target: ZRegister.Value, source: Constant): ZLine = ZLine(LD, TwoRegisters(target, MEM_ABS_8), source)
def ldAbs16(target: ZRegister.Value, source: Constant): ZLine = ZLine(LD_16, TwoRegisters(target, MEM_ABS_16), source)
def ldAbs8(target: ThingInMemory, source: ZRegister.Value): ZLine = ZLine(LD, TwoRegisters(MEM_ABS_8, source), target.toAddress)
def ldAbs8(target: ZRegister.Value, source: Constant, elidability: Elidability.Value): ZLine =
ZLine(LD, TwoRegisters(target, MEM_ABS_8), source, elidability = elidability)
def ldAbs16(target: ThingInMemory, source: ZRegister.Value): ZLine = ZLine(LD_16, TwoRegisters(MEM_ABS_16, source), target.toAddress)
def ldAbs16(target: ZRegister.Value, source: Constant, elidability: Elidability.Value): ZLine =
ZLine(LD_16, TwoRegisters(target, MEM_ABS_16), source, elidability = elidability)
def ldAbs8(target: ThingInMemory, source: ZRegister.Value): ZLine =
ZLine(LD, TwoRegisters(MEM_ABS_8, source), target.toAddress, elidability = elidability(target))
def ldAbs16(target: ThingInMemory, source: ZRegister.Value): ZLine =
ZLine(LD_16, TwoRegisters(MEM_ABS_16, source), target.toAddress, elidability = elidability(target))
def ldAbs8(target: ThingInMemory, source: ZRegister.Value, offset: Int): ZLine =
ZLine(LD, TwoRegisters(MEM_ABS_8, source), target.toAddress + offset, elidability = elidability(target))
def ldAbs16(target: ThingInMemory, source: ZRegister.Value, offset: Int): ZLine =
ZLine(LD_16, TwoRegisters(MEM_ABS_16, source), target.toAddress + offset, elidability = elidability(target))
def ldAbs8(target: Constant, source: ZRegister.Value): ZLine = ZLine(LD, TwoRegisters(MEM_ABS_8, source), target)
def ldAbs16(target: Constant, source: ZRegister.Value): ZLine = ZLine(LD_16, TwoRegisters(MEM_ABS_16, source), target)
def ldAbs8(target: Constant, source: ZRegister.Value, elidability: Elidability.Value): ZLine =
ZLine(LD, TwoRegisters(MEM_ABS_8, source), target, elidability = elidability)
def ldAbs16(target: Constant, source: ZRegister.Value, elidability: Elidability.Value): ZLine =
ZLine(LD_16, TwoRegisters(MEM_ABS_16, source), target, elidability = elidability)
def ldViaIx(target: ZRegister.Value, sourceOffset: Int): ZLine = ZLine(LD, TwoRegistersOffset(target, ZRegister.MEM_IX_D, sourceOffset), Constant.Zero)
def ldViaIx(targetOffset: Int, source: ZRegister.Value): ZLine = ZLine(LD, TwoRegistersOffset(ZRegister.MEM_IX_D, source, targetOffset), Constant.Zero)
@ -194,8 +226,12 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
def mergePos(s: Seq[Option[SourceLine]]): ZLine = if (s.isEmpty) this else pos(SourceLine.merge(this.source, s))
@inline
def elidable: Boolean = elidability == Elidability.Elidable
@inline
def notFixed: Boolean = elidability != Elidability.Fixed
override def sizeInBytes: Int = {
import ZOpcode._
import ZRegister._

View File

@ -722,6 +722,11 @@ case object Elidable extends AssemblyLinePattern {
line.elidable
}
case object NotFixed extends AssemblyLinePattern {
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: ZLine): Boolean =
line.notFixed
}
case object DebugMatching extends AssemblyPattern {
override def matchTo(ctx: AssemblyMatchingContext, code: List[(FlowInfo, ZLine)]): Option[List[(FlowInfo, ZLine)]] = {
println(ctx)

View File

@ -34,7 +34,7 @@ abstract class AbstractStatementPreprocessor(ctx: CompilationContext, statements
}
protected val reentrantVars: Set[String] = trackableVars.filter(v => env.get[Variable](v) match {
case _: StackVariable => true
case UninitializedMemoryVariable(_, _, VariableAllocationMethod.Auto, _, _) => ctx.options.flag(CompilationFlag.DangerousOptimizations)
case v:UninitializedMemoryVariable if v.alloc == VariableAllocationMethod.Auto => ctx.options.flag(CompilationFlag.DangerousOptimizations)
case _ => false
})
protected val nonreentrantVars: Set[String] = trackableVars -- reentrantVars

View File

@ -7,7 +7,6 @@ import millfork.assembly.mos.Opcode._
import millfork.assembly.mos._
import millfork.compiler._
import millfork.env._
import millfork.error.ConsoleLogger
import millfork.node._
import scala.collection.mutable

View File

@ -1,6 +1,7 @@
package millfork.compiler.z80
import millfork.CompilationFlag
import millfork.assembly.Elidability
import millfork.assembly.z80._
import millfork.compiler._
import millfork.env._
@ -248,7 +249,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
import ZRegister._
v.typ.size match {
case 0 => ???
case 1 => loadByte(v.toAddress, target)
case 1 => loadByte(v.toAddress, target, v.isVolatile)
case 2 => target match {
case ZExpressionTarget.NOTHING => Nil
case ZExpressionTarget.HL =>
@ -256,7 +257,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
List(ZLine.ldAbs16(HL, v))
} else {
// TODO: is it optimal?
List(ZLine.ldAbs8(A, v), ZLine.ld8(L, A), ZLine.ldAbs8(A, v.toAddress + 1), ZLine.ld8(H, A))
List(ZLine.ldAbs8(A, v), ZLine.ld8(L, A), ZLine.ldAbs8(A, v, 1), ZLine.ld8(H, A))
}
case ZExpressionTarget.EHL =>
// TODO: signed words
@ -264,7 +265,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
List(ZLine.ldAbs16(HL, v), ZLine.ldImm8(E, 0))
} else {
// TODO: is it optimal?
List(ZLine.ldAbs8(A, v), ZLine.ld8(L, A), ZLine.ldAbs8(A, v.toAddress + 1), ZLine.ld8(H, A), ZLine.ldImm8(E, 0))
List(ZLine.ldAbs8(A, v), ZLine.ld8(L, A), ZLine.ldAbs8(A, v, 1), ZLine.ld8(H, A), ZLine.ldImm8(E, 0))
}
case ZExpressionTarget.DEHL =>
// TODO: signed words
@ -272,7 +273,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
List(ZLine.ldAbs16(HL, v), ZLine.ldImm16(DE, 0))
} else {
// TODO: is it optimal?
List(ZLine.ldAbs8(A, v), ZLine.ld8(L, A), ZLine.ldAbs8(A, v.toAddress + 1), ZLine.ld8(H, A), ZLine.ldImm16(DE, 0))
List(ZLine.ldAbs8(A, v), ZLine.ld8(L, A), ZLine.ldAbs8(A, v, 1), ZLine.ld8(H, A), ZLine.ldImm16(DE, 0))
}
case ZExpressionTarget.BC =>
if (ctx.options.flag(CompilationFlag.EmitZ80Opcodes)) {
@ -281,7 +282,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
List(ZLine.ldAbs16(HL, v), ZLine.ld8(B, H), ZLine.ld8(C, L))
} else {
// TODO: is it optimal?
List(ZLine.ldAbs8(A, v), ZLine.ld8(C, A), ZLine.ldAbs8(A, v.toAddress + 1), ZLine.ld8(B, A))
List(ZLine.ldAbs8(A, v), ZLine.ld8(C, A), ZLine.ldAbs8(A, v, 1), ZLine.ld8(B, A))
}
case ZExpressionTarget.DE =>
if (ctx.options.flag(CompilationFlag.EmitZ80Opcodes)) {
@ -290,31 +291,31 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
List(ZLine.ldAbs16(HL, v), ZLine.ld8(D, H), ZLine.ld8(E, L))
} else {
// TODO: is it optimal?
List(ZLine.ldAbs8(A, v), ZLine.ld8(E, A), ZLine.ldAbs8(A, v.toAddress + 1), ZLine.ld8(D, A))
List(ZLine.ldAbs8(A, v), ZLine.ld8(E, A), ZLine.ldAbs8(A, v, 1), ZLine.ld8(D, A))
}
}
case 3 => target match {
case ZExpressionTarget.NOTHING => Nil
case ZExpressionTarget.EHL =>
if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)) {
List(ZLine.ldAbs16(HL, v), ZLine.ldAbs8(A, v.toAddress + 2), ZLine.ld8(E, A))
List(ZLine.ldAbs16(HL, v), ZLine.ldAbs8(A, v, 2), ZLine.ld8(E, A))
} else {
// TODO: is it optimal?
List(
ZLine.ldAbs8(A, v), ZLine.ld8(L, A),
ZLine.ldAbs8(A, v.toAddress + 1), ZLine.ld8(H, A),
ZLine.ldAbs8(A, v.toAddress + 2), ZLine.ld8(E, A))
ZLine.ldAbs8(A, v, 1), ZLine.ld8(H, A),
ZLine.ldAbs8(A, v, 2), ZLine.ld8(E, A))
}
case ZExpressionTarget.DEHL =>
// TODO: signed farwords
if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)) {
List(ZLine.ldAbs16(HL, v), ZLine.ldAbs8(A, v.toAddress + 2), ZLine.ld8(E, A), ZLine.ldImm8(D, 0))
List(ZLine.ldAbs16(HL, v), ZLine.ldAbs8(A, v, 2), ZLine.ld8(E, A), ZLine.ldImm8(D, 0))
} else {
// TODO: is it optimal?
List(
ZLine.ldAbs8(A, v), ZLine.ld8(L, A),
ZLine.ldAbs8(A, v.toAddress + 1), ZLine.ld8(H, A),
ZLine.ldAbs8(A, v.toAddress + 2), ZLine.ld8(E, A), ZLine.ldImm8(D, 0))
ZLine.ldAbs8(A, v, 1), ZLine.ld8(H, A),
ZLine.ldAbs8(A, v, 2), ZLine.ld8(E, A), ZLine.ldImm8(D, 0))
}
}
case 4 => target match {
@ -330,9 +331,9 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
// TODO: is it optimal?
List(
ZLine.ldAbs8(A, v), ZLine.ld8(L, A),
ZLine.ldAbs8(A, v.toAddress + 1), ZLine.ld8(H, A),
ZLine.ldAbs8(A, v.toAddress + 2), ZLine.ld8(E, A),
ZLine.ldAbs8(A, v.toAddress + 3), ZLine.ld8(D, A))
ZLine.ldAbs8(A, v, 1), ZLine.ld8(H, A),
ZLine.ldAbs8(A, v, 2), ZLine.ld8(E, A),
ZLine.ldAbs8(A, v, 3), ZLine.ld8(D, A))
}
}
}
@ -438,7 +439,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
}
case i: IndexedExpression =>
calculateAddressToHL(ctx, i) match {
case List(ZLine0(LD_16, TwoRegisters(ZRegister.HL, ZRegister.IMM_16), addr)) => loadByte(addr, target)
case List(ZLine0(LD_16, TwoRegisters(ZRegister.HL, ZRegister.IMM_16), addr)) => loadByte(addr, target, volatile = false)
case code => code ++ loadByteViaHL(target)
}
case SumExpression(params, decimal) =>
@ -985,15 +986,16 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
}
}
def loadByte(sourceAddr: Constant, target: ZExpressionTarget.Value): List[ZLine] = {
def loadByte(sourceAddr: Constant, target: ZExpressionTarget.Value, volatile: Boolean): List[ZLine] = {
val elidability = if (volatile) Elidability.Volatile else Elidability.Elidable
target match {
case ZExpressionTarget.NOTHING => Nil
case ZExpressionTarget.A => List(ZLine.ldAbs8(ZRegister.A, sourceAddr))
case ZExpressionTarget.HL => List(ZLine.ldAbs8(ZRegister.A, sourceAddr), ZLine.ld8(ZRegister.L, ZRegister.A), ZLine.ldImm8(ZRegister.H, 0))
case ZExpressionTarget.BC => List(ZLine.ldAbs8(ZRegister.A, sourceAddr), ZLine.ld8(ZRegister.C, ZRegister.A), ZLine.ldImm8(ZRegister.B, 0))
case ZExpressionTarget.DE => List(ZLine.ldAbs8(ZRegister.A, sourceAddr), ZLine.ld8(ZRegister.E, ZRegister.A), ZLine.ldImm8(ZRegister.D, 0))
case ZExpressionTarget.EHL => List(ZLine.ldAbs8(ZRegister.A, sourceAddr), ZLine.ld8(ZRegister.L, ZRegister.A), ZLine.ldImm8(ZRegister.H, 0), ZLine.ldImm8(ZRegister.E, 0))
case ZExpressionTarget.DEHL => List(ZLine.ldAbs8(ZRegister.A, sourceAddr), ZLine.ld8(ZRegister.L, ZRegister.A), ZLine.ldImm8(ZRegister.H, 0), ZLine.ldImm16(ZRegister.DE, 0))
case ZExpressionTarget.A => List(ZLine.ldAbs8(ZRegister.A, sourceAddr, elidability))
case ZExpressionTarget.HL => List(ZLine.ldAbs8(ZRegister.A, sourceAddr, elidability), ZLine.ld8(ZRegister.L, ZRegister.A), ZLine.ldImm8(ZRegister.H, 0))
case ZExpressionTarget.BC => List(ZLine.ldAbs8(ZRegister.A, sourceAddr, elidability), ZLine.ld8(ZRegister.C, ZRegister.A), ZLine.ldImm8(ZRegister.B, 0))
case ZExpressionTarget.DE => List(ZLine.ldAbs8(ZRegister.A, sourceAddr, elidability), ZLine.ld8(ZRegister.E, ZRegister.A), ZLine.ldImm8(ZRegister.D, 0))
case ZExpressionTarget.EHL => List(ZLine.ldAbs8(ZRegister.A, sourceAddr, elidability), ZLine.ld8(ZRegister.L, ZRegister.A), ZLine.ldImm8(ZRegister.H, 0), ZLine.ldImm8(ZRegister.E, 0))
case ZExpressionTarget.DEHL => List(ZLine.ldAbs8(ZRegister.A, sourceAddr, elidability), ZLine.ld8(ZRegister.L, ZRegister.A), ZLine.ldImm8(ZRegister.H, 0), ZLine.ldImm16(ZRegister.DE, 0))
}
}

View File

@ -34,7 +34,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
}
def genRelativeVariable(constant: Constant, typ: Type, zeropage: Boolean): RelativeVariable = {
val variable = RelativeVariable(nextLabel("rv"), constant, typ, zeropage = zeropage, declaredBank = None /*TODO*/)
val variable = RelativeVariable(nextLabel("rv"), constant, typ, zeropage = zeropage, declaredBank = None /*TODO*/, isVolatile = false)
addThing(variable, None)
variable
}
@ -83,7 +83,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
things.values.flatMap {
case RelativeArray(_, NumericConstant(addr, _), size, declaredBank, _, _) =>
List((declaredBank.getOrElse("default"), addr.toInt, size))
case RelativeVariable(_, NumericConstant(addr, _), typ, _, declaredBank) =>
case RelativeVariable(_, NumericConstant(addr, _), typ, _, declaredBank, _) =>
List((declaredBank.getOrElse("default"), addr.toInt, typ.size))
case f: NormalFunction =>
f.environment.getAllFixedAddressObjects
@ -830,11 +830,11 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
if (!thing.zeropage && options.flag(CompilationFlag.LUnixRelocatableCode)) {
val b = get[Type]("byte")
val w = get[Type]("word")
val relocatable = UninitializedMemoryVariable(thing.name + ".addr", w, VariableAllocationMethod.Static, None, defaultVariableAlignment(options, 2))
val relocatable = UninitializedMemoryVariable(thing.name + ".addr", w, VariableAllocationMethod.Static, None, defaultVariableAlignment(options, 2), isVolatile = false)
val addr = relocatable.toAddress
addThing(relocatable, position)
addThing(RelativeVariable(thing.name + ".addr.hi", addr + 1, b, zeropage = false, None), position)
addThing(RelativeVariable(thing.name + ".addr.lo", addr, b, zeropage = false, None), position)
addThing(RelativeVariable(thing.name + ".addr.hi", addr + 1, b, zeropage = false, None, isVolatile = false), position)
addThing(RelativeVariable(thing.name + ".addr.lo", addr, b, zeropage = false, None, isVolatile = false), position)
val rawaddr = thing.toAddress
addThing(ConstantThing(thing.name + ".rawaddr", rawaddr, get[Type]("pointer")), position)
addThing(ConstantThing(thing.name + ".rawaddr.hi", rawaddr.hiByte, get[Type]("byte")), position)
@ -858,12 +858,12 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
stmt.assemblyParamPassingConvention match {
case ByVariable(name) =>
val zp = typ.name == "pointer" // TODO
val v = UninitializedMemoryVariable(prefix + name, typ, if (zp) VariableAllocationMethod.Zeropage else VariableAllocationMethod.Auto, None, defaultVariableAlignment(options, 2))
val v = UninitializedMemoryVariable(prefix + name, typ, if (zp) VariableAllocationMethod.Zeropage else VariableAllocationMethod.Auto, None, defaultVariableAlignment(options, 2), isVolatile = false)
addThing(v, stmt.position)
registerAddressConstant(v, stmt.position, options)
val addr = v.toAddress
for((suffix, offset, t) <- getSubvariables(typ)) {
addThing(RelativeVariable(v.name + suffix, addr + offset, t, zeropage = zp, None), stmt.position)
addThing(RelativeVariable(v.name + suffix, addr + offset, t, zeropage = zp, None, isVolatile = v.isVolatile), stmt.position)
}
case ByMosRegister(_) => ()
case ByZRegister(_) => ()
@ -872,10 +872,10 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
addThing(v, stmt.position)
case ByReference(name) =>
val addr = UnexpandedConstant(prefix + name, typ.size)
val v = RelativeVariable(prefix + name, addr, p, zeropage = false, None)
val v = RelativeVariable(prefix + name, addr, p, zeropage = false, None, isVolatile = false)
addThing(v, stmt.position)
addThing(RelativeVariable(v.name + ".hi", addr + 1, b, zeropage = false, None), stmt.position)
addThing(RelativeVariable(v.name + ".lo", addr, b, zeropage = false, None), stmt.position)
addThing(RelativeVariable(v.name + ".hi", addr + 1, b, zeropage = false, None, isVolatile = false), stmt.position)
addThing(RelativeVariable(v.name + ".lo", addr, b, zeropage = false, None, isVolatile = false), stmt.position)
}
}
@ -966,23 +966,23 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
declaredBank = stmt.bank, indexType, b)
}
addThing(array, stmt.position)
registerAddressConstant(UninitializedMemoryVariable(stmt.name, p, VariableAllocationMethod.None, stmt.bank, NoAlignment), stmt.position, options)
registerAddressConstant(UninitializedMemoryVariable(stmt.name, p, VariableAllocationMethod.None, stmt.bank, NoAlignment, isVolatile = false), stmt.position, options)
val a = address match {
case None => array.toAddress
case Some(aa) => aa
}
addThing(RelativeVariable(stmt.name + ".first", a, b, zeropage = false,
declaredBank = stmt.bank), stmt.position)
declaredBank = stmt.bank, isVolatile = false), stmt.position)
if (options.flag(CompilationFlag.LUnixRelocatableCode)) {
val b = get[Type]("byte")
val w = get[Type]("word")
val relocatable = UninitializedMemoryVariable(stmt.name, w, VariableAllocationMethod.Static, None, NoAlignment)
val relocatable = UninitializedMemoryVariable(stmt.name, w, VariableAllocationMethod.Static, None, NoAlignment, isVolatile = false)
val addr = relocatable.toAddress
addThing(relocatable, stmt.position)
addThing(RelativeVariable(stmt.name + ".addr.hi", addr + 1, b, zeropage = false, None), stmt.position)
addThing(RelativeVariable(stmt.name + ".addr.lo", addr, b, zeropage = false, None), stmt.position)
addThing(RelativeVariable(stmt.name + ".array.hi", addr + 1, b, zeropage = false, None), stmt.position)
addThing(RelativeVariable(stmt.name + ".array.lo", addr, b, zeropage = false, None), stmt.position)
addThing(RelativeVariable(stmt.name + ".addr.hi", addr + 1, b, zeropage = false, None, isVolatile = false), stmt.position)
addThing(RelativeVariable(stmt.name + ".addr.lo", addr, b, zeropage = false, None, isVolatile = false), stmt.position)
addThing(RelativeVariable(stmt.name + ".array.hi", addr + 1, b, zeropage = false, None, isVolatile = false), stmt.position)
addThing(RelativeVariable(stmt.name + ".array.lo", addr, b, zeropage = false, None, isVolatile = false), stmt.position)
} else {
addThing(ConstantThing(stmt.name, a, p), stmt.position)
addThing(ConstantThing(stmt.name + ".hi", a.hiByte.quickSimplify, b), stmt.position)
@ -1035,21 +1035,21 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
val array = InitializedArray(stmt.name + ".array", address, contents, declaredBank = stmt.bank, indexType, b, alignment)
addThing(array, stmt.position)
registerAddressConstant(UninitializedMemoryVariable(stmt.name, p, VariableAllocationMethod.None,
declaredBank = stmt.bank, alignment), stmt.position, options)
declaredBank = stmt.bank, alignment, isVolatile = false), stmt.position, options)
val a = address match {
case None => array.toAddress
case Some(aa) => aa
}
addThing(RelativeVariable(stmt.name + ".first", a, b, zeropage = false,
declaredBank = stmt.bank), stmt.position)
declaredBank = stmt.bank, isVolatile = false), stmt.position)
if (options.flag(CompilationFlag.LUnixRelocatableCode)) {
val b = get[Type]("byte")
val w = get[Type]("word")
val relocatable = UninitializedMemoryVariable(stmt.name, w, VariableAllocationMethod.Static, None, NoAlignment)
val relocatable = UninitializedMemoryVariable(stmt.name, w, VariableAllocationMethod.Static, None, NoAlignment, isVolatile = false)
val addr = relocatable.toAddress
addThing(relocatable, stmt.position)
addThing(RelativeVariable(stmt.name + ".array.hi", addr + 1, b, zeropage = false, None), stmt.position)
addThing(RelativeVariable(stmt.name + ".array.lo", addr, b, zeropage = false, None), stmt.position)
addThing(RelativeVariable(stmt.name + ".array.hi", addr + 1, b, zeropage = false, None, isVolatile = false), stmt.position)
addThing(RelativeVariable(stmt.name + ".array.lo", addr, b, zeropage = false, None, isVolatile = false), stmt.position)
} else {
addThing(ConstantThing(stmt.name, a, p), stmt.position)
addThing(ConstantThing(stmt.name + ".hi", a.hiByte.quickSimplify, b), stmt.position)
@ -1064,9 +1064,6 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
}
def registerVariable(stmt: VariableDeclarationStatement, options: CompilationOptions): Unit = {
if (stmt.volatile) {
log.warn("`volatile` not yet supported", stmt.position)
}
val name = stmt.name
val position = stmt.position
if (stmt.stack && parent.isEmpty) {
@ -1103,6 +1100,8 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
if (stmt.register && typ.size != 1) log.error(s"A register variable `$name` is too large", position)
if (stmt.register && stmt.global) log.error(s"`$name` is static or global and cannot be in a register", position)
if (stmt.register && stmt.stack) log.error(s"`$name` cannot be simultaneously on stack and in a register", position)
if (stmt.volatile && stmt.stack) log.error(s"`$name` cannot be simultaneously on stack and volatile", position)
if (stmt.volatile && stmt.register) log.error(s"`$name` cannot be simultaneously volatile and in a register", position)
if (stmt.initialValue.isDefined && parent.isDefined) log.error(s"`$name` is local and not a constant and therefore cannot have a value", position)
if (stmt.initialValue.isDefined && stmt.address.isDefined) log.warn(s"`$name` has both address and initial value - this may not work as expected!", position)
if (stmt.register && stmt.address.isDefined) log.error(s"`$name` cannot by simultaneously at an address and in a register", position)
@ -1124,11 +1123,11 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
log.error(s"`$name` cannot be preinitialized`", position)
}
val v = stmt.initialValue.fold[MemoryVariable](UninitializedMemoryVariable(prefix + name, typ, alloc,
declaredBank = stmt.bank, alignment)){ive =>
declaredBank = stmt.bank, alignment, isVolatile = stmt.volatile)){ive =>
if (options.flags(CompilationFlag.ReadOnlyArrays)) {
log.warn("Initialized variable in read-only segment", position)
}
InitializedMemoryVariable(name, None, typ, ive, declaredBank = stmt.bank, alignment)
InitializedMemoryVariable(name, None, typ, ive, declaredBank = stmt.bank, alignment, isVolatile = stmt.volatile)
}
registerAddressConstant(v, stmt.position, options)
(v, v.toAddress)
@ -1139,7 +1138,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
case _ => false
}
val v = RelativeVariable(prefix + name, addr, typ, zeropage = zp,
declaredBank = stmt.bank)
declaredBank = stmt.bank, isVolatile = stmt.volatile)
registerAddressConstant(v, stmt.position, options)
(v, addr)
})
@ -1148,7 +1147,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
addThing(ConstantThing(v.name + "`", addr, b), stmt.position)
}
for((suffix, offset, t) <- getSubvariables(typ)) {
addThing(RelativeVariable(prefix + name + suffix, addr + offset, t, zeropage = v.zeropage, declaredBank = stmt.bank), stmt.position)
addThing(RelativeVariable(prefix + name + suffix, addr + offset, t, zeropage = v.zeropage, declaredBank = stmt.bank, isVolatile = v.isVolatile), stmt.position)
}
}
}
@ -1263,7 +1262,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
}
if (options.flag(CompilationFlag.SoftwareStack)) {
if (!things.contains("__sp")) {
things("__sp") = UninitializedMemoryVariable("__sp", b, VariableAllocationMethod.Auto, None, NoAlignment)
things("__sp") = UninitializedMemoryVariable("__sp", b, VariableAllocationMethod.Auto, None, NoAlignment, isVolatile = false)
things("__stack") = UninitializedArray("__stack", 256, None, b, b, WithinPageAlignment)
}
}

View File

@ -105,6 +105,8 @@ sealed trait ThingInMemory extends Thing {
def isFar(compilationOptions: CompilationOptions): Boolean
def bank(compilationOptions: CompilationOptions): String
def isVolatile: Boolean
}
sealed trait PreallocableThing extends ThingInMemory {
@ -129,9 +131,13 @@ case class Label(name: String) extends ThingInMemory {
override val declaredBank: Option[String] = None
override def zeropage: Boolean = false
override def isVolatile: Boolean = false
}
sealed trait Variable extends TypedThing with VariableLikeThing
sealed trait Variable extends TypedThing with VariableLikeThing {
def isVolatile: Boolean
}
sealed trait VariableInMemory extends Variable with ThingInMemory with IndexableThing {
@ -144,13 +150,18 @@ sealed trait VariableInMemory extends Variable with ThingInMemory with Indexable
case class RegisterVariable(register: MosRegister.Value, typ: Type) extends Variable {
def name: String = register.toString
override def isVolatile: Boolean = false
}
case class ZRegisterVariable(register: ZRegister.Value, typ: Type) extends Variable {
def name: String = register.toString
override def isVolatile: Boolean = false
}
case class Placeholder(name: String, typ: Type) extends Variable
case class Placeholder(name: String, typ: Type) extends Variable {
override def isVolatile: Boolean = false
}
sealed trait UninitializedMemory extends ThingInMemory {
def sizeInBytes: Int
@ -166,6 +177,7 @@ object VariableAllocationMethod extends Enumeration {
case class StackVariable(name: String, typ: Type, baseOffset: Int) extends Variable {
def sizeInBytes: Int = typ.size
override def isVolatile: Boolean = false
}
object MemoryVariable {
@ -176,7 +188,14 @@ abstract class MemoryVariable extends VariableInMemory {
def alloc: VariableAllocationMethod.Value
}
case class UninitializedMemoryVariable(name: String, typ: Type, alloc: VariableAllocationMethod.Value, declaredBank: Option[String], override val alignment: MemoryAlignment) extends MemoryVariable with UninitializedMemory {
case class UninitializedMemoryVariable(
name: String,
typ: Type,
alloc:
VariableAllocationMethod.Value,
declaredBank: Option[String],
override val alignment: MemoryAlignment,
override val isVolatile: Boolean) extends MemoryVariable with UninitializedMemory {
override def sizeInBytes: Int = typ.size
override def zeropage: Boolean = alloc == VariableAllocationMethod.Zeropage
@ -184,7 +203,14 @@ case class UninitializedMemoryVariable(name: String, typ: Type, alloc: VariableA
override def toAddress: MemoryAddressConstant = MemoryAddressConstant(this)
}
case class InitializedMemoryVariable(name: String, address: Option[Constant], typ: Type, initialValue: Expression, declaredBank: Option[String], override val alignment: MemoryAlignment) extends MemoryVariable with PreallocableThing {
case class InitializedMemoryVariable(
name: String,
address: Option[Constant],
typ: Type,
initialValue: Expression,
declaredBank: Option[String],
override val alignment: MemoryAlignment,
override val isVolatile: Boolean) extends MemoryVariable with PreallocableThing {
override def zeropage: Boolean = false
override def toAddress: MemoryAddressConstant = MemoryAddressConstant(this)
@ -197,6 +223,7 @@ case class InitializedMemoryVariable(name: String, address: Option[Constant], ty
trait MfArray extends ThingInMemory with IndexableThing {
def indexType: VariableType
def elementType: VariableType
override def isVolatile: Boolean = false
}
case class UninitializedArray(name: String, /* TODO: what if larger elements? */ sizeInBytes: Int, declaredBank: Option[String], indexType: VariableType, elementType: VariableType, override val alignment: MemoryAlignment) extends MfArray with UninitializedMemory {
@ -231,7 +258,7 @@ case class InitializedArray(name: String, address: Option[Constant], contents: L
override def zeropage: Boolean = false
}
case class RelativeVariable(name: String, address: Constant, typ: Type, zeropage: Boolean, declaredBank: Option[String]) extends VariableInMemory {
case class RelativeVariable(name: String, address: Constant, typ: Type, zeropage: Boolean, declaredBank: Option[String], override val isVolatile: Boolean) extends VariableInMemory {
override def toAddress: Constant = address
}
@ -282,6 +309,8 @@ case class ExternFunction(name: String,
override def interrupt = false
override def zeropage: Boolean = false
override def isVolatile: Boolean = false
}
case class NormalFunction(name: String,
@ -300,6 +329,8 @@ case class NormalFunction(name: String,
override def shouldGenerate = true
override def zeropage: Boolean = false
override def isVolatile: Boolean = false
}
case class ConstantThing(name: String, value: Constant, typ: Type) extends TypedThing with VariableLikeThing with IndexableThing {

View File

@ -592,8 +592,9 @@ object MfParser {
val nonStatementLevel = 1 // everything but not `=`
val mathLevel = 4 // the `:` operator
val elidable: P[Elidability.Value] = ("?".! ~/ HWS).?.map{
case Some(_) => Elidability.Elidable
val elidable: P[Elidability.Value] = (("!" | "?").! ~/ HWS).?.map{
case Some("?") => Elidability.Elidable
case Some("!") => Elidability.Volatile
case _ => Elidability.Fixed
}