mirror of
https://github.com/KarolS/millfork.git
synced 2024-05-31 18:41:30 +00:00
Compare commits
56 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
d8c11a9c50 | ||
|
ee47ccef5a | ||
|
24de2f7530 | ||
|
1beb695151 | ||
|
9229092309 | ||
|
ca7166d1ae | ||
|
1594d63a9a | ||
|
75cc34663c | ||
|
f4d2fdd370 | ||
|
29c1e3f2a6 | ||
|
e9cfec54b5 | ||
|
1bc1ab3539 | ||
|
6f294d6dec | ||
|
9866c974ad | ||
|
ef2f5b5918 | ||
|
a70a1c0e6b | ||
|
790c836771 | ||
|
6af84d1628 | ||
|
7b205a2754 | ||
|
d23fe1756e | ||
|
3b3ee1bc55 | ||
|
fb30075ea5 | ||
|
b5084cd180 | ||
|
b1a2be5574 | ||
|
a260d0a806 | ||
|
38ad919ed9 | ||
|
34ef8b8de9 | ||
|
f676e74e38 | ||
|
c9313e5dbe | ||
|
73a223b9b6 | ||
|
b168818bab | ||
|
effa723863 | ||
|
b2ab3dbeab | ||
|
c9ef5e636b | ||
|
fc2f0782c5 | ||
|
f0e22f02a6 | ||
|
b2291d1cb2 | ||
|
166acf2b18 | ||
|
7530b382a8 | ||
|
b66435d06b | ||
|
0c8951d015 | ||
|
84dde8589c | ||
|
3b0aa9a425 | ||
|
1b8de990a5 | ||
|
b060fc599b | ||
|
90e5360bfd | ||
|
bbf430a1c7 | ||
|
7f6a0c6b0d | ||
|
7f0def54bc | ||
|
faf97cee1f | ||
|
da862069a7 | ||
|
431a25d325 | ||
|
73beafd65e | ||
|
307ad90ecf | ||
|
2e592a2331 | ||
|
91c9b42f3d |
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -31,9 +31,12 @@ issue*.mfk
|
|||
*.seq
|
||||
*.asm
|
||||
*.lbl
|
||||
*.labels
|
||||
*.nl
|
||||
*.fns
|
||||
*.sym
|
||||
*.mlb
|
||||
*.dbg
|
||||
*.deb
|
||||
*.xex
|
||||
*.nes
|
||||
|
|
34
CHANGELOG.md
34
CHANGELOG.md
|
@ -1,5 +1,39 @@
|
|||
# Change log
|
||||
|
||||
## 0.3.30 (2021-12-15)
|
||||
|
||||
* Added volatile structure fields (#112).
|
||||
|
||||
* Added `this.function` as the alias for the current function (#118).
|
||||
|
||||
* Added support for constant evaluation in `file` expressions (#114).
|
||||
|
||||
* Allowed declaring local constants and passing untyped parameters for macros.
|
||||
|
||||
* Allowed treating bare function name as a pointer to it.
|
||||
|
||||
* Added Mesen, ld65 and "raw" label file formats (#128).
|
||||
|
||||
* Commodore: the address used by SYS is now determined automatically instead of hardcoded (#111).
|
||||
|
||||
* C64: Fixed address for `sid_v1_sr` (#115).
|
||||
|
||||
* EasyFlash: Fixed address for `switch_hirom` (#121).
|
||||
|
||||
* GB: Fixed standard library (thanks to @retrac0).
|
||||
|
||||
* Commander X16: Updated platform definition file (thanks to @mookiexl).
|
||||
|
||||
* 65CE02: Full assembly support.
|
||||
|
||||
* R800: Full assembly support.
|
||||
|
||||
* Various miscompilation fixes (#123, #125) and parser fixes (e.g. #120).
|
||||
|
||||
* 6809: Various optimizations.
|
||||
|
||||
* Improvements related to constant evaluation.
|
||||
|
||||
## 0.3.28 (2021-05-24)
|
||||
|
||||
* Officially deprecated decimal operators with apostrophes.
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
A middle-level programming language targeting 6502-based, 8080-based, Z80-based and 6809-based microcomputers.
|
||||
|
||||
For binary releases, see: [https://github.com/KarolS/millfork/releases](https://github.com/KarolS/millfork/releases)
|
||||
(latest: 0.3.28).
|
||||
(latest: 0.3.30).
|
||||
For build instructions, see [Build instructions](./COMPILING.md).
|
||||
|
||||
## Features
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
name := "millfork"
|
||||
|
||||
version := "0.3.28"
|
||||
version := "0.3.31-SNAPSHOT"
|
||||
|
||||
// keep it at 2.12.11 for GraalVM native image compatibility!
|
||||
scalaVersion := "2.12.11"
|
||||
|
|
|
@ -14,6 +14,8 @@ It implies the following:
|
|||
|
||||
* cannot contain variable or array declarations
|
||||
|
||||
* but can contain scalar constant declarations; the constants are scoped to the particular macro invocation
|
||||
|
||||
* can be `asm` - in this case, they should **not** end with a return instruction
|
||||
|
||||
* do not have an address
|
||||
|
@ -35,7 +37,13 @@ It implies the following:
|
|||
* `call` parameters exceptionally can have their type declared as `void`;
|
||||
such parameters accept expressions of any type, including `void`, however, you cannot assign from those expressions
|
||||
|
||||
* macros do not have their own scope (they reuse the scope from their invocations) – exceptions: the parameters and the local labels defined in assembly
|
||||
* macros do not have their own scope (they reuse the scope from their invocations) – exceptions:
|
||||
|
||||
* the parameters
|
||||
|
||||
* the local labels defined in assembly
|
||||
|
||||
* the local constants
|
||||
|
||||
* control-flow statements (`break`, `continue`, `return`, `goto`, `label`) are run as if places in the caller function
|
||||
|
||||
|
|
|
@ -47,6 +47,12 @@ The extension and the file format are platform-dependent.
|
|||
* `-G sym` – format used by the WLA DX assembler. The extension is `.sym`.
|
||||
|
||||
* `-G fceux` – multi-file format used by the FCEUX emulator. The extension is `.nl`.
|
||||
|
||||
* `-G mesen` – format used by the Mesen emulator. The extension is `.mlb`.
|
||||
|
||||
* `-G ld65` – a simplified version of the format used by the `ld65` linker (used by CC65 and CA65). The extension is `.dbg`.
|
||||
|
||||
* `-G raw` – Millfork-specific format. The extension is '.labels'. Each row contains bank number, start address, end address (if known), object type, and Millfork-specific object identifier.
|
||||
|
||||
* `-fbreakpoints`, `-fno-breakpoints` –
|
||||
Whether the compiler should use the `breakpoint` macro.
|
||||
|
|
|
@ -2,6 +2,11 @@
|
|||
|
||||
### A note about Commodore 64
|
||||
|
||||
#### Unusual main function location
|
||||
|
||||
If you're creating a prg file and your `main` function may be located at address $2710 hex (10000 decimal) or higher,
|
||||
you might need to define the `DISPLACED_MAIN=1` preprocessor feature.
|
||||
|
||||
#### Multifile programs
|
||||
|
||||
A multifile program is a program stored on a disk that consists of the main program file that is executed first
|
||||
|
|
|
@ -37,6 +37,8 @@ if a line ends with a backslash character, the value continues to the next line.
|
|||
* `z80` (Zilog Z80)
|
||||
|
||||
* `strictz80` (Z80 without illegal instructions)
|
||||
|
||||
* `r800` (R800)
|
||||
|
||||
* `z80next` (Z80 core from ZX Spectrum Next)
|
||||
Note: Millfork version 0.3.18 and earlier uses the name `zx80next` for this architecture.
|
||||
|
@ -80,6 +82,8 @@ This list cannot contain module template instantiations.
|
|||
* `emit_x80` – whether the compiler should emit instructions present on Sharp LR35902 and Z80, but absent on Intel 8080, default is `true` on compatible processors and `false` elsewhere
|
||||
|
||||
* `emit_z80` – whether the compiler should emit Zilog Z80 instructions not covered by `emit_x80`, default is `true` on compatible processors and `false` elsewhere
|
||||
|
||||
* `emit_r800` – whether the compiler should emit R800 instructions, default is `true` on compatible processors and `false` elsewhere
|
||||
|
||||
* `prevent_jmp_indirect_bug` – whether the compiler should try to avoid the indirect JMP bug,
|
||||
default is `false` on 65C02-compatible or non-6502 processors and `true` elsewhere
|
||||
|
@ -263,3 +267,7 @@ Default: `main,*`
|
|||
* `sym` – format used by the WLA/DX assembler. The extension is `.sym`.
|
||||
|
||||
* `fceux` – multi-file format used by the FCEUX emulator. The extension is `.nl`.
|
||||
|
||||
* `mesen` – format used by the Mesen emulator. The extension is `.mlb`.
|
||||
|
||||
* `ld65` – format used by the `ld65` linker. The extension is `.dbg`.
|
||||
|
|
|
@ -25,3 +25,5 @@
|
|||
* `byte segment.N.bank` – the value of `segment_N_bank` from the platform definition
|
||||
|
||||
* `byte segment.N.fill` – the value of `segment_N_fill` from the platform definition
|
||||
|
||||
* `this.function` – the alias of the current function (in macros, it resolves to the actual non-macro function that called the macro)
|
||||
|
|
|
@ -103,6 +103,8 @@ Some libraries may require that some of these be defined.
|
|||
|
||||
* `KEYBOARD` – 1 if the target has a keyboard, 0 otherwise
|
||||
|
||||
* `DISPLACED_MAIN` – set this to 1 if the `main` function is in a very unusual location for the target
|
||||
|
||||
* `USE_MOUSE_MBM` – set this to 1 if you want to enable middle button support for the mouse.
|
||||
|
||||
* `JOYSTICKS` – the maximum number of joysticks using standard hardware configurations, may be 0
|
||||
|
|
|
@ -10,9 +10,9 @@ These suffixes can be only applied to arithmetic or pointer variables:
|
|||
|
||||
* `.hi` – the most significant byte of a two-byte variable (word, pointer) (use `hi(_)` for arbitrary expressions)
|
||||
|
||||
* `.loword` – the least significant byte of a three- or four-byte variable
|
||||
* `.loword` – the least significant word of a three- or four-byte variable
|
||||
|
||||
* `.hiword` – the most significant byte of a three- or four-byte variable
|
||||
* `.hiword` – the most significant word of a three- or four-byte variable
|
||||
|
||||
* `.b0`, `.b1` etc. – the given byte of the multi-byte arithmetic variable, with `.b0` being the least significant byte
|
||||
|
||||
|
|
|
@ -3,6 +3,12 @@
|
|||
The examples showcased here are designed to compile with a compiler built from the newest sources.
|
||||
If you are using a release version of the compiler, consider browsing the older versions of the examples:
|
||||
|
||||
* [for version 0.3.28](https://github.com/KarolS/millfork/tree/v0.3.28/examples)
|
||||
|
||||
* [for version 0.3.26](https://github.com/KarolS/millfork/tree/v0.3.26/examples)
|
||||
|
||||
* [for version 0.3.24](https://github.com/KarolS/millfork/tree/v0.3.24/examples)
|
||||
|
||||
* [for version 0.3.22](https://github.com/KarolS/millfork/tree/v0.3.22/examples)
|
||||
|
||||
* [for version 0.3.18](https://github.com/KarolS/millfork/tree/v0.3.18/examples)
|
||||
|
|
|
@ -2,7 +2,7 @@ import err
|
|||
|
||||
inline asm void switch_hirom(byte register(a) bank) {
|
||||
? and #$3F
|
||||
! sta $DE01
|
||||
! sta $DE00
|
||||
? rts
|
||||
}
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ word sid_v1_freq @$D400
|
|||
word sid_v1_pulse @$D402
|
||||
byte sid_v1_cr @$D404
|
||||
byte sid_v1_ad @$D405
|
||||
byte sid_v1_sr @$D409
|
||||
byte sid_v1_sr @$D406
|
||||
|
||||
word sid_v2_freq @$D407
|
||||
word sid_v2_pulse @$D409
|
||||
|
|
31
include/cbm/basic_loader.mfk
Normal file
31
include/cbm/basic_loader.mfk
Normal file
|
@ -0,0 +1,31 @@
|
|||
#template $ADDR$
|
||||
|
||||
#if not(CBM)
|
||||
#warn cbm/basic_loader module should be only used on Commodore targets
|
||||
#endif
|
||||
|
||||
const array _basic_loader @ $ADDR$ = [
|
||||
#if DISPLACED_MAIN
|
||||
@word_le [
|
||||
$ADDR$ + 0xB
|
||||
],
|
||||
#else
|
||||
@word_le [
|
||||
$ADDR$ + if(main.addr >= 10000, 1/0, 0xA) // use -D DISPLACED_MAIN=1 if you get an error here
|
||||
],
|
||||
#endif
|
||||
10,
|
||||
0,
|
||||
$9e,
|
||||
#if DISPLACED_MAIN
|
||||
$30 + (main.addr/10000)%%10,
|
||||
#endif
|
||||
$30 + (main.addr/1000)%%10,
|
||||
$30 + (main.addr/100)%%10,
|
||||
$30 + (main.addr/10)%%10,
|
||||
$30 + (main.addr/1)%%10,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
]
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
#pragma zilog_syntax
|
||||
|
||||
array __header @ $100 = [
|
||||
const array __header @ $100 = [
|
||||
$00, $C3, $50, $01, $CE, $ED, $66, $66, $CC, $0D, $00, $0B, $03, $73, $00, $83,
|
||||
$00, $0C, $00, $0D, $00, $08, $11, $1F, $88, $89, $00, $0E, $DC, $CC, $6E, $E6,
|
||||
$DD, $DD, $D9, $99, $BB, $BB, $67, $63, $6E, $0E, $EC, $CC, $DD, $DC, $99, $9F,
|
||||
|
|
|
@ -38,9 +38,9 @@ void read_joy() {
|
|||
ld a,(reg_joypad)
|
||||
ld a,(reg_joypad)
|
||||
}
|
||||
byte tmp
|
||||
|
||||
tmp = reg_joypad ^ $ff
|
||||
input_a = (tmp & 1) >> 0
|
||||
input_a = (tmp & 1) // >> 0
|
||||
input_b = (tmp & 2) >> 1
|
||||
input_select = (tmp & 4) >> 2
|
||||
input_start = (tmp & 8) >> 3
|
||||
|
|
|
@ -11,6 +11,12 @@ inline asm byte __mul_u8u8u8() {
|
|||
? LD A, E
|
||||
? RET
|
||||
}
|
||||
#elseif CPUFEATURE_R800
|
||||
inline asm byte __mul_u8u8u8() {
|
||||
? MULUB A,D
|
||||
? LD A,L
|
||||
? RET
|
||||
}
|
||||
#elseif CPUFEATURE_Z80 || CPUFEATURE_GAMEBOY
|
||||
//A = A * D
|
||||
noinline asm byte __mul_u8u8u8() {
|
||||
|
@ -89,6 +95,16 @@ __divmod_u16u8u16u8_skip:
|
|||
? RET
|
||||
}
|
||||
|
||||
|
||||
#if CPUFEATURE_R800
|
||||
inline asm word __mul_u16u8u16() {
|
||||
? LD L,A
|
||||
? LD H,0
|
||||
? MULUW HL,DE
|
||||
? RET
|
||||
}
|
||||
#else
|
||||
// HL=A*DE
|
||||
noinline asm word __mul_u16u8u16() {
|
||||
? LD HL,0
|
||||
? LD B,8
|
||||
|
@ -113,8 +129,17 @@ __mul_u16u8u16_skip:
|
|||
#endif
|
||||
? RET
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CPUFEATURE_Z80 || CPUFEATURE_GAMEBOY
|
||||
|
||||
#if CPUFEATURE_R800
|
||||
inline asm word __mul_u16u16u16() {
|
||||
? EX DE,HL
|
||||
? MULUW HL,BC
|
||||
? RET
|
||||
}
|
||||
#elseif CPUFEATURE_Z80 || CPUFEATURE_GAMEBOY
|
||||
// HL=BC*DE
|
||||
noinline asm word __mul_u16u16u16() {
|
||||
LD HL,0
|
||||
LD A,16
|
||||
|
|
|
@ -1,19 +1 @@
|
|||
#if not(CBM)
|
||||
#warn loader_0401 module should be only used on Commodore targets
|
||||
#endif
|
||||
|
||||
const array _basic_loader @$401 = [
|
||||
$0b,
|
||||
4,
|
||||
10,
|
||||
0,
|
||||
$9e,
|
||||
$31,
|
||||
$30,
|
||||
$33,
|
||||
$37,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
]
|
||||
|
||||
import cbm/basic_loader<$401>
|
||||
|
|
|
@ -1,19 +1 @@
|
|||
#if not(CBM)
|
||||
#warn loader_0801 module should be only used on Commodore targets
|
||||
#endif
|
||||
|
||||
const array _basic_loader @$801 = [
|
||||
$0b,
|
||||
$08,
|
||||
10,
|
||||
0,
|
||||
$9e,
|
||||
$32,
|
||||
$30,
|
||||
$36,
|
||||
$31,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
]
|
||||
|
||||
import cbm/basic_loader<$801>
|
||||
|
|
|
@ -22,4 +22,5 @@ asm void __init_16bit() @$80D {
|
|||
clc
|
||||
xce
|
||||
sep #$30
|
||||
}
|
||||
jmp main
|
||||
}
|
||||
|
|
|
@ -1,19 +1 @@
|
|||
#if not(CBM)
|
||||
#warn loader_1001 module should be only used on Commodore targets
|
||||
#endif
|
||||
|
||||
const array _basic_loader @$1001 = [
|
||||
$0b,
|
||||
$10,
|
||||
10,
|
||||
0,
|
||||
$9e,
|
||||
$34,
|
||||
$31,
|
||||
$30,
|
||||
$39,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
]
|
||||
|
||||
import cbm/basic_loader<$1001>
|
||||
|
|
|
@ -1,19 +1 @@
|
|||
#if not(CBM)
|
||||
#warn loader_1201 module should be only used on Commodore targets
|
||||
#endif
|
||||
|
||||
const array _basic_loader @$1201 = [
|
||||
$0b,
|
||||
$12,
|
||||
10,
|
||||
0,
|
||||
$9e,
|
||||
$34,
|
||||
$36,
|
||||
$32,
|
||||
$31,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
]
|
||||
|
||||
import cbm/basic_loader<$1201>
|
||||
|
|
|
@ -1,19 +1 @@
|
|||
#if not(CBM)
|
||||
#warn loader_1c01 module should be only used on Commodore targets
|
||||
#endif
|
||||
|
||||
const array _basic_loader @$1C01 = [
|
||||
$0b,
|
||||
$1C,
|
||||
10,
|
||||
0,
|
||||
$9e,
|
||||
$37,
|
||||
$31,
|
||||
$38,
|
||||
$31,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
]
|
||||
|
||||
import cbm/basic_loader<$1c01>
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#warn m6809_math module should be used only on 6809-like targets
|
||||
#endif
|
||||
|
||||
noinline asm word __mul_u16u16u16(word register(x) x, word register(d) d) {
|
||||
noinline asm word __mul_u16u16u16(word register(x) x, word register(d) d) !preserves_y !preserves_u {
|
||||
pshs d,x
|
||||
exg d,x
|
||||
lda ,s
|
||||
|
@ -26,7 +26,7 @@ noinline asm word __mul_u16u16u16(word register(x) x, word register(d) d) {
|
|||
}
|
||||
|
||||
// returns p/q: quotient in A, remainder in B
|
||||
noinline asm word __divmod_u8u8u8u8(word register(a) p, word register(b) q) {
|
||||
noinline asm word __divmod_u8u8u8u8(word register(a) p, word register(b) q) !preserves_x !preserves_y !preserves_u {
|
||||
pshs b,cc
|
||||
ldb #8
|
||||
stb ,-s
|
||||
|
@ -47,7 +47,7 @@ noinline asm word __divmod_u8u8u8u8(word register(a) p, word register(b) q) {
|
|||
}
|
||||
|
||||
// returns p/q: quotient in X, remainder in D
|
||||
noinline asm word __divmod_u16u16u16u16(word register(x) p, word register(d) q) {
|
||||
noinline asm word __divmod_u16u16u16u16(word register(x) p, word register(d) q) !preserves_y !preserves_u {
|
||||
pshs x,d,cc
|
||||
ldb #16
|
||||
pshs b
|
||||
|
|
|
@ -11,14 +11,21 @@ modules=loader_0801,x16_kernal,x16_hardware,c64_panic,stdlib
|
|||
|
||||
[allocation]
|
||||
; Let's not use the BASIC:
|
||||
zp_pointers=0-$7F
|
||||
segments=default,himem_00,himem_ff
|
||||
; $00-$01 are used for bank switching
|
||||
zp_pointers=$02-$7F
|
||||
segments=default,user,himem_00,himem_ff
|
||||
default_code_segment=default
|
||||
segment_default_start=$80D
|
||||
segment_default_codeend=$9eff
|
||||
segment_default_datastart=after_code
|
||||
segment_default_end=$9eff
|
||||
|
||||
;1KB user space
|
||||
segment_user_start=$0400
|
||||
segment_user_codeend=$07ff
|
||||
segment_user_datastart=after_code
|
||||
segment_user_end=$07ff
|
||||
|
||||
segment_himem_00_start=$a000
|
||||
segment_himem_00_codeend=$bfff
|
||||
segment_himem_00_datastart=after_code
|
||||
|
|
|
@ -19,11 +19,15 @@
|
|||
+= -= *= >>= <<=
|
||||
+' -' *' <<' >>'
|
||||
+'= -'= *'= <<'= >>'=
|
||||
$+ $- $* $<< $>>
|
||||
$+= $-= $*= $<<= $>>=
|
||||
= ?
|
||||
/ %% /= %%=
|
||||
> >= < <= != ==
|
||||
| || |= ^ ^= & && &=
|
||||
( ) { } @ [ ] : # </Keywords>
|
||||
( ) { } @ [ ] : #
|
||||
; .
|
||||
</Keywords>
|
||||
<Keywords name="Operators2"></Keywords>
|
||||
<Keywords name="Folders in code1, open">{</Keywords>
|
||||
<Keywords name="Folders in code1, middle"></Keywords>
|
||||
|
@ -38,10 +42,14 @@
|
|||
void bool byte sbyte ubyte word farword pointer farpointer long word_be word_le long_be long_le file
|
||||
int8 int16 int24 int32 int40 int48 int56 int64
|
||||
int72 int80 int88 int96 int104 int112 int120 int128
|
||||
signed8
|
||||
signed8 signed16 signed24 signed32 signed40 signed48 signed56 signed64
|
||||
signed72 signed80 signed88 signed96 signed104 signed112 signed120 signed128
|
||||
unsigned8 unsigned16 unsigned24 unsigned32 unsigned40 unsigned48 unsigned56 unsigned64
|
||||
unsigned72 unsigned80 unsigned88 unsigned96 unsigned104 unsigned112 unsigned120 unsigned128
|
||||
array addr fast</Keywords>
|
||||
array addr fast
|
||||
clear_carry clear_zero clear_overflow clear_negative
|
||||
set_carry set_zero set_overflow set_negative
|
||||
</Keywords>
|
||||
<Keywords name="Keywords2">
|
||||
import segment
|
||||
if else for return while do asm extern break continue default goto label
|
||||
|
@ -54,9 +62,11 @@
|
|||
utf8 utf16le utf16be latin0 latin9 iso8859_15 zx80 zx81 vectrex koi7n2 short_koi msx_intl msx_us msx_uk msx_de msx_fr msx_es msx_ru msx_jp msx_br
|
||||
utf8z utf16lez utf16bez latin0z latin9z iso8859_15z zx80z zx81z vectrexz koi7n2z short_koiz msx_intlz msx_usz msx_ukz msx_dez msx_frz msx_esz msx_ruz msx_jpz msx_brz
|
||||
until to downto parallelto paralleluntil
|
||||
function
|
||||
static stack ref const volatile inline noinline macro register kernal_interrupt interrupt align reentrant
|
||||
hi lo sin cos tan call nonet
|
||||
false true nullptr</Keywords>
|
||||
false true nullptr nullchar nullchar_scr
|
||||
</Keywords>
|
||||
<Keywords name="Keywords4">"sta " "lda " "jmp " "bit " "eor " "adc " "sbc " "ora " "and " "ldx " "ldy " "stx " "sty " "tax" "tay" "tya" "txa" "txs" "tsx" "sei" "cli" "clv" "clc" "cld" "sed" "sec" "bra " "beq " "bne " "bmi " "bpl " "bcc " "bcs " "bvs " bvc " "jsr " rts" "rti" "brk" "rol" "ror" "asl" "lsr" "inc " "dec " "cmp " "cpx " "cpy " inx iny dex dey pla pha plp hp phx plx phy ply "stz " "ldz " tza taz "tsb " "trb " ra txy tyx pld plb phb phd phk xce

"STA " "LDA " "JMP " "BIT " "EOR " "ADC " "SBC " "ORA " "AND " "LDX " "LDY " "STX " "STY " "TAX" "TAY" "TYA" "TXA" "TXS" "TSX" "SEI" "CLI" "CLV" "CLC" "CLD" "SED" "SEC" "BEQ " "BRA " "BNE " "BMI " "BPL " "BCC " "BCS " "BVS " BVC " "JSR " RTS" "RTI" "BRK" "ROL" "ROR" "ASL" "LSR" "INC " "DEC " "CMP " "CPX " "CPY " INX INY DEX DEY PLA PHA PLP HP PHX PLX PHY PLY "STZ " "LDZ " TZA TAZ "TSB " "TRB " RA TXY TYX PLD PLB PHB PHD PHK XCE</Keywords>
|
||||
<Keywords name="Keywords5">"sbx " "isc " "dcp " "lax " "sax " "anc " "alr " "arr " "rra " "rla " "lxa " "ane " "xaa "
"SBX " "ISC " "DCP " "LAX " "SAX " "ANC " "ALR " "ARR " "RRA " "RLA " "LXA " "ANE " "XAA "</Keywords>
|
||||
<Keywords name="Keywords6"></Keywords>
|
||||
|
|
|
@ -45,7 +45,7 @@ case class CompilationOptions(platform: Platform,
|
|||
EmitIntel8085Opcodes, EmitIntel8080Opcodes, UseIxForStack, UseIntelSyntaxForInput, UseIntelSyntaxForOutput)
|
||||
|
||||
if (CpuFamily.forType(platform.cpu) != CpuFamily.I80) invalids ++= Set(
|
||||
EmitExtended80Opcodes, EmitZ80Opcodes, EmitSharpOpcodes, EmitEZ80Opcodes, EmitZ80NextOpcodes,
|
||||
EmitExtended80Opcodes, EmitZ80Opcodes, EmitSharpOpcodes, EmitEZ80Opcodes, EmitZ80NextOpcodes, EmitR800Opcodes,
|
||||
UseIyForStack, UseIxForScratch, UseIyForScratch, UseShadowRegistersForInterrupts)
|
||||
|
||||
if (CpuFamily.forType(platform.cpu) != CpuFamily.M6809) invalids ++= Set(
|
||||
|
@ -172,6 +172,11 @@ case class CompilationOptions(platform: Platform,
|
|||
log.error("Extended 8080-like opcodes enabled for architecture that doesn't support them")
|
||||
}
|
||||
}
|
||||
if (flags(EmitR800Opcodes)) {
|
||||
if (platform.cpu != R800) {
|
||||
log.error("R800 opcodes enabled for architecture that doesn't support them")
|
||||
}
|
||||
}
|
||||
if (flags(EmitIntel8080Opcodes)) {
|
||||
if (!Intel8080Compatible(platform.cpu)) {
|
||||
log.error("Intel 8080 opcodes enabled for architecture that doesn't support them")
|
||||
|
@ -220,6 +225,7 @@ case class CompilationOptions(platform: Platform,
|
|||
"OPTIMIZE_IPO" -> toLong(flag(CompilationFlag.InterproceduralOptimization)),
|
||||
"CPUFEATURE_DECIMAL_MODE" -> toLong(flag(CompilationFlag.DecimalMode)),
|
||||
"CPUFEATURE_Z80" -> toLong(flag(CompilationFlag.EmitZ80Opcodes)),
|
||||
"CPUFEATURE_R800" -> toLong(flag(CompilationFlag.EmitR800Opcodes)),
|
||||
"CPUFEATURE_EZ80" -> toLong(flag(CompilationFlag.EmitEZ80Opcodes)),
|
||||
"CPUFEATURE_8080" -> toLong(flag(CompilationFlag.EmitIntel8080Opcodes)),
|
||||
"CPUFEATURE_8085" -> toLong(flag(CompilationFlag.EmitIntel8085Opcodes)),
|
||||
|
@ -290,7 +296,7 @@ object CpuFamily extends Enumeration {
|
|||
import Cpu._
|
||||
cpu match {
|
||||
case Mos | StrictMos | Ricoh | StrictRicoh | Cmos | SC02 | Rockwell | Wdc | HuC6280 | CE02 | Sixteen => M6502
|
||||
case Intel8080 | Intel8085 | StrictIntel8085 | Sharp | Z80 | StrictZ80 | EZ80 | Z80Next => I80
|
||||
case Intel8080 | Intel8085 | StrictIntel8085 | Sharp | Z80 | StrictZ80 | R800 | EZ80 | Z80Next => I80
|
||||
case Intel8086 | Intel80186 => I86
|
||||
case Cpu.Motorola6809 => M6809
|
||||
}
|
||||
|
@ -368,6 +374,10 @@ object Cpu extends Enumeration {
|
|||
* The Zilog Z80 processor, without illegal instructions
|
||||
*/
|
||||
val StrictZ80: Cpu.Value = Value
|
||||
/**
|
||||
* The R800 CPU (used in MSX Turbo-R)
|
||||
*/
|
||||
val R800: Cpu.Value = Value
|
||||
/**
|
||||
* The Zilog eZ80 processor
|
||||
*/
|
||||
|
@ -400,11 +410,11 @@ object Cpu extends Enumeration {
|
|||
/**
|
||||
* Processors that can run code for Zilog Z80
|
||||
*/
|
||||
val Z80Compatible: Set[Cpu.Value] = Set(Z80, StrictZ80, EZ80, Z80Next)
|
||||
val Z80Compatible: Set[Cpu.Value] = Set(Z80, StrictZ80, R800, EZ80, Z80Next)
|
||||
/**
|
||||
* Processors that can run code for Intel 8080
|
||||
*/
|
||||
val Intel8080Compatible: Set[Cpu.Value] = Set(Intel8080, Intel8085, StrictIntel8085, Z80, StrictZ80, EZ80, Z80Next)
|
||||
val Intel8080Compatible: Set[Cpu.Value] = Set(Intel8080, Intel8085, StrictIntel8085, Z80, StrictZ80, R800, EZ80, Z80Next)
|
||||
/**
|
||||
* Processors that can run code for Intel 8085
|
||||
*/
|
||||
|
@ -421,6 +431,7 @@ object Cpu extends Enumeration {
|
|||
EnableBreakpoints,
|
||||
UseOptimizationHints,
|
||||
GenericWarnings,
|
||||
ByteOverflowWarning,
|
||||
UselessCodeWarning,
|
||||
BuggyCodeWarning,
|
||||
FallbackValueUseWarning,
|
||||
|
@ -467,6 +478,8 @@ object Cpu extends Enumeration {
|
|||
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitIntel8085Opcodes, UseIntelSyntaxForInput, UseIntelSyntaxForOutput)
|
||||
case StrictZ80 | Z80 =>
|
||||
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, UseIxForStack, UseShadowRegistersForInterrupts)
|
||||
case R800 =>
|
||||
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, UseIxForStack, UseShadowRegistersForInterrupts, EmitR800Opcodes)
|
||||
case Z80Next =>
|
||||
i80AlwaysDefaultFlags ++ Set(EmitIntel8080Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, UseIxForStack, UseShadowRegistersForInterrupts, EmitIllegals, EmitZ80NextOpcodes)
|
||||
case EZ80 =>
|
||||
|
@ -513,6 +526,7 @@ object Cpu extends Enumeration {
|
|||
case "strict2a07" => StrictRicoh
|
||||
case "z80" => Z80
|
||||
case "strictz80" => Z80
|
||||
case "r800" => R800
|
||||
case "zx80next" => Z80Next
|
||||
case "z80next" => Z80Next
|
||||
// disabled for now:
|
||||
|
@ -561,9 +575,9 @@ object CompilationFlag extends Enumeration {
|
|||
EmitIllegals, DecimalMode, LenientTextEncoding, LineNumbersInAssembly, SourceInAssembly, EnableBreakpoints,
|
||||
// compilation options for MOS:
|
||||
EmitCmosOpcodes, EmitCmosNopOpcodes, EmitSC02Opcodes, EmitRockwellOpcodes, EmitWdcOpcodes, EmitHudsonOpcodes, Emit65CE02Opcodes, EmitEmulation65816Opcodes, EmitNative65816Opcodes,
|
||||
PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator, SoftwareStack,
|
||||
PreventJmpIndirectBug, LargeCode, ReturnWordsViaAccumulator, SoftwareStack, IdentityPage,
|
||||
// compilation options for I80
|
||||
EmitIntel8080Opcodes, EmitIntel8085Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, EmitEZ80Opcodes, EmitSharpOpcodes, EmitZ80NextOpcodes,
|
||||
EmitIntel8080Opcodes, EmitIntel8085Opcodes, EmitExtended80Opcodes, EmitZ80Opcodes, EmitR800Opcodes, EmitEZ80Opcodes, EmitSharpOpcodes, EmitZ80NextOpcodes,
|
||||
UseShadowRegistersForInterrupts,
|
||||
UseIxForStack, UseIyForStack,
|
||||
UseIxForScratch, UseIyForScratch,
|
||||
|
@ -585,6 +599,7 @@ object CompilationFlag extends Enumeration {
|
|||
SingleThreaded,
|
||||
// warning options
|
||||
GenericWarnings,
|
||||
ByteOverflowWarning,
|
||||
UselessCodeWarning,
|
||||
BuggyCodeWarning,
|
||||
DeprecationWarning,
|
||||
|
@ -603,6 +618,7 @@ object CompilationFlag extends Enumeration {
|
|||
|
||||
val allWarnings: Set[CompilationFlag.Value] = Set(
|
||||
GenericWarnings,
|
||||
ByteOverflowWarning,
|
||||
UselessCodeWarning,
|
||||
BuggyCodeWarning,
|
||||
DeprecationWarning,
|
||||
|
@ -625,6 +641,7 @@ object CompilationFlag extends Enumeration {
|
|||
"emit_65ce02" -> Emit65CE02Opcodes,
|
||||
"emit_huc6280" -> EmitHudsonOpcodes,
|
||||
"emit_z80" -> EmitZ80Opcodes,
|
||||
"emit_r800" -> EmitR800Opcodes,
|
||||
"emit_ez80" -> EmitEZ80Opcodes,
|
||||
"emit_x80" -> EmitExtended80Opcodes,
|
||||
"emit_8080" -> EmitIntel8080Opcodes,
|
||||
|
@ -637,6 +654,7 @@ object CompilationFlag extends Enumeration {
|
|||
"u_stack" -> UseUForStack,
|
||||
"y_stack" -> UseYForStack,
|
||||
"software_stack" -> SoftwareStack,
|
||||
"identity_page" -> IdentityPage,
|
||||
"use_shadow_registers_for_irq" -> UseShadowRegistersForInterrupts,
|
||||
"output_intel_syntax" -> UseIntelSyntaxForOutput,
|
||||
"input_intel_syntax" -> UseIntelSyntaxForInput,
|
||||
|
|
|
@ -46,10 +46,24 @@ case class Context(errorReporting: Logger,
|
|||
if (isFlagSet(CompilationFlag.EmitEZ80Opcodes)) {
|
||||
addons += CompilationFlag.EmitZ80Opcodes -> true
|
||||
}
|
||||
if (isFlagSet(CompilationFlag.EmitZ80Opcodes) || isFlagSet(CompilationFlag.EmitSharpOpcodes)) {
|
||||
if (isFlagSet(CompilationFlag.EmitZ80NextOpcodes)) {
|
||||
addons += CompilationFlag.EmitZ80Opcodes -> true
|
||||
}
|
||||
if (isFlagSet(CompilationFlag.EmitR800Opcodes)) {
|
||||
addons += CompilationFlag.EmitZ80Opcodes -> true
|
||||
}
|
||||
if (isFlagSet(CompilationFlag.EmitEZ80Opcodes)
|
||||
|| isFlagSet(CompilationFlag.EmitZ80NextOpcodes)
|
||||
|| isFlagSet(CompilationFlag.EmitR800Opcodes)
|
||||
|| isFlagSet(CompilationFlag.EmitZ80Opcodes)
|
||||
|| isFlagSet(CompilationFlag.EmitSharpOpcodes)) {
|
||||
addons += CompilationFlag.EmitExtended80Opcodes -> true
|
||||
}
|
||||
if (isFlagSet(CompilationFlag.EmitZ80Opcodes) || isFlagSet(CompilationFlag.EmitIntel8085Opcodes)) {
|
||||
if (isFlagSet(CompilationFlag.EmitEZ80Opcodes)
|
||||
|| isFlagSet(CompilationFlag.EmitZ80NextOpcodes)
|
||||
|| isFlagSet(CompilationFlag.EmitR800Opcodes)
|
||||
|| isFlagSet(CompilationFlag.EmitZ80Opcodes)
|
||||
|| isFlagSet(CompilationFlag.EmitIntel8085Opcodes)) {
|
||||
addons += CompilationFlag.EmitIntel8080Opcodes -> true
|
||||
}
|
||||
if (isFlagSet(CompilationFlag.OptimizeForSpeed)) {
|
||||
|
|
|
@ -1,23 +1,37 @@
|
|||
package millfork
|
||||
|
||||
import millfork.output.{BankLayoutInFile, FormattableLabel}
|
||||
|
||||
import java.util.regex.Pattern
|
||||
import scala.collection.mutable
|
||||
import scala.util.control.Breaks.{break, breakable}
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
|
||||
object DebugOutputFormat {
|
||||
val map: Map[String, DebugOutputFormat] = Map(
|
||||
"raw" -> RawDebugOutputFormat,
|
||||
"vice" -> ViceDebugOutputFormat,
|
||||
"nesasm" -> NesasmDebugOutputFormat,
|
||||
"fns" -> NesasmDebugOutputFormat,
|
||||
"fceux" -> FceuxDebugOutputFormat,
|
||||
"nl" -> FceuxDebugOutputFormat,
|
||||
"mlb" -> MesenOutputFormat,
|
||||
"mesen" -> MesenOutputFormat,
|
||||
"asm6f" -> MesenOutputFormat,
|
||||
"ld65" -> Ld65OutputFormat,
|
||||
"ca65" -> Ld65OutputFormat,
|
||||
"cc65" -> Ld65OutputFormat,
|
||||
"dbg" -> Ld65OutputFormat,
|
||||
"sym" -> SymDebugOutputFormat)
|
||||
}
|
||||
|
||||
sealed trait DebugOutputFormat {
|
||||
|
||||
def formatAll(labels: Seq[(String, (Int, Int))], breakpoints: Seq[(Int, Int)]): String = {
|
||||
val labelPart = labelsHeader + labels.map(formatLineTupled).mkString("\n") + "\n"
|
||||
def formatAll(b: BankLayoutInFile, labels: Seq[FormattableLabel], breakpoints: Seq[(Int, Int)]): String = {
|
||||
val labelPart = labelsHeader + labels.map(formatLine).mkString("\n") + "\n"
|
||||
if (breakpoints.isEmpty) {
|
||||
labelPart
|
||||
} else {
|
||||
|
@ -25,9 +39,7 @@ sealed trait DebugOutputFormat {
|
|||
}
|
||||
}
|
||||
|
||||
final def formatLineTupled(labelAndValue: (String, (Int, Int))): String = formatLine(labelAndValue._1, labelAndValue._2._1, labelAndValue._2._2)
|
||||
|
||||
def formatLine(label: String, bank: Int, value: Int): String
|
||||
def formatLine(label: FormattableLabel): String
|
||||
|
||||
final def formatBreakpointTupled(value: (Int, Int)): Seq[String] = formatBreakpoint(value._1, value._2).toSeq
|
||||
|
||||
|
@ -45,10 +57,25 @@ sealed trait DebugOutputFormat {
|
|||
def breakpointsHeader: String = ""
|
||||
}
|
||||
|
||||
object RawDebugOutputFormat extends DebugOutputFormat {
|
||||
override def formatLine(label: FormattableLabel): String = {
|
||||
f"${label.bankNumber}%02X:${label.startValue}%04X:${label.endValue.fold("")(_.formatted("%04X"))}%s:${label.category}%s:$label%s"
|
||||
}
|
||||
|
||||
override def fileExtension(bank: Int): String = ".labels"
|
||||
|
||||
override def filePerBank: Boolean = false
|
||||
|
||||
override def addOutputExtension: Boolean = false
|
||||
|
||||
override def formatBreakpoint(bank: Int, value: Int): Option[String] =
|
||||
Some(f"$bank%02X:$value%04X::b:<breakpoint@$value%04X>")
|
||||
}
|
||||
|
||||
object ViceDebugOutputFormat extends DebugOutputFormat {
|
||||
override def formatLine(label: String, bank: Int, value: Int): String = {
|
||||
val normalized = label.replace('$', '_').replace('.', '_')
|
||||
s"al ${value.toHexString} .$normalized"
|
||||
override def formatLine(label: FormattableLabel): String = {
|
||||
val normalized = label.labelName.replace('$', '_').replace('.', '_')
|
||||
s"al ${label.startValue.toHexString} .$normalized"
|
||||
}
|
||||
|
||||
override def fileExtension(bank: Int): String = ".lbl"
|
||||
|
@ -61,8 +88,8 @@ object ViceDebugOutputFormat extends DebugOutputFormat {
|
|||
}
|
||||
|
||||
object NesasmDebugOutputFormat extends DebugOutputFormat {
|
||||
override def formatLine(label: String, bank: Int, value: Int): String = {
|
||||
label + " = $" + value.toHexString
|
||||
override def formatLine(label: FormattableLabel): String = {
|
||||
label.labelName + " = $" + label.startValue.toHexString
|
||||
}
|
||||
|
||||
override def fileExtension(bank: Int): String = ".fns"
|
||||
|
@ -75,8 +102,8 @@ object NesasmDebugOutputFormat extends DebugOutputFormat {
|
|||
}
|
||||
|
||||
object SymDebugOutputFormat extends DebugOutputFormat {
|
||||
override def formatLine(label: String, bank: Int, value: Int): String = {
|
||||
f"$bank%02x:$value%04x $label%s"
|
||||
override def formatLine(label: FormattableLabel): String = {
|
||||
f"${label.bankNumber}%02x:${label.startValue}%04x ${label.labelName}%s"
|
||||
}
|
||||
|
||||
override def fileExtension(bank: Int): String = ".sym"
|
||||
|
@ -93,8 +120,8 @@ object SymDebugOutputFormat extends DebugOutputFormat {
|
|||
}
|
||||
|
||||
object FceuxDebugOutputFormat extends DebugOutputFormat {
|
||||
override def formatLine(label: String, bank: Int, value: Int): String = {
|
||||
f"$$$value%04x#$label%s#"
|
||||
override def formatLine(label: FormattableLabel): String = {
|
||||
f"$$${label.startValue}%04x#${label.labelName}%s#"
|
||||
}
|
||||
|
||||
override def fileExtension(bank: Int): String = if (bank == 0xff) ".ram.nl" else s".$bank.nl"
|
||||
|
@ -105,3 +132,97 @@ object FceuxDebugOutputFormat extends DebugOutputFormat {
|
|||
|
||||
override def formatBreakpoint(bank: Int, value: Int): Option[String] = None
|
||||
}
|
||||
|
||||
object MesenOutputFormat extends DebugOutputFormat {
|
||||
|
||||
override def formatAll(b: BankLayoutInFile, labels: Seq[FormattableLabel], breakpoints: Seq[(Int, Int)]): String = {
|
||||
val allStarts = labels.groupBy(_.bankName).mapValues(_.map(_.startValue).toSet)
|
||||
labels.flatMap{ l =>
|
||||
val mesenShift = b.getMesenShift(l.bankName)
|
||||
val shiftedStart = l.startValue + mesenShift
|
||||
var shiftedEnd = l.endValue.map(_ + mesenShift).filter(_ != shiftedStart)
|
||||
l.endValue match {
|
||||
case None =>
|
||||
case Some(e) =>
|
||||
// Mesen does not like labels of form XXX-XXX, where both ends are equal
|
||||
breakable {
|
||||
for (i <- l.startValue.+(1) to e) {
|
||||
if (allStarts.getOrElse(l.bankName, Set.empty).contains(i)) {
|
||||
shiftedEnd = None
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (shiftedStart >= 0 && shiftedEnd.forall(_ >= 0)) {
|
||||
val normalized = l.labelName.replace('$', '_').replace('.', '_')
|
||||
val comment = (l.category match {
|
||||
case 'F' => "function "
|
||||
case 'A' => "initialized array "
|
||||
case 'V' => "initialized variable "
|
||||
case 'a' => "array "
|
||||
case 'v' => "variable "
|
||||
case _ => ""
|
||||
}) + l.labelName
|
||||
Some(f"${l.mesenSymbol}%s:${shiftedStart}%04X${shiftedEnd.fold("")(e => f"-$e%04X")}%s:$normalized%s:$comment%s")
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}.mkString("\n")
|
||||
}
|
||||
|
||||
override def formatLine(label: FormattableLabel): String = throw new UnsupportedOperationException()
|
||||
|
||||
override def formatBreakpoint(bank: Int, value: Int): Option[String] = None
|
||||
|
||||
override def fileExtension(bank: Int): String = ".mlb"
|
||||
|
||||
override def filePerBank: Boolean = false
|
||||
|
||||
override def addOutputExtension: Boolean = false
|
||||
}
|
||||
|
||||
object Ld65OutputFormat extends DebugOutputFormat {
|
||||
override def formatLine(label: FormattableLabel): String = throw new UnsupportedOperationException()
|
||||
|
||||
override def formatBreakpoint(bank: Int, value: Int): Option[String] = None
|
||||
|
||||
override def fileExtension(bank: Int): String = ".dbg"
|
||||
|
||||
override def filePerBank: Boolean = false
|
||||
|
||||
override def addOutputExtension: Boolean = true
|
||||
|
||||
override def formatAll(b: BankLayoutInFile, labels: Seq[FormattableLabel], breakpoints: Seq[(Int, Int)]): String = {
|
||||
val q = '"'
|
||||
val allBanksInFile = b.allBanks()
|
||||
val allBanks = (labels.map(_.bankName).distinct ++ allBanksInFile).distinct
|
||||
val result = mutable.ListBuffer[String]()
|
||||
result += "version\tmajor=2,minor=0"
|
||||
result += s"info\tcsym=0,file=0,lib=0,line=0,mod=0,scope=1,seg=${allBanks.size},span=0,sym=${labels.size},type=4"
|
||||
for ((bank, ix) <- allBanks.zipWithIndex) {
|
||||
val common = s"seg\tid=${ix},name=$q${bank}$q,start=0x${b.getStart(bank).toHexString},size=0x0,addrsize=absolute"
|
||||
if (allBanksInFile.contains(bank)) {
|
||||
result += common + s",ooffs=${b.ld65Offset(bank)}"
|
||||
} else {
|
||||
result += common
|
||||
}
|
||||
}
|
||||
result += "scope\tid=0,name=\"\""
|
||||
for ((label, ix) <- labels.sortBy(l => (l.bankName, l.startValue, l.labelName)).zipWithIndex) {
|
||||
val name = label.labelName.replace('$', '@').replace('.', '@')
|
||||
val sb = new StringBuilder
|
||||
sb ++= s"sym\tid=${ix},name=$q${name}$q,addrsize=absolute,"
|
||||
label.endValue match {
|
||||
case Some(e) =>
|
||||
if (!labels.exists(l => l.ne(label) && l.startValue >= label.startValue && l.startValue <= e)) {
|
||||
sb ++= s"size=${ e - label.startValue + 1 },"
|
||||
}
|
||||
case None =>
|
||||
}
|
||||
sb ++= s"scope=0,val=0x${label.startValue.toHexString},seg=${allBanks.indexOf(label.bankName)},type=lab"
|
||||
result += sb.toString
|
||||
}
|
||||
result.mkString("\n")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -119,25 +119,35 @@ object Main {
|
|||
else if (l.startsWith("__")) 7
|
||||
else 0
|
||||
}
|
||||
val sortedLabels = result.labels.groupBy(_._2).values.map(_.minBy(a => labelUnimportance(a._1) -> a._1)).toSeq.sortBy(_._2)
|
||||
val sortedLabels: Seq[FormattableLabel] =
|
||||
result.labels.groupBy(_._2).values
|
||||
.map(_.minBy(a => labelUnimportance(a._1) -> a._1)).toSeq.sortBy(_._2)
|
||||
.map { case (l, (b, s)) =>
|
||||
val bankNumber = options.platform.bankNumbers.getOrElse(b, 0)
|
||||
val mesenCategory = options.platform.getMesenLabelCategory(b, s)
|
||||
result.endLabels.get(l) match {
|
||||
case Some((c, e)) => FormattableLabel(l, b, bankNumber, s, Some(e), c, mesenCategory)
|
||||
case _ => FormattableLabel(l, b, bankNumber, s, None, 'x', mesenCategory)
|
||||
}
|
||||
}
|
||||
val sortedBreakpoints = result.breakpoints
|
||||
val format = c.outputLabelsFormatOverride.getOrElse(platform.outputLabelsFormat)
|
||||
val basename = if (format.addOutputExtension) output + platform.fileExtension else output
|
||||
if (format.filePerBank) {
|
||||
val banks = sortedLabels.map(_._2._1).toSet ++ sortedBreakpoints.map(_._2).toSet
|
||||
val banks: Set[Int] = sortedLabels.map(_.bankNumber).toSet ++ sortedBreakpoints.map(_._1).toSet
|
||||
banks.foreach{ bank =>
|
||||
val labels = sortedLabels.filter(_._2._1.==(bank))
|
||||
val labels = sortedLabels.filter(_.bankNumber.==(bank))
|
||||
val breakpoints = sortedBreakpoints.filter(_._1.==(bank))
|
||||
val labelOutput = basename + format.fileExtension(bank)
|
||||
val path = Paths.get(labelOutput)
|
||||
errorReporting.debug("Writing labels to " + path.toAbsolutePath)
|
||||
Files.write(path, format.formatAll(labels, breakpoints).getBytes(StandardCharsets.UTF_8))
|
||||
Files.write(path, format.formatAll(result.bankLayoutInFile, labels, breakpoints).getBytes(StandardCharsets.UTF_8))
|
||||
}
|
||||
} else {
|
||||
val labelOutput = basename + format.fileExtension(0)
|
||||
val path = Paths.get(labelOutput)
|
||||
errorReporting.debug("Writing labels to " + path.toAbsolutePath)
|
||||
Files.write(path, format.formatAll(sortedLabels, sortedBreakpoints).getBytes(StandardCharsets.UTF_8))
|
||||
Files.write(path, format.formatAll(result.bankLayoutInFile, sortedLabels, sortedBreakpoints).getBytes(StandardCharsets.UTF_8))
|
||||
}
|
||||
}
|
||||
val defaultPrgOutput = if (output.endsWith(platform.fileExtension)) output else output + platform.fileExtension
|
||||
|
@ -165,7 +175,7 @@ object Main {
|
|||
f"${path.getFileName}%s ${start}%04X ${start}%04X ${codeLength}%04X".getBytes(StandardCharsets.UTF_8))
|
||||
}
|
||||
}
|
||||
errorReporting.debug(s"Total time: ${Math.round((System.nanoTime() - startTime)/1e6)} ms")
|
||||
errorReporting.info(s"Total time: ${Math.round((System.nanoTime() - startTime)/1e6)} ms")
|
||||
c.runFileName.foreach{ program =>
|
||||
if (File.separatorChar == '\\') {
|
||||
if (!new File(program).exists() && !new File(program + ".exe").exists()) {
|
||||
|
@ -283,6 +293,7 @@ object Main {
|
|||
if (options.flag(CompilationFlag.EmitHudsonOpcodes)) HudsonOptimizations.All else Nil,
|
||||
if (options.flag(CompilationFlag.EmitEmulation65816Opcodes)) SixteenOptimizations.AllForEmulation else Nil,
|
||||
if (options.flag(CompilationFlag.EmitNative65816Opcodes)) SixteenOptimizations.AllForNative else Nil,
|
||||
if (options.flag(CompilationFlag.IdentityPage)) IdentityPageOptimizations.All else Nil,
|
||||
if (options.flag(CompilationFlag.DangerousOptimizations)) DangerousOptimizations.All else Nil
|
||||
).flatten
|
||||
val goodCycle = List.fill(optLevel - 2)(OptimizationPresets.Good ++ goodExtras).flatten
|
||||
|
@ -321,7 +332,9 @@ object Main {
|
|||
val assemblyOptimizations = optLevel match {
|
||||
case 0 => Nil
|
||||
case _ =>
|
||||
if (options.flag(CompilationFlag.EmitZ80Opcodes))
|
||||
if (options.flag(CompilationFlag.EmitR800Opcodes))
|
||||
Z80OptimizationPresets.GoodForR800
|
||||
else if (options.flag(CompilationFlag.EmitZ80Opcodes))
|
||||
Z80OptimizationPresets.GoodForZ80
|
||||
else if (options.flag(CompilationFlag.EmitIntel8080Opcodes))
|
||||
Z80OptimizationPresets.GoodForIntel8080
|
||||
|
@ -427,7 +440,7 @@ object Main {
|
|||
p.toLowerCase(Locale.ROOT),
|
||||
errorReporting.fatal("Invalid label file format: " + p))
|
||||
c.copy(outputLabels = true, outputLabelsFormatOverride = Some(f))
|
||||
}.description("Generate also the label file in the given format. Available options: vice, nesasm, sym.")
|
||||
}.description("Generate also the label file in the given format. Available options: vice, nesasm, sym, raw.")
|
||||
|
||||
boolean("-fbreakpoints", "-fno-breakpoints").action((c,v) =>
|
||||
c.changeFlag(CompilationFlag.EnableBreakpoints, v)
|
||||
|
@ -487,7 +500,7 @@ object Main {
|
|||
} else {
|
||||
errorReporting.fatal("Invalid syntax for -D option")
|
||||
}
|
||||
}.description("Define a feature value for the preprocessor.")
|
||||
}.description("Define a feature value for the preprocessor.").maxCount(Integer.MAX_VALUE)
|
||||
|
||||
boolean("-finput_intel_syntax", "-finput_zilog_syntax").repeatable().action((c,v) =>
|
||||
c.changeFlag(CompilationFlag.UseIntelSyntaxForInput, v)
|
||||
|
@ -650,7 +663,7 @@ object Main {
|
|||
c.changeFlag(CompilationFlag.CompactReturnDispatchParams, v)
|
||||
}.description("Whether parameter values in return dispatch statements may overlap other objects. Enabled by default.")
|
||||
boolean("-fbounds-checking", "-fno-bounds-checking").action { (c, v) =>
|
||||
c.changeFlag(CompilationFlag.VariableOverlap, v)
|
||||
c.changeFlag(CompilationFlag.CheckIndexOutOfBounds, v)
|
||||
}.description("Whether should insert bounds checking on array access.")
|
||||
boolean("-flenient-encoding", "-fno-lenient-encoding").action { (c, v) =>
|
||||
c.changeFlag(CompilationFlag.LenientTextEncoding, v)
|
||||
|
@ -745,6 +758,9 @@ object Main {
|
|||
boolean("-fregister-variables", "-fno-register-variables").action { (c, v) =>
|
||||
c.changeFlag(CompilationFlag.RegisterVariables, v)
|
||||
}.description("Allow moving local variables into CPU registers. Enabled by default.")
|
||||
boolean("-fidentity-page", "-fno-identity-page").action { (c, v) =>
|
||||
c.changeFlag(CompilationFlag.IdentityPage, v)
|
||||
}.description("Whether should use an identity page to optimize certain operations.")
|
||||
flag("-Os", "--size").repeatable().action { c =>
|
||||
c.changeFlag(CompilationFlag.OptimizeForSize, true).
|
||||
changeFlag(CompilationFlag.OptimizeForSpeed, false).
|
||||
|
@ -757,6 +773,7 @@ object Main {
|
|||
}.description("Prefer faster code even if it is slightly bigger (experimental). Implies -finline.")
|
||||
flag("-Ob", "--blast-processing").repeatable().action { c =>
|
||||
c.changeFlag(CompilationFlag.OptimizeForSize, false).
|
||||
changeFlag(CompilationFlag.IdentityPage, true).
|
||||
changeFlag(CompilationFlag.OptimizeForSpeed, true).
|
||||
changeFlag(CompilationFlag.OptimizeForSonicSpeed, true)
|
||||
}.description("Prefer faster code even if it is much bigger (experimental). Implies -finline.")
|
||||
|
@ -818,6 +835,10 @@ object Main {
|
|||
c.changeFlag(CompilationFlag.RorWarning, v)
|
||||
}.description("Whether should warn about the ROR instruction (6502 only). Default: disabled.")
|
||||
|
||||
boolean("-Woverflow", "-Wno-overflow").repeatable().action { (c, v) =>
|
||||
c.changeFlag(CompilationFlag.ByteOverflowWarning, v)
|
||||
}.description("Whether should warn about byte overflow. Default: enabled.")
|
||||
|
||||
boolean("-Wuseless", "-Wno-useless").repeatable().action { (c, v) =>
|
||||
c.changeFlag(CompilationFlag.UselessCodeWarning, v)
|
||||
}.description("Whether should warn about code that does nothing. Default: enabled.")
|
||||
|
|
|
@ -42,6 +42,7 @@ class Platform(
|
|||
val outputLabelsFormat: DebugOutputFormat,
|
||||
val outputStyle: OutputStyle.Value
|
||||
) {
|
||||
|
||||
def hasZeroPage: Boolean = cpuFamily == CpuFamily.M6502
|
||||
|
||||
def cpuFamily: CpuFamily.Value = CpuFamily.forType(this.cpu)
|
||||
|
@ -60,6 +61,18 @@ class Platform(
|
|||
val e2 = bankEndBefore(bank2)
|
||||
e2 > s1 && s2 < e1
|
||||
}
|
||||
|
||||
lazy val banksExplicitlyWrittenToOutput: Set[String] = bankNumbers.keySet.filter(b => defaultOutputPackager.writes(b)).toSet
|
||||
|
||||
def getMesenLabelCategory(bank: String, address: Int): Char = {
|
||||
// see: https://www.mesen.ca/docs/debugging/debuggerintegration.html#mesen-label-files-mlb
|
||||
val start = bankStart(bank)
|
||||
val end = bankEndBefore(bank)
|
||||
if (start > address || address >= end) 'G'
|
||||
else if (banksExplicitlyWrittenToOutput(bank)) 'P'
|
||||
else if (bank == "default") 'R'
|
||||
else 'W' // TODO: distinguish between W and S
|
||||
}
|
||||
}
|
||||
|
||||
object Platform {
|
||||
|
|
|
@ -2,7 +2,7 @@ package millfork.assembly
|
|||
|
||||
import millfork.{CompilationFlag, CompilationOptions}
|
||||
import millfork.compiler.LabelGenerator
|
||||
import millfork.env.{NormalFunction, ThingInMemory}
|
||||
import millfork.env.{Constant, NormalFunction, ThingInMemory}
|
||||
import millfork.error.Logger
|
||||
import millfork.node.NiceFunctionProperty
|
||||
|
||||
|
@ -10,8 +10,9 @@ import millfork.node.NiceFunctionProperty
|
|||
* @author Karol Stasiak
|
||||
*/
|
||||
case class OptimizationContext(options: CompilationOptions,
|
||||
labelMap: Map[String, (Int, Int)],
|
||||
labelMap: Map[String, (String, Int)],
|
||||
zreg: Option[ThingInMemory],
|
||||
identityPage: Constant,
|
||||
niceFunctionProperties: Set[(NiceFunctionProperty, String)]) {
|
||||
@inline
|
||||
def log: Logger = options.log
|
||||
|
@ -25,4 +26,6 @@ trait AssemblyOptimization[T <: AbstractCode] {
|
|||
def optimize(f: NormalFunction, code: List[T], context: OptimizationContext): List[T]
|
||||
|
||||
def requiredFlags: Set[CompilationFlag.Value] = Set.empty
|
||||
|
||||
def minimumRequiredLines: Int
|
||||
}
|
||||
|
|
|
@ -254,9 +254,10 @@ case class MLine(opcode: MOpcode.Value, addrMode: MAddrMode, parameter: Constant
|
|||
case MUL => overlaps(D)
|
||||
case ABX => reg == X || overlaps(B)
|
||||
case NOP | SWI | SWI2 | SWI3 | SYNC => false
|
||||
case LDB | LDA | LDX | LDY | LDU => false
|
||||
case INC | DEC | ROL | ROR | ASL | ASR | LSR | CLR | COM | NEG | TST => false // variants for A and B handled before
|
||||
case op if Branching(op) => false
|
||||
case JMP => false
|
||||
case JMP | RTS => false
|
||||
case _ => true // TODO
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ package millfork.assembly.m6809.opt
|
|||
|
||||
import millfork.assembly.AssemblyOptimization
|
||||
import millfork.assembly.m6809.MOpcode._
|
||||
import millfork.assembly.m6809.{Absolute, DAccumulatorIndexed, Immediate, InherentA, InherentB, LongRelative, MAddrMode, MLine, MState, PostIncremented, RegisterSet}
|
||||
import millfork.assembly.m6809.{Absolute, DAccumulatorIndexed, Immediate, Indexed, InherentA, InherentB, LongRelative, MAddrMode, MLine, MState, PostIncremented, RegisterSet}
|
||||
import millfork.env.{CompoundConstant, Constant, MathOperator}
|
||||
import millfork.node.M6809Register
|
||||
|
||||
|
@ -253,6 +253,36 @@ object AlwaysGoodMOptimizations {
|
|||
|
||||
)
|
||||
|
||||
val SimplifiableAddressing = new RuleBasedAssemblyOptimization("Simplifiable addressing",
|
||||
needsFlowInfo = FlowInfoRequirement.ForwardFlow,
|
||||
(NotFixed & MatchX(0) & HasAddrMode(Indexed(M6809Register.X, indirect = false)) & Not(HasOpcodeIn(LEAX, LEAY, LEAS, LEAU))) ~~> { (code, ctx) =>
|
||||
val x = ctx.get[Constant](0)
|
||||
code.map(l => l.copy(addrMode = Absolute(indirect = false), parameter = (x + l.parameter).quickSimplify))
|
||||
},
|
||||
(NotFixed & MatchY(0) & HasAddrMode(Indexed(M6809Register.Y, indirect = false)) & Not(HasOpcodeIn(LEAX, LEAY, LEAS, LEAU))) ~~> { (code, ctx) =>
|
||||
val y = ctx.get[Constant](0)
|
||||
code.map(l => l.copy(addrMode = Absolute(indirect = false), parameter = (y + l.parameter).quickSimplify))
|
||||
},
|
||||
(NotFixed & MatchX(0) & HasAddrMode(Indexed(M6809Register.X, indirect = false)) & HasOpcodeIn(LEAX, LEAY, LEAS, LEAU)) ~~> { (code, ctx) =>
|
||||
val x = ctx.get[Constant](0)
|
||||
code.map(l => l.copy(opcode = l.opcode match {
|
||||
case LEAX => LDX
|
||||
case LEAY => LDY
|
||||
case LEAU => LDU
|
||||
case LEAS => LDS
|
||||
}, addrMode = Immediate, parameter = (x + l.parameter).quickSimplify))
|
||||
},
|
||||
(NotFixed & MatchY(0) & HasAddrMode(Indexed(M6809Register.X, indirect = false)) & HasOpcodeIn(LEAX, LEAY, LEAS, LEAU)) ~~> { (code, ctx) =>
|
||||
val y = ctx.get[Constant](0)
|
||||
code.map(l => l.copy(opcode = l.opcode match {
|
||||
case LEAX => LDX
|
||||
case LEAY => LDY
|
||||
case LEAU => LDU
|
||||
case LEAS => LDS
|
||||
}, addrMode = Immediate, parameter = (y + l.parameter).quickSimplify))
|
||||
},
|
||||
)
|
||||
|
||||
val UnusedLabelRemoval = new RuleBasedAssemblyOptimization("Unused label removal",
|
||||
needsFlowInfo = FlowInfoRequirement.JustLabels,
|
||||
(Elidable & HasOpcode(LABEL) & HasCallerCount(0) & ParameterIsLocalLabel) ~~> (_ => Nil)
|
||||
|
@ -263,6 +293,7 @@ object AlwaysGoodMOptimizations {
|
|||
PointlessLoad,
|
||||
PointlessCompare,
|
||||
PointlessRegisterTransfers,
|
||||
SimplifiableAddressing,
|
||||
SimplifiableArithmetics,
|
||||
SimplifiableComparison,
|
||||
SimplifiableJumps,
|
||||
|
|
|
@ -35,6 +35,11 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
|
|||
def nzB(i: Long): CpuStatus =
|
||||
this.copy(n = SingleStatus((i & 0x80) != 0), z = SingleStatus((i & 0xff) == 0))
|
||||
|
||||
def nzB(i: Status[Int]): CpuStatus = i match {
|
||||
case SingleStatus(j) => this.copy(n = SingleStatus((j & 0x80) != 0), z = SingleStatus((j & 0xff) == 0))
|
||||
case _ => this.copy(n = AnyStatus, z = AnyStatus)
|
||||
}
|
||||
|
||||
def nzW(i: Long): CpuStatus =
|
||||
this.copy(n = SingleStatus((i & 0x8000) != 0), z = SingleStatus((i & 0xffff) == 0))
|
||||
|
||||
|
@ -44,6 +49,11 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
|
|||
case _ => this.nz
|
||||
}
|
||||
|
||||
def nzW(i: Status[Int]): CpuStatus = i match {
|
||||
case SingleStatus(j) => this.copy(n = SingleStatus((j & 0x8000) != 0), z = SingleStatus((j & 0xffff) == 0))
|
||||
case _ => this.copy(n = AnyStatus, z = AnyStatus)
|
||||
}
|
||||
|
||||
def ~(that: CpuStatus) = new CpuStatus(
|
||||
a = this.a ~ that.a,
|
||||
b = this.b ~ that.b,
|
||||
|
|
|
@ -2,10 +2,11 @@ package millfork.assembly.m6809.opt
|
|||
|
||||
import millfork.CompilationFlag
|
||||
import millfork.assembly.OptimizationContext
|
||||
import millfork.assembly.m6809.{Immediate, MLine, MLine0}
|
||||
import millfork.assembly.m6809.{Absolute, Immediate, Inherent, InherentA, InherentB, MLine, MLine0, TwoRegisters}
|
||||
import millfork.assembly.opt.Status.SingleFalse
|
||||
import millfork.assembly.opt.{AnyStatus, FlowCache, SingleStatus, Status}
|
||||
import millfork.env._
|
||||
import millfork.node.M6809NiceFunctionProperty.{DoesntChangeA, DoesntChangeB, DoesntChangeU, DoesntChangeX, DoesntChangeY}
|
||||
import millfork.node.{M6809Register, NiceFunctionProperty}
|
||||
|
||||
import scala.util.control.Breaks._
|
||||
|
@ -62,33 +63,108 @@ object ForwardFlowAnalysis {
|
|||
case _ => None
|
||||
}).fold(currentStatus)(_ ~ _)
|
||||
|
||||
case MLine0(JSR, _, MemoryAddressConstant(th)) =>
|
||||
case MLine0(JSR, am, MemoryAddressConstant(th)) =>
|
||||
var prU = bpInU
|
||||
var prY = bpInY
|
||||
var prA = false
|
||||
var prB = false
|
||||
var prX = false
|
||||
(am, th) match {
|
||||
case (Absolute(false), fun: FunctionInMemory) =>
|
||||
val nfp = optimizationContext.niceFunctionProperties
|
||||
val fn = fun.name
|
||||
if (nfp(DoesntChangeX -> fn)) prX = true
|
||||
if (nfp(DoesntChangeY -> fn)) prY = true
|
||||
if (nfp(DoesntChangeU -> fn)) prU = true
|
||||
if (nfp(DoesntChangeA -> fn)) prA = true
|
||||
if (nfp(DoesntChangeB -> fn)) prB = true
|
||||
case _ =>
|
||||
}
|
||||
currentStatus = initialStatus.copy(
|
||||
memStack = currentStatus.memStack,
|
||||
u = if (bpInU) currentStatus.u else AnyStatus,
|
||||
y = if (bpInY) currentStatus.y else AnyStatus
|
||||
u = if (prU) currentStatus.u else AnyStatus,
|
||||
x = if (prX) currentStatus.x else AnyStatus,
|
||||
y = if (prY) currentStatus.y else AnyStatus,
|
||||
a = if (prA) currentStatus.a else AnyStatus,
|
||||
b = if (prB) currentStatus.b else AnyStatus
|
||||
)
|
||||
|
||||
case MLine0(JSR | BYTE, _, _) =>
|
||||
currentStatus = initialStatus
|
||||
|
||||
case MLine0(NOP, _, _) =>
|
||||
()
|
||||
case MLine0(op, Immediate, constant) if ForwardFlowAnalysisForImmediate.hasDefinition(op) =>
|
||||
ForwardFlowAnalysisForImmediate.get(op)(constant, currentStatus)
|
||||
case MLine0(op, Inherent, _) if ForwardFlowAnalysisForInherent.hasDefinition(op) =>
|
||||
ForwardFlowAnalysisForInherent.get(op)(currentStatus)
|
||||
case MLine0(op, InherentA, _) if ForwardFlowAnalysisForInherentA.hasDefinition(op) =>
|
||||
ForwardFlowAnalysisForInherentA.get(op)(currentStatus)
|
||||
case MLine0(op, InherentB, _) if ForwardFlowAnalysisForInherentB.hasDefinition(op) =>
|
||||
ForwardFlowAnalysisForInherentB.get(op)(currentStatus)
|
||||
|
||||
case MLine0(LDA, Immediate, NumericConstant(n, _)) =>
|
||||
currentStatus = currentStatus.copy(a = SingleStatus(n.toInt & 0xff), v = SingleFalse).nzB(n)
|
||||
case MLine0(LDB, Immediate, NumericConstant(n, _)) =>
|
||||
currentStatus = currentStatus.copy(b = SingleStatus(n.toInt & 0xff), v = SingleFalse).nzB(n)
|
||||
case MLine0(LDD, Immediate, NumericConstant(n, _)) =>
|
||||
currentStatus = currentStatus.copy(
|
||||
a = SingleStatus(n.toInt.>>(8) & 0xff),
|
||||
b = SingleStatus(n.toInt & 0xff),
|
||||
v = SingleFalse
|
||||
).nzW(n)
|
||||
case MLine0(LDX, Immediate, c) =>
|
||||
currentStatus = currentStatus.copy(x = SingleStatus(c), v = SingleFalse).nzW(c)
|
||||
case MLine0(LDY, Immediate, c) =>
|
||||
currentStatus = currentStatus.copy(y = SingleStatus(c), v = SingleFalse).nzW(c)
|
||||
case MLine0(LDA, _, _) =>
|
||||
currentStatus = currentStatus.copy(a = AnyStatus, n = AnyStatus, z = AnyStatus, v = SingleFalse)
|
||||
case MLine0(LDB, _, _) =>
|
||||
currentStatus = currentStatus.copy(b = AnyStatus, n = AnyStatus, z = AnyStatus, v = SingleFalse)
|
||||
case MLine0(LDD, _, _) =>
|
||||
currentStatus = currentStatus.copy(a = AnyStatus, b = AnyStatus, n = AnyStatus, z = AnyStatus, v = SingleFalse)
|
||||
case MLine0(LDX, _, _) =>
|
||||
currentStatus = currentStatus.copy(x = AnyStatus, n = AnyStatus, z = AnyStatus, v = SingleFalse)
|
||||
case MLine0(LDY, _, _) =>
|
||||
currentStatus = currentStatus.copy(y = AnyStatus, n = AnyStatus, z = AnyStatus, v = SingleFalse)
|
||||
case MLine0(STA | STB | STD | STX | STU | STY | STS, _, _) =>
|
||||
// don't change
|
||||
case MLine0(TFR, TwoRegisters(source, target), _) =>
|
||||
import M6809Register._
|
||||
(source, target) match {
|
||||
case (A, B) => currentStatus = currentStatus.copy(b = currentStatus.a)
|
||||
case (B, A) => currentStatus = currentStatus.copy(a = currentStatus.b)
|
||||
case (CC, A) => currentStatus = currentStatus.copy(a = AnyStatus)
|
||||
case (CC, B) => currentStatus = currentStatus.copy(b = AnyStatus)
|
||||
case (A, CC) | (B, CC) => currentStatus = currentStatus.copy(c = AnyStatus, z = AnyStatus, n = AnyStatus, v = AnyStatus)
|
||||
case (S, D) => currentStatus = currentStatus.copy(a = AnyStatus, b = AnyStatus)
|
||||
case (S, X) => currentStatus = currentStatus.copy(x = AnyStatus)
|
||||
case (S, Y) => currentStatus = currentStatus.copy(y = AnyStatus)
|
||||
case (S, U) => currentStatus = currentStatus.copy(u = AnyStatus)
|
||||
case (D, X) => currentStatus = currentStatus.copy(x = currentStatus.d.map(n => NumericConstant(n, 2)))
|
||||
case (D, Y) => currentStatus = currentStatus.copy(y = currentStatus.d.map(n => NumericConstant(n, 2)))
|
||||
case (D, U) => currentStatus = currentStatus.copy(u = currentStatus.d.map(n => NumericConstant(n, 2)))
|
||||
case (X, Y) => currentStatus = currentStatus.copy(y = currentStatus.x)
|
||||
case (X, U) => currentStatus = currentStatus.copy(u = currentStatus.x)
|
||||
case (Y, X) => currentStatus = currentStatus.copy(x = currentStatus.y)
|
||||
case (Y, U) => currentStatus = currentStatus.copy(u = currentStatus.y)
|
||||
case (U, X) => currentStatus = currentStatus.copy(x = currentStatus.u)
|
||||
case (U, Y) => currentStatus = currentStatus.copy(y = currentStatus.u)
|
||||
case (X, D) =>
|
||||
val (h, l) = currentStatus.x.toHiLo
|
||||
currentStatus = currentStatus.copy(a = h, b = l)
|
||||
case (Y, D) =>
|
||||
val (h, l) = currentStatus.y.toHiLo
|
||||
currentStatus = currentStatus.copy(a = h, b = l)
|
||||
case (U, D) =>
|
||||
val (h, l) = currentStatus.u.toHiLo
|
||||
currentStatus = currentStatus.copy(a = h, b = l)
|
||||
case _ => currentStatus = initialStatus
|
||||
}
|
||||
case MLine0(EXG, TwoRegisters(source, target), _) =>
|
||||
import M6809Register._
|
||||
(source, target) match {
|
||||
case (A, B) | (B, A) => currentStatus = currentStatus.copy(a = currentStatus.b, b = currentStatus.a)
|
||||
case (A, CC) | (CC, A) => currentStatus = currentStatus.copy(a = AnyStatus, c = AnyStatus, v = AnyStatus, n = AnyStatus, z = AnyStatus)
|
||||
case (B, CC) | (CC, B) => currentStatus = currentStatus.copy(b = AnyStatus, c = AnyStatus, v = AnyStatus, n = AnyStatus, z = AnyStatus)
|
||||
case (X, Y) | (Y, X) => currentStatus = currentStatus.copy(y = currentStatus.x, x = currentStatus.y)
|
||||
case (U, Y) | (Y, U) => currentStatus = currentStatus.copy(y = currentStatus.u, u = currentStatus.y)
|
||||
case (X, U) | (U, X) => currentStatus = currentStatus.copy(u = currentStatus.x, x = currentStatus.u)
|
||||
case (X, D) | (D, X) =>
|
||||
val (h, l) = currentStatus.x.toHiLo
|
||||
currentStatus = currentStatus.copy(a = h, b = l, x = currentStatus.d.map(n => NumericConstant(n, 2)))
|
||||
case (Y, D) | (D, Y) =>
|
||||
val (h, l) = currentStatus.y.toHiLo
|
||||
currentStatus = currentStatus.copy(a = h, b = l, y = currentStatus.d.map(n => NumericConstant(n, 2)))
|
||||
case (U, D) | (D, U) =>
|
||||
val (h, l) = currentStatus.u.toHiLo
|
||||
currentStatus = currentStatus.copy(a = h, b = l, u = currentStatus.d.map(n => NumericConstant(n, 2)))
|
||||
case _ => currentStatus = initialStatus
|
||||
}
|
||||
|
||||
case MLine0(opcode, addrMode, _) =>
|
||||
// TODO
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
package millfork.assembly.m6809.opt
|
||||
|
||||
import millfork.assembly.m6809.MOpcode
|
||||
import millfork.assembly.m6809.MOpcode._
|
||||
import millfork.assembly.opt.{AnyStatus, SingleStatus, Status}
|
||||
import millfork.assembly.opt.Status.SingleFalse
|
||||
import millfork.env.Constant
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object ForwardFlowAnalysisForImmediate {
|
||||
private val map: Map[MOpcode.Value, (Constant, CpuStatus) => CpuStatus] = Map(
|
||||
LDX -> {(c, currentStatus) =>
|
||||
currentStatus.copy(x = SingleStatus(c), v = SingleFalse).nzW(c)
|
||||
},
|
||||
LDY -> {(c, currentStatus) =>
|
||||
currentStatus.copy(y = SingleStatus(c), v = SingleFalse).nzW(c)
|
||||
},
|
||||
LDU -> {(c, currentStatus) =>
|
||||
currentStatus.copy(u = SingleStatus(c), v = SingleFalse).nzW(c)
|
||||
},
|
||||
LDA -> {(c, currentStatus) =>
|
||||
val value = Status.fromByte(c)
|
||||
currentStatus.copy(a = value, v = SingleFalse).nzB(value)
|
||||
},
|
||||
LDB -> {(c, currentStatus) =>
|
||||
val value = Status.fromByte(c)
|
||||
currentStatus.copy(a = value, v = SingleFalse).nzB(value)
|
||||
},
|
||||
LDD -> {(c, currentStatus) =>
|
||||
val value = Status.fromByte(c)
|
||||
currentStatus.copy(a = value.hi, b = value.lo, v = SingleFalse).nzW(value)
|
||||
},
|
||||
ORA -> { (c, currentStatus) =>
|
||||
val newValue = (currentStatus.a <*> Status.fromByte(c)) { (x, y) => (x | y) & 0xff }
|
||||
currentStatus.copy(a = newValue, v = SingleFalse).nzB(newValue)
|
||||
},
|
||||
ORB -> { (c, currentStatus) =>
|
||||
val newValue = (currentStatus.b <*> Status.fromByte(c)) { (x, y) => (x | y) & 0xff }
|
||||
currentStatus.copy(b = newValue, v = SingleFalse).nzB(newValue)
|
||||
},
|
||||
EORA -> { (c, currentStatus) =>
|
||||
val newValue = (currentStatus.a <*> Status.fromByte(c)) { (x, y) => (x ^ y) & 0xff }
|
||||
currentStatus.copy(a = newValue, v = SingleFalse).nzB(newValue)
|
||||
},
|
||||
EORB -> { (c, currentStatus) =>
|
||||
val newValue = (currentStatus.b <*> Status.fromByte(c)) { (x, y) => (x ^ y) & 0xff }
|
||||
currentStatus.copy(b = newValue, v = SingleFalse).nzB(newValue)
|
||||
},
|
||||
ANDA -> { (c, currentStatus) =>
|
||||
val newValue = (currentStatus.a <*> Status.fromByte(c)) { (x, y) => (x & y) & 0xff }
|
||||
currentStatus.copy(a = newValue, v = SingleFalse).nzB(newValue)
|
||||
},
|
||||
ANDB -> { (c, currentStatus) =>
|
||||
val newValue = (currentStatus.b <*> Status.fromByte(c)) { (x, y) => (x & y) & 0xff }
|
||||
currentStatus.copy(b = newValue, v = SingleFalse).nzB(newValue)
|
||||
},
|
||||
ADDA -> { (c, currentStatus) =>
|
||||
val (newValue, newCarry) = currentStatus.a.adc(Status.fromByte(c), SingleFalse)
|
||||
currentStatus.copy(a = newValue, v = AnyStatus, c = newCarry).nzB(newValue)
|
||||
},
|
||||
ADDB -> { (c, currentStatus) =>
|
||||
val (newValue, newCarry) = currentStatus.b.adc(Status.fromByte(c), SingleFalse)
|
||||
currentStatus.copy(b = newValue, v = AnyStatus, c = newCarry).nzB(newValue)
|
||||
},
|
||||
ADCA -> { (c, currentStatus) =>
|
||||
val (newValue, newCarry) = currentStatus.a.adc(Status.fromByte(c), currentStatus.c)
|
||||
currentStatus.copy(a = newValue, v = AnyStatus, c = newCarry).nzB(newValue)
|
||||
},
|
||||
ADCB -> { (c, currentStatus) =>
|
||||
val (newValue, newCarry) = currentStatus.b.adc(Status.fromByte(c), currentStatus.c)
|
||||
currentStatus.copy(b = newValue, v = AnyStatus, c = newCarry).nzB(newValue)
|
||||
},
|
||||
)
|
||||
|
||||
def hasDefinition(opcode: MOpcode.Value): Boolean = map.contains(opcode)
|
||||
|
||||
def get(opcode: MOpcode.Value): (Constant, CpuStatus) => CpuStatus = map(opcode)
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package millfork.assembly.m6809.opt
|
||||
|
||||
import millfork.assembly.m6809.MOpcode
|
||||
import millfork.assembly.m6809.MOpcode._
|
||||
import millfork.assembly.opt.Status.SingleFalse
|
||||
import millfork.assembly.opt.{AnyStatus, SingleStatus, Status}
|
||||
import millfork.env.Constant
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object ForwardFlowAnalysisForInherent {
|
||||
private val map: Map[MOpcode.Value, CpuStatus => CpuStatus] = Map(
|
||||
NOP -> identity,
|
||||
MUL -> { currentStatus =>
|
||||
val newD = (currentStatus.a <*> currentStatus.b) { (a, b) => ((a & 0xff) * (b & 0xff)) & 0xff }
|
||||
currentStatus.copy(c = AnyStatus, z = AnyStatus, a = newD.hi, b = newD.hi)
|
||||
},
|
||||
SEX -> { currentStatus =>
|
||||
val newA = currentStatus.b.map{ n => if (n.&(0x80) == 0) 0 else 0xff }
|
||||
currentStatus.copy(v = Status.SingleFalse, n = AnyStatus, z = AnyStatus, a = newA)
|
||||
},
|
||||
)
|
||||
|
||||
def hasDefinition(opcode: MOpcode.Value): Boolean = map.contains(opcode)
|
||||
|
||||
def get(opcode: MOpcode.Value): CpuStatus => CpuStatus = map(opcode)
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package millfork.assembly.m6809.opt
|
||||
|
||||
import millfork.assembly.m6809.MOpcode
|
||||
import millfork.assembly.m6809.MOpcode._
|
||||
import millfork.assembly.opt.{AnyStatus, SingleStatus, Status}
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object ForwardFlowAnalysisForInherentA {
|
||||
private val map: Map[MOpcode.Value, CpuStatus => CpuStatus] = Map(
|
||||
ASL -> { currentStatus =>
|
||||
val newValue = currentStatus.a.map(n => n.<<(1).&(0xff))
|
||||
currentStatus.copy(a = newValue, c = currentStatus.a.bit7, v = AnyStatus).nzB(newValue)
|
||||
},
|
||||
LSR -> { currentStatus =>
|
||||
val newValue = currentStatus.a.map(n => n.>>(1).&(0x7f))
|
||||
currentStatus.copy(a = newValue, c = currentStatus.a.bit0, v = AnyStatus).nzB(newValue)
|
||||
},
|
||||
CLR -> { currentStatus =>
|
||||
currentStatus.copy(a = Status.SingleZero, c = Status.SingleFalse, v = Status.SingleFalse, n = Status.SingleFalse, z = Status.SingleFalse)
|
||||
},
|
||||
COM -> { currentStatus =>
|
||||
val newValue = currentStatus.a.map(n => n.^(0xff).&(0xff))
|
||||
currentStatus.copy(a = newValue, c = Status.SingleTrue, v = Status.SingleFalse).nzB(newValue)
|
||||
},
|
||||
DEC -> { currentStatus =>
|
||||
val newValue = currentStatus.a.map(n => n.+(1).&(0xff))
|
||||
currentStatus.copy(a = newValue, v = AnyStatus).nzB(newValue)
|
||||
},
|
||||
INC -> { currentStatus =>
|
||||
val newValue = currentStatus.a.map(n => n.-(1).&(0xff))
|
||||
currentStatus.copy(a = newValue, v = AnyStatus).nzB(newValue)
|
||||
},
|
||||
NEG -> { currentStatus =>
|
||||
val newValue = currentStatus.a.map(n => (-n).&(0xff))
|
||||
currentStatus.copy(a = newValue, c = AnyStatus, v = AnyStatus).nzB(newValue)
|
||||
},
|
||||
TST -> { currentStatus =>
|
||||
currentStatus.copy(v = Status.SingleFalse).nzB(currentStatus.a)
|
||||
},
|
||||
)
|
||||
|
||||
def hasDefinition(opcode: MOpcode.Value): Boolean = map.contains(opcode)
|
||||
|
||||
def get(opcode: MOpcode.Value): CpuStatus => CpuStatus = map(opcode)
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
package millfork.assembly.m6809.opt
|
||||
|
||||
import millfork.assembly.m6809.MOpcode
|
||||
import millfork.assembly.m6809.MOpcode._
|
||||
import millfork.assembly.opt.{AnyStatus, Status}
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object ForwardFlowAnalysisForInherentB {
|
||||
private val map: Map[MOpcode.Value, CpuStatus => CpuStatus] = Map(
|
||||
ASL -> { currentStatus =>
|
||||
val newValue = currentStatus.b.map(n => n.<<(1).&(0xff))
|
||||
currentStatus.copy(b = newValue, c = currentStatus.b.bit7, v = AnyStatus).nzB(newValue)
|
||||
},
|
||||
LSR -> { currentStatus =>
|
||||
val newValue = currentStatus.b.map(n => n.>>(1).&(0x7f))
|
||||
currentStatus.copy(b = newValue, c = currentStatus.b.bit0, v = AnyStatus).nzB(newValue)
|
||||
},
|
||||
CLR -> { currentStatus =>
|
||||
currentStatus.copy(b = Status.SingleZero, c = Status.SingleFalse, v = Status.SingleFalse, n = Status.SingleFalse, z = Status.SingleFalse)
|
||||
},
|
||||
COM -> { currentStatus =>
|
||||
val newValue = currentStatus.b.map(n => n.^(0xff).&(0xff))
|
||||
currentStatus.copy(b = newValue, c = Status.SingleTrue, v = Status.SingleFalse).nzB(newValue)
|
||||
},
|
||||
DEC -> { currentStatus =>
|
||||
val newValue = currentStatus.b.map(n => n.+(1).&(0xff))
|
||||
currentStatus.copy(b = newValue, v = AnyStatus).nzB(newValue)
|
||||
},
|
||||
INC -> { currentStatus =>
|
||||
val newValue = currentStatus.b.map(n => n.-(1).&(0xff))
|
||||
currentStatus.copy(b = newValue, v = AnyStatus).nzB(newValue)
|
||||
},
|
||||
NEG -> { currentStatus =>
|
||||
val newValue = currentStatus.b.map(n => (-n).&(0xff))
|
||||
currentStatus.copy(b = newValue, c = AnyStatus, v = AnyStatus).nzB(newValue)
|
||||
},
|
||||
TST -> { currentStatus =>
|
||||
currentStatus.copy(v = Status.SingleFalse).nzB(currentStatus.b)
|
||||
},
|
||||
)
|
||||
|
||||
def hasDefinition(opcode: MOpcode.Value): Boolean = map.contains(opcode)
|
||||
|
||||
def get(opcode: MOpcode.Value): CpuStatus => CpuStatus = map(opcode)
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package millfork.assembly.m6809.opt
|
||||
|
||||
import jdk.jfr.BooleanFlag
|
||||
import millfork.CompilationFlag
|
||||
import millfork.assembly._
|
||||
import millfork.assembly.m6809.{Absolute, MLine, MLine0, MOpcode, MState}
|
||||
import millfork.assembly.opt.FlowCache
|
||||
|
@ -82,6 +83,7 @@ object ReverseFlowAnalyzer {
|
|||
val readsB: Set[String] = Set("call")
|
||||
val readsX: Set[String] = Set("call")
|
||||
val readsY: Set[String] = Set("")
|
||||
val preservesX: Set[String] = Set("__divmod_u8u8u8u8")
|
||||
|
||||
val cache = new FlowCache[MLine, CpuImportance]("m6809 reverse")
|
||||
private val importanceBeforeJsr: CpuImportance = CpuImportance(
|
||||
|
@ -97,7 +99,7 @@ object ReverseFlowAnalyzer {
|
|||
hf = Unimportant)
|
||||
private val finalImportance: CpuImportance = CpuImportance(
|
||||
a = Important, b = Important,
|
||||
x = Important, y = Important, u = Important,
|
||||
x = Important, y = Important, u = Important, // TODO: correctly check which registers are important given the function and the compilation options
|
||||
cf = Unimportant, vf = Unimportant, hf = Unimportant, zf = Unimportant, nf = Unimportant)
|
||||
|
||||
//noinspection RedundantNewCaseClass
|
||||
|
@ -109,11 +111,23 @@ object ReverseFlowAnalyzer {
|
|||
|
||||
var changed = true
|
||||
changed = true
|
||||
val actualFinalImportance = f.returnType match {
|
||||
case FlagBooleanType(_, _, _) => finalImportance.copy(cf = Important, zf = Important, nf = Important, vf = Important)
|
||||
case t if t.size == 1 => finalImportance.copy(a = Unimportant)
|
||||
case t if t.size == 0 => finalImportance.copy(a = Unimportant, b = Unimportant)
|
||||
case _ => finalImportance
|
||||
val actualFinalImportance = {
|
||||
var tmp = f.returnType match {
|
||||
case FlagBooleanType(_, _, _) => finalImportance.copy(cf = Important, zf = Important, nf = Important, vf = Important)
|
||||
case t if t.size == 1 => finalImportance.copy(a = Unimportant)
|
||||
case t if t.size == 0 => finalImportance.copy(a = Unimportant, b = Unimportant)
|
||||
case _ => finalImportance
|
||||
}
|
||||
if (!f.inAssembly) {
|
||||
tmp = tmp.copy(x = Unimportant)
|
||||
if (!optimizationContext.options.flag(CompilationFlag.UseUForStack)) {
|
||||
tmp = tmp.copy(u = Unimportant)
|
||||
}
|
||||
if (!optimizationContext.options.flag(CompilationFlag.UseYForStack)) {
|
||||
tmp = tmp.copy(y = Unimportant)
|
||||
}
|
||||
}
|
||||
tmp
|
||||
}
|
||||
while (changed) {
|
||||
changed = false
|
||||
|
@ -179,6 +193,7 @@ object ReverseFlowAnalyzer {
|
|||
if (readsB(fun.name)) result = result.copy(b = Important)
|
||||
if (readsX(fun.name)) result = result.copy(x = Important)
|
||||
if (readsY(fun.name)) result = result.copy(y = Important)
|
||||
if (preservesX(fun.name)) result = result.copy(x = currentImportance.x)
|
||||
currentImportance = result.copy(
|
||||
a = if (niceFunctionProperties(DoesntChangeA -> fun.name)) currentImportance.a ~ result.a else result.a,
|
||||
b = if (niceFunctionProperties(DoesntChangeB -> fun.name)) currentImportance.b ~ result.b else result.b,
|
||||
|
|
|
@ -30,42 +30,52 @@ object FlowInfoRequirement extends Enumeration {
|
|||
}
|
||||
|
||||
def assertLabels(x: FlowInfoRequirement.Value): Unit = x match {
|
||||
case NoRequirement => FatalErrorReporting.reportFlyingPig("Backward flow info required")
|
||||
case NoRequirement => FatalErrorReporting.reportFlyingPig("Label info required")
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
trait AssemblyRuleSet{
|
||||
def flatten: Seq[AssemblyRule]
|
||||
|
||||
def minimumRequiredLines: Int
|
||||
}
|
||||
|
||||
class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInfoRequirement.Value, val rules: AssemblyRuleSet*) extends AssemblyOptimization[MLine] {
|
||||
|
||||
private val actualRules = rules.flatMap(_.flatten)
|
||||
actualRules.foreach(_.pattern.validate(needsFlowInfo))
|
||||
private val actualRulesWithIndex = actualRules.zipWithIndex
|
||||
|
||||
override val minimumRequiredLines: Int = rules.map(_.minimumRequiredLines).min
|
||||
|
||||
override def toString: String = name
|
||||
|
||||
override def optimize(f: NormalFunction, code: List[MLine], optimizationContext: OptimizationContext): List[MLine] = {
|
||||
val taggedCode = FlowAnalyzer.analyze(f, code, optimizationContext, needsFlowInfo)
|
||||
val (changed, optimized) = optimizeImpl(f, taggedCode, optimizationContext)
|
||||
if (changed) optimized else code
|
||||
optimizeImpl(f, code, taggedCode, optimizationContext)
|
||||
}
|
||||
|
||||
def optimizeImpl(f: NormalFunction, code: List[(FlowInfo, MLine)], optimizationContext: OptimizationContext): (Boolean, List[MLine]) = {
|
||||
final def optimizeImpl(f: NormalFunction, code: List[MLine], taggedCode: List[(FlowInfo, MLine)], optimizationContext: OptimizationContext): List[MLine] = {
|
||||
val log = optimizationContext.log
|
||||
code match {
|
||||
case Nil => false -> Nil
|
||||
taggedCode match {
|
||||
case Nil => code
|
||||
case head :: tail =>
|
||||
for ((rule, index) <- actualRules.zipWithIndex) {
|
||||
val codeLength = code.length
|
||||
for {
|
||||
(rule, index) <- actualRulesWithIndex
|
||||
if codeLength >= rule.minimumRequiredLines
|
||||
} {
|
||||
val ctx = new AssemblyMatchingContext(
|
||||
optimizationContext.options,
|
||||
optimizationContext.labelMap,
|
||||
optimizationContext.niceFunctionProperties,
|
||||
head._1.labelUseCount(_)
|
||||
)
|
||||
rule.pattern.matchTo(ctx, code) match {
|
||||
rule.pattern.matchTo(ctx, taggedCode) match {
|
||||
case Some(rest: List[(FlowInfo, MLine)]) =>
|
||||
val matchedChunkToOptimize: List[MLine] = code.take(code.length - rest.length).map(_._2)
|
||||
val optimizedChunkLengthBefore = taggedCode.length - rest.length
|
||||
val (matchedChunkToOptimize, restOfCode) = code.splitAt(optimizedChunkLengthBefore)
|
||||
val optimizedChunk: List[MLine] = rule.result(matchedChunkToOptimize, ctx)
|
||||
val optimizedChunkWithSource =
|
||||
if (!ctx.compilationOptions.flag(CompilationFlag.LineNumbersInAssembly)) optimizedChunk
|
||||
|
@ -74,34 +84,40 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf
|
|||
else if (optimizedChunk.size == 1) optimizedChunk.map(_.pos(SourceLine.merge(matchedChunkToOptimize.map(_.source))))
|
||||
else if (matchedChunkToOptimize.flatMap(_.source).toSet.size == 1) optimizedChunk.map(_.pos(SourceLine.merge(matchedChunkToOptimize.map(_.source))))
|
||||
else optimizedChunk
|
||||
log.debug(s"Applied $name ($index)")
|
||||
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) {
|
||||
val before = code.head._1.statusBefore
|
||||
val after = code(matchedChunkToOptimize.length - 1)._1.importanceAfter
|
||||
log.trace(s"Before: $before")
|
||||
log.trace(s"After: $after")
|
||||
if (log.debugEnabled) {
|
||||
log.debug(s"Applied $name ($index)")
|
||||
}
|
||||
if (log.traceEnabled) {
|
||||
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) {
|
||||
val before = head._1.statusBefore
|
||||
val after = taggedCode(matchedChunkToOptimize.length - 1)._1.importanceAfter
|
||||
log.trace(s"Before: $before")
|
||||
log.trace(s"After: $after")
|
||||
}
|
||||
matchedChunkToOptimize.filter(_.isPrintable).foreach(l => log.trace(l.toString))
|
||||
log.trace(" ↓")
|
||||
optimizedChunkWithSource.filter(_.isPrintable).foreach(l => log.trace(l.toString))
|
||||
}
|
||||
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) {
|
||||
return true -> (optimizedChunkWithSource ++ optimizeImpl(f, rest, optimizationContext)._2)
|
||||
return optimizedChunkWithSource ++ optimizeImpl(f, restOfCode, rest, optimizationContext)
|
||||
} else {
|
||||
return true -> optimize(f, optimizedChunkWithSource ++ rest.map(_._2), optimizationContext)
|
||||
return optimize(f, optimizedChunkWithSource ++ restOfCode, optimizationContext)
|
||||
}
|
||||
case None => ()
|
||||
}
|
||||
}
|
||||
val (changedTail, optimizedTail) = optimizeImpl(f, tail, optimizationContext)
|
||||
(changedTail, head._2 :: optimizedTail)
|
||||
val optimizedTail = optimizeImpl(f, code.tail, tail, optimizationContext)
|
||||
if (optimizedTail eq code.tail) {
|
||||
code
|
||||
} else {
|
||||
code.head :: optimizedTail
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AssemblyMatchingContext(val compilationOptions: CompilationOptions,
|
||||
val labelMap: Map[String, (Int, Int)],
|
||||
val labelMap: Map[String, (String, Int)],
|
||||
val niceFunctionProperties: Set[(NiceFunctionProperty, String)],
|
||||
val labelUseCount: String => Int) {
|
||||
@inline
|
||||
|
@ -211,10 +227,14 @@ class AssemblyMatchingContext(val compilationOptions: CompilationOptions,
|
|||
|
||||
case class AssemblyRule(pattern: AssemblyPattern, result: (List[MLine], AssemblyMatchingContext) => List[MLine]) extends AssemblyRuleSet {
|
||||
override def flatten: Seq[AssemblyRule] = List(this)
|
||||
|
||||
override def minimumRequiredLines: Int = pattern.minimumRequiredLines
|
||||
}
|
||||
|
||||
case class MultipleAssemblyRules(list: Seq[AssemblyRuleSet]) extends AssemblyRuleSet {
|
||||
override def flatten: Seq[AssemblyRule] = list.flatMap(_.flatten)
|
||||
|
||||
override val minimumRequiredLines: Int = flatten.map(_.minimumRequiredLines).sum
|
||||
}
|
||||
|
||||
trait AssemblyPattern {
|
||||
|
@ -235,6 +255,8 @@ trait AssemblyPattern {
|
|||
|
||||
def captureLength(i: Int) = CaptureLength(i, this)
|
||||
|
||||
def minimumRequiredLines: Int
|
||||
|
||||
}
|
||||
object HelperCheckers {
|
||||
private def badAddrModes(am: MAddrMode): Boolean = am match {
|
||||
|
@ -337,6 +359,8 @@ case class Capture(i: Int, pattern: AssemblyPattern) extends AssemblyPattern {
|
|||
}
|
||||
|
||||
override def toString: String = s"(?<$i>$pattern)"
|
||||
|
||||
override def minimumRequiredLines: Int = 0
|
||||
}
|
||||
|
||||
case class CaptureLength(i: Int, pattern: AssemblyPattern) extends AssemblyPattern {
|
||||
|
@ -349,6 +373,8 @@ case class CaptureLength(i: Int, pattern: AssemblyPattern) extends AssemblyPatte
|
|||
}
|
||||
|
||||
override def toString: String = s"(?<$i>$pattern)"
|
||||
|
||||
override def minimumRequiredLines: Int = 0
|
||||
}
|
||||
|
||||
|
||||
|
@ -358,6 +384,8 @@ case class Where(predicate: AssemblyMatchingContext => Boolean) extends Assembly
|
|||
}
|
||||
|
||||
override def toString: String = "Where(...)"
|
||||
|
||||
override def minimumRequiredLines: Int = 0
|
||||
}
|
||||
|
||||
case class Concatenation(l: AssemblyPattern, r: AssemblyPattern) extends AssemblyPattern {
|
||||
|
@ -380,6 +408,8 @@ case class Concatenation(l: AssemblyPattern, r: AssemblyPattern) extends Assembl
|
|||
case (_: Both, _) => s"($l) · $r"
|
||||
case _ => s"$l · $r"
|
||||
}
|
||||
|
||||
override val minimumRequiredLines: Int = l.minimumRequiredLines + r.minimumRequiredLines
|
||||
}
|
||||
|
||||
case class Many(rule: MLinePattern) extends AssemblyPattern {
|
||||
|
@ -405,6 +435,8 @@ case class Many(rule: MLinePattern) extends AssemblyPattern {
|
|||
}
|
||||
|
||||
override def toString: String = s"[$rule]*"
|
||||
|
||||
override def minimumRequiredLines: Int = 0
|
||||
}
|
||||
|
||||
case class ManyWhereAtLeastOne(rule: MLinePattern, atLeastOneIsThis: MLinePattern) extends AssemblyPattern {
|
||||
|
@ -439,6 +471,8 @@ case class ManyWhereAtLeastOne(rule: MLinePattern, atLeastOneIsThis: MLinePatter
|
|||
}
|
||||
|
||||
override def toString: String = s"[∃$atLeastOneIsThis:$rule]*"
|
||||
|
||||
override def minimumRequiredLines: Int = 1
|
||||
}
|
||||
|
||||
case class Opt(rule: MLinePattern) extends AssemblyPattern {
|
||||
|
@ -461,6 +495,8 @@ case class Opt(rule: MLinePattern) extends AssemblyPattern {
|
|||
}
|
||||
|
||||
override def toString: String = s"[$rule]?"
|
||||
|
||||
override def minimumRequiredLines: Int = 0
|
||||
}
|
||||
|
||||
trait MLinePattern extends AssemblyPattern {
|
||||
|
@ -488,6 +524,8 @@ trait MLinePattern extends AssemblyPattern {
|
|||
else Both(x, this)
|
||||
|
||||
def hitRate: Double
|
||||
|
||||
override def minimumRequiredLines: Int = 1
|
||||
}
|
||||
|
||||
//noinspection ScalaUnnecessaryParentheses
|
||||
|
@ -509,6 +547,8 @@ case class WhereNoMemoryAccessOverlapBetweenTwoLineLists(ix1: Int, ix2: Int) ext
|
|||
val s2s = ctx.get[List[MLine]](ix2)
|
||||
if (s1s.forall(s1 => s2s.forall(s2 => HelperCheckers.memoryAccessDoesntOverlap(s1, s2)))) Some(code) else None
|
||||
}
|
||||
|
||||
override def minimumRequiredLines: Int = 0
|
||||
}
|
||||
|
||||
case class MatchA(i: Int) extends MLinePattern {
|
||||
|
@ -754,6 +794,8 @@ case object DebugMatching extends AssemblyPattern {
|
|||
code.foreach(println)
|
||||
Some(code)
|
||||
}
|
||||
|
||||
override def minimumRequiredLines: Int = 0
|
||||
}
|
||||
|
||||
case object Linear extends MLinePattern {
|
||||
|
@ -1274,6 +1316,8 @@ case class MatchElidableCopyOf(i: Int, firstLinePattern: MLinePattern, lastLineP
|
|||
}
|
||||
Some(after)
|
||||
}
|
||||
|
||||
override def minimumRequiredLines: Int = 0
|
||||
}
|
||||
|
||||
case object IsNotALabelUsedManyTimes extends MLinePattern {
|
||||
|
|
|
@ -693,6 +693,8 @@ case class AssemblyLine(opcode: Opcode.Value, addrMode: AddrMode.Value, var para
|
|||
parameter match {
|
||||
case StructureConstant(_, List(a,b)) => s" $opcode $a,$b"
|
||||
}
|
||||
} else if (addrMode == LongRelative && opcode != BSR) { // BSR on Hudson is always 8-bit short, and on 65CE02 it's always 16-bit
|
||||
s" L$opcode ${AddrMode.addrModeToString(addrMode, parameter.toString)}"
|
||||
} else if (addrMode == ImmediateWithAbsolute || addrMode == ImmediateWithZeroPage) {
|
||||
parameter match {
|
||||
case StructureConstant(_, List(a,b)) => s" $opcode #$a,$b"
|
||||
|
|
|
@ -180,20 +180,31 @@ object Opcode extends Enumeration {
|
|||
case "AXA" => AHX
|
||||
case "AXS" => SBX // could mean SAX
|
||||
case "BCC" => BCC
|
||||
case "LBCC" => BCC
|
||||
case "BCS" => BCS
|
||||
case "LBCS" => BCS
|
||||
case "BEQ" => BEQ
|
||||
case "LBEQ" => BEQ
|
||||
case "BIT" => BIT
|
||||
case "BMI" => BMI
|
||||
case "LBMI" => BMI
|
||||
case "BNE" => BNE
|
||||
case "LBNE" => BNE
|
||||
case "BPL" => BPL
|
||||
case "LBPL" => BPL
|
||||
case "BRA" => BRA
|
||||
case "LBRA" => BRA
|
||||
case "BRK" => BRK
|
||||
case "BRL" => BRL
|
||||
case "BSR" => BSR
|
||||
case "LBSR" => BSR
|
||||
case "BVC" => BVC
|
||||
case "LBVC" => BVC
|
||||
case "BVS" => BVS
|
||||
case "LBVS" => BVS
|
||||
case "CLC" => CLC
|
||||
case "CLD" => CLD
|
||||
case "CLE" => CLE
|
||||
case "CLI" => CLI
|
||||
case "CLV" => CLV
|
||||
case "CLX" => CLX
|
||||
|
@ -232,6 +243,7 @@ object Opcode extends Enumeration {
|
|||
case "LSE" => SRE
|
||||
case "LSR" => LSR
|
||||
case "LXA" => LXA
|
||||
case "MAP" => MAP
|
||||
case "NEG" => NEG
|
||||
case "NOP" => NOP
|
||||
case "OAL" => LXA
|
||||
|
@ -247,17 +259,19 @@ object Opcode extends Enumeration {
|
|||
case "PHW" => PHW
|
||||
case "PHX" => PHX
|
||||
case "PHY" => PHY
|
||||
case "PHZ" => PHZ
|
||||
case "PLA" => PLA
|
||||
case "PLB" => PLB
|
||||
case "PLD" => PLD
|
||||
case "PLP" => PLP
|
||||
case "PLX" => PLX
|
||||
case "PLY" => PLY
|
||||
case "PLZ" => PLZ
|
||||
case "REP" => REP
|
||||
case "RLA" => RLA
|
||||
case "ROL" => ROL
|
||||
case "ROR" => ROR
|
||||
case "ROW" => ROR_W // TODO: is this correct?
|
||||
case "ROW" => ROL_W
|
||||
case "RRA" => RRA
|
||||
case "RTI" => RTI
|
||||
case "RTL" => RTL
|
||||
|
@ -268,6 +282,7 @@ object Opcode extends Enumeration {
|
|||
case "SBX" => SBX
|
||||
case "SEC" => SEC
|
||||
case "SED" => SED
|
||||
case "SEE" => SEE
|
||||
case "SEI" => SEI
|
||||
case "SEP" => SEP
|
||||
case "SET" => SET
|
||||
|
|
|
@ -995,16 +995,10 @@ object AlwaysGoodOptimizations {
|
|||
|
||||
val ConstantFlowAnalysis = new RuleBasedAssemblyOptimization("Constant flow analysis",
|
||||
needsFlowInfo = FlowInfoRequirement.ForwardFlow,
|
||||
(MatchX(0) & HasAddrMode(AbsoluteX) & SupportsAbsolute & Elidable & HasParameterWhere({
|
||||
case MemoryAddressConstant(th) => th.name == "identity$"
|
||||
case _ => false
|
||||
})) ~~> { (code, ctx) =>
|
||||
(MatchX(0) & HasAddrMode(AbsoluteX) & SupportsAbsolute & Not(HasOpcode(BIT)) & Elidable & HasIdentityPageParameter) ~~> { (code, ctx) =>
|
||||
code.map(l => l.copy(addrMode = Immediate, parameter = NumericConstant(ctx.get[Int](0), 1)))
|
||||
},
|
||||
(MatchY(0) & HasAddrMode(AbsoluteY) & SupportsAbsolute & Elidable & HasParameterWhere({
|
||||
case MemoryAddressConstant(th) => th.name == "identity$"
|
||||
case _ => false
|
||||
})) ~~> { (code, ctx) =>
|
||||
(MatchY(0) & HasAddrMode(AbsoluteY) & SupportsAbsolute & Elidable & HasIdentityPageParameter) ~~> { (code, ctx) =>
|
||||
code.map(l => l.copy(addrMode = Immediate, parameter = NumericConstant(ctx.get[Int](0), 1)))
|
||||
},
|
||||
(MatchY(0) & HasAddrMode(AbsoluteY) & SupportsAbsolute & Elidable) ~~> { (code, ctx) =>
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
package millfork.assembly.mos.opt
|
||||
|
||||
import millfork.assembly.mos.{AssemblyLine, AssemblyLine0, OpcodeClasses}
|
||||
import millfork.assembly.mos.{AddrMode, AssemblyLine, AssemblyLine0, OpcodeClasses}
|
||||
import millfork.assembly.{AssemblyOptimization, Elidability, OptimizationContext}
|
||||
import millfork.env.NormalFunction
|
||||
import millfork.error.Logger
|
||||
|
||||
import scala.annotation.tailrec
|
||||
import scala.util.control.TailCalls.{TailRec, done, tailcall}
|
||||
|
||||
/**
|
||||
|
@ -34,20 +35,22 @@ class ChangeIndexRegisterOptimization(preferX2Y: Boolean) extends AssemblyOptimi
|
|||
|
||||
override def name = "Changing index registers"
|
||||
|
||||
override def minimumRequiredLines: Int = 2
|
||||
|
||||
override def optimize(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[AssemblyLine] = {
|
||||
|
||||
val addressingModesUsingX = Set(AbsoluteX, ZeroPageX, IndexedX)
|
||||
val addressingModesUsingY = Set(AbsoluteY, ZeroPageY, IndexedY, LongIndexedY, IndexedSY)
|
||||
val usesX = code.exists(l =>
|
||||
OpcodeClasses.ReadsXAlways(l.opcode) ||
|
||||
OpcodeClasses.ReadsYAlways(l.opcode) ||
|
||||
OpcodeClasses.ChangesX(l.opcode) ||
|
||||
OpcodeClasses.ChangesY(l.opcode) ||
|
||||
Set(AbsoluteX, AbsoluteY, ZeroPageY, ZeroPageX, IndexedX, IndexedY, LongIndexedY, IndexedSY)(l.addrMode)
|
||||
addressingModesUsingX(l.addrMode)
|
||||
)
|
||||
val usesY = code.exists(l =>
|
||||
OpcodeClasses.ReadsXAlways(l.opcode) ||
|
||||
OpcodeClasses.ReadsYAlways(l.opcode) ||
|
||||
OpcodeClasses.ChangesX(l.opcode) ||
|
||||
val usesY = code.exists(l => {
|
||||
OpcodeClasses.ReadsYAlways(l.opcode) ||
|
||||
OpcodeClasses.ChangesY(l.opcode) ||
|
||||
Set(AbsoluteX, AbsoluteY, ZeroPageY, ZeroPageX, IndexedX, IndexedY, LongIndexedY, IndexedSY)(l.addrMode)
|
||||
addressingModesUsingY(l.addrMode)
|
||||
}
|
||||
)
|
||||
if (!usesX && !usesY) {
|
||||
return code
|
||||
|
@ -101,21 +104,25 @@ class ChangeIndexRegisterOptimization(preferX2Y: Boolean) extends AssemblyOptimi
|
|||
}
|
||||
|
||||
//noinspection OptionEqualsSome
|
||||
private def canOptimize(code: List[AssemblyLine], dir: IndexDirection, loaded: Option[IndexReg]): Boolean = code match {
|
||||
@tailrec
|
||||
private def canOptimize(code: List[AssemblyLine], dir: IndexDirection, loaded: Option[IndexReg]): Boolean = {
|
||||
val notX = loaded != Some(X)
|
||||
val notY = loaded != Some(Y)
|
||||
code match {
|
||||
|
||||
case AssemblyLine0(INC | DEC | ASL | ROL | ROR | LSR | STZ | LDZ | BIT, AbsoluteX | ZeroPageX, _) :: xs if dir == X2Y => false
|
||||
case AssemblyLine0(LDY | STY, AbsoluteX | ZeroPageX, _) :: xs => false
|
||||
case AssemblyLine0(LDX | STX, AbsoluteY | ZeroPageY, _) :: xs => false
|
||||
|
||||
case AssemblyLine0(_, AbsoluteY, _) :: xs if loaded != Some(Y) => false
|
||||
case AssemblyLine0(_, ZeroPageY, _) :: xs if loaded != Some(Y) => false
|
||||
case AssemblyLine0(_, IndexedY, _) :: xs if dir == Y2X || loaded != Some(Y) => false
|
||||
case AssemblyLine0(_, LongIndexedY, _) :: xs if dir == Y2X || loaded != Some(Y) => false
|
||||
case AssemblyLine0(_, IndexedSY, _) :: xs if dir == Y2X || loaded != Some(Y) => false
|
||||
case AssemblyLine0(_, AbsoluteX, _) :: xs if loaded != Some(X) => false
|
||||
case AssemblyLine0(_, LongAbsoluteX, _) :: xs if loaded != Some(X) => false
|
||||
case AssemblyLine0(_, ZeroPageX, _) :: xs if loaded != Some(X) => false
|
||||
case AssemblyLine0(_, IndexedX, _) :: xs if dir == X2Y || loaded != Some(X) => false
|
||||
case AssemblyLine0(_, AbsoluteY, _) :: xs if notY => false
|
||||
case AssemblyLine0(_, ZeroPageY, _) :: xs if notY => false
|
||||
case AssemblyLine0(_, IndexedY, _) :: xs if dir == Y2X || notY => false
|
||||
case AssemblyLine0(_, LongIndexedY, _) :: xs if dir == Y2X || notY => false
|
||||
case AssemblyLine0(_, IndexedSY, _) :: xs if dir == Y2X || notY => false
|
||||
case AssemblyLine0(_, AbsoluteX, _) :: xs if notX => false
|
||||
case AssemblyLine0(_, LongAbsoluteX, _) :: xs if notX => false
|
||||
case AssemblyLine0(_, ZeroPageX, _) :: xs if notX => false
|
||||
case AssemblyLine0(_, IndexedX | ImmediateWithAbsoluteX | ImmediateWithZeroPageX, _) :: xs if dir == X2Y || notX => false
|
||||
case AssemblyLine0(_, AbsoluteIndexedX, _) :: xs if dir == X2Y => false
|
||||
case AssemblyLine0(SHX | SHY | AHX | TAS | LAS, _, _) :: xs => false
|
||||
case AssemblyLine(TXY, _, _, e, _) :: xs => (e == Elidability.Elidable || e == Elidability.Volatile) && loaded == Some(X) && canOptimize(xs, dir, Some(Y))
|
||||
|
@ -155,11 +162,12 @@ class ChangeIndexRegisterOptimization(preferX2Y: Boolean) extends AssemblyOptimi
|
|||
(e == Elidability.Elidable || e == Elidability.Volatile || dir == X2Y) && loaded == Some(Y) && canOptimize(xs, dir, Some(Y))
|
||||
|
||||
case AssemblyLine0(SAX | TXS | SBX, _, _) :: xs => dir == Y2X && loaded == Some(X) && canOptimize(xs, dir, Some(X))
|
||||
case AssemblyLine0(TSX, _, _) :: xs => dir == Y2X && loaded != Some(Y) && canOptimize(xs, dir, Some(X))
|
||||
case AssemblyLine0(TSX, _, _) :: xs => dir == Y2X && notY && canOptimize(xs, dir, Some(X))
|
||||
|
||||
case _ :: xs => canOptimize(xs, dir, loaded)
|
||||
|
||||
case Nil => true
|
||||
}
|
||||
}
|
||||
|
||||
private def switchX2Y(code: List[AssemblyLine])(implicit log: Logger): TailRec[List[AssemblyLine]] = code match {
|
||||
|
|
|
@ -56,7 +56,8 @@ object CoarseFlowAnalyzer {
|
|||
changed = false
|
||||
var currentStatus: CpuStatus = functionStartStatus
|
||||
var staSpWasLast = false
|
||||
for (i <- codeArray.indices) {
|
||||
var i = 0
|
||||
while (i < codeArray.length) {
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.assembly.mos.AddrMode._
|
||||
import millfork.node.MosNiceFunctionProperty._
|
||||
|
@ -184,6 +185,7 @@ object CoarseFlowAnalyzer {
|
|||
}
|
||||
}
|
||||
tFlag = codeArray(i).opcode == SET
|
||||
i += 1;
|
||||
}
|
||||
// flagArray.zip(codeArray).foreach{
|
||||
// case (fl, y) => if (y.isPrintable) println(f"$fl%-32s $y%-32s")
|
||||
|
|
|
@ -160,11 +160,6 @@ case class CpuStatus(a: Status[Int] = UnknownStatus,
|
|||
}
|
||||
|
||||
def hasSet(state: State.Value): Boolean = state match {
|
||||
case State.A => false
|
||||
case State.AH => false
|
||||
case State.X => false
|
||||
case State.Y => false
|
||||
case State.IZ => false
|
||||
case State.Z => z.contains(true)
|
||||
case State.N => n.contains(true)
|
||||
case State.C => c.contains(true)
|
||||
|
|
|
@ -15,42 +15,42 @@ object DangerousOptimizations {
|
|||
// TODO: try to guess when overflow can happen
|
||||
needsFlowInfo = FlowInfoRequirement.BothFlows,
|
||||
(Elidable & HasOpcode(CLC)).? ~
|
||||
(Elidable & HasClear(State.C) & HasOpcode(ADC) & MatchImmediate(0) & DoesntMatterWhatItDoesWith(State.V, State.C)) ~
|
||||
(Elidable & HasClear(State.C) & HasClear(State.D) & HasOpcode(ADC) & MatchImmediate(0) & DoesntMatterWhatItDoesWith(State.V, State.C)) ~
|
||||
(
|
||||
(HasOpcode(TAY) & DoesntMatterWhatItDoesWith(State.N, State.Z, State.A)) ~
|
||||
(Linear & Not(ConcernsY)).*
|
||||
).capture(1) ~
|
||||
(Elidable & HasAddrMode(AbsoluteY) & DoesntMatterWhatItDoesWith(State.Y)) ~~> { (code, ctx) =>
|
||||
(Elidable & HasAddrMode(AbsoluteY) & DoesntMatterWhatItDoesWith(State.Y) & HasSmallParameter) ~~> { (code, ctx) =>
|
||||
val last = code.last
|
||||
ctx.get[List[AssemblyLine]](1) :+ last.copy(parameter = last.parameter.+(ctx.get[Constant](0)).quickSimplify)
|
||||
},
|
||||
(Elidable & HasOpcode(CLC)).? ~
|
||||
(Elidable & HasClear(State.C) & HasOpcode(ADC) & MatchImmediate(0) & DoesntMatterWhatItDoesWith(State.V, State.C)) ~
|
||||
(Elidable & HasClear(State.C) & HasClear(State.D) & HasOpcode(ADC) & MatchImmediate(0) & DoesntMatterWhatItDoesWith(State.V, State.C)) ~
|
||||
(
|
||||
(HasOpcode(TAX) & DoesntMatterWhatItDoesWith(State.N, State.Z, State.A)) ~
|
||||
(Linear & Not(ConcernsX)).*
|
||||
).capture(1) ~
|
||||
(Elidable & HasAddrMode(AbsoluteX) & DoesntMatterWhatItDoesWith(State.X)) ~~> { (code, ctx) =>
|
||||
(Elidable & HasAddrMode(AbsoluteX) & DoesntMatterWhatItDoesWith(State.X) & HasSmallParameter) ~~> { (code, ctx) =>
|
||||
val last = code.last
|
||||
ctx.get[List[AssemblyLine]](1) :+ last.copy(parameter = last.parameter.+(ctx.get[Constant](0)).quickSimplify)
|
||||
},
|
||||
(Elidable & HasOpcode(INY) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~
|
||||
(Elidable & HasAddrMode(AbsoluteY) & DoesntMatterWhatItDoesWith(State.Y)) ~~> { (code, ctx) =>
|
||||
(Elidable & HasAddrMode(AbsoluteY) & DoesntMatterWhatItDoesWith(State.Y) & HasSmallParameter) ~~> { (code, ctx) =>
|
||||
val last = code.last
|
||||
List(last.copy(parameter = last.parameter.+(1).quickSimplify))
|
||||
},
|
||||
(Elidable & HasOpcode(DEY) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~
|
||||
(Elidable & HasAddrMode(AbsoluteY) & DoesntMatterWhatItDoesWith(State.Y)) ~~> { (code, ctx) =>
|
||||
(Elidable & HasAddrMode(AbsoluteY) & DoesntMatterWhatItDoesWith(State.Y) & HasSmallParameter) ~~> { (code, ctx) =>
|
||||
val last = code.last
|
||||
List(last.copy(parameter = last.parameter.+(-1).quickSimplify))
|
||||
},
|
||||
(Elidable & HasOpcode(INX) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~
|
||||
(Elidable & HasAddrMode(AbsoluteX) & DoesntMatterWhatItDoesWith(State.X)) ~~> { (code, ctx) =>
|
||||
(Elidable & HasAddrMode(AbsoluteX) & DoesntMatterWhatItDoesWith(State.X) & HasSmallParameter) ~~> { (code, ctx) =>
|
||||
val last = code.last
|
||||
List(last.copy(parameter = last.parameter.+(1).quickSimplify))
|
||||
},
|
||||
(Elidable & HasOpcode(DEX) & DoesntMatterWhatItDoesWith(State.N, State.Z)) ~
|
||||
(Elidable & HasAddrMode(AbsoluteX) & DoesntMatterWhatItDoesWith(State.X)) ~~> { (code, ctx) =>
|
||||
(Elidable & HasAddrMode(AbsoluteX) & DoesntMatterWhatItDoesWith(State.X) & HasSmallParameter) ~~> { (code, ctx) =>
|
||||
val last = code.last
|
||||
List(last.copy(parameter = last.parameter.+(-1).quickSimplify))
|
||||
},
|
||||
|
|
|
@ -16,6 +16,8 @@ import scala.collection.{immutable, mutable}
|
|||
object EmptyMemoryStoreRemoval extends AssemblyOptimization[AssemblyLine] {
|
||||
override def name = "Removing pointless stores to automatic variables"
|
||||
|
||||
override def minimumRequiredLines: Int = 2
|
||||
|
||||
private val storeAddrModes = Set(Absolute, ZeroPage, AbsoluteX, AbsoluteY, ZeroPageX, ZeroPageY)
|
||||
private val directStorageOpcodes = Set(
|
||||
STA, STX, STY, SAX, STZ, SHX, SHY, AHX,
|
||||
|
|
|
@ -8,12 +8,16 @@ import millfork.assembly.{AssemblyOptimization, Elidability, OptimizationContext
|
|||
import millfork.env._
|
||||
import millfork.error.ConsoleLogger
|
||||
|
||||
import scala.collection.mutable
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object EmptyParameterStoreRemoval extends AssemblyOptimization[AssemblyLine] {
|
||||
override def name = "Removing pointless stores to foreign variables"
|
||||
|
||||
override def minimumRequiredLines: Int = 2
|
||||
|
||||
private val storeInstructions = Set(STA, STX, STY, SAX, STZ, STA_W, STX_W, STY_W, STZ_W)
|
||||
private val storeAddrModes = Set(Absolute, ZeroPage, AbsoluteX, AbsoluteY, ZeroPageX, ZeroPageY, LongAbsolute, LongAbsoluteX)
|
||||
|
||||
|
@ -23,48 +27,52 @@ object EmptyParameterStoreRemoval extends AssemblyOptimization[AssemblyLine] {
|
|||
case AssemblyLine0(JSR | BSR | JMP, _, NumericConstant(addr, _)) => Some("$" + addr.toHexString)
|
||||
case _ => None
|
||||
}.toSet
|
||||
val foreignVariables = f.environment.root.things.values.flatMap {
|
||||
val foreignVariables = mutable.Set[String]()
|
||||
f.environment.root.things.values.foreach {
|
||||
case other: NormalFunction if !other.name.endsWith(".trampoline") =>
|
||||
val address = other.address match {
|
||||
case Some(NumericConstant(addr, _)) => "$" + addr.toHexString
|
||||
case _ => ""
|
||||
}
|
||||
if (other.name == f.name || usedFunctions(other.name) || usedFunctions(address)) {
|
||||
Nil
|
||||
// do nothing
|
||||
} else {
|
||||
val params = other.params match {
|
||||
case NormalParamSignature(ps) => ps.map(_.name)
|
||||
case _ => Nil
|
||||
other.params match {
|
||||
case NormalParamSignature(ps) =>
|
||||
ps.foreach(p => foreignVariables += p.name)
|
||||
case _ =>
|
||||
}
|
||||
val locals = other.environment.things.values.flatMap{
|
||||
case th: MemoryVariable if th.alloc == VariableAllocationMethod.Auto => Some(th.name)
|
||||
case th: MemoryVariable if th.alloc == VariableAllocationMethod.Zeropage => Some(th.name) // TODO: ???
|
||||
case _ => None
|
||||
other.environment.things.values.foreach {
|
||||
case th: MemoryVariable if th.alloc == VariableAllocationMethod.Auto =>
|
||||
foreignVariables += th.name
|
||||
case th: MemoryVariable if th.alloc == VariableAllocationMethod.Zeropage =>
|
||||
foreignVariables += th.name // TODO: ???
|
||||
case _ =>
|
||||
}
|
||||
if (other.returnType.size > Cpu.getMaxSizeReturnableViaRegisters(optimizationContext.options.platform.cpu, optimizationContext.options)) {
|
||||
other.name + ".return" :: (params ++ locals)
|
||||
} else {
|
||||
params ++ locals
|
||||
foreignVariables += other.name + ".return"
|
||||
}
|
||||
}
|
||||
case _ => Nil
|
||||
}.toSet
|
||||
val stillReadOrStoredVariables = code.flatMap {
|
||||
case AssemblyLine0(_, _, MemoryAddressConstant(th)) => Some(th.name)
|
||||
case AssemblyLine0(_, _, CompoundConstant(_, MemoryAddressConstant(th), _)) => Some(th.name)
|
||||
case AssemblyLine0(_, Immediate, SubbyteConstant(MemoryAddressConstant(th), _)) => Some(th.name)
|
||||
case _ => None
|
||||
}.toSet
|
||||
val stillReadVariables = code.flatMap {
|
||||
case _ =>
|
||||
}
|
||||
val stillReadOrStoredVariables = mutable.Set[String]()
|
||||
code.foreach {
|
||||
case AssemblyLine0(_, _, MemoryAddressConstant(th)) => stillReadOrStoredVariables += th.name
|
||||
case AssemblyLine0(_, _, CompoundConstant(_, MemoryAddressConstant(th), _)) => stillReadOrStoredVariables += th.name
|
||||
case AssemblyLine0(_, Immediate, SubbyteConstant(MemoryAddressConstant(th), _)) => stillReadOrStoredVariables += th.name
|
||||
case _ =>
|
||||
}
|
||||
val stillReadVariables = mutable.Set[String]()
|
||||
code.foreach {
|
||||
case AssemblyLine(op, am, MemoryAddressConstant(th), Elidability.Elidable, _)
|
||||
if storeInstructions(op) && storeAddrModes(am) => Nil
|
||||
if storeInstructions(op) && storeAddrModes(am) =>
|
||||
case AssemblyLine(op, am, CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th), NumericConstant(_, _)), Elidability.Elidable, _)
|
||||
if storeInstructions(op) && storeAddrModes(am) => Nil
|
||||
case AssemblyLine0(_, _, MemoryAddressConstant(th)) => Some(th.name)
|
||||
case AssemblyLine0(_, _, CompoundConstant(_, MemoryAddressConstant(th), _)) => Some(th.name)
|
||||
case AssemblyLine0(_, Immediate, SubbyteConstant(MemoryAddressConstant(th), _)) => Some(th.name)
|
||||
case _ => None
|
||||
}.toSet
|
||||
if storeInstructions(op) && storeAddrModes(am) =>
|
||||
case AssemblyLine0(_, _, MemoryAddressConstant(th)) => stillReadVariables += th.name
|
||||
case AssemblyLine0(_, _, CompoundConstant(_, MemoryAddressConstant(th), _)) => stillReadVariables += th.name
|
||||
case AssemblyLine0(_, Immediate, SubbyteConstant(MemoryAddressConstant(th), _)) => stillReadVariables += th.name
|
||||
case _ =>
|
||||
}
|
||||
|
||||
val unusedForeignVariables = (foreignVariables & stillReadOrStoredVariables) -- stillReadVariables
|
||||
if (unusedForeignVariables.isEmpty) {
|
||||
|
|
|
@ -21,12 +21,16 @@ case class FlowInfo(holder: FlowHolder, index: Int, _labelUseCountMap: () => Opt
|
|||
lazy val importanceAfter: CpuImportance = holder.importanceAfter(index)
|
||||
lazy val labelUseCountMap: Option[Map[String, Int]] = _labelUseCountMap()
|
||||
|
||||
@inline
|
||||
def hasClear(state: State.Value): Boolean = statusBefore.hasClear(state)
|
||||
|
||||
@inline
|
||||
def hasSet(state: State.Value): Boolean = statusBefore.hasSet(state)
|
||||
|
||||
@inline
|
||||
def isUnimportant(state: State.Value): Boolean = importanceAfter.isUnimportant(state)
|
||||
|
||||
@inline
|
||||
def labelUseCount(label: String): Int = labelUseCountMap.map(_.getOrElse(label, 0)).getOrElse(-1)
|
||||
|
||||
override def toString: String = holder.toString(index)
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
package millfork.assembly.mos.opt
|
||||
|
||||
import millfork.assembly.mos.AddrMode._
|
||||
import millfork.assembly.mos.{AssemblyLine, State}
|
||||
import millfork.assembly.mos.Opcode._
|
||||
import millfork.env.NumericConstant
|
||||
|
||||
object IdentityPageOptimizations {
|
||||
|
||||
val SimplifiableAccess = new RuleBasedAssemblyOptimization("Simplifiable identity page access",
|
||||
needsFlowInfo = FlowInfoRequirement.NoRequirement,
|
||||
(Elidable & HasOpcode(LDA) & HasAddrMode(AbsoluteX) & HasIdentityPageParameter) ~~> { c =>
|
||||
List(AssemblyLine.implied(TXA))
|
||||
},
|
||||
(Elidable & HasOpcode(LDA) & HasAddrMode(AbsoluteY) & HasIdentityPageParameter) ~~> { c =>
|
||||
List(AssemblyLine.implied(TYA))
|
||||
},
|
||||
)
|
||||
|
||||
val UseInsteadOfStack = new RuleBasedAssemblyOptimization("Use identity page instead of stack",
|
||||
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
||||
(Elidable & HasOpcode(PHA)) ~
|
||||
(Not(ConcernsX) & Not(ConcernsStack)).*.capture(0) ~
|
||||
(Elidable & HasOpcode(TSX) & HasAddrMode(AbsoluteX) & HasIdentityPageParameter) ~
|
||||
(Elidable & HasAddrMode(AbsoluteX) & HasParameterWhere{
|
||||
case NumericConstant(0x101, _) => true
|
||||
case _ => false
|
||||
}).capture(1) ~
|
||||
(Elidable & HasOpcode(INX)) ~
|
||||
(Elidable & HasOpcode(TXS) & DoesntMatterWhatItDoesWith(State.X)) ~~> { (code, ctx) =>
|
||||
List(AssemblyLine.implied(TAX)) ++ ctx.get[List[AssemblyLine]](0) ++ List(ctx.get[AssemblyLine](1).copy(parameter = ctx.identityPage))
|
||||
},
|
||||
(Elidable & HasOpcode(PHA)) ~
|
||||
(Not(ConcernsY) & Not(ConcernsStack)).*.capture(0) ~
|
||||
(Elidable & HasOpcode(TSX) & HasAddrMode(AbsoluteX) & HasIdentityPageParameter) ~
|
||||
(Elidable & HasAddrMode(AbsoluteX) & HasParameterWhere{
|
||||
case NumericConstant(0x101, _) => true
|
||||
case _ => false
|
||||
}).capture(1) ~
|
||||
(Elidable & HasOpcode(INX)) ~
|
||||
(Elidable & HasOpcode(TXS) & DoesntMatterWhatItDoesWith(State.Y)) ~~> { (code, ctx) =>
|
||||
List(AssemblyLine.implied(TAY)) ++ ctx.get[List[AssemblyLine]](0) ++ List(ctx.get[AssemblyLine](1).copy(parameter = ctx.identityPage, addrMode = AbsoluteY))
|
||||
},
|
||||
)
|
||||
|
||||
val All: List[RuleBasedAssemblyOptimization] = List(
|
||||
SimplifiableAccess,
|
||||
UseInsteadOfStack)
|
||||
|
||||
}
|
|
@ -145,8 +145,8 @@ object LaterOptimizations {
|
|||
//noinspection ZeroIndexToHead
|
||||
private def InterleavedLoads(load: Opcode.Value, store: Opcode.Value) = {
|
||||
(Elidable & HasOpcode(load) & MatchAddrMode(0) & MatchParameter(1)).capture(12) ~
|
||||
(Elidable & HasOpcode(store)).+.capture(10) ~
|
||||
(Elidable & HasOpcode(load) & MatchAddrMode(2) & MatchParameter(3) & DoesNotConcernMemoryAt(0, 1)).capture(13) ~
|
||||
(Elidable & HasOpcode(store) & MatchAddrMode(20) & MatchParameter(21)).+.capture(10) ~
|
||||
(Elidable & HasOpcode(load) & MatchAddrMode(2) & MatchParameter(3) & DoesNotConcernMemoryAt(0, 1) & DoesNotConcernMemoryAt(20, 21)).capture(13) ~
|
||||
(Elidable & HasOpcode(store) & DoesNotConcernMemoryAt(0, 1) & DoesNotConcernMemoryAt(2, 3)).+.capture(11) ~
|
||||
(Elidable & HasOpcode(load) & MatchAddrMode(0) & MatchParameter(1)) ~
|
||||
WhereNoMemoryAccessOverlapBetweenTwoLineLists(10, 11) ~~> { (_, ctx) =>
|
||||
|
|
|
@ -17,6 +17,8 @@ object LocalVariableReadOptimization extends AssemblyOptimization[AssemblyLine]
|
|||
|
||||
override def name: String = "Local variable read optimization"
|
||||
|
||||
override def minimumRequiredLines: Int = 2
|
||||
|
||||
override def optimize(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[AssemblyLine] = {
|
||||
|
||||
val stillUsedVariables = code.flatMap {
|
||||
|
|
|
@ -18,6 +18,8 @@ import scala.util.control.TailCalls.tailcall
|
|||
case class RepeatedIndexCalculationOptimization(forX: Boolean) extends AssemblyOptimization[AssemblyLine] {
|
||||
override def name: String = "Repeated index calculation into " + (if (forX) "X" else "Y")
|
||||
|
||||
override def minimumRequiredLines: Int = 4
|
||||
|
||||
override def optimize(f: NormalFunction, code: List[AssemblyLine], context: OptimizationContext): List[AssemblyLine] = {
|
||||
val log = context.log
|
||||
val allRuns = findAllRuns(code, 0, None).result
|
||||
|
|
|
@ -30,43 +30,54 @@ object FlowInfoRequirement extends Enumeration {
|
|||
}
|
||||
|
||||
def assertLabels(x: FlowInfoRequirement.Value): Unit = x match {
|
||||
case NoRequirement => FatalErrorReporting.reportFlyingPig("Backward flow info required")
|
||||
case NoRequirement => FatalErrorReporting.reportFlyingPig("Label info required")
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
trait AssemblyRuleSet{
|
||||
def flatten: Seq[AssemblyRule]
|
||||
|
||||
def minimumRequiredLines: Int
|
||||
}
|
||||
|
||||
class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInfoRequirement.Value, val rules: AssemblyRuleSet*) extends AssemblyOptimization[AssemblyLine] {
|
||||
|
||||
private val actualRules = rules.flatMap(_.flatten)
|
||||
actualRules.foreach(_.pattern.validate(needsFlowInfo))
|
||||
private val actualRulesWithIndex = actualRules.zipWithIndex
|
||||
|
||||
override val minimumRequiredLines: Int = rules.map(_.minimumRequiredLines).min
|
||||
|
||||
override def toString: String = name
|
||||
|
||||
override def optimize(f: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[AssemblyLine] = {
|
||||
val taggedCode = FlowAnalyzer.analyze(f, code, optimizationContext, needsFlowInfo)
|
||||
val (changed, optimized) = optimizeImpl(f, taggedCode, optimizationContext)
|
||||
if (changed) optimized else code
|
||||
optimizeImpl(f, code, taggedCode, optimizationContext)
|
||||
}
|
||||
|
||||
def optimizeImpl(f: NormalFunction, code: List[(FlowInfo, AssemblyLine)], optimizationContext: OptimizationContext): (Boolean, List[AssemblyLine]) = {
|
||||
final def optimizeImpl(f: NormalFunction, code: List[AssemblyLine], taggedCode: List[(FlowInfo, AssemblyLine)], optimizationContext: OptimizationContext): List[AssemblyLine] = {
|
||||
val log = optimizationContext.log
|
||||
code match {
|
||||
case Nil => false -> Nil
|
||||
taggedCode match {
|
||||
case Nil => code
|
||||
case head :: tail =>
|
||||
for ((rule, index) <- actualRules.zipWithIndex) {
|
||||
val codeLength = code.length
|
||||
for {
|
||||
(rule, index) <- actualRulesWithIndex
|
||||
if codeLength >= rule.minimumRequiredLines
|
||||
} {
|
||||
val ctx = new AssemblyMatchingContext(
|
||||
optimizationContext.options,
|
||||
optimizationContext.labelMap,
|
||||
optimizationContext.zreg,
|
||||
optimizationContext.identityPage,
|
||||
optimizationContext.niceFunctionProperties,
|
||||
head._1.labelUseCount(_)
|
||||
)
|
||||
rule.pattern.matchTo(ctx, code) match {
|
||||
rule.pattern.matchTo(ctx, taggedCode) match {
|
||||
case Some(rest: List[(FlowInfo, AssemblyLine)]) =>
|
||||
val matchedChunkToOptimize: List[AssemblyLine] = code.take(code.length - rest.length).map(_._2)
|
||||
val optimizedChunkLengthBefore = taggedCode.length - rest.length
|
||||
val (matchedChunkToOptimize, restOfCode) = code.splitAt(optimizedChunkLengthBefore)
|
||||
val optimizedChunk: List[AssemblyLine] = rule.result(matchedChunkToOptimize, ctx)
|
||||
val optimizedChunkWithSource =
|
||||
if (!ctx.compilationOptions.flag(CompilationFlag.LineNumbersInAssembly)) optimizedChunk
|
||||
|
@ -75,35 +86,42 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf
|
|||
else if (optimizedChunk.size == 1) optimizedChunk.map(_.pos(SourceLine.merge(matchedChunkToOptimize.map(_.source))))
|
||||
else if (matchedChunkToOptimize.flatMap(_.source).toSet.size == 1) optimizedChunk.map(_.pos(SourceLine.merge(matchedChunkToOptimize.map(_.source))))
|
||||
else optimizedChunk
|
||||
log.debug(s"Applied $name ($index)")
|
||||
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) {
|
||||
val before = code.head._1.statusBefore
|
||||
val after = code(matchedChunkToOptimize.length - 1)._1.importanceAfter
|
||||
log.trace(s"Before: $before")
|
||||
log.trace(s"After: $after")
|
||||
if (log.debugEnabled) {
|
||||
log.debug(s"Applied $name ($index)")
|
||||
}
|
||||
if (log.traceEnabled) {
|
||||
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) {
|
||||
val before = head._1.statusBefore
|
||||
val after = taggedCode(matchedChunkToOptimize.length - 1)._1.importanceAfter
|
||||
log.trace(s"Before: $before")
|
||||
log.trace(s"After: $after")
|
||||
}
|
||||
matchedChunkToOptimize.filter(_.isPrintable).foreach(l => log.trace(l.toString))
|
||||
log.trace(" ↓")
|
||||
optimizedChunkWithSource.filter(_.isPrintable).foreach(l => log.trace(l.toString))
|
||||
}
|
||||
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) {
|
||||
return true -> (optimizedChunkWithSource ++ optimizeImpl(f, rest, optimizationContext)._2)
|
||||
return optimizedChunkWithSource ++ optimizeImpl(f, restOfCode, rest, optimizationContext)
|
||||
} else {
|
||||
return true -> optimize(f, optimizedChunkWithSource ++ rest.map(_._2), optimizationContext)
|
||||
return optimize(f, optimizedChunkWithSource ++ restOfCode, optimizationContext)
|
||||
}
|
||||
case None => ()
|
||||
}
|
||||
}
|
||||
val (changedTail, optimizedTail) = optimizeImpl(f, tail, optimizationContext)
|
||||
(changedTail, head._2 :: optimizedTail)
|
||||
val optimizedTail = optimizeImpl(f, code.tail, tail, optimizationContext)
|
||||
if (optimizedTail eq code.tail) {
|
||||
code
|
||||
} else {
|
||||
code.head :: optimizedTail
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AssemblyMatchingContext(val compilationOptions: CompilationOptions,
|
||||
val labelMap: Map[String, (Int, Int)],
|
||||
val labelMap: Map[String, (String, Int)],
|
||||
val zeropageRegister: Option[ThingInMemory],
|
||||
val identityPage: Constant,
|
||||
val niceFunctionProperties: Set[(NiceFunctionProperty, String)],
|
||||
val labelUseCount: String => Int) {
|
||||
@inline
|
||||
|
@ -131,7 +149,17 @@ class AssemblyMatchingContext(val compilationOptions: CompilationOptions,
|
|||
|
||||
def functionReadsMemory(name: String): Boolean = !niceFunctionProperties(NiceFunctionProperty.DoesntReadMemory -> name)
|
||||
|
||||
private val map = new mutable.HashMap[Int, Any]()
|
||||
private var m_map: mutable.HashMap[Int, Any] = _
|
||||
|
||||
@inline
|
||||
private def map = {
|
||||
var m = m_map
|
||||
if (m eq null) {
|
||||
m = new mutable.HashMap[Int, Any]()
|
||||
m_map = m
|
||||
}
|
||||
m
|
||||
}
|
||||
|
||||
override def toString: String = if (map.isEmpty) "<empty context>" else map.mkString(", ")
|
||||
|
||||
|
@ -235,10 +263,14 @@ class AssemblyMatchingContext(val compilationOptions: CompilationOptions,
|
|||
|
||||
case class AssemblyRule(pattern: AssemblyPattern, result: (List[AssemblyLine], AssemblyMatchingContext) => List[AssemblyLine]) extends AssemblyRuleSet {
|
||||
override def flatten: Seq[AssemblyRule] = List(this)
|
||||
|
||||
override def minimumRequiredLines: Int = pattern.minimumRequiredLines
|
||||
}
|
||||
|
||||
case class MultipleAssemblyRules(list: Seq[AssemblyRuleSet]) extends AssemblyRuleSet {
|
||||
override def flatten: Seq[AssemblyRule] = list.flatMap(_.flatten)
|
||||
|
||||
override val minimumRequiredLines: Int = flatten.map(_.minimumRequiredLines).min
|
||||
}
|
||||
|
||||
trait AssemblyPattern {
|
||||
|
@ -259,6 +291,8 @@ trait AssemblyPattern {
|
|||
|
||||
def captureLength(i: Int) = CaptureLength(i, this)
|
||||
|
||||
def minimumRequiredLines: Int
|
||||
|
||||
}
|
||||
object HelperCheckers {
|
||||
import AddrMode._
|
||||
|
@ -367,6 +401,8 @@ case class Capture(i: Int, pattern: AssemblyPattern) extends AssemblyPattern {
|
|||
}
|
||||
|
||||
override def toString: String = s"(?<$i>$pattern)"
|
||||
|
||||
override def minimumRequiredLines: Int = pattern.minimumRequiredLines
|
||||
}
|
||||
|
||||
case class CaptureLength(i: Int, pattern: AssemblyPattern) extends AssemblyPattern {
|
||||
|
@ -379,6 +415,8 @@ case class CaptureLength(i: Int, pattern: AssemblyPattern) extends AssemblyPatte
|
|||
}
|
||||
|
||||
override def toString: String = s"(?<$i>$pattern)"
|
||||
|
||||
override def minimumRequiredLines: Int = pattern.minimumRequiredLines
|
||||
}
|
||||
|
||||
|
||||
|
@ -388,6 +426,8 @@ case class Where(predicate: AssemblyMatchingContext => Boolean) extends Assembly
|
|||
}
|
||||
|
||||
override def toString: String = "Where(...)"
|
||||
|
||||
override def minimumRequiredLines: Int = 0
|
||||
}
|
||||
|
||||
case class Concatenation(l: AssemblyPattern, r: AssemblyPattern) extends AssemblyPattern {
|
||||
|
@ -410,6 +450,8 @@ case class Concatenation(l: AssemblyPattern, r: AssemblyPattern) extends Assembl
|
|||
case (_: Both, _) => s"($l) · $r"
|
||||
case _ => s"$l · $r"
|
||||
}
|
||||
|
||||
override val minimumRequiredLines: Int = l.minimumRequiredLines + r.minimumRequiredLines
|
||||
}
|
||||
|
||||
case class Many(rule: AssemblyLinePattern) extends AssemblyPattern {
|
||||
|
@ -435,6 +477,8 @@ case class Many(rule: AssemblyLinePattern) extends AssemblyPattern {
|
|||
}
|
||||
|
||||
override def toString: String = s"[$rule]*"
|
||||
|
||||
override def minimumRequiredLines: Int = 0
|
||||
}
|
||||
|
||||
case class ManyWhereAtLeastOne(rule: AssemblyLinePattern, atLeastOneIsThis: AssemblyLinePattern) extends AssemblyPattern {
|
||||
|
@ -469,6 +513,8 @@ case class ManyWhereAtLeastOne(rule: AssemblyLinePattern, atLeastOneIsThis: Asse
|
|||
}
|
||||
|
||||
override def toString: String = s"[∃$atLeastOneIsThis:$rule]*"
|
||||
|
||||
override def minimumRequiredLines: Int = rule.minimumRequiredLines
|
||||
}
|
||||
|
||||
case class Opt(rule: AssemblyLinePattern) extends AssemblyPattern {
|
||||
|
@ -491,6 +537,8 @@ case class Opt(rule: AssemblyLinePattern) extends AssemblyPattern {
|
|||
}
|
||||
|
||||
override def toString: String = s"[$rule]?"
|
||||
|
||||
override def minimumRequiredLines: Int = 0
|
||||
}
|
||||
|
||||
trait AssemblyLinePattern extends AssemblyPattern {
|
||||
|
@ -518,6 +566,8 @@ trait AssemblyLinePattern extends AssemblyPattern {
|
|||
else Both(x, this)
|
||||
|
||||
def hitRate: Double
|
||||
|
||||
override def minimumRequiredLines: Int = 1
|
||||
}
|
||||
|
||||
//noinspection ScalaUnnecessaryParentheses
|
||||
|
@ -531,6 +581,8 @@ case class Match(predicate: AssemblyMatchingContext => Boolean) extends Assembly
|
|||
override def toString: String = "Match(...)"
|
||||
|
||||
override def hitRate: Double = 0.5 // ?
|
||||
|
||||
override def minimumRequiredLines: Int = 0
|
||||
}
|
||||
|
||||
case class WhereNoMemoryAccessOverlapBetweenTwoLineLists(ix1: Int, ix2: Int) extends AssemblyPattern {
|
||||
|
@ -539,6 +591,8 @@ case class WhereNoMemoryAccessOverlapBetweenTwoLineLists(ix1: Int, ix2: Int) ext
|
|||
val s2s = ctx.get[List[AssemblyLine]](ix2)
|
||||
if (s1s.forall(s1 => s2s.forall(s2 => HelperCheckers.memoryAccessDoesntOverlap(s1, s2)))) Some(code) else None
|
||||
}
|
||||
|
||||
override def minimumRequiredLines: Int = 0
|
||||
}
|
||||
|
||||
case class MatchA(i: Int) extends AssemblyLinePattern {
|
||||
|
@ -859,6 +913,8 @@ case object DebugMatching extends AssemblyPattern {
|
|||
code.foreach(println)
|
||||
Some(code)
|
||||
}
|
||||
|
||||
override def minimumRequiredLines: Int = 0
|
||||
}
|
||||
|
||||
case object Linear extends AssemblyLinePattern {
|
||||
|
@ -1459,6 +1515,27 @@ case class HasParameterWhere(predicate: Constant => Boolean) extends TrivialAsse
|
|||
override def hitRate: Double = 0.332
|
||||
}
|
||||
|
||||
case object HasSmallParameter extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: AssemblyLine): Boolean = line.parameter match {
|
||||
case MemoryAddressConstant(th : VariableInMemory) => th.typ.size < 255
|
||||
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th : VariableInMemory), NumericConstant(_, 1)) => th.typ.size < 255
|
||||
case MemoryAddressConstant(th : MfArray) => th.sizeInBytes < 255
|
||||
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(th : MfArray), NumericConstant(_, 1)) => th.sizeInBytes < 255
|
||||
case _ => false
|
||||
}
|
||||
|
||||
override def hitRate: Double = 0.332
|
||||
}
|
||||
|
||||
case object HasIdentityPageParameter extends TrivialAssemblyLinePattern {
|
||||
override def apply(line: AssemblyLine): Boolean = line.parameter match {
|
||||
case MemoryAddressConstant(th) => th.name == "identity$"
|
||||
case _ => false
|
||||
}
|
||||
|
||||
override def hitRate: Double = 0.04
|
||||
}
|
||||
|
||||
case class MatchObject(i: Int, f: Function[AssemblyLine, Any]) extends AssemblyLinePattern {
|
||||
override def matchLineTo(ctx: AssemblyMatchingContext, flowInfo: FlowInfo, line: AssemblyLine): Boolean =
|
||||
ctx.addObject(i, f(line))
|
||||
|
@ -1601,6 +1678,8 @@ case class MatchElidableCopyOf(i: Int, firstLinePattern: AssemblyLinePattern, la
|
|||
}
|
||||
Some(after)
|
||||
}
|
||||
|
||||
override def minimumRequiredLines: Int = 0
|
||||
}
|
||||
|
||||
case object IsZeroPage extends AssemblyLinePattern {
|
||||
|
|
|
@ -15,6 +15,8 @@ import scala.collection.mutable.ListBuffer
|
|||
*/
|
||||
object SingleAssignmentVariableOptimization extends AssemblyOptimization[AssemblyLine] {
|
||||
|
||||
override def minimumRequiredLines: Int = 2
|
||||
|
||||
private val BadOpcodes = Set(RTS, JSR, JMP, RTI)
|
||||
private val GoodOpcodes = Set(
|
||||
LDA, LDX, LAX, LDY, EOR, AND, ORA, ADC, SBC, CMP, CPX, CPY, BIT,
|
||||
|
|
|
@ -14,6 +14,8 @@ import scala.collection.mutable
|
|||
*/
|
||||
object SuperOptimizer extends AssemblyOptimization[AssemblyLine] {
|
||||
|
||||
override def minimumRequiredLines: Int = 2
|
||||
|
||||
override def optimize(m: NormalFunction, code: List[AssemblyLine], optimizationContext: OptimizationContext): List[AssemblyLine] = {
|
||||
val options = optimizationContext.options
|
||||
val log = optimizationContext.log
|
||||
|
|
|
@ -17,6 +17,8 @@ import scala.util.control.TailCalls.{TailRec, done, tailcall}
|
|||
*/
|
||||
object TwoVariablesToIndexRegistersOptimization extends AssemblyOptimization[AssemblyLine] {
|
||||
|
||||
override def minimumRequiredLines: Int = 2
|
||||
|
||||
override def requiredFlags: Set[CompilationFlag.Value] = Set(CompilationFlag.RegisterVariables)
|
||||
|
||||
object CyclesAndBytes {
|
||||
|
|
|
@ -35,4 +35,6 @@ object UnusedLabelRemoval extends AssemblyOptimization[AssemblyLine] {
|
|||
}
|
||||
|
||||
override def name = "Unused label removal"
|
||||
|
||||
override def minimumRequiredLines: Int = 2
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@ object UseAccumulatorInsteadOfXRegister extends UseAccumulatorInsteadOfIndexRegi
|
|||
class UseAccumulatorInsteadOfIndexRegister(doY: Boolean) extends AssemblyOptimization[AssemblyLine] {
|
||||
override def name: String = if (doY) "Use accumulator instead of the Y register" else "Use accumulator instead of the X register"
|
||||
|
||||
override def minimumRequiredLines: Int = 2
|
||||
|
||||
override def optimize(f: NormalFunction, code: List[AssemblyLine], context: OptimizationContext): List[AssemblyLine] = {
|
||||
val log = context.log
|
||||
if (f.params.length == 1) return code
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
package millfork.assembly.mos.opt
|
||||
|
||||
import millfork.assembly.mos.{AssemblyLine, AssemblyLine0, OpcodeClasses, State}
|
||||
import millfork.assembly.mos.{AddrMode, AssemblyLine, AssemblyLine0, OpcodeClasses, State}
|
||||
import millfork.env._
|
||||
import millfork.error.ConsoleLogger
|
||||
import millfork.node.NiceFunctionProperty
|
||||
|
@ -12,10 +12,10 @@ object VariableLifetime {
|
|||
|
||||
// This only works for non-stack variables.
|
||||
def apply(variableName: String, code: List[AssemblyLine], expandToIncludeIndexing: Boolean = false, expandToIncludeUsesOfLoadedIndices: Option[Set[(NiceFunctionProperty, String)]] = None): Range = {
|
||||
val flags = code.map(_.parameter match {
|
||||
val flags = code.map(line => line.parameter match {
|
||||
case MemoryAddressConstant(MemoryVariable(n, _, _)) if n == variableName => true
|
||||
case CompoundConstant(MathOperator.Plus, MemoryAddressConstant(MemoryVariable(n, _, _)), NumericConstant(_, 1)) if n == variableName => true
|
||||
case _ => false
|
||||
case p => line.addrMode == AddrMode.IndexedX && p.refersTo(variableName)
|
||||
})
|
||||
if (flags.forall(!_)) return Range(0, 0)
|
||||
var min = flags.indexOf(true)
|
||||
|
|
|
@ -9,6 +9,7 @@ import millfork.env._
|
|||
import millfork.error.Logger
|
||||
import millfork.node.MosNiceFunctionProperty
|
||||
|
||||
import scala.collection.mutable
|
||||
import scala.collection.mutable.ListBuffer
|
||||
import scala.util.control.TailCalls.{TailRec, done, tailcall}
|
||||
|
||||
|
@ -17,6 +18,8 @@ import scala.util.control.TailCalls.{TailRec, done, tailcall}
|
|||
*/
|
||||
object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine] {
|
||||
|
||||
override def minimumRequiredLines: Int = 2
|
||||
|
||||
override def requiredFlags: Set[CompilationFlag.Value] = Set(CompilationFlag.RegisterVariables)
|
||||
|
||||
object CyclesAndBytes {
|
||||
|
@ -27,14 +30,14 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
|||
}
|
||||
|
||||
case class FeaturesForIndexRegisters(
|
||||
blastProcessing: Boolean,
|
||||
izIsAlwaysZero: Boolean,
|
||||
indexRegisterTransfers: Boolean,
|
||||
functionsSafeForX: Set[String],
|
||||
functionsSafeForY: Set[String],
|
||||
functionsSafeForZ: Set[String],
|
||||
identityArray: Constant,
|
||||
log: Logger)
|
||||
useIdentityPage: Boolean,
|
||||
izIsAlwaysZero: Boolean,
|
||||
indexRegisterTransfers: Boolean,
|
||||
functionsSafeForX: Set[String],
|
||||
functionsSafeForY: Set[String],
|
||||
functionsSafeForZ: Set[String],
|
||||
identityArray: Constant,
|
||||
log: Logger)
|
||||
|
||||
case class FeaturesForAccumulator(
|
||||
cmos: Boolean,
|
||||
|
@ -147,24 +150,26 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
|||
// assembly functions do not get this optimization
|
||||
return code
|
||||
}
|
||||
val stillUsedVariables = code.flatMap {
|
||||
case AssemblyLine0(_, _, MemoryAddressConstant(th)) => Some(th.name)
|
||||
val stillUsedVariables = mutable.Set[String]()
|
||||
val variablesWithAddressesTaken = mutable.Set[String]()
|
||||
code.foreach {
|
||||
case AssemblyLine0(_, _, MemoryAddressConstant(th)) => stillUsedVariables += th.name
|
||||
case AssemblyLine0(_, _, SubbyteConstant(MemoryAddressConstant(th), _)) => variablesWithAddressesTaken += th.name
|
||||
case _ => None
|
||||
}.toSet
|
||||
val variablesWithAddressesTaken = code.flatMap {
|
||||
case AssemblyLine0(_, _, SubbyteConstant(MemoryAddressConstant(th), _)) => Some(th.name)
|
||||
case _ => None
|
||||
}.toSet
|
||||
val localVariables = f.environment.getAllLocalVariables.filter {
|
||||
case v@MemoryVariable(name, typ, VariableAllocationMethod.Auto | VariableAllocationMethod.Register) =>
|
||||
typ.size == 1 && !paramVariables(name) && stillUsedVariables(name) && !variablesWithAddressesTaken(name) && !v.isVolatile
|
||||
}
|
||||
val localVariables = mutable.Set[Variable]()
|
||||
val variablesWithRegisterHint = mutable.Set[String]()
|
||||
|
||||
f.environment.getAllLocalVariables.foreach {
|
||||
case v@MemoryVariable(name, typ, alloc@(VariableAllocationMethod.Auto | VariableAllocationMethod.Register)) =>
|
||||
if (typ.size == 1 && !paramVariables(name) && stillUsedVariables(name) && !variablesWithAddressesTaken(name) && !v.isVolatile){
|
||||
localVariables += v
|
||||
if (alloc == VariableAllocationMethod.Register) {
|
||||
variablesWithRegisterHint += v.name
|
||||
}
|
||||
}
|
||||
case _ => false
|
||||
}
|
||||
val variablesWithRegisterHint = f.environment.getAllLocalVariables.filter {
|
||||
case v@MemoryVariable(name, typ, VariableAllocationMethod.Register) =>
|
||||
typ.size == 1 && !paramVariables(name) && stillUsedVariables(name) && !variablesWithAddressesTaken(name) && !v.isVolatile
|
||||
case _ => false
|
||||
}.map(_.name).toSet
|
||||
|
||||
val variablesWithLifetimes = localVariables.map(v =>
|
||||
v.name -> VariableLifetime.apply(v.name, code)
|
||||
|
@ -173,11 +178,11 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
|||
val removeVariablesForReal = !options.flag(CompilationFlag.InternalCurrentlyOptimizingForMeasurement)
|
||||
val costFunction: CyclesAndBytes => Int = if (options.flag(CompilationFlag.OptimizeForSpeed)) _.cycles else _.bytes
|
||||
val importances = ReverseFlowAnalyzer.analyze(f, code, optimizationContext)
|
||||
val blastProcessing = options.flag(CompilationFlag.OptimizeForSonicSpeed)
|
||||
val identityArray = f.environment.maybeGet[ThingInMemory]("identity$").map(MemoryAddressConstant).getOrElse(Constant.Zero)
|
||||
val useIdentityPage = options.flag(CompilationFlag.IdentityPage)
|
||||
val identityArray = f.environment.identityPage
|
||||
val izIsAlwaysZero = !options.flag(CompilationFlag.Emit65CE02Opcodes)
|
||||
val featuresForIndices = FeaturesForIndexRegisters(
|
||||
blastProcessing = blastProcessing,
|
||||
useIdentityPage = useIdentityPage,
|
||||
izIsAlwaysZero = izIsAlwaysZero,
|
||||
indexRegisterTransfers = options.flag(CompilationFlag.EmitEmulation65816Opcodes),
|
||||
functionsSafeForX = optimizationContext.niceFunctionProperties.filter(x => x._1 == MosNiceFunctionProperty.DoesntChangeX).map(_._2),
|
||||
|
@ -187,10 +192,12 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
|||
log = log
|
||||
)
|
||||
|
||||
val labelsUsedOnce: Set[String] = code.flatMap {
|
||||
case AssemblyLine0(op, _, MemoryAddressConstant(Label(l))) if op != Opcode.LABEL => Some(l)
|
||||
case _ => None
|
||||
}.groupBy(identity).filter(_._2.size == 1).keySet
|
||||
val tmpUsedLabelList = mutable.ListBuffer[String]()
|
||||
code.foreach{
|
||||
case AssemblyLine0(op, _, MemoryAddressConstant(Label(l))) if op != Opcode.LABEL => tmpUsedLabelList += l
|
||||
case _ =>
|
||||
}
|
||||
val labelsUsedOnce: Set[String] = tmpUsedLabelList.groupBy(identity).filter(_._2.size == 1).keySet
|
||||
|
||||
val featuresForAcc = FeaturesForAccumulator(
|
||||
cmos = options.flag(CompilationFlag.EmitCmosOpcodes),
|
||||
|
@ -378,14 +385,26 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
|||
val vx = xCandidate.getOrElse("-")
|
||||
val vy = yCandidate.getOrElse("-")
|
||||
val vz = zCandidate.getOrElse("-")
|
||||
val accessingX = lines match {
|
||||
case (AssemblyLine0(_, _, MemoryAddressConstant(th)), _) :: _ => th.name == vx
|
||||
case _ => false
|
||||
}
|
||||
val accessingY = lines match {
|
||||
case (AssemblyLine0(_, _, MemoryAddressConstant(th)), _) :: _ => th.name == vy
|
||||
case _ => false
|
||||
}
|
||||
val accessingZ = lines match {
|
||||
case (AssemblyLine0(_, _, MemoryAddressConstant(th)), _) :: _ => th.name == vz
|
||||
case _ => false
|
||||
}
|
||||
lines match {
|
||||
case (AssemblyLine0(_, Immediate, MemoryAddressConstant(th)), _) :: xs
|
||||
if th.name == vx || th.name == vy || th.name == vz =>
|
||||
if accessingX || accessingY || accessingZ =>
|
||||
// if an address of a variable is used, then that variable cannot be assigned to a register
|
||||
None
|
||||
|
||||
case (AssemblyLine0(_, Immediate, SubbyteConstant(MemoryAddressConstant(th), _)), _) :: xs
|
||||
if th.name == vx || th.name == vy || th.name == vz =>
|
||||
if accessingX || accessingY || accessingZ =>
|
||||
// if an address of a variable is used, then that variable cannot be assigned to a register
|
||||
None
|
||||
|
||||
|
@ -405,7 +424,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
|||
Indirect | LongIndirect |
|
||||
AbsoluteIndexedX, MemoryAddressConstant(th)), _) :: xs =>
|
||||
// if a variable is used as an array or a pointer, then it cannot be assigned to a register
|
||||
if (th.name == vx || th.name == vy || th.name == vz) {
|
||||
if (accessingX || accessingY || accessingZ) {
|
||||
None
|
||||
} else {
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs)
|
||||
|
@ -417,56 +436,56 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
|||
|
||||
case (AssemblyLine0(SEP | REP, _, _), _) :: xs => None
|
||||
|
||||
case (AssemblyLine0(STY | LDY, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs if th.name == vx && (features.indexRegisterTransfers) =>
|
||||
case (AssemblyLine0(STY | LDY, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs if accessingX && (features.indexRegisterTransfers) =>
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 2))
|
||||
|
||||
case (AssemblyLine0(STX | LDX, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs if th.name == vy && (features.indexRegisterTransfers) =>
|
||||
case (AssemblyLine0(STX | LDX, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs if accessingY && (features.indexRegisterTransfers) =>
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 2))
|
||||
|
||||
case (AssemblyLine0(LDY, Absolute | ZeroPage, MemoryAddressConstant(th)), _) ::
|
||||
(AssemblyLine0(LDA | STA | ADC | SBC | ORA | EOR | AND | CMP, AbsoluteY, _), f) :: xs if th.name == vx && f.y == Unimportant =>
|
||||
(AssemblyLine0(LDA | STA | ADC | SBC | ORA | EOR | AND | CMP, AbsoluteY, _), f) :: xs if accessingX && f.y == Unimportant =>
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 2))
|
||||
|
||||
case (AssemblyLine0(LDX, Absolute | ZeroPage, MemoryAddressConstant(th)), _) ::
|
||||
(AssemblyLine0(LDA | STA | ADC | SBC | ORA | EOR | AND | CMP, AbsoluteX, _), f) :: xs if th.name == vy && f.x == Unimportant =>
|
||||
(AssemblyLine0(LDA | STA | ADC | SBC | ORA | EOR | AND | CMP, AbsoluteX, _), f) :: xs if accessingY && f.x == Unimportant =>
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 2))
|
||||
|
||||
case (AssemblyLine0(LDY, Absolute | ZeroPage, MemoryAddressConstant(th)), _) ::
|
||||
(AssemblyLine0(SEC | CLC, _, _), _) ::
|
||||
(AssemblyLine0(LDA | STA | ADC | SBC | ORA | EOR | AND | CMP, AbsoluteY, _), f) :: xs if th.name == vx && f.y == Unimportant =>
|
||||
(AssemblyLine0(LDA | STA | ADC | SBC | ORA | EOR | AND | CMP, AbsoluteY, _), f) :: xs if accessingX && f.y == Unimportant =>
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 2))
|
||||
|
||||
case (AssemblyLine0(LDX, Absolute | ZeroPage, MemoryAddressConstant(th)), _) ::
|
||||
(AssemblyLine0(SEC | CLC, _, _), _) ::
|
||||
(AssemblyLine0(LDA | STA | ADC | SBC | ORA | EOR | AND | CMP, AbsoluteX, _), f) :: xs if th.name == vy && f.x == Unimportant =>
|
||||
(AssemblyLine0(LDA | STA | ADC | SBC | ORA | EOR | AND | CMP, AbsoluteX, _), f) :: xs if accessingY && f.x == Unimportant =>
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 2))
|
||||
|
||||
case (AssemblyLine0(STY | LDY, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs if th.name == vx => None
|
||||
case (AssemblyLine0(STY | LDY, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs if accessingX => None
|
||||
|
||||
case (AssemblyLine0(STX | LDX, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs if th.name == vy => None
|
||||
case (AssemblyLine0(STX | LDX, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs if accessingY => None
|
||||
|
||||
case (AssemblyLine(op, Absolute | ZeroPage, MemoryAddressConstant(th), elidability, _), _) :: xs
|
||||
if opcodesIdentityTable(op) && features.blastProcessing =>
|
||||
if (th.name == vx || th.name == vy) {
|
||||
if opcodesIdentityTable(op) && features.useIdentityPage =>
|
||||
if (accessingX || accessingY) {
|
||||
if (elidability == Elidability.Elidable) canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 0, cycles = -1))
|
||||
else None
|
||||
} else {
|
||||
if (th.name == vz) None
|
||||
if (accessingZ) None
|
||||
else canBeInlined(xCandidate, yCandidate, zCandidate, features, xs)
|
||||
}
|
||||
|
||||
case (AssemblyLine0(opcode, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs
|
||||
if th.name == vx && (opcode == LDY || opcode == LDZ || opcodesThatCannotBeUsedWithIndexRegistersAsParameters(opcode)) =>
|
||||
if accessingX && (opcode == LDY || opcode == LDZ || opcodesThatCannotBeUsedWithIndexRegistersAsParameters(opcode)) =>
|
||||
// if a variable is used by some opcodes, then it cannot be assigned to a register
|
||||
None
|
||||
|
||||
case (AssemblyLine0(opcode, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs
|
||||
if th.name == vy && (opcode == LDX || opcode == LAX || opcode == LDZ || opcodesThatCannotBeUsedWithIndexRegistersAsParameters(opcode)) =>
|
||||
if accessingY && (opcode == LDX || opcode == LAX || opcode == LDZ || opcodesThatCannotBeUsedWithIndexRegistersAsParameters(opcode)) =>
|
||||
// if a variable is used by some opcodes, then it cannot be assigned to a register
|
||||
None
|
||||
|
||||
case (AssemblyLine0(opcode, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs
|
||||
if th.name == vz && (opcode == LDX || opcode == LDY || opcodesThatCannotBeUsedWithIndexRegistersAsParameters(opcode)) =>
|
||||
if accessingZ && (opcode == LDX || opcode == LDY || opcodesThatCannotBeUsedWithIndexRegistersAsParameters(opcode)) =>
|
||||
// if a variable is used by some opcodes, then it cannot be assigned to a register
|
||||
None
|
||||
|
||||
|
@ -474,7 +493,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
|||
if xCandidate.isDefined =>
|
||||
// if a register is populated with a different variable, then this variable cannot be assigned to that register
|
||||
// removing LDX saves 3 cycles
|
||||
if (elidability == Elidability.Elidable && th.name == vx) {
|
||||
if (elidability == Elidability.Elidable && accessingX) {
|
||||
if (imp.z == Unimportant && imp.n == Unimportant) {
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 2))
|
||||
} else {
|
||||
|
@ -488,7 +507,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
|||
if xCandidate.isDefined =>
|
||||
// LAX = LDX-LDA, and since LDX simplifies to nothing and LDA simplifies to TXA,
|
||||
// LAX simplifies to TXA, saving two bytes
|
||||
if (elidability == Elidability.Elidable && th.name == vx) {
|
||||
if (elidability == Elidability.Elidable && accessingX) {
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 2))
|
||||
} else {
|
||||
None
|
||||
|
@ -498,7 +517,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
|||
// if a register is populated with a different variable, then this variable cannot be assigned to that register
|
||||
// removing LDX saves 3 bytes
|
||||
// sometimes that LDX has to be converted into CPX#0
|
||||
if (elidability == Elidability.Elidable && th.name == vy) {
|
||||
if (elidability == Elidability.Elidable && accessingY) {
|
||||
if (imp.z == Unimportant && imp.n == Unimportant) {
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 3, cycles = 4))
|
||||
} else {
|
||||
|
@ -509,7 +528,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
|||
}
|
||||
|
||||
case (AssemblyLine(LDZ, Absolute | ZeroPage, MemoryAddressConstant(th), elidability, _), imp) :: xs if zCandidate.isDefined =>
|
||||
if (elidability == Elidability.Elidable && th.name == vz) {
|
||||
if (elidability == Elidability.Elidable && accessingZ) {
|
||||
if (imp.z == Unimportant && imp.n == Unimportant) {
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 3, cycles = 4))
|
||||
} else {
|
||||
|
@ -548,7 +567,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
|||
if (elidability == Elidability.Elidable && elidability2 == Elidability.Elidable) canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 2))
|
||||
else None
|
||||
} else {
|
||||
if (th.name == vz) None
|
||||
if (accessingZ) None
|
||||
else canBeInlined(xCandidate, yCandidate, zCandidate, features, xs)
|
||||
}
|
||||
|
||||
|
@ -556,7 +575,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
|||
if xCandidate.isDefined =>
|
||||
// a variable cannot be inlined if there is TAX not after LDA of that variable
|
||||
// but LDA-TAX can be simplified to TXA
|
||||
if (elidability == Elidability.Elidable && elidability2 == Elidability.Elidable && th.name == vx) {
|
||||
if (elidability == Elidability.Elidable && elidability2 == Elidability.Elidable && accessingX) {
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 3, cycles = 4))
|
||||
} else {
|
||||
None
|
||||
|
@ -566,7 +585,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
|||
if yCandidate.isDefined =>
|
||||
// a variable cannot be inlined if there is TAY not after LDA of that variable
|
||||
// but LDA-TAY can be simplified to TYA
|
||||
if (elidability == Elidability.Elidable && elidability2 == Elidability.Elidable && th.name == vy) {
|
||||
if (elidability == Elidability.Elidable && elidability2 == Elidability.Elidable && accessingY) {
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 3, cycles = 4))
|
||||
} else {
|
||||
None
|
||||
|
@ -576,7 +595,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
|||
if zCandidate.isDefined =>
|
||||
// a variable cannot be inlined if there is TAZ not after LDA of that variable
|
||||
// but LDA-TAZ can be simplified to TZA
|
||||
if (elidability == Elidability.Elidable && elidability2 == Elidability.Elidable && th.name == vy) {
|
||||
if (elidability == Elidability.Elidable && elidability2 == Elidability.Elidable && accessingY) {
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 3, cycles = 4))
|
||||
} else {
|
||||
None
|
||||
|
@ -584,7 +603,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
|||
|
||||
case (AssemblyLine(LDA | STA, Absolute | ZeroPage, MemoryAddressConstant(th), elidability, _), _) :: xs =>
|
||||
// changing LDA->TXA, STA->TAX, INC->INX, DEC->DEX saves 2 bytes
|
||||
if (th.name == vy || th.name == vx || th.name == vz) {
|
||||
if (accessingY || accessingX || accessingZ) {
|
||||
if (elidability == Elidability.Elidable) {
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 2))
|
||||
} else {
|
||||
|
@ -596,7 +615,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
|||
|
||||
case (AssemblyLine(INC | DEC, Absolute | ZeroPage, MemoryAddressConstant(th), elidability, _), _) :: xs =>
|
||||
// changing LDA->TXA, STA->TAX, INC->INX, DEC->DEX saves 2 bytes
|
||||
if (th.name == vy || th.name == vx || th.name == vz) {
|
||||
if (accessingY || accessingX || accessingZ) {
|
||||
if (elidability == Elidability.Elidable) {
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 2, cycles = 4))
|
||||
} else {
|
||||
|
@ -608,7 +627,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
|||
|
||||
case (AssemblyLine(STZ, Absolute | ZeroPage, MemoryAddressConstant(th), elidability, _), _) :: xs =>
|
||||
// changing STZ->LDX saves 1 byte
|
||||
if (th.name == vy || th.name == vx) {
|
||||
if (accessingY || accessingX) {
|
||||
if (elidability == Elidability.Elidable && features.izIsAlwaysZero) {
|
||||
canBeInlined(xCandidate, yCandidate, zCandidate, features, xs).map(_ + CyclesAndBytes(bytes = 1, cycles = 2))
|
||||
} else {
|
||||
|
@ -883,37 +902,53 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
|||
val vy = yCandidate.getOrElse("-")
|
||||
val vz = zCandidate.getOrElse("-")
|
||||
val va = aCandidate.getOrElse("-")
|
||||
val accessingX = lines match {
|
||||
case (AssemblyLine0(_, _, MemoryAddressConstant(th)), _) :: _ => th.name == vx
|
||||
case _ => false
|
||||
}
|
||||
val accessingY = lines match {
|
||||
case (AssemblyLine0(_, _, MemoryAddressConstant(th)), _) :: _ => th.name == vy
|
||||
case _ => false
|
||||
}
|
||||
val accessingZ = lines match {
|
||||
case (AssemblyLine0(_, _, MemoryAddressConstant(th)), _) :: _ => th.name == vz
|
||||
case _ => false
|
||||
}
|
||||
val accessingA = lines match {
|
||||
case (AssemblyLine0(_, _, MemoryAddressConstant(th)), _) :: _ => th.name == va
|
||||
case _ => false
|
||||
}
|
||||
lines match {
|
||||
case (AssemblyLine(INC, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vx =>
|
||||
if accessingX =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map( AssemblyLine.implied(INX).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(INC, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vy =>
|
||||
if accessingY =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(INY).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(INC, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vz =>
|
||||
if accessingZ =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(INZ).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(DEC, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vx =>
|
||||
if accessingX =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(DEX).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(DEC, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vy =>
|
||||
if accessingY =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(DEY).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(DEC, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vz =>
|
||||
if accessingZ =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(DEZ).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(opcode@(DEC | INC | ROL | ROR | ASL | LSR), Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == va =>
|
||||
if accessingA =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(opcode).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(LDX, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), imp) :: xs
|
||||
if th.name == vx =>
|
||||
if accessingX =>
|
||||
if (imp.z == Unimportant && imp.n == Unimportant) {
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs))
|
||||
} else {
|
||||
|
@ -921,22 +956,22 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
|||
}
|
||||
|
||||
case (AssemblyLine(LAX, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vx =>
|
||||
if accessingX =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TXA).pos(s) :: _)
|
||||
|
||||
case (l@AssemblyLine0(op, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs
|
||||
if opcodesIdentityTable(op) && th.name == vx =>
|
||||
if opcodesIdentityTable(op) && accessingX =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(l.copy(addrMode = AbsoluteX, parameter = features.identityArray) :: _)
|
||||
|
||||
case (l@AssemblyLine0(op, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs
|
||||
if opcodesIdentityTable(op) && th.name == vy =>
|
||||
if opcodesIdentityTable(op) && accessingY =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(l.copy(addrMode = AbsoluteY, parameter = features.identityArray) :: _)
|
||||
|
||||
case (l@AssemblyLine0(LDA | TYA | TXA | TZA | CLA, _, _), _) :: xs if aCandidate.isDefined && isReturn(xs) =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(l :: _)
|
||||
|
||||
case (l@AssemblyLine0(LDA, _, _), _) :: (AssemblyLine0(op, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs
|
||||
if opcodesCommutative(op) && th.name == va =>
|
||||
if opcodesCommutative(op) && accessingA =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(l.copy(opcode = op) :: _)
|
||||
|
||||
case (l@AssemblyLine0(LDA, _, _), _) :: (clc@AssemblyLine0(CLC, _, _), _) :: (AssemblyLine0(op, Absolute | ZeroPage, MemoryAddressConstant(th)), _) :: xs
|
||||
|
@ -960,7 +995,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
|||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TYA).pos(s) :: clc :: l.copy(opcode = op) :: _)
|
||||
|
||||
case (AssemblyLine(LDA | STA, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), imp) :: xs
|
||||
if th.name == va =>
|
||||
if accessingA =>
|
||||
if (imp.z == Unimportant && imp.n == Unimportant) {
|
||||
inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
} else {
|
||||
|
@ -968,11 +1003,11 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
|||
}
|
||||
|
||||
case (AssemblyLine(LAX, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == va =>
|
||||
if accessingA =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAX).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(LDY, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), imp) :: xs
|
||||
if th.name == vy =>
|
||||
if accessingY =>
|
||||
if (imp.z == Unimportant && imp.n == Unimportant) {
|
||||
inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
} else {
|
||||
|
@ -980,7 +1015,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
|||
}
|
||||
|
||||
case (AssemblyLine(LDZ, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), imp) :: xs
|
||||
if th.name == vz =>
|
||||
if accessingZ =>
|
||||
if (imp.z == Unimportant && imp.n == Unimportant) {
|
||||
inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)
|
||||
} else {
|
||||
|
@ -988,64 +1023,64 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
|||
}
|
||||
|
||||
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), Elidability.Elidable, s), _) :: (AssemblyLine(TAX, _, _, Elidability.Elidable, _), _) :: xs
|
||||
if th.name == vx =>
|
||||
if accessingX =>
|
||||
// these TXA's may get optimized away by a different optimization
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TXA).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), Elidability.Elidable, s), _) :: (AssemblyLine(TAY, _, _, Elidability.Elidable, _), _) :: xs
|
||||
if th.name == vy =>
|
||||
if accessingY =>
|
||||
// these TYA's may get optimized away by a different optimization
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TYA).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), Elidability.Elidable, s), _) :: (AssemblyLine(TAZ, _, _, Elidability.Elidable, _), _) :: xs
|
||||
if th.name == vz =>
|
||||
if accessingZ =>
|
||||
// these TZA's may get optimized away by a different optimization
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TZA).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(LDX, Absolute | ZeroPage, MemoryAddressConstant(th), Elidability.Elidable, s), _) :: (AssemblyLine(TXA, _, _, Elidability.Elidable, _), _) :: xs
|
||||
if th.name == va =>
|
||||
if accessingA =>
|
||||
// these TAX's may get optimized away by a different optimization
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAX).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(LDY, Absolute | ZeroPage, MemoryAddressConstant(th), Elidability.Elidable, _), _) :: (AssemblyLine(TYA, _, _, Elidability.Elidable, _), _) :: xs
|
||||
if th.name == va =>
|
||||
if accessingA =>
|
||||
// these TAY's may get optimized away by a different optimization
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAY) :: _)
|
||||
|
||||
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _, s1), _) :: (AssemblyLine(CMP, am, param, Elidability.Elidable, s2), _) :: xs
|
||||
if th.name == vx && CpxyzAddrModes(am) && isNot(vx, param) =>
|
||||
if accessingX && CpxyzAddrModes(am) && isNot(vx, param) =>
|
||||
// ditto
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TXA).pos(s1) :: AssemblyLine(CPX, am, param).pos(s2) :: _)
|
||||
|
||||
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _, s1), _) :: (AssemblyLine(CMP, am, param, Elidability.Elidable, s2), _) :: xs
|
||||
if th.name == vy && CpxyzAddrModes(am) && isNot(vx, param) =>
|
||||
if accessingY && CpxyzAddrModes(am) && isNot(vx, param) =>
|
||||
// ditto
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TYA).pos(s1) :: AssemblyLine(CPY, am, param).pos(s2) :: _)
|
||||
|
||||
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _, s1), _) :: (AssemblyLine(CMP, am, param, Elidability.Elidable, s2), _) :: xs
|
||||
if th.name == vy && CpxyzAddrModes(am) && isNot(vx, param) =>
|
||||
if accessingY && CpxyzAddrModes(am) && isNot(vx, param) =>
|
||||
// ditto
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TZA).pos(s1) :: AssemblyLine(CPZ, am, param).pos(s2) :: _)
|
||||
|
||||
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vx =>
|
||||
if accessingX =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TXA).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vy =>
|
||||
if accessingY =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TYA).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(LDY, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vx && features.indexRegisterTransfers =>
|
||||
if accessingX && features.indexRegisterTransfers =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TXY).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(LDX, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vy && features.indexRegisterTransfers =>
|
||||
if accessingY && features.indexRegisterTransfers =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TYX).pos(s) :: _)
|
||||
|
||||
case (l0@AssemblyLine0(LDY, Absolute | ZeroPage, MemoryAddressConstant(th)), _) ::
|
||||
(l1@AssemblyLine0(LDA | STA | ADC | SBC | AND | ORA | EOR | CMP, AbsoluteY, _), f):: xs
|
||||
if th.name == vx =>
|
||||
if accessingX =>
|
||||
if (l1.opcode != STA || f.n == Unimportant && f.z == Unimportant) {
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(l1.copy(addrMode = AbsoluteX) :: _)
|
||||
} else if (f.c == Unimportant) {
|
||||
|
@ -1057,7 +1092,7 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
|||
|
||||
case (l0@AssemblyLine0(LDX, Absolute | ZeroPage, MemoryAddressConstant(th)), _) ::
|
||||
(l1@AssemblyLine0(LDA | STA | ADC | SBC | AND | ORA | EOR | CMP, AbsoluteX, _), f):: xs
|
||||
if th.name == vy =>
|
||||
if accessingY =>
|
||||
if (l1.opcode != STA || f.n == Unimportant && f.z == Unimportant) {
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(l1.copy(addrMode = AbsoluteY) :: _)
|
||||
} else if (f.c == Unimportant) {
|
||||
|
@ -1069,31 +1104,31 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
|||
case (l0@AssemblyLine0(LDY, Absolute | ZeroPage, MemoryAddressConstant(th)), _) ::
|
||||
(l5@AssemblyLine0(SEC | CLC, _, _), _) ::
|
||||
(l1@AssemblyLine0(LDA | STA | ADC | SBC | AND | ORA | EOR | CMP, AbsoluteY, _), _):: xs
|
||||
if th.name == vx =>
|
||||
if accessingX =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(l5 :: l1.copy(addrMode = AbsoluteX) :: _)
|
||||
|
||||
case (l0@AssemblyLine0(LDX, Absolute | ZeroPage, MemoryAddressConstant(th)), _) ::
|
||||
(l5@AssemblyLine0(SEC | CLC, _, _), _) ::
|
||||
(l1@AssemblyLine0(LDA | STA | ADC | SBC | AND | ORA | EOR | CMP, AbsoluteX, _), _):: xs
|
||||
if th.name == vy =>
|
||||
if accessingY =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(l5 :: l1.copy(addrMode = AbsoluteY) :: _)
|
||||
|
||||
case (AssemblyLine(LDY, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vx => features.log.fatal("Unexpected LDY")
|
||||
if accessingX => features.log.fatal("Unexpected LDY")
|
||||
|
||||
case (AssemblyLine(LDX, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vy => features.log.fatal("Unexpected LDX")
|
||||
if accessingY => features.log.fatal("Unexpected LDX")
|
||||
|
||||
case (AssemblyLine(LDA, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vz =>
|
||||
if accessingZ =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TZA).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(LDX, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == va =>
|
||||
if accessingA =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAX).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(LDY, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == va =>
|
||||
if accessingA =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAY).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(LDA, am, param, Elidability.Elidable, s1), _) :: (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), Elidability.Elidable, s2), _) :: xs
|
||||
|
@ -1112,51 +1147,51 @@ object VariableToRegisterOptimization extends AssemblyOptimization[AssemblyLine]
|
|||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine(LDZ, am, param).pos(s1, s2) :: AssemblyLine.implied(TZA).pos(s1, s2) :: _)
|
||||
|
||||
case (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vx =>
|
||||
if accessingX =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAX).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vy =>
|
||||
if accessingY =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAY).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(STA, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vz =>
|
||||
if accessingZ =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TAZ).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(STX, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == va =>
|
||||
if accessingA =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TXA).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(STY, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == va =>
|
||||
if accessingA =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TYA).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(STX, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vy && features.indexRegisterTransfers =>
|
||||
if accessingY && features.indexRegisterTransfers =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TXY).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(STY, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vx && features.indexRegisterTransfers =>
|
||||
if accessingX && features.indexRegisterTransfers =>
|
||||
tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TYX).pos(s) :: _)
|
||||
|
||||
case (AssemblyLine(STX, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vy => features.log.fatal("Unexpected STX")
|
||||
if accessingY => features.log.fatal("Unexpected STX")
|
||||
|
||||
case (AssemblyLine(STY, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vx => features.log.fatal("Unexpected STY")
|
||||
if accessingX => features.log.fatal("Unexpected STY")
|
||||
|
||||
case (AssemblyLine(STZ, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vx =>
|
||||
if accessingX =>
|
||||
if (features.izIsAlwaysZero) tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.immediate(LDX, 0).pos(s) :: _)
|
||||
else features.log.fatal("Unexpected STZ")
|
||||
|
||||
case (AssemblyLine(STZ, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == vy =>
|
||||
if accessingY =>
|
||||
if (features.izIsAlwaysZero) tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.immediate(LDY, 0).pos(s) :: _)
|
||||
else features.log.fatal("Unexpected STZ")
|
||||
|
||||
case (AssemblyLine(STZ, Absolute | ZeroPage, MemoryAddressConstant(th), _, s), _) :: xs
|
||||
if th.name == va =>
|
||||
if accessingA =>
|
||||
if (features.izIsAlwaysZero) tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.immediate(LDA, 0).pos(s) :: _)
|
||||
else tailcall(inlineVars(xCandidate, yCandidate, zCandidate, aCandidate, features, xs)).map(AssemblyLine.implied(TZA).pos(s) :: _)
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package millfork.assembly.opt
|
||||
|
||||
import millfork.assembly.mos.opt.SourceOfNZ
|
||||
import millfork.env.{Constant, NumericConstant}
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
|
@ -78,6 +79,16 @@ object Status {
|
|||
val SingleZero: Status[Int] = SingleStatus(0)
|
||||
val SingleFF: Status[Int] = SingleStatus(0xff)
|
||||
|
||||
def fromByte(c: Constant): Status[Int] = c.loByte match {
|
||||
case NumericConstant(n, _) => SingleStatus(n.toInt & 0xff)
|
||||
case _ => AnyStatus
|
||||
}
|
||||
|
||||
def fromWord(c: Constant): Status[Int] = c match {
|
||||
case NumericConstant(n, _) => SingleStatus(n.toInt & 0xffff)
|
||||
case _ => AnyStatus
|
||||
}
|
||||
|
||||
@inline
|
||||
private def wrapBool(b: Boolean): Status[Boolean] = if (b) SingleTrue else SingleFalse
|
||||
|
||||
|
@ -109,6 +120,13 @@ object Status {
|
|||
case x => x
|
||||
}
|
||||
}
|
||||
|
||||
implicit class ConstantStatusOps(val inner: Status[Constant]) extends AnyVal {
|
||||
def toHiLo: (Status[Int], Status[Int]) = inner match {
|
||||
case SingleStatus(NumericConstant(n, _)) => SingleStatus(n.toInt.>>(8).&(0xff)) -> SingleStatus(n.toInt.&(0xff))
|
||||
case _ => AnyStatus -> AnyStatus
|
||||
}
|
||||
}
|
||||
implicit class SourceOfNZStatusOps(val inner: Status[SourceOfNZ]) extends AnyVal {
|
||||
def isFromA: Boolean = inner match {
|
||||
case SingleStatus(v) => v.a
|
||||
|
|
|
@ -953,7 +953,7 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
|
|||
if (result.contains("???")) s" ??? (${this.toString.stripPrefix(" ")})" else result
|
||||
}
|
||||
|
||||
def readsRegister(r: ZRegister.Value): Boolean = {
|
||||
def readsRegister(r: ZRegister.Value): Boolean = { // TODO: optimize
|
||||
import ZOpcode._
|
||||
import ZRegister._
|
||||
r match {
|
||||
|
@ -1067,6 +1067,17 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
|
|||
case LHLX | RLDE => r == D || r == E
|
||||
case RRHL => r == H || r == L
|
||||
|
||||
case MULUB => r == A || (registers match {
|
||||
case TwoRegisters(p, q) => r == q || r == p
|
||||
case _ => true
|
||||
})
|
||||
case MULUW => r == H || r == L || (registers match {
|
||||
case TwoRegisters(_, BC) => r == B || r == C
|
||||
case TwoRegisters(_, DE) => r == D || r == E
|
||||
case TwoRegisters(_, SP) => r == SP
|
||||
case _ => true
|
||||
})
|
||||
|
||||
case _ => true // TODO
|
||||
}
|
||||
}
|
||||
|
@ -1230,6 +1241,9 @@ case class ZLine(opcode: ZOpcode.Value, registers: ZRegisters, parameter: Consta
|
|||
case LHLX | RRHL | DSUB => r == H || r == L
|
||||
case SHLX => false
|
||||
|
||||
case MULUB => r == H || r == L
|
||||
case MULUW => r == H || r == L || r == D || r == E
|
||||
|
||||
case _ => true // TODO
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,6 +29,8 @@ object ZOpcode extends Enumeration {
|
|||
LD_DESP, LD_DEHL, RRHL, RLDE, DSUB, RSTV, LHLX, SHLX,
|
||||
//sharp:
|
||||
LD_AHLI, LD_AHLD, LD_HLIA, LD_HLDA, SWAP, LDH_DA, LDH_AD, LDH_CA, LDH_AC, LD_HLSP, ADD_SP, STOP,
|
||||
// R800:
|
||||
MULUB, MULUW,
|
||||
// next:
|
||||
LDIX, LDWS, LDIRX, LDDX, LDDRX, LDPIRX, OUTINB, MUL, SWAPNIB, MIRROR, NEXTREG, PIXELDN, PIXELAD, SETAE, TEST,
|
||||
DISCARD_A, DISCARD_F, DISCARD_HL, DISCARD_BC, DISCARD_DE, DISCARD_IX, DISCARD_IY, CHANGED_MEM,
|
||||
|
@ -55,6 +57,7 @@ object ZOpcodeClasses {
|
|||
|
||||
val CbInstructions: Set[ZOpcode.Value] = Set(SLA, SRA, SRL, SLL, RLC, RRC, RL, RR) ++ BIT ++ RES ++ SET
|
||||
val EdInstructions: Set[ZOpcode.Value] = Set(NEG, RETN, RETI, IM, RRD, RLD,
|
||||
MULUB, MULUW,
|
||||
INI, INIR, OUTI, OUTIR, IND, INDR, OUTD, OUTDR,
|
||||
LDI, LDIR, LDD, LDDR, CPI, CPIR, CPD, CPDR) ++ BIT ++ RES ++ SET
|
||||
|
||||
|
@ -63,6 +66,7 @@ object ZOpcodeClasses {
|
|||
val ChangesAFAlways: Set[ZOpcode.Value] = Set( // TODO: !
|
||||
DAA, ADD, ADC, SUB, SBC, XOR, OR, AND, INC, DEC,
|
||||
SCF, CCF, NEG, RIM,
|
||||
MULUB, MULUW,
|
||||
LDH_AC, LDH_AD, LD_AHLI, LD_AHLD,
|
||||
ADD_16, ADC_16, SBC_16, INC_16, DEC_16,
|
||||
INI, INIR, OUTI, OUTIR, IND, INDR, OUTD, OUTDR,
|
||||
|
@ -74,6 +78,7 @@ object ZOpcodeClasses {
|
|||
LDIX, LDIRX, LDDX, LDDRX, LDPIRX,
|
||||
EXX, CALL, JR, JP, LABEL, DJNZ)
|
||||
val ChangesHLAlways: Set[ZOpcode.Value] = Set(
|
||||
MULUB, MULUW,
|
||||
INI, INIR, OUTI, OUTIR, IND, INDR, OUTD, OUTDR,
|
||||
LDI, LDIR, LDD, LDDR, CPI, CPIR, CPD, CPDR,
|
||||
LD_AHLI, LD_AHLD, LD_HLIA, LD_HLDA, LD_HLSP, DSUB,
|
||||
|
@ -81,6 +86,7 @@ object ZOpcodeClasses {
|
|||
LDWS, LDIX, LDIRX, LDDX, LDDRX, LDPIRX, PIXELAD, PIXELDN, OUTINB,
|
||||
EXX, EX_DE_HL, CALL, JR, JP, LABEL)
|
||||
val ChangesDEAlways: Set[ZOpcode.Value] = Set(
|
||||
MULUW,
|
||||
LDI, LDIR, LDD, LDDR,
|
||||
LD_DESP, LD_DEHL, RLDE,
|
||||
LDWS, LDIX, LDIRX, LDDX, LDDRX, LDPIRX, MUL,
|
||||
|
@ -88,6 +94,7 @@ object ZOpcodeClasses {
|
|||
val ChangesOnlyRegister: Set[ZOpcode.Value] = Set(INC, DEC, INC_16, DEC_16, POP, EX_SP, IN_C, IN_IMM, RL, RR, RLC, RRC, SLA, SRA, SRL, SLL) ++ SET ++ RES
|
||||
val ChangesFirstRegister: Set[ZOpcode.Value] = Set(LD, LD_16, ADD_16, SBC_16)
|
||||
val ChangesAAlways: Set[ZOpcode.Value] = Set(
|
||||
MULUB,
|
||||
DAA, ADD, ADC, SUB, SBC, XOR, OR, AND, LD_AHLI, LD_AHLD, RIM,
|
||||
MIRROR, SETAE,
|
||||
)
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
package millfork.assembly.z80.opt
|
||||
|
||||
import millfork.assembly.AssemblyOptimization
|
||||
import millfork.assembly.z80.ZLine
|
||||
import millfork.assembly.z80.ZOpcode.MULUB
|
||||
import millfork.assembly.z80.ZOpcode.MULUW
|
||||
import millfork.node.ZRegister
|
||||
|
||||
/**
|
||||
* Optimizations valid for R800
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
object AlwaysGoodR800Optimizations {
|
||||
|
||||
val UnusedR800Instructions = new RuleBasedAssemblyOptimization("Simplifiable maths (R800)",
|
||||
needsFlowInfo = FlowInfoRequirement.BackwardFlow,
|
||||
(Elidable & HasOpcode(MULUB) & DoesntMatterWhatItDoesWith(ZRegister.H, ZRegister.L) & DoesntMatterWhatItDoesWithFlags) ~~> (_ => Nil),
|
||||
(Elidable & HasOpcode(MULUW) & DoesntMatterWhatItDoesWith(ZRegister.H, ZRegister.L, ZRegister.D, ZRegister.E) & DoesntMatterWhatItDoesWithFlags) ~~> (_ => Nil),
|
||||
)
|
||||
|
||||
val All: List[AssemblyOptimization[ZLine]] = List[AssemblyOptimization[ZLine]](
|
||||
UnusedR800Instructions,
|
||||
)
|
||||
}
|
|
@ -17,6 +17,8 @@ object ByteVariableToRegisterOptimization extends AssemblyOptimization[ZLine] {
|
|||
|
||||
override def name = "Allocating variables to single registers"
|
||||
|
||||
override def minimumRequiredLines: Int = 3
|
||||
|
||||
object CyclesAndBytes {
|
||||
val Zero = CyclesAndBytes(0, 0)
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@ class ChangeRegisterPair(preferBC2DE: Boolean) extends AssemblyOptimization[ZLin
|
|||
|
||||
import millfork.node.ZRegister._
|
||||
|
||||
override def minimumRequiredLines: Int = 3
|
||||
|
||||
case class Loaded(b: Boolean = false, c: Boolean = false, d: Boolean = false, e: Boolean = false) {
|
||||
def load(register: ZRegister.Value): Loaded = register match {
|
||||
case B => copy(b = true, d = false, e = false)
|
||||
|
@ -172,6 +174,11 @@ class ChangeRegisterPair(preferBC2DE: Boolean) extends AssemblyOptimization[ZLin
|
|||
case (x@ZLine0(_, TwoRegisters(r, BC), _)) :: xs =>
|
||||
x.copy(registers = TwoRegisters(r, DE)) :: switchBC2DE(xs)
|
||||
|
||||
case (x@ZLine0(_, TwoRegisters(MEM_BC, r), _)) :: xs =>
|
||||
x.copy(registers = TwoRegisters(MEM_DE, r)) :: switchBC2DE(xs)
|
||||
case (x@ZLine0(_, TwoRegisters(r, MEM_BC), _)) :: xs =>
|
||||
x.copy(registers = TwoRegisters(r, MEM_DE)) :: switchBC2DE(xs)
|
||||
|
||||
case (x@ZLine0(_, TwoRegisters(B, r), _)) :: xs =>
|
||||
x.copy(registers = TwoRegisters(D, r)) :: switchBC2DE(xs)
|
||||
case (x@ZLine0(_, TwoRegisters(r, B), _)) :: xs =>
|
||||
|
@ -220,6 +227,11 @@ class ChangeRegisterPair(preferBC2DE: Boolean) extends AssemblyOptimization[ZLin
|
|||
case (x@ZLine0(_, TwoRegisters(r, DE), _)) :: xs =>
|
||||
x.copy(registers = TwoRegisters(r, BC)) :: switchDE2BC(xs)
|
||||
|
||||
case (x@ZLine0(_, TwoRegisters(MEM_DE, r), _)) :: xs =>
|
||||
x.copy(registers = TwoRegisters(MEM_BC, r)) :: switchDE2BC(xs)
|
||||
case (x@ZLine0(_, TwoRegisters(r, MEM_DE), _)) :: xs =>
|
||||
x.copy(registers = TwoRegisters(r, MEM_BC)) :: switchDE2BC(xs)
|
||||
|
||||
case (x@ZLine0(_, TwoRegisters(D, r), _)) :: xs =>
|
||||
x.copy(registers = TwoRegisters(B, r)) :: switchDE2BC(xs)
|
||||
case (x@ZLine0(_, TwoRegisters(r, D), _)) :: xs =>
|
||||
|
|
|
@ -361,6 +361,38 @@ object CoarseFlowAnalyzer {
|
|||
case ZLine0(RIM, _, _) =>
|
||||
currentStatus = currentStatus.copy(a = AnyStatus)
|
||||
|
||||
case ZLine0(MULUB, TwoRegisters(ZRegister.A, r@(ZRegister.B | ZRegister.C | ZRegister.D | ZRegister.E)), _) =>
|
||||
val hl = (currentStatus.a<*>currentStatus.getRegister(r, 0)){(a,b) => (a*b)&0xff}
|
||||
currentStatus = currentStatus.copy(
|
||||
h = hl.hi,
|
||||
l = hl.lo,
|
||||
hl = hl.map(NumericConstant(_, 2)),
|
||||
cf = AnyStatus,
|
||||
nf = AnyStatus,
|
||||
hf = AnyStatus,
|
||||
zf = AnyStatus,
|
||||
sf = AnyStatus,
|
||||
pf = AnyStatus
|
||||
)
|
||||
case ZLine0(MULUW, TwoRegisters(ZRegister.HL, ZRegister.BC), _) =>
|
||||
val hl = (currentStatus.h<*>currentStatus.l)(currentStatus.mergeBytes)
|
||||
val bc = (currentStatus.b<*>currentStatus.c)(currentStatus.mergeBytes)
|
||||
val hi = (hl<*>bc){(a,b) => (a*b).>>(16).&(0xffff)}
|
||||
val lo = (hl<*>bc){(a,b) => (a*b).&(0xffff)}
|
||||
currentStatus = currentStatus.copy(
|
||||
d = hi.hi,
|
||||
e = hi.lo,
|
||||
h = lo.hi,
|
||||
l = lo.lo,
|
||||
hl = lo.map(NumericConstant(_, 2)),
|
||||
cf = AnyStatus,
|
||||
nf = AnyStatus,
|
||||
hf = AnyStatus,
|
||||
zf = AnyStatus,
|
||||
sf = AnyStatus,
|
||||
pf = AnyStatus
|
||||
)
|
||||
|
||||
case ZLine0(opcode, registers, _) =>
|
||||
currentStatus = currentStatus.copy(cf = AnyStatus, zf = AnyStatus, sf = AnyStatus, pf = AnyStatus, hf = AnyStatus)
|
||||
if (ZOpcodeClasses.ChangesAAlways(opcode)) currentStatus = currentStatus.copy(a = AnyStatus)
|
||||
|
|
|
@ -12,6 +12,8 @@ import millfork.node.ZRegister
|
|||
object CompactStackFrame extends AssemblyOptimization[ZLine] {
|
||||
override def name: String = "Compacting the stack frame"
|
||||
|
||||
override def minimumRequiredLines: Int = 9
|
||||
|
||||
override def optimize(f: NormalFunction, code: List[ZLine], context: OptimizationContext): List[ZLine] = {
|
||||
val register =
|
||||
if (context.options.flag(CompilationFlag.UseIxForStack)) ZRegister.IX
|
||||
|
|
|
@ -14,6 +14,8 @@ import scala.collection.mutable
|
|||
object EmptyMemoryStoreRemoval extends AssemblyOptimization[ZLine] {
|
||||
override def name = "Removing pointless stores to automatic variables"
|
||||
|
||||
override def minimumRequiredLines: Int = 2
|
||||
|
||||
override def optimize(f: NormalFunction, code: List[ZLine], optimizationContext: OptimizationContext): List[ZLine] = {
|
||||
val vs = VariableStatus(f, code, optimizationContext, _ => true, allowParams = true).getOrElse(return code)
|
||||
if (vs.localVariables.isEmpty) {
|
||||
|
|
|
@ -12,6 +12,8 @@ import millfork.env._
|
|||
object EmptyParameterStoreRemoval extends AssemblyOptimization[ZLine] {
|
||||
override def name = "Removing pointless stores to foreign variables"
|
||||
|
||||
override def minimumRequiredLines: Int = 2
|
||||
|
||||
override def optimize(f: NormalFunction, code: List[ZLine], optimizationContext: OptimizationContext): List[ZLine] = {
|
||||
val usedFunctions = code.flatMap {
|
||||
case ZLine0(CALL | JP | JR, _, MemoryAddressConstant(th)) => Some(th.name)
|
||||
|
|
|
@ -30,36 +30,47 @@ object FlowInfoRequirement extends Enumeration {
|
|||
}
|
||||
|
||||
def assertLabels(x: FlowInfoRequirement.Value): Unit = x match {
|
||||
case NoRequirement => FatalErrorReporting.reportFlyingPig("Backward flow info required")
|
||||
case NoRequirement => FatalErrorReporting.reportFlyingPig("Label info required")
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
|
||||
trait AssemblyRuleSet{
|
||||
def flatten: Seq[AssemblyRule]
|
||||
|
||||
def minimumRequiredLines: Int
|
||||
}
|
||||
|
||||
class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInfoRequirement.Value, val rules: AssemblyRuleSet*) extends AssemblyOptimization[ZLine]{
|
||||
|
||||
private val actualRules = rules.flatMap(_.flatten)
|
||||
actualRules.foreach(_.pattern.validate(needsFlowInfo))
|
||||
private val actualRulesWithIndex = actualRules.zipWithIndex
|
||||
|
||||
override val minimumRequiredLines: Int = rules.map(_.minimumRequiredLines).min
|
||||
|
||||
override def toString: String = name
|
||||
|
||||
override def optimize(f: NormalFunction, code: List[ZLine], optimizationContext: OptimizationContext): List[ZLine] = {
|
||||
val taggedCode = FlowAnalyzer.analyze(f, code, optimizationContext, needsFlowInfo)
|
||||
val (changed, optimized) = optimizeImpl(f, taggedCode, optimizationContext)
|
||||
if (changed) optimized else code
|
||||
optimizeImpl(f, code, taggedCode, optimizationContext)
|
||||
}
|
||||
|
||||
def optimizeImpl(f: NormalFunction, code: List[(FlowInfo, ZLine)], optimizationContext: OptimizationContext): (Boolean, List[ZLine]) = {
|
||||
final def optimizeImpl(f: NormalFunction, code:List[ZLine], taggedCode: List[(FlowInfo, ZLine)], optimizationContext: OptimizationContext): List[ZLine] = {
|
||||
val log = optimizationContext.log
|
||||
code match {
|
||||
case Nil => (false, Nil)
|
||||
taggedCode match {
|
||||
case Nil => code
|
||||
case head :: tail =>
|
||||
for ((rule, index) <- actualRules.zipWithIndex) {
|
||||
val codeLength = code.length
|
||||
for {
|
||||
(rule, index) <- actualRulesWithIndex
|
||||
if codeLength >= rule.minimumRequiredLines
|
||||
} {
|
||||
val ctx = new AssemblyMatchingContext(optimizationContext.options)
|
||||
rule.pattern.matchTo(ctx, code) match {
|
||||
rule.pattern.matchTo(ctx, taggedCode) match {
|
||||
case Some(rest: List[(FlowInfo, ZLine)]) =>
|
||||
val matchedChunkToOptimize: List[ZLine] = code.take(code.length - rest.length).map(_._2)
|
||||
val optimizedChunkLengthBefore = taggedCode.length - rest.length
|
||||
val (matchedChunkToOptimize, restOfCode) = code.splitAt(optimizedChunkLengthBefore)
|
||||
val optimizedChunk: List[ZLine] = rule.result(matchedChunkToOptimize, ctx)
|
||||
val optimizedChunkWithSource =
|
||||
if (!ctx.compilationOptions.flag(CompilationFlag.LineNumbersInAssembly)) optimizedChunk
|
||||
|
@ -68,30 +79,34 @@ class RuleBasedAssemblyOptimization(val name: String, val needsFlowInfo: FlowInf
|
|||
else if (optimizedChunk.size == 1) optimizedChunk.map(_.pos(SourceLine.merge(matchedChunkToOptimize.map(_.source))))
|
||||
else if (matchedChunkToOptimize.flatMap(_.source).toSet.size == 1) optimizedChunk.map(_.pos(SourceLine.merge(matchedChunkToOptimize.map(_.source))))
|
||||
else optimizedChunk
|
||||
log.debug(s"Applied $name ($index)")
|
||||
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) {
|
||||
val before = code.head._1.statusBefore
|
||||
val after = code(matchedChunkToOptimize.length - 1)._1.importanceAfter
|
||||
if (log.traceEnabled) {
|
||||
if (log.debugEnabled) {
|
||||
log.debug(s"Applied $name ($index)")
|
||||
}
|
||||
if (log.traceEnabled) {
|
||||
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) {
|
||||
val before = head._1.statusBefore
|
||||
val after = taggedCode(matchedChunkToOptimize.length - 1)._1.importanceAfter
|
||||
log.trace(s"Before: $before")
|
||||
log.trace(s"After: $after")
|
||||
}
|
||||
}
|
||||
if (log.traceEnabled) {
|
||||
matchedChunkToOptimize.filter(_.isPrintable).foreach(l => log.trace(l.toString))
|
||||
log.trace(" ↓")
|
||||
optimizedChunkWithSource.filter(_.isPrintable).foreach(l => log.trace(l.toString))
|
||||
}
|
||||
if (needsFlowInfo != FlowInfoRequirement.NoRequirement) {
|
||||
return true -> (optimizedChunkWithSource ++ optimizeImpl(f, rest, optimizationContext)._2)
|
||||
return optimizedChunkWithSource ++ optimizeImpl(f, restOfCode, rest, optimizationContext)
|
||||
} else {
|
||||
return true -> optimize(f, optimizedChunkWithSource ++ rest.map(_._2), optimizationContext)
|
||||
return optimize(f, optimizedChunkWithSource ++ restOfCode, optimizationContext)
|
||||
}
|
||||
case None => ()
|
||||
}
|
||||
}
|
||||
val (changedTail, optimizedTail) = optimizeImpl(f, tail, optimizationContext)
|
||||
(changedTail, head._2 :: optimizedTail)
|
||||
val optimizedTail = optimizeImpl(f, code.tail, tail, optimizationContext)
|
||||
if (optimizedTail eq code.tail) {
|
||||
code
|
||||
} else {
|
||||
code.head :: optimizedTail
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -240,11 +255,11 @@ object HelperCheckers {
|
|||
case LD | LD_16 | ADD_16 | ADC_16 | SBC_16 => l.registers match {
|
||||
case TwoRegisters(MEM_HL | MEM_IX_D | MEM_IY_D | MEM_BC | MEM_DE, _) => true
|
||||
case TwoRegisters(_, MEM_HL | MEM_IX_D | MEM_IY_D | MEM_BC | MEM_DE) => true
|
||||
case TwoRegisters(_, _) => false
|
||||
case _ => false
|
||||
}
|
||||
case ADD | SUB | SBC | ADC | XOR | CP | OR | AND => l.registers match {
|
||||
case OneRegister(MEM_HL | MEM_IX_D | MEM_IY_D | MEM_BC | MEM_DE) => true
|
||||
case OneRegister(_) => false
|
||||
case _ => false
|
||||
}
|
||||
case CHANGED_MEM => true
|
||||
case POP | PUSH => false
|
||||
|
@ -288,10 +303,14 @@ object HelperCheckers {
|
|||
}
|
||||
case class AssemblyRule(pattern: AssemblyPattern, result: (List[ZLine], AssemblyMatchingContext) => List[ZLine]) extends AssemblyRuleSet {
|
||||
override def flatten: Seq[AssemblyRule] = List(this)
|
||||
|
||||
override def minimumRequiredLines: Int = pattern.minimumRequiredLines
|
||||
}
|
||||
|
||||
case class MultipleAssemblyRules(list: Seq[AssemblyRuleSet]) extends AssemblyRuleSet {
|
||||
override def flatten: Seq[AssemblyRule] = list.flatMap(_.flatten)
|
||||
|
||||
override val minimumRequiredLines: Int = flatten.map(_.minimumRequiredLines).min
|
||||
}
|
||||
|
||||
trait AssemblyPattern {
|
||||
|
@ -312,6 +331,7 @@ trait AssemblyPattern {
|
|||
|
||||
def captureLength(i: Int) = CaptureLength(i, this)
|
||||
|
||||
def minimumRequiredLines: Int
|
||||
}
|
||||
|
||||
case class Capture(i: Int, pattern: AssemblyPattern) extends AssemblyPattern {
|
||||
|
@ -324,6 +344,8 @@ case class Capture(i: Int, pattern: AssemblyPattern) extends AssemblyPattern {
|
|||
}
|
||||
|
||||
override def toString: String = s"(?<$i>$pattern)"
|
||||
|
||||
override def minimumRequiredLines: Int = pattern.minimumRequiredLines
|
||||
}
|
||||
|
||||
case class CaptureLine(i: Int, pattern: AssemblyLinePattern) extends AssemblyLinePattern {
|
||||
|
@ -335,6 +357,8 @@ case class CaptureLine(i: Int, pattern: AssemblyLinePattern) extends AssemblyLin
|
|||
}
|
||||
|
||||
override def hitRate: Double = 0.025
|
||||
|
||||
override def minimumRequiredLines: Int = 0
|
||||
}
|
||||
|
||||
case class CaptureLength(i: Int, pattern: AssemblyPattern) extends AssemblyPattern {
|
||||
|
@ -347,6 +371,8 @@ case class CaptureLength(i: Int, pattern: AssemblyPattern) extends AssemblyPatte
|
|||
}
|
||||
|
||||
override def toString: String = s"(?<$i>$pattern)"
|
||||
|
||||
override def minimumRequiredLines: Int = pattern.minimumRequiredLines
|
||||
}
|
||||
|
||||
|
||||
|
@ -356,6 +382,8 @@ case class Where(predicate: (AssemblyMatchingContext => Boolean)) extends Assemb
|
|||
}
|
||||
|
||||
override def toString: String = "Where(...)"
|
||||
|
||||
override def minimumRequiredLines: Int = 0
|
||||
}
|
||||
|
||||
case class Concatenation(l: AssemblyPattern, r: AssemblyPattern) extends AssemblyPattern {
|
||||
|
@ -378,6 +406,8 @@ case class Concatenation(l: AssemblyPattern, r: AssemblyPattern) extends Assembl
|
|||
case (_: Both, _) => s"($l) · $r"
|
||||
case _ => s"$l · $r"
|
||||
}
|
||||
|
||||
override val minimumRequiredLines: Int = l.minimumRequiredLines + r.minimumRequiredLines
|
||||
}
|
||||
|
||||
case class Many(rule: AssemblyLinePattern) extends AssemblyPattern {
|
||||
|
@ -403,6 +433,8 @@ case class Many(rule: AssemblyLinePattern) extends AssemblyPattern {
|
|||
}
|
||||
|
||||
override def toString: String = s"[$rule]*"
|
||||
|
||||
override def minimumRequiredLines: Int = 0
|
||||
}
|
||||
|
||||
case class ManyWhereAtLeastOne(rule: AssemblyLinePattern, atLeastOneIsThis: AssemblyLinePattern) extends AssemblyPattern {
|
||||
|
@ -437,6 +469,8 @@ case class ManyWhereAtLeastOne(rule: AssemblyLinePattern, atLeastOneIsThis: Asse
|
|||
}
|
||||
|
||||
override def toString: String = s"[∃$atLeastOneIsThis:$rule]*"
|
||||
|
||||
override def minimumRequiredLines: Int = rule.minimumRequiredLines
|
||||
}
|
||||
|
||||
case class Opt(rule: AssemblyLinePattern) extends AssemblyPattern {
|
||||
|
@ -459,6 +493,8 @@ case class Opt(rule: AssemblyLinePattern) extends AssemblyPattern {
|
|||
}
|
||||
|
||||
override def toString: String = s"[$rule]?"
|
||||
|
||||
override def minimumRequiredLines: Int = 0
|
||||
}
|
||||
|
||||
trait AssemblyLinePattern extends AssemblyPattern {
|
||||
|
@ -488,6 +524,8 @@ trait AssemblyLinePattern extends AssemblyPattern {
|
|||
else Both(x, this)
|
||||
|
||||
def hitRate: Double
|
||||
|
||||
override def minimumRequiredLines: Int = 1
|
||||
}
|
||||
|
||||
trait TrivialAssemblyLinePattern extends AssemblyLinePattern with (ZLine => Boolean) {
|
||||
|
@ -926,6 +964,8 @@ case object DebugMatching extends AssemblyPattern {
|
|||
code.foreach(println)
|
||||
Some(code)
|
||||
}
|
||||
|
||||
override def minimumRequiredLines: Int = 0
|
||||
}
|
||||
|
||||
case object Linear extends AssemblyLinePattern {
|
||||
|
@ -1337,6 +1377,8 @@ case class MatchElidableCopyOf(i: Int, firstLinePattern: AssemblyLinePattern, la
|
|||
}
|
||||
Some(after)
|
||||
}
|
||||
|
||||
override def minimumRequiredLines: Int = 0
|
||||
}
|
||||
|
||||
case object IsNotALabelUsedManyTimes extends AssemblyLinePattern {
|
||||
|
|
|
@ -19,6 +19,8 @@ object WordVariableToRegisterOptimization extends AssemblyOptimization[ZLine] {
|
|||
|
||||
override def name = "Allocating variables to register pairs"
|
||||
|
||||
override def minimumRequiredLines: Int = 3
|
||||
|
||||
object CyclesAndBytes {
|
||||
val Zero = CyclesAndBytes(0, 0)
|
||||
}
|
||||
|
@ -565,7 +567,7 @@ object WordVariableToRegisterOptimization extends AssemblyOptimization[ZLine] {
|
|||
} else if (de != "") {
|
||||
tailcall(inlineVars(hl, bc, de, xs)).map(ZLine.register(PUSH, DE).pos(s) :: x :: ZLine.register(POP, DE).pos(s) :: _)
|
||||
} else {
|
||||
throw new IllegalStateException()
|
||||
tailcall(inlineVars(hl, bc, de, xs)).map(x :: _)
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -22,6 +22,21 @@ object Z80OptimizationPresets {
|
|||
).flatten
|
||||
}
|
||||
|
||||
val GoodForR800: List[AssemblyOptimization[ZLine]] = {
|
||||
List.fill(5)(
|
||||
List.fill(5)(
|
||||
AlwaysGoodI80Optimizations.All ++
|
||||
AlwaysGoodZ80Optimizations.All ++
|
||||
AlwaysGoodR800Optimizations.All ++
|
||||
List(
|
||||
EmptyParameterStoreRemoval,
|
||||
EmptyMemoryStoreRemoval)
|
||||
).flatten ++
|
||||
List(ChangeRegisterPairPreferringDE, WordVariableToRegisterOptimization, ByteVariableToRegisterOptimization, ChangeRegisterPairPreferringBC, CompactStackFrame) ++
|
||||
LaterIntel8080Optimizations.All ++ LaterI80Optimizations.All
|
||||
).flatten
|
||||
}
|
||||
|
||||
val GoodForIntel8080: List[AssemblyOptimization[ZLine]] = {
|
||||
List.fill(5)(
|
||||
List.fill(5)(
|
||||
|
|
|
@ -7,6 +7,8 @@ import millfork.error.{ConsoleLogger, Logger}
|
|||
import millfork.assembly.AbstractCode
|
||||
import millfork.output.NoAlignment
|
||||
|
||||
import scala.collection.mutable.ListBuffer
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
|
@ -14,6 +16,33 @@ class AbstractExpressionCompiler[T <: AbstractCode] {
|
|||
|
||||
def getExpressionType(ctx: CompilationContext, expr: Expression): Type = AbstractExpressionCompiler.getExpressionType(ctx, expr)
|
||||
|
||||
def extractWordExpandedBytes(ctx: CompilationContext, params:List[Expression]): Option[List[Expression]] = {
|
||||
val result = ListBuffer[Expression]()
|
||||
for(param <- params) {
|
||||
if (ctx.env.eval(param).isDefined) return None
|
||||
AbstractExpressionCompiler.getExpressionType(ctx, param) match {
|
||||
case t: PlainType if t.size == 1 && !t.isSigned =>
|
||||
result += param
|
||||
case t: PlainType if t.size == 2 =>
|
||||
param match {
|
||||
case FunctionCallExpression(functionName, List(inner)) =>
|
||||
AbstractExpressionCompiler.getExpressionType(ctx, inner) match {
|
||||
case t: PlainType if t.size == 1 && !t.isSigned =>
|
||||
ctx.env.maybeGet[Type](functionName) match {
|
||||
case Some(tw: PlainType) if tw.size == 2 =>
|
||||
result += inner
|
||||
case _ => return None
|
||||
}
|
||||
case _ => return None
|
||||
}
|
||||
case _ => return None
|
||||
}
|
||||
case _ => return None
|
||||
}
|
||||
}
|
||||
Some(result.toList)
|
||||
}
|
||||
|
||||
def assertAllArithmetic(ctx: CompilationContext,expressions: List[Expression], booleanHint: String = ""): Unit = {
|
||||
for(e <- expressions) {
|
||||
val typ = getExpressionType(ctx, e)
|
||||
|
@ -227,6 +256,10 @@ object AbstractExpressionCompiler {
|
|||
def getExpressionTypeLoosely(ctx: CompilationContext, expr: Expression): Type = {
|
||||
getExpressionTypeImpl(ctx.env, ctx.log, expr, loosely = true)
|
||||
}
|
||||
@inline
|
||||
def getExpressionTypeForMacro(ctx: CompilationContext, expr: Expression): Type = {
|
||||
getExpressionTypeImpl(ctx.env, ctx.log, expr, loosely = false, failWithVoid = true)
|
||||
}
|
||||
|
||||
@inline
|
||||
def getExpressionType(env: Environment, log: Logger, expr: Expression): Type = getExpressionTypeImpl(env, log, expr, loosely = false)
|
||||
|
@ -234,7 +267,7 @@ object AbstractExpressionCompiler {
|
|||
@inline
|
||||
def getExpressionTypeLoosely(env: Environment, log: Logger, expr: Expression): Type = getExpressionTypeImpl(env, log, expr, loosely = true)
|
||||
|
||||
def getExpressionTypeImpl(env: Environment, log: Logger, expr: Expression, loosely: Boolean): Type = {
|
||||
def getExpressionTypeImpl(env: Environment, log: Logger, expr: Expression, loosely: Boolean, failWithVoid: Boolean = false): Type = {
|
||||
if (expr.typeCache ne null) return expr.typeCache
|
||||
val b = env.get[Type]("byte")
|
||||
val bool = env.get[Type]("bool$")
|
||||
|
@ -322,6 +355,11 @@ object AbstractExpressionCompiler {
|
|||
b
|
||||
}
|
||||
}
|
||||
} else if (failWithVoid) {
|
||||
env.maybeGet[TypedThing](name) match {
|
||||
case Some(t) => t.typ
|
||||
case None => VoidType
|
||||
}
|
||||
} else {
|
||||
env.get[TypedThing](name, expr.position).typ
|
||||
}
|
||||
|
@ -332,7 +370,7 @@ object AbstractExpressionCompiler {
|
|||
env.getPointy(name).elementType
|
||||
case DerefDebuggingExpression(_, 1) => b
|
||||
case DerefDebuggingExpression(_, 2) => w
|
||||
case DerefExpression(_, _, typ) => typ
|
||||
case DerefExpression(_, _, _, typ) => typ
|
||||
case IndirectFieldExpression(inner, firstIndices, fieldPath) =>
|
||||
var currentType = inner match {
|
||||
case VariableExpression(arrName) =>
|
||||
|
@ -378,12 +416,12 @@ object AbstractExpressionCompiler {
|
|||
log.error(s"Type `$targetType` doesn't have field named `$actualFieldName`", expr.position)
|
||||
ok = false
|
||||
} else {
|
||||
if (tuples.head.arrayIndexTypeAndSize.isDefined) ??? // TODO
|
||||
val subv = tuples.head
|
||||
pointerWrap match {
|
||||
case 0 =>
|
||||
currentType = tuples.head.typ
|
||||
currentType = if (subv.arrayIndexTypeAndSize.isDefined) env.get[Type]("pointer." + subv.typ) else subv.typ
|
||||
case 1 =>
|
||||
currentType = env.get[Type]("pointer." + tuples.head.typ)
|
||||
currentType = env.get[Type]("pointer." + subv.typ)
|
||||
case 2 =>
|
||||
currentType = env.get[Type]("pointer")
|
||||
case 10 | 11 =>
|
||||
|
|
|
@ -150,6 +150,7 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte
|
|||
|
||||
case _ =>
|
||||
}
|
||||
new OverflowDetector(ctx).detectOverflow(stmt)
|
||||
stmt match {
|
||||
case Assignment(ve@VariableExpression(v), arg) if trackableVars(v) =>
|
||||
cv = search(arg, cv)
|
||||
|
@ -175,7 +176,7 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte
|
|||
Assignment(DerefExpression(
|
||||
FunctionCallExpression("pointer", List(VariableExpression(target.name).pos(pos))).pos(pos) #+#
|
||||
FunctionCallExpression("<<", List(optimizeExpr(target.index, cv), LiteralExpression(1, 1))).pos(pos),
|
||||
0, env.getPointy(target.name).elementType).pos(pos), optimizeExpr(arg, cv)).pos(pos) -> cv
|
||||
0, false, env.getPointy(target.name).elementType).pos(pos), optimizeExpr(arg, cv)).pos(pos) -> cv
|
||||
case Assignment(target:IndexedExpression, arg) =>
|
||||
cv = search(arg, cv)
|
||||
cv = search(target, cv)
|
||||
|
@ -347,7 +348,7 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte
|
|||
case HalfWordExpression(arg, _) => search(arg, cv)
|
||||
case IndexedExpression(_, arg) => search(arg, cv)
|
||||
case DerefDebuggingExpression(arg, _) => search(arg, cv)
|
||||
case DerefExpression(arg, _, _) => search(arg, cv)
|
||||
case DerefExpression(arg, _, _, _) => search(arg, cv)
|
||||
case _ => cv // TODO
|
||||
}
|
||||
}
|
||||
|
@ -435,7 +436,7 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte
|
|||
false
|
||||
}
|
||||
if (zero) {
|
||||
DerefExpression(result, 0, targetType)
|
||||
DerefExpression(result, 0, false, targetType)
|
||||
} else {
|
||||
val indexType = AbstractExpressionCompiler.getExpressionType(env, env.log, index)
|
||||
val (newResult, constantOffset) = result match {
|
||||
|
@ -449,11 +450,11 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte
|
|||
if (constantOffset + (targetType.alignedSize * n) <= 127) {
|
||||
DerefExpression(
|
||||
("pointer." + targetType.name) <| newResult,
|
||||
constantOffset + targetType.alignedSize * n.toInt, targetType)
|
||||
constantOffset + targetType.alignedSize * n.toInt, false, targetType)
|
||||
} else {
|
||||
DerefExpression(
|
||||
("pointer." + targetType.name) <| result,
|
||||
targetType.alignedSize * n.toInt, targetType)
|
||||
targetType.alignedSize * n.toInt, false, targetType)
|
||||
}
|
||||
case _ =>
|
||||
val small = smallArraySizeInBytes.isDefined || (indexType.size == 1 && !indexType.isSigned)
|
||||
|
@ -468,12 +469,12 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte
|
|||
}
|
||||
DerefExpression(("pointer." + targetType.name) <| (
|
||||
("pointer" <| newResult) #+# scaledIndexWithOffset
|
||||
), 0, targetType)
|
||||
), 0, false, targetType)
|
||||
case _ =>
|
||||
val scaledIndex: Expression = scaleIndexForArrayAccess(index, targetType, smallArraySizeInBytes)
|
||||
DerefExpression(("pointer." + targetType.name) <| (
|
||||
("pointer" <| result) #+# optimizeExpr(scaledIndex, Map())
|
||||
), 0, targetType)
|
||||
), 0, false, targetType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -492,18 +493,18 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte
|
|||
case Some(NumericConstant(n, _)) if n >= 0 && (targetType.alignedSize * n) <= 127 =>
|
||||
x match {
|
||||
case _: PointerType =>
|
||||
DerefExpression(result, targetType.alignedSize * n.toInt, targetType)
|
||||
DerefExpression(result, targetType.alignedSize * n.toInt, false, targetType)
|
||||
case _ =>
|
||||
DerefExpression(
|
||||
("pointer." + targetType.name) <| result,
|
||||
targetType.alignedSize * n.toInt, targetType)
|
||||
targetType.alignedSize * n.toInt, false, targetType)
|
||||
}
|
||||
case _ =>
|
||||
val scaledIndex: Expression = scaleIndexForArrayAccess(index, targetType, arraySizeInBytes)
|
||||
// TODO: re-cast pointer type
|
||||
DerefExpression(("pointer." + targetType.name) <| (
|
||||
result #+# optimizeExpr(scaledIndex, Map())
|
||||
), 0, targetType)
|
||||
), 0, false, targetType)
|
||||
}
|
||||
case _ =>
|
||||
ctx.log.error("Not a pointer type on the left-hand side of `[`", pos)
|
||||
|
@ -519,9 +520,9 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte
|
|||
for ((dot, fieldName, indices) <- fieldPath) {
|
||||
if (dot && ok) {
|
||||
val pointer = result match {
|
||||
case DerefExpression(inner, 0, _) =>
|
||||
case DerefExpression(inner, 0, _, _) =>
|
||||
optimizeExpr(inner, currentVarValues).pos(pos)
|
||||
case DerefExpression(inner, offset, targetType) =>
|
||||
case DerefExpression(inner, offset, _, targetType) =>
|
||||
if (offset == 0) {
|
||||
("pointer." + targetType.name) <| ("pointer" <| optimizeExpr(inner, currentVarValues).pos(pos))
|
||||
} else {
|
||||
|
@ -597,7 +598,7 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte
|
|||
currentArraySizeInBytes = None
|
||||
pointerWrap match {
|
||||
case 0 =>
|
||||
DerefExpression(inner, fieldOffset, fieldType)
|
||||
DerefExpression(inner, fieldOffset, subvariable.isVolatile, fieldType)
|
||||
case 1 =>
|
||||
if (fieldOffset == 0) {
|
||||
("pointer." + fieldType.name) <| ("pointer" <| inner)
|
||||
|
@ -648,9 +649,9 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte
|
|||
}
|
||||
result
|
||||
case DerefDebuggingExpression(inner, 1) =>
|
||||
DerefExpression(optimizeExpr(inner, currentVarValues, optimizeSum = true), 0, env.get[VariableType]("byte")).pos(pos)
|
||||
DerefExpression(optimizeExpr(inner, currentVarValues, optimizeSum = true), 0, false, env.get[VariableType]("byte")).pos(pos)
|
||||
case DerefDebuggingExpression(inner, 2) =>
|
||||
DerefExpression(optimizeExpr(inner, currentVarValues, optimizeSum = true), 0, env.get[VariableType]("word")).pos(pos)
|
||||
DerefExpression(optimizeExpr(inner, currentVarValues, optimizeSum = true), 0, false, env.get[VariableType]("word")).pos(pos)
|
||||
case e@TextLiteralExpression(characters) =>
|
||||
val name = ctx.env.getTextLiteralArrayName(e)
|
||||
VariableExpression(name).pos(pos)
|
||||
|
@ -731,11 +732,11 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte
|
|||
if (pointy.isArray) {
|
||||
DerefExpression(
|
||||
"pointer" <| VariableExpression(name).pos(pos),
|
||||
o.toInt, pointy.elementType).pos(pos)
|
||||
o.toInt, false, pointy.elementType).pos(pos)
|
||||
} else {
|
||||
DerefExpression(
|
||||
VariableExpression(name).pos(pos),
|
||||
o.toInt, pointy.elementType).pos(pos)
|
||||
o.toInt, false, pointy.elementType).pos(pos)
|
||||
}
|
||||
case _ =>
|
||||
val arraySizeInBytes = pointy match {
|
||||
|
@ -769,7 +770,7 @@ abstract class AbstractStatementPreprocessor(protected val ctx: CompilationConte
|
|||
}
|
||||
DerefExpression(
|
||||
("pointer" <| VariableExpression(name).pos(pos)) #+# optimizeExpr(scaledIndex, Map()),
|
||||
0, pointy.elementType).pos(pos)
|
||||
0, false, pointy.elementType).pos(pos)
|
||||
}
|
||||
}
|
||||
case _ => expr // TODO
|
||||
|
|
|
@ -4,9 +4,12 @@ import millfork.assembly.AbstractCode
|
|||
import millfork.assembly.m6809.MOpcode
|
||||
import millfork.assembly.mos._
|
||||
import millfork.assembly.z80.ZOpcode
|
||||
import millfork.env
|
||||
import millfork.env._
|
||||
import millfork.node._
|
||||
|
||||
import scala.collection.mutable
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
|
@ -104,6 +107,7 @@ abstract class MacroExpander[T <: AbstractCode] {
|
|||
def inlineFunction(ctx: CompilationContext, i: MacroFunction, actualParams: List[Expression], position: Option[Position]): (List[T], List[ExecutableStatement]) = {
|
||||
var paramPreparation = List[T]()
|
||||
var actualCode = i.code
|
||||
var actualConstants = i.constants
|
||||
i.params match {
|
||||
case AssemblyOrMacroParamSignature(params) =>
|
||||
params.foreach{ param =>
|
||||
|
@ -133,6 +137,7 @@ abstract class MacroExpander[T <: AbstractCode] {
|
|||
ctx.log.error("Const parameters to macro functions have to be constants", expr.position)
|
||||
}
|
||||
actualCode = actualCode.map(stmt => replaceVariableX(stmt, paramVariable.name.stripPrefix(i.environment.prefix), expr))
|
||||
actualConstants = actualConstants.map(_.replaceVariableInInitialValue(paramVariable.name.stripPrefix(i.environment.prefix), expr))
|
||||
case (expr, AssemblyOrMacroParam(paramType, paramVariable, AssemblyParameterPassingBehaviour.Eval)) =>
|
||||
val castParam = FunctionCallExpression(paramType.name, List(expr))
|
||||
actualCode = actualCode.map(stmt => replaceVariableX(stmt, paramVariable.name.stripPrefix(i.environment.prefix), castParam))
|
||||
|
@ -142,6 +147,21 @@ abstract class MacroExpander[T <: AbstractCode] {
|
|||
}
|
||||
|
||||
}
|
||||
var flattenedConstants = mutable.MutableList[VariableDeclarationStatement]()
|
||||
while(actualConstants.nonEmpty) {
|
||||
val constant = actualConstants.head
|
||||
flattenedConstants += constant
|
||||
actualConstants = actualConstants.tail.map(_.replaceVariableInInitialValue(constant.name.stripPrefix(i.environment.prefix), constant.initialValue.get))
|
||||
}
|
||||
for (constant <- flattenedConstants) {
|
||||
val valueExpr = constant.initialValue.get
|
||||
ctx.env.eval(valueExpr) match {
|
||||
case Some(c) =>
|
||||
actualCode = actualCode.map(stmt => replaceVariableX(stmt, constant.name.stripPrefix(i.environment.prefix), valueExpr))
|
||||
case None =>
|
||||
ctx.log.error("Not a constant", constant.position)
|
||||
}
|
||||
}
|
||||
// fix local labels:
|
||||
// TODO: do it even if the labels are in an inline assembly block inside a Millfork function
|
||||
val localLabels = actualCode.flatMap {
|
||||
|
|
|
@ -2,11 +2,12 @@ package millfork.compiler.m6809
|
|||
|
||||
import java.util.concurrent.AbstractExecutorService
|
||||
import millfork.CompilationFlag
|
||||
import millfork.assembly.Elidability
|
||||
import millfork.assembly.m6809.{Absolute, DAccumulatorIndexed, Immediate, Indexed, InherentB, MLine, MLine0, MOpcode, RegisterSet, TwoRegisters}
|
||||
import millfork.compiler.{AbstractExpressionCompiler, BranchIfFalse, BranchIfTrue, BranchSpec, ComparisonType, CompilationContext, NoBranching}
|
||||
import millfork.node.{DerefExpression, Expression, FunctionCallExpression, GeneratedConstantExpression, IndexedExpression, LhsExpression, LiteralExpression, M6809Register, SeparateBytesExpression, SumExpression, VariableExpression}
|
||||
import millfork.assembly.m6809.MOpcode._
|
||||
import millfork.env.{AssemblyOrMacroParamSignature, BuiltInBooleanType, Constant, ConstantBooleanType, ConstantPointy, ExternFunction, FatBooleanType, FlagBooleanType, FunctionInMemory, FunctionPointerType, KernalInterruptPointerType, Label, M6809RegisterVariable, MacroFunction, MathOperator, MemoryAddressConstant, MemoryVariable, NonFatalCompilationException, NormalFunction, NormalParamSignature, NumericConstant, StackOffsetThing, StackVariable, StackVariablePointy, StructureConstant, Thing, ThingInMemory, Type, Variable, VariableInMemory, VariableLikeThing, VariablePointy, VariableType}
|
||||
import millfork.env.{AssemblyOrMacroParamSignature, BuiltInBooleanType, Constant, ConstantBooleanType, ConstantPointy, ExternFunction, FatBooleanType, FlagBooleanType, FunctionInMemory, FunctionPointerType, KernalInterruptPointerType, Label, M6809RegisterVariable, MacroFunction, MathOperator, MemoryAddressConstant, MemoryVariable, NonFatalCompilationException, NormalFunction, NormalParamSignature, NumericConstant, PlainType, StackOffsetThing, StackVariable, StackVariablePointy, StructureConstant, Thing, ThingInMemory, Type, Variable, VariableInMemory, VariableLikeThing, VariablePointy, VariableType}
|
||||
|
||||
import scala.collection.GenTraversableOnce
|
||||
|
||||
|
@ -79,10 +80,22 @@ object M6809ExpressionCompiler extends AbstractExpressionCompiler[MLine] {
|
|||
}
|
||||
env.eval(expr) match {
|
||||
case Some(c) =>
|
||||
return target match {
|
||||
return (target match {
|
||||
case MExpressionTarget.NOTHING => Nil
|
||||
case _ => List(MLine.immediate(toLd(target), c))
|
||||
}
|
||||
}) ++ (branches match {
|
||||
case BranchIfTrue(l) if c.isProvablyNonZero => List(MLine(JMP, Absolute(false), Label(l).toAddress))
|
||||
case BranchIfTrue(l) =>
|
||||
// TODO: ??
|
||||
if (target == MExpressionTarget.NOTHING) List(MLine.immediate(LDB, c), MLine.longBranch(BNE, l))
|
||||
else List(MLine.longBranch(BNE, l))
|
||||
case BranchIfFalse(l) if c.isProvablyZero => List(MLine(JMP, Absolute(false), Label(l).toAddress))
|
||||
case BranchIfFalse(l) =>
|
||||
// TODO: ??
|
||||
if (target == MExpressionTarget.NOTHING) List(MLine.immediate(LDB, c), MLine.longBranch(BEQ, l))
|
||||
else List(MLine.longBranch(BEQ, l))
|
||||
case NoBranching => Nil
|
||||
})
|
||||
case None =>
|
||||
}
|
||||
expr match {
|
||||
|
@ -117,17 +130,18 @@ object M6809ExpressionCompiler extends AbstractExpressionCompiler[MLine] {
|
|||
case MExpressionTarget.NOTHING => Nil
|
||||
case _ => List(MLine.immediate(MExpressionTarget.toLd(target), NumericConstant(c, MExpressionTarget.size(target))))
|
||||
}
|
||||
case DerefExpression(inner, offset, _) =>
|
||||
case DerefExpression(inner, offset, vol, _) =>
|
||||
val (i, o) = if (offset == 0) {
|
||||
extractConstantOffset(ctx, inner)
|
||||
} else (inner, offset)
|
||||
val el = if (vol) Elidability.Volatile else Elidability.Elidable
|
||||
compileToX(ctx, i) match {
|
||||
case List(l@MLine0(LDX, Immediate, _)) =>
|
||||
List(l.copy(opcode = toLd(target), addrMode = Absolute(false), parameter = l.parameter + o))
|
||||
List(l.copy(opcode = toLd(target), addrMode = Absolute(false), parameter = l.parameter + o, elidability = el))
|
||||
case List(l@MLine0(LDX, addrMode, _)) if addrMode.isDeferenceable && o == 0 =>
|
||||
List(l.copy(opcode = toLd(target), addrMode = addrMode.dereference()))
|
||||
List(l.copy(opcode = toLd(target), addrMode = addrMode.dereference(), elidability = el))
|
||||
case other =>
|
||||
other :+ MLine(toLd(target), Indexed(M6809Register.X, indirect = false), NumericConstant(o, 2))
|
||||
other :+ MLine(toLd(target), Indexed(M6809Register.X, indirect = false), NumericConstant(o, 2), elidability = el)
|
||||
}
|
||||
case IndexedExpression(name, index) =>
|
||||
env.getPointy(name) match {
|
||||
|
@ -290,7 +304,13 @@ object M6809ExpressionCompiler extends AbstractExpressionCompiler[MLine] {
|
|||
assertSizesForMultiplication(ctx, params, inPlace = false)
|
||||
getArithmeticParamMaxSize(ctx, params) match {
|
||||
case 1 => M6809MulDiv.compileByteMultiplication(ctx, params, updateDerefX = false) ++ targetifyB(ctx, target, isSigned = false)
|
||||
case 2 => M6809MulDiv.compileWordMultiplication(ctx, params, updateDerefX = false) ++ targetifyD(ctx, target)
|
||||
case 2 =>
|
||||
extractWordExpandedBytes(ctx, params) match {
|
||||
case Some(byteParams) if byteParams.size == 2 =>
|
||||
M6809MulDiv.compileByteMultiplication(ctx, byteParams, updateDerefX = false) ++ targetifyD(ctx, target)
|
||||
case _ =>
|
||||
M6809MulDiv.compileWordMultiplication(ctx, params, updateDerefX = false) ++ targetifyD(ctx, target)
|
||||
}
|
||||
case 0 => Nil
|
||||
case _ =>
|
||||
ctx.log.error("Multiplication of variables larger than 2 bytes is not supported", expr.position)
|
||||
|
@ -680,7 +700,20 @@ object M6809ExpressionCompiler extends AbstractExpressionCompiler[MLine] {
|
|||
case FatBooleanType => compileToB(ctx, expr)
|
||||
case t: ConstantBooleanType =>
|
||||
List(MLine.immediate(MOpcode.LDB, if (t.value) 1 else 0))
|
||||
case BuiltInBooleanType | _: FlagBooleanType =>
|
||||
case t: FlagBooleanType =>
|
||||
val condition = compile(ctx, expr, MExpressionTarget.NOTHING, NoBranching)
|
||||
condition ++ (t.jumpIfFalse.m6809 match {
|
||||
case BCC => List(MLine.immediate(LDB, 0), MLine.inherentB(ROL))
|
||||
case BCS => List(MLine.immediate(LDB, 0), MLine.inherentB(ROL), MLine.immediate(EORB, 1))
|
||||
case o =>
|
||||
val label = ctx.env.nextLabel("bo")
|
||||
List(
|
||||
MLine.immediate(LDB, 0),
|
||||
MLine.shortBranch(o, label),
|
||||
MLine.inherentB(INC),
|
||||
MLine.label(label))
|
||||
})
|
||||
case BuiltInBooleanType =>
|
||||
val label = ctx.env.nextLabel("bo")
|
||||
val condition = compile(ctx, expr, MExpressionTarget.NOTHING, BranchIfFalse(label))
|
||||
val conditionWithoutJump = condition.init
|
||||
|
@ -782,8 +815,8 @@ object M6809ExpressionCompiler extends AbstractExpressionCompiler[MLine] {
|
|||
case VariableExpression(name) =>
|
||||
val variable = ctx.env.get[Variable](name)
|
||||
List(MLine.variable(ctx, store, variable))
|
||||
case DerefExpression(inner, offset, _) =>
|
||||
stashIfNeeded(ctx, compileToX(ctx, inner)) :+ MLine.indexedX(store, NumericConstant(offset, 2))
|
||||
case DerefExpression(inner, offset, vol, _) =>
|
||||
stashIfNeeded(ctx, compileToX(ctx, inner)) :+ MLine.indexedX(store, NumericConstant(offset, 2)).copy(elidability = if(vol) Elidability.Volatile else Elidability.Elidable)
|
||||
case IndexedExpression(name, index) =>
|
||||
ctx.env.getPointy(name) match {
|
||||
case p: ConstantPointy =>
|
||||
|
@ -837,14 +870,15 @@ object M6809ExpressionCompiler extends AbstractExpressionCompiler[MLine] {
|
|||
val sh = storeA(ctx, hi)
|
||||
val sl = storeB(ctx, lo)
|
||||
stashBIfNeeded(ctx, sh) ++ sl // TODO: optimize
|
||||
case DerefExpression(inner, offset, _) =>
|
||||
case DerefExpression(inner, offset, vol, _) =>
|
||||
val el = if(vol) Elidability.Volatile else Elidability.Elidable
|
||||
compileToX(ctx, inner) match {
|
||||
case List(MLine0(LDX, Immediate, constAddr)) =>
|
||||
List(MLine(STD, Absolute(false), constAddr + offset))
|
||||
List(MLine(STD, Absolute(false), constAddr + offset, elidability = el))
|
||||
case List(MLine0(LDX, Absolute(false), constAddr)) if offset == 0 =>
|
||||
List(MLine(STD, Absolute(true), constAddr))
|
||||
List(MLine(STD, Absolute(true), constAddr, elidability = el))
|
||||
case xs =>
|
||||
stashDIfNeeded(ctx, xs) :+ MLine(STD, Indexed(M6809Register.X, indirect = false), NumericConstant(offset, 2))
|
||||
stashDIfNeeded(ctx, xs) :+ MLine(STD, Indexed(M6809Register.X, indirect = false), NumericConstant(offset, 2), elidability = el)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -893,8 +927,8 @@ object M6809ExpressionCompiler extends AbstractExpressionCompiler[MLine] {
|
|||
List(MLine.variable(ctx, LEAX, variable))
|
||||
}
|
||||
|
||||
case DerefExpression(inner, offset, _) =>
|
||||
compileToX(ctx, inner) :+ MLine.indexedX(MOpcode.LEAX, Constant(offset))
|
||||
case DerefExpression(inner, offset, vol, _) =>
|
||||
compileToX(ctx, inner) :+ MLine.indexedX(MOpcode.LEAX, Constant(offset)).copy(elidability = if(vol) Elidability.Volatile else Elidability.Elidable)
|
||||
case IndexedExpression(aname, index) =>
|
||||
ctx.env.getPointy(aname) match {
|
||||
case p: VariablePointy => compileToD(ctx, index #*# p.elementType.alignedSize) ++ List(MLine.absolute(ADDD, p.addr), MLine.tfr(M6809Register.D, M6809Register.X))
|
||||
|
@ -1046,9 +1080,10 @@ object M6809ExpressionCompiler extends AbstractExpressionCompiler[MLine] {
|
|||
List(calculateStackAddressToD(ctx, sot.offset), List(MLine.tfr(M6809Register.A, M6809Register.B)))
|
||||
}
|
||||
case e:DerefExpression =>
|
||||
val el = if(e.isVolatile) Elidability.Volatile else Elidability.Elidable
|
||||
List.tabulate(targetSize)(i =>
|
||||
if (i == 0) compileAddressToX(ctx, e) :+ MLine.indexedX(LDB, 0)
|
||||
else List(MLine.indexedX(LDB, i))
|
||||
if (i == 0) compileAddressToX(ctx, e) :+ MLine.indexedX(LDB, 0).copy(elidability = el)
|
||||
else List(MLine.indexedX(LDB, i).copy(elidability = el))
|
||||
)
|
||||
case e:FunctionCallExpression =>
|
||||
ctx.env.maybeGet[NormalFunction](e.functionName) match {
|
||||
|
|
|
@ -15,9 +15,9 @@ import scala.collection.mutable.ListBuffer
|
|||
*/
|
||||
object M6809MulDiv {
|
||||
|
||||
def compileByteMultiplication(ctx: CompilationContext, params: List[Expression], updateDerefX: Boolean): List[MLine] = {
|
||||
def compileByteMultiplication(ctx: CompilationContext, params: List[Expression], updateDerefX: Boolean, forceMul: Boolean = false): List[MLine] = {
|
||||
var constant = Constant.One
|
||||
val variablePart = params.flatMap { p =>
|
||||
val variablePart = if(forceMul) params else params.flatMap { p =>
|
||||
ctx.env.eval(p) match {
|
||||
case Some(c) =>
|
||||
constant = CompoundConstant(MathOperator.Times, constant, c).quickSimplify
|
||||
|
|
|
@ -74,11 +74,17 @@ object BuiltIns {
|
|||
case FunctionCallExpression(name, List(param)) if env.maybeGet[Type](name).isDefined =>
|
||||
return simpleOperation(opcode, ctx, param, indexChoice, preserveA, commutative, decimal)
|
||||
case _: FunctionCallExpression | _: SumExpression if commutative =>
|
||||
val code = MosExpressionCompiler.compileToA(ctx, source)
|
||||
if (ctx.options.flags(CompilationFlag.IdentityPage)
|
||||
&& !code.exists(_.concernsX)
|
||||
&& AssemblyLine.treatment(code, State.X) == Treatment.Unchanged) {
|
||||
return List(AssemblyLine.implied(TAX)) ++ code ++ wrapInSedCldIfNeeded(decimal, List(AssemblyLine.absoluteX(opcode, env.identityPage)))
|
||||
}
|
||||
// TODO: is it ok?
|
||||
if (ctx.options.zpRegisterSize >= 1) {
|
||||
val reg = ctx.env.get[ThingInMemory]("__reg")
|
||||
return List(AssemblyLine.implied(PHA)) ++
|
||||
MosExpressionCompiler.compileToA(ctx, source) ++
|
||||
MosExpressionCompiler.fixTsx(code) ++
|
||||
List(AssemblyLine.zeropage(STA, reg), AssemblyLine.implied(PLA)) ++
|
||||
wrapInSedCldIfNeeded(decimal, List(AssemblyLine.zeropage(opcode, reg)))
|
||||
} else if (ctx.options.flag(CompilationFlag.EmitEmulation65816Opcodes)) {
|
||||
|
@ -336,6 +342,7 @@ object BuiltIns {
|
|||
val reg = ctx.env.get[VariableInMemory]("__reg")
|
||||
lhs match {
|
||||
case dx: DerefExpression =>
|
||||
val el = if(dx.isVolatile) Elidability.Volatile else Elidability.Elidable
|
||||
if (ctx.options.zpRegisterSize < 4) {
|
||||
ctx.log.error("Unsupported shift operation. Consider increasing the size of the zeropage register or simplifying the left hand side expression.", lhs.position)
|
||||
return MosExpressionCompiler.compileToAX(ctx, lhs) ++ MosExpressionCompiler.compileToAX(ctx, rhs)
|
||||
|
@ -348,19 +355,19 @@ object BuiltIns {
|
|||
val loadToR2 =
|
||||
List(
|
||||
AssemblyLine.immediate(LDY, offset),
|
||||
AssemblyLine.indexedY(LDA, ptr),
|
||||
AssemblyLine.indexedY(LDA, ptr).copy(elidability = el),
|
||||
AssemblyLine.zeropage(STA, reg, 2),
|
||||
AssemblyLine.implied(INY),
|
||||
AssemblyLine.indexedY(LDA, ptr),
|
||||
AssemblyLine.indexedY(LDA, ptr).copy(elidability = el),
|
||||
AssemblyLine.zeropage(STA, reg, 3))
|
||||
val storeFromR2 =
|
||||
List(
|
||||
AssemblyLine.immediate(LDY, offset),
|
||||
AssemblyLine.zeropage(LDA, reg, 2),
|
||||
AssemblyLine.indexedY(STA, ptr),
|
||||
AssemblyLine.indexedY(STA, ptr).copy(elidability = el),
|
||||
AssemblyLine.implied(INY),
|
||||
AssemblyLine.zeropage(LDA, reg, 3),
|
||||
AssemblyLine.indexedY(STA, ptr))
|
||||
AssemblyLine.indexedY(STA, ptr).copy(elidability = el))
|
||||
val shiftR2 =
|
||||
if (aslRatherThanLsr) List(AssemblyLine.zeropage(ASL, reg, 2), AssemblyLine.zeropage(ROL, reg, 3))
|
||||
else List(AssemblyLine.zeropage(LSR, reg, 3), AssemblyLine.zeropage(ROR, reg, 2))
|
||||
|
@ -369,23 +376,23 @@ object BuiltIns {
|
|||
case Some(1) if aslRatherThanLsr =>
|
||||
List(
|
||||
AssemblyLine.immediate(LDY, offset),
|
||||
AssemblyLine.indexedY(LDA, ptr),
|
||||
AssemblyLine.indexedY(LDA, ptr).copy(elidability = el),
|
||||
AssemblyLine.implied(ASL),
|
||||
AssemblyLine.indexedY(STA, ptr),
|
||||
AssemblyLine.indexedY(STA, ptr).copy(elidability = el),
|
||||
AssemblyLine.implied(INY),
|
||||
AssemblyLine.indexedY(LDA, ptr),
|
||||
AssemblyLine.indexedY(LDA, ptr).copy(elidability = el),
|
||||
AssemblyLine.implied(ROL),
|
||||
AssemblyLine.indexedY(STA, ptr))
|
||||
AssemblyLine.indexedY(STA, ptr).copy(elidability = el))
|
||||
case Some(1) if !aslRatherThanLsr =>
|
||||
List(
|
||||
AssemblyLine.immediate(LDY, offset + 1),
|
||||
AssemblyLine.indexedY(LDA, ptr),
|
||||
AssemblyLine.indexedY(LDA, ptr).copy(elidability = el),
|
||||
AssemblyLine.implied(LSR),
|
||||
AssemblyLine.indexedY(STA, ptr),
|
||||
AssemblyLine.indexedY(STA, ptr).copy(elidability = el),
|
||||
AssemblyLine.implied(DEY),
|
||||
AssemblyLine.indexedY(LDA, ptr),
|
||||
AssemblyLine.indexedY(LDA, ptr).copy(elidability = el),
|
||||
AssemblyLine.implied(ROR),
|
||||
AssemblyLine.indexedY(STA, ptr))
|
||||
AssemblyLine.indexedY(STA, ptr).copy(elidability = el))
|
||||
case Some(n) if n >= 1 && n <= 7 => // TODO: pick optimal
|
||||
loadToR2 ++ List.fill(n)(shiftR2).flatten ++ storeFromR2
|
||||
case _ =>
|
||||
|
@ -412,9 +419,9 @@ object BuiltIns {
|
|||
AssemblyLine.implied(CLC),
|
||||
AssemblyLine.immediate(LDY, 0),
|
||||
AssemblyLine.label(innerLoopLabel),
|
||||
AssemblyLine.indexedY(LDA, reg),
|
||||
AssemblyLine.indexedY(LDA, reg).copy(elidability = el),
|
||||
AssemblyLine.implied(ROL),
|
||||
AssemblyLine.indexedY(STA, reg),
|
||||
AssemblyLine.indexedY(STA, reg).copy(elidability = el),
|
||||
AssemblyLine.implied(INY),
|
||||
AssemblyLine.immediate(CPY, size),
|
||||
AssemblyLine.relative(BNE, innerLoopLabel))
|
||||
|
@ -423,9 +430,9 @@ object BuiltIns {
|
|||
AssemblyLine.implied(CLC),
|
||||
AssemblyLine.immediate(LDY, size - 1),
|
||||
AssemblyLine.label(innerLoopLabel),
|
||||
AssemblyLine.indexedY(LDA, reg),
|
||||
AssemblyLine.indexedY(LDA, reg).copy(elidability = el),
|
||||
AssemblyLine.implied(ROR),
|
||||
AssemblyLine.indexedY(STA, reg),
|
||||
AssemblyLine.indexedY(STA, reg).copy(elidability = el),
|
||||
AssemblyLine.implied(DEY),
|
||||
AssemblyLine.relative(BPL, innerLoopLabel))
|
||||
}
|
||||
|
@ -436,7 +443,7 @@ object BuiltIns {
|
|||
AssemblyLine.immediate(LDY, size - 1),
|
||||
AssemblyLine.label(innerLoopLabel),
|
||||
AssemblyLine.immediate(LDA, 0),
|
||||
AssemblyLine.indexedY(STA, reg),
|
||||
AssemblyLine.indexedY(STA, reg).copy(elidability = el),
|
||||
AssemblyLine.implied(DEY),
|
||||
AssemblyLine.relative(BPL, innerLoopLabel))
|
||||
case Some(1) => singleShift
|
||||
|
@ -1195,6 +1202,7 @@ object BuiltIns {
|
|||
def compileInPlaceWordMultiplication(ctx: CompilationContext, v: LhsExpression, addend: Expression): List[AssemblyLine] = {
|
||||
v match {
|
||||
case dx: DerefExpression =>
|
||||
val el = if(dx.isVolatile) Elidability.Volatile else Elidability.Elidable
|
||||
// this is ugly, needs a rewrite
|
||||
return handleWordOrLongInPlaceModificationViaDeref(ctx, dx, addend, fromMsb = false){(ptr, reg, offset, r) =>
|
||||
val constR = r match {
|
||||
|
@ -1210,27 +1218,27 @@ object BuiltIns {
|
|||
case Some(0) => List(
|
||||
AssemblyLine.immediate(LDA, 0),
|
||||
AssemblyLine.immediate(LDY, offset),
|
||||
AssemblyLine.indexedY(STA, ptr),
|
||||
AssemblyLine.indexedY(STA, ptr).copy(elidability = el),
|
||||
AssemblyLine.implied(INY),
|
||||
AssemblyLine.indexedY(STA, ptr))
|
||||
AssemblyLine.indexedY(STA, ptr).copy(elidability = el))
|
||||
case Some(2) => List(
|
||||
AssemblyLine.immediate(LDA, 0),
|
||||
AssemblyLine.immediate(LDY, offset),
|
||||
AssemblyLine.indexedY(LDA, ptr),
|
||||
AssemblyLine.indexedY(LDA, ptr).copy(elidability = el),
|
||||
AssemblyLine.implied(ASL),
|
||||
AssemblyLine.indexedY(STA, ptr),
|
||||
AssemblyLine.indexedY(STA, ptr).copy(elidability = el),
|
||||
AssemblyLine.implied(INY),
|
||||
AssemblyLine.indexedY(LDA, ptr),
|
||||
AssemblyLine.indexedY(LDA, ptr).copy(elidability = el),
|
||||
AssemblyLine.implied(ROL),
|
||||
AssemblyLine.indexedY(STA, ptr))
|
||||
AssemblyLine.indexedY(STA, ptr).copy(elidability = el))
|
||||
// TODO: other powers of two
|
||||
case _ if reg.toAddress == ptr => List(
|
||||
AssemblyLine.implied(PHA),
|
||||
AssemblyLine.immediate(LDY, offset),
|
||||
AssemblyLine.indexedY(LDA, reg),
|
||||
AssemblyLine.indexedY(LDA, reg).copy(elidability = el),
|
||||
AssemblyLine.zeropage(STA, reg, 2),
|
||||
AssemblyLine.implied(INY),
|
||||
AssemblyLine.indexedY(LDA, reg),
|
||||
AssemblyLine.indexedY(LDA, reg).copy(elidability = el),
|
||||
AssemblyLine.zeropage(STA, reg, 3),
|
||||
AssemblyLine.implied(PLA),
|
||||
AssemblyLine.implied(TAY),
|
||||
|
@ -1248,26 +1256,26 @@ object BuiltIns {
|
|||
AssemblyLine.zeropage(STA, reg, 1),
|
||||
AssemblyLine.immediate(LDY, offset),
|
||||
AssemblyLine.zeropage(LDA, reg, 2),
|
||||
AssemblyLine.indexedY(STA, reg),
|
||||
AssemblyLine.indexedY(STA, reg).copy(elidability = el),
|
||||
AssemblyLine.implied(INY),
|
||||
AssemblyLine.implied(TXA),
|
||||
AssemblyLine.indexedY(STA, reg),
|
||||
AssemblyLine.indexedY(STA, reg).copy(elidability = el),
|
||||
)
|
||||
case _ => List(
|
||||
AssemblyLine.zeropage(STA, reg),
|
||||
AssemblyLine.zeropage(STX, reg, 1),
|
||||
AssemblyLine.immediate(LDY, offset),
|
||||
AssemblyLine.indexedY(LDA, ptr),
|
||||
AssemblyLine.indexedY(LDA, ptr).copy(elidability = el),
|
||||
AssemblyLine.zeropage(STA, reg, 2),
|
||||
AssemblyLine.implied(INY),
|
||||
AssemblyLine.indexedY(LDA, ptr),
|
||||
AssemblyLine.indexedY(LDA, ptr).copy(elidability = el),
|
||||
AssemblyLine.zeropage(STA, reg, 3),
|
||||
AssemblyLine.absolute(JSR, ctx.env.get[ThingInMemory]("__mul_u16u16u16")),
|
||||
AssemblyLine.immediate(LDY, offset),
|
||||
AssemblyLine.indexedY(STA, ptr),
|
||||
AssemblyLine.indexedY(STA, ptr).copy(elidability = el),
|
||||
AssemblyLine.implied(INY),
|
||||
AssemblyLine.implied(TXA),
|
||||
AssemblyLine.indexedY(STA, ptr),
|
||||
AssemblyLine.indexedY(STA, ptr).copy(elidability = el),
|
||||
)
|
||||
}
|
||||
}(Left({size => ???}))
|
||||
|
@ -1585,6 +1593,7 @@ object BuiltIns {
|
|||
}
|
||||
lhs match {
|
||||
case dx: DerefExpression =>
|
||||
val el = if(dx.isVolatile) Elidability.Volatile else Elidability.Elidable
|
||||
if (subtract && ctx.options.zpRegisterSize < 3) {
|
||||
ctx.log.error("Too complex left hand side. Consider increasing the size of the zeropage register.", lhs.position)
|
||||
return compileInPlaceWordOrLongAddition(ctx, lhs, addend, subtract = false, decimal = false)
|
||||
|
@ -1593,14 +1602,14 @@ object BuiltIns {
|
|||
AssemblyLine.immediate(LDY, offset),
|
||||
AssemblyLine.implied(SEC),
|
||||
AssemblyLine.zeropage(STA, reg, 2),
|
||||
AssemblyLine.indexedY(LDA, ptr),
|
||||
AssemblyLine.indexedY(LDA, ptr).copy(elidability = el),
|
||||
AssemblyLine.zeropage(SBC, reg, 2),
|
||||
AssemblyLine.indexedY(STA, ptr),
|
||||
AssemblyLine.indexedY(STA, ptr).copy(elidability = el),
|
||||
AssemblyLine.implied(INY),
|
||||
AssemblyLine.zeropage(STX, reg, 2),
|
||||
AssemblyLine.indexedY(LDA, ptr),
|
||||
AssemblyLine.indexedY(LDA, ptr).copy(elidability = el),
|
||||
AssemblyLine.zeropage(SBC, reg, 2),
|
||||
AssemblyLine.indexedY(STA, ptr),
|
||||
AssemblyLine.indexedY(STA, ptr).copy(elidability = el),
|
||||
)))(Right({(iy, r, last) =>
|
||||
val reg = ctx.env.get[VariableInMemory]("__reg")
|
||||
val result = ListBuffer[AssemblyLine]()
|
||||
|
@ -1611,7 +1620,7 @@ object BuiltIns {
|
|||
}
|
||||
result += AssemblyLine.immediate(LDY, iy)
|
||||
result += AssemblyLine.zeropage(STA, reg, 2)
|
||||
result += AssemblyLine.indexedY(LDA, reg)
|
||||
result += AssemblyLine.indexedY(LDA, reg).copy(elidability = el)
|
||||
if (iy == 0) {
|
||||
result += AssemblyLine.implied(SEC)
|
||||
}
|
||||
|
@ -1627,12 +1636,12 @@ object BuiltIns {
|
|||
})) else handleWordOrLongInPlaceModificationViaDeref(ctx, dx, addend, fromMsb = false)((ptr, _, offset, _) => wrapInSedCldIfNeeded(decimal, List(
|
||||
AssemblyLine.immediate(LDY, offset),
|
||||
AssemblyLine.implied(CLC),
|
||||
AssemblyLine.indexedY(ADC, ptr),
|
||||
AssemblyLine.indexedY(STA, ptr),
|
||||
AssemblyLine.indexedY(ADC, ptr).copy(elidability = el),
|
||||
AssemblyLine.indexedY(STA, ptr).copy(elidability = el),
|
||||
AssemblyLine.implied(TXA),
|
||||
AssemblyLine.implied(INY),
|
||||
AssemblyLine.indexedY(ADC, ptr),
|
||||
AssemblyLine.indexedY(STA, ptr),
|
||||
AssemblyLine.indexedY(ADC, ptr).copy(elidability = el),
|
||||
AssemblyLine.indexedY(STA, ptr).copy(elidability = el),
|
||||
)))(Right({(iy, r, last) =>
|
||||
val reg = ctx.env.get[VariableInMemory]("__reg")
|
||||
val result = ListBuffer[AssemblyLine]()
|
||||
|
@ -2004,14 +2013,16 @@ object BuiltIns {
|
|||
|
||||
def compileInPlaceWordOrLongBitOp(ctx: CompilationContext, lhs: LhsExpression, param: Expression, operation: Opcode.Value): List[AssemblyLine] = {
|
||||
lhs match {
|
||||
case dx: DerefExpression => return handleWordOrLongInPlaceModificationViaDeref(ctx, dx, param, fromMsb = false)((ptr, _, offset, _) => List(
|
||||
case dx: DerefExpression =>
|
||||
val el = if(dx.isVolatile) Elidability.Volatile else Elidability.Elidable
|
||||
return handleWordOrLongInPlaceModificationViaDeref(ctx, dx, param, fromMsb = false)((ptr, _, offset, _) => List(
|
||||
AssemblyLine.immediate(LDY, offset),
|
||||
AssemblyLine.indexedY(operation, ptr),
|
||||
AssemblyLine.indexedY(STA, ptr),
|
||||
AssemblyLine.indexedY(operation, ptr).copy(elidability = el),
|
||||
AssemblyLine.indexedY(STA, ptr).copy(elidability = el),
|
||||
AssemblyLine.implied(TXA),
|
||||
AssemblyLine.implied(INY),
|
||||
AssemblyLine.indexedY(operation, ptr),
|
||||
AssemblyLine.indexedY(STA, ptr),
|
||||
AssemblyLine.indexedY(operation, ptr).copy(elidability = el),
|
||||
AssemblyLine.indexedY(STA, ptr).copy(elidability = el),
|
||||
))(Right({ (iy, r, last) =>
|
||||
val reg = ctx.env.get[VariableInMemory]("__reg")
|
||||
r ++ List(
|
||||
|
|
|
@ -72,6 +72,7 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
|||
case 2 => if (ctx.options.flag(CompilationFlag.EmitNative65816Opcodes)) {
|
||||
AssemblyLine.accu16 :: AssemblyLine(LDA_W, WordImmediate, expr) :: (AssemblyLine.variable(ctx, STA_W, m) :+ AssemblyLine.accu8)
|
||||
} else AssemblyLine.tsx(ctx) ++ List(
|
||||
// TODO: ???
|
||||
AssemblyLine.implied(TSX),
|
||||
AssemblyLine.immediate(LDA, expr.loByte),
|
||||
AssemblyLine.dataStackX(ctx, STA, offset),
|
||||
|
@ -155,10 +156,10 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
|||
}
|
||||
|
||||
val cmos = ctx.options.flag(CompilationFlag.EmitCmosOpcodes)
|
||||
if (register == MosRegister.AX && !code.exists(_.concernsX)) {
|
||||
if (register == MosRegister.AX && !code.exists(_.concernsX) && code.forall(_.treatment(State.X).==(Treatment.Unchanged))) {
|
||||
return preserveRegisterIfNeeded(ctx, MosRegister.A, code)
|
||||
}
|
||||
if (register == MosRegister.AY && !code.exists(_.concernsY)) {
|
||||
if (register == MosRegister.AY && !code.exists(_.concernsY) && code.forall(_.treatment(State.Y).==(Treatment.Unchanged))) {
|
||||
return preserveRegisterIfNeeded(ctx, MosRegister.A, code)
|
||||
}
|
||||
if (states.exists(state => AssemblyLine.treatment(code, state) != Treatment.Unchanged)) {
|
||||
|
@ -431,21 +432,22 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
|||
ctx.log.error("Invalid index for writing", indexExpr.position)
|
||||
Nil
|
||||
}
|
||||
case DerefExpression(inner, offset, targetType) =>
|
||||
case DerefExpression(inner, offset, isVolatile, targetType) =>
|
||||
val el = if(isVolatile) Elidability.Volatile else Elidability.Elidable
|
||||
val (prepare, addr, am, fast) = getPhysicalPointerForDeref(ctx, inner)
|
||||
if (targetType.size == 1) {
|
||||
fast match {
|
||||
case Some((fastBase, fastAddrMode, fastIndex)) =>
|
||||
return preserveRegisterIfNeeded(ctx, MosRegister.A, fastIndex(offset)) ++ List(AssemblyLine(STA, fastAddrMode, fastBase))
|
||||
return preserveRegisterIfNeeded(ctx, MosRegister.A, fastIndex(offset)) ++ List(AssemblyLine(STA, fastAddrMode, fastBase, elidability = el))
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
val lo = preserveRegisterIfNeeded(ctx, MosRegister.A, prepare) ++ List(AssemblyLine.immediate(LDY, offset), AssemblyLine(STA, am, addr))
|
||||
val lo = preserveRegisterIfNeeded(ctx, MosRegister.A, prepare) ++ List(AssemblyLine.immediate(LDY, offset), AssemblyLine(STA, am, addr, elidability = el))
|
||||
if (targetType.size == 1) {
|
||||
lo
|
||||
} else {
|
||||
lo ++ List(AssemblyLine.immediate(LDA, 0)) ++
|
||||
List.tabulate(targetType.size - 1)(i => List(AssemblyLine.implied(INY), AssemblyLine(STA, am, addr))).flatten
|
||||
List.tabulate(targetType.size - 1)(i => List(AssemblyLine.implied(INY), AssemblyLine(STA, am, addr, elidability = el))).flatten
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -475,7 +477,20 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
|||
compileToA(ctx, expr)
|
||||
case t: ConstantBooleanType =>
|
||||
List(AssemblyLine.immediate(LDA, if (t.value) 1 else 0))
|
||||
case _: FlagBooleanType | BuiltInBooleanType =>
|
||||
case t: FlagBooleanType =>
|
||||
val condition = compile(ctx, expr, None, NoBranching)
|
||||
condition ++ (t.jumpIfFalse.mosOpcode match {
|
||||
case BCC => List(AssemblyLine.immediate(LDA, 0), AssemblyLine.implied(ROL))
|
||||
case BCS => List(AssemblyLine.immediate(LDA, 0), AssemblyLine.implied(ROL), AssemblyLine.immediate(EOR, 1))
|
||||
case o =>
|
||||
val label = env.nextLabel("bo")
|
||||
List(
|
||||
AssemblyLine.immediate(LDA, 0),
|
||||
AssemblyLine.relative(o, label),
|
||||
AssemblyLine.immediate(LDA, 1),
|
||||
AssemblyLine.label(label))
|
||||
})
|
||||
case BuiltInBooleanType =>
|
||||
val label = env.nextLabel("bo")
|
||||
val condition = compile(ctx, expr, None, BranchIfFalse(label))
|
||||
if (condition.isEmpty) {
|
||||
|
@ -563,13 +578,13 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
|||
def getPhysicalPointerForDeref(ctx: CompilationContext, pointerExpression: Expression): (List[AssemblyLine], Constant, AddrMode.Value, Option[(Constant, AddrMode.Value, Int => List[AssemblyLine])]) = {
|
||||
pointerExpression match {
|
||||
case VariableExpression(name) =>
|
||||
val p = ctx.env.get[ThingInMemory](name)
|
||||
val p = ctx.env.maybeGet[ThingInMemory](name)
|
||||
p match {
|
||||
case array: MfArray => return (Nil, p.toAddress, AddrMode.AbsoluteY,
|
||||
if (array.sizeInBytes <= 256) Some((p.toAddress, AbsoluteY, (i: Int) => List(AssemblyLine.immediate(LDY, i)))) else None)
|
||||
case Some(array: MfArray) => return (Nil, array.toAddress, AddrMode.AbsoluteY,
|
||||
if (array.sizeInBytes <= 256) Some((array.toAddress, AbsoluteY, (i: Int) => List(AssemblyLine.immediate(LDY, i)))) else None)
|
||||
case Some(q) => if (q.zeropage) return (Nil, q.toAddress, AddrMode.IndexedY, None)
|
||||
case _ =>
|
||||
}
|
||||
if (p.zeropage) return (Nil, p.toAddress, AddrMode.IndexedY, None)
|
||||
case _ =>
|
||||
}
|
||||
ctx.env.eval(pointerExpression) match {
|
||||
|
@ -658,7 +673,11 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
|||
}
|
||||
case RegisterVariable(MosRegister.Y, _) => actualOffset match {
|
||||
case 0 => List(tsx)
|
||||
case 1 => List(tsx, AssemblyLine.implied(TXA), AssemblyLine.implied(TAY), AssemblyLine.implied(INY))
|
||||
case 1 =>
|
||||
if (ctx.options.flag(CompilationFlag.IdentityPage))
|
||||
List(tsx, AssemblyLine.absoluteX(LDY, ctx.env.identityPage), AssemblyLine.implied(INY))
|
||||
else
|
||||
List(tsx, AssemblyLine.implied(TXA), AssemblyLine.implied(TAY), AssemblyLine.implied(INY))
|
||||
case _ => List(tsx, AssemblyLine.implied(TXA), AssemblyLine.implied(CLC), AssemblyLine.immediate(ADC, actualOffset), AssemblyLine.implied(TAY))
|
||||
}
|
||||
case _ =>
|
||||
|
@ -1145,43 +1164,44 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
|||
result ++ List(AssemblyLine.implied(PHA), AssemblyLine.implied(XBA), AssemblyLine.implied(PLA)) ++ signExtendA(ctx) ++ List(AssemblyLine.implied(XBA))
|
||||
}
|
||||
}
|
||||
case DerefExpression(inner, offset, targetType) =>
|
||||
case DerefExpression(inner, offset, isVolatile, targetType) =>
|
||||
val el = if(isVolatile) Elidability.Volatile else Elidability.Elidable
|
||||
val (prepare, addr, am, fast) = getPhysicalPointerForDeref(ctx, inner)
|
||||
(fast, targetType.size, am) match {
|
||||
case (Some((fastBase, fastAddrMode, fastIndex)), 1, _) =>
|
||||
fastIndex(offset) ++ List(AssemblyLine(LDA, fastAddrMode, fastBase)) ++ expressionStorageFromA(ctx, exprTypeAndVariable, expr.position, exprType.isSigned)
|
||||
fastIndex(offset) ++ List(AssemblyLine(LDA, fastAddrMode, fastBase, elidability = el)) ++ expressionStorageFromA(ctx, exprTypeAndVariable, expr.position, exprType.isSigned)
|
||||
case (_, 1, AbsoluteY) =>
|
||||
prepare ++ List(AssemblyLine.absolute(LDA, addr + offset)) ++ expressionStorageFromA(ctx, exprTypeAndVariable, expr.position, exprType.isSigned)
|
||||
prepare ++ List(AssemblyLine.absolute(LDA, addr + offset).copy(elidability = el)) ++ expressionStorageFromA(ctx, exprTypeAndVariable, expr.position, exprType.isSigned)
|
||||
case (_, 1, _) =>
|
||||
prepare ++ List(AssemblyLine.immediate(LDY, offset), AssemblyLine(LDA, am, addr)) ++ expressionStorageFromA(ctx, exprTypeAndVariable, expr.position, exprType.isSigned)
|
||||
prepare ++ List(AssemblyLine.immediate(LDY, offset), AssemblyLine(LDA, am, addr, elidability = el)) ++ expressionStorageFromA(ctx, exprTypeAndVariable, expr.position, exprType.isSigned)
|
||||
case (Some((fastBase, AbsoluteY, fastIndex)), 2, _) =>
|
||||
fastIndex(offset) ++ List(
|
||||
AssemblyLine.absoluteY(LDA, fastBase),
|
||||
AssemblyLine.absoluteY(LDA, fastBase).copy(elidability = el),
|
||||
AssemblyLine.implied(INY),
|
||||
AssemblyLine.absoluteY(LDX, fastBase)) ++
|
||||
AssemblyLine.absoluteY(LDX, fastBase).copy(elidability = el)) ++
|
||||
expressionStorageFromAX(ctx, exprTypeAndVariable, expr.position)
|
||||
case (Some((fastBase, fastAddrMode, fastIndex)), 2, _) =>
|
||||
fastIndex(offset) ++ List(
|
||||
AssemblyLine.implied(INY),
|
||||
AssemblyLine(LDA, fastAddrMode, fastBase),
|
||||
AssemblyLine(LDA, fastAddrMode, fastBase).copy(elidability = el),
|
||||
AssemblyLine.implied(TAX),
|
||||
AssemblyLine.implied(DEY),
|
||||
AssemblyLine(LDA, fastAddrMode, fastBase)) ++
|
||||
AssemblyLine(LDA, fastAddrMode, fastBase).copy(elidability = el)) ++
|
||||
expressionStorageFromAX(ctx, exprTypeAndVariable, expr.position)
|
||||
case (_, 2, AbsoluteY) =>
|
||||
prepare ++
|
||||
List(
|
||||
AssemblyLine.absolute(LDA, addr + offset),
|
||||
AssemblyLine.absolute(LDX, addr + offset + 1)) ++
|
||||
AssemblyLine.absolute(LDA, addr + offset).copy(elidability = el),
|
||||
AssemblyLine.absolute(LDX, addr + offset + 1).copy(elidability = el)) ++
|
||||
expressionStorageFromAX(ctx, exprTypeAndVariable, expr.position)
|
||||
case (_, 2, _) =>
|
||||
prepare ++
|
||||
List(
|
||||
AssemblyLine.immediate(LDY, offset+1),
|
||||
AssemblyLine(LDA, am, addr),
|
||||
AssemblyLine(LDA, am, addr, elidability = el),
|
||||
AssemblyLine.implied(TAX),
|
||||
AssemblyLine.implied(DEY),
|
||||
AssemblyLine(LDA, am, addr)) ++
|
||||
AssemblyLine(LDA, am, addr, elidability = el)) ++
|
||||
expressionStorageFromAX(ctx, exprTypeAndVariable, expr.position)
|
||||
case _ =>
|
||||
exprTypeAndVariable match {
|
||||
|
@ -2229,7 +2249,8 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
|||
case SeparateBytesExpression(_, _) =>
|
||||
ctx.log.error("Invalid left-hand-side use of `:`")
|
||||
Nil
|
||||
case DerefExpression(inner, offset, targetType) =>
|
||||
case DerefExpression(inner, offset, isVolatile, targetType) =>
|
||||
val el = if(isVolatile) Elidability.Volatile else Elidability.Elidable
|
||||
val (prepare, addr, am, fastTarget) = getPhysicalPointerForDeref(ctx, inner)
|
||||
env.eval(source) match {
|
||||
case Some(constant) =>
|
||||
|
@ -2237,7 +2258,7 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
|||
case AbsoluteY =>
|
||||
prepare ++ (0 until targetType.size).flatMap(i => List(
|
||||
AssemblyLine.immediate(LDA, constant.subbyte(i)),
|
||||
AssemblyLine.absolute(STA, addr + offset + i)))
|
||||
AssemblyLine.absolute(STA, addr + offset + i).copy(elidability = el)))
|
||||
case _ =>
|
||||
fastTarget match {
|
||||
case Some((constAddr, fastAddrMode, initializeY)) =>
|
||||
|
@ -2245,13 +2266,13 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
|||
val load = List(AssemblyLine.immediate(LDA, constant.subbyte(i)))
|
||||
load ++ (if (i == 0) List(AssemblyLine(STA, fastAddrMode, constAddr)) else List(
|
||||
AssemblyLine.implied(INY),
|
||||
AssemblyLine(STA, fastAddrMode, constAddr)))
|
||||
AssemblyLine(STA, fastAddrMode, constAddr, elidability = el)))
|
||||
}
|
||||
case _ =>
|
||||
prepare ++ (0 until targetType.size).flatMap(i => List(
|
||||
if (i == 0) AssemblyLine.immediate(LDY, offset) else AssemblyLine.implied(INY),
|
||||
AssemblyLine.immediate(LDA, constant.subbyte(i)),
|
||||
AssemblyLine(STA, am, addr)))
|
||||
AssemblyLine(STA, am, addr, elidability = el)))
|
||||
}
|
||||
}
|
||||
case None =>
|
||||
|
@ -2262,7 +2283,7 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
|||
case (1, AbsoluteY) =>
|
||||
prepare ++
|
||||
AssemblyLine.variable(ctx, LDA, variable) :+
|
||||
AssemblyLine.absolute(STA, addr + offset)
|
||||
AssemblyLine.absolute(STA, addr + offset).copy(elidability = el)
|
||||
case (1, _) if fastTarget.isEmpty =>
|
||||
prepare ++
|
||||
AssemblyLine.variable(ctx, LDA, variable) ++ List(
|
||||
|
@ -2272,7 +2293,7 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
|||
prepare ++ (0 until targetType.size).flatMap { i =>
|
||||
val load = loadSingleByteAssumingAPreserved(ctx, sourceType, variable, i)
|
||||
load ++ List(
|
||||
AssemblyLine.absolute(STA, addr + offset + i))
|
||||
AssemblyLine.absolute(STA, addr + offset + i).copy(elidability = el))
|
||||
}
|
||||
case (_, _) =>
|
||||
fastTarget match {
|
||||
|
@ -2281,37 +2302,38 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
|||
val load = loadSingleByteAssumingAPreserved(ctx, sourceType, variable, i)
|
||||
load ++ (if (i == 0) List(AssemblyLine(STA, fastAddrMode, constAddr)) else List(
|
||||
AssemblyLine.implied(INY),
|
||||
AssemblyLine(STA, fastAddrMode, constAddr)))
|
||||
AssemblyLine(STA, fastAddrMode, constAddr, elidability = el)))
|
||||
}
|
||||
case _ =>
|
||||
prepare ++ (0 until targetType.size).flatMap { i =>
|
||||
val load = loadSingleByteAssumingAPreserved(ctx, sourceType, variable, i)
|
||||
load ++ List(
|
||||
if (i == 0) AssemblyLine.immediate(LDY, offset) else AssemblyLine.implied(INY),
|
||||
AssemblyLine(STA, am, addr))
|
||||
AssemblyLine(STA, am, addr).copy(elidability = el))
|
||||
}
|
||||
}
|
||||
case _ =>
|
||||
ctx.log.error("Cannot assign to a large object indirectly", target.position)
|
||||
Nil
|
||||
}
|
||||
case DerefExpression(innerSource, sourceOffset, _) =>
|
||||
case DerefExpression(innerSource, sourceOffset, sourceVolatile, _) =>
|
||||
val sEl = if (sourceVolatile) Elidability.Volatile else Elidability.Elidable
|
||||
val (prepareSource, addrSource, amSource, fastSource) = getPhysicalPointerForDeref(ctx, innerSource)
|
||||
(am, amSource) match {
|
||||
case (AbsoluteY, AbsoluteY) =>
|
||||
prepare ++ prepareSource ++ (0 until targetType.size).flatMap { i =>
|
||||
loadSingleByteAssumingAPreserved(ctx, sourceType, addrSource + sourceOffset, i) :+ AssemblyLine.absolute(STA, addr + offset + i)
|
||||
loadSingleByteAssumingAPreserved(ctx, sourceType, addrSource + sourceOffset, i, sEl) :+ AssemblyLine.absolute(STA, addr + offset + i).copy(elidability = el)
|
||||
}
|
||||
case (AbsoluteY, _) =>
|
||||
prepare ++ prepareSource ++ (0 until targetType.size).flatMap { i =>
|
||||
loadSingleByteIndexedCountingYAssumingAPreserved(ctx, sourceType, amSource, addrSource, sourceOffset, i) :+
|
||||
AssemblyLine.absolute(STA, addr + offset + i)
|
||||
loadSingleByteIndexedCountingYAssumingAPreserved(ctx, sourceType, amSource, addrSource, sourceOffset, i, sEl) :+
|
||||
AssemblyLine.absolute(STA, addr + offset + i).copy(elidability = el)
|
||||
}
|
||||
case (_, AbsoluteY) =>
|
||||
prepare ++ prepareSource ++ (0 until targetType.size).flatMap { i =>
|
||||
(if (i == 0) AssemblyLine.immediate(LDY, sourceOffset) else AssemblyLine.implied(INY)) ::
|
||||
(loadSingleByteAssumingAPreserved(ctx, sourceType, addrSource + sourceOffset, i) :+
|
||||
AssemblyLine(STA, am, addr))
|
||||
(loadSingleByteAssumingAPreserved(ctx, sourceType, addrSource + sourceOffset, i, sEl) :+
|
||||
AssemblyLine(STA, am, addr, elidability = el))
|
||||
}
|
||||
case (IndexedY, IndexedY) =>
|
||||
val reg = env.get[ThingInMemory]("__reg.loword")
|
||||
|
@ -2327,8 +2349,8 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
|||
AssemblyLine.zeropage(LDA, reg, 1),
|
||||
AssemblyLine.zeropage(STA, reg, 3)) ++ prepareSource ++ (0 until targetType.size).flatMap { i =>
|
||||
(if (i == 0) AssemblyLine.immediate(LDY, sourceOffset) else AssemblyLine.implied(INY)) ::
|
||||
(loadSingleByteIndexedAssumingAPreserved(ctx, sourceType, IndexedY, reg.toAddress, i) :+
|
||||
AssemblyLine.indexedY(STA, reg, 2))
|
||||
(loadSingleByteIndexedAssumingAPreserved(ctx, sourceType, IndexedY, reg.toAddress, i, elidability = sEl) :+
|
||||
AssemblyLine.indexedY(STA, reg, 2).copy(elidability = el))
|
||||
}
|
||||
case (false, true) =>
|
||||
prepareSource ++ List(
|
||||
|
@ -2337,8 +2359,8 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
|||
AssemblyLine.zeropage(LDA, reg, 1),
|
||||
AssemblyLine.zeropage(STA, reg, 3)) ++ prepare ++ (0 until targetType.size).flatMap { i =>
|
||||
(if (i == 0) AssemblyLine.immediate(LDY, sourceOffset) else AssemblyLine.implied(INY)) ::
|
||||
(loadSingleByteIndexedAssumingAPreserved(ctx, sourceType, IndexedY, reg.toAddress + 2, i) :+
|
||||
AssemblyLine.indexedY(STA, reg))
|
||||
(loadSingleByteIndexedAssumingAPreserved(ctx, sourceType, IndexedY, reg.toAddress + 2, i, elidability = sEl) :+
|
||||
AssemblyLine.indexedY(STA, reg).copy(elidability = el))
|
||||
}
|
||||
case _ =>
|
||||
prepare ++ List(
|
||||
|
@ -2351,15 +2373,15 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
|||
AssemblyLine.implied(PLA),
|
||||
AssemblyLine.zeropage(STA, reg, 3)) ++ (0 until targetType.size).flatMap { i =>
|
||||
(if (i == 0) AssemblyLine.immediate(LDY, sourceOffset) else AssemblyLine.implied(INY)) ::
|
||||
(loadSingleByteIndexedAssumingAPreserved(ctx, sourceType, IndexedY, reg.toAddress, i) :+
|
||||
AssemblyLine.indexedY(STA, reg, 2))
|
||||
(loadSingleByteIndexedAssumingAPreserved(ctx, sourceType, IndexedY, reg.toAddress, i, sEl) :+
|
||||
AssemblyLine.indexedY(STA, reg, 2).copy(elidability = el))
|
||||
}
|
||||
}
|
||||
case (MemoryAddressConstant(thT: MemoryVariable), MemoryAddressConstant(thS: MemoryVariable)) if thT.name != thS.name =>
|
||||
prepare ++ prepareSource ++ (0 until targetType.size).flatMap { i =>
|
||||
(if (i == 0) AssemblyLine.immediate(LDY, sourceOffset) else AssemblyLine.implied(INY)) ::
|
||||
(loadSingleByteIndexedAssumingAPreserved(ctx, sourceType, IndexedY, thS.toAddress, i) :+
|
||||
AssemblyLine.indexedY(STA, thT))
|
||||
(loadSingleByteIndexedAssumingAPreserved(ctx, sourceType, IndexedY, thS.toAddress, i, sEl) :+
|
||||
AssemblyLine.indexedY(STA, thT).copy(elidability = el))
|
||||
}
|
||||
case _ =>
|
||||
???
|
||||
|
@ -2376,17 +2398,17 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
|||
// TODO: optimize if prepare is empty
|
||||
if (prepare.isEmpty) {
|
||||
compile(ctx, source, someTuple, BranchSpec.None) ++ List(
|
||||
AssemblyLine.absolute(STA, addr + offset),
|
||||
AssemblyLine.absolute(STX, addr + offset + 1))
|
||||
AssemblyLine.absolute(STA, addr + offset).copy(elidability = el),
|
||||
AssemblyLine.absolute(STX, addr + offset + 1).copy(elidability = el))
|
||||
} else {
|
||||
compile(ctx, source, someTuple, BranchSpec.None) ++ List(
|
||||
AssemblyLine.implied(PHA),
|
||||
AssemblyLine.implied(TXA),
|
||||
AssemblyLine.implied(PHA)) ++ prepare ++ List(
|
||||
AssemblyLine.implied(PLA),
|
||||
AssemblyLine.absolute(STA, addr + offset + 1),
|
||||
AssemblyLine.absolute(STA, addr + offset + 1).copy(elidability = el),
|
||||
AssemblyLine.implied(PLA),
|
||||
AssemblyLine.absolute(STA, addr + offset))
|
||||
AssemblyLine.absolute(STA, addr + offset).copy(elidability = el))
|
||||
}
|
||||
case (2, _) =>
|
||||
val someTuple = Some(targetType, RegisterVariable(MosRegister.AX, targetType))
|
||||
|
@ -2395,18 +2417,18 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
|||
compile(ctx, source, someTuple, BranchSpec.None) ++
|
||||
preserveRegisterIfNeeded(ctx, MosRegister.AX, initializeY(offset)) ++
|
||||
List(
|
||||
AssemblyLine(STA, fastAddrMode, baseOffset),
|
||||
AssemblyLine(STA, fastAddrMode, baseOffset, elidability = el),
|
||||
AssemblyLine.implied(TXA),
|
||||
AssemblyLine.implied(INY),
|
||||
AssemblyLine(STA, fastAddrMode, baseOffset))
|
||||
AssemblyLine(STA, fastAddrMode, baseOffset, elidability = el))
|
||||
case _ =>
|
||||
if (prepare.isEmpty) {
|
||||
compile(ctx, source, someTuple, BranchSpec.None) ++ List(
|
||||
AssemblyLine.immediate(LDY, offset),
|
||||
AssemblyLine.indexedY(STA, addr),
|
||||
AssemblyLine.indexedY(STA, addr).copy(elidability = el),
|
||||
AssemblyLine.implied(TXA),
|
||||
AssemblyLine.implied(INY),
|
||||
AssemblyLine.indexedY(STA, addr))
|
||||
AssemblyLine.indexedY(STA, addr).copy(elidability = el))
|
||||
} else {
|
||||
compile(ctx, source, someTuple, BranchSpec.None) ++ List(
|
||||
AssemblyLine.implied(PHA),
|
||||
|
@ -2414,10 +2436,10 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
|||
AssemblyLine.implied(PHA)) ++ prepare ++ List(
|
||||
AssemblyLine.immediate(LDY, offset+1),
|
||||
AssemblyLine.implied(PLA),
|
||||
AssemblyLine.indexedY(STA, addr),
|
||||
AssemblyLine.indexedY(STA, addr).copy(elidability = el),
|
||||
AssemblyLine.implied(PLA),
|
||||
AssemblyLine.implied(DEY),
|
||||
AssemblyLine.indexedY(STA, addr))
|
||||
AssemblyLine.indexedY(STA, addr).copy(elidability = el))
|
||||
}
|
||||
}
|
||||
case _ =>
|
||||
|
@ -2443,25 +2465,25 @@ object MosExpressionCompiler extends AbstractExpressionCompiler[AssemblyLine] {
|
|||
else AssemblyLine.variable(ctx, LDA, variable, i)
|
||||
}
|
||||
|
||||
private def loadSingleByteAssumingAPreserved(ctx: CompilationContext, sourceType: Type, varAddr: Constant, i: Int) = {
|
||||
private def loadSingleByteAssumingAPreserved(ctx: CompilationContext, sourceType: Type, varAddr: Constant, i: Int, elidability: Elidability.Value) = {
|
||||
if (i == sourceType.size) {
|
||||
if (sourceType.isSigned) signExtendA(ctx) else List(AssemblyLine.immediate(LDA, 0))
|
||||
} else if (i > sourceType.size) Nil
|
||||
else List(AssemblyLine.absolute(LDA, varAddr + i))
|
||||
else List(AssemblyLine(LDA, Absolute, varAddr + i, elidability))
|
||||
}
|
||||
|
||||
private def loadSingleByteIndexedAssumingAPreserved(ctx: CompilationContext, sourceType: Type, addrMode: AddrMode.Value, baseAddr: Constant, i: Int) = {
|
||||
private def loadSingleByteIndexedAssumingAPreserved(ctx: CompilationContext, sourceType: Type, addrMode: AddrMode.Value, baseAddr: Constant, i: Int, elidability: Elidability.Value) = {
|
||||
if (i == sourceType.size) {
|
||||
if (sourceType.isSigned) signExtendA(ctx) else List(AssemblyLine.immediate(LDA, 0))
|
||||
} else if (i > sourceType.size) Nil
|
||||
else List(AssemblyLine(LDA, addrMode, baseAddr))
|
||||
else List(AssemblyLine(LDA, addrMode, baseAddr, elidability))
|
||||
}
|
||||
|
||||
private def loadSingleByteIndexedCountingYAssumingAPreserved(ctx: CompilationContext, sourceType: Type, addrMode: AddrMode.Value, baseAddr: Constant, sourceOffset:Int, i: Int) = {
|
||||
private def loadSingleByteIndexedCountingYAssumingAPreserved(ctx: CompilationContext, sourceType: Type, addrMode: AddrMode.Value, baseAddr: Constant, sourceOffset:Int, i: Int, elidability: Elidability.Value) = {
|
||||
if (i == sourceType.size) {
|
||||
if (sourceType.isSigned) signExtendA(ctx) else List(AssemblyLine.immediate(LDA, 0))
|
||||
} else if (i > sourceType.size) Nil
|
||||
else List(if(i==0) AssemblyLine.immediate(LDY, sourceOffset) else AssemblyLine.implied(INY), AssemblyLine(LDA, addrMode, baseAddr))
|
||||
else List(if(i==0) AssemblyLine.immediate(LDY, sourceOffset) else AssemblyLine.implied(INY), AssemblyLine(LDA, addrMode, baseAddr, elidability))
|
||||
}
|
||||
|
||||
def arrayBoundsCheck(ctx: CompilationContext, pointy: Pointy, register: MosRegister.Value, index: Expression): List[AssemblyLine] = {
|
||||
|
|
|
@ -164,14 +164,14 @@ object MosStatementCompiler extends AbstractStatementCompiler[AssemblyLine] {
|
|||
val c: Constant = compileParameterForAssemblyStatement(env, o, x)
|
||||
val actualAddrMode = a match {
|
||||
case Absolute if OpcodeClasses.ShortBranching(o) => Relative
|
||||
case Absolute if (o == ROR_W || o == ASL_W) && ctx.options.flag(CompilationFlag.Emit65CE02Opcodes) =>
|
||||
case Absolute if (o == ROL_W || o == ASL_W) && ctx.options.flag(CompilationFlag.Emit65CE02Opcodes) =>
|
||||
Absolute
|
||||
case Absolute if OpcodeClasses.SupportsZeropage(o) && c.fitsProvablyIntoByte => ZeroPage
|
||||
case ImmediateWithAbsolute if (c match {
|
||||
case StructureConstant(_, List(a, b)) => b.fitsProvablyIntoByte
|
||||
}) => ImmediateWithZeroPage
|
||||
case IndexedX if o == JMP => AbsoluteIndexedX
|
||||
case Indirect if o != JMP => IndexedZ
|
||||
case IndexedX if o == JMP || o == JSR => AbsoluteIndexedX
|
||||
case Indirect if o != JMP && o != JSR => IndexedZ
|
||||
case _ => a
|
||||
}
|
||||
List(AssemblyLine(o, actualAddrMode, c, e)) -> Nil
|
||||
|
@ -191,7 +191,7 @@ object MosStatementCompiler extends AbstractStatementCompiler[AssemblyLine] {
|
|||
case Some(_) =>
|
||||
params.flatMap(p => MosExpressionCompiler.compile(ctx, p, None, NoBranching))-> Nil
|
||||
case None =>
|
||||
env.lookupFunction(name, params.map(p => MosExpressionCompiler.getExpressionType(ctx, p) -> p)) match {
|
||||
env.lookupFunction(name, params.map(p => AbstractExpressionCompiler.getExpressionTypeForMacro(ctx, p) -> p)) match {
|
||||
case Some(i: MacroFunction) =>
|
||||
val (paramPreparation, inlinedStatements) = MosMacroExpander.inlineFunction(ctx, i, params, e.position)
|
||||
paramPreparation ++ compile(ctx.withInlinedEnv(i.environment, ctx.nextLabel("en")), inlinedStatements)._1 -> Nil
|
||||
|
|
|
@ -730,11 +730,23 @@ object PseudoregisterBuiltIns {
|
|||
case (1, 1) => // ok
|
||||
case _ => ctx.log.fatal("Invalid code path", param2.position)
|
||||
}
|
||||
val b = ctx.env.get[Type]("byte")
|
||||
val w = ctx.env.get[Type]("word")
|
||||
val reg = ctx.env.get[VariableInMemory]("__reg")
|
||||
if (!storeInRegLo && param1OrRegister.isDefined) {
|
||||
(ctx.env.eval(param1OrRegister.get), ctx.env.eval(param2)) match {
|
||||
case (Some(l), Some(r)) =>
|
||||
val product = CompoundConstant(MathOperator.Times, l, r).quickSimplify
|
||||
return List(AssemblyLine.immediate(LDA, product.loByte), AssemblyLine.immediate(LDX, product.hiByte))
|
||||
case (Some(NumericConstant(2, _)), _) =>
|
||||
val evalParam2 = MosExpressionCompiler.compile(ctx, param2, Some(b -> RegisterVariable(MosRegister.A, b)), BranchSpec.None)
|
||||
val label = ctx.nextLabel("sh")
|
||||
return evalParam2 ++ List(
|
||||
AssemblyLine.implied(ASL),
|
||||
AssemblyLine.immediate(LDX, 0),
|
||||
AssemblyLine.relative(BCC, label),
|
||||
AssemblyLine.implied(INX),
|
||||
AssemblyLine.label(label))
|
||||
case (Some(NumericConstant(c, _)), _) if isPowerOfTwoUpTo15(c)=>
|
||||
return compileWordShiftOps(left = true, ctx, param2, LiteralExpression(java.lang.Long.bitCount(c - 1), 1))
|
||||
case (_, Some(NumericConstant(c, _))) if isPowerOfTwoUpTo15(c)=>
|
||||
|
@ -742,9 +754,6 @@ object PseudoregisterBuiltIns {
|
|||
case _ =>
|
||||
}
|
||||
}
|
||||
val b = ctx.env.get[Type]("byte")
|
||||
val w = ctx.env.get[Type]("word")
|
||||
val reg = ctx.env.get[VariableInMemory]("__reg")
|
||||
val load: List[AssemblyLine] = param1OrRegister match {
|
||||
case Some(param1) =>
|
||||
val code1 = MosExpressionCompiler.compile(ctx, param1, Some(w -> RegisterVariable(MosRegister.AX, w)), BranchSpec.None)
|
||||
|
|
|
@ -27,7 +27,18 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||
case FatBooleanType => compileToA(ctx, expression)
|
||||
case t: ConstantBooleanType =>
|
||||
List(ZLine.ldImm8(ZRegister.A, if (t.value) 1 else 0))
|
||||
case BuiltInBooleanType | _: FlagBooleanType =>
|
||||
case t: FlagBooleanType =>
|
||||
val condition = Z80ExpressionCompiler.compile(ctx, expression, ZExpressionTarget.NOTHING, NoBranching)
|
||||
condition ++ (t.jumpIfFalse.z80Flags match {
|
||||
case o =>
|
||||
val label = ctx.env.nextLabel("bo")
|
||||
List(
|
||||
ZLine.ldImm8(ZRegister.A, 0),
|
||||
ZLine.jumpR(ctx, label, o),
|
||||
ZLine.register(INC, ZRegister.A),
|
||||
ZLine.label(label))
|
||||
})
|
||||
case BuiltInBooleanType =>
|
||||
// TODO optimize if using CARRY
|
||||
// TODO: helper functions to convert flags to booleans, to make code smaller
|
||||
val label = ctx.env.nextLabel("bo")
|
||||
|
@ -623,70 +634,71 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||
case ZExpressionTarget.DE => xxx(ZRegister.D, ZRegister.E, allowRedirect = true)
|
||||
case ZExpressionTarget.BC => xxx(ZRegister.B, ZRegister.C, allowRedirect = true)
|
||||
}
|
||||
case e@DerefExpression(inner, offset, targetType) =>
|
||||
case e@DerefExpression(inner, offset, isVolatile, targetType) =>
|
||||
val el = if (isVolatile) Elidability.Volatile else Elidability.Elidable
|
||||
compileDerefPointer(ctx, e) ++ (targetType.size match {
|
||||
case 1 => target match {
|
||||
case ZExpressionTarget.A => List(ZLine.ld8(ZRegister.A, ZRegister.MEM_HL))
|
||||
case ZExpressionTarget.A => List(ZLine.ld8(ZRegister.A, ZRegister.MEM_HL).copy(elidability = el))
|
||||
case ZExpressionTarget.DEHL =>
|
||||
if (targetType.isSigned) {
|
||||
List(ZLine.ld8(ZRegister.A, ZRegister.MEM_HL), ZLine.ld8(ZRegister.L, ZRegister.A)) ++
|
||||
List(ZLine.ld8(ZRegister.A, ZRegister.MEM_HL).copy(elidability = el), ZLine.ld8(ZRegister.L, ZRegister.A)) ++
|
||||
signExtendHighestByte(ctx, ZRegister.A) ++
|
||||
List(ZLine.ld8(ZRegister.H, ZRegister.A), ZLine.ld8(ZRegister.E, ZRegister.A), ZLine.ld8(ZRegister.D, ZRegister.A))
|
||||
} else {
|
||||
List(ZLine.ld8(ZRegister.L, ZRegister.MEM_HL), ZLine.ldImm8(ZRegister.H, 0), ZLine.ldImm8(ZRegister.E, 0), ZLine.ldImm8(ZRegister.D, 0))
|
||||
List(ZLine.ld8(ZRegister.L, ZRegister.MEM_HL).copy(elidability = el), ZLine.ldImm8(ZRegister.H, 0), ZLine.ldImm8(ZRegister.E, 0), ZLine.ldImm8(ZRegister.D, 0))
|
||||
}
|
||||
case ZExpressionTarget.HL =>
|
||||
if (targetType.isSigned) {
|
||||
List(ZLine.ld8(ZRegister.A, ZRegister.MEM_HL), ZLine.ld8(ZRegister.L, ZRegister.A)) ++ signExtendHighestByte(ctx, ZRegister.H)
|
||||
List(ZLine.ld8(ZRegister.A, ZRegister.MEM_HL).copy(elidability = el), ZLine.ld8(ZRegister.L, ZRegister.A)) ++ signExtendHighestByte(ctx, ZRegister.H)
|
||||
} else {
|
||||
List(ZLine.ld8(ZRegister.L, ZRegister.MEM_HL), ZLine.ldImm8(ZRegister.H, 0))
|
||||
List(ZLine.ld8(ZRegister.L, ZRegister.MEM_HL).copy(elidability = el), ZLine.ldImm8(ZRegister.H, 0))
|
||||
}
|
||||
case ZExpressionTarget.BC =>
|
||||
if (targetType.isSigned) {
|
||||
List(ZLine.ld8(ZRegister.A, ZRegister.MEM_HL), ZLine.ld8(ZRegister.C, ZRegister.A)) ++ signExtendHighestByte(ctx, ZRegister.B)
|
||||
List(ZLine.ld8(ZRegister.A, ZRegister.MEM_HL).copy(elidability = el), ZLine.ld8(ZRegister.C, ZRegister.A)) ++ signExtendHighestByte(ctx, ZRegister.B)
|
||||
} else {
|
||||
List(ZLine.ld8(ZRegister.C, ZRegister.MEM_HL), ZLine.ldImm8(ZRegister.B, 0))
|
||||
List(ZLine.ld8(ZRegister.C, ZRegister.MEM_HL).copy(elidability = el), ZLine.ldImm8(ZRegister.B, 0))
|
||||
}
|
||||
case ZExpressionTarget.DE =>
|
||||
if (targetType.isSigned) {
|
||||
List(ZLine.ld8(ZRegister.A, ZRegister.MEM_HL), ZLine.ld8(ZRegister.E, ZRegister.A)) ++ signExtendHighestByte(ctx, ZRegister.D)
|
||||
List(ZLine.ld8(ZRegister.A, ZRegister.MEM_HL).copy(elidability = el), ZLine.ld8(ZRegister.E, ZRegister.A)) ++ signExtendHighestByte(ctx, ZRegister.D)
|
||||
} else {
|
||||
List(ZLine.ld8(ZRegister.E, ZRegister.MEM_HL), ZLine.ldImm8(ZRegister.D, 0))
|
||||
List(ZLine.ld8(ZRegister.E, ZRegister.MEM_HL).copy(elidability = el), ZLine.ldImm8(ZRegister.D, 0))
|
||||
}
|
||||
case ZExpressionTarget.NOTHING => Nil
|
||||
}
|
||||
case 2 => target match {
|
||||
case ZExpressionTarget.DEHL =>
|
||||
List(
|
||||
ZLine.ld8(ZRegister.A, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.MEM_HL).copy(elidability = el),
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.H, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.H, ZRegister.MEM_HL).copy(elidability = el),
|
||||
ZLine.ld8(ZRegister.L, ZRegister.A),
|
||||
ZLine.ldImm8(ZRegister.E, 0),
|
||||
ZLine.ldImm8(ZRegister.D, 0)) // TODO
|
||||
case ZExpressionTarget.EHL =>
|
||||
List(
|
||||
ZLine.ld8(ZRegister.A, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.MEM_HL).copy(elidability = el),
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.H, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.H, ZRegister.MEM_HL).copy(elidability = el),
|
||||
ZLine.ld8(ZRegister.L, ZRegister.A),
|
||||
ZLine.ldImm8(ZRegister.E, 0)) // TODO
|
||||
case ZExpressionTarget.HL =>
|
||||
List(
|
||||
ZLine.ld8(ZRegister.A, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.MEM_HL).copy(elidability = el),
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.H, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.H, ZRegister.MEM_HL).copy(elidability = el),
|
||||
ZLine.ld8(ZRegister.L, ZRegister.A))
|
||||
case ZExpressionTarget.BC =>
|
||||
List(
|
||||
ZLine.ld8(ZRegister.C, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.C, ZRegister.MEM_HL).copy(elidability = el),
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.B, ZRegister.MEM_HL))
|
||||
ZLine.ld8(ZRegister.B, ZRegister.MEM_HL).copy(elidability = el))
|
||||
case ZExpressionTarget.DE =>
|
||||
List(
|
||||
ZLine.ld8(ZRegister.E, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.E, ZRegister.MEM_HL).copy(elidability = el),
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.D, ZRegister.MEM_HL))
|
||||
ZLine.ld8(ZRegister.D, ZRegister.MEM_HL).copy(elidability = el))
|
||||
case ZExpressionTarget.NOTHING => Nil
|
||||
}
|
||||
case 3 => target match {
|
||||
|
@ -694,20 +706,20 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||
case ZExpressionTarget.EHL | ZExpressionTarget.DEHL =>
|
||||
if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)) {
|
||||
List(
|
||||
ZLine.ld8(ZRegister.E, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.E, ZRegister.MEM_HL).copy(elidability = el),
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.D, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.D, ZRegister.MEM_HL).copy(elidability = el),
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.L, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.L, ZRegister.MEM_HL).copy(elidability = el),
|
||||
ZLine.ldImm8(ZRegister.H, 0),
|
||||
ZLine.implied(EX_DE_HL)) // TODO
|
||||
} else {
|
||||
List(
|
||||
ZLine.ld8(ZRegister.C, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.C, ZRegister.MEM_HL).copy(elidability = el),
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.B, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.B, ZRegister.MEM_HL).copy(elidability = el),
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.E, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.E, ZRegister.MEM_HL).copy(elidability = el),
|
||||
ZLine.ld8(ZRegister.L, ZRegister.C),
|
||||
ZLine.ld8(ZRegister.H, ZRegister.B),
|
||||
ZLine.ldImm8(ZRegister.D, 0)) // TODO
|
||||
|
@ -718,24 +730,24 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||
case ZExpressionTarget.DEHL =>
|
||||
if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)) {
|
||||
List(
|
||||
ZLine.ld8(ZRegister.E, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.E, ZRegister.MEM_HL).copy(elidability = el),
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.D, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.D, ZRegister.MEM_HL).copy(elidability = el),
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.MEM_HL).copy(elidability = el),
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.H, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.H, ZRegister.MEM_HL).copy(elidability = el),
|
||||
ZLine.ld8(ZRegister.L, ZRegister.A),
|
||||
ZLine.implied(EX_DE_HL)) // TODO
|
||||
} else {
|
||||
List(
|
||||
ZLine.ld8(ZRegister.C, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.C, ZRegister.MEM_HL).copy(elidability = el),
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.B, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.B, ZRegister.MEM_HL).copy(elidability = el),
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.E, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.E, ZRegister.MEM_HL).copy(elidability = el),
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.D, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.D, ZRegister.MEM_HL).copy(elidability = el),
|
||||
ZLine.ld8(ZRegister.L, ZRegister.C),
|
||||
ZLine.ld8(ZRegister.H, ZRegister.B)) // TODO
|
||||
}
|
||||
|
@ -1590,11 +1602,15 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||
prepareA ++ fillUpperBytes
|
||||
}
|
||||
|
||||
private def signExtendHighestByte(ctx: CompilationContext, sourceHiRegister: ZRegister.Value, targetHiRegister: ZRegister.Value = ZRegister.A, signedSource: Boolean = true): List[ZLine] = {
|
||||
private def signExtendHighestByte(ctx: CompilationContext,
|
||||
sourceHiRegister: ZRegister.Value,
|
||||
targetHiRegister: ZRegister.Value = ZRegister.A,
|
||||
signedSource: Boolean = true,
|
||||
elidability: Elidability.Value = Elidability.Elidable): List[ZLine] = {
|
||||
if (!signedSource) {
|
||||
return List(ZLine.ldImm8(targetHiRegister, 0))
|
||||
}
|
||||
val prefix = if (sourceHiRegister == ZRegister.A) Nil else List(ZLine.ld8(ZRegister.A, sourceHiRegister))
|
||||
val prefix = if (sourceHiRegister == ZRegister.A) Nil else List(ZLine.ld8(ZRegister.A, sourceHiRegister).copy(elidability = elidability))
|
||||
val label = ctx.nextLabel("sx")
|
||||
val resultInA = if (ctx.options.flag(CompilationFlag.EmitIntel8080Opcodes)) {
|
||||
prefix ++ List(
|
||||
|
@ -1613,7 +1629,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||
} else {
|
||||
throw new IllegalStateException()
|
||||
}
|
||||
if (targetHiRegister == ZRegister.A) resultInA else resultInA :+ ZLine.ld8(targetHiRegister, ZRegister.A)
|
||||
if (targetHiRegister == ZRegister.A) resultInA else resultInA :+ ZLine.ld8(targetHiRegister, ZRegister.A).copy(elidability = elidability)
|
||||
}
|
||||
|
||||
def signExtendViaIX(ctx: CompilationContext, targetOffset: Int, hiRegister: ZRegister.Value, bytes: Int, signedSource: Boolean): List[ZLine] = {
|
||||
|
@ -1868,17 +1884,18 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||
List(ZLine.ld8(ZRegister.E, ZRegister.A)) ++ stashDEIfChanged(ctx, code) :+ ZLine.ld8(ZRegister.MEM_HL, ZRegister.E)
|
||||
} else code :+ ZLine.ld8(ZRegister.MEM_HL, ZRegister.A)
|
||||
}
|
||||
case e@DerefExpression(_, _, targetType) =>
|
||||
val lo = stashAFIfChanged(ctx, compileDerefPointer(ctx, e)) :+ ZLine.ld8(ZRegister.MEM_HL, ZRegister.A)
|
||||
case e@DerefExpression(_, _, vol, targetType) =>
|
||||
val el = if (vol) Elidability.Volatile else Elidability.Elidable
|
||||
val lo = stashAFIfChanged(ctx, compileDerefPointer(ctx, e)) :+ ZLine.ld8(ZRegister.MEM_HL, ZRegister.A).copy(elidability = el)
|
||||
if (targetType.size == 1) lo
|
||||
else if (targetType.size == 2) {
|
||||
lo ++ List(ZLine.register(INC_16, ZRegister.HL)) ++
|
||||
signExtendHighestByte(ctx, ZRegister.A, signedSource = signedSource) ++
|
||||
List(ZLine.ld8(ZRegister.MEM_HL, ZRegister.A))
|
||||
List(ZLine.ld8(ZRegister.MEM_HL, ZRegister.A).copy(elidability = el))
|
||||
} else {
|
||||
lo ++ signExtendHighestByte(ctx, ZRegister.A, signedSource = signedSource) ++ List.tabulate(targetType.size - 1)(_ => List(
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A)
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A).copy(elidability = el)
|
||||
)).flatten
|
||||
}
|
||||
//TODO
|
||||
|
@ -1889,10 +1906,11 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||
def storeConstantWord(ctx: CompilationContext, target: LhsExpression, source: Constant, signedSource: Boolean): List[ZLine] = {
|
||||
target match {
|
||||
case e: DerefExpression =>
|
||||
val el = if(e.isVolatile) Elidability.Volatile else Elidability.Elidable
|
||||
compileDerefPointer(ctx, e) ++ List(
|
||||
ZLine.ldImm8(ZRegister.MEM_HL, source.loByte),
|
||||
ZLine.ldImm8(ZRegister.MEM_HL, source.loByte).copy(elidability = el),
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ldImm8(ZRegister.MEM_HL, source.hiByte))
|
||||
ZLine.ldImm8(ZRegister.MEM_HL, source.hiByte).copy(elidability = el))
|
||||
case _ => ZLine.ldImm16(ZRegister.HL, source) :: storeHL(ctx, target, signedSource)
|
||||
}
|
||||
}
|
||||
|
@ -1958,6 +1976,7 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||
Z80ExpressionCompiler.stashHLIfChanged(ctx, ZLine.ld8(ZRegister.A, ZRegister.L) :: storeA(ctx, lo, signedSource)) ++
|
||||
(ZLine.ld8(ZRegister.A, ZRegister.H) :: storeA(ctx, hi, signedSource))
|
||||
case e:DerefExpression =>
|
||||
val el = if(e.isVolatile) Elidability.Volatile else Elidability.Elidable
|
||||
if (ctx.options.flag(CompilationFlag.EmitIntel8085Opcodes) && ctx.options.flag(CompilationFlag.EmitIllegals)) {
|
||||
List(ZLine.register(PUSH, ZRegister.HL)) ++ fixTsx(ctx, compileDerefPointer(ctx, e)) ++ List(
|
||||
ZLine.register(POP, ZRegister.DE),
|
||||
|
@ -1968,20 +1987,20 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||
List(ZLine.register(INC_16, DE)) ++
|
||||
List.fill(e.targetType.size - 2)(List(
|
||||
ZLine.register(INC_16, DE),
|
||||
ZLine.ld8(MEM_DE, A)
|
||||
ZLine.ld8(MEM_DE, A).copy(elidability = el)
|
||||
)).flatten
|
||||
else Nil)
|
||||
} else {
|
||||
List(ZLine.register(PUSH, ZRegister.HL)) ++ fixTsx(ctx, compileDerefPointer(ctx, e)) ++ List(
|
||||
ZLine.register(POP, ZRegister.BC),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.C),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.C).copy(elidability = el),
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.B)) ++
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.B).copy(elidability = el)) ++
|
||||
(if (e.targetType.size > 2)
|
||||
signExtendHighestByte(ctx, B) ++
|
||||
List.fill(e.targetType.size - 2)(List(
|
||||
ZLine.register(INC_16, HL),
|
||||
ZLine.ld8(MEM_HL, A)
|
||||
ZLine.ld8(MEM_HL, A).copy(elidability = el)
|
||||
)).flatten
|
||||
else Nil)
|
||||
}
|
||||
|
@ -2298,16 +2317,17 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||
case _ => List(ZLine.ldImm8(ZRegister.A, 0)) // TODO: signed large types?
|
||||
}
|
||||
}
|
||||
case e@DerefExpression(_, _, _) =>
|
||||
case e: DerefExpression =>
|
||||
val el = if(e.isVolatile) Elidability.Volatile else Elidability.Elidable
|
||||
import ZRegister._
|
||||
val prepareHL = compileDerefPointer(ctx, e)
|
||||
List.tabulate(size) { i =>
|
||||
if (i == 0) {
|
||||
prepareHL :+ ZLine.ld8(A, MEM_HL)
|
||||
prepareHL :+ ZLine.ld8(A, MEM_HL).copy(elidability = el)
|
||||
} else if (i < e.targetType.size) {
|
||||
List(ZLine.register(INC_16, HL), ZLine.ld8(A, MEM_HL))
|
||||
List(ZLine.register(INC_16, HL), ZLine.ld8(A, MEM_HL).copy(elidability = el))
|
||||
} else if (e.targetType.isSigned) {
|
||||
signExtendHighestByte(ctx, ZRegister.MEM_HL)
|
||||
signExtendHighestByte(ctx, ZRegister.MEM_HL, elidability = el)
|
||||
} else {
|
||||
List(ZLine.ldImm8(A, 0))
|
||||
}
|
||||
|
@ -2383,15 +2403,16 @@ object Z80ExpressionCompiler extends AbstractExpressionCompiler[ZLine] {
|
|||
Nil
|
||||
}
|
||||
}
|
||||
case de@DerefExpression(inner, offset, targetType) =>
|
||||
case de@DerefExpression(inner, offset, isVolatile, targetType) =>
|
||||
val el = if(isVolatile) Elidability.Volatile else Elidability.Elidable
|
||||
import ZRegister._
|
||||
val prepareHL = compileDerefPointer(ctx, de)
|
||||
List.tabulate(size) { i =>
|
||||
if (i == 0) {
|
||||
prepareHL :+ ZLine.ld8(MEM_HL, A)
|
||||
prepareHL :+ ZLine.ld8(MEM_HL, A).copy(elidability = el)
|
||||
} else if (i < targetType.size) {
|
||||
if (includeStep) List(ZLine.register(INC_16, HL), ZLine.ld8(MEM_HL, A))
|
||||
else List(ZLine.ld8(MEM_HL, A))
|
||||
if (includeStep) List(ZLine.register(INC_16, HL), ZLine.ld8(MEM_HL, A).copy(elidability = el))
|
||||
else List(ZLine.ld8(MEM_HL, A).copy(elidability = el))
|
||||
} else Nil
|
||||
}
|
||||
case _ =>
|
||||
|
|
|
@ -326,8 +326,14 @@ object Z80Multiply {
|
|||
case (1, 1) => // ok
|
||||
case _ => ctx.log.fatal("Invalid code path", l.position)
|
||||
}
|
||||
ctx.env.eval(r) match {
|
||||
case Some(c) =>
|
||||
(ctx.env.eval(l), ctx.env.eval(r)) match {
|
||||
case (Some(p), Some(q)) =>
|
||||
List(ZLine.ldImm16(ZRegister.HL, CompoundConstant(MathOperator.Times, p, q).quickSimplify))
|
||||
case (Some(NumericConstant(c, _)), _) if isPowerOfTwoUpTo15(c) =>
|
||||
Z80ExpressionCompiler.compileToHL(ctx, l) ++ List.fill(Integer.numberOfTrailingZeros(c.toInt))(ZLine.registers(ZOpcode.ADD_16, ZRegister.HL, ZRegister.HL))
|
||||
case (_, Some(NumericConstant(c, _))) if isPowerOfTwoUpTo15(c) =>
|
||||
Z80ExpressionCompiler.compileToHL(ctx, l) ++ List.fill(Integer.numberOfTrailingZeros(c.toInt))(ZLine.registers(ZOpcode.ADD_16, ZRegister.HL, ZRegister.HL))
|
||||
case (_, Some(c)) =>
|
||||
Z80ExpressionCompiler.compileToDE(ctx, l) ++ List(ZLine.ldImm8(ZRegister.A, c)) ++ multiplication16And8(ctx)
|
||||
case _ =>
|
||||
val lw = Z80ExpressionCompiler.compileToDE(ctx, l)
|
||||
|
|
|
@ -241,7 +241,7 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] {
|
|||
}
|
||||
}
|
||||
val registers = (reg, offset) match {
|
||||
case (OneRegister(r), Some(o)) => env.evalForAsm(expression) match {
|
||||
case (OneRegister(r), Some(o)) => env.evalForAsm(o) match {
|
||||
case Some(NumericConstant(v, _)) => OneRegisterOffset(r, v.toInt)
|
||||
case Some(_) =>
|
||||
ctx.log.error("Non-numeric constant", o.position)
|
||||
|
@ -250,7 +250,7 @@ object Z80StatementCompiler extends AbstractStatementCompiler[ZLine] {
|
|||
ctx.log.error("Inlining failed due to non-constant things", o.position)
|
||||
reg
|
||||
}
|
||||
case (TwoRegisters(t, s), Some(o)) => env.evalForAsm(expression) match {
|
||||
case (TwoRegisters(t, s), Some(o)) => env.evalForAsm(o) match {
|
||||
case Some(NumericConstant(v, _)) => TwoRegistersOffset(t, s, v.toInt)
|
||||
case Some(_) =>
|
||||
ctx.log.error("Non-numeric constant", o.position)
|
||||
|
|
|
@ -5,6 +5,7 @@ import millfork.compiler.CompilationContext
|
|||
import millfork.node._
|
||||
import ZOpcode._
|
||||
import millfork.CompilationFlag
|
||||
import millfork.assembly.Elidability
|
||||
import millfork.env._
|
||||
import millfork.error.ConsoleLogger
|
||||
|
||||
|
@ -485,24 +486,25 @@ object ZBuiltIns {
|
|||
|
||||
def performLongInPlace(ctx: CompilationContext, lhs: LhsExpression, rhs: Expression, opcodeFirst: ZOpcode.Value, opcodeLater: ZOpcode.Value, size: Int, decimal: Boolean = false): List[ZLine] = {
|
||||
lhs match {
|
||||
case dx@DerefExpression(inner, offset, targetType) =>
|
||||
case dx@DerefExpression(inner, offset, isVolatile, targetType) =>
|
||||
val el = if(isVolatile) Elidability.Volatile else Elidability.Elidable
|
||||
val env = ctx.env
|
||||
if (targetType.size == 2) {
|
||||
if (opcodeFirst == ADD && !decimal && ctx.options.flag(CompilationFlag.EmitZ80Opcodes)) {
|
||||
val l = Z80ExpressionCompiler.compileToBC(ctx, inner #+# offset)
|
||||
val r = Z80ExpressionCompiler.compileToDE(ctx, rhs)
|
||||
val s = List(
|
||||
ZLine.ld8(ZRegister.A, ZRegister.MEM_BC),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.MEM_BC).copy(elidability = el),
|
||||
ZLine.ld8(ZRegister.L, ZRegister.A),
|
||||
ZLine.register(INC_16, ZRegister.BC),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.MEM_BC),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.MEM_BC).copy(elidability = el),
|
||||
ZLine.ld8(ZRegister.H, ZRegister.A),
|
||||
ZLine.registers(ADD_16, ZRegister.HL, ZRegister.DE),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.H),
|
||||
ZLine.ld8(ZRegister.MEM_BC, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.MEM_BC, ZRegister.A).copy(elidability = el),
|
||||
ZLine.register(DEC_16, ZRegister.BC),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.L),
|
||||
ZLine.ld8(ZRegister.MEM_BC, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.MEM_BC, ZRegister.A).copy(elidability = el),
|
||||
)
|
||||
if (!r.exists(Z80ExpressionCompiler.changesBC)) {
|
||||
return l ++ r ++ s
|
||||
|
@ -517,15 +519,15 @@ object ZBuiltIns {
|
|||
val s = if (opcodeFirst == SUB && decimal) {
|
||||
if (ctx.options.flag(CompilationFlag.EmitExtended80Opcodes)) {
|
||||
List(
|
||||
ZLine.ld8(ZRegister.A, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.MEM_HL).copy(elidability = el),
|
||||
ZLine.register(SUB, ZRegister.E),
|
||||
ZLine.implied(DAA),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A).copy(elidability = el),
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.MEM_HL).copy(elidability = el),
|
||||
ZLine.register(SBC, ZRegister.D),
|
||||
ZLine.implied(DAA),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A).copy(elidability = el),
|
||||
)
|
||||
} else {
|
||||
ctx.log.error("Decimal subtraction from such a complex LHS is not yet supported", dx.position)
|
||||
|
@ -533,25 +535,25 @@ object ZBuiltIns {
|
|||
}
|
||||
} else if (opcodeFirst == ADD && decimal) {
|
||||
List(
|
||||
ZLine.ld8(ZRegister.A, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.MEM_HL).copy(elidability = el),
|
||||
ZLine.register(ADD, ZRegister.E),
|
||||
ZLine.implied(DAA),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A).copy(elidability = el),
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.MEM_HL).copy(elidability = el),
|
||||
ZLine.register(ADC, ZRegister.D),
|
||||
ZLine.implied(DAA),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A).copy(elidability = el),
|
||||
)
|
||||
} else {
|
||||
List(
|
||||
ZLine.ld8(ZRegister.A, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.MEM_HL).copy(elidability = el),
|
||||
ZLine.register(opcodeFirst, ZRegister.E),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A).copy(elidability = el),
|
||||
ZLine.register(INC_16, ZRegister.HL),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.MEM_HL),
|
||||
ZLine.ld8(ZRegister.A, ZRegister.MEM_HL).copy(elidability = el),
|
||||
ZLine.register(opcodeLater, ZRegister.D),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A),
|
||||
ZLine.ld8(ZRegister.MEM_HL, ZRegister.A).copy(elidability = el),
|
||||
)
|
||||
}
|
||||
if (!r.exists(Z80ExpressionCompiler.changesHL)) {
|
||||
|
@ -575,17 +577,17 @@ object ZBuiltIns {
|
|||
result ++= sourceBytes(i)
|
||||
result += ZLine.ld8(ZRegister.E, ZRegister.A)
|
||||
if (i != 0) result += ZLine.register(POP, ZRegister.AF)
|
||||
result += ZLine.ld8(ZRegister.A, ZRegister.MEM_HL)
|
||||
result += ZLine.ld8(ZRegister.A, ZRegister.MEM_HL).copy(elidability = el)
|
||||
result += ZLine.register(if (i == 0) opcodeFirst else opcodeLater, ZRegister.E)
|
||||
result += ZLine.ld8(ZRegister.MEM_HL, ZRegister.A)
|
||||
result += ZLine.ld8(ZRegister.MEM_HL, ZRegister.A).copy(elidability = el)
|
||||
if (i != size - 1) result += ZLine.register(INC_16, ZRegister.HL)
|
||||
}
|
||||
} else {
|
||||
for (i <- 0 until size) {
|
||||
result ++= sourceBytes(i)
|
||||
result += ZLine.register(if (i==0) opcodeFirst else opcodeLater, ZRegister.MEM_HL)
|
||||
result += ZLine.register(if (i==0) opcodeFirst else opcodeLater, ZRegister.MEM_HL).copy(elidability = el)
|
||||
if (decimal) result += ZLine.implied(DAA)
|
||||
result += ZLine.ld8(ZRegister.MEM_HL, ZRegister.A)
|
||||
result += ZLine.ld8(ZRegister.MEM_HL, ZRegister.A).copy(elidability = el)
|
||||
if (i != size -1) result += ZLine.register(INC_16, ZRegister.HL)
|
||||
}
|
||||
}
|
||||
|
|
33
src/main/scala/millfork/env/Constant.scala
vendored
33
src/main/scala/millfork/env/Constant.scala
vendored
|
@ -28,6 +28,7 @@ sealed trait Constant {
|
|||
|
||||
def isQuiteNegative: Boolean = false
|
||||
def isProvablyZero: Boolean = false
|
||||
def isProvablyNonZero: Boolean = false
|
||||
def isProvably(value: Int): Boolean = false
|
||||
def isProvablyInRange(startInclusive: Int, endInclusive: Int): Boolean = false
|
||||
def isProvablyNonnegative: Boolean = false
|
||||
|
@ -183,6 +184,7 @@ case class AssertByte(c: Constant) extends Constant {
|
|||
override def isQuiteNegative: Boolean = c.isQuiteNegative
|
||||
override def isProvablyGreaterOrEqualThan(other: Constant): Boolean = c.isProvablyGreaterOrEqualThan(other)
|
||||
override def isProvablyZero: Boolean = c.isProvablyZero
|
||||
override def isProvablyNonZero: Boolean = c.isProvablyNonZero
|
||||
override def isProvably(i: Int): Boolean = c.isProvably(i)
|
||||
override def isProvablyNonnegative: Boolean = c.isProvablyNonnegative
|
||||
override def isProvablyNegative(asType: Type): Boolean = c.isProvablyNegative(asType)
|
||||
|
@ -221,7 +223,7 @@ case class StructureConstant(typ: StructType, fields: List[Constant]) extends Co
|
|||
|
||||
override def subbyte(index: Int): Constant = {
|
||||
var offset = 0
|
||||
for ((fv, ResolvedFieldDesc(ft, _, arrayIndexTypeAndSize)) <- fields.zip(typ.mutableFieldsWithTypes)) {
|
||||
for ((fv, ResolvedFieldDesc(ft, _, _, arrayIndexTypeAndSize)) <- fields.zip(typ.mutableFieldsWithTypes)) {
|
||||
// TODO: handle array members?
|
||||
val fs = ft.size
|
||||
if (index < offset + fs) {
|
||||
|
@ -234,7 +236,7 @@ case class StructureConstant(typ: StructType, fields: List[Constant]) extends Co
|
|||
}
|
||||
override def subbyteBe(index: Int, totalSize: Int): Constant = {
|
||||
var offset = 0
|
||||
for ((fv, ResolvedFieldDesc(ft, _, arrayIndexTypeAndSize)) <- fields.zip(typ.mutableFieldsWithTypes)) {
|
||||
for ((fv, ResolvedFieldDesc(ft, _, _, arrayIndexTypeAndSize)) <- fields.zip(typ.mutableFieldsWithTypes)) {
|
||||
// TODO: handle array members?
|
||||
val fs = ft.size
|
||||
if (index < offset + fs) {
|
||||
|
@ -277,6 +279,7 @@ case class NumericConstant(value: Long, requiredSize: Int) extends Constant {
|
|||
})
|
||||
override def isProvablyInRange(startInclusive: Int, endInclusive: Int): Boolean = value >= startInclusive && value <= endInclusive
|
||||
override def isProvablyZero: Boolean = value == 0
|
||||
override def isProvablyNonZero: Boolean = value.&(0x1L.<<(8 * requiredSize - 1)) != 0
|
||||
override def isProvably(i: Int): Boolean = value == i
|
||||
override def isProvablyNonnegative: Boolean = value >= 0
|
||||
override def isProvablyNegative(asType: Type): Boolean = {
|
||||
|
@ -440,6 +443,7 @@ case class SubbyteConstant(base: Constant, index: Int) extends Constant {
|
|||
object MathOperator extends Enumeration {
|
||||
val Plus, Minus, Times, Shl, Shr, Shl9, Shr9, Plus9, DecimalPlus9,
|
||||
DecimalPlus, DecimalMinus, DecimalTimes, DecimalShl, DecimalShl9, DecimalShr,
|
||||
Equal, NotEqual, Less, LessEqual, Greater, GreaterEqual,
|
||||
Minimum, Maximum,
|
||||
Divide, Modulo,
|
||||
And, Or, Exor = Value
|
||||
|
@ -599,7 +603,7 @@ case class CompoundConstant(operator: MathOperator.Value, lhs: Constant, rhs: Co
|
|||
case MathOperator.Exor => (lv ^ rv) & bitmask
|
||||
case MathOperator.Or => lv | rv
|
||||
case MathOperator.And => lv & rv & bitmask
|
||||
case MathOperator.Divide if lv >= 0 && rv >= 0 => lv / rv
|
||||
case MathOperator.Divide if lv >= 0 && rv > 0 => lv / rv
|
||||
case MathOperator.Modulo if lv >= 0 && rv >= 0 => lv % rv
|
||||
case MathOperator.DecimalPlus if ls == 1 && rs == 1 =>
|
||||
asDecimal(lv & 0xff, rv & 0xff, _ + _) & 0xff
|
||||
|
@ -692,6 +696,12 @@ case class CompoundConstant(operator: MathOperator.Value, lhs: Constant, rhs: Co
|
|||
case Exor => s"$plhs ^ $prhs"
|
||||
case Divide => s"$plhs / $prhs"
|
||||
case Modulo => s"$plhs %% $prhs"
|
||||
case Equal => s"$plhs == $prhs"
|
||||
case NotEqual => s"$plhs != $prhs"
|
||||
case Greater => s"$plhs > $prhs"
|
||||
case GreaterEqual => s"$plhs >= $prhs"
|
||||
case Less=> s"$plhs < $prhs"
|
||||
case LessEqual=> s"$plhs <= $prhs"
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -783,3 +793,20 @@ case class CompoundConstant(operator: MathOperator.Value, lhs: Constant, rhs: Co
|
|||
|
||||
override def extractLabels: List[String] = lhs.extractLabels ++ rhs.extractLabels
|
||||
}
|
||||
|
||||
case class IfConstant(cond: Constant, ifTrue: Constant, ifFalse: Constant) extends Constant {
|
||||
|
||||
override def toIntelString: String = s"if(${cond.toIntelString},${ifTrue.toIntelString},${ifFalse.toIntelString})"
|
||||
|
||||
override def toString: String = s"if(${cond.toString},${ifTrue.toString},${ifFalse.toString})"
|
||||
|
||||
override def requiredSize: Int = ifTrue.requiredSize max ifFalse.requiredSize
|
||||
|
||||
override def isRelatedTo(v: Thing): Boolean = cond.isRelatedTo(v) || ifTrue.isRelatedTo(v) || ifFalse.isRelatedTo(v)
|
||||
|
||||
override def refersTo(name: String): Boolean = cond.refersTo(name) || ifTrue.refersTo(name) || ifFalse.refersTo(name)
|
||||
|
||||
override def extractLabels: List[String] = cond.extractLabels ++ ifTrue.extractLabels ++ ifFalse.extractLabels
|
||||
|
||||
override def rootThingName: String = "?"
|
||||
}
|
377
src/main/scala/millfork/env/Environment.scala
vendored
377
src/main/scala/millfork/env/Environment.scala
vendored
|
@ -11,6 +11,7 @@ import millfork.node._
|
|||
import millfork.output._
|
||||
import org.apache.commons.lang3.StringUtils
|
||||
|
||||
import java.nio.file.Files
|
||||
import scala.collection.mutable
|
||||
import scala.collection.mutable.ListBuffer
|
||||
|
||||
|
@ -116,7 +117,8 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
callGraph: CallGraph,
|
||||
allocators: Map[String, VariableAllocator],
|
||||
options: CompilationOptions,
|
||||
onEachVariable: (String, (Int, Int)) => Unit,
|
||||
onEachVariable: (String, (String, Int)) => Unit,
|
||||
onEachVariableEnd: (String, (Char, Int)) => Unit,
|
||||
pass: Int,
|
||||
forZpOnly: Boolean): Unit = {
|
||||
if (forZpOnly && !options.platform.hasZeroPage) {
|
||||
|
@ -182,7 +184,8 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
val addr =
|
||||
allocators(bank).allocateBytes(bank0, callGraph, vertex, options, m.sizeInBytes, initialized = false, writeable = true, location = AllocationLocation.Zeropage, alignment = m.alignment)
|
||||
if (log.traceEnabled) log.trace("addr $" + addr.toHexString)
|
||||
onEachVariable(m.name, bank0.index -> addr)
|
||||
onEachVariable(m.name, bank -> addr)
|
||||
onEachVariableEnd(m.name, (if (m.isInstanceOf[MfArray])'a' else 'v') -> (addr + m.sizeInBytes - 1))
|
||||
List(
|
||||
ConstantThing(m.name.stripPrefix(prefix) + "`", NumericConstant(addr, 2), p)
|
||||
)
|
||||
|
@ -205,7 +208,8 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
case None => Nil
|
||||
case Some(addr) =>
|
||||
if (log.traceEnabled) log.trace("addr $" + addr.toHexString)
|
||||
onEachVariable(m.name, bank0.index -> addr)
|
||||
onEachVariable(m.name, bank -> addr)
|
||||
onEachVariableEnd(m.name, (if (m.isInstanceOf[MfArray])'a' else 'v') -> (addr + m.sizeInBytes - 1))
|
||||
List(
|
||||
ConstantThing(m.name.stripPrefix(prefix) + "`", NumericConstant(addr, 2), p)
|
||||
)
|
||||
|
@ -216,7 +220,8 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
} else {
|
||||
val addr = allocators(bank).allocateBytes(bank0, callGraph, vertex, options, m.sizeInBytes, initialized = false, writeable = true, location = AllocationLocation.Either, alignment = m.alignment)
|
||||
if (log.traceEnabled) log.trace("addr $" + addr.toHexString)
|
||||
onEachVariable(m.name, bank0.index -> addr)
|
||||
onEachVariable(m.name, bank -> addr)
|
||||
onEachVariableEnd(m.name, (if (m.isInstanceOf[MfArray])'a' else 'v') -> (addr + m.sizeInBytes - 1))
|
||||
List(
|
||||
ConstantThing(graveName, NumericConstant(addr, 2), p)
|
||||
)
|
||||
|
@ -224,7 +229,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
}
|
||||
}
|
||||
case f: NormalFunction =>
|
||||
f.environment.allocateVariables(Some(f), mem, callGraph, allocators, options, onEachVariable, pass, forZpOnly)
|
||||
f.environment.allocateVariables(Some(f), mem, callGraph, allocators, options, onEachVariable, onEachVariableEnd, pass, forZpOnly)
|
||||
Nil
|
||||
case _ => Nil
|
||||
}.toList
|
||||
|
@ -290,18 +295,20 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
|
||||
private def removeVariableImpl(str: String): Unit = {
|
||||
def extractThingName(fullName: String): String = {
|
||||
var result = fullName.takeWhile(_ != '.')
|
||||
if (result.length == fullName.length) return result
|
||||
val suffix = fullName.drop(result.length)
|
||||
val ix = fullName.indexOf('.')
|
||||
if (ix < 0) return fullName
|
||||
var result = fullName.substring(0, ix)
|
||||
val suffix = fullName.substring(ix)
|
||||
if (suffix == ".return" || suffix.startsWith(".return.")) {
|
||||
result += ".return"
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
val strWithoutPrefix = str.stripPrefix(prefix)
|
||||
val toRemove = things.keys.filter { n =>
|
||||
val baseName = extractThingName(n)
|
||||
baseName == str || baseName == str.stripPrefix(prefix)
|
||||
baseName == str || baseName == strWithoutPrefix
|
||||
}.toSet
|
||||
removedThings ++= toRemove.map(_.stripPrefix(prefix))
|
||||
things --= toRemove
|
||||
|
@ -332,11 +339,11 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
t.asInstanceOf[T]
|
||||
} else {
|
||||
t match {
|
||||
case Alias(_, target, deprectated) =>
|
||||
case Alias(_, target, deprectated, local) =>
|
||||
if (deprectated && options.flag(CompilationFlag.DeprecationWarning)) {
|
||||
log.warn(s"Alias `$name` is deprecated, use `$target` instead", position)
|
||||
}
|
||||
root.get[T](target)
|
||||
if (local) get[T](target) else root.get[T](target)
|
||||
case _ => throw IdentifierHasWrongTypeOfThingException(clazz, name, position)
|
||||
}
|
||||
}
|
||||
|
@ -360,11 +367,11 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
val t: Thing = things(name)
|
||||
val clazz = implicitly[Manifest[T]].runtimeClass
|
||||
t match {
|
||||
case Alias(_, target, deprectated) =>
|
||||
case Alias(_, target, deprectated, local) =>
|
||||
if (deprectated && options.flag(CompilationFlag.DeprecationWarning)) {
|
||||
log.warn(s"Alias `$name` is deprecated, use `$target` instead")
|
||||
}
|
||||
root.maybeGet[T](target)
|
||||
if (local) maybeGet[T](target) else root.maybeGet[T](target)
|
||||
case _ =>
|
||||
if ((t ne null) && clazz.isInstance(t)) {
|
||||
Some(t.asInstanceOf[T])
|
||||
|
@ -388,6 +395,9 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
}
|
||||
}
|
||||
|
||||
@inline
|
||||
final def identityPage: Constant = maybeGet[MfArray]("identity$").fold(Constant.Zero)(_.toAddress)
|
||||
|
||||
def getPointy(name: String): Pointy = {
|
||||
InitializedMemoryVariable
|
||||
UninitializedMemoryVariable
|
||||
|
@ -549,8 +559,8 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
BranchingOpcodeMapping(Opcode.BPL, IfFlagClear(ZFlag.S), MOpcode.BPL),
|
||||
BranchingOpcodeMapping(Opcode.BMI, IfFlagSet(ZFlag.S), MOpcode.BMI)),
|
||||
None)
|
||||
val byte_and_pointer$ = StructType("byte_and_pointer$", List(FieldDesc("byte", "zp", None), FieldDesc("pointer", "branch", None)), NoAlignment)
|
||||
val hudson_transfer$ = StructType("hudson_transfer$", List(FieldDesc("word", "a", None), FieldDesc("word", "b", None), FieldDesc("word", "c", None)), NoAlignment)
|
||||
val byte_and_pointer$ = StructType("byte_and_pointer$", List(FieldDesc("byte", "zp", false, None), FieldDesc("pointer", "branch", false, None)), NoAlignment)
|
||||
val hudson_transfer$ = StructType("hudson_transfer$", List(FieldDesc("word", "a", false, None), FieldDesc("word", "b", false, None), FieldDesc("word", "c", false, None)), NoAlignment)
|
||||
addThing(byte_and_pointer$, None)
|
||||
addThing(hudson_transfer$, None)
|
||||
Environment.constOnlyBuiltinFunction.foreach(n => addThing(ConstOnlyCallable(n), None))
|
||||
|
@ -715,7 +725,10 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
if (name.startsWith(".")) return Some(MemoryAddressConstant(Label(prefix + name)))
|
||||
vv match {
|
||||
case Some(m) if m.contains(name) => Some(m(name))
|
||||
case _ => maybeGet[ConstantThing](name).map(_.value)
|
||||
case _ => maybeGet[ConstantLikeThing](name).map {
|
||||
case x: ConstantThing => x.value
|
||||
case x: FunctionInMemory => x.toAddress
|
||||
}
|
||||
}
|
||||
case IndexedExpression(arrName, index) =>
|
||||
getPointy(arrName) match {
|
||||
|
@ -832,7 +845,10 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
case Some(c) =>
|
||||
if (c.isProvablyGreaterOrEqualThan(1)) evalImpl(params(1), vv)
|
||||
else if (c.isProvablyZero) evalImpl(params(2), vv)
|
||||
else None
|
||||
else (evalImpl(params(1), vv), evalImpl(params(2), vv)) match {
|
||||
case (Some(t), Some(f)) => Some(IfConstant(c, t, f))
|
||||
case _ => None
|
||||
}
|
||||
case _ => None
|
||||
}
|
||||
} else {
|
||||
|
@ -879,15 +895,17 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
constantOperation(MathOperator.Exor, params, vv)
|
||||
case "||" | "|" =>
|
||||
constantOperation(MathOperator.Or, params, vv)
|
||||
case ">" => evalComparisons(params, vv, _ > _)
|
||||
case "<" => evalComparisons(params, vv, _ < _)
|
||||
case ">=" => evalComparisons(params, vv, _ >= _)
|
||||
case "<=" => evalComparisons(params, vv, _ <= _)
|
||||
case "==" => evalComparisons(params, vv, _ == _)
|
||||
case ">" => evalComparisons(params, vv, MathOperator.Greater, _ > _)
|
||||
case "<" => evalComparisons(params, vv, MathOperator.Less,_ < _)
|
||||
case ">=" => evalComparisons(params, vv, MathOperator.GreaterEqual,_ >= _)
|
||||
case "<=" => evalComparisons(params, vv, MathOperator.LessEqual,_ <= _)
|
||||
case "==" => evalComparisons(params, vv, MathOperator.Equal,_ == _)
|
||||
case "!=" =>
|
||||
sequence(params.map(p => evalImpl(p, vv))) match {
|
||||
case Some(List(NumericConstant(n1, _), NumericConstant(n2, _))) =>
|
||||
Some(if (n1 != n2) Constant.One else Constant.Zero)
|
||||
case Some(List(c1, c2)) =>
|
||||
Some(CompoundConstant(MathOperator.NotEqual, c1, c2))
|
||||
case _ => None
|
||||
}
|
||||
case _ =>
|
||||
|
@ -942,14 +960,22 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
}
|
||||
}
|
||||
|
||||
private def evalComparisons(params: List[Expression], vv: Option[Map[String, Constant]], cond: (Long, Long) => Boolean): Option[Constant] = {
|
||||
private def evalComparisons(params: List[Expression], vv: Option[Map[String, Constant]], operator: MathOperator.Value, cond: (Long, Long) => Boolean): Option[Constant] = {
|
||||
if (params.size < 2) return None
|
||||
val numbers = sequence(params.map{ e =>
|
||||
evalImpl(e, vv) match {
|
||||
case Some(NumericConstant(n, _)) => Some(n)
|
||||
case _ => None
|
||||
}
|
||||
val paramsEvaluated = params.map { e =>
|
||||
evalImpl(e, vv)
|
||||
}
|
||||
val numbers = sequence(paramsEvaluated.map {
|
||||
case Some(NumericConstant(n, _)) => Some(n)
|
||||
case _ => None
|
||||
})
|
||||
if (numbers.isEmpty) {
|
||||
paramsEvaluated match {
|
||||
case List(Some(c1), Some(c2)) =>
|
||||
return Some(CompoundConstant(operator, c1, c2))
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
numbers.map { ns =>
|
||||
if (ns.init.zip(ns.tail).forall(cond.tupled)) Constant.One else Constant.Zero
|
||||
}
|
||||
|
@ -1155,7 +1181,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
case s: CompoundVariableType =>
|
||||
if (s.mutableAlignment ne null) return s.mutableAlignment
|
||||
var alignment = s.baseAlignment
|
||||
for( ResolvedFieldDesc(fieldType, _, _) <- s.mutableFieldsWithTypes) {
|
||||
for( ResolvedFieldDesc(fieldType, _, _, _) <- s.mutableFieldsWithTypes) {
|
||||
val a = getTypeAlignment(fieldType, path + name)
|
||||
if (a eq null) return null
|
||||
alignment = alignment & a
|
||||
|
@ -1175,7 +1201,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
else {
|
||||
val newPath = path + name
|
||||
var sum = 0
|
||||
for( ResolvedFieldDesc(fieldType, _, indexTypeAndCount) <- s.mutableFieldsWithTypes) {
|
||||
for( ResolvedFieldDesc(fieldType, _, _, indexTypeAndCount) <- s.mutableFieldsWithTypes) {
|
||||
val fieldSize = getTypeSize(fieldType, newPath) * indexTypeAndCount.fold(1)(_._2)
|
||||
if (fieldSize < 0) return -1
|
||||
sum = fieldType.alignment.roundSizeUp(sum)
|
||||
|
@ -1188,7 +1214,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
}
|
||||
val b = get[Type]("byte")
|
||||
var offset = 0
|
||||
for( ResolvedFieldDesc(fieldType, fieldName, indexTypeAndCount) <- s.mutableFieldsWithTypes) {
|
||||
for( ResolvedFieldDesc(fieldType, fieldName, _, indexTypeAndCount) <- s.mutableFieldsWithTypes) {
|
||||
offset = fieldType.alignment.roundSizeUp(offset)
|
||||
addThing(ConstantThing(s"$name.$fieldName.offset", NumericConstant(offset, 1), b), None)
|
||||
offset += getTypeSize(fieldType, newPath) * indexTypeAndCount.fold(1)(_._2)
|
||||
|
@ -1201,7 +1227,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
else {
|
||||
val newPath = path + name
|
||||
var max = 0
|
||||
for( ResolvedFieldDesc(fieldType, _, indexTypeAndCount) <- s.mutableFieldsWithTypes) {
|
||||
for( ResolvedFieldDesc(fieldType, _, _, indexTypeAndCount) <- s.mutableFieldsWithTypes) {
|
||||
val fieldSize = getTypeSize(fieldType, newPath) * indexTypeAndCount.fold(1)(_._2)
|
||||
if (fieldSize < 0) return -1
|
||||
max = max max fieldSize
|
||||
|
@ -1211,7 +1237,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
log.error(s"Union `$name` is larger than 255 bytes")
|
||||
}
|
||||
val b = get[Type]("byte")
|
||||
for (ResolvedFieldDesc(fieldType, fieldName, _) <- s.mutableFieldsWithTypes) {
|
||||
for (ResolvedFieldDesc(fieldType, fieldName, _, _) <- s.mutableFieldsWithTypes) {
|
||||
addThing(ConstantThing(s"$name.$fieldName.offset", NumericConstant(0, 1), b), None)
|
||||
}
|
||||
max
|
||||
|
@ -1304,6 +1330,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
if (hasReturnVariable) {
|
||||
registerVariable(VariableDeclarationStatement(stmt.name + ".return", stmt.resultType, None, global = true, stack = false, constant = false, volatile = false, register = false, None, None, Set.empty, None), options, isPointy = false)
|
||||
}
|
||||
val constants = mutable.MutableList[VariableDeclarationStatement]()
|
||||
stmt.statements match {
|
||||
case None =>
|
||||
stmt.address match {
|
||||
|
@ -1326,10 +1353,17 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
}
|
||||
|
||||
case Some(statements) =>
|
||||
statements.foreach {
|
||||
case v: VariableDeclarationStatement => env.registerVariable(v, options, pointies(v.name))
|
||||
case a: ArrayDeclarationStatement => env.registerArray(a, options)
|
||||
case _ => ()
|
||||
if (stmt.isMacro) {
|
||||
statements.foreach {
|
||||
case v: VariableDeclarationStatement => constants += v
|
||||
case _ => ()
|
||||
}
|
||||
} else {
|
||||
statements.foreach {
|
||||
case v: VariableDeclarationStatement => env.registerVariable(v, options, pointies(v.name))
|
||||
case a: ArrayDeclarationStatement => env.registerArray(a, options)
|
||||
case _ => ()
|
||||
}
|
||||
}
|
||||
def scanForLabels(statement: Statement): Unit = statement match {
|
||||
case c: CompoundStatement => c.getChildStatements.foreach(scanForLabels)
|
||||
|
@ -1422,6 +1456,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
params.asInstanceOf[AssemblyOrMacroParamSignature],
|
||||
stmt.assembly,
|
||||
env,
|
||||
constants.toList,
|
||||
executableStatements
|
||||
)
|
||||
addThing(mangled, stmt.position)
|
||||
|
@ -1456,6 +1491,11 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
}
|
||||
}
|
||||
}
|
||||
if (!stmt.isMacro) {
|
||||
val alias = Alias("this.function", name, local = true)
|
||||
env.addThing("this.function", alias, None)
|
||||
env.expandAlias(alias)
|
||||
}
|
||||
}
|
||||
|
||||
private def getFunctionPointerType(f: FunctionInMemory) = f.params.types match {
|
||||
|
@ -1613,9 +1653,9 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
addThing(v, stmt.position)
|
||||
registerAddressConstant(v, stmt.position, options, Some(typ))
|
||||
val addr = v.toAddress
|
||||
for(Subvariable(suffix, offset, t, arraySize) <- getSubvariables(typ)) {
|
||||
for(Subvariable(suffix, offset, vol, t, arraySize) <- getSubvariables(typ)) {
|
||||
if (arraySize.isDefined) ??? // TODO
|
||||
val subv = RelativeVariable(v.name + suffix, addr + offset, t, zeropage = zp, None, isVolatile = v.isVolatile)
|
||||
val subv = RelativeVariable(v.name + suffix, addr + offset, t, zeropage = zp, None, isVolatile = v.isVolatile || vol)
|
||||
addThing(subv, stmt.position)
|
||||
registerAddressConstant(subv, stmt.position, options, Some(t))
|
||||
}
|
||||
|
@ -1657,7 +1697,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
List.fill(tt.size)(LiteralExpression(0, 1))
|
||||
} else {
|
||||
tt.fields.zip(fieldValues).flatMap {
|
||||
case (FieldDesc(fieldTypeName, _, count), expr) =>
|
||||
case (FieldDesc(fieldTypeName, _, _, count), expr) =>
|
||||
// TODO: handle array fields
|
||||
if (count.isDefined) ???
|
||||
extractStructArrayContents(expr, Some(get[Type](fieldTypeName)))
|
||||
|
@ -1694,7 +1734,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
List.fill(tt.size)(LiteralExpression(0, 1))
|
||||
} else {
|
||||
tt.fields.zip(fieldValues).flatMap {
|
||||
case (FieldDesc(fieldTypeName, _, count), expr) =>
|
||||
case (FieldDesc(fieldTypeName, _, _, count), expr) =>
|
||||
// TODO: handle array fields
|
||||
if (count.isDefined) ???
|
||||
extractStructArrayContents(expr, Some(get[Type](fieldTypeName)))
|
||||
|
@ -1738,7 +1778,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
case IndexedExpression(a, i) =>
|
||||
constantsThatShouldHaveBeenImportedEarlier.addBinding(a, node.position)
|
||||
markAsConstantsThatShouldHaveBeenImportedEarlier(i)
|
||||
case DerefExpression(p, _, _) =>
|
||||
case DerefExpression(p, _, _, _) =>
|
||||
markAsConstantsThatShouldHaveBeenImportedEarlier(p)
|
||||
case DerefDebuggingExpression(p, _) =>
|
||||
markAsConstantsThatShouldHaveBeenImportedEarlier(p)
|
||||
|
@ -1749,6 +1789,38 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
|
||||
def extractArrayContents(contents1: ArrayContents): List[Expression] = contents1 match {
|
||||
case LiteralContents(xs) => xs
|
||||
case FileChunkContents(filePath, startE, lengthE) =>
|
||||
val data = Files.readAllBytes(filePath)
|
||||
val p = contents1.position
|
||||
val slice = (eval(startE).map(_.quickSimplify), lengthE.map(l => eval(l).map(_.quickSimplify))) match {
|
||||
case (Some(NumericConstant(start, _)), Some(Some(NumericConstant(length, _)))) =>
|
||||
if (data.length < start) {
|
||||
log.error(s"File $filePath is shorter (${data.length} B) that the start offset $start", p)
|
||||
Array.fill(length.toInt)(0.toByte)
|
||||
} else if (data.length < start + length) {
|
||||
log.error(s"File $filePath is shorter (${data.length} B) that the start offset plus length ${start + length}", p)
|
||||
Array.fill(length.toInt)(0.toByte)
|
||||
} else {
|
||||
data.slice(start.toInt, start.toInt + length.toInt)
|
||||
}
|
||||
case (Some(NumericConstant(start, _)), None) =>
|
||||
if (data.length < start) {
|
||||
log.error(s"File $filePath is shorter (${data.length} B) that the start offset $start", p)
|
||||
Array[Byte](0)
|
||||
} else {
|
||||
data.drop(start.toInt)
|
||||
}
|
||||
case (None, Some(Some(_))) =>
|
||||
log.error(s"Start offset is not a constant", p)
|
||||
Array[Byte](0)
|
||||
case (_, Some(None)) =>
|
||||
log.error(s"Length is not a constant", p)
|
||||
Array[Byte](0)
|
||||
case (None, Some(None)) =>
|
||||
log.error(s"Start offset and length are not constants", p)
|
||||
Array[Byte](0)
|
||||
}
|
||||
slice.map(c => LiteralExpression(c & 0xff, 1)).toList
|
||||
case CombinedContents(xs) => xs.flatMap(extractArrayContents)
|
||||
case pc@ProcessedContents("struct", xs: CombinedContents) =>
|
||||
checkIfArrayContentsAreSimple(xs)
|
||||
|
@ -1896,6 +1968,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
}
|
||||
|
||||
def registerArray(stmt: ArrayDeclarationStatement, options: CompilationOptions): Unit = {
|
||||
new OverflowDetector(this, options).detectOverflow(stmt)
|
||||
if (options.flag(CompilationFlag.LUnixRelocatableCode) && stmt.alignment.exists(_.isMultiplePages)) {
|
||||
log.error("Invalid alignment for LUnix code", stmt.position)
|
||||
}
|
||||
|
@ -2057,6 +2130,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
}
|
||||
|
||||
def registerVariable(stmt: VariableDeclarationStatement, options: CompilationOptions, isPointy: Boolean): Unit = {
|
||||
new OverflowDetector(this, options).detectOverflow(stmt)
|
||||
val name = stmt.name
|
||||
val position = stmt.position
|
||||
if (name == "" || name.contains(".") && !name.contains(".return")) {
|
||||
|
@ -2097,7 +2171,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
val constantValue = rawConstantValue.fitInto(typ)
|
||||
if (constantValue.requiredSize > typ.size) log.error(s"`$name` is has an invalid value: not in the range of `$typ`", position)
|
||||
addThing(ConstantThing(prefix + name, constantValue, typ), stmt.position)
|
||||
for(Subvariable(suffix, offset, t, arraySize) <- getSubvariables(typ)) {
|
||||
for(Subvariable(suffix, offset, vol, t, arraySize) <- getSubvariables(typ)) {
|
||||
if (arraySize.isDefined) {
|
||||
log.error(s"Constants of type ${t.name} that contains array fields are not supported", stmt.position)
|
||||
} else {
|
||||
|
@ -2178,7 +2252,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
variable match {
|
||||
case v: StackVariable =>
|
||||
addThing(localName, v, position)
|
||||
for (Subvariable(suffix, offset, t, arraySize) <- getSubvariables(v.typ)) {
|
||||
for (Subvariable(suffix, offset, vol, t, arraySize) <- getSubvariables(v.typ)) {
|
||||
if (arraySize.isDefined) {
|
||||
log.error(s"Cannot create a stack variable $localName of compound type ${v.typ.name} that contains an array member", position)
|
||||
} else {
|
||||
|
@ -2187,10 +2261,10 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
}
|
||||
case v: MemoryVariable =>
|
||||
addThing(localName, v, position)
|
||||
for (Subvariable(suffix, offset, t, arrayIndexTypeAndSize) <- getSubvariables(v.typ)) {
|
||||
for (Subvariable(suffix, offset, vol, t, arrayIndexTypeAndSize) <- getSubvariables(v.typ)) {
|
||||
arrayIndexTypeAndSize match {
|
||||
case None =>
|
||||
val subv = RelativeVariable(prefix + localName + suffix, v.toAddress + offset, t, zeropage = v.zeropage, declaredBank = v.declaredBank, isVolatile = v.isVolatile)
|
||||
val subv = RelativeVariable(prefix + localName + suffix, v.toAddress + offset, t, zeropage = v.zeropage, declaredBank = v.declaredBank, isVolatile = v.isVolatile || vol)
|
||||
addThing(subv, position)
|
||||
registerAddressConstant(subv, position, options, Some(t))
|
||||
case Some((indexType, elemCount)) =>
|
||||
|
@ -2202,10 +2276,10 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
case v: VariableInMemory =>
|
||||
addThing(localName, v, position)
|
||||
addThing(ConstantThing(v.name + "`", v.toAddress, get[Type]("word")), position)
|
||||
for (Subvariable(suffix, offset, t, arrayIndexTypeAndSize) <- getSubvariables(v.typ)) {
|
||||
for (Subvariable(suffix, offset, vol, t, arrayIndexTypeAndSize) <- getSubvariables(v.typ)) {
|
||||
arrayIndexTypeAndSize match {
|
||||
case None =>
|
||||
val subv = RelativeVariable(prefix + localName + suffix, v.toAddress + offset, t, zeropage = v.zeropage, declaredBank = v.declaredBank, isVolatile = v.isVolatile)
|
||||
val subv = RelativeVariable(prefix + localName + suffix, v.toAddress + offset, t, zeropage = v.zeropage, declaredBank = v.declaredBank, isVolatile = v.isVolatile || vol)
|
||||
addThing(subv, position)
|
||||
registerAddressConstant(subv, position, options, Some(t))
|
||||
case Some((indexType, elemCount)) =>
|
||||
|
@ -2218,6 +2292,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
}
|
||||
}
|
||||
|
||||
//noinspection NameBooleanParameters
|
||||
def getSubvariables(typ: Type): List[Subvariable] = {
|
||||
val b = get[VariableType]("byte")
|
||||
val w = get[VariableType]("word")
|
||||
|
@ -2225,109 +2300,109 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
if (options.isBigEndian) {
|
||||
throw new IllegalArgumentException("__reg$type on 6809???")
|
||||
}
|
||||
return Subvariable(".lo", 0, b) ::
|
||||
Subvariable(".hi", 1, b) ::
|
||||
Subvariable(".loword", 0, w) ::
|
||||
Subvariable(".loword.lo", 0, b) ::
|
||||
Subvariable(".loword.hi", 1, b) ::
|
||||
Subvariable(".b2b3", 2, w) ::
|
||||
Subvariable(".b2b3.lo", 2, b) ::
|
||||
Subvariable(".b2b3.hi", 3, b) ::
|
||||
List.tabulate(typ.size) { i => Subvariable(".b" + i, i, b) }
|
||||
return Subvariable(".lo", 0, false, b) ::
|
||||
Subvariable(".hi", 1, false, b) ::
|
||||
Subvariable(".loword", 0, false, w) ::
|
||||
Subvariable(".loword.lo", 0, false, b) ::
|
||||
Subvariable(".loword.hi", 1, false, b) ::
|
||||
Subvariable(".b2b3", 2, false, w) ::
|
||||
Subvariable(".b2b3.lo", 2, false, b) ::
|
||||
Subvariable(".b2b3.hi", 3, false, b) ::
|
||||
List.tabulate(typ.size) { i => Subvariable(".b" + i, i, false, b) }
|
||||
}
|
||||
typ match {
|
||||
case _: PlainType => typ.size match {
|
||||
case 2 => if (options.isBigEndian) List(
|
||||
Subvariable(".lo", 1, b),
|
||||
Subvariable(".hi", 0, b)
|
||||
Subvariable(".lo", 1, false, b),
|
||||
Subvariable(".hi", 0, false, b)
|
||||
) else List(
|
||||
Subvariable(".lo", 0, b),
|
||||
Subvariable(".hi", 1, b))
|
||||
Subvariable(".lo", 0, false, b),
|
||||
Subvariable(".hi", 1, false, b))
|
||||
case 3 => if (options.isBigEndian) List(
|
||||
Subvariable(".loword", 1, w),
|
||||
Subvariable(".loword.lo", 2, b),
|
||||
Subvariable(".loword.hi", 1, b),
|
||||
Subvariable(".hiword", 0, w),
|
||||
Subvariable(".hiword.lo", 1, b),
|
||||
Subvariable(".hiword.hi", 0, b),
|
||||
Subvariable(".lo", 2, b),
|
||||
Subvariable(".b0", 2, b),
|
||||
Subvariable(".b1", 1, b),
|
||||
Subvariable(".b2", 0, b)
|
||||
Subvariable(".loword", 1, false, w),
|
||||
Subvariable(".loword.lo", 2, false, b),
|
||||
Subvariable(".loword.hi", 1, false, b),
|
||||
Subvariable(".hiword", 0, false, w),
|
||||
Subvariable(".hiword.lo", 1, false, b),
|
||||
Subvariable(".hiword.hi", 0, false, b),
|
||||
Subvariable(".lo", 2, false, b),
|
||||
Subvariable(".b0", 2, false, b),
|
||||
Subvariable(".b1", 1, false, b),
|
||||
Subvariable(".b2", 0, false, b)
|
||||
) else List(
|
||||
Subvariable(".loword", 0, w),
|
||||
Subvariable(".loword.lo", 0, b),
|
||||
Subvariable(".loword.hi", 1, b),
|
||||
Subvariable(".hiword", 1, w),
|
||||
Subvariable(".hiword.lo", 1, b),
|
||||
Subvariable(".hiword.hi", 2, b),
|
||||
Subvariable(".lo", 0, b),
|
||||
Subvariable(".b0", 0, b),
|
||||
Subvariable(".b1", 1, b),
|
||||
Subvariable(".b2", 2, b))
|
||||
Subvariable(".loword", 0, false, w),
|
||||
Subvariable(".loword.lo", 0, false, b),
|
||||
Subvariable(".loword.hi", 1, false, b),
|
||||
Subvariable(".hiword", 1, false, w),
|
||||
Subvariable(".hiword.lo", 1, false, b),
|
||||
Subvariable(".hiword.hi", 2, false, b),
|
||||
Subvariable(".lo", 0, false, b),
|
||||
Subvariable(".b0", 0, false, b),
|
||||
Subvariable(".b1", 1, false, b),
|
||||
Subvariable(".b2", 2, false, b))
|
||||
case 4 => if (options.isBigEndian) List(
|
||||
Subvariable(".loword", 2, w),
|
||||
Subvariable(".hiword", 0, w),
|
||||
Subvariable(".loword.lo", 3, b),
|
||||
Subvariable(".loword.hi", 2, b),
|
||||
Subvariable(".hiword.lo", 1, b),
|
||||
Subvariable(".hiword.hi", 0, b),
|
||||
Subvariable(".lo", 3, b),
|
||||
Subvariable(".b0", 3, b),
|
||||
Subvariable(".b1", 2, b),
|
||||
Subvariable(".b2", 1, b),
|
||||
Subvariable(".b3", 0, b)
|
||||
Subvariable(".loword", 2, false, w),
|
||||
Subvariable(".hiword", 0, false, w),
|
||||
Subvariable(".loword.lo", 3, false, b),
|
||||
Subvariable(".loword.hi", 2, false, b),
|
||||
Subvariable(".hiword.lo", 1, false, b),
|
||||
Subvariable(".hiword.hi", 0, false, b),
|
||||
Subvariable(".lo", 3, false, b),
|
||||
Subvariable(".b0", 3, false, b),
|
||||
Subvariable(".b1", 2, false, b),
|
||||
Subvariable(".b2", 1, false, b),
|
||||
Subvariable(".b3", 0, false, b)
|
||||
) else List(
|
||||
Subvariable(".loword", 0, w),
|
||||
Subvariable(".hiword", 2, w),
|
||||
Subvariable(".loword.lo", 0, b),
|
||||
Subvariable(".loword.hi", 1, b),
|
||||
Subvariable(".hiword.lo", 2, b),
|
||||
Subvariable(".hiword.hi", 3, b),
|
||||
Subvariable(".lo", 0, b),
|
||||
Subvariable(".b0", 0, b),
|
||||
Subvariable(".b1", 1, b),
|
||||
Subvariable(".b2", 2, b),
|
||||
Subvariable(".b3", 3, b)
|
||||
Subvariable(".loword", 0, false, w),
|
||||
Subvariable(".hiword", 2, false, w),
|
||||
Subvariable(".loword.lo", 0, false, b),
|
||||
Subvariable(".loword.hi", 1, false, b),
|
||||
Subvariable(".hiword.lo", 2, false, b),
|
||||
Subvariable(".hiword.hi", 3, false, b),
|
||||
Subvariable(".lo", 0, false, b),
|
||||
Subvariable(".b0", 0, false, b),
|
||||
Subvariable(".b1", 1, false, b),
|
||||
Subvariable(".b2", 2, false, b),
|
||||
Subvariable(".b3", 3, false, b)
|
||||
)
|
||||
case sz if sz > 4 =>
|
||||
if (options.isBigEndian) {
|
||||
Subvariable(".lo", sz - 1, b) ::
|
||||
Subvariable(".loword", sz - 2, w) ::
|
||||
Subvariable(".loword.lo", sz - 1, b) ::
|
||||
Subvariable(".loword.hi", sz - 2, b) ::
|
||||
List.tabulate(sz){ i => Subvariable(".b" + i, sz - 1 - i, b) }
|
||||
Subvariable(".lo", sz - 1, false, b) ::
|
||||
Subvariable(".loword", sz - 2, false, w) ::
|
||||
Subvariable(".loword.lo", sz - 1, false, b) ::
|
||||
Subvariable(".loword.hi", sz - 2, false, b) ::
|
||||
List.tabulate(sz){ i => Subvariable(".b" + i, sz - 1 - i, false, b) }
|
||||
} else {
|
||||
Subvariable(".lo", 0, b) ::
|
||||
Subvariable(".loword", 0, w) ::
|
||||
Subvariable(".loword.lo", 0, b) ::
|
||||
Subvariable(".loword.hi", 1, b) ::
|
||||
List.tabulate(sz){ i => Subvariable(".b" + i, i, b) }
|
||||
Subvariable(".lo", 0, false, b) ::
|
||||
Subvariable(".loword", 0, false, w) ::
|
||||
Subvariable(".loword.lo", 0, false, b) ::
|
||||
Subvariable(".loword.hi", 1, false, b) ::
|
||||
List.tabulate(sz){ i => Subvariable(".b" + i, i, false, b) }
|
||||
}
|
||||
case _ => Nil
|
||||
}
|
||||
case InterruptPointerType | _: FunctionPointerType | _: PointerType => if (options.isBigEndian) List(
|
||||
Subvariable(".raw", 0, get[VariableType]("pointer")),
|
||||
Subvariable(".raw.lo", 1, b),
|
||||
Subvariable(".raw.hi", 0, b),
|
||||
Subvariable(".lo", 1, b),
|
||||
Subvariable(".hi", 0, b)
|
||||
Subvariable(".raw", 0, false, get[VariableType]("pointer")),
|
||||
Subvariable(".raw.lo", 1, false, b),
|
||||
Subvariable(".raw.hi", 0, false, b),
|
||||
Subvariable(".lo", 1, false, b),
|
||||
Subvariable(".hi", 0, false, b)
|
||||
) else List(
|
||||
Subvariable(".raw", 0, get[VariableType]("pointer")),
|
||||
Subvariable(".raw.lo", 0, b),
|
||||
Subvariable(".raw.hi", 1, b),
|
||||
Subvariable(".lo", 0, b),
|
||||
Subvariable(".hi", 1, b))
|
||||
Subvariable(".raw", 0, false, get[VariableType]("pointer")),
|
||||
Subvariable(".raw.lo", 0, false, b),
|
||||
Subvariable(".raw.hi", 1, false, b),
|
||||
Subvariable(".lo", 0, false, b),
|
||||
Subvariable(".hi", 1, false, b))
|
||||
case s: StructType =>
|
||||
val builder = new ListBuffer[Subvariable]
|
||||
var offset = 0
|
||||
for(ResolvedFieldDesc(typ, fieldName, indexTypeAndCount) <- s.mutableFieldsWithTypes) {
|
||||
for(ResolvedFieldDesc(typ, fieldName, vol, indexTypeAndCount) <- s.mutableFieldsWithTypes) {
|
||||
offset = getTypeAlignment(typ, Set()).roundSizeUp(offset)
|
||||
val suffix = "." + fieldName
|
||||
builder += Subvariable(suffix, offset, typ, indexTypeAndCount)
|
||||
builder += Subvariable(suffix, offset, vol, typ, indexTypeAndCount)
|
||||
if (indexTypeAndCount.isEmpty) {
|
||||
builder ++= getSubvariables(typ).map {
|
||||
case Subvariable(innerSuffix, innerOffset, innerType, innerSize) => Subvariable(suffix + innerSuffix, offset + innerOffset, innerType, innerSize)
|
||||
case Subvariable(innerSuffix, innerOffset, innerVolatile, innerType, innerSize) => Subvariable(suffix + innerSuffix, offset + innerOffset, vol || innerVolatile, innerType, innerSize)
|
||||
}
|
||||
}
|
||||
offset += typ.size * indexTypeAndCount.fold(1)(_._2)
|
||||
|
@ -2336,13 +2411,12 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
builder.toList
|
||||
case s: UnionType =>
|
||||
val builder = new ListBuffer[Subvariable]
|
||||
for(FieldDesc(typeName, fieldName, arraySize) <- s.fields) {
|
||||
val typ = get[VariableType](typeName)
|
||||
for(ResolvedFieldDesc(typ, fieldName, vol1, indexTypeAndCount) <- s.mutableFieldsWithTypes) {
|
||||
val suffix = "." + fieldName
|
||||
builder += Subvariable(suffix, 0, typ)
|
||||
if (arraySize.isEmpty) {
|
||||
builder += Subvariable(suffix, 0, vol1, typ)
|
||||
if (indexTypeAndCount.isEmpty) {
|
||||
builder ++= getSubvariables(typ).map {
|
||||
case Subvariable(innerSuffix, innerOffset, innerType, innerSize) => Subvariable(suffix + innerSuffix, innerOffset, innerType, innerSize)
|
||||
case Subvariable(innerSuffix, innerOffset, vol2, innerType, innerSize) => Subvariable(suffix + innerSuffix, innerOffset, vol1 || vol2, innerType, innerSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2419,19 +2493,28 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
private def expandAliases(): Unit = {
|
||||
val aliasesToAdd = mutable.ListBuffer[Alias]()
|
||||
things.values.foreach{
|
||||
case Alias(aliasName, target, deprecated) =>
|
||||
val prefix = target + "."
|
||||
things.foreach{
|
||||
case (thingName, thing) =>
|
||||
if (thingName.startsWith(prefix)) {
|
||||
aliasesToAdd += Alias(aliasName + "." + thingName.stripPrefix(prefix), thingName, deprecated)
|
||||
}
|
||||
}
|
||||
case a:Alias => aliasesToAdd ++= expandAliasImpl(a)
|
||||
case _ => ()
|
||||
}
|
||||
aliasesToAdd.foreach(a => things += a.name -> a)
|
||||
}
|
||||
|
||||
private def expandAliasImpl(a: Alias): Seq[Alias] = {
|
||||
val aliasesToAdd = mutable.ListBuffer[Alias]()
|
||||
val prefix = a.target + "."
|
||||
root.things.foreach {
|
||||
case (thingName, thing) =>
|
||||
if (thingName.startsWith(prefix)) {
|
||||
aliasesToAdd += Alias(a.name + "." + thingName.stripPrefix(prefix), thingName, a.deprecated, a.local)
|
||||
}
|
||||
}
|
||||
aliasesToAdd
|
||||
}
|
||||
|
||||
private def expandAlias(a: Alias): Unit = {
|
||||
expandAliasImpl(a).foreach(a => things += a.name -> a)
|
||||
}
|
||||
|
||||
def fixStructAlignments(): Unit = {
|
||||
val allStructTypes: Iterable[CompoundVariableType] = things.values.flatMap {
|
||||
case s@StructType(name, _, _) => Some(s)
|
||||
|
@ -2526,11 +2609,11 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
things.values.foreach {
|
||||
case st@StructType(_, fields, _) =>
|
||||
st.mutableFieldsWithTypes = fields.map {
|
||||
case FieldDesc(tn, name, arraySize) => ResolvedFieldDesc(get[VariableType](tn), name, arraySize.map(getArrayFieldIndexTypeAndSize))
|
||||
case FieldDesc(tn, name, vol, arraySize) => ResolvedFieldDesc(get[VariableType](tn), name, vol, arraySize.map(getArrayFieldIndexTypeAndSize))
|
||||
}
|
||||
case ut@UnionType(_, fields, _) =>
|
||||
ut.mutableFieldsWithTypes = fields.map {
|
||||
case FieldDesc(tn, name, arraySize) => ResolvedFieldDesc(get[VariableType](tn), name, arraySize.map(getArrayFieldIndexTypeAndSize))
|
||||
case FieldDesc(tn, name, vol, arraySize) => ResolvedFieldDesc(get[VariableType](tn), name, vol, arraySize.map(getArrayFieldIndexTypeAndSize))
|
||||
}
|
||||
case _ => ()
|
||||
}
|
||||
|
@ -2540,7 +2623,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
def collectDeclarations(program: Program, options: CompilationOptions): Unit = {
|
||||
val b = get[VariableType]("byte")
|
||||
val v = get[Type]("void")
|
||||
if (options.flag(CompilationFlag.OptimizeForSonicSpeed)) {
|
||||
if (options.flag(CompilationFlag.IdentityPage)) {
|
||||
addThing(InitializedArray("identity$", None, IndexedSeq.tabulate(256)(n => LiteralExpression(n, 1)), declaredBank = None, b, b, readOnly = true, Set.empty, defaultArrayAlignment(options, 256)), None)
|
||||
}
|
||||
program.declarations.foreach {
|
||||
|
@ -2605,7 +2688,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
}
|
||||
|
||||
if (!things.contains("memory_barrier")) {
|
||||
things("memory_barrier") = MacroFunction("memory_barrier", v, AssemblyOrMacroParamSignature(Nil), isInAssembly = true, this, CpuFamily.forType(options.platform.cpu) match {
|
||||
things("memory_barrier") = MacroFunction("memory_barrier", v, AssemblyOrMacroParamSignature(Nil), isInAssembly = true, this, Nil, CpuFamily.forType(options.platform.cpu) match {
|
||||
case CpuFamily.M6502 => List(MosAssemblyStatement(Opcode.CHANGED_MEM, AddrMode.DoesNotExist, LiteralExpression(0, 1), Elidability.Fixed))
|
||||
case CpuFamily.I80 => List(Z80AssemblyStatement(ZOpcode.CHANGED_MEM, NoRegisters, None, LiteralExpression(0, 1), Elidability.Fixed))
|
||||
case CpuFamily.I86 => List(Z80AssemblyStatement(ZOpcode.CHANGED_MEM, NoRegisters, None, LiteralExpression(0, 1), Elidability.Fixed))
|
||||
|
@ -2617,7 +2700,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
if (!things.contains("breakpoint")) {
|
||||
val p = get[VariableType]("pointer")
|
||||
if (options.flag(CompilationFlag.EnableBreakpoints)) {
|
||||
things("breakpoint") = MacroFunction("breakpoint", v, AssemblyOrMacroParamSignature(Nil), isInAssembly = true, this, CpuFamily.forType(options.platform.cpu) match {
|
||||
things("breakpoint") = MacroFunction("breakpoint", v, AssemblyOrMacroParamSignature(Nil), isInAssembly = true, this, Nil, CpuFamily.forType(options.platform.cpu) match {
|
||||
case CpuFamily.M6502 => List(MosAssemblyStatement(Opcode.CHANGED_MEM, AddrMode.DoesNotExist, VariableExpression("..brk"), Elidability.Fixed))
|
||||
case CpuFamily.I80 => List(Z80AssemblyStatement(ZOpcode.CHANGED_MEM, NoRegisters, None, VariableExpression("..brk"), Elidability.Fixed))
|
||||
case CpuFamily.I86 => List(Z80AssemblyStatement(ZOpcode.CHANGED_MEM, NoRegisters, None, VariableExpression("..brk"), Elidability.Fixed))
|
||||
|
@ -2625,7 +2708,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
case _ => ???
|
||||
})
|
||||
} else {
|
||||
things("breakpoint") = MacroFunction("breakpoint", v, AssemblyOrMacroParamSignature(Nil), isInAssembly = true, this, Nil)
|
||||
things("breakpoint") = MacroFunction("breakpoint", v, AssemblyOrMacroParamSignature(Nil), isInAssembly = true, this, Nil, Nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2713,7 +2796,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
nameCheck(index)
|
||||
case DerefDebuggingExpression(inner, _) =>
|
||||
nameCheck(inner)
|
||||
case DerefExpression(inner, _, _) =>
|
||||
case DerefExpression(inner, _, _, _) =>
|
||||
nameCheck(inner)
|
||||
case IndirectFieldExpression(inner, firstIndices, fields) =>
|
||||
nameCheck(inner)
|
||||
|
@ -2745,7 +2828,7 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa
|
|||
|
||||
def getAliases: Map[String, String] = {
|
||||
things.values.flatMap {
|
||||
case Alias(a, b, _) => Some(a -> b)
|
||||
case Alias(a, b, _, _) => Some(a -> b)
|
||||
case _ => None
|
||||
}.toMap ++ parent.map(_.getAliases).getOrElse(Map.empty)
|
||||
}
|
||||
|
|
134
src/main/scala/millfork/env/OverflowDetector.scala
vendored
Normal file
134
src/main/scala/millfork/env/OverflowDetector.scala
vendored
Normal file
|
@ -0,0 +1,134 @@
|
|||
package millfork.env
|
||||
|
||||
import millfork.{CompilationFlag, CompilationOptions}
|
||||
import millfork.compiler.{AbstractExpressionCompiler, CompilationContext}
|
||||
import millfork.error.Logger
|
||||
import millfork.node._
|
||||
|
||||
/**
|
||||
* @author Karol Stasiak
|
||||
*/
|
||||
class OverflowDetector(env: Environment, options: CompilationOptions) {
|
||||
|
||||
def this(ctx: CompilationContext) {
|
||||
this(ctx.env, ctx.options)
|
||||
}
|
||||
|
||||
private def log: Logger = options.log
|
||||
|
||||
private def isWord(e: Expression): Boolean =
|
||||
AbstractExpressionCompiler.getExpressionType(env, log, e) match {
|
||||
case t: PlainType => t.size == 2
|
||||
case _ => false
|
||||
}
|
||||
|
||||
private def isWord(typeName: String): Boolean =
|
||||
env.maybeGet[Thing](typeName) match {
|
||||
case Some(t: PlainType) => t.size == 2
|
||||
case _ => false
|
||||
}
|
||||
|
||||
private def isWord(typ: Type): Boolean =
|
||||
typ match {
|
||||
case t: PlainType => t.size == 2
|
||||
case _ => false
|
||||
}
|
||||
|
||||
private def isByte(e: Expression): Boolean =
|
||||
AbstractExpressionCompiler.getExpressionType(env, log, e) match {
|
||||
case t: PlainType => t.size == 1
|
||||
case _ => false
|
||||
}
|
||||
|
||||
def warnConstantOverflow(e: Expression, op: String): Unit = {
|
||||
if (options.flag(CompilationFlag.ByteOverflowWarning)) {
|
||||
log.warn(s"Constant byte overflow. Consider wrapping one of the arguments of $op with word( )", e.position)
|
||||
}
|
||||
}
|
||||
|
||||
def warnDynamicOverflow(e: Expression, op: String): Unit = {
|
||||
if (options.flag(CompilationFlag.ByteOverflowWarning)) {
|
||||
log.warn(s"Potential byte overflow. Consider wrapping one of the arguments of $op with word( )", e.position)
|
||||
}
|
||||
}
|
||||
|
||||
def scanExpression(e: Expression, willBeAssignedToWord: Boolean): Unit = {
|
||||
if (willBeAssignedToWord) {
|
||||
e match {
|
||||
case FunctionCallExpression("<<", List(l, r)) =>
|
||||
if (isByte(l) && isByte(r)) {
|
||||
(env.eval(l), env.eval(r)) match {
|
||||
case (Some(NumericConstant(lc, 1)), Some(NumericConstant(rc, 1))) =>
|
||||
if (lc >= 0 && rc >= 0 && (lc << rc) > 255) {
|
||||
warnConstantOverflow(e, "<<")
|
||||
}
|
||||
case (_, Some(NumericConstant(0, _))) =>
|
||||
case _ =>
|
||||
warnDynamicOverflow(e, "<<")
|
||||
}
|
||||
}
|
||||
case FunctionCallExpression("*", List(l, r)) =>
|
||||
if (isByte(l) && isByte(r)) {
|
||||
(env.eval(l), env.eval(r)) match {
|
||||
case (Some(NumericConstant(lc, 1)), Some(NumericConstant(rc, 1))) =>
|
||||
if (lc >= 0 && rc >= 0 && (lc * rc) > 255) {
|
||||
warnConstantOverflow(e, "*")
|
||||
}
|
||||
case (_, Some(NumericConstant(0, _))) =>
|
||||
case (_, Some(NumericConstant(1, _))) =>
|
||||
case (Some(NumericConstant(0, _)), _) =>
|
||||
case (Some(NumericConstant(1, _)), _) =>
|
||||
case _ =>
|
||||
warnDynamicOverflow(e, "*")
|
||||
}
|
||||
}
|
||||
case FunctionCallExpression("word" | "unsigned16" | "signed16" | "pointer", List(SumExpression(expressions, _))) =>
|
||||
if (expressions.map(_._2).forall(isByte)) {
|
||||
|
||||
}
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
e match {
|
||||
case SumExpression(expressions, decimal) =>
|
||||
if (willBeAssignedToWord && !decimal && isByte(e)) env.eval(e) match {
|
||||
case Some(NumericConstant(n, _)) if n < -128 || n > 255 =>
|
||||
warnConstantOverflow(e, "+")
|
||||
case _ =>
|
||||
}
|
||||
for ((_, e) <- expressions) {
|
||||
scanExpression(e, willBeAssignedToWord = willBeAssignedToWord)
|
||||
}
|
||||
case FunctionCallExpression("word" | "unsigned16" | "signed16" | "pointer", expressions) =>
|
||||
expressions.foreach(x => scanExpression(x, willBeAssignedToWord = true))
|
||||
case FunctionCallExpression("|" | "^" | "&" | "not", expressions) =>
|
||||
expressions.foreach(x => scanExpression(x, willBeAssignedToWord = false))
|
||||
case FunctionCallExpression(fname, expressions) =>
|
||||
env.maybeGet[Thing](fname) match {
|
||||
case Some(f: FunctionInMemory) if f.params.length == expressions.length =>
|
||||
for ((e, t) <- expressions zip f.params.types) {
|
||||
scanExpression(e, willBeAssignedToWord = isWord(t))
|
||||
}
|
||||
case _ =>
|
||||
for (e <- expressions) {
|
||||
scanExpression(e, willBeAssignedToWord = false)
|
||||
}
|
||||
}
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
|
||||
def detectOverflow(stmt: Statement): Unit = {
|
||||
stmt match {
|
||||
case Assignment(lhs, rhs) =>
|
||||
if (isWord(lhs)) scanExpression(rhs, willBeAssignedToWord = true)
|
||||
case v: VariableDeclarationStatement =>
|
||||
v.initialValue match {
|
||||
case Some(e) => scanExpression(e, willBeAssignedToWord = isWord(v.typ))
|
||||
case _ =>
|
||||
}
|
||||
case s =>
|
||||
s.getAllExpressions.foreach(e => scanExpression(e, willBeAssignedToWord = false))
|
||||
}
|
||||
}
|
||||
}
|
49
src/main/scala/millfork/env/Thing.scala
vendored
49
src/main/scala/millfork/env/Thing.scala
vendored
|
@ -1,7 +1,7 @@
|
|||
package millfork.env
|
||||
|
||||
import millfork.assembly.BranchingOpcodeMapping
|
||||
import millfork.{CompilationFlag, CompilationOptions, CpuFamily}
|
||||
import millfork.{CompilationFlag, CompilationOptions, CpuFamily, env}
|
||||
import millfork.node._
|
||||
import millfork.output.{MemoryAlignment, NoAlignment}
|
||||
|
||||
|
@ -10,7 +10,7 @@ sealed trait Thing {
|
|||
def rootName: String = name
|
||||
}
|
||||
|
||||
case class Alias(name: String, target: String, deprecated: Boolean = false) extends Thing
|
||||
case class Alias(name: String, target: String, deprecated: Boolean = false, local: Boolean = false) extends Thing
|
||||
|
||||
sealed trait CallableThing extends Thing
|
||||
|
||||
|
@ -55,7 +55,7 @@ sealed trait VariableType extends Type {
|
|||
|
||||
}
|
||||
|
||||
case class Subvariable(suffix: String, offset: Int, typ: VariableType, arrayIndexTypeAndSize: Option[(VariableType, Int)] = None)
|
||||
case class Subvariable(suffix: String, offset: Int, isVolatile: Boolean, typ: VariableType, arrayIndexTypeAndSize: Option[(VariableType, Int)] = None)
|
||||
|
||||
case object VoidType extends Type {
|
||||
def size = 0
|
||||
|
@ -69,6 +69,18 @@ case object VoidType extends Type {
|
|||
override def alignment: MemoryAlignment = NoAlignment
|
||||
}
|
||||
|
||||
case class InvalidType(nonce: Long) extends Type {
|
||||
def size = 0
|
||||
|
||||
def alignedSize = 0
|
||||
|
||||
def isSigned = false
|
||||
|
||||
override def name = "$invalid"
|
||||
|
||||
override def alignment: MemoryAlignment = NoAlignment
|
||||
}
|
||||
|
||||
sealed trait PlainType extends VariableType {
|
||||
override def isCompatible(other: Type): Boolean = this == other || this.isSubtypeOf(other) || other.isSubtypeOf(this)
|
||||
|
||||
|
@ -248,6 +260,10 @@ sealed trait TypedThing extends Thing {
|
|||
def typ: Type
|
||||
}
|
||||
|
||||
sealed trait ConstantLikeThing extends TypedThing {
|
||||
|
||||
}
|
||||
|
||||
|
||||
sealed trait ThingInMemory extends Thing {
|
||||
def zeropage: Boolean
|
||||
|
@ -490,6 +506,8 @@ sealed trait MangledFunction extends CallableThing {
|
|||
|
||||
def interrupt: Boolean
|
||||
|
||||
def kernalInterrupt: Boolean
|
||||
|
||||
def isConstPure: Boolean
|
||||
|
||||
def canBePointedTo: Boolean
|
||||
|
@ -504,6 +522,8 @@ case class EmptyFunction(name: String,
|
|||
|
||||
override def interrupt = false
|
||||
|
||||
override def kernalInterrupt: Boolean = false
|
||||
|
||||
override def isConstPure = false
|
||||
|
||||
override def canBePointedTo: Boolean = false
|
||||
|
@ -514,15 +534,18 @@ case class MacroFunction(name: String,
|
|||
params: AssemblyOrMacroParamSignature,
|
||||
isInAssembly: Boolean,
|
||||
environment: Environment,
|
||||
constants: List[VariableDeclarationStatement],
|
||||
code: List[ExecutableStatement]) extends MangledFunction {
|
||||
override def interrupt = false
|
||||
|
||||
override def kernalInterrupt: Boolean = false
|
||||
|
||||
override def isConstPure = false
|
||||
|
||||
override def canBePointedTo: Boolean = false
|
||||
}
|
||||
|
||||
sealed trait FunctionInMemory extends MangledFunction with ThingInMemory {
|
||||
sealed trait FunctionInMemory extends MangledFunction with ThingInMemory with TypedThing with VariableLikeThing with ConstantLikeThing {
|
||||
def environment: Environment
|
||||
|
||||
override def isFar(compilationOptions: CompilationOptions): Boolean =
|
||||
|
@ -538,6 +561,20 @@ sealed trait FunctionInMemory extends MangledFunction with ThingInMemory {
|
|||
def optimizationHints: Set[String]
|
||||
|
||||
override def hasOptimizationHints: Boolean = optimizationHints.nonEmpty
|
||||
|
||||
override lazy val typ: Type = {
|
||||
if (interrupt || kernalInterrupt) {
|
||||
InvalidType(name.hashCode())
|
||||
} else {
|
||||
val paramType = params.types match {
|
||||
case Nil => VoidType
|
||||
case List(t) => t
|
||||
case _ => InvalidType(params.types.hashCode())
|
||||
}
|
||||
val typeName = "function." + paramType.name + ".to." + returnType.name
|
||||
FunctionPointerType(typeName, paramType.name, returnType.name, Some(paramType), Some(returnType))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case class ExternFunction(name: String,
|
||||
|
@ -551,6 +588,8 @@ case class ExternFunction(name: String,
|
|||
|
||||
override def interrupt = false
|
||||
|
||||
override def kernalInterrupt: Boolean = false
|
||||
|
||||
override def isConstPure = false
|
||||
|
||||
override def zeropage: Boolean = false
|
||||
|
@ -582,7 +621,7 @@ case class NormalFunction(name: String,
|
|||
override def isVolatile: Boolean = false
|
||||
}
|
||||
|
||||
case class ConstantThing(name: String, value: Constant, typ: Type) extends TypedThing with VariableLikeThing with IndexableThing {
|
||||
case class ConstantThing(name: String, value: Constant, typ: Type) extends TypedThing with VariableLikeThing with IndexableThing with ConstantLikeThing {
|
||||
def map(f: Constant => Constant) = ConstantThing("", f(value), typ)
|
||||
}
|
||||
|
||||
|
|
|
@ -48,7 +48,7 @@ abstract class CallGraph(program: Program, log: Logger) {
|
|||
case f: FunctionDeclarationStatement =>
|
||||
allFunctions += f.name
|
||||
allFunctions += f.name + ".trampoline" // TODO: ???
|
||||
if (f.address.isDefined || f.interrupt) entryPoints += f.name
|
||||
if (f.address.isDefined || f.interrupt || f.kernalInterrupt) entryPoints += f.name
|
||||
f.statements.getOrElse(Nil).foreach(s => this.add(Some(f.name), Nil, s))
|
||||
case s: Statement =>
|
||||
s.getAllExpressions.foreach(e => add(currentFunction, callingFunctions, e))
|
||||
|
|
|
@ -17,7 +17,7 @@ abstract class FloatFormat(val name: String, val hasInfinity: Boolean, val manti
|
|||
val alignment = env.get[Type]("word").alignment
|
||||
val size = format(0.0).size
|
||||
StructDefinitionStatement("float." + name,
|
||||
List.tabulate(size)(i => FieldDesc("byte", "b" + i, None)),
|
||||
List.tabulate(size)(i => FieldDesc("byte", "b" + i, false, None)),
|
||||
Some(if (size % 2 == 0) alignment else NoAlignment))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,11 +8,13 @@ import millfork.assembly.z80.{NoRegisters, OneRegister, ZOpcode, ZRegisters}
|
|||
import millfork.env.{Constant, EnumType, ParamPassingConvention, Type, VariableType}
|
||||
import millfork.output.MemoryAlignment
|
||||
|
||||
import java.nio.file.Path
|
||||
|
||||
case class Position(moduleName: String, line: Int, column: Int, cursor: Int)
|
||||
|
||||
case class FieldDesc(typeName:String, fieldName: String, arraySize: Option[Expression])
|
||||
case class FieldDesc(typeName:String, fieldName: String, isVolatile: Boolean, arraySize: Option[Expression])
|
||||
|
||||
case class ResolvedFieldDesc(typ:VariableType, fieldName: String, arrayIndexTypeAndSize: Option[(VariableType, Int)])
|
||||
case class ResolvedFieldDesc(typ:VariableType, fieldName: String, isVolatile: Boolean, arrayIndexTypeAndSize: Option[(VariableType, Int)])
|
||||
|
||||
sealed trait Node {
|
||||
var position: Option[Position] = None
|
||||
|
@ -171,18 +173,31 @@ case class SumExpression(expressions: List[(Boolean, Expression)], decimal: Bool
|
|||
|
||||
case class FunctionCallExpression(functionName: String, expressions: List[Expression]) extends Expression {
|
||||
override def renameVariable(variable: String, newVariable: String): Expression =
|
||||
FunctionCallExpression(functionName, expressions.map {
|
||||
FunctionCallExpression(if (functionName == variable) newVariable else functionName, expressions.map {
|
||||
_.renameVariable(variable, newVariable)
|
||||
}).pos(position)
|
||||
override def replaceVariable(variable: String, actualParam: Expression): Expression =
|
||||
FunctionCallExpression(functionName, expressions.map {
|
||||
_.replaceVariable(variable, actualParam)
|
||||
}).pos(position)
|
||||
override def replaceIndexedExpression(predicate: IndexedExpression => Boolean, replacement: IndexedExpression => Expression): Expression =
|
||||
override def replaceVariable(variable: String, actualParam: Expression): Expression = {
|
||||
if (variable == functionName) {
|
||||
actualParam match {
|
||||
case VariableExpression(v) =>
|
||||
FunctionCallExpression(v, expressions.map {
|
||||
_.replaceVariable(variable, actualParam)
|
||||
}).pos(position)
|
||||
case _ =>
|
||||
??? // TODO
|
||||
}
|
||||
} else {
|
||||
FunctionCallExpression(functionName, expressions.map {
|
||||
_.replaceVariable(variable, actualParam)
|
||||
}).pos(position)
|
||||
}
|
||||
}
|
||||
|
||||
override def replaceIndexedExpression(predicate: IndexedExpression => Boolean, replacement: IndexedExpression => Expression): Expression =
|
||||
FunctionCallExpression(functionName, expressions.map {
|
||||
_.replaceIndexedExpression(predicate, replacement)
|
||||
}).pos(position)
|
||||
override def containsVariable(variable: String): Boolean = expressions.exists(_.containsVariable(variable))
|
||||
override def containsVariable(variable: String): Boolean = variable == functionName || expressions.exists(_.containsVariable(variable))
|
||||
override def getPointies: Seq[String] = expressions.flatMap(_.getPointies)
|
||||
override def isPure: Boolean = false // TODO
|
||||
override def getAllIdentifiers: Set[String] = expressions.map(_.getAllIdentifiers).fold(Set[String]())(_ ++ _) + functionName
|
||||
|
@ -225,8 +240,8 @@ object MosNiceFunctionProperty {
|
|||
case object DoesntConcernD extends NiceFunctionProperty("D")
|
||||
case object DoesntChangeZpRegister extends NiceFunctionProperty("reg")
|
||||
case class SetsSourceOfNZ(sourceOfNZ: SourceOfNZ) extends NiceFunctionProperty(sourceOfNZ + "NZ")
|
||||
case class SetsXTo(value: Int) extends NiceFunctionProperty("Y=" + value)
|
||||
case class SetsYTo(value: Int) extends NiceFunctionProperty("Z=" + value)
|
||||
case class SetsXTo(value: Int) extends NiceFunctionProperty("X=" + value)
|
||||
case class SetsYTo(value: Int) extends NiceFunctionProperty("Y=" + value)
|
||||
case class SetsATo(value: Int) extends NiceFunctionProperty("A=" + value)
|
||||
case class Bit0OfA(value: Boolean) extends NiceFunctionProperty("A0=" + value)
|
||||
case class Bit7OfA(value: Boolean) extends NiceFunctionProperty("A7=" + value)
|
||||
|
@ -419,12 +434,12 @@ case class DerefDebuggingExpression(inner: Expression, preferredSize: Int) exten
|
|||
override def prettyPrint: String = s"¥deref(${inner.prettyPrint})"
|
||||
}
|
||||
|
||||
case class DerefExpression(inner: Expression, offset: Int, targetType: Type) extends LhsExpression {
|
||||
override def renameVariable(variable: String, newVariable: String): Expression = DerefExpression(inner.renameVariable(variable, newVariable), offset, targetType)
|
||||
override def replaceVariable(variable: String, actualParam: Expression): Expression = DerefExpression(inner.replaceVariable(variable, actualParam), offset, targetType)
|
||||
case class DerefExpression(inner: Expression, offset: Int, isVolatile: Boolean, targetType: Type) extends LhsExpression {
|
||||
override def renameVariable(variable: String, newVariable: String): Expression = DerefExpression(inner.renameVariable(variable, newVariable), offset, isVolatile, targetType)
|
||||
override def replaceVariable(variable: String, actualParam: Expression): Expression = DerefExpression(inner.replaceVariable(variable, actualParam), offset, isVolatile, targetType)
|
||||
|
||||
override def replaceIndexedExpression(predicate: IndexedExpression => Boolean, replacement: IndexedExpression => Expression): Expression =
|
||||
DerefExpression(inner.replaceIndexedExpression(predicate, replacement), offset, targetType)
|
||||
DerefExpression(inner.replaceIndexedExpression(predicate, replacement), offset, isVolatile, targetType)
|
||||
|
||||
override def containsVariable(variable: String): Boolean = inner.containsVariable(variable)
|
||||
|
||||
|
@ -475,6 +490,11 @@ case class VariableDeclarationStatement(name: String,
|
|||
override def getAllExpressions: List[Expression] = List(initialValue, address).flatten
|
||||
|
||||
override def withChangedBank(bank: String): BankedDeclarationStatement = copy(bank = Some(bank))
|
||||
|
||||
def replaceVariableInInitialValue(variable: String, expression: Expression): VariableDeclarationStatement = initialValue match {
|
||||
case Some(v) => copy(initialValue = Some(v.replaceVariable(variable, expression)))
|
||||
case None => this
|
||||
}
|
||||
}
|
||||
|
||||
trait ArrayContents extends Node {
|
||||
|
@ -483,6 +503,21 @@ trait ArrayContents extends Node {
|
|||
def replaceVariable(variableToReplace: String, expression: Expression): ArrayContents
|
||||
}
|
||||
|
||||
case class FileChunkContents(filePath: Path, start: Expression, length: Option[Expression]) extends ArrayContents {
|
||||
def getAllExpressions(bigEndian: Boolean): List[Expression] = length match {
|
||||
case Some(l) => List(start, l)
|
||||
case None => List(start)
|
||||
}
|
||||
def renameVariable(variableToRename: String, newVariable: String): ArrayContents =
|
||||
FileChunkContents(filePath,
|
||||
start.renameVariable(variableToRename, newVariable),
|
||||
length.map(_.renameVariable(variableToRename, newVariable)))
|
||||
def replaceVariable(variableToReplace: String, expression: Expression): ArrayContents =
|
||||
FileChunkContents(filePath,
|
||||
start.replaceVariable(variableToReplace, expression),
|
||||
length.map(_.replaceVariable(variableToReplace, expression)))
|
||||
}
|
||||
|
||||
case class LiteralContents(contents: List[Expression]) extends ArrayContents {
|
||||
override def getAllExpressions(bigEndian: Boolean): List[Expression] = contents
|
||||
|
||||
|
@ -687,7 +722,7 @@ case class StandardReturnDispatchLabel(labels:List[Expression]) extends ReturnDi
|
|||
}
|
||||
|
||||
case class ReturnDispatchBranch(label: ReturnDispatchLabel, function: Expression, params: List[Expression]) extends Node {
|
||||
def getAllExpressions: List[Expression] = label.getAllExpressions ++ params
|
||||
def getAllExpressions: List[Expression] = label.getAllExpressions ++ params :+ function
|
||||
}
|
||||
|
||||
case class ReturnDispatchStatement(indexer: Expression, params: List[LhsExpression], branches: List[ReturnDispatchBranch]) extends ExecutableStatement {
|
||||
|
@ -796,7 +831,7 @@ case class MemsetStatement(start: Expression, size: Constant, value: Expression,
|
|||
}
|
||||
|
||||
case class ForEachStatement(variable: String, pointerVariable: Option[String], values: Either[Expression, List[Expression]], body: List[ExecutableStatement]) extends CompoundStatement {
|
||||
override def getAllExpressions: List[Expression] = VariableExpression(variable) :: (values.fold[List[Expression]](_ => Nil, identity) ++ body.flatMap(_.getAllExpressions))
|
||||
override def getAllExpressions: List[Expression] = VariableExpression(variable) :: (pointerVariable.map(VariableExpression).toList ++ values.fold[List[Expression]](List(_), identity) ++ body.flatMap(_.getAllExpressions))
|
||||
|
||||
override def getChildStatements: Seq[Statement] = body
|
||||
override def flatMap(f: ExecutableStatement => Option[ExecutableStatement]): Option[ExecutableStatement] = {
|
||||
|
|
|
@ -42,7 +42,7 @@ object UnusedLocalVariables extends NodeOptimization {
|
|||
case SumExpression(xs, _) => getAllReadVariables(xs.map(_._2))
|
||||
case FunctionCallExpression(_, xs) => getAllReadVariables(xs)
|
||||
case IndexedExpression(arr, index) => arr :: getAllReadVariables(List(index))
|
||||
case DerefExpression(inner, _, _) => getAllReadVariables(List(inner))
|
||||
case DerefExpression(inner, _, _, _) => getAllReadVariables(List(inner))
|
||||
case DerefDebuggingExpression(inner, _) => getAllReadVariables(List(inner))
|
||||
case IndirectFieldExpression(inner, firstIndices, fields) => getAllReadVariables(List(inner) ++ firstIndices ++ fields.flatMap(_._3))
|
||||
case SeparateBytesExpression(h, l) => getAllReadVariables(List(h, l))
|
||||
|
|
|
@ -19,7 +19,12 @@ import scala.collection.mutable.ArrayBuffer
|
|||
* @author Karol Stasiak
|
||||
*/
|
||||
|
||||
case class AssemblerOutput(code: Map[String, Array[Byte]], asm: Array[String], labels: List[(String, (Int, Int))], breakpoints: List[(Int, Int)])
|
||||
case class AssemblerOutput(code: Map[String, Array[Byte]],
|
||||
bankLayoutInFile: BankLayoutInFile,
|
||||
asm: Array[String],
|
||||
labels: List[(String, (String, Int))],
|
||||
endLabels: Map[String, (Char, Int)],
|
||||
breakpoints: List[(Int, Int)])
|
||||
|
||||
abstract class AbstractAssembler[T <: AbstractCode](private val program: Program,
|
||||
private val rootEnv: Environment,
|
||||
|
@ -33,8 +38,9 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
|||
var initializedVariablesSize: Int = 0
|
||||
protected val log: Logger = rootEnv.log
|
||||
|
||||
val labelMap: mutable.Map[String, (Int, Int)] = mutable.Map()
|
||||
val unimportantLabelMap: mutable.Map[String, (Int, Int)] = mutable.Map()
|
||||
val labelMap: mutable.Map[String, (String, Int)] = mutable.Map()
|
||||
val endLabelMap: mutable.Map[String, (Char, Int)] = mutable.Map()
|
||||
val unimportantLabelMap: mutable.Map[String, (String, Int)] = mutable.Map()
|
||||
val mem = new CompiledMemory(platform.bankNumbers.toList, platform.bankFill, platform.isBigEndian, labelMap, log)
|
||||
val breakpointSet: mutable.Set[(Int, Int)] = mutable.Set()
|
||||
private val bytesToWriteLater = mutable.ListBuffer[(String, Int, Constant, Option[Position])]()
|
||||
|
@ -105,6 +111,8 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
|||
}
|
||||
c match {
|
||||
case NumericConstant(v, _) => v
|
||||
case IfConstant(c, t, f) =>
|
||||
if (deepConstResolve(c) != 0) deepConstResolve(t) else deepConstResolve(f)
|
||||
case AssertByte(inner) =>
|
||||
val value = deepConstResolve(inner)
|
||||
if (value.toByte == value) value else {
|
||||
|
@ -115,7 +123,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
|||
try {
|
||||
if (labelMap.contains(th.name)) return labelMap(th.name)._2
|
||||
if (labelMap.contains(th.name + "`")) return labelMap(th.name)._2
|
||||
if (labelMap.contains(th.name + ".addr")) return labelMap.getOrElse[(Int, Int)](th.name, labelMap(th.name + ".array"))._2
|
||||
if (labelMap.contains(th.name + ".addr")) return labelMap.getOrElse[(String, Int)](th.name, labelMap(th.name + ".array"))._2
|
||||
if (unimportantLabelMap.contains(th.name)) return labelMap(th.name)._2
|
||||
val x1 = env.maybeGet[ConstantThing](th.name).map(_.value)
|
||||
val x2 = env.maybeGet[ConstantThing](th.name + "`").map(_.value)
|
||||
|
@ -178,6 +186,12 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
|||
case MathOperator.DecimalShl => asDecimal(l, 1 << r, _ * _)
|
||||
case MathOperator.DecimalShl9 => asDecimal(l, 1 << r, _ * _) & 0x1ff
|
||||
case MathOperator.DecimalShr => asDecimal(l, 1 << r, _ / _)
|
||||
case MathOperator.Equal => if (l == r) 1 else 0
|
||||
case MathOperator.NotEqual => if (l != r) 1 else 0
|
||||
case MathOperator.Less => if (l < r) 1 else 0
|
||||
case MathOperator.LessEqual => if (l <= r) 1 else 0
|
||||
case MathOperator.Greater => if (l > r) 1 else 0
|
||||
case MathOperator.GreaterEqual => if (l >= r) 1 else 0
|
||||
case MathOperator.And => l & r
|
||||
case MathOperator.Exor => l ^ r
|
||||
case MathOperator.Or => l | r
|
||||
|
@ -246,30 +260,38 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
|||
case tim: VariableInMemory =>
|
||||
tim.toAddress match {
|
||||
case NumericConstant(n, _) =>
|
||||
val m = mem.banks(tim.bank(options))
|
||||
val bank = tim.bank(options)
|
||||
val m = mem.banks(bank)
|
||||
for (i <- 0 until tim.typ.size) {
|
||||
m.occupied(i + n.toInt) = true
|
||||
}
|
||||
labelMap.put(tim.name, m.index -> n.toInt)
|
||||
labelMap.put(tim.name, bank -> n.toInt)
|
||||
endLabelMap.put(tim.name,
|
||||
(if (tim.isInstanceOf[InitializedMemoryVariable]) 'V' else 'v') ->
|
||||
(n.toInt + tim.typ.size - 1))
|
||||
case _ =>
|
||||
}
|
||||
case arr: MfArray =>
|
||||
arr.toAddress match {
|
||||
case NumericConstant(n, _) =>
|
||||
val m = mem.banks(arr.bank(options))
|
||||
val bank = arr.bank(options)
|
||||
val m = mem.banks(bank)
|
||||
for (i <- 0 until arr.sizeInBytes) {
|
||||
m.occupied(i + n.toInt) = true
|
||||
}
|
||||
labelMap.put(arr.name, m.index -> n.toInt)
|
||||
labelMap.put(arr.name, bank -> n.toInt)
|
||||
endLabelMap.put(arr.name,
|
||||
(if (arr.isInstanceOf[InitializedArray]) 'A' else 'a') ->
|
||||
(n.toInt + arr.sizeInBytes - 1))
|
||||
case _ =>
|
||||
}
|
||||
case _ =>
|
||||
}
|
||||
}
|
||||
|
||||
env.allocateVariables(None, mem, callGraph, variableAllocators, options, labelMap.put, 1, forZpOnly = true)
|
||||
env.allocateVariables(None, mem, callGraph, variableAllocators, options, labelMap.put, 2, forZpOnly = true)
|
||||
env.allocateVariables(None, mem, callGraph, variableAllocators, options, labelMap.put, 3, forZpOnly = true)
|
||||
env.allocateVariables(None, mem, callGraph, variableAllocators, options, labelMap.put, endLabelMap.put, 1, forZpOnly = true)
|
||||
env.allocateVariables(None, mem, callGraph, variableAllocators, options, labelMap.put, endLabelMap.put, 2, forZpOnly = true)
|
||||
env.allocateVariables(None, mem, callGraph, variableAllocators, options, labelMap.put, endLabelMap.put, 3, forZpOnly = true)
|
||||
|
||||
var inlinedFunctions = Map[String, List[T]]()
|
||||
val compiledFunctions = mutable.Map[String, CompiledFunction[T]]()
|
||||
|
@ -306,8 +328,12 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
|||
val labelMapImm = labelMap.toMap
|
||||
val niceFunctionPropertiesImm = niceFunctionProperties.toSet
|
||||
val extraOptimizedCode = veryLateOptimizations(thisFunctionNiceProperties, options).foldLeft(code) { (c, opt) =>
|
||||
val ocode = opt.optimize(function, c, OptimizationContext(options, labelMapImm, env.maybeGet[ThingInMemory]("__reg"), niceFunctionPropertiesImm))
|
||||
if (ocode eq c) code else quickSimplify(ocode)
|
||||
if (c.length < opt.minimumRequiredLines) {
|
||||
c
|
||||
} else {
|
||||
val ocode = opt.optimize(function, c, OptimizationContext(options, labelMapImm, env.maybeGet[ThingInMemory]("__reg"), env.identityPage, niceFunctionPropertiesImm))
|
||||
if (ocode eq c) c else quickSimplify(ocode)
|
||||
}
|
||||
}
|
||||
compiledFunctions(f) = NormalCompiledFunction(
|
||||
function.declaredBank.getOrElse(platform.defaultCodeBank),
|
||||
|
@ -373,7 +399,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
|||
env.eval(item) match {
|
||||
case Some(c) =>
|
||||
for(i <- 0 until elementType.alignedSize) {
|
||||
writeByte(bank, index, subbyte(c, i, elementType.size))(None)
|
||||
writeByte(bank, index, subbyte(c, i, elementType.size))(item.position)
|
||||
bank0.occupied(index) = true
|
||||
bank0.initialized(index) = true
|
||||
bank0.writeable(index) = true
|
||||
|
@ -394,8 +420,9 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
|||
val index = f.address.get.asInstanceOf[NumericConstant].value.toInt
|
||||
compiledFunctions(f.name) match {
|
||||
case NormalCompiledFunction(_, functionCode, _, _, _) =>
|
||||
labelMap(f.name) = bank0.index -> index
|
||||
labelMap(f.name) = bank -> index
|
||||
val end = outputFunction(bank, functionCode, index, assembly, options)
|
||||
endLabelMap(f.name) = 'F' -> (end - 1)
|
||||
for (i <- index until end) {
|
||||
bank0.occupied(index) = true
|
||||
bank0.initialized(index) = true
|
||||
|
@ -443,7 +470,8 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
|||
val size = functionCode.map(_.sizeInBytes).sum
|
||||
val bank0 = mem.banks(bank)
|
||||
val index = codeAllocators(bank).allocateBytes(bank0, options, size, initialized = true, writeable = false, location = AllocationLocation.High, alignment = alignment)
|
||||
labelMap(name) = bank0.index -> index
|
||||
labelMap(name) = bank -> index
|
||||
endLabelMap(name) = 'F' -> (index + size - 1)
|
||||
justAfterCode += bank -> outputFunction(bank, functionCode, index, assembly, options)
|
||||
case _ =>
|
||||
}
|
||||
|
@ -477,7 +505,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
|||
if (bank != "default") ???
|
||||
val bank0 = mem.banks(bank)
|
||||
var index = codeAllocators(bank).allocateBytes(bank0, options, typ.size + 1, initialized = true, writeable = false, location = AllocationLocation.High, alignment = m.alignment)
|
||||
labelMap(name) = bank0.index -> (index + 1)
|
||||
labelMap(name) = bank -> (index + 1)
|
||||
val altName = m.name.stripPrefix(env.prefix) + "`"
|
||||
val thing = if (name.endsWith(".addr")) env.get[ThingInMemory](name.stripSuffix(".addr")) else env.get[ThingInMemory](name + ".array")
|
||||
env.things += altName -> ConstantThing(altName, NumericConstant(index, 2), env.get[Type]("pointer"))
|
||||
|
@ -528,7 +556,8 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
|||
}
|
||||
val bank0 = mem.banks(bank)
|
||||
var index = codeAllocators(bank).allocateBytes(bank0, options, thing.sizeInBytes, initialized = true, writeable = true, location = AllocationLocation.High, alignment = alignment)
|
||||
labelMap(name) = bank0.index -> index
|
||||
labelMap(name) = bank -> index
|
||||
endLabelMap(name) = 'A' -> (index + thing.sizeInBytes - 1)
|
||||
if (!readOnlyPass) {
|
||||
rwDataStart = rwDataStart.min(index)
|
||||
rwDataEnd = rwDataEnd.max(index + thing.sizeInBytes)
|
||||
|
@ -540,7 +569,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
|||
env.eval(item) match {
|
||||
case Some(c) =>
|
||||
for (i <- 0 until elementType.size) {
|
||||
writeByte(bank, index, subbyte(c, i, elementType.size))(None)
|
||||
writeByte(bank, index, subbyte(c, i, elementType.size))(item.position)
|
||||
index += 1
|
||||
}
|
||||
case None =>
|
||||
|
@ -558,7 +587,8 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
|||
}
|
||||
val bank0 = mem.banks(bank)
|
||||
var index = codeAllocators(bank).allocateBytes(bank0, options, typ.alignedSize, initialized = true, writeable = true, location = AllocationLocation.High, alignment = alignment)
|
||||
labelMap(name) = bank0.index -> index
|
||||
labelMap(name) = bank -> index
|
||||
endLabelMap(name) = 'V' -> (index + typ.size - 1)
|
||||
if (!readOnlyPass) {
|
||||
rwDataStart = rwDataStart.min(index)
|
||||
rwDataEnd = rwDataEnd.max(index + typ.alignedSize)
|
||||
|
@ -570,7 +600,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
|||
env.eval(value) match {
|
||||
case Some(c) =>
|
||||
for (i <- 0 until typ.size) {
|
||||
writeByte(bank, index, subbyte(c, i, typ.size))(None)
|
||||
writeByte(bank, index, subbyte(c, i, typ.size))(value.position)
|
||||
assembly.append(" " + bytePseudoopcode + " " + subbyte(c, i, typ.size).quickSimplify)
|
||||
index += 1
|
||||
}
|
||||
|
@ -599,9 +629,9 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
|||
}
|
||||
if (size < 0) log.fatal("Negative writable memory size. It's a compiler bug.")
|
||||
val ivAddr = codeAllocators(ivBank).allocateBytes(ib, options, size, initialized = true, writeable = false, AllocationLocation.High, NoAlignment)
|
||||
unimportantLabelMap += "__rwdata_init_start" -> (ib.index -> ivAddr)
|
||||
unimportantLabelMap += "__rwdata_init_end" -> (ib.index -> (ivAddr + size))
|
||||
unimportantLabelMap += "__rwdata_size" -> (ib.index -> size)
|
||||
unimportantLabelMap += "__rwdata_init_start" -> (ivBank -> ivAddr)
|
||||
unimportantLabelMap += "__rwdata_init_end" -> (ivBank -> (ivAddr + size))
|
||||
unimportantLabelMap += "__rwdata_size" -> (ivBank -> size)
|
||||
for (i <- 0 until size) {
|
||||
ib.output(ivAddr + i) = db.output(rwDataStart + i)
|
||||
db.outputted(rwDataStart + i) = true
|
||||
|
@ -632,37 +662,38 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
|||
variableAllocators("default").notifyAboutEndOfData(rwDataEnd)
|
||||
}
|
||||
variableAllocators.foreach { case (b, a) => a.notifyAboutEndOfCode(justAfterCode(b)) }
|
||||
env.allocateVariables(None, mem, callGraph, variableAllocators, options, labelMap.put, 2, forZpOnly = false)
|
||||
env.allocateVariables(None, mem, callGraph, variableAllocators, options, labelMap.put, 3, forZpOnly = false)
|
||||
env.allocateVariables(None, mem, callGraph, variableAllocators, options, labelMap.put, endLabelMap.put, 2, forZpOnly = false)
|
||||
env.allocateVariables(None, mem, callGraph, variableAllocators, options, labelMap.put, endLabelMap.put, 3, forZpOnly = false)
|
||||
|
||||
val defaultBank = mem.banks("default").index
|
||||
if (platform.freeZpBytes.nonEmpty) {
|
||||
// TODO: reimplement indexOf/LastIndexOf for speed
|
||||
val zpUsageOffset = platform.freeZpBytes.min
|
||||
val zeropageOccupation = zpOccupied.slice(zpUsageOffset, platform.freeZpBytes.max + 1)
|
||||
unimportantLabelMap += "__zeropage_usage" -> (defaultBank, zeropageOccupation.lastIndexOf(true) - zeropageOccupation.indexOf(true) + 1)
|
||||
unimportantLabelMap += "__zeropage_first" -> (defaultBank, zpUsageOffset + (zeropageOccupation.indexOf(true) max 0))
|
||||
unimportantLabelMap += "__zeropage_last" -> (defaultBank, zpUsageOffset + (zeropageOccupation.lastIndexOf(true) max 0))
|
||||
unimportantLabelMap += "__zeropage_end" -> (defaultBank, zpUsageOffset + zeropageOccupation.lastIndexOf(true) + 1)
|
||||
unimportantLabelMap += "__zeropage_usage" -> ("default", zeropageOccupation.lastIndexOf(true) - zeropageOccupation.indexOf(true) + 1)
|
||||
unimportantLabelMap += "__zeropage_first" -> ("default", zpUsageOffset + (zeropageOccupation.indexOf(true) max 0))
|
||||
unimportantLabelMap += "__zeropage_last" -> ("default", zpUsageOffset + (zeropageOccupation.lastIndexOf(true) max 0))
|
||||
unimportantLabelMap += "__zeropage_end" -> ("default", zpUsageOffset + zeropageOccupation.lastIndexOf(true) + 1)
|
||||
} else {
|
||||
unimportantLabelMap += "__zeropage_usage" -> (defaultBank -> 0)
|
||||
unimportantLabelMap += "__zeropage_first" -> (defaultBank -> 3)
|
||||
unimportantLabelMap += "__zeropage_last" -> (defaultBank -> 2)
|
||||
unimportantLabelMap += "__zeropage_end" -> (defaultBank -> 3)
|
||||
unimportantLabelMap += "__zeropage_usage" -> ("default" -> 0)
|
||||
unimportantLabelMap += "__zeropage_first" -> ("default" -> 3)
|
||||
unimportantLabelMap += "__zeropage_last" -> ("default" -> 2)
|
||||
unimportantLabelMap += "__zeropage_end" -> ("default" -> 3)
|
||||
}
|
||||
unimportantLabelMap += "__rwdata_start" -> (defaultBank -> rwDataStart)
|
||||
unimportantLabelMap += "__rwdata_end" -> (defaultBank -> rwDataEnd)
|
||||
unimportantLabelMap += "__heap_start" -> (defaultBank -> variableAllocators("default").heapStart)
|
||||
unimportantLabelMap += "__rwdata_start" -> ("default" -> rwDataStart)
|
||||
unimportantLabelMap += "__rwdata_end" -> ("default" -> rwDataEnd)
|
||||
unimportantLabelMap += "__heap_start" -> ("default" -> variableAllocators("default").heapStart)
|
||||
for (segment <- platform.bankNumbers.keys) {
|
||||
val variableAllocator = options.platform.variableAllocators(segment)
|
||||
val codeAllocator = options.platform.codeAllocators(segment)
|
||||
unimportantLabelMap += s"segment.$segment.start" -> (defaultBank -> codeAllocator.startAt)
|
||||
unimportantLabelMap += s"segment.$segment.codeend" -> (defaultBank -> (codeAllocator.endBefore - 1))
|
||||
unimportantLabelMap += s"segment.$segment.datastart" -> (defaultBank -> variableAllocator.startAt)
|
||||
unimportantLabelMap += s"segment.$segment.heapstart" -> (defaultBank -> variableAllocator.heapStart)
|
||||
unimportantLabelMap += s"segment.$segment.end" -> (defaultBank -> (variableAllocator.endBefore - 1))
|
||||
unimportantLabelMap += s"segment.$segment.length" -> (defaultBank -> (variableAllocator.endBefore - codeAllocator.startAt))
|
||||
unimportantLabelMap += s"segment.$segment.bank" -> (defaultBank -> platform.bankNumbers(segment))
|
||||
unimportantLabelMap += s"segment.$segment.fill" -> (defaultBank -> platform.bankFill(segment))
|
||||
unimportantLabelMap += s"segment.$segment.start" -> ("default" -> codeAllocator.startAt)
|
||||
unimportantLabelMap += s"segment.$segment.codeend" -> ("default" -> (codeAllocator.endBefore - 1))
|
||||
unimportantLabelMap += s"segment.$segment.datastart" -> ("default" -> variableAllocator.startAt)
|
||||
unimportantLabelMap += s"segment.$segment.heapstart" -> ("default" -> variableAllocator.heapStart)
|
||||
unimportantLabelMap += s"segment.$segment.end" -> ("default" -> (variableAllocator.endBefore - 1))
|
||||
unimportantLabelMap += s"segment.$segment.length" -> ("default" -> (variableAllocator.endBefore - codeAllocator.startAt))
|
||||
unimportantLabelMap += s"segment.$segment.bank" -> ("default" -> platform.bankNumbers(segment))
|
||||
unimportantLabelMap += s"segment.$segment.fill" -> ("default" -> platform.bankFill(segment))
|
||||
}
|
||||
|
||||
env = rootEnv.allThings
|
||||
|
@ -692,9 +723,35 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
|||
for (bank <- mem.banks.keys.toSeq.sorted) {
|
||||
val b = mem.banks(bank)
|
||||
if (b.start >= 0 && b.end >= 0) {
|
||||
val (allotedStart, allotedEndBefore) = {
|
||||
val al1 = variableAllocators(bank)
|
||||
val al2 = codeAllocators(bank)
|
||||
(al1.startAt max al2.startAt) -> (al1.endBefore max al2.endBefore)
|
||||
}
|
||||
val holes = (b.start to b.end).count(i => !b.occupied(i))
|
||||
val freeMemory = (allotedStart until allotedEndBefore).count(i => !b.occupied(i))
|
||||
val largeHoles = {
|
||||
val builder = Seq.newBuilder[(Int, Int)]
|
||||
var i = b.start
|
||||
while(i < b.end) {
|
||||
if (b.occupied(i)) {
|
||||
i+= 1
|
||||
} else {
|
||||
val holeSize = b.occupied.segmentLength(!_, i)
|
||||
if (holeSize >= 4) {
|
||||
builder += i -> (i + holeSize - 1)
|
||||
}
|
||||
i += holeSize
|
||||
}
|
||||
}
|
||||
builder.result()
|
||||
}
|
||||
val size = b.end - b.start + 1
|
||||
log.info(f"Segment ${bank}%s: $$${b.start}%04x-$$${b.end}%04x, size: $size%d B ($holes%d B unused)")
|
||||
log.info(f"Segment ${bank}%s: $$${b.start}%04x-$$${b.end}%04x, size: $size%d B ($freeMemory B free, of which unused gaps $holes%d B)")
|
||||
if (largeHoles.nonEmpty) {
|
||||
log.info("Unused gaps: " + largeHoles.map{ case (s,e) => f"$$$s%04x-$$$e%04x"}.mkString(", "))
|
||||
}
|
||||
// TODO: report holes:
|
||||
}
|
||||
}
|
||||
if (platform.cpuFamily == CpuFamily.M6502) {
|
||||
|
@ -706,19 +763,29 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
|||
|
||||
val allLabelList = labelMap.toList ++ unimportantLabelMap.toList
|
||||
allLabelList.sorted.foreach { case (l, (_, v)) =>
|
||||
assembly += f"$l%-30s = $$$v%04X"
|
||||
endLabelMap.get(l) match {
|
||||
case Some((category, end)) =>
|
||||
assembly += f"$l%-30s = $$$v%04X ;-$$$end%04X $category%s"
|
||||
case _ =>
|
||||
assembly += f"$l%-30s = $$$v%04X"
|
||||
}
|
||||
}
|
||||
allLabelList.sortBy { case (a, (_, v)) => v -> a }.foreach { case (l, (_, v)) =>
|
||||
assembly += f" ; $$$v%04X = $l%s"
|
||||
}
|
||||
|
||||
var bankLayoutInFile = BankLayoutInFile.empty
|
||||
// TODO:
|
||||
val code = (platform.outputStyle match {
|
||||
case OutputStyle.Single | OutputStyle.LUnix => List("default")
|
||||
case OutputStyle.PerBank => platform.bankNumbers.keys.toList
|
||||
}).map{b =>
|
||||
val outputPackager = platform.outputPackagers.getOrElse(b, platform.defaultOutputPackager)
|
||||
b -> outputPackager.packageOutput(mem, b)
|
||||
val tuple = outputPackager.packageOutputAndLayout(mem, b)
|
||||
if (b == "default") {
|
||||
bankLayoutInFile = tuple._2
|
||||
}
|
||||
b -> tuple._1
|
||||
}.toMap
|
||||
if (options.flag(CompilationFlag.DataMissingInOutputWarning)) {
|
||||
for (bank <- mem.banks.keys.toSeq.sorted) {
|
||||
|
@ -729,7 +796,7 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
|||
}
|
||||
}
|
||||
}
|
||||
AssemblerOutput(code, assembly.toArray, labelMap.toList, breakpointSet.toList.sorted)
|
||||
AssemblerOutput(code, bankLayoutInFile, assembly.toArray, labelMap.toList, endLabelMap.toMap, breakpointSet.toList.sorted)
|
||||
}
|
||||
|
||||
private def printArrayToAssemblyOutput(assembly: ArrayBuffer[String], name: String, elementType: Type, items: Seq[Expression]): Unit = {
|
||||
|
@ -762,13 +829,13 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
|||
}
|
||||
}
|
||||
|
||||
def injectLabels(labelMap: Map[String, (Int, Int)], code: List[T]): List[T]
|
||||
def injectLabels(labelMap: Map[String, (String, Int)], code: List[T]): List[T]
|
||||
|
||||
private def compileFunction(f: NormalFunction,
|
||||
optimizations: Seq[AssemblyOptimization[T]],
|
||||
options: CompilationOptions,
|
||||
inlinedFunctions: Map[String, List[T]],
|
||||
labelMap: Map[String, (Int, Int)],
|
||||
labelMap: Map[String, (String, Int)],
|
||||
niceFunctionProperties: Set[(NiceFunctionProperty, String)]): List[T] = {
|
||||
log.debug("Compiling: " + f.name, f.position)
|
||||
val unoptimized: List[T] =
|
||||
|
@ -779,8 +846,12 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program
|
|||
unoptimizedCodeSize += unoptimized.map(_.sizeInBytes).sum
|
||||
// unoptimized.foreach(l => log.trace(l.toString))
|
||||
val code = optimizations.foldLeft(quickSimplify(unoptimized)) { (c, opt) =>
|
||||
val ocode = opt.optimize(f, c, OptimizationContext(options, labelMap, env.maybeGet[ThingInMemory]("__reg"), niceFunctionProperties))
|
||||
if (ocode eq c) ocode else quickSimplify(ocode)
|
||||
if (c.length < opt.minimumRequiredLines) {
|
||||
c
|
||||
} else {
|
||||
val ocode = opt.optimize(f, c, OptimizationContext(options, labelMap, env.maybeGet[ThingInMemory]("__reg"), env.identityPage, niceFunctionProperties))
|
||||
if (ocode eq c) ocode else quickSimplify(ocode)
|
||||
}
|
||||
}
|
||||
performFinalOptimizationPass(f, optimizations.nonEmpty, options, code)
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user