mirror of
https://github.com/gbdev/rgbds.git
synced 2026-09-15 18:27:06 +00:00
Compare commits
418
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bffe7eb4de | ||
|
|
cd454d2e9a | ||
|
|
c814a616d6 | ||
|
|
e6ae1992fe | ||
|
|
d9b46cdec9 | ||
|
|
570cd62b81 | ||
|
|
a4ead0c25f | ||
|
|
da66eeb40e | ||
|
|
0c4f1f8334 | ||
|
|
af70d555fc | ||
|
|
e07bd92314 | ||
|
|
9169028e57 | ||
|
|
7dd8ba37f1 | ||
|
|
6842c831fd | ||
|
|
6b903059fe | ||
|
|
eb5af70d79 | ||
|
|
cf19879281 | ||
|
|
ac59ecf3c0 | ||
|
|
72b677a8d7 | ||
|
|
bbae9966e9 | ||
|
|
b3304ae1ac | ||
|
|
a48801a675 | ||
|
|
7dc81a64d3 | ||
|
|
3afa6b5a5a | ||
|
|
9b49f788e4 | ||
|
|
acc31feaa1 | ||
|
|
4ed5ba7508 | ||
|
|
20a26599a3 | ||
|
|
7bdfc9da23 | ||
|
|
d073cffa74 | ||
|
|
8435a29c4e | ||
|
|
b7fe78cad8 | ||
|
|
9d993d84e8 | ||
|
|
fef168b2a5 | ||
|
|
4a4b22c78c | ||
|
|
db79689e81 | ||
|
|
c2ca46c27d | ||
|
|
aac839f389 | ||
|
|
9d9febe1d3 | ||
|
|
b9fd85470e | ||
|
|
ec6d63bce3 | ||
|
|
bdcef6f252 | ||
|
|
54293a9184 | ||
|
|
b04e71ed34 | ||
|
|
f82603f196 | ||
|
|
cedfd2582a | ||
|
|
c7322258fc | ||
|
|
036b6c1b89 | ||
|
|
8e2a164a32 | ||
|
|
b76819792d | ||
|
|
3e945679ad | ||
|
|
efccf6c931 | ||
|
|
e5552e27f2 | ||
|
|
438963fb24 | ||
|
|
0bb815edc0 | ||
|
|
55a02981b5 | ||
|
|
b06e3b239d | ||
|
|
47442941b6 | ||
|
|
b16d2d0695 | ||
|
|
8b1cc72f09 | ||
|
|
11a6a81169 | ||
|
|
0ce66009c1 | ||
|
|
33f2f555a6 | ||
|
|
0487f9f841 | ||
|
|
0d6bfb84ce | ||
|
|
1e4ace8974 | ||
|
|
a378d1e8cb | ||
|
|
81ea39effe | ||
|
|
1a07391a97 | ||
|
|
b002d95459 | ||
|
|
646fc62b89 | ||
|
|
699c00dc20 | ||
|
|
9b9f3ffb96 | ||
|
|
00a67c3fb2 | ||
|
|
50d6403c72 | ||
|
|
9111157b82 | ||
|
|
794dd6cd7e | ||
|
|
ae4352c198 | ||
|
|
4a73eb56ea | ||
|
|
0f321bc797 | ||
|
|
94d07c78d8 | ||
|
|
b51e1c7c2c | ||
|
|
26ddf1ff4d | ||
|
|
20fd6eabbb | ||
|
|
fbe29006d4 | ||
|
|
03bb510588 | ||
|
|
695dfe9dbd | ||
|
|
9782f7d942 | ||
|
|
1b5648bb06 | ||
|
|
a67f5d6e01 | ||
|
|
06b57aa1ce | ||
|
|
6d2db2ef64 | ||
|
|
9868a01163 | ||
|
|
0c8cdd92d6 | ||
|
|
80a376f045 | ||
|
|
0068c1375c | ||
|
|
872af9c7ed | ||
|
|
06ea7b20bf | ||
|
|
a3c4652bfd | ||
|
|
5ad48851ed | ||
|
|
e3b7339dd6 | ||
|
|
69d7f84502 | ||
|
|
e970b6d6eb | ||
|
|
d9cce3fa1f | ||
|
|
23721694ea | ||
|
|
aa02958e18 | ||
|
|
011d4ec392 | ||
|
|
afbaf10185 | ||
|
|
6a5e2f439e | ||
|
|
fba77c4dce | ||
|
|
830df360ae | ||
|
|
c75551b1b3 | ||
|
|
5a38f6e148 | ||
|
|
869021f47d | ||
|
|
c06985a7ad | ||
|
|
c502804192 | ||
|
|
75f1bcde31 | ||
|
|
60b85298a9 | ||
|
|
8bbafb7200 | ||
|
|
75ce230dce | ||
|
|
1d01268249 | ||
|
|
02cb5a0526 | ||
|
|
8397b3d8ec | ||
|
|
296e5489c9 | ||
|
|
9ab9d0f39c | ||
|
|
6e1a5dcc9d | ||
|
|
d360d03403 | ||
|
|
e9bfe849ad | ||
|
|
665eb916a2 | ||
|
|
04788e15af | ||
|
|
dcb8c69661 | ||
|
|
cc6b70f1d5 | ||
|
|
38a9a613da | ||
|
|
ad9a766a56 | ||
|
|
21b59c4651 | ||
|
|
3ffdd50909 | ||
|
|
ca36422ac9 | ||
|
|
8e4ba8d2e4 | ||
|
|
ee67f1039c | ||
|
|
d37aa93a7d | ||
|
|
bba532193b | ||
|
|
b4814b06b9 | ||
|
|
4ee2eb845b | ||
|
|
3fdf01c0f5 | ||
|
|
1949a61c6f | ||
|
|
43cf20b155 | ||
|
|
e27a6d53a0 | ||
|
|
d17e9c663e | ||
|
|
dd8f396227 | ||
|
|
b60853ea21 | ||
|
|
b936ca27ab | ||
|
|
e050803ed1 | ||
|
|
27f38770d4 | ||
|
|
db1f77f90b | ||
|
|
4d21588eb2 | ||
|
|
e596dbfc80 | ||
|
|
1aeaca2af6 | ||
|
|
267e4bc25c | ||
|
|
c3e27217dd | ||
|
|
fe3521c7a4 | ||
|
|
b0f8d75d1d | ||
|
|
7a314e7aff | ||
|
|
637bbbdf43 | ||
|
|
8230e8165c | ||
|
|
a727a0f81f | ||
|
|
7a587eb7d6 | ||
|
|
7ac8bd6e24 | ||
|
|
be2572edca | ||
|
|
cf2bbe6435 | ||
|
|
dc5b7802c8 | ||
|
|
b1e6c73197 | ||
|
|
459773b3f0 | ||
|
|
6d0a3c75e9 | ||
|
|
e35585960c | ||
|
|
3bea7930a9 | ||
|
|
52797b6f68 | ||
|
|
5108c5643c | ||
|
|
2005ed1df9 | ||
|
|
d43408f4f3 | ||
|
|
2c30ab8731 | ||
|
|
432c769d60 | ||
|
|
9923fa3eee | ||
|
|
750e93be3d | ||
|
|
ee5da4468d | ||
|
|
503c3b5364 | ||
|
|
992be3fd9b | ||
|
|
c755fa3469 | ||
|
|
e78a1d5bfd | ||
|
|
d2f6def2eb | ||
|
|
1ffd7cb5bb | ||
|
|
215e26b478 | ||
|
|
8885f7bcf6 | ||
|
|
5334fc334e | ||
|
|
f97663aa37 | ||
|
|
08bdbd1949 | ||
|
|
5c852c7651 | ||
|
|
6be3584467 | ||
|
|
8c90d9d2d7 | ||
|
|
f69e666b00 | ||
|
|
eba06404f0 | ||
|
|
9558ccea1b | ||
|
|
260d372acd | ||
|
|
4e1b0ce793 | ||
|
|
363ee9578c | ||
|
|
8fa5a4255e | ||
|
|
b3312886fb | ||
|
|
7fc8a65d0a | ||
|
|
c278a361da | ||
|
|
a2f52867ad | ||
|
|
ab79e6bede | ||
|
|
850c78aaf4 | ||
|
|
c08cf783c8 | ||
|
|
25a8518fbf | ||
|
|
49174f4486 | ||
|
|
81327b0d99 | ||
|
|
c0859e64f7 | ||
|
|
3e0b7d428f | ||
|
|
ba3428314b | ||
|
|
bcb78f5d18 | ||
|
|
de7d1facf3 | ||
|
|
310d34c655 | ||
|
|
39c38f9838 | ||
|
|
576b063519 | ||
|
|
596e17ee61 | ||
|
|
363b3d0134 | ||
|
|
c7ed9a275e | ||
|
|
49aac2961d | ||
|
|
3741bd4617 | ||
|
|
937c9888a4 | ||
|
|
61a9bfd33c | ||
|
|
d08bcc455d | ||
|
|
aaa92659ea | ||
|
|
be877134e5 | ||
|
|
d05703c692 | ||
|
|
5dbfafcc55 | ||
|
|
a265b85d9d | ||
|
|
28abf03c0a | ||
|
|
7e7f92f18c | ||
|
|
7461170956 | ||
|
|
57d966d6e0 | ||
|
|
17752d7094 | ||
|
|
4216f0a9b0 | ||
|
|
e3fde986ad | ||
|
|
aa99ed056c | ||
|
|
46d6652df1 | ||
|
|
5406674cdd | ||
|
|
5d2e2e2182 | ||
|
|
a929f36bc5 | ||
|
|
bdb84a901f | ||
|
|
093631ca0b | ||
|
|
7e127a4e52 | ||
|
|
b8093847dc | ||
|
|
8d1b56bcf5 | ||
|
|
5fb7fcf461 | ||
|
|
ee900ae7a3 | ||
|
|
3ca58e13dc | ||
|
|
aaa4e17454 | ||
|
|
a81d383f75 | ||
|
|
60019cf476 | ||
|
|
5a6a44cbc1 | ||
|
|
b61a187b19 | ||
|
|
6382f13647 | ||
|
|
714d39c86f | ||
|
|
f11241c2ae | ||
|
|
cb47ac8b97 | ||
|
|
028e7af7d1 | ||
|
|
e141da1d94 | ||
|
|
35367282e4 | ||
|
|
4c0fa6732e | ||
|
|
e9d9a44687 | ||
|
|
611bd46e0b | ||
|
|
e67254093e | ||
|
|
3ce3d2f662 | ||
|
|
3da9d81071 | ||
|
|
88646093ca | ||
|
|
0a0427afbb | ||
|
|
88048cdcd7 | ||
|
|
c878ff8775 | ||
|
|
a4049a3f1b | ||
|
|
8a75cc5051 | ||
|
|
b674c5fcaa | ||
|
|
a6d3df7ac9 | ||
|
|
365484ef18 | ||
|
|
5e2bd35239 | ||
|
|
6655e04ef0 | ||
|
|
40c6b840f8 | ||
|
|
5d6e0677d9 | ||
|
|
56071599e7 | ||
|
|
c637447d5d | ||
|
|
ac2cefdd87 | ||
|
|
0774f5eb9d | ||
|
|
76d8862900 | ||
|
|
1dafc1c762 | ||
|
|
63d15ac8c9 | ||
|
|
1f579deaff | ||
|
|
fad48326f8 | ||
|
|
e7d6ddf593 | ||
|
|
953f79c0d9 | ||
|
|
3c5e1caa7c | ||
|
|
d4028fff10 | ||
|
|
dd892d61d8 | ||
|
|
a09f2d4115 | ||
|
|
dafef5a953 | ||
|
|
929e2a4490 | ||
|
|
cc1129093d | ||
|
|
72911ada2d | ||
|
|
037bc7abb3 | ||
|
|
5fd636ac4b | ||
|
|
a6d844a9a5 | ||
|
|
bee62076c6 | ||
|
|
cd072d5e6a | ||
|
|
6ef3ee1391 | ||
|
|
c29b616f93 | ||
|
|
39c179aa32 | ||
|
|
5c1ae4ce22 | ||
|
|
efbfeca292 | ||
|
|
7dd34f1572 | ||
|
|
748e7dd4c7 | ||
|
|
d049ffc0f0 | ||
|
|
8c0275480c | ||
|
|
76d6ef8695 | ||
|
|
c67a696a87 | ||
|
|
ee20d9010e | ||
|
|
2bc12447e2 | ||
|
|
cb61da8842 | ||
|
|
122ba6eba5 | ||
|
|
dddff0f450 | ||
|
|
a919f922a1 | ||
|
|
8f20620c16 | ||
|
|
96bce05be2 | ||
|
|
fc2bf3d11d | ||
|
|
8415ce3ed0 | ||
|
|
ebb5aab6db | ||
|
|
464a3a4892 | ||
|
|
88e1cc7302 | ||
|
|
b3c0db218d | ||
|
|
76446e6d00 | ||
|
|
6623b1dc45 | ||
|
|
70bbb098d3 | ||
|
|
1926065377 | ||
|
|
e3d355d976 | ||
|
|
a679e02246 | ||
|
|
592e9b3725 | ||
|
|
effc58241d | ||
|
|
3697065afc | ||
|
|
fa0fa4d5ac | ||
|
|
5acc48fa54 | ||
|
|
1487eebe79 | ||
|
|
993c034039 | ||
|
|
09f16bda4a | ||
|
|
41d544a4eb | ||
|
|
f28b4abafc | ||
|
|
ca1c934629 | ||
|
|
d5a00cf634 | ||
|
|
fb39c3a70e | ||
|
|
15ec6efc28 | ||
|
|
93d83e17dc | ||
|
|
df16e64fc6 | ||
|
|
a4ebb87858 | ||
|
|
5ef8e0a1f6 | ||
|
|
eb4952c188 | ||
|
|
57b734a7df | ||
|
|
5be1c0da62 | ||
|
|
b598911e96 | ||
|
|
cab9cb06a3 | ||
|
|
62bea23c49 | ||
|
|
7ce5cf1595 | ||
|
|
7e3fc1db03 | ||
|
|
77279984a5 | ||
|
|
669a392fcd | ||
|
|
51ce0b038a | ||
|
|
bd244e6865 | ||
|
|
a70ecba06f | ||
|
|
9d2d5cfcfe | ||
|
|
18f3c8ff9a | ||
|
|
895ec5564d | ||
|
|
7bb6f71f0b | ||
|
|
10e3f1a02b | ||
|
|
2a9d52871b | ||
|
|
c4fb5591ee | ||
|
|
c0ce1da4c3 | ||
|
|
aa27e714d4 | ||
|
|
6874f694e5 | ||
|
|
3690546795 | ||
|
|
7bc42d468b | ||
|
|
097999cad3 | ||
|
|
f82edaa7ec | ||
|
|
d8e8b796e7 | ||
|
|
900fd8cabc | ||
|
|
c20ac35074 | ||
|
|
255b8bf9ba | ||
|
|
ad6f17cd93 | ||
|
|
063a22ddf9 | ||
|
|
1d9cc01ae1 | ||
|
|
f31deb5010 | ||
|
|
0956d300c4 | ||
|
|
4ef490f3cb | ||
|
|
8f2a894b88 | ||
|
|
0e40543757 | ||
|
|
ce58f6d6be | ||
|
|
5aabb915ec | ||
|
|
0d9de01f9d | ||
|
|
f5b0eae9cd | ||
|
|
e6552064bf | ||
|
|
861cb552c4 | ||
|
|
417cceb0de | ||
|
|
165bd8cb71 | ||
|
|
e54e02dc77 | ||
|
|
2bf3b08d76 | ||
|
|
21b58b08b8 | ||
|
|
af530859f0 | ||
|
|
58739b0bf2 | ||
|
|
3e4c2fe712 | ||
|
|
bdfce25db0 | ||
|
|
2b6d9cd1e0 | ||
|
|
bf789dd7b3 | ||
|
|
9b6f01047c | ||
|
|
3fe2fa43bb |
@@ -0,0 +1,99 @@
|
|||||||
|
AccessModifierOffset: -4
|
||||||
|
AlignAfterOpenBracket: Align
|
||||||
|
AlignArrayOfStructures: Left
|
||||||
|
AlignConsecutiveAssignments: None
|
||||||
|
AlignConsecutiveBitFields: Consecutive
|
||||||
|
AlignConsecutiveDeclarations: None
|
||||||
|
AlignConsecutiveMacros: Consecutive
|
||||||
|
AlignEscapedNewlines: Left
|
||||||
|
AlignOperands: Align
|
||||||
|
AlignTrailingComments: false
|
||||||
|
AllowShortBlocksOnASingleLine: Empty
|
||||||
|
AllowShortCaseLabelsOnASingleLine: false
|
||||||
|
AllowShortEnumsOnASingleLine: true
|
||||||
|
AllowShortFunctionsOnASingleLine: InlineOnly
|
||||||
|
AllowShortIfStatementsOnASingleLine: Never
|
||||||
|
AllowShortLambdasOnASingleLine: All
|
||||||
|
AllowShortLoopsOnASingleLine: false
|
||||||
|
AlwaysBreakAfterReturnType: None
|
||||||
|
AlwaysBreakBeforeMultilineStrings: false
|
||||||
|
AlwaysBreakTemplateDeclarations: Yes
|
||||||
|
AttributeMacros:
|
||||||
|
- format_
|
||||||
|
- attr_
|
||||||
|
BinPackArguments: true
|
||||||
|
BinPackParameters: true
|
||||||
|
BitFieldColonSpacing: Both
|
||||||
|
BreakBeforeBinaryOperators: NonAssignment
|
||||||
|
BreakBeforeBraces: Attach
|
||||||
|
BreakBeforeConceptDeclarations: true
|
||||||
|
BreakBeforeTernaryOperators: true
|
||||||
|
BreakConstructorInitializers: BeforeColon
|
||||||
|
BreakInheritanceList: AfterComma
|
||||||
|
BreakStringLiterals: true
|
||||||
|
ColumnLimit: 100
|
||||||
|
CompactNamespaces: false
|
||||||
|
ContinuationIndentWidth: 4
|
||||||
|
Cpp11BracedListStyle: true
|
||||||
|
DeriveLineEnding: true
|
||||||
|
DerivePointerAlignment: false
|
||||||
|
EmptyLineBeforeAccessModifier: Leave
|
||||||
|
FixNamespaceComments: false
|
||||||
|
IncludeBlocks: Regroup
|
||||||
|
IncludeCategories:
|
||||||
|
- Regex: '^<sys/'
|
||||||
|
Priority: 0
|
||||||
|
- Regex: '^<'
|
||||||
|
Priority: 1
|
||||||
|
- Regex: '^"extern/'
|
||||||
|
Priority: 2
|
||||||
|
- Regex: '^"(asm|link|fix|gfx)/'
|
||||||
|
Priority: 3
|
||||||
|
- Regex: '^"'
|
||||||
|
Priority: 2
|
||||||
|
IndentAccessModifiers: false
|
||||||
|
IndentCaseBlocks: false
|
||||||
|
IndentCaseLabels: false
|
||||||
|
IndentExternBlock: NoIndent
|
||||||
|
IndentGotoLabels: false
|
||||||
|
IndentPPDirectives: BeforeHash
|
||||||
|
IndentRequires: true
|
||||||
|
IndentWidth: 4
|
||||||
|
IndentWrappedFunctionNames: true
|
||||||
|
# Only support for Javascript as of clang-format 13...
|
||||||
|
# InsertTrailingCommas: true
|
||||||
|
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||||
|
LambdaBodyIndentation: Signature
|
||||||
|
Language: Cpp
|
||||||
|
MaxEmptyLinesToKeep: 1
|
||||||
|
NamespaceIndentation: None
|
||||||
|
PPIndentWidth: -1
|
||||||
|
PointerAlignment: Right
|
||||||
|
ReflowComments: true
|
||||||
|
SortIncludes: CaseSensitive
|
||||||
|
SortUsingDeclarations: true
|
||||||
|
SpaceAfterCStyleCast: false
|
||||||
|
SpaceAfterLogicalNot: false
|
||||||
|
SpaceAfterTemplateKeyword: false
|
||||||
|
SpaceAroundPointerQualifiers: Both
|
||||||
|
SpaceBeforeAssignmentOperators: true
|
||||||
|
SpaceBeforeCaseColon: false
|
||||||
|
SpaceBeforeCpp11BracedList: false
|
||||||
|
SpaceBeforeCtorInitializerColon: true
|
||||||
|
SpaceBeforeInheritanceColon: true
|
||||||
|
SpaceBeforeParens: ControlStatements
|
||||||
|
SpaceBeforeRangeBasedForLoopColon: true
|
||||||
|
SpaceBeforeSquareBrackets: false
|
||||||
|
SpaceInEmptyBlock: false
|
||||||
|
SpaceInEmptyParentheses: false
|
||||||
|
SpacesBeforeTrailingComments: 1
|
||||||
|
SpacesInAngles: false
|
||||||
|
SpacesInCStyleCastParentheses: false
|
||||||
|
SpacesInConditionalStatement: false
|
||||||
|
SpacesInContainerLiterals: false
|
||||||
|
SpacesInParentheses: false
|
||||||
|
SpacesInSquareBrackets: false
|
||||||
|
Standard: c++20
|
||||||
|
TabWidth: 4
|
||||||
|
UseCRLF: false
|
||||||
|
UseTab: AlignWithSpaces
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
[*]
|
||||||
|
root = true
|
||||||
|
indent_style = tab
|
||||||
|
indent_size = tab
|
||||||
|
tab_width = 8
|
||||||
|
charset = utf-8
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
end_of_line = lf
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# Shell scripts need Unix line endings (see https://github.com/gbdev/rgbds/issues/841)
|
||||||
|
*.sh text eol=lf
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
github: avivace
|
||||||
|
patreon: gbdev01
|
||||||
|
open_collective: gbdev
|
||||||
@@ -7,6 +7,14 @@
|
|||||||
sub(/b><\/td/, "th");
|
sub(/b><\/td/, "th");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# The whole page is being generated, so it's not meant to contain any Liquid
|
||||||
|
BEGIN {
|
||||||
|
print "{% raw %}"
|
||||||
|
}
|
||||||
|
END {
|
||||||
|
print "{% endraw %}"
|
||||||
|
}
|
||||||
|
|
||||||
BEGIN {
|
BEGIN {
|
||||||
in_synopsis = 0
|
in_synopsis = 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,20 +5,21 @@ usage() {
|
|||||||
Usage: $0 [-h] [-r] <rgbds-www> <version>
|
Usage: $0 [-h] [-r] <rgbds-www> <version>
|
||||||
Copy renders from RGBDS repository to rgbds-www documentation
|
Copy renders from RGBDS repository to rgbds-www documentation
|
||||||
Execute from the root folder of the RGBDS repo, checked out at the desired tag
|
Execute from the root folder of the RGBDS repo, checked out at the desired tag
|
||||||
<rgbds-www> : Path to the '_documentation' folder in the rgbds-www repository
|
<rgbds-www> : Path to the rgbds-www repository
|
||||||
<version> : Version to be copied, such as 'v0.4.1' or 'master'
|
<version> : Version to be copied, such as 'v0.4.1' or 'master'
|
||||||
|
|
||||||
-h Display this help message
|
-h Display this help message
|
||||||
-r Update "latest stable" redirection pages (use for releases, not master)
|
-r Update "latest stable" redirection pages and add a new entry to the index
|
||||||
|
(use for releases, not master)
|
||||||
EOF
|
EOF
|
||||||
}
|
}
|
||||||
|
|
||||||
update_redirects=0
|
is_release=0
|
||||||
bad_usage=0
|
bad_usage=0
|
||||||
while getopts ":hr" opt; do
|
while getopts ":hr" opt; do
|
||||||
case $opt in
|
case $opt in
|
||||||
r)
|
r)
|
||||||
update_redirects=1
|
is_release=1
|
||||||
;;
|
;;
|
||||||
h)
|
h)
|
||||||
usage
|
usage
|
||||||
@@ -50,10 +51,10 @@ PAGES=(
|
|||||||
[gbz80.7.html]=src/gbz80.7
|
[gbz80.7.html]=src/gbz80.7
|
||||||
)
|
)
|
||||||
WWWPATH="/docs"
|
WWWPATH="/docs"
|
||||||
mkdir -p "$1/$2"
|
mkdir -p "$1/_documentation/$2"
|
||||||
|
|
||||||
# `mandoc` uses a different format for referring to man pages present in the **current** directory
|
# `mandoc` uses a different format for referring to man pages present in the **current** directory.
|
||||||
# We want that format for RGBDS man pages, and the other one for the t=rest;
|
# We want that format for RGBDS man pages, and the other one for the rest;
|
||||||
# we thus need to copy all pages to a temporary directory, and process them there.
|
# we thus need to copy all pages to a temporary directory, and process them there.
|
||||||
|
|
||||||
# Copy all pages to current dir
|
# Copy all pages to current dir
|
||||||
@@ -64,7 +65,7 @@ stem="${page%.html}"
|
|||||||
manpage="${stem%.?}(${stem#*.})"
|
manpage="${stem%.?}(${stem#*.})"
|
||||||
descr="$(awk -v 'FS=.Nd ' '/.Nd/ { print $2; }' "${PAGES[$page]}")"
|
descr="$(awk -v 'FS=.Nd ' '/.Nd/ { print $2; }' "${PAGES[$page]}")"
|
||||||
|
|
||||||
cat - >"$1/$2/$page" <<EOF
|
cat >"$1/_documentation/$2/$page" <<EOF
|
||||||
---
|
---
|
||||||
layout: doc
|
layout: doc
|
||||||
title: $manpage [$2]
|
title: $manpage [$2]
|
||||||
@@ -75,9 +76,10 @@ EOF
|
|||||||
if [ $stem = rgbasm.5 ]; then
|
if [ $stem = rgbasm.5 ]; then
|
||||||
options+=,toc
|
options+=,toc
|
||||||
fi
|
fi
|
||||||
mandoc -Thtml -I os=Linux -O$options "${PAGES[$page]##*/}" | .github/actions/doc_postproc.awk >> "$1/$2/$page"
|
mandoc -Thtml -I os=Linux -O$options "${PAGES[$page]##*/}" | .github/actions/doc_postproc.awk >> "$1/_documentation/$2/$page"
|
||||||
if [ $update_redirects -ne 0 ]; then
|
groff -Tpdf -mdoc -wall "${PAGES[$page]##*/}" >"$1/_documentation/$2/$stem.pdf"
|
||||||
cat - >"$1/$page" <<EOF
|
if [ $is_release -ne 0 ]; then
|
||||||
|
cat - >"$1/_documentation/$page" <<EOF
|
||||||
---
|
---
|
||||||
redirect_to: $WWWPATH/$2/${page%.html}
|
redirect_to: $WWWPATH/$2/${page%.html}
|
||||||
permalink: $WWWPATH/${page%.html}/
|
permalink: $WWWPATH/${page%.html}/
|
||||||
@@ -87,7 +89,8 @@ description: RGBDS latest stable — $descr
|
|||||||
EOF
|
EOF
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
cat - >"$1/$2/index.html" <<EOF
|
|
||||||
|
cat - >"$1/_documentation/$2/index.html" <<EOF
|
||||||
---
|
---
|
||||||
layout: doc_index
|
layout: doc_index
|
||||||
permalink: /docs/$2/
|
permalink: /docs/$2/
|
||||||
@@ -96,5 +99,15 @@ description: RGBDS $2 - Online manual
|
|||||||
---
|
---
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
|
||||||
|
# If making a release, add a new entry right after `master`
|
||||||
|
if [ $is_release -ne 0 ]; then
|
||||||
|
awk '{ print }
|
||||||
|
/"name": "master"/ { print "\t\t{\"name\": \"'$2'\", \"text\": \"'$2'\" }," }
|
||||||
|
' "$1/_data/doc.json" >"$1/_data/doc.json.tmp"
|
||||||
|
mv "$1/_data/doc.json"{.tmp,}
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
# Clean up
|
# Clean up
|
||||||
rm "${PAGES[@]##*/}"
|
rm "${PAGES[@]##*/}"
|
||||||
|
|||||||
@@ -1,14 +1,21 @@
|
|||||||
case `echo $1 | cut -d '-' -f 1` in
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
case "${1%-*}" in
|
||||||
ubuntu)
|
ubuntu)
|
||||||
sudo apt-get -qq update
|
sudo apt-get -qq update
|
||||||
sudo apt-get install -yq bison libpng-dev pkg-config
|
sudo apt-get install -yq bison libpng-dev pkg-config
|
||||||
;;
|
;;
|
||||||
macos)
|
macos)
|
||||||
brew install libpng pkg-config md5sha1sum
|
brew install bison libpng pkg-config md5sha1sum
|
||||||
|
# For the version check below exclusively, re-do this before building
|
||||||
|
export PATH="/usr/local/opt/bison/bin:$PATH"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
echo "WARNING: Cannot install deps for OS '$1'"
|
echo "WARNING: Cannot install deps for OS '$1'"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
yacc --version
|
bison --version
|
||||||
|
make --version
|
||||||
|
cmake --version
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
name: "Code coverage checking"
|
||||||
|
on: pull_request
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
checkdiff:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Set up repo
|
||||||
|
run: |
|
||||||
|
git clone -b "${{ github.event.pull_request.head.ref }}" "${{ github.event.pull_request.head.repo.clone_url }}" rgbds
|
||||||
|
cd rgbds
|
||||||
|
git remote add upstream "${{ github.event.pull_request.base.repo.clone_url }}"
|
||||||
|
git fetch upstream
|
||||||
|
- name: Checkdiff
|
||||||
|
working-directory: rgbds
|
||||||
|
run: |
|
||||||
|
make checkdiff "BASE_REF=${{ github.event.pull_request.base.sha }}" Q= | tee log
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
name: "Create release artifacts"
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- v[0-9]*
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
windows:
|
||||||
|
runs-on: windows-2019
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- name: Get version from tag
|
||||||
|
shell: bash
|
||||||
|
run: | # Turn "refs/tags/vX.Y.Z" into "X.Y.Z"
|
||||||
|
VERSION="${{ github.ref }}"
|
||||||
|
echo "version=${VERSION##*/v}" >> $GITHUB_ENV
|
||||||
|
- name: Get zlib, libpng and bison
|
||||||
|
run: | # TODO: use an array
|
||||||
|
$wc = New-Object System.Net.WebClient
|
||||||
|
$wc.DownloadFile('https://www.zlib.net/zlib1212.zip', 'zlib.zip')
|
||||||
|
$hash = (Get-FileHash "zlib.zip" -Algorithm SHA256).Hash
|
||||||
|
if ($hash -ne '173e89893dcb8b4a150d7731cd72f0602f1d6b45e60e2a54efdf7f3fc3325fd7') {
|
||||||
|
Write-Host "zlib SHA256 mismatch! ($hash)"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
$wc.DownloadFile('https://download.sourceforge.net/libpng/lpng1637.zip', 'libpng.zip')
|
||||||
|
$hash = (Get-FileHash "libpng.zip" -Algorithm SHA256).Hash
|
||||||
|
if ($hash -ne '3b4b1cbd0bae6822f749d39b1ccadd6297f05e2b85a83dd2ce6ecd7d09eabdf2') {
|
||||||
|
Write-Host "libpng SHA256 mismatch! ($hash)"
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
$wc.DownloadFile('https://github.com/lexxmark/winflexbison/releases/download/v2.5.24/win_flex_bison-2.5.24.zip', 'winflexbison.zip')
|
||||||
|
$hash = (Get-FileHash "winflexbison.zip" -Algorithm SHA256).Hash
|
||||||
|
if ($hash -ne '39c6086ce211d5415500acc5ed2d8939861ca1696aee48909c7f6daf5122b505') {
|
||||||
|
Write-Host "bison SHA256 mismatch! ($hash)"
|
||||||
|
}
|
||||||
|
Expand-Archive -DestinationPath . "zlib.zip"
|
||||||
|
Expand-Archive -DestinationPath . "libpng.zip"
|
||||||
|
Expand-Archive -DestinationPath install_dir "winflexbison.zip"
|
||||||
|
Move-Item zlib-1.2.12 zlib
|
||||||
|
Move-Item lpng1637 libpng
|
||||||
|
- name: Build 32-bit zlib
|
||||||
|
run: | # BUILD_SHARED_LIBS causes the output DLL to be correctly called `zlib1.dll`
|
||||||
|
cmake -S zlib -B zbuild32 -A Win32 -DCMAKE_INSTALL_PREFIX=install_dir -DBUILD_SHARED_LIBS=ON
|
||||||
|
cmake --build zbuild32 --config Release
|
||||||
|
cmake --install zbuild32
|
||||||
|
- name: Build 32-bit libpng
|
||||||
|
run: |
|
||||||
|
cmake -S libpng -B pngbuild32 -A Win32 -DCMAKE_INSTALL_PREFIX=install_dir -DPNG_SHARED=ON -DPNG_STATIC=ON -DPNG_TESTS=OFF
|
||||||
|
cmake --build pngbuild32 --config Release
|
||||||
|
cmake --install pngbuild32
|
||||||
|
- name: Build 32-bit Windows binaries
|
||||||
|
run: |
|
||||||
|
cmake -S . -B build32 -A Win32 -DCMAKE_INSTALL_PREFIX=install_dir -DCMAKE_BUILD_TYPE=Release
|
||||||
|
cmake --build build32 --config Release
|
||||||
|
cmake --install build32
|
||||||
|
- name: Package 32-bit binaries
|
||||||
|
run: |
|
||||||
|
Compress-Archive -LiteralPath @("install_dir/bin/rgbasm.exe", "install_dir/bin/rgblink.exe", "install_dir/bin/rgbfix.exe", "install_dir/bin/rgbgfx.exe", "install_dir/bin/zlib1.dll", "install_dir/bin/libpng16.dll") "rgbds-${{ env.version }}-win32.zip"
|
||||||
|
- name: Build 64-bit zlib
|
||||||
|
run: | # BUILD_SHARED_LIBS causes the output DLL to be correctly called `zlib1.dll`
|
||||||
|
cmake -S zlib -B zbuild64 -A x64 -DCMAKE_INSTALL_PREFIX=install_dir -DBUILD_SHARED_LIBS=ON
|
||||||
|
cmake --build zbuild64 --config Release
|
||||||
|
cmake --install zbuild64
|
||||||
|
- name: Build 64-bit libpng
|
||||||
|
run: |
|
||||||
|
cmake -S libpng -B pngbuild64 -A x64 -DCMAKE_INSTALL_PREFIX=install_dir -DPNG_SHARED=ON -DPNG_STATIC=ON -DPNG_TESTS=OFF
|
||||||
|
cmake --build pngbuild64 --config Release
|
||||||
|
cmake --install pngbuild64
|
||||||
|
- name: Build 64-bit Windows binaries
|
||||||
|
run: |
|
||||||
|
cmake -S . -B build64 -A x64 -DCMAKE_INSTALL_PREFIX=install_dir -DCMAKE_BUILD_TYPE=Release
|
||||||
|
cmake --build build64 --config Release
|
||||||
|
cmake --install build64
|
||||||
|
- name: Package 64-bit binaries
|
||||||
|
run: |
|
||||||
|
Compress-Archive -LiteralPath @("install_dir/bin/rgbasm.exe", "install_dir/bin/rgblink.exe", "install_dir/bin/rgbfix.exe", "install_dir/bin/rgbgfx.exe", "install_dir/bin/zlib1.dll", "install_dir/bin/libpng16.dll") "rgbds-${{ env.version }}-win64.zip"
|
||||||
|
- name: Package sources
|
||||||
|
run: |
|
||||||
|
make dist
|
||||||
|
- name: Release
|
||||||
|
uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
body: |
|
||||||
|
Please ensure that the three assets below work properly.
|
||||||
|
Once that's done, replace this text with the changelog, un-draft the release, and update the `release` branch.
|
||||||
|
By the way, if you forgot to update `include/version.h`, RGBASM's version test is gonna fail in the tag's regression testing! (Use `git push --delete origin <tag>` to delete it)
|
||||||
|
draft: true # Don't publish the release quite yet...
|
||||||
|
prerelease: ${{ contains(github.ref, '-rc') }}
|
||||||
|
files: |
|
||||||
|
rgbds-${{ env.version }}-win32.zip
|
||||||
|
rgbds-${{ env.version }}-win64.zip
|
||||||
|
rgbds-${{ env.version }}.tar.gz
|
||||||
|
fail_on_unmatched_files: true
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -2,28 +2,27 @@ name: "Create release docs"
|
|||||||
on:
|
on:
|
||||||
release:
|
release:
|
||||||
types:
|
types:
|
||||||
- created
|
- released # This avoids triggering on pre-releases
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
if: github.repository_owner == 'gbdev'
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-18.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout rgbds@master
|
- name: Checkout rgbds@release
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
repository: gbdev/rgbds
|
|
||||||
ref: master
|
|
||||||
path: rgbds
|
path: rgbds
|
||||||
- name: Checkout rgbds-www@master
|
- name: Checkout rgbds-www@master
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
with:
|
with:
|
||||||
repository: gbdev/rgbds-www
|
repository: ${{ github.repository_owner }}/rgbds-www
|
||||||
ref: master
|
|
||||||
path: rgbds-www
|
path: rgbds-www
|
||||||
- name: Build and install mandoc
|
# `-O toc` was added in 1.14.5, but the repos only have 1.14.4
|
||||||
|
- name: Build and install mandoc + install groff
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get -qq update
|
sudo apt-get -qq update
|
||||||
sudo apt-get install -yq zlib1g-dev
|
sudo apt-get install -yq groff zlib1g-dev
|
||||||
wget 'http://mandoc.bsd.lv/snapshots/mandoc-1.14.5.tar.gz'
|
wget 'http://mandoc.bsd.lv/snapshots/mandoc-1.14.5.tar.gz'
|
||||||
tar xf mandoc-1.14.5.tar.gz
|
tar xf mandoc-1.14.5.tar.gz
|
||||||
cd mandoc-1.14.5
|
cd mandoc-1.14.5
|
||||||
@@ -32,23 +31,23 @@ jobs:
|
|||||||
sudo make install
|
sudo make install
|
||||||
- name: Update pages
|
- name: Update pages
|
||||||
working-directory: rgbds
|
working-directory: rgbds
|
||||||
run: |
|
run: | # The ref appears to be in the format "refs/tags/<version>", so strip that
|
||||||
./.github/actions/get-pages.sh ../rgbds-www/_documentation ${GITHUB_REF}
|
./.github/actions/get-pages.sh -r ../rgbds-www ${GITHUB_REF##*/}
|
||||||
- name: Push new pages
|
- name: Push new pages
|
||||||
working-directory: rgbds-www
|
working-directory: rgbds-www
|
||||||
run: |
|
run: |
|
||||||
mkdir -p -m 700 ~/.ssh
|
mkdir -p -m 700 ~/.ssh
|
||||||
echo "${{ secrets.SSH_KEY_SECRET }}" > ~/.ssh/id_ed25519
|
cat > ~/.ssh/id_ed25519 <<<"${{ secrets.SSH_KEY_SECRET }}"
|
||||||
chmod 0600 ~/.ssh/id_ed25519
|
chmod 0600 ~/.ssh/id_ed25519
|
||||||
eval $(ssh-agent -s)
|
eval $(ssh-agent -s)
|
||||||
ssh-add ~/.ssh/id_ed25519
|
ssh-add ~/.ssh/id_ed25519
|
||||||
git config --global user.name "GitHub Action"
|
git config --global user.name "GitHub Action"
|
||||||
git config --global user.email "[email protected]"
|
git config --global user.email "[email protected]"
|
||||||
git add .
|
git add -A
|
||||||
git commit -m "Create RGBDS ${GITHUB_REF} documentation"
|
git commit -m "Create RGBDS ${GITHUB_REF##*/} documentation"
|
||||||
if git remote | grep -q origin; then
|
if git remote | grep -q origin; then
|
||||||
git remote set-url origin [email protected]:gbdev/rgbds-www.git
|
git remote set-url origin [email protected]:${{ github.repository_owner }}/rgbds-www.git
|
||||||
else
|
else
|
||||||
git remote add origin [email protected]:gbdev/rgbds-www.git
|
git remote add origin [email protected]:${{ github.repository_owner }}/rgbds-www.git
|
||||||
fi
|
fi
|
||||||
git push origin master
|
git push origin master
|
||||||
|
|||||||
@@ -7,18 +7,15 @@ jobs:
|
|||||||
unix-testing:
|
unix-testing:
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
os: [ubuntu-20.04, ubuntu-18.04, ubuntu-16.04, macos-10.15]
|
os: [ubuntu-20.04, ubuntu-18.04, macos-11.0, macos-10.15]
|
||||||
cc: [gcc, clang]
|
cc: [gcc, clang]
|
||||||
buildsys: [make, cmake]
|
buildsys: [make, cmake]
|
||||||
include:
|
exclude:
|
||||||
- os: ubuntu-18.04
|
# `gcc` is just an alias to `clang` on macOS, don't bother
|
||||||
|
- os: macos-10.15
|
||||||
cc: gcc
|
cc: gcc
|
||||||
target: develop
|
- os: macos-11.0
|
||||||
cmakevars: -DSANITIZERS=ON -DMORE_WARNINGS=ON -DCMAKE_BUILD_TYPE=Debug
|
|
||||||
- os: ubuntu-20.04
|
|
||||||
cc: gcc
|
cc: gcc
|
||||||
target: develop
|
|
||||||
cmakevars: -DSANITIZERS=ON -DMORE_WARNINGS=ON -DCMAKE_BUILD_TYPE=Debug
|
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
@@ -27,15 +24,20 @@ jobs:
|
|||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
./.github/actions/install_deps.sh ${{ matrix.os }}
|
./.github/actions/install_deps.sh ${{ matrix.os }}
|
||||||
|
# The `export` lines are to allow working on macOS...
|
||||||
|
# Apple's base version is severely outdated, not even supporting -Wall,
|
||||||
|
# but it overrides Homebrew's version nonetheless...
|
||||||
- name: Build & install using Make
|
- name: Build & install using Make
|
||||||
run: |
|
run: |
|
||||||
make ${{ matrix.target }} -j Q= CC=${{ matrix.cc }}
|
export PATH="/usr/local/opt/bison/bin:$PATH"
|
||||||
|
make develop -j Q= CC=${{ matrix.cc }}
|
||||||
sudo make install -j Q=
|
sudo make install -j Q=
|
||||||
if: matrix.buildsys == 'make'
|
if: matrix.buildsys == 'make'
|
||||||
- name: Build & install using CMake
|
- name: Build & install using CMake
|
||||||
run: |
|
run: |
|
||||||
cmake -S . -B build -DCMAKE_VERBOSE_MAKEFILE=ON ${{ matrix.cmakevars }}
|
export PATH="/usr/local/opt/bison/bin:$PATH"
|
||||||
cmake --build build
|
cmake -S . -B build -DCMAKE_VERBOSE_MAKEFILE=ON -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER=${{ matrix.cc }} -DSANITIZERS=ON -DMORE_WARNINGS=ON
|
||||||
|
cmake --build build -j
|
||||||
cp build/src/rgb{asm,link,fix,gfx} .
|
cp build/src/rgb{asm,link,fix,gfx} .
|
||||||
sudo cmake --install build
|
sudo cmake --install build
|
||||||
if: matrix.buildsys == 'cmake'
|
if: matrix.buildsys == 'cmake'
|
||||||
@@ -71,9 +73,9 @@ jobs:
|
|||||||
- name: Get zlib, libpng and bison
|
- name: Get zlib, libpng and bison
|
||||||
run: | # TODO: use an array
|
run: | # TODO: use an array
|
||||||
$wc = New-Object System.Net.WebClient
|
$wc = New-Object System.Net.WebClient
|
||||||
$wc.DownloadFile('https://www.zlib.net/zlib1211.zip', 'zlib.zip')
|
$wc.DownloadFile('https://www.zlib.net/zlib1212.zip', 'zlib.zip')
|
||||||
$hash = (Get-FileHash "zlib.zip" -Algorithm SHA256).Hash
|
$hash = (Get-FileHash "zlib.zip" -Algorithm SHA256).Hash
|
||||||
if ($hash -ne 'd7510a8ee1918b7d0cad197a089c0a2cd4d6df05fee22389f67f115e738b178d') {
|
if ($hash -ne '173e89893dcb8b4a150d7731cd72f0602f1d6b45e60e2a54efdf7f3fc3325fd7') {
|
||||||
Write-Host "zlib SHA256 mismatch! ($hash)"
|
Write-Host "zlib SHA256 mismatch! ($hash)"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
@@ -83,30 +85,30 @@ jobs:
|
|||||||
Write-Host "libpng SHA256 mismatch! ($hash)"
|
Write-Host "libpng SHA256 mismatch! ($hash)"
|
||||||
exit 1
|
exit 1
|
||||||
}
|
}
|
||||||
$wc.DownloadFile('https://github.com/lexxmark/winflexbison/releases/download/v2.5.23/win_flex_bison-2.5.23.zip', 'winflexbison.zip')
|
$wc.DownloadFile('https://github.com/lexxmark/winflexbison/releases/download/v2.5.24/win_flex_bison-2.5.24.zip', 'winflexbison.zip')
|
||||||
$hash = (Get-FileHash "winflexbison.zip" -Algorithm SHA256).Hash
|
$hash = (Get-FileHash "winflexbison.zip" -Algorithm SHA256).Hash
|
||||||
if ($hash -ne '6AA5C8EA662DA1550020A5804C28BE63FFAA53486DA9F6842E24C379EC422DFC') {
|
if ($hash -ne '39c6086ce211d5415500acc5ed2d8939861ca1696aee48909c7f6daf5122b505') {
|
||||||
Write-Host "bison SHA256 mismatch! ($hash)"
|
Write-Host "bison SHA256 mismatch! ($hash)"
|
||||||
}
|
}
|
||||||
Expand-Archive -DestinationPath . "zlib.zip"
|
Expand-Archive -DestinationPath . "zlib.zip"
|
||||||
Expand-Archive -DestinationPath . "libpng.zip"
|
Expand-Archive -DestinationPath . "libpng.zip"
|
||||||
Expand-Archive -DestinationPath install_dir "winflexbison.zip"
|
Expand-Archive -DestinationPath install_dir "winflexbison.zip"
|
||||||
Move-Item zlib-1.2.11 zlib
|
Move-Item zlib-1.2.12 zlib
|
||||||
Move-Item lpng1637 libpng
|
Move-Item lpng1637 libpng
|
||||||
- name: Build zlib
|
- name: Build zlib
|
||||||
run: | # BUILD_SHARED_LIBS causes the output DLL to be correctly called `zlib1.dll`
|
run: | # BUILD_SHARED_LIBS causes the output DLL to be correctly called `zlib1.dll`
|
||||||
cmake -S zlib -B zbuild -A ${{ matrix.platform }} -DCMAKE_INSTALL_PREFIX=install_dir -DBUILD_SHARED_LIBS=ON
|
cmake -S zlib -B zbuild -A ${{ matrix.platform }} -DCMAKE_INSTALL_PREFIX=install_dir -DBUILD_SHARED_LIBS=ON
|
||||||
cmake --build zbuild --config Release
|
cmake --build zbuild --config Release -j
|
||||||
cmake --install zbuild
|
cmake --install zbuild
|
||||||
- name: Build libpng
|
- name: Build libpng
|
||||||
run: |
|
run: |
|
||||||
cmake -S libpng -B pngbuild -A ${{ matrix.platform }} -DCMAKE_INSTALL_PREFIX=install_dir -DPNG_SHARED=ON -DPNG_STATIC=ON -DPNG_TESTS=OFF
|
cmake -S libpng -B pngbuild -A ${{ matrix.platform }} -DCMAKE_INSTALL_PREFIX=install_dir -DPNG_SHARED=ON -DPNG_STATIC=ON -DPNG_TESTS=OFF
|
||||||
cmake --build pngbuild --config Release
|
cmake --build pngbuild --config Release -j
|
||||||
cmake --install pngbuild
|
cmake --install pngbuild
|
||||||
- name: Build Windows binaries
|
- name: Build Windows binaries
|
||||||
run: |
|
run: |
|
||||||
cmake -S . -B build -A ${{ matrix.platform }} -DCMAKE_INSTALL_PREFIX=install_dir -DCMAKE_BUILD_TYPE=Release
|
cmake -S . -B build -A ${{ matrix.platform }} -DCMAKE_INSTALL_PREFIX=install_dir -DCMAKE_BUILD_TYPE=Release
|
||||||
cmake --build build --config Release
|
cmake --build build --config Release -j
|
||||||
cmake --install build
|
cmake --install build
|
||||||
- name: Package binaries
|
- name: Package binaries
|
||||||
shell: bash
|
shell: bash
|
||||||
@@ -128,7 +130,7 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
bits: [32, 64]
|
bits: [32, 64]
|
||||||
os: [ubuntu-latest]
|
os: [ubuntu-18.04]
|
||||||
include:
|
include:
|
||||||
- bits: 32
|
- bits: 32
|
||||||
arch: i686
|
arch: i686
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
if: github.repository_owner == 'gbdev'
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-18.04
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout rgbds@master
|
- name: Checkout rgbds@master
|
||||||
@@ -31,10 +32,10 @@ jobs:
|
|||||||
repository: gbdev/rgbds-www
|
repository: gbdev/rgbds-www
|
||||||
ref: master
|
ref: master
|
||||||
path: rgbds-www
|
path: rgbds-www
|
||||||
- name: Build and install mandoc
|
- name: Build and install mandoc + install groff
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get -qq update
|
sudo apt-get -qq update
|
||||||
sudo apt-get install -yq zlib1g-dev
|
sudo apt-get install -yq groff zlib1g-dev
|
||||||
wget 'http://mandoc.bsd.lv/snapshots/mandoc-1.14.5.tar.gz'
|
wget 'http://mandoc.bsd.lv/snapshots/mandoc-1.14.5.tar.gz'
|
||||||
tar xf mandoc-1.14.5.tar.gz
|
tar xf mandoc-1.14.5.tar.gz
|
||||||
cd mandoc-1.14.5
|
cd mandoc-1.14.5
|
||||||
@@ -44,7 +45,7 @@ jobs:
|
|||||||
- name: Update pages
|
- name: Update pages
|
||||||
working-directory: rgbds
|
working-directory: rgbds
|
||||||
run: |
|
run: |
|
||||||
./.github/actions/get-pages.sh ../rgbds-www/_documentation master
|
./.github/actions/get-pages.sh ../rgbds-www master
|
||||||
- name: Push new pages
|
- name: Push new pages
|
||||||
working-directory: rgbds-www
|
working-directory: rgbds-www
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
+7
-10
@@ -1,15 +1,12 @@
|
|||||||
rgbasm
|
/rgbasm
|
||||||
rgblink
|
/rgblink
|
||||||
rgbfix
|
/rgbfix
|
||||||
rgbgfx
|
/rgbgfx
|
||||||
rgbshim.sh
|
/rgbshim.sh
|
||||||
*.o
|
*.o
|
||||||
*.exe
|
*.exe
|
||||||
|
*.dll
|
||||||
.checkpatch-camelcase.*
|
.checkpatch-camelcase.*
|
||||||
CMakeCache.txt
|
CMakeCache.txt
|
||||||
CMakeFiles
|
CMakeFiles/
|
||||||
cmake_install.cmake
|
cmake_install.cmake
|
||||||
|
|
||||||
test/pokecrystal
|
|
||||||
test/pokered
|
|
||||||
test/ucity
|
|
||||||
|
|||||||
+47
-29
@@ -6,8 +6,8 @@
|
|||||||
# SPDX-License-Identifier: MIT
|
# SPDX-License-Identifier: MIT
|
||||||
#
|
#
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.0)
|
# 3.9 required for LTO checks
|
||||||
cmake_policy(VERSION 3.0)
|
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)
|
||||||
|
|
||||||
project(rgbds
|
project(rgbds
|
||||||
LANGUAGES C)
|
LANGUAGES C)
|
||||||
@@ -23,59 +23,77 @@ if(srcdir STREQUAL bindir)
|
|||||||
message(FATAL_ERROR "Terminating configuration")
|
message(FATAL_ERROR "Terminating configuration")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
include_directories("${PROJECT_SOURCE_DIR}/include")
|
option(SANITIZERS "Build with sanitizers enabled" OFF) # Ignored on MSVC
|
||||||
|
option(MORE_WARNINGS "Turn on more warnings" OFF) # Ignored on MSVC
|
||||||
option(SANITIZERS "Build with sanitizers enabled" OFF)
|
|
||||||
option(MORE_WARNINGS "Turn on more warnings" OFF)
|
|
||||||
option(TRACE_PARSER "Trace parser execution" OFF)
|
|
||||||
option(TRACE_LEXER "Trace lexer execution" OFF)
|
|
||||||
|
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
add_compile_options(/W1 /MP)
|
# MSVC's standard library triggers warning C5105,
|
||||||
|
# "macro expansion producing 'defined' has undefined behavior"
|
||||||
|
add_compile_options(/std:c11 /W1 /MP /wd5105)
|
||||||
add_definitions(/D_CRT_SECURE_NO_WARNINGS)
|
add_definitions(/D_CRT_SECURE_NO_WARNINGS)
|
||||||
else()
|
else()
|
||||||
add_compile_options(-Wall -pedantic)
|
add_compile_options(-Wall -pedantic)
|
||||||
|
add_definitions(-D_POSIX_C_SOURCE=200809L -D_ISOC11_SOURCE)
|
||||||
if(SANITIZERS)
|
if(SANITIZERS)
|
||||||
set(SAN_FLAGS -fsanitize=shift -fsanitize=integer-divide-by-zero
|
set(SAN_FLAGS -fsanitize=shift -fsanitize=integer-divide-by-zero
|
||||||
-fsanitize=unreachable -fsanitize=vla-bound
|
-fsanitize=unreachable -fsanitize=vla-bound
|
||||||
-fsanitize=signed-integer-overflow -fsanitize=bounds
|
-fsanitize=signed-integer-overflow -fsanitize=bounds
|
||||||
-fsanitize=object-size -fsanitize=bool -fsanitize=enum
|
-fsanitize=object-size -fsanitize=bool -fsanitize=enum
|
||||||
-fsanitize=alignment -fsanitize=null)
|
-fsanitize=alignment -fsanitize=null -fsanitize=address)
|
||||||
add_compile_options(${SAN_FLAGS})
|
add_compile_options(${SAN_FLAGS})
|
||||||
link_libraries(${SAN_FLAGS})
|
link_libraries(${SAN_FLAGS})
|
||||||
|
# A non-zero optimization level is desired in debug mode, but allow overriding it nonetheless
|
||||||
|
# TODO: this overrides anything previously set... that's a bit sloppy!
|
||||||
|
set(CMAKE_C_FLAGS_DEBUG "-g -Og -fno-omit-frame-pointer -fno-optimize-sibling-calls" CACHE STRING "" FORCE)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(MORE_WARNINGS)
|
if(MORE_WARNINGS)
|
||||||
add_compile_options(-Werror -Wextra -Wno-type-limits
|
add_compile_options(-Werror -Wextra
|
||||||
-Wno-sign-compare -Wvla -Wformat -Wformat-security -Wformat-overflow=2
|
-Walloc-zero -Wcast-align -Wcast-qual -Wduplicated-branches -Wduplicated-cond
|
||||||
-Wformat-truncation=1 -Wformat-y2k -Wswitch-enum -Wunused
|
-Wfloat-equal -Winline -Wlogical-op -Wnested-externs -Wnull-dereference
|
||||||
-Wuninitialized -Wunknown-pragmas -Wstrict-overflow=5
|
-Wold-style-definition -Wshift-overflow=2 -Wstrict-overflow=5
|
||||||
-Wstringop-overflow=4 -Walloc-zero -Wduplicated-cond
|
-Wstrict-prototypes -Wstringop-overflow=4 -Wundef -Wuninitialized -Wunused
|
||||||
-Wfloat-equal -Wshadow -Wcast-qual -Wcast-align -Wlogical-op
|
-Wshadow # TODO: -Wshadow=compatible-local ?
|
||||||
-Wnested-externs -Wno-aggressive-loop-optimizations -Winline
|
-Wformat=2 -Wformat-overflow=2 -Wformat-truncation=1
|
||||||
-Wundef -Wstrict-prototypes -Wold-style-definition)
|
-Wno-format-nonliteral # We have a couple of "dynamic" prints
|
||||||
|
# We do some range checks that are always false on some platforms (GCC, Clang)
|
||||||
|
-Wno-type-limits -Wno-tautological-constant-out-of-range-compare
|
||||||
|
-Wvla # MSVC does not support VLAs
|
||||||
|
-Wno-unknown-warning-option) # Clang shouldn't diagnose unknown warnings
|
||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Use versioning consistent with Makefile
|
# Use versioning consistent with Makefile
|
||||||
# the git revision is used but uses the fallback in an archive
|
# the git revision is used but uses the fallback in an archive
|
||||||
|
|
||||||
execute_process(COMMAND git describe --tags --dirty --always
|
find_program(GIT git)
|
||||||
OUTPUT_VARIABLE GIT_REV
|
if(GIT)
|
||||||
ERROR_QUIET)
|
execute_process(COMMAND ${GIT} describe --tags --dirty --always
|
||||||
string(STRIP "${GIT_REV}" GIT_REV)
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
|
OUTPUT_VARIABLE GIT_REV OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||||
|
ERROR_QUIET)
|
||||||
|
message(STATUS "RGBDS version: ${GIT_REV}")
|
||||||
|
else(GIT)
|
||||||
|
message(STATUS "Cannot determine RGBDS version (Git not installed), falling back")
|
||||||
|
endif(GIT)
|
||||||
|
|
||||||
add_definitions(-DBUILD_VERSION_STRING="${GIT_REV}")
|
include_directories("${PROJECT_SOURCE_DIR}/include")
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 11)
|
set(CMAKE_C_STANDARD 11)
|
||||||
set(CMAKE_C_STANDARD_REQUIRED True)
|
set(CMAKE_C_STANDARD_REQUIRED True)
|
||||||
|
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
|
|
||||||
if(TRACE_PARSER)
|
# By default, build in Release mode; Debug mode must be explicitly requested
|
||||||
target_compile_definitions(rgbasm PRIVATE -DYYDEBUG)
|
# (You may want to augment it with the options above)
|
||||||
endif()
|
if(CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||||
|
message(CHECK_START "Checking if LTO is supported")
|
||||||
if(TRACE_LEXER)
|
include(CheckIPOSupported)
|
||||||
target_compile_definitions(rgbasm PRIVATE -DLEXER_DEBUG)
|
check_ipo_supported(RESULT enable_lto)
|
||||||
|
if(enable_lto)
|
||||||
|
message(CHECK_PASS "yes")
|
||||||
|
set_property(TARGET rgbasm rgblink rgbfix rgbgfx PROPERTY INTERPROCEDURAL_OPTIMIZATION ON)
|
||||||
|
else()
|
||||||
|
message(CHECK_FAIL "no")
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|||||||
+2
-3
@@ -1,10 +1,9 @@
|
|||||||
Contributing
|
Contributing
|
||||||
============
|
============
|
||||||
|
|
||||||
RGBDS was created in the late 90's and has received contributions from several
|
RGBDS was created in the late '90s and has received contributions from several
|
||||||
developers since then. It wouldn't have been possible to get to this point
|
developers since then. It wouldn't have been possible to get to this point
|
||||||
without their work and, for that reason, it is always open to the contributions
|
without their work, and it is always open to the contributions of other people.
|
||||||
of other people.
|
|
||||||
|
|
||||||
Reporting Bugs
|
Reporting Bugs
|
||||||
--------------
|
--------------
|
||||||
|
|||||||
+7
-1
@@ -19,6 +19,10 @@ Main contributors
|
|||||||
|
|
||||||
- Antonio Niño Díaz <[email protected]>
|
- Antonio Niño Díaz <[email protected]>
|
||||||
|
|
||||||
|
- Eldred "ISSOtm" Habert <[email protected]>
|
||||||
|
|
||||||
|
- Rangi <http://github.com/Rangi42>
|
||||||
|
|
||||||
Other contributors
|
Other contributors
|
||||||
------------------
|
------------------
|
||||||
|
|
||||||
@@ -30,7 +34,9 @@ Other contributors
|
|||||||
|
|
||||||
- David Brotz <[email protected]>
|
- David Brotz <[email protected]>
|
||||||
|
|
||||||
- Eldred Habert <[email protected]>
|
- Jakub Kądziołka <[email protected]>
|
||||||
|
|
||||||
|
- James "JL2210" Larrowe <https://github.com/JL2210>
|
||||||
|
|
||||||
- The Musl C library <http://www.musl-libc.org>
|
- The Musl C library <http://www.musl-libc.org>
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -7,7 +7,7 @@
|
|||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
RUN apk add --update \
|
RUN apk add --update \
|
||||||
build-base \
|
build-base \
|
||||||
byacc \
|
bison \
|
||||||
libpng-dev
|
libpng-dev
|
||||||
COPY . /rgbds
|
COPY . /rgbds
|
||||||
WORKDIR /rgbds
|
WORKDIR /rgbds
|
||||||
|
|||||||
@@ -30,47 +30,54 @@ PNGLDLIBS := `${PKG_CONFIG} --libs-only-l libpng`
|
|||||||
# Note: if this comes up empty, `version.c` will automatically fall back to last release number
|
# Note: if this comes up empty, `version.c` will automatically fall back to last release number
|
||||||
VERSION_STRING := `git describe --tags --dirty --always 2>/dev/null`
|
VERSION_STRING := `git describe --tags --dirty --always 2>/dev/null`
|
||||||
|
|
||||||
WARNFLAGS := -Wall
|
WARNFLAGS := -Wall -pedantic
|
||||||
|
|
||||||
# Overridable CFLAGS
|
# Overridable CFLAGS
|
||||||
CFLAGS ?= -O3 -DNDEBUG
|
CFLAGS ?= -O3 -flto -DNDEBUG
|
||||||
# Non-overridable CFLAGS
|
# Non-overridable CFLAGS
|
||||||
REALCFLAGS := ${CFLAGS} ${WARNFLAGS} -std=gnu11 -D_POSIX_C_SOURCE=200809L \
|
# _ISOC11_SOURCE is required on certain platforms to get C11 on top of the C99-based POSIX 2008
|
||||||
-Iinclude
|
REALCFLAGS := ${CFLAGS} ${WARNFLAGS} -std=gnu11 -I include \
|
||||||
|
-D_POSIX_C_SOURCE=200809L -D_ISOC11_SOURCE
|
||||||
# Overridable LDFLAGS
|
# Overridable LDFLAGS
|
||||||
LDFLAGS ?=
|
LDFLAGS ?=
|
||||||
# Non-overridable LDFLAGS
|
# Non-overridable LDFLAGS
|
||||||
REALLDFLAGS := ${LDFLAGS} ${WARNFLAGS} \
|
REALLDFLAGS := ${LDFLAGS} ${WARNFLAGS} \
|
||||||
-DBUILD_VERSION_STRING=\"${VERSION_STRING}\"
|
-DBUILD_VERSION_STRING=\"${VERSION_STRING}\"
|
||||||
|
|
||||||
YFLAGS ?=
|
YFLAGS ?= -Wall
|
||||||
|
|
||||||
YACC := yacc
|
BISON := bison
|
||||||
RM := rm -rf
|
RM := rm -rf
|
||||||
|
|
||||||
|
# Used for checking pull requests
|
||||||
|
BASE_REF := origin/master
|
||||||
|
|
||||||
# Rules to build the RGBDS binaries
|
# Rules to build the RGBDS binaries
|
||||||
|
|
||||||
all: rgbasm rgblink rgbfix rgbgfx
|
all: rgbasm rgblink rgbfix rgbgfx
|
||||||
|
|
||||||
rgbasm_obj := \
|
rgbasm_obj := \
|
||||||
src/asm/charmap.o \
|
src/asm/charmap.o \
|
||||||
|
src/asm/fixpoint.o \
|
||||||
|
src/asm/format.o \
|
||||||
src/asm/fstack.o \
|
src/asm/fstack.o \
|
||||||
src/asm/lexer.o \
|
src/asm/lexer.o \
|
||||||
src/asm/macro.o \
|
src/asm/macro.o \
|
||||||
src/asm/main.o \
|
src/asm/main.o \
|
||||||
src/asm/math.o \
|
src/asm/opt.o \
|
||||||
src/asm/parser.o \
|
|
||||||
src/asm/output.o \
|
src/asm/output.o \
|
||||||
|
src/asm/parser.o \
|
||||||
src/asm/rpn.o \
|
src/asm/rpn.o \
|
||||||
src/asm/section.o \
|
src/asm/section.o \
|
||||||
src/asm/symbol.o \
|
src/asm/symbol.o \
|
||||||
src/asm/util.o \
|
src/asm/util.o \
|
||||||
src/asm/warning.o \
|
src/asm/warning.o \
|
||||||
src/extern/err.o \
|
|
||||||
src/extern/getopt.o \
|
src/extern/getopt.o \
|
||||||
src/extern/utf8decoder.o \
|
src/extern/utf8decoder.o \
|
||||||
|
src/error.o \
|
||||||
src/hashmap.o \
|
src/hashmap.o \
|
||||||
src/linkdefs.o
|
src/linkdefs.o \
|
||||||
|
src/opmath.o
|
||||||
|
|
||||||
src/asm/lexer.o src/asm/main.o: src/asm/parser.h
|
src/asm/lexer.o src/asm/main.o: src/asm/parser.h
|
||||||
|
|
||||||
@@ -83,22 +90,23 @@ rgblink_obj := \
|
|||||||
src/link/script.o \
|
src/link/script.o \
|
||||||
src/link/section.o \
|
src/link/section.o \
|
||||||
src/link/symbol.o \
|
src/link/symbol.o \
|
||||||
src/extern/err.o \
|
|
||||||
src/extern/getopt.o \
|
src/extern/getopt.o \
|
||||||
|
src/error.o \
|
||||||
src/hashmap.o \
|
src/hashmap.o \
|
||||||
src/linkdefs.o
|
src/linkdefs.o \
|
||||||
|
src/opmath.o
|
||||||
|
|
||||||
rgbfix_obj := \
|
rgbfix_obj := \
|
||||||
src/fix/main.o \
|
src/fix/main.o \
|
||||||
src/extern/err.o \
|
src/extern/getopt.o \
|
||||||
src/extern/getopt.o
|
src/error.o
|
||||||
|
|
||||||
rgbgfx_obj := \
|
rgbgfx_obj := \
|
||||||
src/gfx/gb.o \
|
src/gfx/gb.o \
|
||||||
src/gfx/main.o \
|
src/gfx/main.o \
|
||||||
src/gfx/makepng.o \
|
src/gfx/makepng.o \
|
||||||
src/extern/err.o \
|
src/extern/getopt.o \
|
||||||
src/extern/getopt.o
|
src/error.o
|
||||||
|
|
||||||
rgbasm: ${rgbasm_obj}
|
rgbasm: ${rgbasm_obj}
|
||||||
$Q${CC} ${REALLDFLAGS} -o $@ ${rgbasm_obj} ${REALCFLAGS} src/version.c -lm
|
$Q${CC} ${REALLDFLAGS} -o $@ ${rgbasm_obj} ${REALCFLAGS} src/version.c -lm
|
||||||
@@ -114,15 +122,27 @@ rgbgfx: ${rgbgfx_obj}
|
|||||||
|
|
||||||
# Rules to process files
|
# Rules to process files
|
||||||
|
|
||||||
# We want the yacc invocations to pass through our rules, not default ones
|
# We want the Bison invocation to pass through our rules, not default ones
|
||||||
.y.o:
|
.y.o:
|
||||||
|
|
||||||
# yacc-generated C files have an accompanying header
|
# Bison-generated C files have an accompanying header
|
||||||
.c.h:
|
src/asm/parser.h: src/asm/parser.c
|
||||||
$Qtouch $@
|
$Qtouch $@
|
||||||
|
|
||||||
.y.c:
|
src/asm/parser.c: src/asm/parser.y
|
||||||
$Q${YACC} -d ${YFLAGS} -o $@ $<
|
$QDEFS=; \
|
||||||
|
add_flag(){ \
|
||||||
|
if src/check_bison_ver.sh $$1 $$2; then \
|
||||||
|
DEFS="-D$$3 $$DEFS"; \
|
||||||
|
fi \
|
||||||
|
}; \
|
||||||
|
add_flag 3 5 api.token.raw=true; \
|
||||||
|
add_flag 3 6 parse.error=detailed; \
|
||||||
|
add_flag 3 0 parse.error=verbose; \
|
||||||
|
add_flag 3 0 parse.lac=full; \
|
||||||
|
add_flag 3 0 lr.type=ielr; \
|
||||||
|
echo "DEFS=$$DEFS"; \
|
||||||
|
${BISON} $$DEFS -d ${YFLAGS} -o $@ $<
|
||||||
|
|
||||||
.c.o:
|
.c.o:
|
||||||
$Q${CC} ${REALCFLAGS} ${PNGCFLAGS} -c -o $@ $<
|
$Q${CC} ${REALCFLAGS} ${PNGCFLAGS} -c -o $@ $<
|
||||||
@@ -172,33 +192,44 @@ checkcodebase:
|
|||||||
# the first common commit between the HEAD and origin/master.
|
# the first common commit between the HEAD and origin/master.
|
||||||
# `.y` files aren't checked, unfortunately...
|
# `.y` files aren't checked, unfortunately...
|
||||||
|
|
||||||
BASE_REF:= origin/master
|
|
||||||
checkpatch:
|
checkpatch:
|
||||||
$Qeval COMMON_COMMIT=$$(git merge-base HEAD ${BASE_REF}); \
|
$QCOMMON_COMMIT=`git merge-base HEAD ${BASE_REF}`; \
|
||||||
for commit in `git rev-list $$COMMON_COMMIT..HEAD`; do \
|
for commit in `git rev-list $$COMMON_COMMIT..HEAD`; do \
|
||||||
echo "[*] Analyzing commit '$$commit'"; \
|
echo "[*] Analyzing commit '$$commit'"; \
|
||||||
git format-patch --stdout "$$commit~..$$commit" \
|
git format-patch --stdout "$$commit~..$$commit" \
|
||||||
|
-- src include '!src/extern' '!include/extern' \
|
||||||
| ${CHECKPATCH} - || true; \
|
| ${CHECKPATCH} - || true; \
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# Target used to check for suspiciously missing changed files.
|
||||||
|
|
||||||
|
checkdiff:
|
||||||
|
$Qcontrib/checkdiff.bash `git merge-base HEAD ${BASE_REF}`
|
||||||
|
|
||||||
# This target is used during development in order to prevent adding new issues
|
# This target is used during development in order to prevent adding new issues
|
||||||
# to the source code. All warnings are treated as errors in order to block the
|
# to the source code. All warnings are treated as errors in order to block the
|
||||||
# compilation and make the continous integration infrastructure return failure.
|
# compilation and make the continous integration infrastructure return failure.
|
||||||
|
# The rationale for some of the flags is documented in the CMakeLists.
|
||||||
|
|
||||||
develop:
|
develop:
|
||||||
$Qenv $(MAKE) -j WARNFLAGS="-Werror -Wall -Wextra -Wpedantic -Wno-type-limits \
|
$Qenv ${MAKE} WARNFLAGS="-Werror -Wextra \
|
||||||
-Wno-sign-compare -Wvla -Wformat -Wformat-security -Wformat-overflow=2 \
|
-Walloc-zero -Wcast-align -Wcast-qual -Wduplicated-branches -Wduplicated-cond \
|
||||||
-Wformat-truncation=1 -Wformat-y2k -Wswitch-enum -Wunused \
|
-Wfloat-equal -Winline -Wlogical-op -Wnested-externs -Wold-style-definition \
|
||||||
-Wuninitialized -Wunknown-pragmas -Wstrict-overflow=5 \
|
-Wshift-overflow=2 \
|
||||||
-Wstringop-overflow=4 -Walloc-zero -Wduplicated-cond \
|
-Wstrict-overflow=5 -Wstrict-prototypes -Wundef -Wuninitialized -Wunused \
|
||||||
-Wfloat-equal -Wshadow -Wcast-qual -Wcast-align -Wlogical-op \
|
-Wshadow \
|
||||||
-Wnested-externs -Wno-aggressive-loop-optimizations -Winline \
|
-Wnull-dereference -Wstringop-overflow=4 \
|
||||||
-Wundef -Wstrict-prototypes -Wold-style-definition \
|
-Wformat=2 -Wformat-overflow=2 -Wformat-truncation=1 \
|
||||||
|
-Wno-format-nonliteral \
|
||||||
|
-Wno-type-limits -Wno-tautological-constant-out-of-range-compare \
|
||||||
|
-Wvla \
|
||||||
|
-Wno-unknown-warning-option \
|
||||||
-fsanitize=shift -fsanitize=integer-divide-by-zero \
|
-fsanitize=shift -fsanitize=integer-divide-by-zero \
|
||||||
-fsanitize=unreachable -fsanitize=vla-bound \
|
-fsanitize=unreachable -fsanitize=vla-bound \
|
||||||
-fsanitize=signed-integer-overflow -fsanitize=bounds \
|
-fsanitize=signed-integer-overflow -fsanitize=bounds \
|
||||||
-fsanitize=object-size -fsanitize=bool -fsanitize=enum \
|
-fsanitize=object-size -fsanitize=bool -fsanitize=enum \
|
||||||
-fsanitize=alignment -fsanitize=null" CFLAGS="-ggdb3 -O0"
|
-fsanitize=alignment -fsanitize=null -fsanitize=address" \
|
||||||
|
CFLAGS="-ggdb3 -Og -fno-omit-frame-pointer -fno-optimize-sibling-calls"
|
||||||
|
|
||||||
# Targets for the project maintainer to easily create Windows exes.
|
# Targets for the project maintainer to easily create Windows exes.
|
||||||
# This is not for Windows users!
|
# This is not for Windows users!
|
||||||
@@ -206,11 +237,11 @@ develop:
|
|||||||
# install instructions instead.
|
# install instructions instead.
|
||||||
|
|
||||||
mingw32:
|
mingw32:
|
||||||
$Qmake CC=i686-w64-mingw32-gcc YACC=bison \
|
$Q${MAKE} CC=i686-w64-mingw32-gcc BISON=bison \
|
||||||
PKG_CONFIG=i686-w64-mingw32-pkg-config -j
|
PKG_CONFIG=i686-w64-mingw32-pkg-config -j
|
||||||
|
|
||||||
mingw64:
|
mingw64:
|
||||||
$Qmake CC=x86_64-w64-mingw32-gcc YACC=bison \
|
$Q${MAKE} CC=x86_64-w64-mingw32-gcc BISON=bison \
|
||||||
PKG_CONFIG=x86_64-w64-mingw32-pkg-config -j
|
PKG_CONFIG=x86_64-w64-mingw32-pkg-config -j
|
||||||
|
|
||||||
wine-shim:
|
wine-shim:
|
||||||
|
|||||||
+34
-21
@@ -18,6 +18,8 @@ The documentation of this toolchain can be viewed online
|
|||||||
`here <https://rgbds.gbdev.io/docs/>`__, it is generated from the man pages
|
`here <https://rgbds.gbdev.io/docs/>`__, it is generated from the man pages
|
||||||
found in this repository.
|
found in this repository.
|
||||||
|
|
||||||
|
If you want to contribute or maintain RGBDS, and have questions regarding the code, its organisation, etc. you can find me `on GBDev <https://gbdev.io/chat>`__ or via mail at ``rgbds at eldred dot fr``.
|
||||||
|
|
||||||
1. Installing RGBDS
|
1. Installing RGBDS
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
@@ -45,44 +47,53 @@ The RGBDS source code file structure somewhat resembles the following:
|
|||||||
|
|
||||||
.
|
.
|
||||||
├── .github/
|
├── .github/
|
||||||
│ ├── actions/
|
│ ├── actions/
|
||||||
│ │ └── ...
|
│ │ └── ...
|
||||||
│ └── workflows/
|
│ └── workflows/
|
||||||
│ └── ...
|
│ └── ...
|
||||||
├── contrib/
|
├── contrib/
|
||||||
│ ├── zsh_compl/
|
│ ├── zsh_compl/
|
||||||
│ │ └── ...
|
│ │ └── ...
|
||||||
│ └── ...
|
│ └── ...
|
||||||
├── include/
|
├── include/
|
||||||
│ └── ...
|
│ └── ...
|
||||||
├── src/
|
├── src/
|
||||||
│ ├── asm/
|
│ ├── asm/
|
||||||
│ │ └── ...
|
│ │ └── ...
|
||||||
│ ├── extern/
|
│ ├── extern/
|
||||||
│ │ └── ...
|
│ │ └── ...
|
||||||
│ ├── fix/
|
│ ├── fix/
|
||||||
│ │ └── ...
|
│ │ └── ...
|
||||||
│ ├── gfx/
|
│ ├── gfx/
|
||||||
│ │ └── ...
|
│ │ └── ...
|
||||||
│ ├── link/
|
│ ├── link/
|
||||||
│ │ └── ...
|
│ │ └── ...
|
||||||
│ ├── CMakeLists.txt
|
│ ├── CMakeLists.txt
|
||||||
│ └── ...
|
│ └── ...
|
||||||
├── test/
|
├── test/
|
||||||
│ ├── ...
|
│ ├── ...
|
||||||
│ └── run-tests.sh
|
│ └── run-tests.sh
|
||||||
|
├── .clang-format
|
||||||
├── CMakeLists.txt
|
├── CMakeLists.txt
|
||||||
├── Makefile
|
├── Makefile
|
||||||
└── README.rst
|
└── README.rst
|
||||||
|
|
||||||
|
.. |clang-format| replace:: ``clang-format``
|
||||||
|
.. _clang-format: https://clang.llvm.org/docs/ClangFormat.html
|
||||||
|
|
||||||
- ``.github/`` - files and scripts related to the integration of the RGBDS codebase with
|
- ``.github/`` - files and scripts related to the integration of the RGBDS codebase with
|
||||||
GitHub.
|
GitHub.
|
||||||
|
|
||||||
|
* ``actions/`` - scripts used by workflow files.
|
||||||
|
* ``workflows/`` - CI workflow description files.
|
||||||
|
|
||||||
- ``contrib/`` - scripts and other resources which may be useful to users and developers of
|
- ``contrib/`` - scripts and other resources which may be useful to users and developers of
|
||||||
RGBDS.
|
RGBDS.
|
||||||
|
|
||||||
* ``zsh_compl`` contains tab completion scripts for use with zsh. Put them somewhere in your ``fpath``, and they should auto-load.
|
* ``zsh_compl`` contains tab completion scripts for use with zsh. Put them somewhere in your ``fpath``, and they should auto-load.
|
||||||
|
|
||||||
|
* ``bash_compl`` contains tab completion scripts for use with bash. Run them with ``source`` somewhere in your ``.bashrc``, and they should load every time you open a shell.
|
||||||
|
|
||||||
- ``include/`` - header files for each respective C files in `src`.
|
- ``include/`` - header files for each respective C files in `src`.
|
||||||
|
|
||||||
- ``src/`` - source code and manual pages for RGBDS.
|
- ``src/`` - source code and manual pages for RGBDS.
|
||||||
@@ -92,6 +103,8 @@ The RGBDS source code file structure somewhat resembles the following:
|
|||||||
|
|
||||||
- ``test/`` - testing framework used to verify that changes to the code don't break or modify the behavior of RGBDS.
|
- ``test/`` - testing framework used to verify that changes to the code don't break or modify the behavior of RGBDS.
|
||||||
|
|
||||||
|
- ``.clang-format`` - code style for automated formatting with |clang-format|_. The C code does not currently follow this style, but all C++ code should.
|
||||||
|
|
||||||
3. History
|
3. History
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
|||||||
+67
@@ -0,0 +1,67 @@
|
|||||||
|
Releasing
|
||||||
|
=========
|
||||||
|
|
||||||
|
This describes for the maintainers of RGBDS how to publish a new release on
|
||||||
|
GitHub.
|
||||||
|
|
||||||
|
1. Update, commit, and push `include/version.h <include/version.h>`__ with
|
||||||
|
values for ``PACKAGE_VERSION_MAJOR``, ``PACKAGE_VERSION_MINOR``,
|
||||||
|
``PACKAGE_VERSION_PATCH``, and ``PACKAGE_VERSION_RC``. Only define
|
||||||
|
``PACKAGE_VERSION_RC`` if you are publishing a release candidate! You can
|
||||||
|
use ``git commit -m "Release <version>"`` and ``git push origin master``.
|
||||||
|
|
||||||
|
2. Create a Git tag formatted as ``v<MAJOR>.<MINOR>.<PATCH>``, or
|
||||||
|
``v<MAJOR>.<MINOR>.<PATCH>-rc<RC>`` for a release candidate. ``MAJOR``,
|
||||||
|
``MINOR``, ``PATCH``, and ``RC`` should match their values from
|
||||||
|
`include/version.h <include/version.h>`__. You can use ``git tag <tag>``.
|
||||||
|
|
||||||
|
3. Push the tag to GitHub. You can use ``git push origin <tag>``.
|
||||||
|
|
||||||
|
GitHub Actions will run the `create-release-artifacts.yaml
|
||||||
|
<.github/workflows/create-release-artifacts.yaml>`__ workflow to detect the
|
||||||
|
tag starting with "``v[0-9]``" and automatically do the following:
|
||||||
|
|
||||||
|
1. Build 32-bit and 64-bit RGBDS binaries for Windows with ``cmake``.
|
||||||
|
|
||||||
|
2. Package the binaries into zip files.
|
||||||
|
|
||||||
|
3. Package the source code into a tar.gz file with ``make dist``.
|
||||||
|
|
||||||
|
4. Create a draft GitHub release for the tag, attaching the three
|
||||||
|
packaged files. It will be a prerelease if the tag contains "``-rc``".
|
||||||
|
|
||||||
|
If an error occurred in the above steps, delete the tag and restart the
|
||||||
|
procedure. You can use ``git push --delete origin <tag>`` and
|
||||||
|
``git tag --delete <tag>``.
|
||||||
|
|
||||||
|
4. GitHub Actions will run the `create-release-docs.yml
|
||||||
|
<.github/workflows/create-release-docs.yml>`__ workflow to add the release
|
||||||
|
documentation to `rgbds-www <https://github.com/gbdev/rgbds-www>`__.
|
||||||
|
|
||||||
|
For a release candidate, which creates a prerelease, you will have to
|
||||||
|
take these steps yourself.
|
||||||
|
|
||||||
|
1. Clone `rgbds-www <https://github.com/gbdev/rgbds-www>`__. You can use
|
||||||
|
``git clone https://github.com/gbdev/rgbds-www.git``.
|
||||||
|
|
||||||
|
2. Make sure that you have installed ``groff`` and ``mandoc``. You will
|
||||||
|
need ``mandoc`` 1.14.5 or later to support ``-O toc``.
|
||||||
|
|
||||||
|
3. Run ``.github/actions/get-pages.sh -r <path/to/rgbds-www> <tag>``. This
|
||||||
|
will render the RGBDS documentation as HTML and PDF and copy it to
|
||||||
|
``rgbds-www``.
|
||||||
|
|
||||||
|
If you do not have ``groff`` installed, you can change
|
||||||
|
``groff -Tpdf -mdoc -wall`` to ``mandoc -Tpdf -I os=Linux`` in
|
||||||
|
`.github/actions/get-pages.sh <.github/actions/get-pages.sh>`__ and it
|
||||||
|
will suffice.
|
||||||
|
|
||||||
|
4. Commit and push the documentation. You can use ``git commit -m
|
||||||
|
"Create RGBDS <tag> documentation"`` and ``git push origin master``
|
||||||
|
(within the ``rgbds-www`` directory, not RGBDS).
|
||||||
|
|
||||||
|
5. Write a changelog in the GitHub draft release.
|
||||||
|
|
||||||
|
6. Click the "Publish release" button to publish it!
|
||||||
|
|
||||||
|
7. Update the `release` branch. You can use ``git push origin release``.
|
||||||
Executable
+215
@@ -0,0 +1,215 @@
|
|||||||
|
#/usr/bin/env bash
|
||||||
|
|
||||||
|
# Known bugs:
|
||||||
|
# - Newlines in file/directory names break this script
|
||||||
|
# This is because we rely on `compgen -A`, which is broken like this.
|
||||||
|
# A fix would require implementing it ourselves, and no thanks!
|
||||||
|
# - `rgbasm --binary-digits=a` is treated the same as `rgbasm --binary-digits=` (for example)
|
||||||
|
# This is not our fault, Bash passes both of these identically.
|
||||||
|
# Maybe it could be worked around, but such a fix would likely be involved.
|
||||||
|
# The user can work around it by typing `--binary-digits ''` instead, for example.
|
||||||
|
# - Directories are not completed as such in "coalesced" short-opt arguments. For example,
|
||||||
|
# `rgbasm -M d<tab>` can autocomplete to `rgbasm -M dir/` (no space), but
|
||||||
|
# `rgbasm -Md<tab>` would autocomplete to `rgbasm -Mdir ` (trailing space) instead.
|
||||||
|
# This is because dircetory handling is performed by Readline, whom we can't tell about the short
|
||||||
|
# opt kerfuffle. The user can work around by separating the argument, as shown above.
|
||||||
|
# (Also, there might be more possible bugs if `-Mdir` is actually a directory. Ugh.)
|
||||||
|
|
||||||
|
# Something to note:
|
||||||
|
# `rgbasm --binary-digits=a` gets passed to us as ('rgbasm' '--binary-digits' '=' 'a')
|
||||||
|
# Thus, we don't need to do much to handle that form of argument passing: skip '=' after long opts.
|
||||||
|
|
||||||
|
_rgbasm_completions() {
|
||||||
|
COMPREPLY=()
|
||||||
|
|
||||||
|
# Format: "long_opt:state_after"
|
||||||
|
# Empty long opt = it doesn't exit
|
||||||
|
# See the `state` variable below for info about `state_after`
|
||||||
|
declare -A opts=(
|
||||||
|
[V]="version:normal"
|
||||||
|
[E]="export-all:normal"
|
||||||
|
[h]="halt-without-nop:normal"
|
||||||
|
[L]="preserve-ld:normal"
|
||||||
|
[v]="verbose:normal"
|
||||||
|
[w]=":normal"
|
||||||
|
[b]="binary-digits:unk"
|
||||||
|
[D]="define:unk"
|
||||||
|
[g]="gfx-chars:unk"
|
||||||
|
[i]="include:dir"
|
||||||
|
[M]="dependfile:glob-*.mk *.d"
|
||||||
|
[o]="output:glob-*.o"
|
||||||
|
[p]="pad-value:unk"
|
||||||
|
[r]="recursion-depth:unk"
|
||||||
|
[W]="warning:warning"
|
||||||
|
)
|
||||||
|
# Parse command-line up to current word
|
||||||
|
local opt_ena=true
|
||||||
|
# Possible states:
|
||||||
|
# - normal = Well, normal. Options are parsed normally.
|
||||||
|
# - unk = An argument that can't be completed, and should just be skipped.
|
||||||
|
# - warning = A warning flag.
|
||||||
|
# - dir = A directory path
|
||||||
|
# - glob-* = A glob, after the dash is a whitespace-separated list of file globs to use
|
||||||
|
local state=normal
|
||||||
|
# The length of the option, used as a return value by the function below
|
||||||
|
local optlen=0
|
||||||
|
# $1: a short option word
|
||||||
|
# `state` will be set to the parsing state after the last option character in the word. If
|
||||||
|
# "normal" is not returned, `optlen` will be set to the length (dash included) of the "option"
|
||||||
|
# part of the argument.
|
||||||
|
parse_short_opt() {
|
||||||
|
for (( i = 1; i < "${#1}"; i++ )); do
|
||||||
|
# If the option is not known, assume it doesn't take an argument
|
||||||
|
local opt="${opts["${1:$i:1}"]:-":normal"}"
|
||||||
|
state="${opt#*:}"
|
||||||
|
# If the option takes an argument, record the length and exit
|
||||||
|
if [[ "$state" != 'normal' ]]; then
|
||||||
|
let optlen="$i + 1"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
optlen=0
|
||||||
|
}
|
||||||
|
|
||||||
|
for (( i = 1; i < $COMP_CWORD; i++ )); do
|
||||||
|
local word="${COMP_WORDS[$i]}"
|
||||||
|
|
||||||
|
# If currently processing an argument, skip this word
|
||||||
|
if [[ "$state" != 'normal' ]]; then
|
||||||
|
state=normal
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$word" = '--' ]]; then
|
||||||
|
# Options stop being parsed after this
|
||||||
|
opt_ena=false
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if it's a long option
|
||||||
|
if [[ "${word:0:2}" = '--' ]]; then
|
||||||
|
# If the option is unknown, assume it takes no arguments: keep the state at "normal"
|
||||||
|
for long_opt in "${opts[@]}"; do
|
||||||
|
if [[ "$word" = "--${long_opt%%:*}" ]]; then
|
||||||
|
state="${long_opt#*:}"
|
||||||
|
# Check if the next word is just '='; if so, skip it, the argument must follow
|
||||||
|
# (See "known bugs" at the top of this script)
|
||||||
|
let i++
|
||||||
|
if [[ "${COMP_WORDS[$i]}" != '=' ]]; then
|
||||||
|
let i--
|
||||||
|
fi
|
||||||
|
optlen=0
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
# Check if it's a short option
|
||||||
|
elif [[ "${word:0:1}" = '-' ]]; then
|
||||||
|
# The `-M?` ones are a mix of short and long, augh
|
||||||
|
# They must match the *full* word, but only take a single dash
|
||||||
|
# So, handle them here
|
||||||
|
if [[ "$1" = "-M"[GP] ]]; then
|
||||||
|
state=normal
|
||||||
|
elif [[ "$1" = "-M"[TQ] ]]; then
|
||||||
|
state='glob-*.d *.mk *.o'
|
||||||
|
else
|
||||||
|
parse_short_opt "$word"
|
||||||
|
# The last option takes an argument...
|
||||||
|
if [[ "$state" != 'normal' ]]; then
|
||||||
|
if [[ "$optlen" -ne "${#word}" ]]; then
|
||||||
|
# If it's contained within the word, we won't complete it, revert to "normal"
|
||||||
|
state=normal
|
||||||
|
else
|
||||||
|
# Otherwise, complete it, but start at the beginning of *that* word
|
||||||
|
optlen=0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Parse current word
|
||||||
|
# Careful that it might look like an option, so use `--` aggressively!
|
||||||
|
local cur_word="${COMP_WORDS[$COMP_CWORD]}"
|
||||||
|
|
||||||
|
# Process options, as short ones may change the state
|
||||||
|
if $opt_ena && [[ "$state" = 'normal' && "${cur_word:0:1}" = '-' ]]; then
|
||||||
|
# We might want to complete to an option or an arg to that option
|
||||||
|
# Parse the option word to check
|
||||||
|
# There's no whitespace in the option names, so we can ride a little dirty...
|
||||||
|
|
||||||
|
# Is this a long option?
|
||||||
|
if [[ "${cur_word:1:1}" = '-' ]]; then
|
||||||
|
# It is, try to complete one
|
||||||
|
COMPREPLY+=( $(compgen -W "${opts[*]%%:*}" -P '--' -- "${cur_word#--}") )
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
# Short options may be grouped, parse them to determine what to complete
|
||||||
|
# The `-M?` ones may not be followed by anything
|
||||||
|
if [[ "$1" != "-M"[GPTQ] ]]; then
|
||||||
|
parse_short_opt "$cur_word"
|
||||||
|
# We got some short options that behave like long ones
|
||||||
|
COMPREPLY+=( $(compgen -W '-MG -MP -MT -MQ' -- "$cur_word") )
|
||||||
|
|
||||||
|
if [[ "$state" = 'normal' ]]; then
|
||||||
|
COMPREPLY+=( $(compgen -W "${!opts[*]}" -P "$cur_word" '') )
|
||||||
|
return 0
|
||||||
|
elif [[ "$optlen" = "${#cur_word}" && "$state" != "warning" ]]; then
|
||||||
|
# This short option group only awaits its argument!
|
||||||
|
# Post the option group as-is as a reply so that Readline inserts a space,
|
||||||
|
# so that the next completion request switches to the argument
|
||||||
|
# An exception is made for warnings, since it's idiomatic to stick them to the
|
||||||
|
# `-W`, and it doesn't break anything.
|
||||||
|
COMPREPLY+=( "$cur_word" )
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$state" in
|
||||||
|
unk) # Return with no replies: no idea what to complete!
|
||||||
|
;;
|
||||||
|
warning)
|
||||||
|
COMPREPLY+=( $(compgen -W "
|
||||||
|
assert
|
||||||
|
backwards-for
|
||||||
|
builtin-args
|
||||||
|
charmap-redef
|
||||||
|
div
|
||||||
|
empty-data-directive
|
||||||
|
empty-macro-arg
|
||||||
|
empty-strrpl
|
||||||
|
large-constant
|
||||||
|
long-string
|
||||||
|
macro-shift
|
||||||
|
nested-comment
|
||||||
|
numeric-string
|
||||||
|
obsolete
|
||||||
|
shift
|
||||||
|
shift-amount
|
||||||
|
truncation
|
||||||
|
user
|
||||||
|
all
|
||||||
|
extra
|
||||||
|
everything
|
||||||
|
error" -P "${cur_word:0:$optlen}" -- "${cur_word:$optlen}") )
|
||||||
|
;;
|
||||||
|
normal) # Acts like a glob...
|
||||||
|
state="glob-*.asm *.inc *.sm83"
|
||||||
|
;&
|
||||||
|
glob-*)
|
||||||
|
while read -r word; do
|
||||||
|
COMPREPLY+=("${cur_word:0:$optlen}$word")
|
||||||
|
done < <(for glob in ${state#glob-}; do compgen -A file -X \!"$glob" -- "${cur_word:$optlen}"; done)
|
||||||
|
# Also complete directories
|
||||||
|
;&
|
||||||
|
dir)
|
||||||
|
while read -r word; do
|
||||||
|
COMPREPLY+=("${cur_word:0:$optlen}$word")
|
||||||
|
done < <(compgen -A directory -- "${cur_word:$optlen}")
|
||||||
|
compopt -o filenames
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
complete -F _rgbasm_completions rgbasm
|
||||||
Executable
+181
@@ -0,0 +1,181 @@
|
|||||||
|
#/usr/bin/env bash
|
||||||
|
|
||||||
|
# Same notes as RGBASM
|
||||||
|
|
||||||
|
_rgbfix_completions() {
|
||||||
|
COMPREPLY=()
|
||||||
|
|
||||||
|
# Format: "long_opt:state_after"
|
||||||
|
# Empty long opt = it doesn't exit
|
||||||
|
# See the `state` variable below for info about `state_after`
|
||||||
|
declare -A opts=(
|
||||||
|
[V]="version:normal"
|
||||||
|
[j]="non-japanese:normal"
|
||||||
|
[s]="sgb-compatible:normal"
|
||||||
|
[v]="validate:normal"
|
||||||
|
[C]="color-only:normal"
|
||||||
|
[c]="color-compatible:normal"
|
||||||
|
[f]="fix-spec:fix-spec"
|
||||||
|
[i]="game-id:unk"
|
||||||
|
[k]="new-licensee:unk"
|
||||||
|
[l]="old-licensee:unk"
|
||||||
|
[m]="mbc-type:mbc"
|
||||||
|
[n]="rom-version:unk"
|
||||||
|
[p]="pad-value:unk"
|
||||||
|
[r]="ram-size:unk"
|
||||||
|
[t]="title:unk"
|
||||||
|
)
|
||||||
|
# Parse command-line up to current word
|
||||||
|
local opt_ena=true
|
||||||
|
# Possible states:
|
||||||
|
# - normal = Well, normal. Options are parsed normally.
|
||||||
|
# - unk = An argument that can't be completed, and should just be skipped.
|
||||||
|
# - warning = A warning flag.
|
||||||
|
# - dir = A directory path
|
||||||
|
# - glob-* = A glob, after the dash is a whitespace-separated list of file globs to use
|
||||||
|
local state=normal
|
||||||
|
# The length of the option, used as a return value by the function below
|
||||||
|
local optlen=0
|
||||||
|
# $1: a short option word
|
||||||
|
# `state` will be set to the parsing state after the last option character in the word. If
|
||||||
|
# "normal" is not returned, `optlen` will be set to the length (dash included) of the "option"
|
||||||
|
# part of the argument.
|
||||||
|
parse_short_opt() {
|
||||||
|
for (( i = 1; i < "${#1}"; i++ )); do
|
||||||
|
# If the option is not known, assume it doesn't take an argument
|
||||||
|
local opt="${opts["${1:$i:1}"]:-":normal"}"
|
||||||
|
state="${opt#*:}"
|
||||||
|
# If the option takes an argument, record the length and exit
|
||||||
|
if [[ "$state" != 'normal' ]]; then
|
||||||
|
let optlen="$i + 1"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
optlen=0
|
||||||
|
}
|
||||||
|
|
||||||
|
for (( i = 1; i < $COMP_CWORD; i++ )); do
|
||||||
|
local word="${COMP_WORDS[$i]}"
|
||||||
|
|
||||||
|
# If currently processing an argument, skip this word
|
||||||
|
if [[ "$state" != 'normal' ]]; then
|
||||||
|
state=normal
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$word" = '--' ]]; then
|
||||||
|
# Options stop being parsed after this
|
||||||
|
opt_ena=false
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if it's a long option
|
||||||
|
if [[ "${word:0:2}" = '--' ]]; then
|
||||||
|
# If the option is unknown, assume it takes no arguments: keep the state at "normal"
|
||||||
|
for long_opt in "${opts[@]}"; do
|
||||||
|
if [[ "$word" = "--${long_opt%%:*}" ]]; then
|
||||||
|
state="${long_opt#*:}"
|
||||||
|
# Check if the next word is just '='; if so, skip it, the argument must follow
|
||||||
|
# (See "known bugs" at the top of this script)
|
||||||
|
let i++
|
||||||
|
if [[ "${COMP_WORDS[$i]}" != '=' ]]; then
|
||||||
|
let i--
|
||||||
|
fi
|
||||||
|
optlen=0
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
# Check if it's a short option
|
||||||
|
elif [[ "${word:0:1}" = '-' ]]; then
|
||||||
|
parse_short_opt "$word"
|
||||||
|
# The last option takes an argument...
|
||||||
|
if [[ "$state" != 'normal' ]]; then
|
||||||
|
if [[ "$optlen" -ne "${#word}" ]]; then
|
||||||
|
# If it's contained within the word, we won't complete it, revert to "normal"
|
||||||
|
state=normal
|
||||||
|
else
|
||||||
|
# Otherwise, complete it, but start at the beginning of *that* word
|
||||||
|
optlen=0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Parse current word
|
||||||
|
# Careful that it might look like an option, so use `--` aggressively!
|
||||||
|
local cur_word="${COMP_WORDS[$COMP_CWORD]}"
|
||||||
|
|
||||||
|
# Process options, as short ones may change the state
|
||||||
|
if $opt_ena && [[ "$state" = 'normal' && "${cur_word:0:1}" = '-' ]]; then
|
||||||
|
# We might want to complete to an option or an arg to that option
|
||||||
|
# Parse the option word to check
|
||||||
|
# There's no whitespace in the option names, so we can ride a little dirty...
|
||||||
|
|
||||||
|
# Is this a long option?
|
||||||
|
if [[ "${cur_word:1:1}" = '-' ]]; then
|
||||||
|
# It is, try to complete one
|
||||||
|
COMPREPLY+=( $(compgen -W "${opts[*]%%:*}" -P '--' -- "${cur_word#--}") )
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
# Short options may be grouped, parse them to determine what to complete
|
||||||
|
parse_short_opt "$cur_word"
|
||||||
|
|
||||||
|
if [[ "$state" = 'normal' ]]; then
|
||||||
|
COMPREPLY+=( $(compgen -W "${!opts[*]}" -P "$cur_word" '') )
|
||||||
|
return 0
|
||||||
|
elif [[ "$optlen" = "${#cur_word}" && "$state" != "warning" ]]; then
|
||||||
|
# This short option group only awaits its argument!
|
||||||
|
# Post the option group as-is as a reply so that Readline inserts a space,
|
||||||
|
# so that the next completion request switches to the argument
|
||||||
|
# An exception is made for warnings, since it's idiomatic to stick them to the
|
||||||
|
# `-W`, and it doesn't break anything.
|
||||||
|
COMPREPLY+=( "$cur_word" )
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$state" in
|
||||||
|
unk) # Return with no replies: no idea what to complete!
|
||||||
|
;;
|
||||||
|
fix-spec)
|
||||||
|
COMPREPLY+=( "${cur_word}"{l,h,g,L,H,G} )
|
||||||
|
;;
|
||||||
|
mbc)
|
||||||
|
local cur_arg="${cur_word:$optlen}"
|
||||||
|
cur_arg="${cur_arg@U}"
|
||||||
|
COMPREPLY=( $(compgen -W "
|
||||||
|
ROM_ONLY
|
||||||
|
MBC1{,+RAM,+RAM+BATTERY}
|
||||||
|
MBC2{,+BATTERY}
|
||||||
|
MMM01{,+RAM}
|
||||||
|
MBC3{+TIMER+BATTERY,+TIMER+RAM+BATTERY,,+RAM,+RAM+BATTERY}
|
||||||
|
MBC5{,+RAM,+RAM+BATTERY,+RUMBLE,+RUMBLE+RAM,+RUMBLE+RAM+BATTERY}
|
||||||
|
MBC6
|
||||||
|
MBC7+SENSOR+RUMBLE+RAM+BATTERY
|
||||||
|
POCKET_CAMERA
|
||||||
|
BANDAI_TAMA5
|
||||||
|
HUC3
|
||||||
|
HUC1+RAM+BATTERY
|
||||||
|
TPP1_1.0{,+BATTERY}{,+RTC}{,+RUMBLE,+MULTIRUMBLE}" -P "${cur_word:0:$optlen}" -- "`tr 'a-z ' 'A-Z_' <<<"${cur_word/ /_}"`") )
|
||||||
|
COMPREPLY+=( $(compgen -W "help" -P "${cur_word:0:$optlen}" -- "${cur_word:$optlen}") )
|
||||||
|
;;
|
||||||
|
normal) # Acts like a glob...
|
||||||
|
state="glob-*.gb *.gbc *.sgb"
|
||||||
|
;&
|
||||||
|
glob-*)
|
||||||
|
while read -r word; do
|
||||||
|
COMPREPLY+=("${cur_word:0:$optlen}$word")
|
||||||
|
done < <(for glob in ${state#glob-}; do compgen -A file -X \!"$glob" -- "${cur_word:$optlen}"; done)
|
||||||
|
# Also complete directories
|
||||||
|
;&
|
||||||
|
dir)
|
||||||
|
while read -r word; do
|
||||||
|
COMPREPLY+=("${cur_word:0:$optlen}$word")
|
||||||
|
done < <(compgen -A directory -- "${cur_word:$optlen}")
|
||||||
|
compopt -o filenames
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
complete -F _rgbfix_completions rgbfix
|
||||||
Executable
+162
@@ -0,0 +1,162 @@
|
|||||||
|
#/usr/bin/env bash
|
||||||
|
|
||||||
|
# Same notes as RGBASM
|
||||||
|
|
||||||
|
_rgbgfx_completions() {
|
||||||
|
COMPREPLY=()
|
||||||
|
|
||||||
|
# Format: "long_opt:state_after"
|
||||||
|
# Empty long opt = it doesn't exit
|
||||||
|
# See the `state` variable below for info about `state_after`
|
||||||
|
declare -A opts=(
|
||||||
|
[V]="version:normal"
|
||||||
|
[C]="color-curve:normal"
|
||||||
|
[D]="debug:normal"
|
||||||
|
[h]="horizontal:normal"
|
||||||
|
[m]="mirror-tiles:normal"
|
||||||
|
[u]="unique-tiles:normal"
|
||||||
|
[v]="verbose:normal"
|
||||||
|
[f]="fix:normal"
|
||||||
|
[F]="fix-and-save:normal"
|
||||||
|
[a]="attr-map:*.attrmap"
|
||||||
|
[A]="output-attr-map:normal"
|
||||||
|
[d]="depth:unk"
|
||||||
|
[o]="output:glob *.2bpp"
|
||||||
|
[p]="palette:glob *.pal"
|
||||||
|
[P]="output-palette:normal"
|
||||||
|
[t]="tilemap:glob *.tilemap"
|
||||||
|
[T]="output-tilemap:normal"
|
||||||
|
[x]="trim-end:unk"
|
||||||
|
)
|
||||||
|
# Parse command-line up to current word
|
||||||
|
local opt_ena=true
|
||||||
|
# Possible states:
|
||||||
|
# - normal = Well, normal. Options are parsed normally.
|
||||||
|
# - unk = An argument that can't be completed, and should just be skipped.
|
||||||
|
# - warning = A warning flag.
|
||||||
|
# - dir = A directory path
|
||||||
|
# - glob-* = A glob, after the dash is a whitespace-separated list of file globs to use
|
||||||
|
local state=normal
|
||||||
|
# The length of the option, used as a return value by the function below
|
||||||
|
local optlen=0
|
||||||
|
# $1: a short option word
|
||||||
|
# `state` will be set to the parsing state after the last option character in the word. If
|
||||||
|
# "normal" is not returned, `optlen` will be set to the length (dash included) of the "option"
|
||||||
|
# part of the argument.
|
||||||
|
parse_short_opt() {
|
||||||
|
for (( i = 1; i < "${#1}"; i++ )); do
|
||||||
|
# If the option is not known, assume it doesn't take an argument
|
||||||
|
local opt="${opts["${1:$i:1}"]:-":normal"}"
|
||||||
|
state="${opt#*:}"
|
||||||
|
# If the option takes an argument, record the length and exit
|
||||||
|
if [[ "$state" != 'normal' ]]; then
|
||||||
|
let optlen="$i + 1"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
optlen=0
|
||||||
|
}
|
||||||
|
|
||||||
|
for (( i = 1; i < $COMP_CWORD; i++ )); do
|
||||||
|
local word="${COMP_WORDS[$i]}"
|
||||||
|
|
||||||
|
# If currently processing an argument, skip this word
|
||||||
|
if [[ "$state" != 'normal' ]]; then
|
||||||
|
state=normal
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$word" = '--' ]]; then
|
||||||
|
# Options stop being parsed after this
|
||||||
|
opt_ena=false
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if it's a long option
|
||||||
|
if [[ "${word:0:2}" = '--' ]]; then
|
||||||
|
# If the option is unknown, assume it takes no arguments: keep the state at "normal"
|
||||||
|
for long_opt in "${opts[@]}"; do
|
||||||
|
if [[ "$word" = "--${long_opt%%:*}" ]]; then
|
||||||
|
state="${long_opt#*:}"
|
||||||
|
# Check if the next word is just '='; if so, skip it, the argument must follow
|
||||||
|
# (See "known bugs" at the top of this script)
|
||||||
|
let i++
|
||||||
|
if [[ "${COMP_WORDS[$i]}" != '=' ]]; then
|
||||||
|
let i--
|
||||||
|
fi
|
||||||
|
optlen=0
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
# Check if it's a short option
|
||||||
|
elif [[ "${word:0:1}" = '-' ]]; then
|
||||||
|
parse_short_opt "$word"
|
||||||
|
# The last option takes an argument...
|
||||||
|
if [[ "$state" != 'normal' ]]; then
|
||||||
|
if [[ "$optlen" -ne "${#word}" ]]; then
|
||||||
|
# If it's contained within the word, we won't complete it, revert to "normal"
|
||||||
|
state=normal
|
||||||
|
else
|
||||||
|
# Otherwise, complete it, but start at the beginning of *that* word
|
||||||
|
optlen=0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Parse current word
|
||||||
|
# Careful that it might look like an option, so use `--` aggressively!
|
||||||
|
local cur_word="${COMP_WORDS[$COMP_CWORD]}"
|
||||||
|
|
||||||
|
# Process options, as short ones may change the state
|
||||||
|
if $opt_ena && [[ "$state" = 'normal' && "${cur_word:0:1}" = '-' ]]; then
|
||||||
|
# We might want to complete to an option or an arg to that option
|
||||||
|
# Parse the option word to check
|
||||||
|
# There's no whitespace in the option names, so we can ride a little dirty...
|
||||||
|
|
||||||
|
# Is this a long option?
|
||||||
|
if [[ "${cur_word:1:1}" = '-' ]]; then
|
||||||
|
# It is, try to complete one
|
||||||
|
COMPREPLY+=( $(compgen -W "${opts[*]%%:*}" -P '--' -- "${cur_word#--}") )
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
# Short options may be grouped, parse them to determine what to complete
|
||||||
|
parse_short_opt "$cur_word"
|
||||||
|
|
||||||
|
if [[ "$state" = 'normal' ]]; then
|
||||||
|
COMPREPLY+=( $(compgen -W "${!opts[*]}" -P "$cur_word" '') )
|
||||||
|
return 0
|
||||||
|
elif [[ "$optlen" = "${#cur_word}" && "$state" != "warning" ]]; then
|
||||||
|
# This short option group only awaits its argument!
|
||||||
|
# Post the option group as-is as a reply so that Readline inserts a space,
|
||||||
|
# so that the next completion request switches to the argument
|
||||||
|
# An exception is made for warnings, since it's idiomatic to stick them to the
|
||||||
|
# `-W`, and it doesn't break anything.
|
||||||
|
COMPREPLY+=( "$cur_word" )
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$state" in
|
||||||
|
unk) # Return with no replies: no idea what to complete!
|
||||||
|
;;
|
||||||
|
normal) # Acts like a glob...
|
||||||
|
state="glob-*.png"
|
||||||
|
;&
|
||||||
|
glob-*)
|
||||||
|
while read -r word; do
|
||||||
|
COMPREPLY+=("${cur_word:0:$optlen}$word")
|
||||||
|
done < <(for glob in ${state#glob-}; do compgen -A file -X \!"$glob" -- "${cur_word:$optlen}"; done)
|
||||||
|
# Also complete directories
|
||||||
|
;&
|
||||||
|
dir)
|
||||||
|
while read -r word; do
|
||||||
|
COMPREPLY+=("${cur_word:0:$optlen}$word")
|
||||||
|
done < <(compgen -A directory -- "${cur_word:$optlen}")
|
||||||
|
compopt -o filenames
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
complete -F _rgbgfx_completions rgbgfx
|
||||||
Executable
+157
@@ -0,0 +1,157 @@
|
|||||||
|
#/usr/bin/env bash
|
||||||
|
|
||||||
|
# Same notes as RGBASM
|
||||||
|
|
||||||
|
_rgblink_completions() {
|
||||||
|
COMPREPLY=()
|
||||||
|
|
||||||
|
# Format: "long_opt:state_after"
|
||||||
|
# Empty long opt = it doesn't exit
|
||||||
|
# See the `state` variable below for info about `state_after`
|
||||||
|
declare -A opts=(
|
||||||
|
[V]="version:normal"
|
||||||
|
[d]="dmg:normal"
|
||||||
|
[t]="tiny:normal"
|
||||||
|
[v]="verbose:normal"
|
||||||
|
[w]="wramx:normal"
|
||||||
|
[x]="nopad:normal"
|
||||||
|
[l]="linkerscript:glob-*"
|
||||||
|
[m]="map:glob-*.map"
|
||||||
|
[n]="sym:glob-*.sym"
|
||||||
|
[O]="overlay:glob-*.gb *.gbc *.sgb"
|
||||||
|
[o]="output:glob-*.gb *.gbc *.sgb"
|
||||||
|
[p]="pad:unk"
|
||||||
|
[s]="smart:unk"
|
||||||
|
)
|
||||||
|
# Parse command-line up to current word
|
||||||
|
local opt_ena=true
|
||||||
|
# Possible states:
|
||||||
|
# - normal = Well, normal. Options are parsed normally.
|
||||||
|
# - unk = An argument that can't be completed, and should just be skipped.
|
||||||
|
# - warning = A warning flag.
|
||||||
|
# - dir = A directory path
|
||||||
|
# - glob-* = A glob, after the dash is a whitespace-separated list of file globs to use
|
||||||
|
local state=normal
|
||||||
|
# The length of the option, used as a return value by the function below
|
||||||
|
local optlen=0
|
||||||
|
# $1: a short option word
|
||||||
|
# `state` will be set to the parsing state after the last option character in the word. If
|
||||||
|
# "normal" is not returned, `optlen` will be set to the length (dash included) of the "option"
|
||||||
|
# part of the argument.
|
||||||
|
parse_short_opt() {
|
||||||
|
for (( i = 1; i < "${#1}"; i++ )); do
|
||||||
|
# If the option is not known, assume it doesn't take an argument
|
||||||
|
local opt="${opts["${1:$i:1}"]:-":normal"}"
|
||||||
|
state="${opt#*:}"
|
||||||
|
# If the option takes an argument, record the length and exit
|
||||||
|
if [[ "$state" != 'normal' ]]; then
|
||||||
|
let optlen="$i + 1"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
optlen=0
|
||||||
|
}
|
||||||
|
|
||||||
|
for (( i = 1; i < $COMP_CWORD; i++ )); do
|
||||||
|
local word="${COMP_WORDS[$i]}"
|
||||||
|
|
||||||
|
# If currently processing an argument, skip this word
|
||||||
|
if [[ "$state" != 'normal' ]]; then
|
||||||
|
state=normal
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$word" = '--' ]]; then
|
||||||
|
# Options stop being parsed after this
|
||||||
|
opt_ena=false
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check if it's a long option
|
||||||
|
if [[ "${word:0:2}" = '--' ]]; then
|
||||||
|
# If the option is unknown, assume it takes no arguments: keep the state at "normal"
|
||||||
|
for long_opt in "${opts[@]}"; do
|
||||||
|
if [[ "$word" = "--${long_opt%%:*}" ]]; then
|
||||||
|
state="${long_opt#*:}"
|
||||||
|
# Check if the next word is just '='; if so, skip it, the argument must follow
|
||||||
|
# (See "known bugs" at the top of this script)
|
||||||
|
let i++
|
||||||
|
if [[ "${COMP_WORDS[$i]}" != '=' ]]; then
|
||||||
|
let i--
|
||||||
|
fi
|
||||||
|
optlen=0
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
# Check if it's a short option
|
||||||
|
elif [[ "${word:0:1}" = '-' ]]; then
|
||||||
|
parse_short_opt "$word"
|
||||||
|
# The last option takes an argument...
|
||||||
|
if [[ "$state" != 'normal' ]]; then
|
||||||
|
if [[ "$optlen" -ne "${#word}" ]]; then
|
||||||
|
# If it's contained within the word, we won't complete it, revert to "normal"
|
||||||
|
state=normal
|
||||||
|
else
|
||||||
|
# Otherwise, complete it, but start at the beginning of *that* word
|
||||||
|
optlen=0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Parse current word
|
||||||
|
# Careful that it might look like an option, so use `--` aggressively!
|
||||||
|
local cur_word="${COMP_WORDS[$COMP_CWORD]}"
|
||||||
|
|
||||||
|
# Process options, as short ones may change the state
|
||||||
|
if $opt_ena && [[ "$state" = 'normal' && "${cur_word:0:1}" = '-' ]]; then
|
||||||
|
# We might want to complete to an option or an arg to that option
|
||||||
|
# Parse the option word to check
|
||||||
|
# There's no whitespace in the option names, so we can ride a little dirty...
|
||||||
|
|
||||||
|
# Is this a long option?
|
||||||
|
if [[ "${cur_word:1:1}" = '-' ]]; then
|
||||||
|
# It is, try to complete one
|
||||||
|
COMPREPLY+=( $(compgen -W "${opts[*]%%:*}" -P '--' -- "${cur_word#--}") )
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
# Short options may be grouped, parse them to determine what to complete
|
||||||
|
parse_short_opt "$cur_word"
|
||||||
|
|
||||||
|
if [[ "$state" = 'normal' ]]; then
|
||||||
|
COMPREPLY+=( $(compgen -W "${!opts[*]}" -P "$cur_word" '') )
|
||||||
|
return 0
|
||||||
|
elif [[ "$optlen" = "${#cur_word}" && "$state" != "warning" ]]; then
|
||||||
|
# This short option group only awaits its argument!
|
||||||
|
# Post the option group as-is as a reply so that Readline inserts a space,
|
||||||
|
# so that the next completion request switches to the argument
|
||||||
|
# An exception is made for warnings, since it's idiomatic to stick them to the
|
||||||
|
# `-W`, and it doesn't break anything.
|
||||||
|
COMPREPLY+=( "$cur_word" )
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$state" in
|
||||||
|
unk) # Return with no replies: no idea what to complete!
|
||||||
|
;;
|
||||||
|
normal) # Acts like a glob...
|
||||||
|
state="glob-*.o *.obj"
|
||||||
|
;&
|
||||||
|
glob-*)
|
||||||
|
while read -r word; do
|
||||||
|
COMPREPLY+=("${cur_word:0:$optlen}$word")
|
||||||
|
done < <(for glob in ${state#glob-}; do compgen -A file -X \!"$glob" -- "${cur_word:$optlen}"; done)
|
||||||
|
# Also complete directories
|
||||||
|
;&
|
||||||
|
dir)
|
||||||
|
while read -r word; do
|
||||||
|
COMPREPLY+=("${cur_word:0:$optlen}$word")
|
||||||
|
done < <(compgen -A directory -- "${cur_word:$optlen}")
|
||||||
|
compopt -o filenames
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
complete -F _rgblink_completions rgblink
|
||||||
Executable
+85
@@ -0,0 +1,85 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# SPDX-License-Identifier: MIT
|
||||||
|
#
|
||||||
|
# Copyright (c) 2021 Rangi
|
||||||
|
#
|
||||||
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
# of this software and associated documentation files (the "Software"), to deal
|
||||||
|
# in the Software without restriction, including without limitation the rights
|
||||||
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
# copies of the Software, and to permit persons to whom the Software is
|
||||||
|
# furnished to do so, subject to the following conditions:
|
||||||
|
#
|
||||||
|
# The above copyright notice and this permission notice shall be included in all
|
||||||
|
# copies or substantial portions of the Software.
|
||||||
|
#
|
||||||
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
# SOFTWARE.
|
||||||
|
|
||||||
|
declare -A FILES
|
||||||
|
while read -r -d '' file; do
|
||||||
|
FILES["$file"]="true"
|
||||||
|
done < <(git diff --name-only -z $1 HEAD)
|
||||||
|
|
||||||
|
edited () {
|
||||||
|
${FILES["$1"]:-"false"}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependency () {
|
||||||
|
if edited "$1" && ! edited "$2"; then
|
||||||
|
echo "'$1' was modified, but not '$2'! $3" | xargs
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Pull requests that edit the first file without the second may be correct,
|
||||||
|
# but are suspicious enough to require review.
|
||||||
|
|
||||||
|
dependency include/linkdefs.h src/rgbds.5 \
|
||||||
|
"Was the object file format changed?"
|
||||||
|
|
||||||
|
dependency src/asm/parser.y src/asm/rgbasm.5 \
|
||||||
|
"Was the rgbasm grammar changed?"
|
||||||
|
|
||||||
|
dependency include/asm/warning.h src/asm/rgbasm.1 \
|
||||||
|
"Were the rgbasm warnings changed?"
|
||||||
|
|
||||||
|
dependency src/asm/object.c include/linkdefs.h \
|
||||||
|
"Should the object file revision be bumped?"
|
||||||
|
dependency src/link/object.c include/linkdefs.h \
|
||||||
|
"Should the object file revision be bumped?"
|
||||||
|
|
||||||
|
dependency Makefile CMakeLists.txt \
|
||||||
|
"Did the build process change?"
|
||||||
|
dependency Makefile src/CMakeLists.txt \
|
||||||
|
"Did the build process change?"
|
||||||
|
|
||||||
|
dependency src/asm/main.c src/asm/rgbasm.1 \
|
||||||
|
"Did the rgbasm CLI change?"
|
||||||
|
dependency src/asm/main.c contrib/zsh_compl/_rgbasm \
|
||||||
|
"Did the rgbasm CLI change?"
|
||||||
|
dependency src/asm/main.c contrib/bash_compl/_rgbasm.bash \
|
||||||
|
"Did the rgbasm CLI change?"
|
||||||
|
dependency src/link/main.c src/link/rgblink.1 \
|
||||||
|
"Did the rgblink CLI change?"
|
||||||
|
dependency src/link/main.c contrib/zsh_compl/_rgblink \
|
||||||
|
"Did the rgblink CLI change?"
|
||||||
|
dependency src/link/main.c contrib/bash_compl/_rgblink.bash \
|
||||||
|
"Did the rgblink CLI change?"
|
||||||
|
dependency src/fix/main.c src/fix/rgbfix.1 \
|
||||||
|
"Did the rgbfix CLI change?"
|
||||||
|
dependency src/fix/main.c contrib/zsh_compl/_rgbfix \
|
||||||
|
"Did the rgbfix CLI change?"
|
||||||
|
dependency src/fix/main.c contrib/bash_compl/_rgbfix.bash \
|
||||||
|
"Did the rgbfix CLI change?"
|
||||||
|
dependency src/gfx/main.c src/gfx/rgbgfx.1 \
|
||||||
|
"Did the rgbgfx CLI change?"
|
||||||
|
dependency src/gfx/main.c contrib/zsh_compl/_rgbgfx \
|
||||||
|
"Did the rgbgfx CLI change?"
|
||||||
|
dependency src/gfx/main.c contrib/bash_compl/_rgbgfx.bash \
|
||||||
|
"Did the rgbgfx CLI change?"
|
||||||
+12
-10
@@ -31,31 +31,33 @@ diff <(xxd $1) <(xxd $2) | while read -r LINE; do
|
|||||||
# Separator between files switches states
|
# Separator between files switches states
|
||||||
echo $LINE
|
echo $LINE
|
||||||
STATE=3
|
STATE=3
|
||||||
elif echo $LINE | grep -Eq '^[0-9]+(,[0-9]+)?c[0-9]+(,[0-9]+)?'; then
|
elif grep -Eq '^[0-9]+(,[0-9]+)?[cd][0-9]+(,[0-9]+)?' <<< "$LINE"; then
|
||||||
# Line info resets the whole thing
|
# Line info resets the whole thing
|
||||||
STATE=1
|
STATE=1
|
||||||
elif [ $STATE -eq 1 -o $STATE -eq 3 ]; then
|
elif [ $STATE -eq 1 -o $STATE -eq 3 ]; then
|
||||||
# Compute the GB address from the ROM offset
|
# Compute the GB address from the ROM offset
|
||||||
OFS=$(echo $LINE | cut -d ' ' -f 2 | tr -d ':')
|
OFS=$(cut -d ' ' -f 2 <<< "$LINE" | tr -d ':')
|
||||||
BANK=$((0x$OFS / 0x4000))
|
BANK=$((0x$OFS / 0x4000))
|
||||||
ADDR=$((0x$OFS % 0x4000 + ($BANK != 0) * 0x4000))
|
ADDR=$((0x$OFS % 0x4000 + ($BANK != 0) * 0x4000))
|
||||||
# Try finding the preceding symbol closest to the diff
|
# Try finding the preceding symbol closest to the diff
|
||||||
if [ $STATE -eq 1 ]; then
|
if [ $STATE -eq 1 ]; then
|
||||||
STATE=2
|
STATE=2
|
||||||
SYMFILE=$(echo $1 | cut -d '.' -f 1).sym
|
SYMFILE=${1%.*}.sym
|
||||||
else
|
else
|
||||||
STATE=4
|
STATE=4
|
||||||
SYMFILE=$(echo $2 | cut -d '.' -f 1).sym
|
SYMFILE=${2%.*}.sym
|
||||||
fi
|
fi
|
||||||
EXTRA=$(if [ -f "$SYMFILE" ]; then
|
EXTRA=$(if [ -f "$SYMFILE" ]; then
|
||||||
# Read the sym file for such a symbol
|
# Read the sym file for such a symbol
|
||||||
# Ignore comment lines, only pick matching bank
|
# Ignore comment lines, only pick matching bank
|
||||||
grep -Fv ';' "$SYMFILE" |
|
# (The bank regex ignores comments already, make `cut` and `tr` process less lines)
|
||||||
grep -Ei $(printf "^%02x:" $BANK) |
|
grep -Ei $(printf "^%02x:" $BANK) "$SYMFILE" |
|
||||||
|
cut -d ';' -f 1 |
|
||||||
|
tr -d "\r" |
|
||||||
while read -r SYMADDR SYM; do
|
while read -r SYMADDR SYM; do
|
||||||
SYMADDR=$((0x$(echo $SYMADDR | cut -d ':' -f 2)))
|
SYMADDR=$((0x${SYMADDR#*:}))
|
||||||
if [ $SYMADDR -le $ADDR ]; then
|
if [ $SYMADDR -le $ADDR ]; then
|
||||||
printf " (%s+%#x)\n" $SYM $(($ADDR - $SYMADDR))
|
printf " (%s+%#x)\n" "$SYM" $(($ADDR - $SYMADDR))
|
||||||
fi
|
fi
|
||||||
# TODO: assumes sorted sym files
|
# TODO: assumes sorted sym files
|
||||||
done | tail -n 1
|
done | tail -n 1
|
||||||
@@ -63,9 +65,9 @@ diff <(xxd $1) <(xxd $2) | while read -r LINE; do
|
|||||||
printf "%02x:%04x %s\n" $BANK $ADDR $EXTRA
|
printf "%02x:%04x %s\n" $BANK $ADDR $EXTRA
|
||||||
fi
|
fi
|
||||||
if [ $STATE -eq 2 -o $STATE -eq 4 ]; then
|
if [ $STATE -eq 2 -o $STATE -eq 4 ]; then
|
||||||
OFS=$(echo $LINE | cut -d ' ' -f 2 | tr -d ':')
|
OFS=$(cut -d ' ' -f 2 <<< "$LINE" | tr -d ':')
|
||||||
BANK=$((0x$OFS / 0x4000))
|
BANK=$((0x$OFS / 0x4000))
|
||||||
ADDR=$((0x$OFS % 0x4000 + ($BANK != 0) * 0x4000))
|
ADDR=$((0x$OFS % 0x4000 + ($BANK != 0) * 0x4000))
|
||||||
printf "%s %02x:%04x: %s\n" "$(echo $LINE | cut -d ' ' -f 1)" $BANK $ADDR "$(echo $LINE | cut -d ' ' -f 3-)"
|
printf "%s %02x:%04x: %s\n" "${LINE:0:1}" $BANK $ADDR "${LINE#*: }"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|||||||
@@ -9,18 +9,26 @@ _rgbasm_warnings() {
|
|||||||
'everything:Enable literally everything'
|
'everything:Enable literally everything'
|
||||||
|
|
||||||
'assert:Warn when WARN-type asserts fail'
|
'assert:Warn when WARN-type asserts fail'
|
||||||
|
'backwards-for:Warn when start and stop are backwards relative to step'
|
||||||
'builtin-args:Report incorrect args to built-in funcs'
|
'builtin-args:Report incorrect args to built-in funcs'
|
||||||
|
'charmap-redef:Warn when redefining a charmap mapping'
|
||||||
'div:Warn when dividing the smallest int by -1'
|
'div:Warn when dividing the smallest int by -1'
|
||||||
'empty-entry:Warn on empty entries in db, dw, dl args'
|
'empty-data-directive:Warn on arg-less d[bwl] in ROM'
|
||||||
|
'empty-macro-arg:Warn on empty macro arg'
|
||||||
|
'empty-strrpl:Warn on calling STRRPL with empty pattern'
|
||||||
'large-constant:Warn on constants too large for a signed 32-bit int'
|
'large-constant:Warn on constants too large for a signed 32-bit int'
|
||||||
'long-string:Warn on strings too long'
|
'long-string:Warn on strings too long'
|
||||||
|
'macro-shift:Warn when shifting macro args part their limits'
|
||||||
|
'nested-comment:Warn on "/*" inside block comments'
|
||||||
|
'numeric-string:Warn when a multi-character string is treated as a number'
|
||||||
'obsolete:Warn when using deprecated features'
|
'obsolete:Warn when using deprecated features'
|
||||||
'shift:Warn when shifting negative values'
|
'shift:Warn when shifting negative values'
|
||||||
'shift-amount:Warn when a shift'\''s operand it negative or \> 32'
|
'shift-amount:Warn when a shift'\''s operand it negative or \> 32'
|
||||||
'truncation:Warn when implicit truncations lose bits'
|
'truncation:Warn when implicit truncation loses bits'
|
||||||
'user:Warn when executing the WARN built-in'
|
'user:Warn when executing the WARN built-in'
|
||||||
)
|
)
|
||||||
# TODO: handle `no-` and `error=` somehow?
|
# TODO: handle `no-` and `error=` somehow?
|
||||||
|
# TODO: handle `=0|1|2` levels for `numeric-string` and `truncation`?
|
||||||
_describe warning warnings
|
_describe warning warnings
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,15 +43,19 @@ local args=(
|
|||||||
-w'[Disable all warnings]'
|
-w'[Disable all warnings]'
|
||||||
|
|
||||||
'(-b --binary-digits)'{-b,--binary-digits}'+[Change chars for binary constants]:digit spec:'
|
'(-b --binary-digits)'{-b,--binary-digits}'+[Change chars for binary constants]:digit spec:'
|
||||||
'(-D --define)'{-D,--define}'+[Define a string symbol]:name + value (default 1):'
|
'*'{-D,--define}'+[Define a string symbol]:name + value (default 1):'
|
||||||
'(-g --gfx-chars)'{-g,--gfx-chars}'+[Change chars for gfx constants]:chars spec:'
|
'(-g --gfx-chars)'{-g,--gfx-chars}'+[Change chars for gfx constants]:chars spec:'
|
||||||
'(-i --include)'{-i,--include}'+[Add an include directory]:include path:_files -/'
|
'(-i --include)'{-i,--include}'+[Add an include directory]:include path:_files -/'
|
||||||
'(-M --dependfile)'{-M,--dependfile}"+[List deps in make format]:output file:_files -g '*.{d,mk}"
|
'(-M --dependfile)'{-M,--dependfile}"+[List deps in make format]:output file:_files -g '*.{d,mk}'"
|
||||||
|
-MG'[Assume missing files should be generated]'
|
||||||
|
-MP'[Add phony targets to all deps]'
|
||||||
|
'*'-MT"+[Add a target to the rules]:target:_files -g '*.{d,mk,o}'"
|
||||||
|
'*'-MQ"+[Add a target to the rules]:target:_files -g '*.{d,mk,o}'"
|
||||||
'(-o --output)'{-o,--output}'+[Output file]:output file:_files'
|
'(-o --output)'{-o,--output}'+[Output file]:output file:_files'
|
||||||
'(-p --pad-value)'{-p,--pad-value}'+[Set padding byte]:padding byte:'
|
'(-p --pad-value)'{-p,--pad-value}'+[Set padding byte]:padding byte:'
|
||||||
'(-r --recursion-depth)'{-r,--recursion-depth}'+[Set maximum recursion depth]:depth:'
|
'(-r --recursion-depth)'{-r,--recursion-depth}'+[Set maximum recursion depth]:depth:'
|
||||||
'(-W --warning)'{-W,--warning}'+[Toggle warning flags]:warning flag:_rgbasm_warnings'
|
'(-W --warning)'{-W,--warning}'+[Toggle warning flags]:warning flag:_rgbasm_warnings'
|
||||||
|
|
||||||
'*'":assembly sources:_files -g '*.asm'"
|
":assembly sources:_files -g '*.asm'"
|
||||||
)
|
)
|
||||||
_arguments -s -S : $args
|
_arguments -s -S : $args
|
||||||
|
|||||||
@@ -1,12 +1,47 @@
|
|||||||
#compdef rgbfix
|
#compdef rgbfix
|
||||||
|
|
||||||
|
_mbc_names() {
|
||||||
|
local mbc_names=(
|
||||||
|
'ROM:$00'
|
||||||
|
'MBC1:$01'
|
||||||
|
'MBC1+RAM:$02'
|
||||||
|
'MBC1+RAM+BATTERY:$03'
|
||||||
|
'MBC2:$05'
|
||||||
|
'MBC2+BATTERY:$06'
|
||||||
|
'ROM+RAM:$08'
|
||||||
|
'ROM+RAM+BATTERY:$09'
|
||||||
|
'MMM01:$0B'
|
||||||
|
'MMM01+RAM:$0C'
|
||||||
|
'MMM01+RAM+BATTERY:$0D'
|
||||||
|
'MBC3+TIMER+BATTERY:$0F'
|
||||||
|
'MBC3+TIMER+RAM+BATTERY:$10'
|
||||||
|
'MBC3:$11'
|
||||||
|
'MBC3+RAM:$12'
|
||||||
|
'MBC3+RAM+BATTERY:$13'
|
||||||
|
'MBC5:$19'
|
||||||
|
'MBC5+RAM:$1A'
|
||||||
|
'MBC5+RAM+BATTERY:$1B'
|
||||||
|
'MBC5+RUMBLE:$1C'
|
||||||
|
'MBC5+RUMBLE+RAM:$1D'
|
||||||
|
'MBC5+RUMBLE+RAM+BATTERY:$1E'
|
||||||
|
'MBC6:$20'
|
||||||
|
'MBC7+SENSOR+RUMBLE+RAM+BATTERY:$22'
|
||||||
|
'POCKET_CAMERA:$FC'
|
||||||
|
'BANDAI_TAMA5:$FD'
|
||||||
|
'HUC3:$FE'
|
||||||
|
'HUC1+RAM+BATTERY:$FF'
|
||||||
|
)
|
||||||
|
_describe "MBC name" mbc_names
|
||||||
|
}
|
||||||
|
|
||||||
local args=(
|
local args=(
|
||||||
# Arguments are listed here in the same order as in the manual, except for the version
|
# Arguments are listed here in the same order as in the manual, except for the version
|
||||||
'(- : * options)'{-V,--version}'[Print version number]'
|
'(- : * options)'{-V,--version}'[Print version number]'
|
||||||
|
|
||||||
'(-C --color-only)'{-C,--color-only}'[Mark ROM as GBC-only]'
|
'(-C --color-only -c --color-compatible)'{-C,--color-only}'[Mark ROM as GBC-only]'
|
||||||
'(-c --color-compatible)'{-c,--color-compatible}'[Mark ROM as GBC-compatible]'
|
'(-C --color-only -c --color-compatible)'{-c,--color-compatible}'[Mark ROM as GBC-compatible]'
|
||||||
'(-j --non-japanese)'{-j,--non-japanese}'[Set the non-Japanese region flag]'
|
'(-j --non-japanese)'{-j,--non-japanese}'[Set the non-Japanese region flag]'
|
||||||
|
'(-O --overwrite)'{-O,--overwrite}'[Allow overwriting non-zero bytes]'
|
||||||
'(-s --sgb-compatible)'{-s,--sgb-compatible}'[Set the SGB flag]'
|
'(-s --sgb-compatible)'{-s,--sgb-compatible}'[Set the SGB flag]'
|
||||||
'(-f --fix-spec -v --validate)'{-v,--validate}'[Shorthand for -f lhg]'
|
'(-f --fix-spec -v --validate)'{-v,--validate}'[Shorthand for -f lhg]'
|
||||||
|
|
||||||
@@ -14,7 +49,7 @@ local args=(
|
|||||||
'(-i --game-id)'{-i,--game-id}'+[Set game ID string]:4-char game ID:'
|
'(-i --game-id)'{-i,--game-id}'+[Set game ID string]:4-char game ID:'
|
||||||
'(-k --new-licensee)'{-k,--new-licensee}'+[Set new licensee string]:2-char licensee ID:'
|
'(-k --new-licensee)'{-k,--new-licensee}'+[Set new licensee string]:2-char licensee ID:'
|
||||||
'(-l --old-licensee)'{-l,--old-licensee}'+[Set old licensee ID]:licensee number:'
|
'(-l --old-licensee)'{-l,--old-licensee}'+[Set old licensee ID]:licensee number:'
|
||||||
'(-m --mbc-type)'{-m,--mbc-type}'+[Set MBC flags]:mbc flags byte:'
|
'(-m --mbc-type)'{-m,--mbc-type}"+[Set MBC flags]:mbc name:_mbc_names"
|
||||||
'(-n --rom-version)'{-n,--rom-version}'+[Set ROM version]:rom version byte:'
|
'(-n --rom-version)'{-n,--rom-version}'+[Set ROM version]:rom version byte:'
|
||||||
'(-p --pad-value)'{-p,--pad-value}'+[Pad to next valid size using this byte as padding]:padding byte:'
|
'(-p --pad-value)'{-p,--pad-value}'+[Pad to next valid size using this byte as padding]:padding byte:'
|
||||||
'(-r --ram-size)'{-r,--ram-size}'+[Set RAM size]:ram size byte:'
|
'(-r --ram-size)'{-r,--ram-size}'+[Set RAM size]:ram size byte:'
|
||||||
|
|||||||
@@ -32,6 +32,6 @@ local args=(
|
|||||||
'(-t --tilemap -T --output-tilemap)'{-t,--tilemap}'+[Generate a map of tile indices]:tilemap file:_files'
|
'(-t --tilemap -T --output-tilemap)'{-t,--tilemap}'+[Generate a map of tile indices]:tilemap file:_files'
|
||||||
'(-x --trim-end)'{-x,--trim-end}'+[Trim end of output by this many tiles]:tile count:'
|
'(-x --trim-end)'{-x,--trim-end}'+[Trim end of output by this many tiles]:tile count:'
|
||||||
|
|
||||||
'*'":input png files:_files -g '*.png'"
|
":input png file:_files -g '*.png'"
|
||||||
)
|
)
|
||||||
_arguments -s -S : $args
|
_arguments -s -S : $args
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ local args=(
|
|||||||
'(-t --tiny)'{-t,--tiny}'[Enable tiny mode, disabling ROM banking]'
|
'(-t --tiny)'{-t,--tiny}'[Enable tiny mode, disabling ROM banking]'
|
||||||
'(-v --verbose)'{-v,--verbose}'[Enable verbose output]'
|
'(-v --verbose)'{-v,--verbose}'[Enable verbose output]'
|
||||||
'(-w --wramx)'{-w,--wramx}'[Disable WRAM banking]'
|
'(-w --wramx)'{-w,--wramx}'[Disable WRAM banking]'
|
||||||
|
'(-x --nopad)'{-x,--nopad}'[Disable padding the end of the final file]'
|
||||||
|
|
||||||
'(-l --linkerscript)'{-l,--linkerscript}"+[Use a linker script]:linker script:_files -g '*.link'"
|
'(-l --linkerscript)'{-l,--linkerscript}"+[Use a linker script]:linker script:_files -g '*.link'"
|
||||||
'(-m --map)'{-m,--map}"+[Produce a map file]:map file:_files -g '*.map'"
|
'(-m --map)'{-m,--map}"+[Produce a map file]:map file:_files -g '*.map'"
|
||||||
@@ -15,6 +16,7 @@ local args=(
|
|||||||
'(-O --overlay)'{-O,--overlay}'+[Overlay sections over on top of bin file]:base overlay:_files'
|
'(-O --overlay)'{-O,--overlay}'+[Overlay sections over on top of bin file]:base overlay:_files'
|
||||||
'(-o --output)'{-o,--output}"+[Write ROM image to this file]:rom file:_files -g '*.{gb,sgb,gbc}'"
|
'(-o --output)'{-o,--output}"+[Write ROM image to this file]:rom file:_files -g '*.{gb,sgb,gbc}'"
|
||||||
'(-p --pad-value)'{-p,--pad-value}'+[Set padding byte]:padding byte:'
|
'(-p --pad-value)'{-p,--pad-value}'+[Set padding byte]:padding byte:'
|
||||||
|
'(-S --scramble)'{-s,--scramble}'+[Activate scrambling]:scramble spec'
|
||||||
'(-s --smart)'{-s,--smart}'+[!BROKEN! Perform smart linking from this symbol]:symbol name:'
|
'(-s --smart)'{-s,--smart}'+[!BROKEN! Perform smart linking from this symbol]:symbol name:'
|
||||||
|
|
||||||
'*'":object files:_files -g '*.o'"
|
'*'":object files:_files -g '*.o'"
|
||||||
|
|||||||
@@ -1,31 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of RGBDS.
|
|
||||||
*
|
|
||||||
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Contains some assembler-wide defines and externs
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef RGBDS_ASM_ASM_H
|
|
||||||
#define RGBDS_ASM_ASM_H
|
|
||||||
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#include "asm/localasm.h"
|
|
||||||
#include "asm/symbol.h"
|
|
||||||
|
|
||||||
#define MAXMACROARGS 99999
|
|
||||||
#define MAXINCPATHS 128
|
|
||||||
|
|
||||||
extern uint32_t nTotalLines;
|
|
||||||
extern uint32_t nIFDepth;
|
|
||||||
extern struct Section *pCurrentSection;
|
|
||||||
|
|
||||||
#endif /* RGBDS_ASM_ASM_H */
|
|
||||||
@@ -11,12 +11,13 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
struct Charmap *charmap_New(const char *name, const char *baseName);
|
struct Charmap *charmap_New(char const *name, char const *baseName);
|
||||||
void charmap_Delete(struct Charmap *charmap);
|
void charmap_Delete(struct Charmap *charmap);
|
||||||
void charmap_Set(const char *name);
|
void charmap_Set(char const *name);
|
||||||
void charmap_Push(void);
|
void charmap_Push(void);
|
||||||
void charmap_Pop(void);
|
void charmap_Pop(void);
|
||||||
void charmap_Add(char *mapping, uint8_t value);
|
void charmap_Add(char *mapping, uint8_t value);
|
||||||
size_t charmap_Convert(char const *input, uint8_t *output);
|
size_t charmap_Convert(char const *input, uint8_t *output);
|
||||||
|
size_t charmap_ConvertNext(char const **input, uint8_t **output);
|
||||||
|
|
||||||
#endif /* RGBDS_ASM_CHARMAP_H */
|
#endif /* RGBDS_ASM_CHARMAP_H */
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of RGBDS.
|
||||||
|
*
|
||||||
|
* Copyright (c) 1997-2021, Carsten Sorensen and RGBDS contributors.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef RGBDS_ASM_FIXPOINT_H
|
||||||
|
#define RGBDS_ASM_FIXPOINT_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
int32_t fix_Callback_PI(void);
|
||||||
|
void fix_Print(int32_t i);
|
||||||
|
int32_t fix_Sin(int32_t i);
|
||||||
|
int32_t fix_Cos(int32_t i);
|
||||||
|
int32_t fix_Tan(int32_t i);
|
||||||
|
int32_t fix_ASin(int32_t i);
|
||||||
|
int32_t fix_ACos(int32_t i);
|
||||||
|
int32_t fix_ATan(int32_t i);
|
||||||
|
int32_t fix_ATan2(int32_t i, int32_t j);
|
||||||
|
int32_t fix_Mul(int32_t i, int32_t j);
|
||||||
|
int32_t fix_Div(int32_t i, int32_t j);
|
||||||
|
int32_t fix_Pow(int32_t i, int32_t j);
|
||||||
|
int32_t fix_Log(int32_t i, int32_t j);
|
||||||
|
int32_t fix_Round(int32_t i);
|
||||||
|
int32_t fix_Ceil(int32_t i);
|
||||||
|
int32_t fix_Floor(int32_t i);
|
||||||
|
|
||||||
|
#endif /* RGBDS_ASM_FIXPOINT_H */
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of RGBDS.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020, RGBDS contributors.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef RGBDS_FORMAT_SPEC_H
|
||||||
|
#define RGBDS_FORMAT_SPEC_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
enum FormatState {
|
||||||
|
FORMAT_SIGN, // expects '+' or ' ' (optional)
|
||||||
|
FORMAT_PREFIX, // expects '#' (optional)
|
||||||
|
FORMAT_ALIGN, // expects '-' (optional)
|
||||||
|
FORMAT_WIDTH, // expects '0'-'9', max 255 (optional) (leading '0' indicates pad)
|
||||||
|
FORMAT_FRAC, // got '.', expects '0'-'9', max 255 (optional)
|
||||||
|
FORMAT_DONE, // got [duXxbofs] (required)
|
||||||
|
FORMAT_INVALID, // got unexpected character
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FormatSpec {
|
||||||
|
enum FormatState state;
|
||||||
|
int sign;
|
||||||
|
bool prefix;
|
||||||
|
bool alignLeft;
|
||||||
|
bool padZero;
|
||||||
|
size_t width;
|
||||||
|
bool hasFrac;
|
||||||
|
size_t fracWidth;
|
||||||
|
int type;
|
||||||
|
bool valid;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct StrFmtArg {
|
||||||
|
union {
|
||||||
|
uint32_t number;
|
||||||
|
char *string;
|
||||||
|
};
|
||||||
|
bool isNumeric;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define INITIAL_STRFMT_ARG_SIZE 4
|
||||||
|
struct StrFmtArgList {
|
||||||
|
char *format;
|
||||||
|
size_t nbArgs;
|
||||||
|
size_t capacity;
|
||||||
|
struct StrFmtArg *args;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FormatSpec fmt_NewSpec(void);
|
||||||
|
bool fmt_IsEmpty(struct FormatSpec const *fmt);
|
||||||
|
bool fmt_IsValid(struct FormatSpec const *fmt);
|
||||||
|
bool fmt_IsFinished(struct FormatSpec const *fmt);
|
||||||
|
void fmt_UseCharacter(struct FormatSpec *fmt, int c);
|
||||||
|
void fmt_FinishCharacters(struct FormatSpec *fmt);
|
||||||
|
void fmt_PrintString(char *buf, size_t bufLen, struct FormatSpec const *fmt, char const *value);
|
||||||
|
void fmt_PrintNumber(char *buf, size_t bufLen, struct FormatSpec const *fmt, uint32_t value);
|
||||||
|
|
||||||
|
#endif /* RGBDS_FORMAT_SPEC_H */
|
||||||
@@ -13,13 +13,12 @@
|
|||||||
#ifndef RGBDS_ASM_FSTACK_H
|
#ifndef RGBDS_ASM_FSTACK_H
|
||||||
#define RGBDS_ASM_FSTACK_H
|
#define RGBDS_ASM_FSTACK_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#include "asm/asm.h"
|
|
||||||
#include "asm/lexer.h"
|
#include "asm/lexer.h"
|
||||||
|
|
||||||
#include "types.h"
|
|
||||||
|
|
||||||
struct FileStackNode {
|
struct FileStackNode {
|
||||||
struct FileStackNode *parent; /* Pointer to parent node, for error reporting */
|
struct FileStackNode *parent; /* Pointer to parent node, for error reporting */
|
||||||
@@ -49,7 +48,7 @@ struct FileStackNamedNode { /* NODE_FILE, NODE_MACRO */
|
|||||||
char name[]; /* File name for files, file::macro name for macros */
|
char name[]; /* File name for files, file::macro name for macros */
|
||||||
};
|
};
|
||||||
|
|
||||||
extern size_t nMaxRecursionDepth;
|
extern size_t maxRecursionDepth;
|
||||||
|
|
||||||
struct MacroArgs;
|
struct MacroArgs;
|
||||||
|
|
||||||
@@ -72,8 +71,13 @@ bool fstk_FindFile(char const *path, char **fullPath, size_t *size);
|
|||||||
bool yywrap(void);
|
bool yywrap(void);
|
||||||
void fstk_RunInclude(char const *path);
|
void fstk_RunInclude(char const *path);
|
||||||
void fstk_RunMacro(char const *macroName, struct MacroArgs *args);
|
void fstk_RunMacro(char const *macroName, struct MacroArgs *args);
|
||||||
void fstk_RunRept(uint32_t count, int32_t nReptLineNo, char *body, size_t size);
|
void fstk_RunRept(uint32_t count, int32_t reptLineNo, char *body, size_t size);
|
||||||
|
void fstk_RunFor(char const *symName, int32_t start, int32_t stop, int32_t step,
|
||||||
|
int32_t reptLineNo, char *body, size_t size);
|
||||||
|
void fstk_StopRept(void);
|
||||||
|
bool fstk_Break(void);
|
||||||
|
|
||||||
void fstk_Init(char const *mainPath, size_t maxRecursionDepth);
|
void fstk_NewRecursionDepth(size_t newDepth);
|
||||||
|
void fstk_Init(char const *mainPath, size_t maxDepth);
|
||||||
|
|
||||||
#endif /* RGBDS_ASM_FSTACK_H */
|
#endif /* RGBDS_ASM_FSTACK_H */
|
||||||
|
|||||||
+39
-10
@@ -9,6 +9,8 @@
|
|||||||
#ifndef RGBDS_ASM_LEXER_H
|
#ifndef RGBDS_ASM_LEXER_H
|
||||||
#define RGBDS_ASM_LEXER_H
|
#define RGBDS_ASM_LEXER_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
#define MAXSTRLEN 255
|
#define MAXSTRLEN 255
|
||||||
|
|
||||||
struct LexerState;
|
struct LexerState;
|
||||||
@@ -30,24 +32,28 @@ static inline void lexer_SetStateAtEOL(struct LexerState *state)
|
|||||||
lexerStateEOL = state;
|
lexerStateEOL = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
extern char const *binDigits;
|
extern char binDigits[2];
|
||||||
extern char const *gfxDigits;
|
extern char gfxDigits[4];
|
||||||
|
|
||||||
static inline void lexer_SetBinDigits(char const *digits)
|
static inline void lexer_SetBinDigits(char const digits[2])
|
||||||
{
|
{
|
||||||
binDigits = digits;
|
binDigits[0] = digits[0];
|
||||||
|
binDigits[1] = digits[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void lexer_SetGfxDigits(char const *digits)
|
static inline void lexer_SetGfxDigits(char const digits[4])
|
||||||
{
|
{
|
||||||
gfxDigits = digits;
|
gfxDigits[0] = digits[0];
|
||||||
|
gfxDigits[1] = digits[1];
|
||||||
|
gfxDigits[2] = digits[2];
|
||||||
|
gfxDigits[3] = digits[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* `path` is referenced, but not held onto..!
|
* `path` is referenced, but not held onto..!
|
||||||
*/
|
*/
|
||||||
struct LexerState *lexer_OpenFile(char const *path);
|
struct LexerState *lexer_OpenFile(char const *path);
|
||||||
struct LexerState *lexer_OpenFileView(char *buf, size_t size, uint32_t lineNo);
|
struct LexerState *lexer_OpenFileView(char const *path, char *buf, size_t size, uint32_t lineNo);
|
||||||
void lexer_RestartRept(uint32_t lineNo);
|
void lexer_RestartRept(uint32_t lineNo);
|
||||||
void lexer_DeleteState(struct LexerState *state);
|
void lexer_DeleteState(struct LexerState *state);
|
||||||
void lexer_Init(void);
|
void lexer_Init(void);
|
||||||
@@ -56,18 +62,41 @@ enum LexerMode {
|
|||||||
LEXER_NORMAL,
|
LEXER_NORMAL,
|
||||||
LEXER_RAW,
|
LEXER_RAW,
|
||||||
LEXER_SKIP_TO_ELIF,
|
LEXER_SKIP_TO_ELIF,
|
||||||
LEXER_SKIP_TO_ENDC
|
LEXER_SKIP_TO_ENDC,
|
||||||
|
LEXER_SKIP_TO_ENDR
|
||||||
};
|
};
|
||||||
|
|
||||||
void lexer_SetMode(enum LexerMode mode);
|
void lexer_SetMode(enum LexerMode mode);
|
||||||
void lexer_ToggleStringExpansion(bool enable);
|
void lexer_ToggleStringExpansion(bool enable);
|
||||||
|
|
||||||
|
uint32_t lexer_GetIFDepth(void);
|
||||||
|
void lexer_IncIFDepth(void);
|
||||||
|
void lexer_DecIFDepth(void);
|
||||||
|
bool lexer_RanIFBlock(void);
|
||||||
|
bool lexer_ReachedELSEBlock(void);
|
||||||
|
void lexer_RunIFBlock(void);
|
||||||
|
void lexer_ReachELSEBlock(void);
|
||||||
|
|
||||||
|
struct CaptureBody {
|
||||||
|
uint32_t lineNo;
|
||||||
|
char *body;
|
||||||
|
size_t size;
|
||||||
|
};
|
||||||
|
|
||||||
|
void lexer_CheckRecursionDepth(void);
|
||||||
char const *lexer_GetFileName(void);
|
char const *lexer_GetFileName(void);
|
||||||
uint32_t lexer_GetLineNo(void);
|
uint32_t lexer_GetLineNo(void);
|
||||||
uint32_t lexer_GetColNo(void);
|
uint32_t lexer_GetColNo(void);
|
||||||
void lexer_DumpStringExpansions(void);
|
void lexer_DumpStringExpansions(void);
|
||||||
int yylex(void);
|
int yylex(void);
|
||||||
void lexer_CaptureRept(char **capture, size_t *size);
|
bool lexer_CaptureRept(struct CaptureBody *capture);
|
||||||
void lexer_CaptureMacroBody(char **capture, size_t *size);
|
bool lexer_CaptureMacroBody(struct CaptureBody *capture);
|
||||||
|
|
||||||
|
#define INITIAL_DS_ARG_SIZE 2
|
||||||
|
struct DsArgList {
|
||||||
|
size_t nbArgs;
|
||||||
|
size_t capacity;
|
||||||
|
struct Expression *args;
|
||||||
|
};
|
||||||
|
|
||||||
#endif /* RGBDS_ASM_LEXER_H */
|
#endif /* RGBDS_ASM_LEXER_H */
|
||||||
|
|||||||
@@ -1,131 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of RGBDS.
|
|
||||||
*
|
|
||||||
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef RGBDS_ASM_LOCALASM_H
|
|
||||||
#define RGBDS_ASM_LOCALASM_H
|
|
||||||
|
|
||||||
/*
|
|
||||||
* GB Z80 instruction groups
|
|
||||||
*
|
|
||||||
* n3 = 3-bit
|
|
||||||
* n = 8-bit
|
|
||||||
* nn = 16-bit
|
|
||||||
*
|
|
||||||
* ADC A,n : 0xCE
|
|
||||||
* ADC A,r : 0x88|r
|
|
||||||
* ADD A,n : 0xC6
|
|
||||||
* ADD A,r : 0x80|r
|
|
||||||
* ADD HL,ss : 0x09|(ss<<4)
|
|
||||||
* ADD SP,n : 0xE8
|
|
||||||
* AND A,n : 0xE6
|
|
||||||
* AND A,r : 0xA0|r
|
|
||||||
* BIT n3,r : 0xCB 0x40|(n3<<3)|r
|
|
||||||
* CALL cc,nn : 0xC4|(cc<<3)
|
|
||||||
* CALL nn : 0xCD
|
|
||||||
* CCF : 0x3F
|
|
||||||
* CP A,n : 0xFE
|
|
||||||
* CP A,r : 0xB8|r
|
|
||||||
* CPL : 0x2F
|
|
||||||
* DAA : 0x27
|
|
||||||
* DEC r : 0x05|(r<<3)
|
|
||||||
* DEC ss : 0x0B|(ss<<4)
|
|
||||||
* DI : 0xF3
|
|
||||||
* EI : 0xFB
|
|
||||||
* HALT : 0x76
|
|
||||||
* INC r : 0x04|(r<<3)
|
|
||||||
* INC ss : 0x03|(ss<<4)
|
|
||||||
* JP HL : 0xE9
|
|
||||||
* JP cc,nn : 0xC2|(cc<<3)
|
|
||||||
* JP nn : 0xC3|(cc<<3)
|
|
||||||
* JR n : 0x18
|
|
||||||
* JR cc,n : 0x20|(cc<<3)
|
|
||||||
* LD (nn),SP : 0x08
|
|
||||||
* LD ($FF00+C),A : 0xE2
|
|
||||||
* LD ($FF00+n),A : 0xE0
|
|
||||||
* LD (nn),A : 0xEA
|
|
||||||
* LD (rr),A : 0x02|(rr<<4) // HL+ and HL- included
|
|
||||||
* LD A,($FF00+C) : 0xF2
|
|
||||||
* LD A,($FF00+n) : 0xF0
|
|
||||||
* LD A,(nn) : 0xFA
|
|
||||||
* LD A,(rr) : 0x0A|(rr<<4) // HL+ and HL- included
|
|
||||||
* LD HL,SP+n : 0xF8
|
|
||||||
* LD SP,HL : 0xF9
|
|
||||||
* LD r,n : 0x06|(r<<3)
|
|
||||||
* LD r,r' : 0x40|(r<<3)|r' // NOTE: LD (HL),(HL) not allowed
|
|
||||||
* LD ss,nn : 0x01|(ss<<4)
|
|
||||||
* NOP : 0x00
|
|
||||||
* OR A,n : 0xF6
|
|
||||||
* OR A,r : 0xB0|r
|
|
||||||
* POP tt : 0xC1|(tt<<4)
|
|
||||||
* PUSH tt : 0xC5|(tt<<4)
|
|
||||||
* RES n3,r : 0xCB 0x80|(n3<<3)|r
|
|
||||||
* RET : 0xC9
|
|
||||||
* RET cc : 0xC0|(cc<<3)
|
|
||||||
* RETI : 0xD9
|
|
||||||
* RL r : 0xCB 0x10|r
|
|
||||||
* RLA : 0x17
|
|
||||||
* RLC r : 0xCB 0x00|r
|
|
||||||
* RLCA : 0x07
|
|
||||||
* RR r : 0xCB 0x18|r
|
|
||||||
* RRA : 0x1F
|
|
||||||
* RRC r : 0xCB 0x08|r
|
|
||||||
* RRCA : 0x0F
|
|
||||||
* RST n : 0xC7|n
|
|
||||||
* SBC A,n : 0xDE
|
|
||||||
* SBC A,r : 0x98|r
|
|
||||||
* SCF : 0x37
|
|
||||||
* SET n3,r : 0xCB 0xC0|(n8<<3)|r
|
|
||||||
* SLA r : 0xCB 0x20|r
|
|
||||||
* SRA r : 0xCB 0x28|r
|
|
||||||
* SRL r : 0xCB 0x38|r
|
|
||||||
* STOP : 0x10 0x00
|
|
||||||
* SUB A,n : 0xD6
|
|
||||||
* SUB A,r : 0x90|r
|
|
||||||
* SWAP r : 0xCB 0x30|r
|
|
||||||
* XOR A,n : 0xEE
|
|
||||||
* XOR A,r : 0xA8|r
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* "r" defs */
|
|
||||||
enum {
|
|
||||||
REG_B = 0,
|
|
||||||
REG_C,
|
|
||||||
REG_D,
|
|
||||||
REG_E,
|
|
||||||
REG_H,
|
|
||||||
REG_L,
|
|
||||||
REG_HL_IND,
|
|
||||||
REG_A
|
|
||||||
};
|
|
||||||
|
|
||||||
/* "rr" defs */
|
|
||||||
enum {
|
|
||||||
REG_BC_IND = 0,
|
|
||||||
REG_DE_IND,
|
|
||||||
REG_HL_INDINC,
|
|
||||||
REG_HL_INDDEC,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* "ss" defs (SP) and "tt" defs (AF) */
|
|
||||||
enum {
|
|
||||||
REG_BC = 0,
|
|
||||||
REG_DE = 1,
|
|
||||||
REG_HL = 2,
|
|
||||||
REG_SP = 3,
|
|
||||||
REG_AF = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
/* "cc" defs */
|
|
||||||
enum {
|
|
||||||
CC_NZ = 0,
|
|
||||||
CC_Z,
|
|
||||||
CC_NC,
|
|
||||||
CC_C
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif /* RGBDS_ASM_LOCALASM_H */
|
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
#define RGBDS_MACRO_H
|
#define RGBDS_MACRO_H
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "asm/warning.h"
|
#include "asm/warning.h"
|
||||||
@@ -24,6 +25,7 @@ void macro_AppendArg(struct MacroArgs **args, char *s);
|
|||||||
void macro_UseNewArgs(struct MacroArgs *args);
|
void macro_UseNewArgs(struct MacroArgs *args);
|
||||||
void macro_FreeArgs(struct MacroArgs *args);
|
void macro_FreeArgs(struct MacroArgs *args);
|
||||||
char const *macro_GetArg(uint32_t i);
|
char const *macro_GetArg(uint32_t i);
|
||||||
|
char const *macro_GetAllArgs(void);
|
||||||
|
|
||||||
uint32_t macro_GetUniqueID(void);
|
uint32_t macro_GetUniqueID(void);
|
||||||
char const *macro_GetUniqueIDStr(void);
|
char const *macro_GetUniqueIDStr(void);
|
||||||
|
|||||||
+6
-34
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* This file is part of RGBDS.
|
* This file is part of RGBDS.
|
||||||
*
|
*
|
||||||
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
|
* Copyright (c) 1997-2021, Carsten Sorensen and RGBDS contributors.
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
@@ -15,43 +15,15 @@
|
|||||||
|
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
|
|
||||||
struct sOptions {
|
|
||||||
char binary[2];
|
|
||||||
char gbgfx[4];
|
|
||||||
int32_t fillchar;
|
|
||||||
};
|
|
||||||
|
|
||||||
extern char *tzNewMacro;
|
|
||||||
extern uint32_t ulNewMacroSize;
|
|
||||||
extern int32_t nGBGfxID;
|
|
||||||
extern int32_t nBinaryID;
|
|
||||||
|
|
||||||
extern struct sOptions DefaultOptions;
|
|
||||||
extern struct sOptions CurrentOptions;
|
|
||||||
extern bool haltnop;
|
extern bool haltnop;
|
||||||
extern bool optimizeloads;
|
extern bool optimizeLoads;
|
||||||
extern bool verbose;
|
extern bool verbose;
|
||||||
extern bool warnings; /* True to enable warnings, false to disable them. */
|
extern bool warnings; /* True to enable warnings, false to disable them. */
|
||||||
|
|
||||||
extern FILE *dependfile;
|
extern FILE *dependfile;
|
||||||
extern char *tzTargetFileName;
|
extern char *targetFileName;
|
||||||
extern bool oGeneratedMissingIncludes;
|
extern bool generatedMissingIncludes;
|
||||||
extern bool oFailedOnMissingInclude;
|
extern bool failedOnMissingInclude;
|
||||||
extern bool oGeneratePhonyDeps;
|
extern bool generatePhonyDeps;
|
||||||
|
|
||||||
void opt_Push(void);
|
|
||||||
void opt_Pop(void);
|
|
||||||
void opt_Parse(char *s);
|
|
||||||
|
|
||||||
void upperstring(char *s);
|
|
||||||
void lowerstring(char *s);
|
|
||||||
|
|
||||||
/* TODO: are these really needed? */
|
|
||||||
#define YY_FATAL_ERROR fatalerror
|
|
||||||
|
|
||||||
#ifdef YYLMAX
|
|
||||||
#undef YYLMAX
|
|
||||||
#endif
|
|
||||||
#define YYLMAX 65536
|
|
||||||
|
|
||||||
#endif /* RGBDS_MAIN_H */
|
#endif /* RGBDS_MAIN_H */
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of RGBDS.
|
|
||||||
*
|
|
||||||
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef RGBDS_ASM_MATH_H
|
|
||||||
#define RGBDS_ASM_MATH_H
|
|
||||||
|
|
||||||
#include <stdint.h>
|
|
||||||
|
|
||||||
void math_DefinePI(void);
|
|
||||||
void math_Print(int32_t i);
|
|
||||||
int32_t math_Sin(int32_t i);
|
|
||||||
int32_t math_Cos(int32_t i);
|
|
||||||
int32_t math_Tan(int32_t i);
|
|
||||||
int32_t math_ASin(int32_t i);
|
|
||||||
int32_t math_ACos(int32_t i);
|
|
||||||
int32_t math_ATan(int32_t i);
|
|
||||||
int32_t math_ATan2(int32_t i, int32_t j);
|
|
||||||
int32_t math_Mul(int32_t i, int32_t j);
|
|
||||||
int32_t math_Div(int32_t i, int32_t j);
|
|
||||||
int32_t math_Round(int32_t i);
|
|
||||||
int32_t math_Ceil(int32_t i);
|
|
||||||
int32_t math_Floor(int32_t i);
|
|
||||||
|
|
||||||
#endif /* RGBDS_ASM_MATH_H */
|
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of RGBDS.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2021, Eldred Habert and RGBDS contributors.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef RGBDS_OPT_H
|
||||||
|
#define RGBDS_OPT_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
void opt_B(char const chars[2]);
|
||||||
|
void opt_G(char const chars[4]);
|
||||||
|
void opt_P(uint8_t fill);
|
||||||
|
void opt_L(bool optimize);
|
||||||
|
void opt_W(char const *flag);
|
||||||
|
void opt_Parse(char const *option);
|
||||||
|
|
||||||
|
void opt_Push(void);
|
||||||
|
void opt_Pop(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
@@ -14,15 +14,15 @@
|
|||||||
#include "linkdefs.h"
|
#include "linkdefs.h"
|
||||||
|
|
||||||
struct Expression;
|
struct Expression;
|
||||||
|
struct FileStackNode;
|
||||||
|
|
||||||
extern char *tzObjectname;
|
extern char *objectName;
|
||||||
extern struct Section *pSectionList, *pCurrentSection;
|
extern struct Section *sectionList;
|
||||||
|
|
||||||
void out_RegisterNode(struct FileStackNode *node);
|
void out_RegisterNode(struct FileStackNode *node);
|
||||||
void out_ReplaceNode(struct FileStackNode *node);
|
void out_ReplaceNode(struct FileStackNode *node);
|
||||||
void out_SetFileName(char *s);
|
void out_SetFileName(char *s);
|
||||||
void out_CreatePatch(uint32_t type, struct Expression const *expr,
|
void out_CreatePatch(uint32_t type, struct Expression const *expr, uint32_t ofs, uint32_t pcShift);
|
||||||
uint32_t ofs);
|
|
||||||
bool out_CreateAssert(enum AssertionType type, struct Expression const *expr,
|
bool out_CreateAssert(enum AssertionType type, struct Expression const *expr,
|
||||||
char const *message, uint32_t ofs);
|
char const *message, uint32_t ofs);
|
||||||
void out_WriteObject(void);
|
void out_WriteObject(void);
|
||||||
|
|||||||
+17
-16
@@ -17,23 +17,20 @@
|
|||||||
#define MAXRPNLEN 1048576
|
#define MAXRPNLEN 1048576
|
||||||
|
|
||||||
struct Expression {
|
struct Expression {
|
||||||
int32_t nVal; // If the expression's value is known, it's here
|
int32_t val; // If the expression's value is known, it's here
|
||||||
char *reason; // Why the expression is not known, if it isn't
|
char *reason; // Why the expression is not known, if it isn't
|
||||||
bool isKnown; // Whether the expression's value is known
|
bool isKnown; // Whether the expression's value is known
|
||||||
bool isSymbol; // Whether the expression represents a symbol
|
bool isSymbol; // Whether the expression represents a symbol
|
||||||
uint8_t *tRPN; // Array of bytes serializing the RPN expression
|
uint8_t *rpn; // Array of bytes serializing the RPN expression
|
||||||
uint32_t nRPNCapacity; // Size of the `tRPN` buffer
|
uint32_t rpnCapacity; // Size of the `rpn` buffer
|
||||||
uint32_t nRPNLength; // Used size of the `tRPN` buffer
|
uint32_t rpnLength; // Used size of the `rpn` buffer
|
||||||
uint32_t nRPNPatchSize; // Size the expression will take in the obj file
|
uint32_t rpnPatchSize; // Size the expression will take in the object file
|
||||||
};
|
};
|
||||||
|
|
||||||
/* FIXME: Should be defined in `parser.h`, but impossible with POSIX Yacc */
|
|
||||||
extern int32_t nPCOffset;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Determines if an expression is known at assembly time
|
* Determines if an expression is known at assembly time
|
||||||
*/
|
*/
|
||||||
static inline bool rpn_isKnown(const struct Expression *expr)
|
static inline bool rpn_isKnown(struct Expression const *expr)
|
||||||
{
|
{
|
||||||
return expr->isKnown;
|
return expr->isKnown;
|
||||||
}
|
}
|
||||||
@@ -46,11 +43,11 @@ static inline bool rpn_isSymbol(const struct Expression *expr)
|
|||||||
return expr->isSymbol;
|
return expr->isSymbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
void rpn_Symbol(struct Expression *expr, char const *tzSym);
|
void rpn_Symbol(struct Expression *expr, char const *symName);
|
||||||
void rpn_Number(struct Expression *expr, uint32_t i);
|
void rpn_Number(struct Expression *expr, uint32_t i);
|
||||||
void rpn_LOGNOT(struct Expression *expr, const struct Expression *src);
|
void rpn_LOGNOT(struct Expression *expr, const struct Expression *src);
|
||||||
struct Symbol const *rpn_SymbolOf(struct Expression const *expr);
|
struct Symbol const *rpn_SymbolOf(struct Expression const *expr);
|
||||||
bool rpn_IsDiffConstant(struct Expression const *src, struct Symbol const *sym);
|
bool rpn_IsDiffConstant(struct Expression const *src, struct Symbol const *symName);
|
||||||
void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
||||||
const struct Expression *src1,
|
const struct Expression *src1,
|
||||||
const struct Expression *src2);
|
const struct Expression *src2);
|
||||||
@@ -59,11 +56,15 @@ void rpn_LOW(struct Expression *expr, const struct Expression *src);
|
|||||||
void rpn_ISCONST(struct Expression *expr, const struct Expression *src);
|
void rpn_ISCONST(struct Expression *expr, const struct Expression *src);
|
||||||
void rpn_UNNEG(struct Expression *expr, const struct Expression *src);
|
void rpn_UNNEG(struct Expression *expr, const struct Expression *src);
|
||||||
void rpn_UNNOT(struct Expression *expr, const struct Expression *src);
|
void rpn_UNNOT(struct Expression *expr, const struct Expression *src);
|
||||||
void rpn_BankSymbol(struct Expression *expr, char const *tzSym);
|
void rpn_BankSymbol(struct Expression *expr, char const *symName);
|
||||||
void rpn_BankSection(struct Expression *expr, char const *tzSectionName);
|
void rpn_BankSection(struct Expression *expr, char const *sectionName);
|
||||||
void rpn_BankSelf(struct Expression *expr);
|
void rpn_BankSelf(struct Expression *expr);
|
||||||
|
void rpn_SizeOfSection(struct Expression *expr, char const *sectionName);
|
||||||
|
void rpn_StartOfSection(struct Expression *expr, char const *sectionName);
|
||||||
void rpn_Free(struct Expression *expr);
|
void rpn_Free(struct Expression *expr);
|
||||||
void rpn_CheckHRAM(struct Expression *expr, const struct Expression *src);
|
void rpn_CheckHRAM(struct Expression *expr, const struct Expression *src);
|
||||||
void rpn_CheckRST(struct Expression *expr, const struct Expression *src);
|
void rpn_CheckRST(struct Expression *expr, const struct Expression *src);
|
||||||
|
void rpn_CheckNBit(struct Expression const *expr, uint8_t n);
|
||||||
|
int32_t rpn_GetConstVal(struct Expression const *expr);
|
||||||
|
|
||||||
#endif /* RGBDS_ASM_RPN_H */
|
#endif /* RGBDS_ASM_RPN_H */
|
||||||
|
|||||||
+31
-21
@@ -13,6 +13,9 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
|
|
||||||
#include "linkdefs.h"
|
#include "linkdefs.h"
|
||||||
|
#include "platform.h" // NONNULL
|
||||||
|
|
||||||
|
extern uint8_t fillByte;
|
||||||
|
|
||||||
struct Expression;
|
struct Expression;
|
||||||
|
|
||||||
@@ -20,10 +23,12 @@ struct Section {
|
|||||||
char *name;
|
char *name;
|
||||||
enum SectionType type;
|
enum SectionType type;
|
||||||
enum SectionModifier modifier;
|
enum SectionModifier modifier;
|
||||||
|
struct FileStackNode *src; /* Where the section was defined */
|
||||||
|
uint32_t fileLine; /* Line where the section was defined */
|
||||||
uint32_t size;
|
uint32_t size;
|
||||||
uint32_t org;
|
uint32_t org;
|
||||||
uint32_t bank;
|
uint32_t bank;
|
||||||
uint8_t align;
|
uint8_t align; // Exactly as specified in `ALIGN[]`
|
||||||
uint16_t alignOfs;
|
uint16_t alignOfs;
|
||||||
struct Section *next;
|
struct Section *next;
|
||||||
struct Patch *patches;
|
struct Patch *patches;
|
||||||
@@ -36,13 +41,14 @@ struct SectionSpec {
|
|||||||
uint16_t alignOfs;
|
uint16_t alignOfs;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Section *out_FindSectionByName(const char *name);
|
extern struct Section *currentSection;
|
||||||
void out_NewSection(char const *name, uint32_t secttype, uint32_t org,
|
|
||||||
struct SectionSpec const *attributes,
|
struct Section *sect_FindSectionByName(char const *name);
|
||||||
enum SectionModifier mod);
|
void sect_NewSection(char const *name, uint32_t secttype, uint32_t org,
|
||||||
void out_SetLoadSection(char const *name, uint32_t secttype, uint32_t org,
|
struct SectionSpec const *attributes, enum SectionModifier mod);
|
||||||
struct SectionSpec const *attributes);
|
void sect_SetLoadSection(char const *name, uint32_t secttype, uint32_t org,
|
||||||
void out_EndLoadSection(void);
|
struct SectionSpec const *attributes, enum SectionModifier mod);
|
||||||
|
void sect_EndLoadSection(void);
|
||||||
|
|
||||||
struct Section *sect_GetSymbolSection(void);
|
struct Section *sect_GetSymbolSection(void);
|
||||||
uint32_t sect_GetSymbolOffset(void);
|
uint32_t sect_GetSymbolOffset(void);
|
||||||
@@ -54,19 +60,23 @@ void sect_NextUnionMember(void);
|
|||||||
void sect_EndUnion(void);
|
void sect_EndUnion(void);
|
||||||
void sect_CheckUnionClosed(void);
|
void sect_CheckUnionClosed(void);
|
||||||
|
|
||||||
void out_AbsByte(uint8_t b);
|
void sect_AbsByte(uint8_t b);
|
||||||
void out_AbsByteGroup(uint8_t const *s, int32_t length);
|
void sect_AbsByteGroup(uint8_t const *s, size_t length);
|
||||||
void out_Skip(int32_t skip, bool ds);
|
void sect_AbsWordGroup(uint8_t const *s, size_t length);
|
||||||
void out_String(char const *s);
|
void sect_AbsLongGroup(uint8_t const *s, size_t length);
|
||||||
void out_RelByte(struct Expression *expr);
|
void sect_Skip(uint32_t skip, bool ds);
|
||||||
void out_RelBytes(struct Expression *expr, uint32_t n);
|
void sect_String(char const *s);
|
||||||
void out_RelWord(struct Expression *expr);
|
void sect_RelByte(struct Expression *expr, uint32_t pcShift);
|
||||||
void out_RelLong(struct Expression *expr);
|
void sect_RelBytes(uint32_t n, struct Expression *exprs, size_t size);
|
||||||
void out_PCRelByte(struct Expression *expr);
|
void sect_RelWord(struct Expression *expr, uint32_t pcShift);
|
||||||
void out_BinaryFile(char const *s, int32_t startPos);
|
void sect_RelLong(struct Expression *expr, uint32_t pcShift);
|
||||||
void out_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length);
|
void sect_PCRelByte(struct Expression *expr, uint32_t pcShift);
|
||||||
|
void sect_BinaryFile(char const *s, int32_t startPos);
|
||||||
|
void sect_BinaryFileSlice(char const *s, int32_t start_pos, int32_t length);
|
||||||
|
|
||||||
void out_PushSection(void);
|
void sect_PushSection(void);
|
||||||
void out_PopSection(void);
|
void sect_PopSection(void);
|
||||||
|
|
||||||
|
bool sect_IsSizeKnown(struct Section const NONNULL(name));
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+20
-17
@@ -12,18 +12,18 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
#include "asm/section.h"
|
#include "asm/section.h"
|
||||||
|
|
||||||
#include "types.h"
|
#include "platform.h" // MIN_NB_ELMS
|
||||||
|
|
||||||
#define HASHSIZE (1 << 16)
|
#define MAXSYMLEN 255
|
||||||
#define MAXSYMLEN 256
|
|
||||||
|
|
||||||
enum SymbolType {
|
enum SymbolType {
|
||||||
SYM_LABEL,
|
SYM_LABEL,
|
||||||
SYM_EQU,
|
SYM_EQU,
|
||||||
SYM_SET,
|
SYM_VAR,
|
||||||
SYM_MACRO,
|
SYM_MACRO,
|
||||||
SYM_EQUS,
|
SYM_EQUS,
|
||||||
SYM_REF // Forward reference to a label
|
SYM_REF // Forward reference to a label
|
||||||
@@ -43,13 +43,13 @@ struct Symbol {
|
|||||||
/* If sym_IsNumeric */
|
/* If sym_IsNumeric */
|
||||||
int32_t value;
|
int32_t value;
|
||||||
int32_t (*numCallback)(void);
|
int32_t (*numCallback)(void);
|
||||||
/* For SYM_MACRO */
|
/* For SYM_MACRO and SYM_EQUS; TODO: have separate fields */
|
||||||
struct {
|
struct {
|
||||||
size_t macroSize;
|
size_t macroSize;
|
||||||
char *macro;
|
char *macro;
|
||||||
};
|
};
|
||||||
/* For SYM_EQUS, TODO: separate "base" fields from SYM_MACRO */
|
/* For SYM_EQUS */
|
||||||
char const *(*strCallback)(void); /* For SYM_EQUS */
|
char const *(*strCallback)(void);
|
||||||
};
|
};
|
||||||
|
|
||||||
uint32_t ID; /* ID of the symbol in the object file (-1 if none) */
|
uint32_t ID; /* ID of the symbol in the object file (-1 if none) */
|
||||||
@@ -73,15 +73,14 @@ static inline bool sym_IsConstant(struct Symbol const *sym)
|
|||||||
if (sym->type == SYM_LABEL) {
|
if (sym->type == SYM_LABEL) {
|
||||||
struct Section const *sect = sym_GetSection(sym);
|
struct Section const *sect = sym_GetSection(sym);
|
||||||
|
|
||||||
return sect && sect->org != -1;
|
return sect && sect->org != (uint32_t)-1;
|
||||||
}
|
}
|
||||||
return sym->type == SYM_EQU || sym->type == SYM_SET;
|
return sym->type == SYM_EQU || sym->type == SYM_VAR;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool sym_IsNumeric(struct Symbol const *sym)
|
static inline bool sym_IsNumeric(struct Symbol const *sym)
|
||||||
{
|
{
|
||||||
return sym->type == SYM_LABEL || sym->type == SYM_EQU
|
return sym->type == SYM_LABEL || sym->type == SYM_EQU || sym->type == SYM_VAR;
|
||||||
|| sym->type == SYM_SET;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool sym_IsLabel(struct Symbol const *sym)
|
static inline bool sym_IsLabel(struct Symbol const *sym)
|
||||||
@@ -115,30 +114,34 @@ int32_t sym_GetValue(struct Symbol const *sym);
|
|||||||
void sym_SetExportAll(bool set);
|
void sym_SetExportAll(bool set);
|
||||||
struct Symbol *sym_AddLocalLabel(char const *symName);
|
struct Symbol *sym_AddLocalLabel(char const *symName);
|
||||||
struct Symbol *sym_AddLabel(char const *symName);
|
struct Symbol *sym_AddLabel(char const *symName);
|
||||||
|
struct Symbol *sym_AddAnonLabel(void);
|
||||||
|
void sym_WriteAnonLabelName(char buf[MIN_NB_ELMS(MAXSYMLEN + 1)], uint32_t ofs, bool neg);
|
||||||
void sym_Export(char const *symName);
|
void sym_Export(char const *symName);
|
||||||
struct Symbol *sym_AddEqu(char const *symName, int32_t value);
|
struct Symbol *sym_AddEqu(char const *symName, int32_t value);
|
||||||
struct Symbol *sym_AddSet(char const *symName, int32_t value);
|
struct Symbol *sym_RedefEqu(char const *symName, int32_t value);
|
||||||
|
struct Symbol *sym_AddVar(char const *symName, int32_t value);
|
||||||
uint32_t sym_GetPCValue(void);
|
uint32_t sym_GetPCValue(void);
|
||||||
uint32_t sym_GetConstantSymValue(struct Symbol const *sym);
|
uint32_t sym_GetConstantSymValue(struct Symbol const *sym);
|
||||||
uint32_t sym_GetConstantValue(char const *s);
|
uint32_t sym_GetConstantValue(char const *symName);
|
||||||
/*
|
/*
|
||||||
* Find a symbol by exact name, bypassing expansion checks
|
* Find a symbol by exact name, bypassing expansion checks
|
||||||
*/
|
*/
|
||||||
struct Symbol *sym_FindExactSymbol(char const *name);
|
struct Symbol *sym_FindExactSymbol(char const *symName);
|
||||||
/*
|
/*
|
||||||
* Find a symbol by exact name; may not be scoped, produces an error if it is
|
* Find a symbol by exact name; may not be scoped, produces an error if it is
|
||||||
*/
|
*/
|
||||||
struct Symbol *sym_FindUnscopedSymbol(char const *name);
|
struct Symbol *sym_FindUnscopedSymbol(char const *symName);
|
||||||
/*
|
/*
|
||||||
* Find a symbol, possibly scoped, by name
|
* Find a symbol, possibly scoped, by name
|
||||||
*/
|
*/
|
||||||
struct Symbol *sym_FindScopedSymbol(char const *name);
|
struct Symbol *sym_FindScopedSymbol(char const *symName);
|
||||||
struct Symbol const *sym_GetPC(void);
|
struct Symbol const *sym_GetPC(void);
|
||||||
struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char *body, size_t size);
|
struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char *body, size_t size);
|
||||||
struct Symbol *sym_Ref(char const *symName);
|
struct Symbol *sym_Ref(char const *symName);
|
||||||
struct Symbol *sym_AddString(char const *symName, char const *value);
|
struct Symbol *sym_AddString(char const *symName, char const *value);
|
||||||
|
struct Symbol *sym_RedefString(char const *symName, char const *value);
|
||||||
void sym_Purge(char const *symName);
|
void sym_Purge(char const *symName);
|
||||||
void sym_Init(void);
|
void sym_Init(time_t now);
|
||||||
|
|
||||||
/* Functions to save and restore the current symbol scope. */
|
/* Functions to save and restore the current symbol scope. */
|
||||||
char const *sym_GetCurrentSymbolScope(void);
|
char const *sym_GetCurrentSymbolScope(void);
|
||||||
|
|||||||
+5
-2
@@ -11,8 +11,11 @@
|
|||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
uint32_t calchash(const char *s);
|
char const *printChar(int c);
|
||||||
char const *print(int c);
|
|
||||||
|
/*
|
||||||
|
* @return The number of bytes read, or 0 if invalid data was found
|
||||||
|
*/
|
||||||
size_t readUTF8Char(uint8_t *dest, char const *src);
|
size_t readUTF8Char(uint8_t *dest, char const *src);
|
||||||
|
|
||||||
#endif /* RGBDS_UTIL_H */
|
#endif /* RGBDS_UTIL_H */
|
||||||
|
|||||||
+48
-23
@@ -13,40 +13,65 @@
|
|||||||
|
|
||||||
extern unsigned int nbErrors;
|
extern unsigned int nbErrors;
|
||||||
|
|
||||||
|
enum WarningState {
|
||||||
|
WARNING_DEFAULT,
|
||||||
|
WARNING_DISABLED,
|
||||||
|
WARNING_ENABLED,
|
||||||
|
WARNING_ERROR
|
||||||
|
};
|
||||||
|
|
||||||
enum WarningID {
|
enum WarningID {
|
||||||
WARNING_ASSERT, /* Assertions */
|
WARNING_ASSERT, // Assertions
|
||||||
WARNING_BUILTIN_ARG, /* Invalid args to builtins */
|
WARNING_BACKWARDS_FOR, // `for` loop with backwards range
|
||||||
WARNING_CHARMAP_REDEF, /* Charmap entry re-definition */
|
WARNING_BUILTIN_ARG, // Invalid args to builtins
|
||||||
WARNING_DIV, /* Division undefined behavior */
|
WARNING_CHARMAP_REDEF, // Charmap entry re-definition
|
||||||
WARNING_EMPTY_DATA_DIRECTIVE, /* `db`, `dw` or `dl` with no directive in ROM */
|
WARNING_DIV, // Division undefined behavior
|
||||||
WARNING_EMPTY_ENTRY, /* Empty entry in `db`, `dw` or `dl` */
|
WARNING_EMPTY_DATA_DIRECTIVE, // `db`, `dw` or `dl` directive without data in ROM
|
||||||
WARNING_LARGE_CONSTANT, /* Constants too large */
|
WARNING_EMPTY_MACRO_ARG, // Empty macro argument
|
||||||
WARNING_LONG_STR, /* String too long for internal buffers */
|
WARNING_EMPTY_STRRPL, // Empty second argument in `STRRPL`
|
||||||
WARNING_NESTED_COMMENT, /* Comment-start delimeter in a block comment */
|
WARNING_LARGE_CONSTANT, // Constants too large
|
||||||
WARNING_OBSOLETE, /* Obsolete things */
|
WARNING_LONG_STR, // String too long for internal buffers
|
||||||
WARNING_SHIFT, /* Shifting undefined behavior */
|
WARNING_MACRO_SHIFT, // Shift past available arguments in macro
|
||||||
WARNING_SHIFT_AMOUNT, /* Strange shift amount */
|
WARNING_NESTED_COMMENT, // Comment-start delimiter in a block comment
|
||||||
WARNING_TRUNCATION, /* Implicit truncation loses some bits */
|
WARNING_OBSOLETE, // Obsolete things
|
||||||
WARNING_USER, /* User warnings */
|
WARNING_SHIFT, // Shifting undefined behavior
|
||||||
|
WARNING_SHIFT_AMOUNT, // Strange shift amount
|
||||||
|
WARNING_USER, // User warnings
|
||||||
|
|
||||||
NB_WARNINGS,
|
NB_PLAIN_WARNINGS,
|
||||||
|
|
||||||
/* Warnings past this point are "meta" warnings */
|
// Warnings past this point are "parametric" warnings, only mapping to a single flag
|
||||||
WARNING_ALL = NB_WARNINGS,
|
#define PARAM_WARNINGS_START NB_PLAIN_WARNINGS
|
||||||
|
// Treating string as number may lose some bits
|
||||||
|
WARNING_NUMERIC_STRING_1 = PARAM_WARNINGS_START,
|
||||||
|
WARNING_NUMERIC_STRING_2,
|
||||||
|
// Implicit truncation loses some bits
|
||||||
|
WARNING_TRUNCATION_1,
|
||||||
|
WARNING_TRUNCATION_2,
|
||||||
|
|
||||||
|
NB_PLAIN_AND_PARAM_WARNINGS,
|
||||||
|
#define NB_PARAM_WARNINGS (NB_PLAIN_AND_PARAM_WARNINGS - PARAM_WARNINGS_START)
|
||||||
|
|
||||||
|
// Warnings past this point are "meta" warnings
|
||||||
|
#define META_WARNINGS_START NB_PLAIN_AND_PARAM_WARNINGS
|
||||||
|
WARNING_ALL = META_WARNINGS_START,
|
||||||
WARNING_EXTRA,
|
WARNING_EXTRA,
|
||||||
WARNING_EVERYTHING,
|
WARNING_EVERYTHING,
|
||||||
|
|
||||||
NB_WARNINGS_ALL
|
NB_WARNINGS,
|
||||||
#define NB_META_WARNINGS (NB_WARNINGS_ALL - NB_WARNINGS)
|
#define NB_META_WARNINGS (NB_WARNINGS - META_WARNINGS_START)
|
||||||
};
|
};
|
||||||
|
|
||||||
void processWarningFlag(char const *flag);
|
extern enum WarningState warningStates[NB_PLAIN_AND_PARAM_WARNINGS];
|
||||||
|
extern bool warningsAreErrors;
|
||||||
|
|
||||||
|
void processWarningFlag(char *flag);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Used to warn the user about problems that don't prevent the generation of
|
* Used to warn the user about problems that don't prevent the generation of
|
||||||
* valid code.
|
* valid code.
|
||||||
*/
|
*/
|
||||||
void warning(enum WarningID id, const char *fmt, ...);
|
void warning(enum WarningID id, char const *fmt, ...) format_(printf, 2, 3);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Used for errors that compromise the whole assembly process by affecting the
|
* Used for errors that compromise the whole assembly process by affecting the
|
||||||
@@ -55,7 +80,7 @@ void warning(enum WarningID id, const char *fmt, ...);
|
|||||||
* It is also used when the assembler goes into an invalid state (for example,
|
* It is also used when the assembler goes into an invalid state (for example,
|
||||||
* when it fails to allocate memory).
|
* when it fails to allocate memory).
|
||||||
*/
|
*/
|
||||||
_Noreturn void fatalerror(const char *fmt, ...);
|
_Noreturn void fatalerror(char const *fmt, ...) format_(printf, 1, 2);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Used for errors that make it impossible to assemble correctly, but don't
|
* Used for errors that make it impossible to assemble correctly, but don't
|
||||||
@@ -63,6 +88,6 @@ _Noreturn void fatalerror(const char *fmt, ...);
|
|||||||
* get a list of all errors at the end, making it easier to fix all of them at
|
* get a list of all errors at the end, making it easier to fix all of them at
|
||||||
* once.
|
* once.
|
||||||
*/
|
*/
|
||||||
void error(const char *fmt, ...);
|
void error(char const *fmt, ...) format_(printf, 1, 2);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of RGBDS.
|
||||||
|
*
|
||||||
|
* Copyright (c) 1997-2021, RGBDS contributors.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef RGBDS_ERROR_H
|
||||||
|
#define RGBDS_ERROR_H
|
||||||
|
|
||||||
|
#include "helpers.h"
|
||||||
|
#include "platform.h"
|
||||||
|
|
||||||
|
void warn(char const NONNULL(fmt), ...) format_(printf, 1, 2);
|
||||||
|
void warnx(char const NONNULL(fmt), ...) format_(printf, 1, 2);
|
||||||
|
|
||||||
|
_Noreturn void err(char const NONNULL(fmt), ...) format_(printf, 1, 2);
|
||||||
|
_Noreturn void errx(char const NONNULL(fmt), ...) format_(printf, 1, 2);
|
||||||
|
|
||||||
|
#endif /* RGBDS_ERROR_H */
|
||||||
Vendored
-44
@@ -1,44 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of RGBDS.
|
|
||||||
*
|
|
||||||
* Copyright (c) 1997-2018, RGBDS contributors.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef EXTERN_ERR_H
|
|
||||||
#define EXTERN_ERR_H
|
|
||||||
|
|
||||||
#ifdef ERR_IN_LIBC
|
|
||||||
|
|
||||||
#include <err.h>
|
|
||||||
|
|
||||||
#else /* ERR_IN_LIBC */
|
|
||||||
|
|
||||||
#include <stdarg.h>
|
|
||||||
|
|
||||||
#include "helpers.h"
|
|
||||||
|
|
||||||
#define warn rgbds_warn
|
|
||||||
#define vwarn rgbds_vwarn
|
|
||||||
#define warnx rgbds_warnx
|
|
||||||
#define vwarnx rgbds_vwarnx
|
|
||||||
|
|
||||||
#define err rgbds_err
|
|
||||||
#define verr rgbds_verr
|
|
||||||
#define errx rgbds_errx
|
|
||||||
#define verrx rgbds_verrx
|
|
||||||
|
|
||||||
void warn(const char *fmt, ...) format_(printf, 1, 2);
|
|
||||||
void vwarn(const char *fmt, va_list ap) format_(printf, 1, 0);
|
|
||||||
void warnx(const char *fmt, ...) format_(printf, 1, 2);
|
|
||||||
void vwarnx(const char *fmt, va_list ap) format_(printf, 1, 0);
|
|
||||||
|
|
||||||
_Noreturn void err(int status, const char *fmt, ...) format_(printf, 2, 3);
|
|
||||||
_Noreturn void verr(int status, const char *fmt, va_list ap) format_(printf, 2, 0);
|
|
||||||
_Noreturn void errx(int status, const char *fmt, ...) format_(printf, 2, 3);
|
|
||||||
_Noreturn void verrx(int status, const char *fmt, va_list ap) format_(printf, 2, 0);
|
|
||||||
|
|
||||||
#endif /* ERR_IN_LIBC */
|
|
||||||
|
|
||||||
#endif /* EXTERN_ERR_H */
|
|
||||||
Vendored
+5
-4
@@ -26,17 +26,18 @@
|
|||||||
#ifndef RGBDS_EXTERN_GETOPT_H
|
#ifndef RGBDS_EXTERN_GETOPT_H
|
||||||
#define RGBDS_EXTERN_GETOPT_H
|
#define RGBDS_EXTERN_GETOPT_H
|
||||||
|
|
||||||
extern char *optarg;
|
extern char *musl_optarg;
|
||||||
extern int optind, opterr, optopt, optreset;
|
extern int musl_optind, musl_opterr, musl_optopt, musl_optreset;
|
||||||
|
|
||||||
struct option {
|
struct option {
|
||||||
const char *name;
|
char const *name;
|
||||||
int has_arg;
|
int has_arg;
|
||||||
int *flag;
|
int *flag;
|
||||||
int val;
|
int val;
|
||||||
};
|
};
|
||||||
|
|
||||||
int musl_getopt_long_only(int, char **, const char *, const struct option *, int *);
|
int musl_getopt_long_only(int argc, char **argv, char const *optstring,
|
||||||
|
const struct option *longopts, int *idx);
|
||||||
|
|
||||||
#define no_argument 0
|
#define no_argument 0
|
||||||
#define required_argument 1
|
#define required_argument 1
|
||||||
|
|||||||
+3
-3
@@ -13,7 +13,7 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
#include "extern/err.h"
|
#include "error.h"
|
||||||
|
|
||||||
struct Options {
|
struct Options {
|
||||||
bool debug;
|
bool debug;
|
||||||
@@ -24,7 +24,7 @@ struct Options {
|
|||||||
bool mirror;
|
bool mirror;
|
||||||
bool unique;
|
bool unique;
|
||||||
bool colorcurve;
|
bool colorcurve;
|
||||||
int trim;
|
unsigned int trim;
|
||||||
char *tilemapfile;
|
char *tilemapfile;
|
||||||
bool tilemapout;
|
bool tilemapout;
|
||||||
char *attrmapfile;
|
char *attrmapfile;
|
||||||
@@ -43,7 +43,7 @@ struct RGBColor {
|
|||||||
|
|
||||||
struct ImageOptions {
|
struct ImageOptions {
|
||||||
bool horizontal;
|
bool horizontal;
|
||||||
int trim;
|
unsigned int trim;
|
||||||
char *tilemapfile;
|
char *tilemapfile;
|
||||||
bool tilemapout;
|
bool tilemapout;
|
||||||
char *attrmapfile;
|
char *attrmapfile;
|
||||||
|
|||||||
+10
-12
@@ -29,19 +29,9 @@ typedef struct HashMapEntry *HashMap[HASHMAP_NB_BUCKETS];
|
|||||||
* @param map The HashMap to add the element to
|
* @param map The HashMap to add the element to
|
||||||
* @param key The key with which the element will be stored and retrieved
|
* @param key The key with which the element will be stored and retrieved
|
||||||
* @param element The element to add
|
* @param element The element to add
|
||||||
* @return True if a collision occurred (for statistics)
|
* @return A pointer to the pointer to the element.
|
||||||
*/
|
*/
|
||||||
bool hash_AddElement(HashMap map, char const *key, void *element);
|
void **hash_AddElement(HashMap map, char const *key, void *element);
|
||||||
|
|
||||||
/**
|
|
||||||
* Replaces an element with an already-present key in a hashmap.
|
|
||||||
* @warning Inserting a NULL will make `hash_GetElement`'s return ambiguous!
|
|
||||||
* @param map The HashMap to replace the element in
|
|
||||||
* @param key The key with which the element will be stored and retrieved
|
|
||||||
* @param element The element to replace
|
|
||||||
* @return True if the element was found and replaced
|
|
||||||
*/
|
|
||||||
bool hash_ReplaceElement(HashMap const map, char const *key, void *element);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes an element from a hashmap.
|
* Removes an element from a hashmap.
|
||||||
@@ -51,6 +41,14 @@ bool hash_ReplaceElement(HashMap const map, char const *key, void *element);
|
|||||||
*/
|
*/
|
||||||
bool hash_RemoveElement(HashMap map, char const *key);
|
bool hash_RemoveElement(HashMap map, char const *key);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Finds an element in a hashmap, and returns a pointer to its value field.
|
||||||
|
* @param map The map to consider the elements of
|
||||||
|
* @param key The key to search an element for
|
||||||
|
* @return A pointer to the pointer to the element, or NULL if not found.
|
||||||
|
*/
|
||||||
|
void **hash_GetNode(HashMap const map, char const *key);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds an element in a hashmap.
|
* Finds an element in a hashmap.
|
||||||
* @param map The map to consider the elements of
|
* @param map The map to consider the elements of
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#ifdef __GNUC__ // GCC or compatible
|
#ifdef __GNUC__ // GCC or compatible
|
||||||
#define format_(archetype, str_index, first_arg) \
|
#define format_(archetype, str_index, first_arg) \
|
||||||
__attribute__ ((format (archetype, str_index, first_arg)))
|
__attribute__ ((format (archetype, str_index, first_arg)))
|
||||||
|
#define attr_(...) __attribute__ ((__VA_ARGS__))
|
||||||
// In release builds, define "unreachable" as such, but trap in debug builds
|
// In release builds, define "unreachable" as such, but trap in debug builds
|
||||||
#ifdef NDEBUG
|
#ifdef NDEBUG
|
||||||
#define unreachable_ __builtin_unreachable
|
#define unreachable_ __builtin_unreachable
|
||||||
@@ -27,13 +28,69 @@
|
|||||||
#else
|
#else
|
||||||
// Unsupported, but no need to throw a fit
|
// Unsupported, but no need to throw a fit
|
||||||
#define format_(archetype, str_index, first_arg)
|
#define format_(archetype, str_index, first_arg)
|
||||||
|
#define attr_(...)
|
||||||
// This seems to generate similar code to __builtin_unreachable, despite different semantics
|
// This seems to generate similar code to __builtin_unreachable, despite different semantics
|
||||||
// Note that executing this is undefined behavior (declared _Noreturn, but does return)
|
// Note that executing this is undefined behavior (declared _Noreturn, but does return)
|
||||||
static inline _Noreturn unreachable_(void) {}
|
static inline _Noreturn unreachable_(void) {}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Use builtins whenever possible, and shim them otherwise
|
||||||
|
#ifdef __GNUC__ // GCC or compatible
|
||||||
|
#define ctz __builtin_ctz
|
||||||
|
#define clz __builtin_clz
|
||||||
|
|
||||||
|
#elif defined(_MSC_VER)
|
||||||
|
#include <assert.h>
|
||||||
|
#include <intrin.h>
|
||||||
|
#pragma intrinsic(_BitScanReverse, _BitScanForward)
|
||||||
|
static inline int ctz(unsigned int x)
|
||||||
|
{
|
||||||
|
unsigned long cnt;
|
||||||
|
|
||||||
|
assert(x != 0);
|
||||||
|
_BitScanForward(&cnt, x);
|
||||||
|
return cnt;
|
||||||
|
}
|
||||||
|
static inline int clz(unsigned int x)
|
||||||
|
{
|
||||||
|
unsigned long cnt;
|
||||||
|
|
||||||
|
assert(x != 0);
|
||||||
|
_BitScanReverse(&cnt, x);
|
||||||
|
return 31 - cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
#include <limits.h>
|
||||||
|
static inline int ctz(unsigned int x)
|
||||||
|
{
|
||||||
|
int cnt = 0;
|
||||||
|
|
||||||
|
while (!(x & 1)) {
|
||||||
|
x >>= 1;
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
return cnt;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int clz(unsigned int x)
|
||||||
|
{
|
||||||
|
int cnt = 0;
|
||||||
|
|
||||||
|
while (x <= UINT_MAX / 2) {
|
||||||
|
x <<= 1;
|
||||||
|
cnt++;
|
||||||
|
}
|
||||||
|
return cnt;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Macros for stringification
|
// Macros for stringification
|
||||||
#define STR(x) #x
|
#define STR(x) #x
|
||||||
#define EXPAND_AND_STR(x) STR(x)
|
#define EXPAND_AND_STR(x) STR(x)
|
||||||
|
|
||||||
|
// Obtaining the size of an array; `arr` must be an expression, not a type!
|
||||||
|
// (Having two instances of `arr` is OK because the contents of `sizeof` are not evaluated.)
|
||||||
|
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof *(arr))
|
||||||
|
|
||||||
#endif /* HELPERS_H */
|
#endif /* HELPERS_H */
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ extern char const *symFileName;
|
|||||||
extern char const *overlayFileName;
|
extern char const *overlayFileName;
|
||||||
extern char const *outputFileName;
|
extern char const *outputFileName;
|
||||||
extern uint8_t padValue;
|
extern uint8_t padValue;
|
||||||
|
extern uint16_t scrambleROMX;
|
||||||
|
extern uint8_t scrambleWRAMX;
|
||||||
|
extern uint8_t scrambleSRAM;
|
||||||
extern bool is32kMode;
|
extern bool is32kMode;
|
||||||
extern bool beVerbose;
|
extern bool beVerbose;
|
||||||
extern bool isWRA0Mode;
|
extern bool isWRA0Mode;
|
||||||
|
|||||||
@@ -21,10 +21,7 @@ struct Assertion {
|
|||||||
struct Patch patch;
|
struct Patch patch;
|
||||||
// enum AssertionType type; The `patch`'s field is instead re-used
|
// enum AssertionType type; The `patch`'s field is instead re-used
|
||||||
char *message;
|
char *message;
|
||||||
/*
|
// This would be redundant with `.section->fileSymbols`... but `section` is sometimes NULL!
|
||||||
* This would be redundant with `.section->fileSymbols`... but
|
|
||||||
* `section` is sometimes NULL!
|
|
||||||
*/
|
|
||||||
struct Symbol **fileSymbols;
|
struct Symbol **fileSymbols;
|
||||||
|
|
||||||
struct Assertion *next;
|
struct Assertion *next;
|
||||||
|
|||||||
@@ -30,11 +30,11 @@ struct AttachedSymbol {
|
|||||||
struct Patch {
|
struct Patch {
|
||||||
struct FileStackNode const *src;
|
struct FileStackNode const *src;
|
||||||
uint32_t lineNo;
|
uint32_t lineNo;
|
||||||
int32_t offset;
|
uint32_t offset;
|
||||||
uint32_t pcSectionID;
|
uint32_t pcSectionID;
|
||||||
uint32_t pcOffset;
|
uint32_t pcOffset;
|
||||||
enum PatchType type;
|
enum PatchType type;
|
||||||
int32_t rpnSize;
|
uint32_t rpnSize;
|
||||||
uint8_t *rpnExpression;
|
uint8_t *rpnExpression;
|
||||||
|
|
||||||
struct Section const *pcSection;
|
struct Section const *pcSection;
|
||||||
|
|||||||
+33
-29
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
#define RGBDS_OBJECT_VERSION_STRING "RGB%1u"
|
#define RGBDS_OBJECT_VERSION_STRING "RGB%1u"
|
||||||
#define RGBDS_OBJECT_VERSION_NUMBER 9U
|
#define RGBDS_OBJECT_VERSION_NUMBER 9U
|
||||||
#define RGBDS_OBJECT_REV 6U
|
#define RGBDS_OBJECT_REV 9U
|
||||||
|
|
||||||
enum AssertionType {
|
enum AssertionType {
|
||||||
ASSERT_WARN,
|
ASSERT_WARN,
|
||||||
@@ -23,41 +23,45 @@ enum AssertionType {
|
|||||||
};
|
};
|
||||||
|
|
||||||
enum RPNCommand {
|
enum RPNCommand {
|
||||||
RPN_ADD = 0x00,
|
RPN_ADD = 0x00,
|
||||||
RPN_SUB = 0x01,
|
RPN_SUB = 0x01,
|
||||||
RPN_MUL = 0x02,
|
RPN_MUL = 0x02,
|
||||||
RPN_DIV = 0x03,
|
RPN_DIV = 0x03,
|
||||||
RPN_MOD = 0x04,
|
RPN_MOD = 0x04,
|
||||||
RPN_UNSUB = 0x05,
|
RPN_UNSUB = 0x05, // FIXME: should be renamed to "NEG" for consistency
|
||||||
|
RPN_EXP = 0x06,
|
||||||
|
|
||||||
RPN_OR = 0x10,
|
RPN_OR = 0x10,
|
||||||
RPN_AND = 0x11,
|
RPN_AND = 0x11,
|
||||||
RPN_XOR = 0x12,
|
RPN_XOR = 0x12,
|
||||||
RPN_UNNOT = 0x13,
|
RPN_UNNOT = 0x13, // FIXME: should be renamed to "NOT" for consistency
|
||||||
|
|
||||||
RPN_LOGAND = 0x21,
|
RPN_LOGAND = 0x21,
|
||||||
RPN_LOGOR = 0x22,
|
RPN_LOGOR = 0x22,
|
||||||
RPN_LOGUNNOT = 0x23,
|
RPN_LOGUNNOT = 0x23, // FIXME: should be renamed to "LOGNOT" for consistency
|
||||||
|
|
||||||
RPN_LOGEQ = 0x30,
|
RPN_LOGEQ = 0x30,
|
||||||
RPN_LOGNE = 0x31,
|
RPN_LOGNE = 0x31,
|
||||||
RPN_LOGGT = 0x32,
|
RPN_LOGGT = 0x32,
|
||||||
RPN_LOGLT = 0x33,
|
RPN_LOGLT = 0x33,
|
||||||
RPN_LOGGE = 0x34,
|
RPN_LOGGE = 0x34,
|
||||||
RPN_LOGLE = 0x35,
|
RPN_LOGLE = 0x35,
|
||||||
|
|
||||||
RPN_SHL = 0x40,
|
RPN_SHL = 0x40,
|
||||||
RPN_SHR = 0x41,
|
RPN_SHR = 0x41,
|
||||||
|
RPN_USHR = 0x42,
|
||||||
|
|
||||||
RPN_BANK_SYM = 0x50,
|
RPN_BANK_SYM = 0x50,
|
||||||
RPN_BANK_SECT = 0x51,
|
RPN_BANK_SECT = 0x51,
|
||||||
RPN_BANK_SELF = 0x52,
|
RPN_BANK_SELF = 0x52,
|
||||||
|
RPN_SIZEOF_SECT = 0x53,
|
||||||
|
RPN_STARTOF_SECT = 0x54,
|
||||||
|
|
||||||
RPN_HRAM = 0x60,
|
RPN_HRAM = 0x60,
|
||||||
RPN_RST = 0x61,
|
RPN_RST = 0x61,
|
||||||
|
|
||||||
RPN_CONST = 0x80,
|
RPN_CONST = 0x80,
|
||||||
RPN_SYM = 0x81
|
RPN_SYM = 0x81
|
||||||
};
|
};
|
||||||
|
|
||||||
enum SectionType {
|
enum SectionType {
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of RGBDS.
|
||||||
|
*
|
||||||
|
* Copyright (c) 1997-2021, RGBDS contributors.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef RGBDS_OP_MATH_H
|
||||||
|
#define RGBDS_OP_MATH_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
int32_t op_divide(int32_t dividend, int32_t divisor);
|
||||||
|
int32_t op_modulo(int32_t dividend, int32_t divisor);
|
||||||
|
int32_t op_exponent(int32_t base, uint32_t power);
|
||||||
|
int32_t op_shift_left(int32_t value, int32_t amount);
|
||||||
|
int32_t op_shift_right(int32_t value, int32_t amount);
|
||||||
|
int32_t op_shift_right_unsigned(int32_t value, int32_t amount);
|
||||||
|
|
||||||
|
#endif /* RGBDS_OP_MATH_H */
|
||||||
+35
-1
@@ -11,9 +11,10 @@
|
|||||||
#ifndef RGBDS_PLATFORM_H
|
#ifndef RGBDS_PLATFORM_H
|
||||||
#define RGBDS_PLATFORM_H
|
#define RGBDS_PLATFORM_H
|
||||||
|
|
||||||
/* MSVC doesn't have strncasecmp, use a suitable replacement */
|
// MSVC doesn't have str(n)casecmp, use a suitable replacement
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
# include <string.h>
|
# include <string.h>
|
||||||
|
# define strcasecmp _stricmp
|
||||||
# define strncasecmp _strnicmp
|
# define strncasecmp _strnicmp
|
||||||
#else
|
#else
|
||||||
# include <strings.h>
|
# include <strings.h>
|
||||||
@@ -34,9 +35,42 @@
|
|||||||
|
|
||||||
/* MSVC doesn't use POSIX types or defines for `read` */
|
/* MSVC doesn't use POSIX types or defines for `read` */
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
|
# include <io.h>
|
||||||
# define STDIN_FILENO 0
|
# define STDIN_FILENO 0
|
||||||
|
# define STDOUT_FILENO 1
|
||||||
|
# define STDERR_FILENO 2
|
||||||
# define ssize_t int
|
# define ssize_t int
|
||||||
# define SSIZE_MAX INT_MAX
|
# define SSIZE_MAX INT_MAX
|
||||||
|
#else
|
||||||
|
# include <fcntl.h>
|
||||||
|
# include <unistd.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* MSVC doesn't support `[static N]` for array arguments from C99 */
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# define MIN_NB_ELMS(N)
|
||||||
|
# define NONNULL(ptr) *ptr
|
||||||
|
#else
|
||||||
|
# define MIN_NB_ELMS(N) static (N)
|
||||||
|
# define NONNULL(ptr) ptr[static 1]
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// MSVC uses a different name for O_RDWR, and needs an additional _O_BINARY flag
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# include <fcntl.h>
|
||||||
|
# define O_RDWR _O_RDWR
|
||||||
|
# define S_ISREG(field) ((field) & _S_IFREG)
|
||||||
|
# define O_BINARY _O_BINARY
|
||||||
|
#elif !defined(O_BINARY) // Cross-compilers define O_BINARY
|
||||||
|
# define O_BINARY 0 // POSIX says we shouldn't care!
|
||||||
|
#endif // _MSC_VER
|
||||||
|
|
||||||
|
// Windows has stdin and stdout open as text by default, which we may not want
|
||||||
|
#if defined(_MSC_VER) || defined(__MINGW32__)
|
||||||
|
# include <io.h>
|
||||||
|
# define setmode(fd, mode) _setmode(fd, mode)
|
||||||
|
#else
|
||||||
|
# define setmode(fd, mode) ((void)0)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif /* RGBDS_PLATFORM_H */
|
#endif /* RGBDS_PLATFORM_H */
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of RGBDS.
|
|
||||||
*
|
|
||||||
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef RGBDS_TYPES_H
|
|
||||||
#define RGBDS_TYPES_H
|
|
||||||
|
|
||||||
#ifndef _MAX_PATH
|
|
||||||
#define _MAX_PATH 512
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* RGBDS_TYPES_H */
|
|
||||||
+5
-5
@@ -1,7 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* This file is part of RGBDS.
|
* This file is part of RGBDS.
|
||||||
*
|
*
|
||||||
* Copyright (c) 2017-2018, Antonio Nino Diaz and RGBDS contributors.
|
* Copyright (c) 2017-2021, Antonio Nino Diaz and RGBDS contributors.
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
@@ -9,10 +9,10 @@
|
|||||||
#ifndef EXTERN_VERSION_H
|
#ifndef EXTERN_VERSION_H
|
||||||
#define EXTERN_VERSION_H
|
#define EXTERN_VERSION_H
|
||||||
|
|
||||||
#define PACKAGE_VERSION_MAJOR (0)
|
#define PACKAGE_VERSION_MAJOR 0
|
||||||
#define PACKAGE_VERSION_MINOR (4)
|
#define PACKAGE_VERSION_MINOR 5
|
||||||
#define PACKAGE_VERSION_PATCH (2)
|
#define PACKAGE_VERSION_PATCH 2
|
||||||
|
|
||||||
const char *get_package_version_string(void);
|
char const *get_package_version_string(void);
|
||||||
|
|
||||||
#endif /* EXTERN_VERSION_H */
|
#endif /* EXTERN_VERSION_H */
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
# Generated by CMake
|
||||||
|
/.version.c
|
||||||
+26
-6
@@ -6,35 +6,53 @@
|
|||||||
# SPDX-License-Identifier: MIT
|
# SPDX-License-Identifier: MIT
|
||||||
#
|
#
|
||||||
|
|
||||||
|
configure_file(version.c _version.c ESCAPE_QUOTES)
|
||||||
|
|
||||||
set(common_src
|
set(common_src
|
||||||
"extern/err.c"
|
"error.c"
|
||||||
"extern/getopt.c"
|
"extern/getopt.c"
|
||||||
"version.c"
|
"_version.c"
|
||||||
)
|
)
|
||||||
|
|
||||||
find_package(BISON REQUIRED)
|
|
||||||
find_package(PkgConfig)
|
find_package(PkgConfig)
|
||||||
|
if(MSVC OR NOT PKG_CONFIG_FOUND)
|
||||||
if(NOT PKG_CONFIG_FOUND)
|
|
||||||
# fallback to find_package
|
# fallback to find_package
|
||||||
find_package(PNG REQUIRED)
|
find_package(PNG REQUIRED)
|
||||||
else()
|
else()
|
||||||
pkg_check_modules(LIBPNG REQUIRED libpng)
|
pkg_check_modules(LIBPNG REQUIRED libpng)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
find_package(BISON REQUIRED)
|
||||||
|
set(BISON_FLAGS "-Wall")
|
||||||
|
# Set sompe optimization flags on versions that support them
|
||||||
|
if(BISON_VERSION VERSION_GREATER_EQUAL "3.5")
|
||||||
|
set(BISON_FLAGS "${BISON_FLAGS} -Dapi.token.raw=true")
|
||||||
|
endif()
|
||||||
|
if(BISON_VERSION VERSION_GREATER_EQUAL "3.6")
|
||||||
|
set(BISON_FLAGS "${BISON_FLAGS} -Dparse.error=detailed")
|
||||||
|
elseif(BISON_VERSION VERSION_GREATER_EQUAL "3.0")
|
||||||
|
set(BISON_FLAGS "${BISON_FLAGS} -Dparse.error=verbose")
|
||||||
|
endif()
|
||||||
|
if(BISON_VERSION VERSION_GREATER_EQUAL "3.0")
|
||||||
|
set(BISON_FLAGS "${BISON_FLAGS} -Dparse.lac=full")
|
||||||
|
set(BISON_FLAGS "${BISON_FLAGS} -Dlr.type=ielr")
|
||||||
|
endif()
|
||||||
BISON_TARGET(PARSER "asm/parser.y"
|
BISON_TARGET(PARSER "asm/parser.y"
|
||||||
"${PROJECT_SOURCE_DIR}/src/asm/parser.c"
|
"${PROJECT_SOURCE_DIR}/src/asm/parser.c"
|
||||||
|
COMPILE_FLAGS "${BISON_FLAGS}"
|
||||||
DEFINES_FILE "${PROJECT_SOURCE_DIR}/src/asm/parser.h"
|
DEFINES_FILE "${PROJECT_SOURCE_DIR}/src/asm/parser.h"
|
||||||
)
|
)
|
||||||
|
|
||||||
set(rgbasm_src
|
set(rgbasm_src
|
||||||
"${BISON_PARSER_OUTPUT_SOURCE}"
|
"${BISON_PARSER_OUTPUT_SOURCE}"
|
||||||
"asm/charmap.c"
|
"asm/charmap.c"
|
||||||
|
"asm/fixpoint.c"
|
||||||
|
"asm/format.c"
|
||||||
"asm/fstack.c"
|
"asm/fstack.c"
|
||||||
"asm/lexer.c"
|
"asm/lexer.c"
|
||||||
"asm/macro.c"
|
"asm/macro.c"
|
||||||
"asm/main.c"
|
"asm/main.c"
|
||||||
"asm/math.c"
|
"asm/opt.c"
|
||||||
"asm/output.c"
|
"asm/output.c"
|
||||||
"asm/rpn.c"
|
"asm/rpn.c"
|
||||||
"asm/section.c"
|
"asm/section.c"
|
||||||
@@ -44,6 +62,7 @@ set(rgbasm_src
|
|||||||
"extern/utf8decoder.c"
|
"extern/utf8decoder.c"
|
||||||
"hashmap.c"
|
"hashmap.c"
|
||||||
"linkdefs.c"
|
"linkdefs.c"
|
||||||
|
"opmath.c"
|
||||||
)
|
)
|
||||||
|
|
||||||
set(rgbfix_src
|
set(rgbfix_src
|
||||||
@@ -67,6 +86,7 @@ set(rgblink_src
|
|||||||
"link/symbol.c"
|
"link/symbol.c"
|
||||||
"hashmap.c"
|
"hashmap.c"
|
||||||
"linkdefs.c"
|
"linkdefs.c"
|
||||||
|
"opmath.c"
|
||||||
)
|
)
|
||||||
|
|
||||||
foreach(PROG "asm" "fix" "gfx" "link")
|
foreach(PROG "asm" "fix" "gfx" "link")
|
||||||
|
|||||||
+73
-52
@@ -13,7 +13,6 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "asm/asm.h"
|
|
||||||
#include "asm/charmap.h"
|
#include "asm/charmap.h"
|
||||||
#include "asm/main.h"
|
#include "asm/main.h"
|
||||||
#include "asm/output.h"
|
#include "asm/output.h"
|
||||||
@@ -45,38 +44,41 @@ struct Charmap {
|
|||||||
|
|
||||||
static HashMap charmaps;
|
static HashMap charmaps;
|
||||||
|
|
||||||
static struct Charmap *currentCharmap;
|
/*
|
||||||
|
* Store pointers to hashmap nodes, so that there is only one pointer to the memory block
|
||||||
|
* that gets reallocated.
|
||||||
|
*/
|
||||||
|
static struct Charmap **currentCharmap;
|
||||||
|
|
||||||
struct CharmapStackEntry {
|
struct CharmapStackEntry {
|
||||||
struct Charmap *charmap;
|
struct Charmap **charmap;
|
||||||
struct CharmapStackEntry *next;
|
struct CharmapStackEntry *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CharmapStackEntry *charmapStack;
|
struct CharmapStackEntry *charmapStack;
|
||||||
|
|
||||||
static inline struct Charmap *charmap_Get(const char *name)
|
static struct Charmap *charmap_Get(char const *name)
|
||||||
{
|
{
|
||||||
return hash_GetElement(charmaps, name);
|
return hash_GetElement(charmaps, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline struct Charmap *resizeCharmap(struct Charmap *map, size_t capacity)
|
static void resizeCharmap(struct Charmap **map, size_t capacity)
|
||||||
{
|
{
|
||||||
struct Charmap *new = realloc(map, sizeof(*map) + sizeof(*map->nodes) * capacity);
|
*map = realloc(*map, sizeof(**map) + sizeof(*(*map)->nodes) * capacity);
|
||||||
|
|
||||||
if (!new)
|
if (!*map)
|
||||||
fatalerror("Failed to %s charmap: %s\n",
|
fatalerror("Failed to %s charmap: %s\n",
|
||||||
map ? "create" : "resize", strerror(errno));
|
*map ? "create" : "resize", strerror(errno));
|
||||||
new->capacity = capacity;
|
(**map).capacity = capacity;
|
||||||
return new;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void initNode(struct Charnode *node)
|
static void initNode(struct Charnode *node)
|
||||||
{
|
{
|
||||||
node->isTerminal = false;
|
node->isTerminal = false;
|
||||||
memset(node->next, 0, sizeof(node->next));
|
memset(node->next, 0, sizeof(node->next));
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Charmap *charmap_New(const char *name, const char *baseName)
|
struct Charmap *charmap_New(char const *name, char const *baseName)
|
||||||
{
|
{
|
||||||
struct Charmap *base = NULL;
|
struct Charmap *base = NULL;
|
||||||
|
|
||||||
@@ -96,19 +98,18 @@ struct Charmap *charmap_New(const char *name, const char *baseName)
|
|||||||
|
|
||||||
/* Init the new charmap's fields */
|
/* Init the new charmap's fields */
|
||||||
if (base) {
|
if (base) {
|
||||||
charmap = resizeCharmap(NULL, base->capacity);
|
resizeCharmap(&charmap, base->capacity);
|
||||||
charmap->usedNodes = base->usedNodes;
|
charmap->usedNodes = base->usedNodes;
|
||||||
|
|
||||||
memcpy(charmap->nodes, base->nodes, sizeof(base->nodes[0]) * charmap->usedNodes);
|
memcpy(charmap->nodes, base->nodes, sizeof(base->nodes[0]) * charmap->usedNodes);
|
||||||
} else {
|
} else {
|
||||||
charmap = resizeCharmap(NULL, INITIAL_CAPACITY);
|
resizeCharmap(&charmap, INITIAL_CAPACITY);
|
||||||
charmap->usedNodes = 1;
|
charmap->usedNodes = 1;
|
||||||
initNode(&charmap->nodes[0]); /* Init the root node */
|
initNode(&charmap->nodes[0]); /* Init the root node */
|
||||||
}
|
}
|
||||||
charmap->name = strdup(name);
|
charmap->name = strdup(name);
|
||||||
|
|
||||||
hash_AddElement(charmaps, charmap->name, charmap);
|
currentCharmap = (struct Charmap **)hash_AddElement(charmaps, charmap->name, charmap);
|
||||||
currentCharmap = charmap;
|
|
||||||
|
|
||||||
return charmap;
|
return charmap;
|
||||||
}
|
}
|
||||||
@@ -119,9 +120,9 @@ void charmap_Delete(struct Charmap *charmap)
|
|||||||
free(charmap);
|
free(charmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
void charmap_Set(const char *name)
|
void charmap_Set(char const *name)
|
||||||
{
|
{
|
||||||
struct Charmap *charmap = charmap_Get(name);
|
struct Charmap **charmap = (struct Charmap **)hash_GetNode(charmaps, name);
|
||||||
|
|
||||||
if (charmap == NULL)
|
if (charmap == NULL)
|
||||||
error("Charmap '%s' doesn't exist\n", name);
|
error("Charmap '%s' doesn't exist\n", name);
|
||||||
@@ -159,37 +160,48 @@ void charmap_Pop(void)
|
|||||||
|
|
||||||
void charmap_Add(char *mapping, uint8_t value)
|
void charmap_Add(char *mapping, uint8_t value)
|
||||||
{
|
{
|
||||||
struct Charnode *node = ¤tCharmap->nodes[0];
|
struct Charmap *charmap = *currentCharmap;
|
||||||
|
struct Charnode *node = &charmap->nodes[0];
|
||||||
|
|
||||||
for (uint8_t c; *mapping; mapping++) {
|
for (uint8_t c; *mapping; mapping++) {
|
||||||
c = *mapping - 1;
|
c = *mapping - 1;
|
||||||
|
|
||||||
if (node->next[c]) {
|
if (node->next[c]) {
|
||||||
node = ¤tCharmap->nodes[node->next[c]];
|
node = &charmap->nodes[node->next[c]];
|
||||||
} else {
|
} else {
|
||||||
/* Register next available node */
|
/* Register next available node */
|
||||||
node->next[c] = currentCharmap->usedNodes;
|
node->next[c] = charmap->usedNodes;
|
||||||
/* If no more nodes are available, get new ones */
|
/* If no more nodes are available, get new ones */
|
||||||
if (currentCharmap->usedNodes == currentCharmap->capacity) {
|
if (charmap->usedNodes == charmap->capacity) {
|
||||||
currentCharmap->capacity *= 2;
|
charmap->capacity *= 2;
|
||||||
currentCharmap = resizeCharmap(currentCharmap, currentCharmap->capacity);
|
resizeCharmap(currentCharmap, charmap->capacity);
|
||||||
hash_ReplaceElement(charmaps, currentCharmap->name, currentCharmap);
|
charmap = *currentCharmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Switch to and init new node */
|
/* Switch to and init new node */
|
||||||
node = ¤tCharmap->nodes[currentCharmap->usedNodes++];
|
node = &charmap->nodes[charmap->usedNodes++];
|
||||||
initNode(node);
|
initNode(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (node->isTerminal)
|
if (node->isTerminal)
|
||||||
warning(WARNING_CHARMAP_REDEF, "Overriding charmap mapping");
|
warning(WARNING_CHARMAP_REDEF, "Overriding charmap mapping\n");
|
||||||
|
|
||||||
node->isTerminal = true;
|
node->isTerminal = true;
|
||||||
node->value = value;
|
node->value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t charmap_Convert(char const *input, uint8_t *output)
|
size_t charmap_Convert(char const *input, uint8_t *output)
|
||||||
|
{
|
||||||
|
uint8_t *start = output;
|
||||||
|
|
||||||
|
while (charmap_ConvertNext(&input, &output))
|
||||||
|
;
|
||||||
|
|
||||||
|
return output - start;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t charmap_ConvertNext(char const **input, uint8_t **output)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
* The goal is to match the longest mapping possible.
|
* The goal is to match the longest mapping possible.
|
||||||
@@ -197,47 +209,56 @@ size_t charmap_Convert(char const *input, uint8_t *output)
|
|||||||
* If that would lead to a dead end, rewind characters until the last match, and output.
|
* If that would lead to a dead end, rewind characters until the last match, and output.
|
||||||
* If no match, read a UTF-8 codepoint and output that.
|
* If no match, read a UTF-8 codepoint and output that.
|
||||||
*/
|
*/
|
||||||
size_t outputLen = 0;
|
struct Charmap const *charmap = *currentCharmap;
|
||||||
struct Charnode const *node = ¤tCharmap->nodes[0];
|
struct Charnode const *node = &charmap->nodes[0];
|
||||||
struct Charnode const *match = NULL;
|
struct Charnode const *match = NULL;
|
||||||
size_t rewindDistance = 0;
|
size_t rewindDistance = 0;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
/* We still want NULs to reach the `else` path, to give a chance to rewind */
|
uint8_t c = **input - 1;
|
||||||
uint8_t c = *input - 1;
|
|
||||||
|
|
||||||
if (*input && node->next[c]) {
|
if (**input && node->next[c]) {
|
||||||
input++; /* Consume that char */
|
// Consume that char
|
||||||
|
(*input)++;
|
||||||
rewindDistance++;
|
rewindDistance++;
|
||||||
|
|
||||||
node = ¤tCharmap->nodes[node->next[c]];
|
// Advance to next node (index starts at 1)
|
||||||
|
node = &charmap->nodes[node->next[c]];
|
||||||
if (node->isTerminal) {
|
if (node->isTerminal) {
|
||||||
|
// This node matches, register it
|
||||||
match = node;
|
match = node;
|
||||||
rewindDistance = 0; /* Rewind from after the match */
|
rewindDistance = 0; // If no longer match is found, rewind here
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
input -= rewindDistance; /* Rewind */
|
// We are at a dead end (either because we reached the end of input, or of
|
||||||
rewindDistance = 0;
|
// the trie), so rewind up to the last match, and output.
|
||||||
node = ¤tCharmap->nodes[0];
|
*input -= rewindDistance; // This will rewind all the way if no match found
|
||||||
|
|
||||||
if (match) { /* Arrived at a dead end with a match found */
|
if (match) { // A match was found, use it
|
||||||
*output++ = match->value;
|
if (output)
|
||||||
outputLen++;
|
*(*output)++ = match->value;
|
||||||
match = NULL; /* Reset match for next round */
|
|
||||||
|
|
||||||
} else if (*input) { /* No match found */
|
return 1;
|
||||||
size_t codepointLen = readUTF8Char(output, input);
|
|
||||||
|
|
||||||
input += codepointLen; /* OK because UTF-8 has no NUL in multi-byte chars */
|
} else if (**input) { // No match found, but there is some input left
|
||||||
output += codepointLen;
|
// This will write the codepoint's value to `output`, little-endian
|
||||||
outputLen += codepointLen;
|
size_t codepointLen = readUTF8Char(output ? *output : NULL,
|
||||||
|
*input);
|
||||||
|
|
||||||
|
if (codepointLen == 0)
|
||||||
|
error("Input string is not valid UTF-8!\n");
|
||||||
|
|
||||||
|
// OK because UTF-8 has no NUL in multi-byte chars
|
||||||
|
*input += codepointLen;
|
||||||
|
if (output)
|
||||||
|
*output += codepointLen;
|
||||||
|
|
||||||
|
return codepointLen;
|
||||||
|
|
||||||
|
} else { // End of input
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!*input)
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return outputLen;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,170 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of RGBDS.
|
||||||
|
*
|
||||||
|
* Copyright (c) 1997-2021, Carsten Sorensen and RGBDS contributors.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fixed-point math routines
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include "asm/fixpoint.h"
|
||||||
|
#include "asm/symbol.h"
|
||||||
|
#include "asm/warning.h"
|
||||||
|
|
||||||
|
#define fix2double(i) ((double)((i) / 65536.0))
|
||||||
|
#define double2fix(d) ((int32_t)round((d) * 65536.0))
|
||||||
|
|
||||||
|
// pi radians == 32768 fixed-point "degrees"
|
||||||
|
#define fdeg2rad(f) ((f) * (M_PI / 32768.0))
|
||||||
|
#define rad2fdeg(r) ((r) * (32768.0 / M_PI))
|
||||||
|
|
||||||
|
#ifndef M_PI
|
||||||
|
#define M_PI 3.14159265358979323846
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Return the _PI symbol value
|
||||||
|
*/
|
||||||
|
int32_t fix_Callback_PI(void)
|
||||||
|
{
|
||||||
|
warning(WARNING_OBSOLETE, "`_PI` is deprecated; use 3.14159\n");
|
||||||
|
|
||||||
|
return double2fix(M_PI);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Print a fixed point value
|
||||||
|
*/
|
||||||
|
void fix_Print(int32_t i)
|
||||||
|
{
|
||||||
|
uint32_t u = i;
|
||||||
|
char const *sign = "";
|
||||||
|
|
||||||
|
if (i < 0) {
|
||||||
|
u = -u;
|
||||||
|
sign = "-";
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("%s%" PRIu32 ".%05" PRIu32, sign, u >> 16,
|
||||||
|
((uint32_t)(fix2double(u) * 100000 + 0.5)) % 100000);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate sine
|
||||||
|
*/
|
||||||
|
int32_t fix_Sin(int32_t i)
|
||||||
|
{
|
||||||
|
return double2fix(sin(fdeg2rad(fix2double(i))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate cosine
|
||||||
|
*/
|
||||||
|
int32_t fix_Cos(int32_t i)
|
||||||
|
{
|
||||||
|
return double2fix(cos(fdeg2rad(fix2double(i))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate tangent
|
||||||
|
*/
|
||||||
|
int32_t fix_Tan(int32_t i)
|
||||||
|
{
|
||||||
|
return double2fix(tan(fdeg2rad(fix2double(i))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate arcsine
|
||||||
|
*/
|
||||||
|
int32_t fix_ASin(int32_t i)
|
||||||
|
{
|
||||||
|
return double2fix(rad2fdeg(asin(fix2double(i))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate arccosine
|
||||||
|
*/
|
||||||
|
int32_t fix_ACos(int32_t i)
|
||||||
|
{
|
||||||
|
return double2fix(rad2fdeg(acos(fix2double(i))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate arctangent
|
||||||
|
*/
|
||||||
|
int32_t fix_ATan(int32_t i)
|
||||||
|
{
|
||||||
|
return double2fix(rad2fdeg(atan(fix2double(i))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Calculate atan2
|
||||||
|
*/
|
||||||
|
int32_t fix_ATan2(int32_t i, int32_t j)
|
||||||
|
{
|
||||||
|
return double2fix(rad2fdeg(atan2(fix2double(i), fix2double(j))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Multiplication
|
||||||
|
*/
|
||||||
|
int32_t fix_Mul(int32_t i, int32_t j)
|
||||||
|
{
|
||||||
|
return double2fix(fix2double(i) * fix2double(j));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Division
|
||||||
|
*/
|
||||||
|
int32_t fix_Div(int32_t i, int32_t j)
|
||||||
|
{
|
||||||
|
return double2fix(fix2double(i) / fix2double(j));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Power
|
||||||
|
*/
|
||||||
|
int32_t fix_Pow(int32_t i, int32_t j)
|
||||||
|
{
|
||||||
|
return double2fix(pow(fix2double(i), fix2double(j)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Logarithm
|
||||||
|
*/
|
||||||
|
int32_t fix_Log(int32_t i, int32_t j)
|
||||||
|
{
|
||||||
|
return double2fix(log(fix2double(i)) / log(fix2double(j)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Round
|
||||||
|
*/
|
||||||
|
int32_t fix_Round(int32_t i)
|
||||||
|
{
|
||||||
|
return double2fix(round(fix2double(i)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ceil
|
||||||
|
*/
|
||||||
|
int32_t fix_Ceil(int32_t i)
|
||||||
|
{
|
||||||
|
return double2fix(ceil(fix2double(i)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Floor
|
||||||
|
*/
|
||||||
|
int32_t fix_Floor(int32_t i)
|
||||||
|
{
|
||||||
|
return double2fix(floor(fix2double(i)));
|
||||||
|
}
|
||||||
@@ -0,0 +1,296 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of RGBDS.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2020, RGBDS contributors.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "asm/format.h"
|
||||||
|
#include "asm/warning.h"
|
||||||
|
|
||||||
|
struct FormatSpec fmt_NewSpec(void)
|
||||||
|
{
|
||||||
|
struct FormatSpec fmt = {0};
|
||||||
|
|
||||||
|
return fmt;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fmt_IsEmpty(struct FormatSpec const *fmt)
|
||||||
|
{
|
||||||
|
return !fmt->state;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fmt_IsValid(struct FormatSpec const *fmt)
|
||||||
|
{
|
||||||
|
return fmt->valid || fmt->state == FORMAT_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fmt_IsFinished(struct FormatSpec const *fmt)
|
||||||
|
{
|
||||||
|
return fmt->state >= FORMAT_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fmt_UseCharacter(struct FormatSpec *fmt, int c)
|
||||||
|
{
|
||||||
|
if (fmt->state == FORMAT_INVALID)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
/* sign */
|
||||||
|
case ' ':
|
||||||
|
case '+':
|
||||||
|
if (fmt->state > FORMAT_SIGN)
|
||||||
|
goto invalid;
|
||||||
|
fmt->state = FORMAT_PREFIX;
|
||||||
|
fmt->sign = c;
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* prefix */
|
||||||
|
case '#':
|
||||||
|
if (fmt->state > FORMAT_PREFIX)
|
||||||
|
goto invalid;
|
||||||
|
fmt->state = FORMAT_ALIGN;
|
||||||
|
fmt->prefix = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* align */
|
||||||
|
case '-':
|
||||||
|
if (fmt->state > FORMAT_ALIGN)
|
||||||
|
goto invalid;
|
||||||
|
fmt->state = FORMAT_WIDTH;
|
||||||
|
fmt->alignLeft = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* pad and width */
|
||||||
|
case '0':
|
||||||
|
if (fmt->state < FORMAT_WIDTH)
|
||||||
|
fmt->padZero = true;
|
||||||
|
/* fallthrough */
|
||||||
|
case '1':
|
||||||
|
case '2':
|
||||||
|
case '3':
|
||||||
|
case '4':
|
||||||
|
case '5':
|
||||||
|
case '6':
|
||||||
|
case '7':
|
||||||
|
case '8':
|
||||||
|
case '9':
|
||||||
|
if (fmt->state < FORMAT_WIDTH) {
|
||||||
|
fmt->state = FORMAT_WIDTH;
|
||||||
|
fmt->width = c - '0';
|
||||||
|
} else if (fmt->state == FORMAT_WIDTH) {
|
||||||
|
fmt->width = fmt->width * 10 + (c - '0');
|
||||||
|
} else if (fmt->state == FORMAT_FRAC) {
|
||||||
|
fmt->fracWidth = fmt->fracWidth * 10 + (c - '0');
|
||||||
|
} else {
|
||||||
|
goto invalid;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '.':
|
||||||
|
if (fmt->state > FORMAT_WIDTH)
|
||||||
|
goto invalid;
|
||||||
|
fmt->state = FORMAT_FRAC;
|
||||||
|
fmt->hasFrac = true;
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* type */
|
||||||
|
case 'd':
|
||||||
|
case 'u':
|
||||||
|
case 'X':
|
||||||
|
case 'x':
|
||||||
|
case 'b':
|
||||||
|
case 'o':
|
||||||
|
case 'f':
|
||||||
|
case 's':
|
||||||
|
if (fmt->state >= FORMAT_DONE)
|
||||||
|
goto invalid;
|
||||||
|
fmt->state = FORMAT_DONE;
|
||||||
|
fmt->valid = true;
|
||||||
|
fmt->type = c;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
invalid:
|
||||||
|
fmt->state = FORMAT_INVALID;
|
||||||
|
fmt->valid = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fmt_FinishCharacters(struct FormatSpec *fmt)
|
||||||
|
{
|
||||||
|
if (!fmt_IsValid(fmt))
|
||||||
|
fmt->state = FORMAT_INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fmt_PrintString(char *buf, size_t bufLen, struct FormatSpec const *fmt, char const *value)
|
||||||
|
{
|
||||||
|
if (fmt->sign)
|
||||||
|
error("Formatting string with sign flag '%c'\n", fmt->sign);
|
||||||
|
if (fmt->prefix)
|
||||||
|
error("Formatting string with prefix flag '#'\n");
|
||||||
|
if (fmt->padZero)
|
||||||
|
error("Formatting string with padding flag '0'\n");
|
||||||
|
if (fmt->hasFrac)
|
||||||
|
error("Formatting string with fractional width\n");
|
||||||
|
if (fmt->type != 's')
|
||||||
|
error("Formatting string as type '%c'\n", fmt->type);
|
||||||
|
|
||||||
|
size_t len = strlen(value);
|
||||||
|
size_t totalLen = fmt->width > len ? fmt->width : len;
|
||||||
|
|
||||||
|
if (totalLen > bufLen - 1) { /* bufLen includes terminator */
|
||||||
|
error("Formatted string value too long\n");
|
||||||
|
totalLen = bufLen - 1;
|
||||||
|
if (len > totalLen)
|
||||||
|
len = totalLen;
|
||||||
|
}
|
||||||
|
assert(len < bufLen && totalLen < bufLen && len <= totalLen);
|
||||||
|
|
||||||
|
size_t padLen = totalLen - len;
|
||||||
|
|
||||||
|
if (fmt->alignLeft) {
|
||||||
|
memcpy(buf, value, len);
|
||||||
|
for (size_t i = len; i < totalLen; i++)
|
||||||
|
buf[i] = ' ';
|
||||||
|
} else {
|
||||||
|
for (size_t i = 0; i < padLen; i++)
|
||||||
|
buf[i] = ' ';
|
||||||
|
memcpy(buf + padLen, value, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[totalLen] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
void fmt_PrintNumber(char *buf, size_t bufLen, struct FormatSpec const *fmt, uint32_t value)
|
||||||
|
{
|
||||||
|
if (fmt->type != 'X' && fmt->type != 'x' && fmt->type != 'b' && fmt->type != 'o'
|
||||||
|
&& fmt->prefix)
|
||||||
|
error("Formatting type '%c' with prefix flag '#'\n", fmt->type);
|
||||||
|
if (fmt->type != 'f' && fmt->hasFrac)
|
||||||
|
error("Formatting type '%c' with fractional width\n", fmt->type);
|
||||||
|
if (fmt->type == 's')
|
||||||
|
error("Formatting number as type 's'\n");
|
||||||
|
|
||||||
|
char sign = fmt->sign; /* 0 or ' ' or '+' */
|
||||||
|
|
||||||
|
if (fmt->type == 'd' || fmt->type == 'f') {
|
||||||
|
int32_t v = value;
|
||||||
|
|
||||||
|
if (v < 0 && v != INT32_MIN) {
|
||||||
|
sign = '-';
|
||||||
|
value = -v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char prefix = !fmt->prefix ? 0
|
||||||
|
: fmt->type == 'X' ? '$'
|
||||||
|
: fmt->type == 'x' ? '$'
|
||||||
|
: fmt->type == 'b' ? '%'
|
||||||
|
: fmt->type == 'o' ? '&'
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
char valueBuf[262]; /* Max 5 digits + decimal + 255 fraction digits + terminator */
|
||||||
|
|
||||||
|
if (fmt->type == 'b') {
|
||||||
|
/* Special case for binary */
|
||||||
|
char *ptr = valueBuf;
|
||||||
|
|
||||||
|
do {
|
||||||
|
*ptr++ = (value & 1) + '0';
|
||||||
|
value >>= 1;
|
||||||
|
} while (value);
|
||||||
|
|
||||||
|
*ptr = '\0';
|
||||||
|
|
||||||
|
/* Reverse the digits */
|
||||||
|
size_t valueLen = ptr - valueBuf;
|
||||||
|
|
||||||
|
for (size_t i = 0, j = valueLen - 1; i < j; i++, j--) {
|
||||||
|
char c = valueBuf[i];
|
||||||
|
|
||||||
|
valueBuf[i] = valueBuf[j];
|
||||||
|
valueBuf[j] = c;
|
||||||
|
}
|
||||||
|
} else if (fmt->type == 'f') {
|
||||||
|
/* Special case for fixed-point */
|
||||||
|
|
||||||
|
/* Default fractional width (C's is 6 for "%f"; here 5 is enough) */
|
||||||
|
size_t fracWidth = fmt->hasFrac ? fmt->fracWidth : 5;
|
||||||
|
|
||||||
|
if (fracWidth > 255) {
|
||||||
|
error("Fractional width %zu too long, limiting to 255\n",
|
||||||
|
fracWidth);
|
||||||
|
fracWidth = 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
snprintf(valueBuf, sizeof(valueBuf), "%.*f", (int)fracWidth, value / 65536.0);
|
||||||
|
} else {
|
||||||
|
char const *spec = fmt->type == 'd' ? "%" PRId32
|
||||||
|
: fmt->type == 'u' ? "%" PRIu32
|
||||||
|
: fmt->type == 'X' ? "%" PRIX32
|
||||||
|
: fmt->type == 'x' ? "%" PRIx32
|
||||||
|
: fmt->type == 'o' ? "%" PRIo32
|
||||||
|
: "%" PRId32;
|
||||||
|
|
||||||
|
snprintf(valueBuf, sizeof(valueBuf), spec, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t len = strlen(valueBuf);
|
||||||
|
size_t numLen = !!sign + !!prefix + len;
|
||||||
|
size_t totalLen = fmt->width > numLen ? fmt->width : numLen;
|
||||||
|
|
||||||
|
if (totalLen > bufLen - 1) { /* bufLen includes terminator */
|
||||||
|
error("Formatted numeric value too long\n");
|
||||||
|
totalLen = bufLen - 1;
|
||||||
|
if (numLen > totalLen) {
|
||||||
|
len -= numLen - totalLen;
|
||||||
|
numLen = totalLen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(numLen < bufLen && totalLen < bufLen && numLen <= totalLen && len <= numLen);
|
||||||
|
|
||||||
|
size_t padLen = totalLen - numLen;
|
||||||
|
size_t pos = 0;
|
||||||
|
|
||||||
|
if (fmt->alignLeft) {
|
||||||
|
if (sign)
|
||||||
|
buf[pos++] = sign;
|
||||||
|
if (prefix)
|
||||||
|
buf[pos++] = prefix;
|
||||||
|
memcpy(buf + pos, valueBuf, len);
|
||||||
|
for (size_t i = pos + len; i < totalLen; i++)
|
||||||
|
buf[i] = ' ';
|
||||||
|
} else {
|
||||||
|
if (fmt->padZero) {
|
||||||
|
/* sign, then prefix, then zero padding */
|
||||||
|
if (sign)
|
||||||
|
buf[pos++] = sign;
|
||||||
|
if (prefix)
|
||||||
|
buf[pos++] = prefix;
|
||||||
|
for (size_t i = 0; i < padLen; i++)
|
||||||
|
buf[pos++] = '0';
|
||||||
|
} else {
|
||||||
|
/* space padding, then sign, then prefix */
|
||||||
|
for (size_t i = 0; i < padLen; i++)
|
||||||
|
buf[pos++] = ' ';
|
||||||
|
if (sign)
|
||||||
|
buf[pos++] = sign;
|
||||||
|
if (prefix)
|
||||||
|
buf[pos++] = prefix;
|
||||||
|
}
|
||||||
|
memcpy(buf + pos, valueBuf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
buf[totalLen] = '\0';
|
||||||
|
}
|
||||||
+142
-47
@@ -21,11 +21,7 @@
|
|||||||
#include "asm/warning.h"
|
#include "asm/warning.h"
|
||||||
#include "platform.h" /* S_ISDIR (stat macro) */
|
#include "platform.h" /* S_ISDIR (stat macro) */
|
||||||
|
|
||||||
#ifdef LEXER_DEBUG
|
#define MAXINCPATHS 128
|
||||||
#define dbgPrint(...) fprintf(stderr, "[lexer] " __VA_ARGS__)
|
|
||||||
#else
|
|
||||||
#define dbgPrint(...)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
struct Context {
|
struct Context {
|
||||||
struct Context *parent;
|
struct Context *parent;
|
||||||
@@ -34,17 +30,20 @@ struct Context {
|
|||||||
uint32_t uniqueID;
|
uint32_t uniqueID;
|
||||||
struct MacroArgs *macroArgs; /* Macro args are *saved* here */
|
struct MacroArgs *macroArgs; /* Macro args are *saved* here */
|
||||||
uint32_t nbReptIters;
|
uint32_t nbReptIters;
|
||||||
|
int32_t forValue;
|
||||||
|
int32_t forStep;
|
||||||
|
char *forName;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct Context *contextStack;
|
static struct Context *contextStack;
|
||||||
static size_t contextDepth = 0;
|
static size_t contextDepth = 0;
|
||||||
#define DEFAULT_MAX_DEPTH 64
|
#define DEFAULT_MAX_DEPTH 64
|
||||||
size_t nMaxRecursionDepth;
|
size_t maxRecursionDepth;
|
||||||
|
|
||||||
static unsigned int nbIncPaths = 0;
|
static unsigned int nbIncPaths = 0;
|
||||||
static char const *includePaths[MAXINCPATHS];
|
static char const *includePaths[MAXINCPATHS];
|
||||||
|
|
||||||
char const *dumpNodeAndParents(struct FileStackNode const *node)
|
static const char *dumpNodeAndParents(struct FileStackNode const *node)
|
||||||
{
|
{
|
||||||
char const *name;
|
char const *name;
|
||||||
|
|
||||||
@@ -85,6 +84,9 @@ void fstk_DumpCurrent(void)
|
|||||||
|
|
||||||
struct FileStackNode *fstk_GetFileStack(void)
|
struct FileStackNode *fstk_GetFileStack(void)
|
||||||
{
|
{
|
||||||
|
if (!contextStack)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
struct FileStackNode *node = contextStack->fileInfo;
|
struct FileStackNode *node = contextStack->fileInfo;
|
||||||
|
|
||||||
/* Mark node and all of its parents as referenced if not already so they don't get freed */
|
/* Mark node and all of its parents as referenced if not already so they don't get freed */
|
||||||
@@ -135,8 +137,8 @@ void fstk_AddIncludePath(char const *path)
|
|||||||
static void printDep(char const *path)
|
static void printDep(char const *path)
|
||||||
{
|
{
|
||||||
if (dependfile) {
|
if (dependfile) {
|
||||||
fprintf(dependfile, "%s: %s\n", tzTargetFileName, path);
|
fprintf(dependfile, "%s: %s\n", targetFileName, path);
|
||||||
if (oGeneratePhonyDeps)
|
if (generatePhonyDeps)
|
||||||
fprintf(dependfile, "%s:\n", path);
|
fprintf(dependfile, "%s:\n", path);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -167,8 +169,14 @@ bool fstk_FindFile(char const *path, char **fullPath, size_t *size)
|
|||||||
char const *incPath = i ? includePaths[i - 1] : "";
|
char const *incPath = i ? includePaths[i - 1] : "";
|
||||||
int len = snprintf(*fullPath, *size, "%s%s", incPath, path);
|
int len = snprintf(*fullPath, *size, "%s%s", incPath, path);
|
||||||
|
|
||||||
|
if (len < 0) {
|
||||||
|
error("snprintf error during include path search: %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
/* Oh how I wish `asnprintf` was standard... */
|
/* Oh how I wish `asnprintf` was standard... */
|
||||||
if (len >= *size) { /* `len` doesn't include the terminator, `size` does */
|
if ((size_t)len >= *size) { /* `len` doesn't include the terminator, `size` does */
|
||||||
*size = len + 1;
|
*size = len + 1;
|
||||||
*fullPath = realloc(*fullPath, *size);
|
*fullPath = realloc(*fullPath, *size);
|
||||||
if (!*fullPath) {
|
if (!*fullPath) {
|
||||||
@@ -177,12 +185,14 @@ bool fstk_FindFile(char const *path, char **fullPath, size_t *size)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
len = sprintf(*fullPath, "%s%s", incPath, path);
|
len = sprintf(*fullPath, "%s%s", incPath, path);
|
||||||
|
if (len < 0) {
|
||||||
|
error("sprintf error during include path search: %s\n",
|
||||||
|
strerror(errno));
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (len < 0) {
|
if (isPathValid(*fullPath)) {
|
||||||
error("snprintf error during include path search: %s\n",
|
|
||||||
strerror(errno));
|
|
||||||
} else if (isPathValid(*fullPath)) {
|
|
||||||
printDep(*fullPath);
|
printDep(*fullPath);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -190,13 +200,19 @@ bool fstk_FindFile(char const *path, char **fullPath, size_t *size)
|
|||||||
}
|
}
|
||||||
|
|
||||||
errno = ENOENT;
|
errno = ENOENT;
|
||||||
if (oGeneratedMissingIncludes)
|
if (generatedMissingIncludes)
|
||||||
printDep(path);
|
printDep(path);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool yywrap(void)
|
bool yywrap(void)
|
||||||
{
|
{
|
||||||
|
uint32_t ifDepth = lexer_GetIFDepth();
|
||||||
|
|
||||||
|
if (ifDepth != 0)
|
||||||
|
fatalerror("Ended block with %" PRIu32 " unterminated IF construct%s\n",
|
||||||
|
ifDepth, ifDepth == 1 ? "" : "s");
|
||||||
|
|
||||||
if (contextStack->fileInfo->type == NODE_REPT) { /* The context is a REPT block, which may loop */
|
if (contextStack->fileInfo->type == NODE_REPT) { /* The context is a REPT block, which may loop */
|
||||||
struct FileStackReptNode *fileInfo = (struct FileStackReptNode *)contextStack->fileInfo;
|
struct FileStackReptNode *fileInfo = (struct FileStackReptNode *)contextStack->fileInfo;
|
||||||
|
|
||||||
@@ -216,6 +232,17 @@ bool yywrap(void)
|
|||||||
contextStack->fileInfo = (struct FileStackNode *)fileInfo;
|
contextStack->fileInfo = (struct FileStackNode *)fileInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* If this is a FOR, update the symbol value */
|
||||||
|
if (contextStack->forName && fileInfo->iters[0] <= contextStack->nbReptIters) {
|
||||||
|
contextStack->forValue += contextStack->forStep;
|
||||||
|
struct Symbol *sym = sym_AddVar(contextStack->forName,
|
||||||
|
contextStack->forValue);
|
||||||
|
|
||||||
|
/* This error message will refer to the current iteration */
|
||||||
|
if (sym->type != SYM_VAR)
|
||||||
|
fatalerror("Failed to update FOR symbol value\n");
|
||||||
|
}
|
||||||
|
/* Advance to the next iteration */
|
||||||
fileInfo->iters[0]++;
|
fileInfo->iters[0]++;
|
||||||
/* If this wasn't the last iteration, wrap instead of popping */
|
/* If this wasn't the last iteration, wrap instead of popping */
|
||||||
if (fileInfo->iters[0] <= contextStack->nbReptIters) {
|
if (fileInfo->iters[0] <= contextStack->nbReptIters) {
|
||||||
@@ -226,22 +253,22 @@ bool yywrap(void)
|
|||||||
} else if (!contextStack->parent) {
|
} else if (!contextStack->parent) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
dbgPrint("Popping context\n");
|
|
||||||
|
|
||||||
struct Context *context = contextStack;
|
struct Context *context = contextStack;
|
||||||
|
|
||||||
contextStack = contextStack->parent;
|
contextStack = contextStack->parent;
|
||||||
|
assert(contextDepth != 0); // This is never supposed to underflow
|
||||||
contextDepth--;
|
contextDepth--;
|
||||||
|
|
||||||
lexer_DeleteState(context->lexerState);
|
lexer_DeleteState(context->lexerState);
|
||||||
/* Restore args if a macro (not REPT) saved them */
|
/* Restore args if a macro (not REPT) saved them */
|
||||||
if (context->fileInfo->type == NODE_MACRO) {
|
if (context->fileInfo->type == NODE_MACRO)
|
||||||
dbgPrint("Restoring macro args %p\n", contextStack->macroArgs);
|
|
||||||
macro_UseNewArgs(contextStack->macroArgs);
|
macro_UseNewArgs(contextStack->macroArgs);
|
||||||
}
|
|
||||||
/* Free the file stack node */
|
/* Free the file stack node */
|
||||||
if (!context->fileInfo->referenced)
|
if (!context->fileInfo->referenced)
|
||||||
free(context->fileInfo);
|
free(context->fileInfo);
|
||||||
|
/* Free the FOR symbol name */
|
||||||
|
free(context->forName);
|
||||||
/* Free the entry and make its parent the current entry */
|
/* Free the entry and make its parent the current entry */
|
||||||
free(context);
|
free(context);
|
||||||
|
|
||||||
@@ -253,11 +280,13 @@ bool yywrap(void)
|
|||||||
/*
|
/*
|
||||||
* Make sure not to switch the lexer state before calling this, so the saved line no is correct
|
* Make sure not to switch the lexer state before calling this, so the saved line no is correct
|
||||||
* BE CAREFUL!! This modifies the file stack directly, you should have set up the file info first
|
* BE CAREFUL!! This modifies the file stack directly, you should have set up the file info first
|
||||||
|
* Callers should set contextStack->lexerState after this so it is not NULL
|
||||||
*/
|
*/
|
||||||
static void newContext(struct FileStackNode *fileInfo)
|
static void newContext(struct FileStackNode *fileInfo)
|
||||||
{
|
{
|
||||||
if (++contextDepth >= nMaxRecursionDepth)
|
++contextDepth;
|
||||||
fatalerror("Recursion limit (%zu) exceeded\n", nMaxRecursionDepth);
|
fstk_NewRecursionDepth(maxRecursionDepth); // Only checks if the max depth was exceeded
|
||||||
|
|
||||||
struct Context *context = malloc(sizeof(*context));
|
struct Context *context = malloc(sizeof(*context));
|
||||||
|
|
||||||
if (!context)
|
if (!context)
|
||||||
@@ -267,31 +296,32 @@ static void newContext(struct FileStackNode *fileInfo)
|
|||||||
fileInfo->referenced = false;
|
fileInfo->referenced = false;
|
||||||
fileInfo->lineNo = lexer_GetLineNo();
|
fileInfo->lineNo = lexer_GetLineNo();
|
||||||
context->fileInfo = fileInfo;
|
context->fileInfo = fileInfo;
|
||||||
|
context->forName = NULL;
|
||||||
/*
|
/*
|
||||||
* Link new entry to its parent so it's reachable later
|
* Link new entry to its parent so it's reachable later
|
||||||
* ERRORS SHOULD NOT OCCUR AFTER THIS!!
|
* ERRORS SHOULD NOT OCCUR AFTER THIS!!
|
||||||
*/
|
*/
|
||||||
context->parent = contextStack;
|
context->parent = contextStack;
|
||||||
contextStack = context;
|
contextStack = context;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void fstk_RunInclude(char const *path)
|
void fstk_RunInclude(char const *path)
|
||||||
{
|
{
|
||||||
dbgPrint("Including path \"%s\"\n", path);
|
|
||||||
|
|
||||||
char *fullPath = NULL;
|
char *fullPath = NULL;
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
|
|
||||||
if (!fstk_FindFile(path, &fullPath, &size)) {
|
if (!fstk_FindFile(path, &fullPath, &size)) {
|
||||||
free(fullPath);
|
free(fullPath);
|
||||||
if (oGeneratedMissingIncludes)
|
if (generatedMissingIncludes) {
|
||||||
oFailedOnMissingInclude = true;
|
if (verbose)
|
||||||
else
|
printf("Aborting (-MG) on INCLUDE file '%s' (%s)\n",
|
||||||
|
path, strerror(errno));
|
||||||
|
failedOnMissingInclude = true;
|
||||||
|
} else {
|
||||||
error("Unable to open included file '%s': %s\n", path, strerror(errno));
|
error("Unable to open included file '%s': %s\n", path, strerror(errno));
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
dbgPrint("Full path: \"%s\"\n", fullPath);
|
|
||||||
|
|
||||||
struct FileStackNamedNode *fileInfo = malloc(sizeof(*fileInfo) + size);
|
struct FileStackNamedNode *fileInfo = malloc(sizeof(*fileInfo) + size);
|
||||||
|
|
||||||
@@ -315,8 +345,6 @@ void fstk_RunInclude(char const *path)
|
|||||||
|
|
||||||
void fstk_RunMacro(char const *macroName, struct MacroArgs *args)
|
void fstk_RunMacro(char const *macroName, struct MacroArgs *args)
|
||||||
{
|
{
|
||||||
dbgPrint("Running macro \"%s\"\n", macroName);
|
|
||||||
|
|
||||||
struct Symbol *macro = sym_FindExactSymbol(macroName);
|
struct Symbol *macro = sym_FindExactSymbol(macroName);
|
||||||
|
|
||||||
if (!macro) {
|
if (!macro) {
|
||||||
@@ -376,9 +404,8 @@ void fstk_RunMacro(char const *macroName, struct MacroArgs *args)
|
|||||||
memcpy(dest, macro->name, macroNameLen + 1);
|
memcpy(dest, macro->name, macroNameLen + 1);
|
||||||
|
|
||||||
newContext((struct FileStackNode *)fileInfo);
|
newContext((struct FileStackNode *)fileInfo);
|
||||||
/* Line minus 1 because buffer begins with a newline */
|
contextStack->lexerState = lexer_OpenFileView("MACRO", macro->macro, macro->macroSize,
|
||||||
contextStack->lexerState = lexer_OpenFileView(macro->macro, macro->macroSize,
|
macro->fileLine);
|
||||||
macro->fileLine - 1);
|
|
||||||
if (!contextStack->lexerState)
|
if (!contextStack->lexerState)
|
||||||
fatalerror("Failed to set up lexer for macro invocation\n");
|
fatalerror("Failed to set up lexer for macro invocation\n");
|
||||||
lexer_SetStateAtEOL(contextStack->lexerState);
|
lexer_SetStateAtEOL(contextStack->lexerState);
|
||||||
@@ -386,12 +413,8 @@ void fstk_RunMacro(char const *macroName, struct MacroArgs *args)
|
|||||||
macro_UseNewArgs(args);
|
macro_UseNewArgs(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
void fstk_RunRept(uint32_t count, int32_t reptLineNo, char *body, size_t size)
|
static bool newReptContext(int32_t reptLineNo, char *body, size_t size)
|
||||||
{
|
{
|
||||||
dbgPrint("Running REPT(%" PRIu32 ")\n", count);
|
|
||||||
if (count == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
uint32_t reptDepth = contextStack->fileInfo->type == NODE_REPT
|
uint32_t reptDepth = contextStack->fileInfo->type == NODE_REPT
|
||||||
? ((struct FileStackReptNode *)contextStack->fileInfo)->reptDepth
|
? ((struct FileStackReptNode *)contextStack->fileInfo)->reptDepth
|
||||||
: 0;
|
: 0;
|
||||||
@@ -400,7 +423,7 @@ void fstk_RunRept(uint32_t count, int32_t reptLineNo, char *body, size_t size)
|
|||||||
|
|
||||||
if (!fileInfo) {
|
if (!fileInfo) {
|
||||||
error("Failed to alloc file info for REPT: %s\n", strerror(errno));
|
error("Failed to alloc file info for REPT: %s\n", strerror(errno));
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
fileInfo->node.type = NODE_REPT;
|
fileInfo->node.type = NODE_REPT;
|
||||||
fileInfo->reptDepth = reptDepth + 1;
|
fileInfo->reptDepth = reptDepth + 1;
|
||||||
@@ -415,16 +438,84 @@ void fstk_RunRept(uint32_t count, int32_t reptLineNo, char *body, size_t size)
|
|||||||
/* Correct our line number, which currently points to the `ENDR` line */
|
/* Correct our line number, which currently points to the `ENDR` line */
|
||||||
contextStack->fileInfo->lineNo = reptLineNo;
|
contextStack->fileInfo->lineNo = reptLineNo;
|
||||||
|
|
||||||
contextStack->lexerState = lexer_OpenFileView(body, size, reptLineNo);
|
contextStack->lexerState = lexer_OpenFileView("REPT", body, size, reptLineNo);
|
||||||
if (!contextStack->lexerState)
|
if (!contextStack->lexerState)
|
||||||
fatalerror("Failed to set up lexer for rept block\n");
|
fatalerror("Failed to set up lexer for REPT block\n");
|
||||||
lexer_SetStateAtEOL(contextStack->lexerState);
|
lexer_SetStateAtEOL(contextStack->lexerState);
|
||||||
contextStack->uniqueID = macro_UseNewUniqueID();
|
contextStack->uniqueID = macro_UseNewUniqueID();
|
||||||
contextStack->nbReptIters = count;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void fstk_Init(char const *mainPath, size_t maxRecursionDepth)
|
void fstk_RunRept(uint32_t count, int32_t reptLineNo, char *body, size_t size)
|
||||||
|
{
|
||||||
|
if (count == 0)
|
||||||
|
return;
|
||||||
|
if (!newReptContext(reptLineNo, body, size))
|
||||||
|
return;
|
||||||
|
|
||||||
|
contextStack->nbReptIters = count;
|
||||||
|
contextStack->forName = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fstk_RunFor(char const *symName, int32_t start, int32_t stop, int32_t step,
|
||||||
|
int32_t reptLineNo, char *body, size_t size)
|
||||||
|
{
|
||||||
|
struct Symbol *sym = sym_AddVar(symName, start);
|
||||||
|
|
||||||
|
if (sym->type != SYM_VAR)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint32_t count = 0;
|
||||||
|
|
||||||
|
if (step > 0 && start < stop)
|
||||||
|
count = (stop - start - 1) / step + 1;
|
||||||
|
else if (step < 0 && stop < start)
|
||||||
|
count = (start - stop - 1) / -step + 1;
|
||||||
|
else if (step == 0)
|
||||||
|
error("FOR cannot have a step value of 0\n");
|
||||||
|
|
||||||
|
if ((step > 0 && start > stop) || (step < 0 && start < stop))
|
||||||
|
warning(WARNING_BACKWARDS_FOR, "FOR goes backwards from %d to %d by %d\n",
|
||||||
|
start, stop, step);
|
||||||
|
|
||||||
|
if (count == 0)
|
||||||
|
return;
|
||||||
|
if (!newReptContext(reptLineNo, body, size))
|
||||||
|
return;
|
||||||
|
|
||||||
|
contextStack->nbReptIters = count;
|
||||||
|
contextStack->forValue = start;
|
||||||
|
contextStack->forStep = step;
|
||||||
|
contextStack->forName = strdup(symName);
|
||||||
|
if (!contextStack->forName)
|
||||||
|
fatalerror("Not enough memory for FOR symbol name: %s\n", strerror(errno));
|
||||||
|
}
|
||||||
|
|
||||||
|
void fstk_StopRept(void)
|
||||||
|
{
|
||||||
|
/* Prevent more iterations */
|
||||||
|
contextStack->nbReptIters = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fstk_Break(void)
|
||||||
|
{
|
||||||
|
if (contextStack->fileInfo->type != NODE_REPT) {
|
||||||
|
error("BREAK can only be used inside a REPT/FOR block\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
fstk_StopRept();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fstk_NewRecursionDepth(size_t newDepth)
|
||||||
|
{
|
||||||
|
if (contextDepth >= newDepth)
|
||||||
|
fatalerror("Recursion limit (%zu) exceeded\n", newDepth);
|
||||||
|
maxRecursionDepth = newDepth;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fstk_Init(char const *mainPath, size_t maxDepth)
|
||||||
{
|
{
|
||||||
struct LexerState *state = lexer_OpenFile(mainPath);
|
struct LexerState *state = lexer_OpenFile(mainPath);
|
||||||
|
|
||||||
@@ -444,6 +535,7 @@ void fstk_Init(char const *mainPath, size_t maxRecursionDepth)
|
|||||||
context->fileInfo = (struct FileStackNode *)fileInfo;
|
context->fileInfo = (struct FileStackNode *)fileInfo;
|
||||||
/* lineNo and reptIter are unused on the top-level context */
|
/* lineNo and reptIter are unused on the top-level context */
|
||||||
context->fileInfo->parent = NULL;
|
context->fileInfo->parent = NULL;
|
||||||
|
context->fileInfo->lineNo = 0; // This still gets written to the object file, so init it
|
||||||
context->fileInfo->referenced = false;
|
context->fileInfo->referenced = false;
|
||||||
context->fileInfo->type = NODE_FILE;
|
context->fileInfo->type = NODE_FILE;
|
||||||
memcpy(fileInfo->name, fileName, len + 1);
|
memcpy(fileInfo->name, fileName, len + 1);
|
||||||
@@ -453,6 +545,9 @@ void fstk_Init(char const *mainPath, size_t maxRecursionDepth)
|
|||||||
context->uniqueID = 0;
|
context->uniqueID = 0;
|
||||||
macro_SetUniqueID(0);
|
macro_SetUniqueID(0);
|
||||||
context->nbReptIters = 0;
|
context->nbReptIters = 0;
|
||||||
|
context->forValue = 0;
|
||||||
|
context->forStep = 0;
|
||||||
|
context->forName = NULL;
|
||||||
|
|
||||||
/* Now that it's set up properly, register the context */
|
/* Now that it's set up properly, register the context */
|
||||||
contextStack = context;
|
contextStack = context;
|
||||||
@@ -462,12 +557,12 @@ void fstk_Init(char const *mainPath, size_t maxRecursionDepth)
|
|||||||
* This assumes that the rept node is larger
|
* This assumes that the rept node is larger
|
||||||
*/
|
*/
|
||||||
#define DEPTH_LIMIT ((SIZE_MAX - sizeof(struct FileStackReptNode)) / sizeof(uint32_t))
|
#define DEPTH_LIMIT ((SIZE_MAX - sizeof(struct FileStackReptNode)) / sizeof(uint32_t))
|
||||||
if (maxRecursionDepth > DEPTH_LIMIT) {
|
if (maxDepth > DEPTH_LIMIT) {
|
||||||
error("Recursion depth may not be higher than %zu, defaulting to "
|
error("Recursion depth may not be higher than %zu, defaulting to "
|
||||||
EXPAND_AND_STR(DEFAULT_MAX_DEPTH) "\n", DEPTH_LIMIT);
|
EXPAND_AND_STR(DEFAULT_MAX_DEPTH) "\n", DEPTH_LIMIT);
|
||||||
nMaxRecursionDepth = DEFAULT_MAX_DEPTH;
|
maxRecursionDepth = DEFAULT_MAX_DEPTH;
|
||||||
} else {
|
} else {
|
||||||
nMaxRecursionDepth = maxRecursionDepth;
|
maxRecursionDepth = maxDepth;
|
||||||
}
|
}
|
||||||
/* Make sure that the default of 64 is OK, though */
|
/* Make sure that the default of 64 is OK, though */
|
||||||
assert(DEPTH_LIMIT >= DEFAULT_MAX_DEPTH);
|
assert(DEPTH_LIMIT >= DEFAULT_MAX_DEPTH);
|
||||||
|
|||||||
+1386
-911
File diff suppressed because it is too large
Load Diff
+54
-11
@@ -2,14 +2,16 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "asm/asm.h"
|
|
||||||
#include "asm/macro.h"
|
#include "asm/macro.h"
|
||||||
#include "asm/warning.h"
|
#include "asm/warning.h"
|
||||||
|
|
||||||
|
#define MAXMACROARGS 99999
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Your average macro invocation does not go past the tens, but some go further
|
* Your average macro invocation does not go past the tens, but some go further
|
||||||
* This ensures that sane and slightly insane invocations suffer no penalties,
|
* This ensures that sane and slightly insane invocations suffer no penalties,
|
||||||
@@ -36,7 +38,7 @@ static uint32_t maxUniqueID = 0;
|
|||||||
* guarantees the size of the buffer will be correct. I was unable to find a
|
* guarantees the size of the buffer will be correct. I was unable to find a
|
||||||
* better solution, but if you have one, please feel free!
|
* better solution, but if you have one, please feel free!
|
||||||
*/
|
*/
|
||||||
static char uniqueIDBuf[] = "_" EXPAND_AND_STR(UINT32_MAX);
|
static char uniqueIDBuf[] = "_u4294967295"; // UINT32_MAX
|
||||||
static char *uniqueIDPtr = NULL;
|
static char *uniqueIDPtr = NULL;
|
||||||
|
|
||||||
struct MacroArgs *macro_GetCurrentArgs(void)
|
struct MacroArgs *macro_GetCurrentArgs(void)
|
||||||
@@ -60,14 +62,15 @@ struct MacroArgs *macro_NewArgs(void)
|
|||||||
void macro_AppendArg(struct MacroArgs **argPtr, char *s)
|
void macro_AppendArg(struct MacroArgs **argPtr, char *s)
|
||||||
{
|
{
|
||||||
#define macArgs (*argPtr)
|
#define macArgs (*argPtr)
|
||||||
|
if (s[0] == '\0')
|
||||||
|
warning(WARNING_EMPTY_MACRO_ARG, "Empty macro argument\n");
|
||||||
if (macArgs->nbArgs == MAXMACROARGS)
|
if (macArgs->nbArgs == MAXMACROARGS)
|
||||||
error("A maximum of " EXPAND_AND_STR(MAXMACROARGS)
|
error("A maximum of " EXPAND_AND_STR(MAXMACROARGS) " arguments is allowed\n");
|
||||||
" arguments is allowed\n");
|
|
||||||
if (macArgs->nbArgs >= macArgs->capacity) {
|
if (macArgs->nbArgs >= macArgs->capacity) {
|
||||||
macArgs->capacity *= 2;
|
macArgs->capacity *= 2;
|
||||||
/* Check that overflow didn't roll us back */
|
/* Check that overflow didn't roll us back */
|
||||||
if (macArgs->capacity <= macArgs->nbArgs)
|
if (macArgs->capacity <= macArgs->nbArgs)
|
||||||
fatalerror("Failed to add new macro argument: possible capacity overflow\n");
|
fatalerror("Failed to add new macro argument: capacity overflow\n");
|
||||||
macArgs = realloc(macArgs, SIZEOF_ARGS(macArgs->capacity));
|
macArgs = realloc(macArgs, SIZEOF_ARGS(macArgs->capacity));
|
||||||
if (!macArgs)
|
if (!macArgs)
|
||||||
fatalerror("Error adding new macro argument: %s\n", strerror(errno));
|
fatalerror("Error adding new macro argument: %s\n", strerror(errno));
|
||||||
@@ -98,6 +101,40 @@ char const *macro_GetArg(uint32_t i)
|
|||||||
: macroArgs->args[realIndex];
|
: macroArgs->args[realIndex];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char const *macro_GetAllArgs(void)
|
||||||
|
{
|
||||||
|
if (!macroArgs)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (macroArgs->shift >= macroArgs->nbArgs)
|
||||||
|
return "";
|
||||||
|
|
||||||
|
size_t len = 0;
|
||||||
|
|
||||||
|
for (uint32_t i = macroArgs->shift; i < macroArgs->nbArgs; i++)
|
||||||
|
len += strlen(macroArgs->args[i]) + 1; /* 1 for comma */
|
||||||
|
|
||||||
|
char *str = malloc(len + 1); /* 1 for '\0' */
|
||||||
|
char *ptr = str;
|
||||||
|
|
||||||
|
if (!str)
|
||||||
|
fatalerror("Failed to allocate memory for expanding '\\#': %s\n", strerror(errno));
|
||||||
|
|
||||||
|
for (uint32_t i = macroArgs->shift; i < macroArgs->nbArgs; i++) {
|
||||||
|
size_t n = strlen(macroArgs->args[i]);
|
||||||
|
|
||||||
|
memcpy(ptr, macroArgs->args[i], n);
|
||||||
|
ptr += n;
|
||||||
|
|
||||||
|
/* Commas go between args and after a last empty arg */
|
||||||
|
if (i < macroArgs->nbArgs - 1 || n == 0)
|
||||||
|
*ptr++ = ','; /* no space after comma */
|
||||||
|
}
|
||||||
|
*ptr = '\0';
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t macro_GetUniqueID(void)
|
uint32_t macro_GetUniqueID(void)
|
||||||
{
|
{
|
||||||
return uniqueID;
|
return uniqueID;
|
||||||
@@ -117,7 +154,8 @@ void macro_SetUniqueID(uint32_t id)
|
|||||||
if (uniqueID > maxUniqueID)
|
if (uniqueID > maxUniqueID)
|
||||||
maxUniqueID = uniqueID;
|
maxUniqueID = uniqueID;
|
||||||
/* The buffer is guaranteed to be the correct size */
|
/* The buffer is guaranteed to be the correct size */
|
||||||
sprintf(uniqueIDBuf, "_%" PRIu32, id);
|
/* This is a valid label fragment, but not a valid numeric */
|
||||||
|
sprintf(uniqueIDBuf, "_u%" PRIu32, id);
|
||||||
uniqueIDPtr = uniqueIDBuf;
|
uniqueIDPtr = uniqueIDBuf;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -132,12 +170,17 @@ void macro_ShiftCurrentArgs(int32_t count)
|
|||||||
{
|
{
|
||||||
if (!macroArgs) {
|
if (!macroArgs) {
|
||||||
error("Cannot shift macro arguments outside of a macro\n");
|
error("Cannot shift macro arguments outside of a macro\n");
|
||||||
} else if (count < 0) {
|
} else if (count > 0 && ((uint32_t)count > macroArgs->nbArgs
|
||||||
error("Cannot shift arguments by negative amount %" PRId32 "\n", count);
|
|| macroArgs->shift > macroArgs->nbArgs - count)) {
|
||||||
} else if (macroArgs->shift < macroArgs->nbArgs) {
|
warning(WARNING_MACRO_SHIFT,
|
||||||
|
"Cannot shift macro arguments past their end\n");
|
||||||
|
macroArgs->shift = macroArgs->nbArgs;
|
||||||
|
} else if (count < 0 && macroArgs->shift < (uint32_t)-count) {
|
||||||
|
warning(WARNING_MACRO_SHIFT,
|
||||||
|
"Cannot shift macro arguments past their beginning\n");
|
||||||
|
macroArgs->shift = 0;
|
||||||
|
} else {
|
||||||
macroArgs->shift += count;
|
macroArgs->shift += count;
|
||||||
if (macroArgs->shift > macroArgs->nbArgs)
|
|
||||||
macroArgs->shift = macroArgs->nbArgs;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+142
-318
@@ -19,208 +19,58 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#include "asm/charmap.h"
|
#include "asm/charmap.h"
|
||||||
|
#include "asm/format.h"
|
||||||
#include "asm/fstack.h"
|
#include "asm/fstack.h"
|
||||||
#include "asm/lexer.h"
|
#include "asm/lexer.h"
|
||||||
#include "asm/main.h"
|
#include "asm/main.h"
|
||||||
|
#include "asm/opt.h"
|
||||||
#include "asm/output.h"
|
#include "asm/output.h"
|
||||||
#include "asm/rpn.h"
|
#include "asm/rpn.h"
|
||||||
#include "asm/symbol.h"
|
#include "asm/symbol.h"
|
||||||
#include "asm/warning.h"
|
#include "asm/warning.h"
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
|
|
||||||
#include "extern/err.h"
|
|
||||||
#include "extern/getopt.h"
|
#include "extern/getopt.h"
|
||||||
|
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
|
#include "error.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
|
#ifdef __clang__
|
||||||
|
#if __has_feature(address_sanitizer) && !defined(__SANITIZE_ADDRESS__)
|
||||||
|
#define __SANITIZE_ADDRESS__
|
||||||
|
#endif /* __has_feature(address_sanitizer) && !defined(__SANITIZE_ADDRESS__) */
|
||||||
|
#endif /* __clang__ */
|
||||||
|
|
||||||
|
#ifdef __SANITIZE_ADDRESS__
|
||||||
|
// There are known, non-trivial to fix leaks. We would still like to have `make develop'
|
||||||
|
// detect memory corruption, though.
|
||||||
|
char const *__asan_default_options(void) { return "detect_leaks=0"; }
|
||||||
|
#endif
|
||||||
|
|
||||||
// Old Bison versions (confirmed for 2.3) do not forward-declare `yyparse` in the generated header
|
// Old Bison versions (confirmed for 2.3) do not forward-declare `yyparse` in the generated header
|
||||||
// Unfortunately, macOS still ships 2.3, which is from 2008...
|
// Unfortunately, macOS still ships 2.3, which is from 2008...
|
||||||
int yyparse(void);
|
int yyparse(void);
|
||||||
|
|
||||||
size_t cldefines_index;
|
|
||||||
size_t cldefines_numindices;
|
|
||||||
size_t cldefines_bufsize;
|
|
||||||
const size_t cldefine_entrysize = 2 * sizeof(void *);
|
|
||||||
char **cldefines;
|
|
||||||
|
|
||||||
clock_t nStartClock, nEndClock;
|
|
||||||
uint32_t nTotalLines, nIFDepth;
|
|
||||||
|
|
||||||
#if defined(YYDEBUG) && YYDEBUG
|
|
||||||
extern int yydebug;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
FILE * dependfile;
|
FILE * dependfile;
|
||||||
bool oGeneratedMissingIncludes;
|
bool generatedMissingIncludes;
|
||||||
bool oFailedOnMissingInclude;
|
bool failedOnMissingInclude;
|
||||||
bool oGeneratePhonyDeps;
|
bool generatePhonyDeps;
|
||||||
char *tzTargetFileName;
|
char *targetFileName;
|
||||||
|
|
||||||
/*
|
|
||||||
* Option stack
|
|
||||||
*/
|
|
||||||
|
|
||||||
struct sOptions DefaultOptions;
|
|
||||||
struct sOptions CurrentOptions;
|
|
||||||
bool haltnop;
|
bool haltnop;
|
||||||
bool optimizeloads;
|
bool optimizeLoads;
|
||||||
bool verbose;
|
bool verbose;
|
||||||
bool warnings; /* True to enable warnings, false to disable them. */
|
bool warnings; /* True to enable warnings, false to disable them. */
|
||||||
|
|
||||||
struct sOptionStackEntry {
|
|
||||||
struct sOptions Options;
|
|
||||||
struct sOptionStackEntry *next;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sOptionStackEntry *pOptionStack;
|
|
||||||
|
|
||||||
void opt_SetCurrentOptions(struct sOptions *opt)
|
|
||||||
{
|
|
||||||
CurrentOptions = *opt;
|
|
||||||
lexer_SetGfxDigits(CurrentOptions.gbgfx);
|
|
||||||
lexer_SetBinDigits(CurrentOptions.binary);
|
|
||||||
}
|
|
||||||
|
|
||||||
void opt_Parse(char *s)
|
|
||||||
{
|
|
||||||
struct sOptions newopt;
|
|
||||||
|
|
||||||
newopt = CurrentOptions;
|
|
||||||
|
|
||||||
switch (s[0]) {
|
|
||||||
case 'g':
|
|
||||||
if (strlen(&s[1]) == 4) {
|
|
||||||
newopt.gbgfx[0] = s[1];
|
|
||||||
newopt.gbgfx[1] = s[2];
|
|
||||||
newopt.gbgfx[2] = s[3];
|
|
||||||
newopt.gbgfx[3] = s[4];
|
|
||||||
} else {
|
|
||||||
error("Must specify exactly 4 characters for option 'g'\n");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'b':
|
|
||||||
if (strlen(&s[1]) == 2) {
|
|
||||||
newopt.binary[0] = s[1];
|
|
||||||
newopt.binary[1] = s[2];
|
|
||||||
} else {
|
|
||||||
error("Must specify exactly 2 characters for option 'b'\n");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'z':
|
|
||||||
warning(WARNING_OBSOLETE, "Option 'z' is a deprecated alias for 'p'\n");
|
|
||||||
/* fallthrough */
|
|
||||||
case 'p':
|
|
||||||
if (strlen(&s[1]) <= 2) {
|
|
||||||
int result;
|
|
||||||
unsigned int fillchar;
|
|
||||||
|
|
||||||
result = sscanf(&s[1], "%x", &fillchar);
|
|
||||||
if (result != EOF && result != 1)
|
|
||||||
error("Invalid argument for option 'z'\n");
|
|
||||||
else
|
|
||||||
newopt.fillchar = fillchar;
|
|
||||||
} else {
|
|
||||||
error("Invalid argument for option 'z'\n");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
error("Unknown option\n");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
opt_SetCurrentOptions(&newopt);
|
|
||||||
}
|
|
||||||
|
|
||||||
void opt_Push(void)
|
|
||||||
{
|
|
||||||
struct sOptionStackEntry *pOpt;
|
|
||||||
|
|
||||||
pOpt = malloc(sizeof(struct sOptionStackEntry));
|
|
||||||
|
|
||||||
if (pOpt == NULL)
|
|
||||||
fatalerror("No memory for option stack\n");
|
|
||||||
|
|
||||||
pOpt->Options = CurrentOptions;
|
|
||||||
pOpt->next = pOptionStack;
|
|
||||||
pOptionStack = pOpt;
|
|
||||||
}
|
|
||||||
|
|
||||||
void opt_Pop(void)
|
|
||||||
{
|
|
||||||
if (pOptionStack == NULL)
|
|
||||||
fatalerror("No entries in the option stack\n");
|
|
||||||
|
|
||||||
struct sOptionStackEntry *pOpt;
|
|
||||||
|
|
||||||
pOpt = pOptionStack;
|
|
||||||
opt_SetCurrentOptions(&(pOpt->Options));
|
|
||||||
pOptionStack = pOpt->next;
|
|
||||||
free(pOpt);
|
|
||||||
}
|
|
||||||
|
|
||||||
void opt_AddDefine(char *s)
|
|
||||||
{
|
|
||||||
char *value, *equals;
|
|
||||||
|
|
||||||
if (cldefines_index >= cldefines_numindices) {
|
|
||||||
/* Check for overflows */
|
|
||||||
if ((cldefines_numindices * 2) < cldefines_numindices)
|
|
||||||
fatalerror("No memory for command line defines\n");
|
|
||||||
|
|
||||||
if ((cldefines_bufsize * 2) < cldefines_bufsize)
|
|
||||||
fatalerror("No memory for command line defines\n");
|
|
||||||
|
|
||||||
cldefines_numindices *= 2;
|
|
||||||
cldefines_bufsize *= 2;
|
|
||||||
|
|
||||||
cldefines = realloc(cldefines, cldefines_bufsize);
|
|
||||||
if (!cldefines)
|
|
||||||
fatalerror("No memory for command line defines\n");
|
|
||||||
}
|
|
||||||
equals = strchr(s, '=');
|
|
||||||
if (equals) {
|
|
||||||
*equals = '\0';
|
|
||||||
value = equals + 1;
|
|
||||||
} else {
|
|
||||||
value = "1";
|
|
||||||
}
|
|
||||||
cldefines[cldefines_index++] = s;
|
|
||||||
cldefines[cldefines_index++] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void opt_ParseDefines(void)
|
|
||||||
{
|
|
||||||
uint32_t i;
|
|
||||||
|
|
||||||
for (i = 0; i < cldefines_index; i += 2)
|
|
||||||
sym_AddString(cldefines[i], cldefines[i + 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
void upperstring(char *s)
|
|
||||||
{
|
|
||||||
while (*s) {
|
|
||||||
*s = toupper(*s);
|
|
||||||
s++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void lowerstring(char *s)
|
|
||||||
{
|
|
||||||
while (*s) {
|
|
||||||
*s = tolower(*s);
|
|
||||||
s++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Escapes Make-special chars from a string */
|
/* Escapes Make-special chars from a string */
|
||||||
static char *make_escape(const char *str)
|
static char *make_escape(char const *str)
|
||||||
{
|
{
|
||||||
char * const escaped_str = malloc(strlen(str) * 2 + 1);
|
char * const escaped_str = malloc(strlen(str) * 2 + 1);
|
||||||
char *dest = escaped_str;
|
char *dest = escaped_str;
|
||||||
|
|
||||||
if (escaped_str == NULL)
|
if (escaped_str == NULL)
|
||||||
err(1, "%s: Failed to allocate memory", __func__);
|
err("%s: Failed to allocate memory", __func__);
|
||||||
|
|
||||||
while (*str) {
|
while (*str) {
|
||||||
/* All dollars needs to be doubled */
|
/* All dollars needs to be doubled */
|
||||||
@@ -234,7 +84,7 @@ static char *make_escape(const char *str)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Short options */
|
/* Short options */
|
||||||
static char const *optstring = "b:D:Eg:hi:LM:o:p:r:VvW:w";
|
static const char *optstring = "b:D:Eg:hi:LM:o:p:r:VvW:w";
|
||||||
|
|
||||||
/* Variables for the long-only options */
|
/* Variables for the long-only options */
|
||||||
static int depType; /* Variants of `-M` */
|
static int depType; /* Variants of `-M` */
|
||||||
@@ -276,7 +126,7 @@ static void print_usage(void)
|
|||||||
fputs(
|
fputs(
|
||||||
"Usage: rgbasm [-EhLVvw] [-b chars] [-D name[=value]] [-g chars] [-i path]\n"
|
"Usage: rgbasm [-EhLVvw] [-b chars] [-D name[=value]] [-g chars] [-i path]\n"
|
||||||
" [-M depend_file] [-MG] [-MP] [-MT target_file] [-MQ target_file]\n"
|
" [-M depend_file] [-MG] [-MP] [-MT target_file] [-MQ target_file]\n"
|
||||||
" [-o out_file] [-p pad_value] [-r depth] [-W warning] <file> ...\n"
|
" [-o out_file] [-p pad_value] [-r depth] [-W warning] <file>\n"
|
||||||
"Useful options:\n"
|
"Useful options:\n"
|
||||||
" -E, --export-all export all labels\n"
|
" -E, --export-all export all labels\n"
|
||||||
" -M, --dependfile <path> set the output dependency file\n"
|
" -M, --dependfile <path> set the output dependency file\n"
|
||||||
@@ -292,123 +142,132 @@ static void print_usage(void)
|
|||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
#if YYDEBUG
|
||||||
|
yydebug = 1;
|
||||||
|
#endif
|
||||||
int ch;
|
int ch;
|
||||||
char *ep;
|
char *ep;
|
||||||
|
|
||||||
struct sOptions newopt;
|
time_t now = time(NULL);
|
||||||
|
char const *sourceDateEpoch = getenv("SOURCE_DATE_EPOCH");
|
||||||
|
|
||||||
char *tzMainfile;
|
/*
|
||||||
|
* Support SOURCE_DATE_EPOCH for reproducible builds
|
||||||
|
* https://reproducible-builds.org/docs/source-date-epoch/
|
||||||
|
*/
|
||||||
|
if (sourceDateEpoch)
|
||||||
|
now = (time_t)strtoul(sourceDateEpoch, NULL, 0);
|
||||||
|
|
||||||
dependfile = NULL;
|
dependfile = NULL;
|
||||||
|
|
||||||
/* Initial number of allocated elements in array */
|
// Perform some init for below
|
||||||
cldefines_numindices = 32;
|
sym_Init(now);
|
||||||
cldefines_bufsize = cldefines_numindices * cldefine_entrysize;
|
|
||||||
cldefines = malloc(cldefines_bufsize);
|
|
||||||
if (!cldefines)
|
|
||||||
fatalerror("No memory for command line defines\n");
|
|
||||||
|
|
||||||
#if defined(YYDEBUG) && YYDEBUG
|
// Set defaults
|
||||||
yydebug = 1;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
oGeneratePhonyDeps = false;
|
generatePhonyDeps = false;
|
||||||
oGeneratedMissingIncludes = false;
|
generatedMissingIncludes = false;
|
||||||
oFailedOnMissingInclude = false;
|
failedOnMissingInclude = false;
|
||||||
tzTargetFileName = NULL;
|
targetFileName = NULL;
|
||||||
uint32_t maxRecursionDepth = 64;
|
|
||||||
size_t nTargetFileNameLen = 0;
|
|
||||||
|
|
||||||
DefaultOptions.gbgfx[0] = '0';
|
opt_B("01");
|
||||||
DefaultOptions.gbgfx[1] = '1';
|
opt_G("0123");
|
||||||
DefaultOptions.gbgfx[2] = '2';
|
opt_P(0);
|
||||||
DefaultOptions.gbgfx[3] = '3';
|
optimizeLoads = true;
|
||||||
DefaultOptions.binary[0] = '0';
|
|
||||||
DefaultOptions.binary[1] = '1';
|
|
||||||
DefaultOptions.fillchar = 0;
|
|
||||||
optimizeloads = true;
|
|
||||||
haltnop = true;
|
haltnop = true;
|
||||||
verbose = false;
|
verbose = false;
|
||||||
warnings = true;
|
warnings = true;
|
||||||
bool exportall = false;
|
sym_SetExportAll(false);
|
||||||
|
uint32_t maxDepth = 64;
|
||||||
|
size_t targetFileNameLen = 0;
|
||||||
|
|
||||||
opt_SetCurrentOptions(&DefaultOptions);
|
while ((ch = musl_getopt_long_only(argc, argv, optstring, longopts, NULL)) != -1) {
|
||||||
|
|
||||||
newopt = CurrentOptions;
|
|
||||||
|
|
||||||
while ((ch = musl_getopt_long_only(argc, argv, optstring, longopts,
|
|
||||||
NULL)) != -1) {
|
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
case 'b':
|
case 'b':
|
||||||
if (strlen(optarg) == 2) {
|
if (strlen(musl_optarg) == 2)
|
||||||
newopt.binary[0] = optarg[1];
|
opt_B(&musl_optarg[1]);
|
||||||
newopt.binary[1] = optarg[2];
|
else
|
||||||
} else {
|
errx("Must specify exactly 2 characters for option 'b'");
|
||||||
errx(1, "Must specify exactly 2 characters for option 'b'");
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
char *equals;
|
||||||
case 'D':
|
case 'D':
|
||||||
opt_AddDefine(optarg);
|
equals = strchr(musl_optarg, '=');
|
||||||
break;
|
if (equals) {
|
||||||
case 'E':
|
*equals = '\0';
|
||||||
exportall = true;
|
sym_AddString(musl_optarg, equals + 1);
|
||||||
break;
|
|
||||||
case 'g':
|
|
||||||
if (strlen(optarg) == 4) {
|
|
||||||
newopt.gbgfx[0] = optarg[1];
|
|
||||||
newopt.gbgfx[1] = optarg[2];
|
|
||||||
newopt.gbgfx[2] = optarg[3];
|
|
||||||
newopt.gbgfx[3] = optarg[4];
|
|
||||||
} else {
|
} else {
|
||||||
errx(1, "Must specify exactly 4 characters for option 'g'");
|
sym_AddString(musl_optarg, "1");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'E':
|
||||||
|
sym_SetExportAll(true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'g':
|
||||||
|
if (strlen(musl_optarg) == 4)
|
||||||
|
opt_G(&musl_optarg[1]);
|
||||||
|
else
|
||||||
|
errx("Must specify exactly 4 characters for option 'g'");
|
||||||
|
break;
|
||||||
|
|
||||||
case 'h':
|
case 'h':
|
||||||
haltnop = false;
|
haltnop = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'i':
|
case 'i':
|
||||||
fstk_AddIncludePath(optarg);
|
fstk_AddIncludePath(musl_optarg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'L':
|
case 'L':
|
||||||
optimizeloads = false;
|
optimizeLoads = false;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'M':
|
case 'M':
|
||||||
if (!strcmp("-", optarg))
|
if (!strcmp("-", musl_optarg))
|
||||||
dependfile = stdout;
|
dependfile = stdout;
|
||||||
else
|
else
|
||||||
dependfile = fopen(optarg, "w");
|
dependfile = fopen(musl_optarg, "w");
|
||||||
if (dependfile == NULL)
|
if (dependfile == NULL)
|
||||||
err(1, "Could not open dependfile %s",
|
err("Could not open dependfile %s", musl_optarg);
|
||||||
optarg);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'o':
|
case 'o':
|
||||||
out_SetFileName(optarg);
|
out_SetFileName(musl_optarg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
unsigned long fill;
|
||||||
case 'p':
|
case 'p':
|
||||||
newopt.fillchar = strtoul(optarg, &ep, 0);
|
fill = strtoul(musl_optarg, &ep, 0);
|
||||||
|
|
||||||
if (optarg[0] == '\0' || *ep != '\0')
|
if (musl_optarg[0] == '\0' || *ep != '\0')
|
||||||
errx(1, "Invalid argument for option 'p'");
|
errx("Invalid argument for option 'p'");
|
||||||
|
|
||||||
if (newopt.fillchar < 0 || newopt.fillchar > 0xFF)
|
if (fill > 0xFF)
|
||||||
errx(1, "Argument for option 'p' must be between 0 and 0xFF");
|
errx("Argument for option 'p' must be between 0 and 0xFF");
|
||||||
|
|
||||||
|
opt_P(fill);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'r':
|
case 'r':
|
||||||
maxRecursionDepth = strtoul(optarg, &ep, 0);
|
maxDepth = strtoul(musl_optarg, &ep, 0);
|
||||||
|
|
||||||
if (optarg[0] == '\0' || *ep != '\0')
|
if (musl_optarg[0] == '\0' || *ep != '\0')
|
||||||
errx(1, "Invalid argument for option 'r'");
|
errx("Invalid argument for option 'r'");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'V':
|
case 'V':
|
||||||
printf("rgbasm %s\n", get_package_version_string());
|
printf("rgbasm %s\n", get_package_version_string());
|
||||||
exit(0);
|
exit(0);
|
||||||
case 'v':
|
case 'v':
|
||||||
verbose = true;
|
verbose = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'W':
|
case 'W':
|
||||||
processWarningFlag(optarg);
|
processWarningFlag(musl_optarg);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'w':
|
case 'w':
|
||||||
warnings = false;
|
warnings = false;
|
||||||
break;
|
break;
|
||||||
@@ -417,41 +276,30 @@ int main(int argc, char *argv[])
|
|||||||
case 0:
|
case 0:
|
||||||
switch (depType) {
|
switch (depType) {
|
||||||
case 'G':
|
case 'G':
|
||||||
oGeneratedMissingIncludes = true;
|
generatedMissingIncludes = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'P':
|
case 'P':
|
||||||
oGeneratePhonyDeps = true;
|
generatePhonyDeps = true;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
char *newTarget;
|
||||||
case 'Q':
|
case 'Q':
|
||||||
case 'T':
|
case 'T':
|
||||||
if (optind == argc)
|
newTarget = musl_optarg;
|
||||||
errx(1, "-M%c takes a target file name argument",
|
|
||||||
depType);
|
|
||||||
ep = optarg;
|
|
||||||
if (depType == 'Q')
|
if (depType == 'Q')
|
||||||
ep = make_escape(ep);
|
newTarget = make_escape(newTarget);
|
||||||
|
size_t newTargetLen = strlen(newTarget) + 1; // Plus the space
|
||||||
|
|
||||||
nTargetFileNameLen += strlen(ep) + 1;
|
targetFileName = realloc(targetFileName,
|
||||||
if (!tzTargetFileName) {
|
targetFileNameLen + newTargetLen + 1);
|
||||||
/* On first alloc, make an empty str */
|
if (targetFileName == NULL)
|
||||||
tzTargetFileName =
|
err("Cannot append new file to target file list");
|
||||||
malloc(nTargetFileNameLen + 1);
|
memcpy(&targetFileName[targetFileNameLen], newTarget, newTargetLen);
|
||||||
if (tzTargetFileName)
|
|
||||||
*tzTargetFileName = '\0';
|
|
||||||
} else {
|
|
||||||
tzTargetFileName =
|
|
||||||
realloc(tzTargetFileName,
|
|
||||||
nTargetFileNameLen + 1);
|
|
||||||
}
|
|
||||||
if (tzTargetFileName == NULL)
|
|
||||||
err(1, "Cannot append new file to target file list");
|
|
||||||
strcat(tzTargetFileName, ep);
|
|
||||||
if (depType == 'Q')
|
if (depType == 'Q')
|
||||||
free(ep);
|
free(newTarget);
|
||||||
char *ptr = tzTargetFileName +
|
targetFileNameLen += newTargetLen;
|
||||||
strlen(tzTargetFileName);
|
targetFileName[targetFileNameLen - 1] = ' ';
|
||||||
*ptr++ = ' ';
|
|
||||||
*ptr = '\0';
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -462,81 +310,57 @@ int main(int argc, char *argv[])
|
|||||||
/* NOTREACHED */
|
/* NOTREACHED */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
argc -= optind;
|
|
||||||
argv += optind;
|
|
||||||
|
|
||||||
if (tzTargetFileName == NULL)
|
if (targetFileName == NULL)
|
||||||
tzTargetFileName = tzObjectname;
|
targetFileName = objectName;
|
||||||
|
else
|
||||||
|
targetFileName[targetFileNameLen - 1] = '\0'; // Overwrite the last space
|
||||||
|
|
||||||
opt_SetCurrentOptions(&newopt);
|
if (argc == musl_optind) {
|
||||||
|
fputs("FATAL: No input files\n", stderr);
|
||||||
DefaultOptions = CurrentOptions;
|
print_usage();
|
||||||
|
} else if (argc != musl_optind + 1) {
|
||||||
if (argc == 0) {
|
fputs("FATAL: More than one input file given\n", stderr);
|
||||||
fputs("FATAL: no input files\n", stderr);
|
|
||||||
print_usage();
|
print_usage();
|
||||||
}
|
}
|
||||||
|
|
||||||
tzMainfile = argv[argc - 1];
|
char const *mainFileName = argv[musl_optind];
|
||||||
|
|
||||||
if (verbose)
|
if (verbose)
|
||||||
printf("Assembling %s\n", tzMainfile);
|
printf("Assembling %s\n", mainFileName);
|
||||||
|
|
||||||
if (dependfile) {
|
if (dependfile) {
|
||||||
if (!tzTargetFileName)
|
if (!targetFileName)
|
||||||
errx(1, "Dependency files can only be created if a target file is specified with either -o, -MQ or -MT.\n");
|
errx("Dependency files can only be created if a target file is specified with either -o, -MQ or -MT");
|
||||||
|
|
||||||
fprintf(dependfile, "%s: %s\n", tzTargetFileName, tzMainfile);
|
fprintf(dependfile, "%s: %s\n", targetFileName, mainFileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Init file stack; important to do first, since it provides the file name, line, etc */
|
|
||||||
lexer_Init();
|
|
||||||
fstk_Init(tzMainfile, maxRecursionDepth);
|
|
||||||
|
|
||||||
nStartClock = clock();
|
|
||||||
|
|
||||||
nTotalLines = 0;
|
|
||||||
nIFDepth = 0;
|
|
||||||
sym_Init();
|
|
||||||
sym_SetExportAll(exportall);
|
|
||||||
|
|
||||||
opt_ParseDefines();
|
|
||||||
charmap_New("main", NULL);
|
charmap_New("main", NULL);
|
||||||
|
|
||||||
opt_SetCurrentOptions(&DefaultOptions);
|
// Init lexer and file stack, prodiving file info
|
||||||
|
lexer_Init();
|
||||||
|
fstk_Init(mainFileName, maxDepth);
|
||||||
|
|
||||||
|
// Perform parse (yyparse is auto-generated from `parser.y`)
|
||||||
|
if (yyparse() != 0 && nbErrors == 0)
|
||||||
|
nbErrors = 1;
|
||||||
|
|
||||||
if (yyparse() != 0 || nbErrors != 0)
|
|
||||||
errx(1, "Assembly aborted (%u errors)!", nbErrors);
|
|
||||||
if (dependfile)
|
if (dependfile)
|
||||||
fclose(dependfile);
|
fclose(dependfile);
|
||||||
|
|
||||||
if (nIFDepth != 0)
|
|
||||||
errx(1, "Unterminated IF construct (%" PRIu32 " levels)!",
|
|
||||||
nIFDepth);
|
|
||||||
|
|
||||||
sect_CheckUnionClosed();
|
sect_CheckUnionClosed();
|
||||||
|
|
||||||
double timespent;
|
if (nbErrors != 0)
|
||||||
|
errx("Assembly aborted (%u error%s)!", nbErrors,
|
||||||
|
nbErrors == 1 ? "" : "s");
|
||||||
|
|
||||||
nEndClock = clock();
|
// If parse aborted due to missing an include, and `-MG` was given, exit normally
|
||||||
timespent = ((double)(nEndClock - nStartClock))
|
if (failedOnMissingInclude)
|
||||||
/ (double)CLOCKS_PER_SEC;
|
|
||||||
if (verbose) {
|
|
||||||
printf("Success! %" PRIu32 " lines in %d.%02d seconds ",
|
|
||||||
nTotalLines, (int)timespent,
|
|
||||||
((int)(timespent * 100.0)) % 100);
|
|
||||||
if (timespent < FLT_MIN_EXP)
|
|
||||||
printf("(INFINITY lines/minute)\n");
|
|
||||||
else
|
|
||||||
printf("(%d lines/minute)\n",
|
|
||||||
(int)(60 / timespent * nTotalLines));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (oFailedOnMissingInclude)
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* If no path specified, don't write file */
|
/* If no path specified, don't write file */
|
||||||
if (tzObjectname != NULL)
|
if (objectName != NULL)
|
||||||
out_WriteObject();
|
out_WriteObject();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
-147
@@ -1,147 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of RGBDS.
|
|
||||||
*
|
|
||||||
* Copyright (c) 1997-2018, Carsten Sorensen and RGBDS contributors.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Fixedpoint math routines
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <inttypes.h>
|
|
||||||
#include <math.h>
|
|
||||||
#include <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#include "asm/mymath.h"
|
|
||||||
#include "asm/symbol.h"
|
|
||||||
|
|
||||||
#define fx2double(i) ((double)((i) / 65536.0))
|
|
||||||
#define double2fx(d) ((int32_t)((d) * 65536.0))
|
|
||||||
|
|
||||||
#ifndef M_PI
|
|
||||||
#define M_PI 3.14159265358979323846
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Define the _PI symbol
|
|
||||||
*/
|
|
||||||
void math_DefinePI(void)
|
|
||||||
{
|
|
||||||
sym_AddEqu("_PI", double2fx(M_PI));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Print a fixed point value
|
|
||||||
*/
|
|
||||||
void math_Print(int32_t i)
|
|
||||||
{
|
|
||||||
uint32_t u = i;
|
|
||||||
const char *sign = "";
|
|
||||||
|
|
||||||
if (i < 0) {
|
|
||||||
u = -u;
|
|
||||||
sign = "-";
|
|
||||||
}
|
|
||||||
|
|
||||||
printf("%s%" PRIu32 ".%05" PRIu32, sign, u >> 16,
|
|
||||||
((uint32_t)(fx2double(u) * 100000 + 0.5)) % 100000);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Calculate sine
|
|
||||||
*/
|
|
||||||
int32_t math_Sin(int32_t i)
|
|
||||||
{
|
|
||||||
return double2fx(sin(fx2double(i) * 2 * M_PI / 65536));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Calculate cosine
|
|
||||||
*/
|
|
||||||
int32_t math_Cos(int32_t i)
|
|
||||||
{
|
|
||||||
return double2fx(cos(fx2double(i) * 2 * M_PI / 65536));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Calculate tangent
|
|
||||||
*/
|
|
||||||
int32_t math_Tan(int32_t i)
|
|
||||||
{
|
|
||||||
return double2fx(tan(fx2double(i) * 2 * M_PI / 65536));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Calculate arcsine
|
|
||||||
*/
|
|
||||||
int32_t math_ASin(int32_t i)
|
|
||||||
{
|
|
||||||
return double2fx(asin(fx2double(i)) / 2 / M_PI * 65536);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Calculate arccosine
|
|
||||||
*/
|
|
||||||
int32_t math_ACos(int32_t i)
|
|
||||||
{
|
|
||||||
return double2fx(acos(fx2double(i)) / 2 / M_PI * 65536);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Calculate arctangent
|
|
||||||
*/
|
|
||||||
int32_t math_ATan(int32_t i)
|
|
||||||
{
|
|
||||||
return double2fx(atan(fx2double(i)) / 2 / M_PI * 65536);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Calculate atan2
|
|
||||||
*/
|
|
||||||
int32_t math_ATan2(int32_t i, int32_t j)
|
|
||||||
{
|
|
||||||
return double2fx(atan2(fx2double(i), fx2double(j)) / 2 / M_PI * 65536);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Multiplication
|
|
||||||
*/
|
|
||||||
int32_t math_Mul(int32_t i, int32_t j)
|
|
||||||
{
|
|
||||||
return double2fx(fx2double(i) * fx2double(j));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Division
|
|
||||||
*/
|
|
||||||
int32_t math_Div(int32_t i, int32_t j)
|
|
||||||
{
|
|
||||||
return double2fx(fx2double(i) / fx2double(j));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Round
|
|
||||||
*/
|
|
||||||
int32_t math_Round(int32_t i)
|
|
||||||
{
|
|
||||||
return double2fx(round(fx2double(i)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Ceil
|
|
||||||
*/
|
|
||||||
int32_t math_Ceil(int32_t i)
|
|
||||||
{
|
|
||||||
return double2fx(ceil(fx2double(i)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Floor
|
|
||||||
*/
|
|
||||||
int32_t math_Floor(int32_t i)
|
|
||||||
{
|
|
||||||
return double2fx(floor(fx2double(i)));
|
|
||||||
}
|
|
||||||
+221
@@ -0,0 +1,221 @@
|
|||||||
|
#include <ctype.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "asm/fstack.h"
|
||||||
|
#include "asm/lexer.h"
|
||||||
|
#include "asm/main.h"
|
||||||
|
#include "asm/section.h"
|
||||||
|
#include "asm/warning.h"
|
||||||
|
|
||||||
|
struct OptStackEntry {
|
||||||
|
char binary[2];
|
||||||
|
char gbgfx[4];
|
||||||
|
int32_t fillByte;
|
||||||
|
bool haltnop;
|
||||||
|
bool optimizeLoads;
|
||||||
|
bool warningsAreErrors;
|
||||||
|
size_t maxRecursionDepth;
|
||||||
|
// Don't be confused: we use the size of the **global variable** `warningStates`!
|
||||||
|
enum WarningState warningStates[sizeof(warningStates)];
|
||||||
|
struct OptStackEntry *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct OptStackEntry *stack = NULL;
|
||||||
|
|
||||||
|
void opt_B(char const chars[2])
|
||||||
|
{
|
||||||
|
lexer_SetBinDigits(chars);
|
||||||
|
}
|
||||||
|
|
||||||
|
void opt_G(char const chars[4])
|
||||||
|
{
|
||||||
|
lexer_SetGfxDigits(chars);
|
||||||
|
}
|
||||||
|
|
||||||
|
void opt_P(uint8_t fill)
|
||||||
|
{
|
||||||
|
fillByte = fill;
|
||||||
|
}
|
||||||
|
|
||||||
|
void opt_R(size_t newDepth)
|
||||||
|
{
|
||||||
|
fstk_NewRecursionDepth(newDepth);
|
||||||
|
lexer_CheckRecursionDepth();
|
||||||
|
}
|
||||||
|
|
||||||
|
void opt_h(bool halt)
|
||||||
|
{
|
||||||
|
haltnop = halt;
|
||||||
|
}
|
||||||
|
|
||||||
|
void opt_L(bool optimize)
|
||||||
|
{
|
||||||
|
optimizeLoads = optimize;
|
||||||
|
}
|
||||||
|
|
||||||
|
void opt_W(char *flag)
|
||||||
|
{
|
||||||
|
processWarningFlag(flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
void opt_Parse(char *s)
|
||||||
|
{
|
||||||
|
switch (s[0]) {
|
||||||
|
case 'b':
|
||||||
|
if (strlen(&s[1]) == 2)
|
||||||
|
opt_B(&s[1]);
|
||||||
|
else
|
||||||
|
error("Must specify exactly 2 characters for option 'b'\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'g':
|
||||||
|
if (strlen(&s[1]) == 4)
|
||||||
|
opt_G(&s[1]);
|
||||||
|
else
|
||||||
|
error("Must specify exactly 4 characters for option 'g'\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'p':
|
||||||
|
if (strlen(&s[1]) <= 2) {
|
||||||
|
int result;
|
||||||
|
unsigned int fillchar;
|
||||||
|
|
||||||
|
result = sscanf(&s[1], "%x", &fillchar);
|
||||||
|
if (result != EOF && result != 1)
|
||||||
|
error("Invalid argument for option 'p'\n");
|
||||||
|
else
|
||||||
|
opt_P(fillchar);
|
||||||
|
} else {
|
||||||
|
error("Invalid argument for option 'p'\n");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'r': {
|
||||||
|
++s; // Skip 'r'
|
||||||
|
while (isblank(*s))
|
||||||
|
++s; // Skip leading whitespace
|
||||||
|
|
||||||
|
if (s[0] == '\0') {
|
||||||
|
error("Missing argument to option 'r'\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *endptr;
|
||||||
|
unsigned long newDepth = strtoul(s, &endptr, 10);
|
||||||
|
|
||||||
|
if (*endptr != '\0') {
|
||||||
|
error("Invalid argument to option 'r' (\"%s\")\n", s);
|
||||||
|
} else if (errno == ERANGE) {
|
||||||
|
error("Argument to 'r' is out of range (\"%s\")\n", s);
|
||||||
|
} else {
|
||||||
|
opt_R(newDepth);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'h':
|
||||||
|
if (s[1] == '\0')
|
||||||
|
opt_h(false);
|
||||||
|
else
|
||||||
|
error("Option 'h' does not take an argument\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'L':
|
||||||
|
if (s[1] == '\0')
|
||||||
|
opt_L(false);
|
||||||
|
else
|
||||||
|
error("Option 'L' does not take an argument\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'W':
|
||||||
|
if (strlen(&s[1]) > 0)
|
||||||
|
opt_W(&s[1]);
|
||||||
|
else
|
||||||
|
error("Must specify an argument for option 'W'\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case '!': // negates flag options that do not take an argument
|
||||||
|
switch (s[1]) {
|
||||||
|
case 'h':
|
||||||
|
if (s[2] == '\0')
|
||||||
|
opt_h(true);
|
||||||
|
else
|
||||||
|
error("Option '!h' does not take an argument\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'L':
|
||||||
|
if (s[2] == '\0')
|
||||||
|
opt_L(true);
|
||||||
|
else
|
||||||
|
error("Option '!L' does not take an argument\n");
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
error("Unknown option '!%c'\n", s[1]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
error("Unknown option '%c'\n", s[0]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void opt_Push(void)
|
||||||
|
{
|
||||||
|
struct OptStackEntry *entry = malloc(sizeof(*entry));
|
||||||
|
|
||||||
|
if (entry == NULL)
|
||||||
|
fatalerror("Failed to alloc option stack entry: %s\n", strerror(errno));
|
||||||
|
|
||||||
|
// Both of these pulled from lexer.h
|
||||||
|
entry->binary[0] = binDigits[0];
|
||||||
|
entry->binary[1] = binDigits[1];
|
||||||
|
|
||||||
|
entry->gbgfx[0] = gfxDigits[0];
|
||||||
|
entry->gbgfx[1] = gfxDigits[1];
|
||||||
|
entry->gbgfx[2] = gfxDigits[2];
|
||||||
|
entry->gbgfx[3] = gfxDigits[3];
|
||||||
|
|
||||||
|
entry->fillByte = fillByte; // Pulled from section.h
|
||||||
|
|
||||||
|
entry->haltnop = haltnop; // Pulled from main.h
|
||||||
|
|
||||||
|
entry->optimizeLoads = optimizeLoads; // Pulled from main.h
|
||||||
|
|
||||||
|
// Both of these pulled from warning.h
|
||||||
|
entry->warningsAreErrors = warningsAreErrors;
|
||||||
|
memcpy(entry->warningStates, warningStates, sizeof(warningStates));
|
||||||
|
|
||||||
|
entry->next = stack;
|
||||||
|
stack = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
void opt_Pop(void)
|
||||||
|
{
|
||||||
|
if (stack == NULL) {
|
||||||
|
error("No entries in the option stack\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OptStackEntry *entry = stack;
|
||||||
|
|
||||||
|
opt_B(entry->binary);
|
||||||
|
opt_G(entry->gbgfx);
|
||||||
|
opt_P(entry->fillByte);
|
||||||
|
opt_h(entry->haltnop);
|
||||||
|
opt_L(entry->optimizeLoads);
|
||||||
|
|
||||||
|
// opt_W does not apply a whole warning state; it processes one flag string
|
||||||
|
warningsAreErrors = entry->warningsAreErrors;
|
||||||
|
memcpy(warningStates, entry->warningStates, sizeof(warningStates));
|
||||||
|
|
||||||
|
stack = entry->next;
|
||||||
|
free(entry);
|
||||||
|
}
|
||||||
+152
-103
@@ -18,7 +18,6 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "asm/asm.h"
|
|
||||||
#include "asm/charmap.h"
|
#include "asm/charmap.h"
|
||||||
#include "asm/fstack.h"
|
#include "asm/fstack.h"
|
||||||
#include "asm/main.h"
|
#include "asm/main.h"
|
||||||
@@ -28,20 +27,19 @@
|
|||||||
#include "asm/symbol.h"
|
#include "asm/symbol.h"
|
||||||
#include "asm/warning.h"
|
#include "asm/warning.h"
|
||||||
|
|
||||||
#include "extern/err.h"
|
#include "error.h"
|
||||||
|
|
||||||
#include "linkdefs.h"
|
#include "linkdefs.h"
|
||||||
#include "platform.h" // strdup
|
#include "platform.h" // strdup
|
||||||
|
|
||||||
struct Patch {
|
struct Patch {
|
||||||
struct FileStackNode const *src;
|
struct FileStackNode const *src;
|
||||||
uint32_t lineNo;
|
uint32_t lineNo;
|
||||||
uint32_t nOffset;
|
uint32_t offset;
|
||||||
struct Section *pcSection;
|
struct Section *pcSection;
|
||||||
uint32_t pcOffset;
|
uint32_t pcOffset;
|
||||||
uint8_t type;
|
uint8_t type;
|
||||||
uint32_t nRPNSize;
|
uint32_t rpnSize;
|
||||||
uint8_t *pRPN;
|
uint8_t *rpn;
|
||||||
struct Patch *next;
|
struct Patch *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -52,10 +50,9 @@ struct Assertion {
|
|||||||
struct Assertion *next;
|
struct Assertion *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
char *tzObjectname;
|
char *objectName;
|
||||||
|
|
||||||
/* TODO: shouldn't `pCurrentSection` be somewhere else? */
|
struct Section *sectionList;
|
||||||
struct Section *pSectionList, *pCurrentSection;
|
|
||||||
|
|
||||||
/* Linked list of symbols to put in the object file */
|
/* Linked list of symbols to put in the object file */
|
||||||
static struct Symbol *objectSymbols = NULL;
|
static struct Symbol *objectSymbols = NULL;
|
||||||
@@ -69,11 +66,11 @@ static struct FileStackNode *fileStackNodes = NULL;
|
|||||||
/*
|
/*
|
||||||
* Count the number of sections used in this object
|
* Count the number of sections used in this object
|
||||||
*/
|
*/
|
||||||
static uint32_t countsections(void)
|
static uint32_t countSections(void)
|
||||||
{
|
{
|
||||||
uint32_t count = 0;
|
uint32_t count = 0;
|
||||||
|
|
||||||
for (struct Section const *sect = pSectionList; sect; sect = sect->next)
|
for (struct Section const *sect = sectionList; sect; sect = sect->next)
|
||||||
count++;
|
count++;
|
||||||
|
|
||||||
return count;
|
return count;
|
||||||
@@ -82,7 +79,7 @@ static uint32_t countsections(void)
|
|||||||
/*
|
/*
|
||||||
* Count the number of patches used in this object
|
* Count the number of patches used in this object
|
||||||
*/
|
*/
|
||||||
static uint32_t countpatches(struct Section const *sect)
|
static uint32_t countPatches(struct Section const *sect)
|
||||||
{
|
{
|
||||||
uint32_t r = 0;
|
uint32_t r = 0;
|
||||||
|
|
||||||
@@ -96,7 +93,7 @@ static uint32_t countpatches(struct Section const *sect)
|
|||||||
/**
|
/**
|
||||||
* Count the number of assertions used in this object
|
* Count the number of assertions used in this object
|
||||||
*/
|
*/
|
||||||
static uint32_t countasserts(void)
|
static uint32_t countAsserts(void)
|
||||||
{
|
{
|
||||||
struct Assertion *assert = assertions;
|
struct Assertion *assert = assertions;
|
||||||
uint32_t count = 0;
|
uint32_t count = 0;
|
||||||
@@ -111,22 +108,22 @@ static uint32_t countasserts(void)
|
|||||||
/*
|
/*
|
||||||
* Write a long to a file (little-endian)
|
* Write a long to a file (little-endian)
|
||||||
*/
|
*/
|
||||||
static void fputlong(uint32_t i, FILE *f)
|
static void putlong(uint32_t i, FILE *f)
|
||||||
{
|
{
|
||||||
fputc(i, f);
|
putc(i, f);
|
||||||
fputc(i >> 8, f);
|
putc(i >> 8, f);
|
||||||
fputc(i >> 16, f);
|
putc(i >> 16, f);
|
||||||
fputc(i >> 24, f);
|
putc(i >> 24, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Write a NULL-terminated string to a file
|
* Write a NULL-terminated string to a file
|
||||||
*/
|
*/
|
||||||
static void fputstring(char const *s, FILE *f)
|
static void putstring(char const *s, FILE *f)
|
||||||
{
|
{
|
||||||
while (*s)
|
while (*s)
|
||||||
fputc(*s++, f);
|
putc(*s++, f);
|
||||||
fputc(0, f);
|
putc(0, f);
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint32_t getNbFileStackNodes(void)
|
static uint32_t getNbFileStackNodes(void)
|
||||||
@@ -137,9 +134,9 @@ static uint32_t getNbFileStackNodes(void)
|
|||||||
void out_RegisterNode(struct FileStackNode *node)
|
void out_RegisterNode(struct FileStackNode *node)
|
||||||
{
|
{
|
||||||
/* If node is not already registered, register it (and parents), and give it a unique ID */
|
/* If node is not already registered, register it (and parents), and give it a unique ID */
|
||||||
while (node->ID == -1) {
|
while (node->ID == (uint32_t)-1) {
|
||||||
node->ID = getNbFileStackNodes();
|
node->ID = getNbFileStackNodes();
|
||||||
if (node->ID == -1)
|
if (node->ID == (uint32_t)-1)
|
||||||
fatalerror("Reached too many file stack nodes; try splitting the file up\n");
|
fatalerror("Reached too many file stack nodes; try splitting the file up\n");
|
||||||
node->next = fileStackNodes;
|
node->next = fileStackNodes;
|
||||||
fileStackNodes = node;
|
fileStackNodes = node;
|
||||||
@@ -180,7 +177,7 @@ This is code intended to replace a node, which is pretty useless until ref count
|
|||||||
*/
|
*/
|
||||||
static uint32_t getsectid(struct Section const *sect)
|
static uint32_t getsectid(struct Section const *sect)
|
||||||
{
|
{
|
||||||
struct Section const *sec = pSectionList;
|
struct Section const *sec = sectionList;
|
||||||
uint32_t ID = 0;
|
uint32_t ID = 0;
|
||||||
|
|
||||||
while (sec) {
|
while (sec) {
|
||||||
@@ -195,7 +192,7 @@ static uint32_t getsectid(struct Section const *sect)
|
|||||||
|
|
||||||
static uint32_t getSectIDIfAny(struct Section const *sect)
|
static uint32_t getSectIDIfAny(struct Section const *sect)
|
||||||
{
|
{
|
||||||
return sect ? getsectid(sect) : -1;
|
return sect ? getsectid(sect) : (uint32_t)-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -203,16 +200,15 @@ static uint32_t getSectIDIfAny(struct Section const *sect)
|
|||||||
*/
|
*/
|
||||||
static void writepatch(struct Patch const *patch, FILE *f)
|
static void writepatch(struct Patch const *patch, FILE *f)
|
||||||
{
|
{
|
||||||
assert(patch->src->ID != -1);
|
assert(patch->src->ID != (uint32_t)-1);
|
||||||
|
putlong(patch->src->ID, f);
|
||||||
fputlong(patch->src->ID, f);
|
putlong(patch->lineNo, f);
|
||||||
fputlong(patch->lineNo, f);
|
putlong(patch->offset, f);
|
||||||
fputlong(patch->nOffset, f);
|
putlong(getSectIDIfAny(patch->pcSection), f);
|
||||||
fputlong(getSectIDIfAny(patch->pcSection), f);
|
putlong(patch->pcOffset, f);
|
||||||
fputlong(patch->pcOffset, f);
|
putc(patch->type, f);
|
||||||
fputc(patch->type, f);
|
putlong(patch->rpnSize, f);
|
||||||
fputlong(patch->nRPNSize, f);
|
fwrite(patch->rpn, 1, patch->rpnSize, f);
|
||||||
fwrite(patch->pRPN, 1, patch->nRPNSize, f);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -220,23 +216,23 @@ static void writepatch(struct Patch const *patch, FILE *f)
|
|||||||
*/
|
*/
|
||||||
static void writesection(struct Section const *sect, FILE *f)
|
static void writesection(struct Section const *sect, FILE *f)
|
||||||
{
|
{
|
||||||
fputstring(sect->name, f);
|
putstring(sect->name, f);
|
||||||
|
|
||||||
fputlong(sect->size, f);
|
putlong(sect->size, f);
|
||||||
|
|
||||||
bool isUnion = sect->modifier == SECTION_UNION;
|
bool isUnion = sect->modifier == SECTION_UNION;
|
||||||
bool isFragment = sect->modifier == SECTION_FRAGMENT;
|
bool isFragment = sect->modifier == SECTION_FRAGMENT;
|
||||||
|
|
||||||
fputc(sect->type | isUnion << 7 | isFragment << 6, f);
|
putc(sect->type | isUnion << 7 | isFragment << 6, f);
|
||||||
|
|
||||||
fputlong(sect->org, f);
|
putlong(sect->org, f);
|
||||||
fputlong(sect->bank, f);
|
putlong(sect->bank, f);
|
||||||
fputc(sect->align, f);
|
putc(sect->align, f);
|
||||||
fputlong(sect->alignOfs, f);
|
putlong(sect->alignOfs, f);
|
||||||
|
|
||||||
if (sect_HasData(sect->type)) {
|
if (sect_HasData(sect->type)) {
|
||||||
fwrite(sect->data, 1, sect->size, f);
|
fwrite(sect->data, 1, sect->size, f);
|
||||||
fputlong(countpatches(sect), f);
|
putlong(countPatches(sect), f);
|
||||||
|
|
||||||
for (struct Patch const *patch = sect->patches; patch != NULL;
|
for (struct Patch const *patch = sect->patches; patch != NULL;
|
||||||
patch = patch->next)
|
patch = patch->next)
|
||||||
@@ -244,22 +240,37 @@ static void writesection(struct Section const *sect, FILE *f)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void freesection(struct Section const *sect)
|
||||||
|
{
|
||||||
|
if (sect_HasData(sect->type)) {
|
||||||
|
struct Patch *patch = sect->patches;
|
||||||
|
|
||||||
|
while (patch != NULL) {
|
||||||
|
struct Patch *next = patch->next;
|
||||||
|
|
||||||
|
free(patch->rpn);
|
||||||
|
free(patch);
|
||||||
|
patch = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Write a symbol to a file
|
* Write a symbol to a file
|
||||||
*/
|
*/
|
||||||
static void writesymbol(struct Symbol const *sym, FILE *f)
|
static void writesymbol(struct Symbol const *sym, FILE *f)
|
||||||
{
|
{
|
||||||
fputstring(sym->name, f);
|
putstring(sym->name, f);
|
||||||
if (!sym_IsDefined(sym)) {
|
if (!sym_IsDefined(sym)) {
|
||||||
fputc(SYMTYPE_IMPORT, f);
|
putc(SYMTYPE_IMPORT, f);
|
||||||
} else {
|
} else {
|
||||||
assert(sym->src->ID != -1);
|
assert(sym->src->ID != (uint32_t)-1);
|
||||||
|
|
||||||
fputc(sym->isExported ? SYMTYPE_EXPORT : SYMTYPE_LOCAL, f);
|
putc(sym->isExported ? SYMTYPE_EXPORT : SYMTYPE_LOCAL, f);
|
||||||
fputlong(sym->src->ID, f);
|
putlong(sym->src->ID, f);
|
||||||
fputlong(sym->fileLine, f);
|
putlong(sym->fileLine, f);
|
||||||
fputlong(getSectIDIfAny(sym_GetSection(sym)), f);
|
putlong(getSectIDIfAny(sym_GetSection(sym)), f);
|
||||||
fputlong(sym->value, f);
|
putlong(sym->value, f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -268,7 +279,7 @@ static void registerSymbol(struct Symbol *sym)
|
|||||||
*objectSymbolsTail = sym;
|
*objectSymbolsTail = sym;
|
||||||
objectSymbolsTail = &sym->next;
|
objectSymbolsTail = &sym->next;
|
||||||
out_RegisterNode(sym->src);
|
out_RegisterNode(sym->src);
|
||||||
if (nbSymbols == -1)
|
if (nbSymbols == (uint32_t)-1)
|
||||||
fatalerror("Registered too many symbols (%" PRIu32
|
fatalerror("Registered too many symbols (%" PRIu32
|
||||||
"); try splitting up your files\n", (uint32_t)-1);
|
"); try splitting up your files\n", (uint32_t)-1);
|
||||||
sym->ID = nbSymbols++;
|
sym->ID = nbSymbols++;
|
||||||
@@ -280,7 +291,7 @@ static void registerSymbol(struct Symbol *sym)
|
|||||||
*/
|
*/
|
||||||
static uint32_t getSymbolID(struct Symbol *sym)
|
static uint32_t getSymbolID(struct Symbol *sym)
|
||||||
{
|
{
|
||||||
if (sym->ID == -1 && !sym_IsPC(sym))
|
if (sym->ID == (uint32_t)-1 && !sym_IsPC(sym))
|
||||||
registerSymbol(sym);
|
registerSymbol(sym);
|
||||||
return sym->ID;
|
return sym->ID;
|
||||||
}
|
}
|
||||||
@@ -288,7 +299,7 @@ static uint32_t getSymbolID(struct Symbol *sym)
|
|||||||
static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
|
static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
|
||||||
uint32_t rpnlen)
|
uint32_t rpnlen)
|
||||||
{
|
{
|
||||||
char tzSym[512];
|
char symName[512];
|
||||||
|
|
||||||
for (size_t offset = 0; offset < rpnlen; ) {
|
for (size_t offset = 0; offset < rpnlen; ) {
|
||||||
#define popbyte() rpn[offset++]
|
#define popbyte() rpn[offset++]
|
||||||
@@ -312,14 +323,14 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
|
|||||||
case RPN_SYM:
|
case RPN_SYM:
|
||||||
i = 0;
|
i = 0;
|
||||||
do {
|
do {
|
||||||
tzSym[i] = popbyte();
|
symName[i] = popbyte();
|
||||||
} while (tzSym[i++]);
|
} while (symName[i++]);
|
||||||
|
|
||||||
// The symbol name is always written expanded
|
// The symbol name is always written expanded
|
||||||
sym = sym_FindExactSymbol(tzSym);
|
sym = sym_FindExactSymbol(symName);
|
||||||
if (sym_IsConstant(sym)) {
|
if (sym_IsConstant(sym)) {
|
||||||
writebyte(RPN_CONST);
|
writebyte(RPN_CONST);
|
||||||
value = sym_GetConstantValue(tzSym);
|
value = sym_GetConstantValue(symName);
|
||||||
} else {
|
} else {
|
||||||
writebyte(RPN_SYM);
|
writebyte(RPN_SYM);
|
||||||
value = getSymbolID(sym);
|
value = getSymbolID(sym);
|
||||||
@@ -334,11 +345,11 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
|
|||||||
case RPN_BANK_SYM:
|
case RPN_BANK_SYM:
|
||||||
i = 0;
|
i = 0;
|
||||||
do {
|
do {
|
||||||
tzSym[i] = popbyte();
|
symName[i] = popbyte();
|
||||||
} while (tzSym[i++]);
|
} while (symName[i++]);
|
||||||
|
|
||||||
// The symbol name is always written expanded
|
// The symbol name is always written expanded
|
||||||
sym = sym_FindExactSymbol(tzSym);
|
sym = sym_FindExactSymbol(symName);
|
||||||
value = getSymbolID(sym);
|
value = getSymbolID(sym);
|
||||||
|
|
||||||
writebyte(RPN_BANK_SYM);
|
writebyte(RPN_BANK_SYM);
|
||||||
@@ -356,6 +367,22 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
|
|||||||
} while (b != 0);
|
} while (b != 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case RPN_SIZEOF_SECT:
|
||||||
|
writebyte(RPN_SIZEOF_SECT);
|
||||||
|
do {
|
||||||
|
b = popbyte();
|
||||||
|
writebyte(b);
|
||||||
|
} while (b != 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RPN_STARTOF_SECT:
|
||||||
|
writebyte(RPN_STARTOF_SECT);
|
||||||
|
do {
|
||||||
|
b = popbyte();
|
||||||
|
writebyte(b);
|
||||||
|
} while (b != 0);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
writebyte(rpndata);
|
writebyte(rpndata);
|
||||||
break;
|
break;
|
||||||
@@ -372,38 +399,38 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
|
|||||||
static struct Patch *allocpatch(uint32_t type, struct Expression const *expr, uint32_t ofs)
|
static struct Patch *allocpatch(uint32_t type, struct Expression const *expr, uint32_t ofs)
|
||||||
{
|
{
|
||||||
struct Patch *patch = malloc(sizeof(struct Patch));
|
struct Patch *patch = malloc(sizeof(struct Patch));
|
||||||
uint32_t rpnSize = expr->isKnown ? 5 : expr->nRPNPatchSize;
|
uint32_t rpnSize = expr->isKnown ? 5 : expr->rpnPatchSize;
|
||||||
struct FileStackNode *node = fstk_GetFileStack();
|
struct FileStackNode *node = fstk_GetFileStack();
|
||||||
|
|
||||||
if (!patch)
|
if (!patch)
|
||||||
fatalerror("No memory for patch: %s\n", strerror(errno));
|
fatalerror("No memory for patch: %s\n", strerror(errno));
|
||||||
|
|
||||||
patch->pRPN = malloc(sizeof(*patch->pRPN) * rpnSize);
|
patch->rpn = malloc(sizeof(*patch->rpn) * rpnSize);
|
||||||
if (!patch->pRPN)
|
if (!patch->rpn)
|
||||||
fatalerror("No memory for patch's RPN expression: %s\n", strerror(errno));
|
fatalerror("No memory for patch's RPN rpnSize: %s\n", strerror(errno));
|
||||||
|
|
||||||
patch->type = type;
|
patch->type = type;
|
||||||
patch->src = node;
|
patch->src = node;
|
||||||
out_RegisterNode(node);
|
out_RegisterNode(node);
|
||||||
patch->lineNo = lexer_GetLineNo();
|
patch->lineNo = lexer_GetLineNo();
|
||||||
patch->nOffset = ofs;
|
patch->offset = ofs;
|
||||||
patch->pcSection = sect_GetSymbolSection();
|
patch->pcSection = sect_GetSymbolSection();
|
||||||
patch->pcOffset = sect_GetSymbolOffset();
|
patch->pcOffset = sect_GetSymbolOffset();
|
||||||
|
|
||||||
/* If the expression's value is known, output a constant RPN expression directly */
|
/* If the rpnSize's value is known, output a constant RPN rpnSize directly */
|
||||||
if (expr->isKnown) {
|
if (expr->isKnown) {
|
||||||
patch->nRPNSize = rpnSize;
|
patch->rpnSize = rpnSize;
|
||||||
/* Make sure to update `rpnSize` above if modifying this! */
|
/* Make sure to update `rpnSize` above if modifying this! */
|
||||||
patch->pRPN[0] = RPN_CONST;
|
patch->rpn[0] = RPN_CONST;
|
||||||
patch->pRPN[1] = (uint32_t)(expr->nVal) & 0xFF;
|
patch->rpn[1] = (uint32_t)(expr->val) & 0xFF;
|
||||||
patch->pRPN[2] = (uint32_t)(expr->nVal) >> 8;
|
patch->rpn[2] = (uint32_t)(expr->val) >> 8;
|
||||||
patch->pRPN[3] = (uint32_t)(expr->nVal) >> 16;
|
patch->rpn[3] = (uint32_t)(expr->val) >> 16;
|
||||||
patch->pRPN[4] = (uint32_t)(expr->nVal) >> 24;
|
patch->rpn[4] = (uint32_t)(expr->val) >> 24;
|
||||||
} else {
|
} else {
|
||||||
patch->nRPNSize = 0;
|
patch->rpnSize = 0;
|
||||||
writerpn(patch->pRPN, &patch->nRPNSize, expr->tRPN, expr->nRPNLength);
|
writerpn(patch->rpn, &patch->rpnSize, expr->rpn, expr->rpnLength);
|
||||||
}
|
}
|
||||||
assert(patch->nRPNSize == rpnSize);
|
assert(patch->rpnSize == rpnSize);
|
||||||
|
|
||||||
return patch;
|
return patch;
|
||||||
}
|
}
|
||||||
@@ -411,12 +438,17 @@ static struct Patch *allocpatch(uint32_t type, struct Expression const *expr, ui
|
|||||||
/*
|
/*
|
||||||
* Create a new patch (includes the rpn expr)
|
* Create a new patch (includes the rpn expr)
|
||||||
*/
|
*/
|
||||||
void out_CreatePatch(uint32_t type, struct Expression const *expr, uint32_t ofs)
|
void out_CreatePatch(uint32_t type, struct Expression const *expr, uint32_t ofs, uint32_t pcShift)
|
||||||
{
|
{
|
||||||
struct Patch *patch = allocpatch(type, expr, ofs);
|
struct Patch *patch = allocpatch(type, expr, ofs);
|
||||||
|
|
||||||
patch->next = pCurrentSection->patches;
|
// If the patch had a quantity of bytes output before it,
|
||||||
pCurrentSection->patches = patch;
|
// PC is not at the patch's location, but at the location
|
||||||
|
// before those bytes.
|
||||||
|
patch->pcOffset -= pcShift;
|
||||||
|
|
||||||
|
patch->next = currentSection->patches;
|
||||||
|
currentSection->patches = patch;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -446,30 +478,39 @@ bool out_CreateAssert(enum AssertionType type, struct Expression const *expr,
|
|||||||
static void writeassert(struct Assertion *assert, FILE *f)
|
static void writeassert(struct Assertion *assert, FILE *f)
|
||||||
{
|
{
|
||||||
writepatch(assert->patch, f);
|
writepatch(assert->patch, f);
|
||||||
fputstring(assert->message, f);
|
putstring(assert->message, f);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void freeassert(struct Assertion *assert)
|
||||||
|
{
|
||||||
|
free(assert->patch->rpn);
|
||||||
|
free(assert->patch);
|
||||||
|
free(assert);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void writeFileStackNode(struct FileStackNode const *node, FILE *f)
|
static void writeFileStackNode(struct FileStackNode const *node, FILE *f)
|
||||||
{
|
{
|
||||||
fputlong(node->parent ? node->parent->ID : -1, f);
|
putlong(node->parent ? node->parent->ID : (uint32_t)-1, f);
|
||||||
fputlong(node->lineNo, f);
|
putlong(node->lineNo, f);
|
||||||
fputc(node->type, f);
|
putc(node->type, f);
|
||||||
if (node->type != NODE_REPT) {
|
if (node->type != NODE_REPT) {
|
||||||
fputstring(((struct FileStackNamedNode const *)node)->name, f);
|
putstring(((struct FileStackNamedNode const *)node)->name, f);
|
||||||
} else {
|
} else {
|
||||||
struct FileStackReptNode const *reptNode = (struct FileStackReptNode const *)node;
|
struct FileStackReptNode const *reptNode = (struct FileStackReptNode const *)node;
|
||||||
|
|
||||||
fputlong(reptNode->reptDepth, f);
|
putlong(reptNode->reptDepth, f);
|
||||||
/* Iters are stored by decreasing depth, so reverse the order for output */
|
/* Iters are stored by decreasing depth, so reverse the order for output */
|
||||||
for (uint32_t i = reptNode->reptDepth; i--; )
|
for (uint32_t i = reptNode->reptDepth; i--; )
|
||||||
fputlong(reptNode->iters[i], f);
|
putlong(reptNode->iters[i], f);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void registerExportedSymbol(struct Symbol *symbol, void *arg)
|
static void registerUnregisteredSymbol(struct Symbol *symbol, void *arg)
|
||||||
{
|
{
|
||||||
(void)arg;
|
(void)arg; // sym_ForEach requires a void* parameter, but we are not using it.
|
||||||
if (sym_IsExported(symbol) && symbol->ID == -1) {
|
|
||||||
|
// Check for symbol->src, to skip any built-in symbol from rgbasm
|
||||||
|
if (symbol->src && symbol->ID == (uint32_t)-1) {
|
||||||
registerSymbol(symbol);
|
registerSymbol(symbol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -480,24 +521,24 @@ static void registerExportedSymbol(struct Symbol *symbol, void *arg)
|
|||||||
void out_WriteObject(void)
|
void out_WriteObject(void)
|
||||||
{
|
{
|
||||||
FILE *f;
|
FILE *f;
|
||||||
if (strcmp(tzObjectname, "-") != 0)
|
if (strcmp(objectName, "-") != 0)
|
||||||
f = fopen(tzObjectname, "wb");
|
f = fopen(objectName, "wb");
|
||||||
else
|
else
|
||||||
f = fdopen(1, "wb");
|
f = fdopen(1, "wb");
|
||||||
|
|
||||||
if (!f)
|
if (!f)
|
||||||
err(1, "Couldn't write file '%s'", tzObjectname);
|
err("Couldn't write file '%s'", objectName);
|
||||||
|
|
||||||
/* Also write exported symbols that weren't written above */
|
/* Also write symbols that weren't written above */
|
||||||
sym_ForEach(registerExportedSymbol, NULL);
|
sym_ForEach(registerUnregisteredSymbol, NULL);
|
||||||
|
|
||||||
fprintf(f, RGBDS_OBJECT_VERSION_STRING, RGBDS_OBJECT_VERSION_NUMBER);
|
fprintf(f, RGBDS_OBJECT_VERSION_STRING, RGBDS_OBJECT_VERSION_NUMBER);
|
||||||
fputlong(RGBDS_OBJECT_REV, f);
|
putlong(RGBDS_OBJECT_REV, f);
|
||||||
|
|
||||||
fputlong(nbSymbols, f);
|
putlong(nbSymbols, f);
|
||||||
fputlong(countsections(), f);
|
putlong(countSections(), f);
|
||||||
|
|
||||||
fputlong(getNbFileStackNodes(), f);
|
putlong(getNbFileStackNodes(), f);
|
||||||
for (struct FileStackNode const *node = fileStackNodes; node; node = node->next) {
|
for (struct FileStackNode const *node = fileStackNodes; node; node = node->next) {
|
||||||
writeFileStackNode(node, f);
|
writeFileStackNode(node, f);
|
||||||
if (node->next && node->next->ID != node->ID - 1)
|
if (node->next && node->next->ID != node->ID - 1)
|
||||||
@@ -509,13 +550,21 @@ void out_WriteObject(void)
|
|||||||
for (struct Symbol const *sym = objectSymbols; sym; sym = sym->next)
|
for (struct Symbol const *sym = objectSymbols; sym; sym = sym->next)
|
||||||
writesymbol(sym, f);
|
writesymbol(sym, f);
|
||||||
|
|
||||||
for (struct Section *sect = pSectionList; sect; sect = sect->next)
|
for (struct Section *sect = sectionList; sect; sect = sect->next) {
|
||||||
writesection(sect, f);
|
writesection(sect, f);
|
||||||
|
freesection(sect);
|
||||||
|
}
|
||||||
|
|
||||||
|
putlong(countAsserts(), f);
|
||||||
|
struct Assertion *assert = assertions;
|
||||||
|
|
||||||
|
while (assert != NULL) {
|
||||||
|
struct Assertion *next = assert->next;
|
||||||
|
|
||||||
fputlong(countasserts(), f);
|
|
||||||
for (struct Assertion *assert = assertions; assert;
|
|
||||||
assert = assert->next)
|
|
||||||
writeassert(assert, f);
|
writeassert(assert, f);
|
||||||
|
freeassert(assert);
|
||||||
|
assert = next;
|
||||||
|
}
|
||||||
|
|
||||||
fclose(f);
|
fclose(f);
|
||||||
}
|
}
|
||||||
@@ -525,7 +574,7 @@ void out_WriteObject(void)
|
|||||||
*/
|
*/
|
||||||
void out_SetFileName(char *s)
|
void out_SetFileName(char *s)
|
||||||
{
|
{
|
||||||
tzObjectname = s;
|
objectName = s;
|
||||||
if (verbose)
|
if (verbose)
|
||||||
printf("Output filename %s\n", s);
|
printf("Output filename %s\n", s);
|
||||||
}
|
}
|
||||||
|
|||||||
+1368
-734
File diff suppressed because it is too large
Load Diff
+54
-18
@@ -1,11 +1,11 @@
|
|||||||
.\"
|
.\"
|
||||||
.\" This file is part of RGBDS.
|
.\" This file is part of RGBDS.
|
||||||
.\"
|
.\"
|
||||||
.\" Copyright (c) 2010-2019, Anthony J. Bentley and RGBDS contributors.
|
.\" Copyright (c) 2010-2021, Anthony J. Bentley and RGBDS contributors.
|
||||||
.\"
|
.\"
|
||||||
.\" SPDX-License-Identifier: MIT
|
.\" SPDX-License-Identifier: MIT
|
||||||
.\"
|
.\"
|
||||||
.Dd July 8, 2019
|
.Dd March 28, 2021
|
||||||
.Dt RGBASM 1
|
.Dt RGBASM 1
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -179,9 +179,9 @@ Enables literally every warning.
|
|||||||
.Pp
|
.Pp
|
||||||
The following warnings are actual warning flags; with each description, the corresponding warning flag is included.
|
The following warnings are actual warning flags; with each description, the corresponding warning flag is included.
|
||||||
Note that each of these flag also has a negation (for example,
|
Note that each of these flag also has a negation (for example,
|
||||||
.Fl Wempty-entry
|
.Fl Wcharmap-redef
|
||||||
enables the warning that
|
enables the warning that
|
||||||
.Fl Wno-empty-entry
|
.Fl Wno-charmap-redef
|
||||||
disables).
|
disables).
|
||||||
Only the non-default flag is listed here.
|
Only the non-default flag is listed here.
|
||||||
Ignoring the
|
Ignoring the
|
||||||
@@ -189,7 +189,7 @@ Ignoring the
|
|||||||
prefix, entries are listed alphabetically.
|
prefix, entries are listed alphabetically.
|
||||||
.Bl -tag -width Ds
|
.Bl -tag -width Ds
|
||||||
.It Fl Wno-assert
|
.It Fl Wno-assert
|
||||||
Warns when
|
Warn when
|
||||||
.Ic WARN Ns No -type
|
.Ic WARN Ns No -type
|
||||||
assertions fail. (See
|
assertions fail. (See
|
||||||
.Dq Aborting the assembly process
|
.Dq Aborting the assembly process
|
||||||
@@ -197,6 +197,12 @@ in
|
|||||||
.Xr rgbasm 5
|
.Xr rgbasm 5
|
||||||
for
|
for
|
||||||
.Ic ASSERT ) .
|
.Ic ASSERT ) .
|
||||||
|
.It Fl Wbackwards-for
|
||||||
|
Warn when
|
||||||
|
.Ic FOR
|
||||||
|
loops have their start and stop values switched according to the step value.
|
||||||
|
This warning is enabled by
|
||||||
|
.Fl Wall .
|
||||||
.It Fl Wbuiltin-args
|
.It Fl Wbuiltin-args
|
||||||
Warn about incorrect arguments to built-in functions, such as
|
Warn about incorrect arguments to built-in functions, such as
|
||||||
.Fn STRSUB
|
.Fn STRSUB
|
||||||
@@ -208,13 +214,17 @@ Warn when re-defining a charmap mapping.
|
|||||||
This warning is enabled by
|
This warning is enabled by
|
||||||
.Fl Wall .
|
.Fl Wall .
|
||||||
.It Fl Wdiv
|
.It Fl Wdiv
|
||||||
Warn when dividing the smallest negative integer by -1, which yields itself due to integer overflow.
|
Warn when dividing the smallest negative integer (-2**31) by -1, which yields itself due to integer overflow.
|
||||||
.It Fl Wempty-entry
|
.It Fl Wempty-macro-arg
|
||||||
Warn when an empty entry is encountered in a
|
Warn when a macro argument is empty.
|
||||||
.Ic db , dw , dl
|
|
||||||
list.
|
|
||||||
This warning is enabled by
|
This warning is enabled by
|
||||||
.Fl Wextra .
|
.Fl Wextra .
|
||||||
|
.It Fl Wempty-strrpl
|
||||||
|
Warn when
|
||||||
|
.Fn STRRPL
|
||||||
|
is called with an empty string as its second argument (the substring to replace).
|
||||||
|
This warning is enabled by
|
||||||
|
.Fl Wall .
|
||||||
.It Fl Wlarge-constant
|
.It Fl Wlarge-constant
|
||||||
Warn when a constant too large to fit in a signed 32-bit integer is encountered.
|
Warn when a constant too large to fit in a signed 32-bit integer is encountered.
|
||||||
This warning is enabled by
|
This warning is enabled by
|
||||||
@@ -223,21 +233,47 @@ This warning is enabled by
|
|||||||
Warn when a string too long to fit in internal buffers is encountered.
|
Warn when a string too long to fit in internal buffers is encountered.
|
||||||
This warning is enabled by
|
This warning is enabled by
|
||||||
.Fl Wall .
|
.Fl Wall .
|
||||||
|
.It Fl Wmacro-shift
|
||||||
|
Warn when shifting macro arguments past their limits.
|
||||||
|
This warning is enabled by
|
||||||
|
.Fl Wextra .
|
||||||
.It Fl Wno-obsolete
|
.It Fl Wno-obsolete
|
||||||
Warn when obsolete constructs such as the
|
Warn when obsolete constructs such as the
|
||||||
.Ic jp [hl]
|
.Ic _PI
|
||||||
instruction or
|
constant or
|
||||||
.Ic HOME
|
.Ic PRINTT
|
||||||
section type are encountered.
|
directive are encountered.
|
||||||
|
.It Fl Wnumeric-string=
|
||||||
|
Warn when a multi-character string is treated as a number.
|
||||||
|
.Fl Wnumeric-string=0
|
||||||
|
or
|
||||||
|
.Fl Wno-numeric-string
|
||||||
|
disables this warning.
|
||||||
|
.Fl Wnumeric-string=1
|
||||||
|
or just
|
||||||
|
.Fl Wnumeric-string
|
||||||
|
warns about strings longer than four characters, since four or fewer characters fit within a 32-bit integer.
|
||||||
|
.Fl Wnumeric-string=2
|
||||||
|
warns about any multi-character string.
|
||||||
.It Fl Wshift
|
.It Fl Wshift
|
||||||
Warn when shifting right a negative value.
|
Warn when shifting right a negative value.
|
||||||
Use a division by 2^N instead.
|
Use a division by 2**N instead.
|
||||||
.It Fl Wshift-amount
|
.It Fl Wshift-amount
|
||||||
Warn when a shift's operand is negative or greater than 32.
|
Warn when a shift's operand is negative or greater than 32.
|
||||||
.It Fl Wno-truncation
|
.It Fl Wtruncation=
|
||||||
Warn when an implicit truncation (for example,
|
Warn when an implicit truncation (for example,
|
||||||
.Ic db )
|
.Ic db
|
||||||
loses some bits.
|
to an 8-bit value) loses some bits.
|
||||||
|
.Fl Wtruncation=0
|
||||||
|
or
|
||||||
|
.Fl Wno-truncation
|
||||||
|
disables this warning.
|
||||||
|
.Fl Wtruncation=1
|
||||||
|
warns when an N-bit value's absolute value is 2**N or greater.
|
||||||
|
.Fl Wtruncation=2
|
||||||
|
or just
|
||||||
|
.Fl Wtruncation
|
||||||
|
also warns when an N-bit value is less than -2**(N-1), which will not fit in two's complement encoding.
|
||||||
.It Fl Wno-user
|
.It Fl Wno-user
|
||||||
Warn when the
|
Warn when the
|
||||||
.Ic WARN
|
.Ic WARN
|
||||||
|
|||||||
+828
-360
File diff suppressed because it is too large
Load Diff
+267
-168
@@ -13,17 +13,21 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
#include <limits.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "asm/asm.h"
|
|
||||||
#include "asm/main.h"
|
#include "asm/main.h"
|
||||||
|
#include "asm/output.h"
|
||||||
#include "asm/rpn.h"
|
#include "asm/rpn.h"
|
||||||
#include "asm/section.h"
|
#include "asm/section.h"
|
||||||
#include "asm/symbol.h"
|
#include "asm/symbol.h"
|
||||||
#include "asm/warning.h"
|
#include "asm/warning.h"
|
||||||
|
|
||||||
|
#include "opmath.h"
|
||||||
|
|
||||||
/* Makes an expression "not known", also setting its error message */
|
/* Makes an expression "not known", also setting its error message */
|
||||||
#define makeUnknown(expr_, ...) do { \
|
#define makeUnknown(expr_, ...) do { \
|
||||||
struct Expression *_expr = expr_; \
|
struct Expression *_expr = expr_; \
|
||||||
@@ -42,47 +46,47 @@
|
|||||||
static uint8_t *reserveSpace(struct Expression *expr, uint32_t size)
|
static uint8_t *reserveSpace(struct Expression *expr, uint32_t size)
|
||||||
{
|
{
|
||||||
/* This assumes the RPN length is always less than the capacity */
|
/* This assumes the RPN length is always less than the capacity */
|
||||||
if (expr->nRPNCapacity - expr->nRPNLength < size) {
|
if (expr->rpnCapacity - expr->rpnLength < size) {
|
||||||
/* If there isn't enough room to reserve the space, realloc */
|
/* If there isn't enough room to reserve the space, realloc */
|
||||||
if (!expr->tRPN)
|
if (!expr->rpn)
|
||||||
expr->nRPNCapacity = 256; /* Initial size */
|
expr->rpnCapacity = 256; /* Initial size */
|
||||||
while (expr->nRPNCapacity - expr->nRPNLength < size) {
|
while (expr->rpnCapacity - expr->rpnLength < size) {
|
||||||
if (expr->nRPNCapacity >= MAXRPNLEN)
|
if (expr->rpnCapacity >= MAXRPNLEN)
|
||||||
/*
|
/*
|
||||||
* To avoid generating humongous object files, cap the
|
* To avoid generating humongous object files, cap the
|
||||||
* size of RPN expressions
|
* size of RPN expressions
|
||||||
*/
|
*/
|
||||||
fatalerror("RPN expression cannot grow larger than "
|
fatalerror("RPN expression cannot grow larger than "
|
||||||
EXPAND_AND_STR(MAXRPNLEN) " bytes\n");
|
EXPAND_AND_STR(MAXRPNLEN) " bytes\n");
|
||||||
else if (expr->nRPNCapacity > MAXRPNLEN / 2)
|
else if (expr->rpnCapacity > MAXRPNLEN / 2)
|
||||||
expr->nRPNCapacity = MAXRPNLEN;
|
expr->rpnCapacity = MAXRPNLEN;
|
||||||
else
|
else
|
||||||
expr->nRPNCapacity *= 2;
|
expr->rpnCapacity *= 2;
|
||||||
}
|
}
|
||||||
expr->tRPN = realloc(expr->tRPN, expr->nRPNCapacity);
|
expr->rpn = realloc(expr->rpn, expr->rpnCapacity);
|
||||||
|
|
||||||
if (!expr->tRPN)
|
if (!expr->rpn)
|
||||||
fatalerror("Failed to grow RPN expression: %s\n", strerror(errno));
|
fatalerror("Failed to grow RPN expression: %s\n", strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t *ptr = expr->tRPN + expr->nRPNLength;
|
uint8_t *ptr = expr->rpn + expr->rpnLength;
|
||||||
|
|
||||||
expr->nRPNLength += size;
|
expr->rpnLength += size;
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Init the RPN expression
|
* Init a RPN expression
|
||||||
*/
|
*/
|
||||||
static void rpn_Init(struct Expression *expr)
|
static void rpn_Init(struct Expression *expr)
|
||||||
{
|
{
|
||||||
expr->reason = NULL;
|
expr->reason = NULL;
|
||||||
expr->isKnown = true;
|
expr->isKnown = true;
|
||||||
expr->isSymbol = false;
|
expr->isSymbol = false;
|
||||||
expr->tRPN = NULL;
|
expr->rpn = NULL;
|
||||||
expr->nRPNCapacity = 0;
|
expr->rpnCapacity = 0;
|
||||||
expr->nRPNLength = 0;
|
expr->rpnLength = 0;
|
||||||
expr->nRPNPatchSize = 0;
|
expr->rpnPatchSize = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -90,7 +94,7 @@ static void rpn_Init(struct Expression *expr)
|
|||||||
*/
|
*/
|
||||||
void rpn_Free(struct Expression *expr)
|
void rpn_Free(struct Expression *expr)
|
||||||
{
|
{
|
||||||
free(expr->tRPN);
|
free(expr->rpn);
|
||||||
free(expr->reason);
|
free(expr->reason);
|
||||||
rpn_Init(expr);
|
rpn_Init(expr);
|
||||||
}
|
}
|
||||||
@@ -101,12 +105,12 @@ void rpn_Free(struct Expression *expr)
|
|||||||
void rpn_Number(struct Expression *expr, uint32_t i)
|
void rpn_Number(struct Expression *expr, uint32_t i)
|
||||||
{
|
{
|
||||||
rpn_Init(expr);
|
rpn_Init(expr);
|
||||||
expr->nVal = i;
|
expr->val = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
void rpn_Symbol(struct Expression *expr, char const *tzSym)
|
void rpn_Symbol(struct Expression *expr, char const *symName)
|
||||||
{
|
{
|
||||||
struct Symbol *sym = sym_FindScopedSymbol(tzSym);
|
struct Symbol *sym = sym_FindScopedSymbol(symName);
|
||||||
|
|
||||||
if (sym_IsPC(sym) && !sect_GetSymbolSection()) {
|
if (sym_IsPC(sym) && !sect_GetSymbolSection()) {
|
||||||
error("PC has no value outside a section\n");
|
error("PC has no value outside a section\n");
|
||||||
@@ -116,26 +120,16 @@ void rpn_Symbol(struct Expression *expr, char const *tzSym)
|
|||||||
expr->isSymbol = true;
|
expr->isSymbol = true;
|
||||||
|
|
||||||
makeUnknown(expr, sym_IsPC(sym) ? "PC is not constant at assembly time"
|
makeUnknown(expr, sym_IsPC(sym) ? "PC is not constant at assembly time"
|
||||||
: "'%s' is not constant at assembly time", tzSym);
|
: "'%s' is not constant at assembly time", symName);
|
||||||
sym = sym_Ref(tzSym);
|
sym = sym_Ref(symName);
|
||||||
expr->nRPNPatchSize += 5; /* 1-byte opcode + 4-byte symbol ID */
|
expr->rpnPatchSize += 5; /* 1-byte opcode + 4-byte symbol ID */
|
||||||
|
|
||||||
size_t nameLen = strlen(sym->name) + 1; /* Don't forget NUL! */
|
size_t nameLen = strlen(sym->name) + 1; /* Don't forget NUL! */
|
||||||
uint8_t *ptr = reserveSpace(expr, nameLen + 1);
|
uint8_t *ptr = reserveSpace(expr, nameLen + 1);
|
||||||
*ptr++ = RPN_SYM;
|
*ptr++ = RPN_SYM;
|
||||||
memcpy(ptr, sym->name, nameLen);
|
memcpy(ptr, sym->name, nameLen);
|
||||||
|
|
||||||
/* RGBLINK assumes PC is at the byte being computed... */
|
|
||||||
if (sym_IsPC(sym) && nPCOffset) {
|
|
||||||
struct Expression pc = *expr, offset;
|
|
||||||
|
|
||||||
rpn_Number(&offset, nPCOffset);
|
|
||||||
rpn_BinaryOp(RPN_SUB, expr, &pc, &offset);
|
|
||||||
if (!rpn_isKnown(expr))
|
|
||||||
expr->isSymbol = true;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
rpn_Number(expr, sym_GetConstantValue(tzSym));
|
rpn_Number(expr, sym_GetConstantValue(symName));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -143,21 +137,21 @@ void rpn_BankSelf(struct Expression *expr)
|
|||||||
{
|
{
|
||||||
rpn_Init(expr);
|
rpn_Init(expr);
|
||||||
|
|
||||||
if (!pCurrentSection) {
|
if (!currentSection) {
|
||||||
error("PC has no bank outside a section\n");
|
error("PC has no bank outside a section\n");
|
||||||
expr->nVal = 1;
|
expr->val = 1;
|
||||||
} else if (pCurrentSection->bank == -1) {
|
} else if (currentSection->bank == (uint32_t)-1) {
|
||||||
makeUnknown(expr, "Current section's bank is not known");
|
makeUnknown(expr, "Current section's bank is not known");
|
||||||
expr->nRPNPatchSize++;
|
expr->rpnPatchSize++;
|
||||||
*reserveSpace(expr, 1) = RPN_BANK_SELF;
|
*reserveSpace(expr, 1) = RPN_BANK_SELF;
|
||||||
} else {
|
} else {
|
||||||
expr->nVal = pCurrentSection->bank;
|
expr->val = currentSection->bank;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void rpn_BankSymbol(struct Expression *expr, char const *tzSym)
|
void rpn_BankSymbol(struct Expression *expr, char const *symName)
|
||||||
{
|
{
|
||||||
struct Symbol const *sym = sym_FindScopedSymbol(tzSym);
|
struct Symbol const *sym = sym_FindScopedSymbol(symName);
|
||||||
|
|
||||||
/* The @ symbol is treated differently. */
|
/* The @ symbol is treated differently. */
|
||||||
if (sym_IsPC(sym)) {
|
if (sym_IsPC(sym)) {
|
||||||
@@ -169,15 +163,15 @@ void rpn_BankSymbol(struct Expression *expr, char const *tzSym)
|
|||||||
if (sym && !sym_IsLabel(sym)) {
|
if (sym && !sym_IsLabel(sym)) {
|
||||||
error("BANK argument must be a label\n");
|
error("BANK argument must be a label\n");
|
||||||
} else {
|
} else {
|
||||||
sym = sym_Ref(tzSym);
|
sym = sym_Ref(symName);
|
||||||
assert(sym); // If the symbol didn't exist, it should have been created
|
assert(sym); // If the symbol didn't exist, it should have been created
|
||||||
|
|
||||||
if (sym_GetSection(sym) && sym_GetSection(sym)->bank != -1) {
|
if (sym_GetSection(sym) && sym_GetSection(sym)->bank != (uint32_t)-1) {
|
||||||
/* Symbol's section is known and bank is fixed */
|
/* Symbol's section is known and bank is fixed */
|
||||||
expr->nVal = sym_GetSection(sym)->bank;
|
expr->val = sym_GetSection(sym)->bank;
|
||||||
} else {
|
} else {
|
||||||
makeUnknown(expr, "\"%s\"'s bank is not known", tzSym);
|
makeUnknown(expr, "\"%s\"'s bank is not known", symName);
|
||||||
expr->nRPNPatchSize += 5; /* opcode + 4-byte sect ID */
|
expr->rpnPatchSize += 5; /* opcode + 4-byte sect ID */
|
||||||
|
|
||||||
size_t nameLen = strlen(sym->name) + 1; /* Room for NUL! */
|
size_t nameLen = strlen(sym->name) + 1; /* Room for NUL! */
|
||||||
uint8_t *ptr = reserveSpace(expr, nameLen + 1);
|
uint8_t *ptr = reserveSpace(expr, nameLen + 1);
|
||||||
@@ -187,24 +181,63 @@ void rpn_BankSymbol(struct Expression *expr, char const *tzSym)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void rpn_BankSection(struct Expression *expr, char const *tzSectionName)
|
void rpn_BankSection(struct Expression *expr, char const *sectionName)
|
||||||
{
|
{
|
||||||
rpn_Init(expr);
|
rpn_Init(expr);
|
||||||
|
|
||||||
struct Section *pSection = out_FindSectionByName(tzSectionName);
|
struct Section *section = sect_FindSectionByName(sectionName);
|
||||||
|
|
||||||
if (pSection && pSection->bank != -1) {
|
if (section && section->bank != (uint32_t)-1) {
|
||||||
expr->nVal = pSection->bank;
|
expr->val = section->bank;
|
||||||
} else {
|
} else {
|
||||||
makeUnknown(expr, "Section \"%s\"'s bank is not known",
|
makeUnknown(expr, "Section \"%s\"'s bank is not known", sectionName);
|
||||||
tzSectionName);
|
|
||||||
|
|
||||||
size_t nameLen = strlen(tzSectionName) + 1; /* Room for NUL! */
|
size_t nameLen = strlen(sectionName) + 1; /* Room for NUL! */
|
||||||
uint8_t *ptr = reserveSpace(expr, nameLen + 1);
|
uint8_t *ptr = reserveSpace(expr, nameLen + 1);
|
||||||
|
|
||||||
expr->nRPNPatchSize += nameLen + 1;
|
expr->rpnPatchSize += nameLen + 1;
|
||||||
*ptr++ = RPN_BANK_SECT;
|
*ptr++ = RPN_BANK_SECT;
|
||||||
memcpy(ptr, tzSectionName, nameLen);
|
memcpy(ptr, sectionName, nameLen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpn_SizeOfSection(struct Expression *expr, char const *sectionName)
|
||||||
|
{
|
||||||
|
rpn_Init(expr);
|
||||||
|
|
||||||
|
struct Section *section = sect_FindSectionByName(sectionName);
|
||||||
|
|
||||||
|
if (section && sect_IsSizeKnown(section)) {
|
||||||
|
expr->val = section->size;
|
||||||
|
} else {
|
||||||
|
makeUnknown(expr, "Section \"%s\"'s size is not known", sectionName);
|
||||||
|
|
||||||
|
size_t nameLen = strlen(sectionName) + 1; /* Room for NUL! */
|
||||||
|
uint8_t *ptr = reserveSpace(expr, nameLen + 1);
|
||||||
|
|
||||||
|
expr->rpnPatchSize += nameLen + 1;
|
||||||
|
*ptr++ = RPN_SIZEOF_SECT;
|
||||||
|
memcpy(ptr, sectionName, nameLen);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void rpn_StartOfSection(struct Expression *expr, char const *sectionName)
|
||||||
|
{
|
||||||
|
rpn_Init(expr);
|
||||||
|
|
||||||
|
struct Section *section = sect_FindSectionByName(sectionName);
|
||||||
|
|
||||||
|
if (section && section->org != (uint32_t)-1) {
|
||||||
|
expr->val = section->org;
|
||||||
|
} else {
|
||||||
|
makeUnknown(expr, "Section \"%s\"'s start is not known", sectionName);
|
||||||
|
|
||||||
|
size_t nameLen = strlen(sectionName) + 1; /* Room for NUL! */
|
||||||
|
uint8_t *ptr = reserveSpace(expr, nameLen + 1);
|
||||||
|
|
||||||
|
expr->rpnPatchSize += nameLen + 1;
|
||||||
|
*ptr++ = RPN_STARTOF_SECT;
|
||||||
|
memcpy(ptr, sectionName, nameLen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,13 +247,13 @@ void rpn_CheckHRAM(struct Expression *expr, const struct Expression *src)
|
|||||||
expr->isSymbol = false;
|
expr->isSymbol = false;
|
||||||
|
|
||||||
if (!rpn_isKnown(expr)) {
|
if (!rpn_isKnown(expr)) {
|
||||||
expr->nRPNPatchSize++;
|
expr->rpnPatchSize++;
|
||||||
*reserveSpace(expr, 1) = RPN_HRAM;
|
*reserveSpace(expr, 1) = RPN_HRAM;
|
||||||
} else if (expr->nVal >= 0xFF00 && expr->nVal <= 0xFFFF) {
|
} else if (expr->val >= 0xFF00 && expr->val <= 0xFFFF) {
|
||||||
/* That range is valid, but only keep the lower byte */
|
/* That range is valid, but only keep the lower byte */
|
||||||
expr->nVal &= 0xFF;
|
expr->val &= 0xFF;
|
||||||
} else if (expr->nVal < 0 || expr->nVal > 0xFF) {
|
} else if (expr->val < 0 || expr->val > 0xFF) {
|
||||||
error("Source address $%" PRIx32 " not between $FF00 to $FFFF\n", expr->nVal);
|
error("Source address $%" PRIx32 " not between $FF00 to $FFFF\n", expr->val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -230,73 +263,61 @@ void rpn_CheckRST(struct Expression *expr, const struct Expression *src)
|
|||||||
|
|
||||||
if (rpn_isKnown(expr)) {
|
if (rpn_isKnown(expr)) {
|
||||||
/* A valid RST address must be masked with 0x38 */
|
/* A valid RST address must be masked with 0x38 */
|
||||||
if (expr->nVal & ~0x38)
|
if (expr->val & ~0x38)
|
||||||
error("Invalid address $%" PRIx32 " for RST\n", expr->nVal);
|
error("Invalid address $%" PRIx32 " for RST\n", expr->val);
|
||||||
/* The target is in the "0x38" bits, all other bits are set */
|
/* The target is in the "0x38" bits, all other bits are set */
|
||||||
expr->nVal |= 0xC7;
|
expr->val |= 0xC7;
|
||||||
} else {
|
} else {
|
||||||
expr->nRPNPatchSize++;
|
expr->rpnPatchSize++;
|
||||||
*reserveSpace(expr, 1) = RPN_RST;
|
*reserveSpace(expr, 1) = RPN_RST;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Checks that an RPN expression's value fits within N bits (signed or unsigned)
|
||||||
|
*/
|
||||||
|
void rpn_CheckNBit(struct Expression const *expr, uint8_t n)
|
||||||
|
{
|
||||||
|
assert(n != 0); // That doesn't make sense
|
||||||
|
assert(n < CHAR_BIT * sizeof(int)); // Otherwise `1 << n` is UB
|
||||||
|
|
||||||
|
if (rpn_isKnown(expr)) {
|
||||||
|
int32_t val = expr->val;
|
||||||
|
|
||||||
|
if (val <= -(1 << n) || val >= 1 << n)
|
||||||
|
warning(WARNING_TRUNCATION_1, "Expression must be %u-bit\n", n);
|
||||||
|
else if (val < -(1 << (n - 1)))
|
||||||
|
warning(WARNING_TRUNCATION_2, "Expression must be %u-bit\n", n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t rpn_GetConstVal(struct Expression const *expr)
|
||||||
|
{
|
||||||
|
if (!rpn_isKnown(expr)) {
|
||||||
|
error("Expected constant expression: %s\n", expr->reason);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return expr->val;
|
||||||
|
}
|
||||||
|
|
||||||
void rpn_LOGNOT(struct Expression *expr, const struct Expression *src)
|
void rpn_LOGNOT(struct Expression *expr, const struct Expression *src)
|
||||||
{
|
{
|
||||||
*expr = *src;
|
*expr = *src;
|
||||||
expr->isSymbol = false;
|
expr->isSymbol = false;
|
||||||
|
|
||||||
if (rpn_isKnown(expr)) {
|
if (rpn_isKnown(expr)) {
|
||||||
expr->nVal = !expr->nVal;
|
expr->val = !expr->val;
|
||||||
} else {
|
} else {
|
||||||
expr->nRPNPatchSize++;
|
expr->rpnPatchSize++;
|
||||||
*reserveSpace(expr, 1) = RPN_LOGUNNOT;
|
*reserveSpace(expr, 1) = RPN_LOGUNNOT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int32_t shift(int32_t shiftee, int32_t amount)
|
|
||||||
{
|
|
||||||
if (amount >= 0) {
|
|
||||||
// Left shift
|
|
||||||
if (amount >= 32) {
|
|
||||||
warning(WARNING_SHIFT_AMOUNT, "Shifting left by large amount %"
|
|
||||||
PRId32 "\n", amount);
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
* Use unsigned to force a bitwise shift
|
|
||||||
* Casting back is OK because the types implement two's
|
|
||||||
* complement behavior
|
|
||||||
*/
|
|
||||||
return (uint32_t)shiftee << amount;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Right shift
|
|
||||||
amount = -amount;
|
|
||||||
if (amount >= 32) {
|
|
||||||
warning(WARNING_SHIFT_AMOUNT,
|
|
||||||
"Shifting right by large amount %" PRId32 "\n", amount);
|
|
||||||
return shiftee < 0 ? -1 : 0;
|
|
||||||
|
|
||||||
} else if (shiftee >= 0) {
|
|
||||||
return shiftee >> amount;
|
|
||||||
|
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
* The C standard leaves shifting right negative values
|
|
||||||
* undefined, so use a left shift manually sign-extended
|
|
||||||
*/
|
|
||||||
return (uint32_t)shiftee >> amount
|
|
||||||
| -(UINT32_C(1) << (32 - amount));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Symbol const *rpn_SymbolOf(struct Expression const *expr)
|
struct Symbol const *rpn_SymbolOf(struct Expression const *expr)
|
||||||
{
|
{
|
||||||
if (!rpn_isSymbol(expr))
|
if (!rpn_isSymbol(expr))
|
||||||
return NULL;
|
return NULL;
|
||||||
return sym_FindScopedSymbol((char *)expr->tRPN + 1);
|
return sym_FindScopedSymbol((char const *)expr->rpn + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool rpn_IsDiffConstant(struct Expression const *src, struct Symbol const *sym)
|
bool rpn_IsDiffConstant(struct Expression const *src, struct Symbol const *sym)
|
||||||
@@ -318,10 +339,51 @@ static bool isDiffConstant(struct Expression const *src1,
|
|||||||
return rpn_IsDiffConstant(src1, rpn_SymbolOf(src2));
|
return rpn_IsDiffConstant(src1, rpn_SymbolOf(src2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Attempts to compute a constant binary AND from non-constant operands
|
||||||
|
* This is possible if one operand is a symbol belonging to an `ALIGN[N]` section, and the other is
|
||||||
|
* a constant that only keeps (some of) the lower N bits.
|
||||||
|
*
|
||||||
|
* @return The constant result if it can be computed, or -1 otherwise.
|
||||||
|
*/
|
||||||
|
static int32_t tryConstMask(struct Expression const *lhs, struct Expression const *rhs)
|
||||||
|
{
|
||||||
|
struct Symbol const *sym = rpn_SymbolOf(lhs);
|
||||||
|
struct Expression const *expr = rhs;
|
||||||
|
|
||||||
|
if (!sym || !sym_GetSection(sym)) {
|
||||||
|
// If the lhs isn't a symbol, try again the other way around
|
||||||
|
sym = rpn_SymbolOf(rhs);
|
||||||
|
expr = lhs;
|
||||||
|
|
||||||
|
if (!sym || !sym_GetSection(sym))
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
assert(sym_IsNumeric(sym));
|
||||||
|
|
||||||
|
if (!rpn_isKnown(expr))
|
||||||
|
return -1;
|
||||||
|
// We can now safely use `expr->val`
|
||||||
|
struct Section const *sect = sym_GetSection(sym);
|
||||||
|
int32_t unknownBits = (1 << 16) - (1 << sect->align); // The max alignment is 16
|
||||||
|
|
||||||
|
// The mask must ignore all unknown bits
|
||||||
|
if ((expr->val & unknownBits) != 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
// `sym_GetValue()` attempts to add the section's address,
|
||||||
|
// but that's "-1" because the section is floating (otherwise we wouldn't be here)
|
||||||
|
assert(sect->org == (uint32_t)-1);
|
||||||
|
int32_t symbolOfs = sym_GetValue(sym) + 1;
|
||||||
|
|
||||||
|
return (symbolOfs + sect->alignOfs) & ~unknownBits;
|
||||||
|
}
|
||||||
|
|
||||||
void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
||||||
const struct Expression *src1, const struct Expression *src2)
|
const struct Expression *src1, const struct Expression *src2)
|
||||||
{
|
{
|
||||||
expr->isSymbol = false;
|
expr->isSymbol = false;
|
||||||
|
int32_t constMaskVal;
|
||||||
|
|
||||||
/* First, check if the expression is known */
|
/* First, check if the expression is known */
|
||||||
expr->isKnown = src1->isKnown && src2->isKnown;
|
expr->isKnown = src1->isKnown && src2->isKnown;
|
||||||
@@ -329,91 +391,123 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
|||||||
rpn_Init(expr); /* Init the expression to something sane */
|
rpn_Init(expr); /* Init the expression to something sane */
|
||||||
|
|
||||||
/* If both expressions are known, just compute the value */
|
/* If both expressions are known, just compute the value */
|
||||||
uint32_t uleft = src1->nVal, uright = src2->nVal;
|
uint32_t uleft = src1->val, uright = src2->val;
|
||||||
|
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case RPN_LOGOR:
|
case RPN_LOGOR:
|
||||||
expr->nVal = src1->nVal || src2->nVal;
|
expr->val = src1->val || src2->val;
|
||||||
break;
|
break;
|
||||||
case RPN_LOGAND:
|
case RPN_LOGAND:
|
||||||
expr->nVal = src1->nVal && src2->nVal;
|
expr->val = src1->val && src2->val;
|
||||||
break;
|
break;
|
||||||
case RPN_LOGEQ:
|
case RPN_LOGEQ:
|
||||||
expr->nVal = src1->nVal == src2->nVal;
|
expr->val = src1->val == src2->val;
|
||||||
break;
|
break;
|
||||||
case RPN_LOGGT:
|
case RPN_LOGGT:
|
||||||
expr->nVal = src1->nVal > src2->nVal;
|
expr->val = src1->val > src2->val;
|
||||||
break;
|
break;
|
||||||
case RPN_LOGLT:
|
case RPN_LOGLT:
|
||||||
expr->nVal = src1->nVal < src2->nVal;
|
expr->val = src1->val < src2->val;
|
||||||
break;
|
break;
|
||||||
case RPN_LOGGE:
|
case RPN_LOGGE:
|
||||||
expr->nVal = src1->nVal >= src2->nVal;
|
expr->val = src1->val >= src2->val;
|
||||||
break;
|
break;
|
||||||
case RPN_LOGLE:
|
case RPN_LOGLE:
|
||||||
expr->nVal = src1->nVal <= src2->nVal;
|
expr->val = src1->val <= src2->val;
|
||||||
break;
|
break;
|
||||||
case RPN_LOGNE:
|
case RPN_LOGNE:
|
||||||
expr->nVal = src1->nVal != src2->nVal;
|
expr->val = src1->val != src2->val;
|
||||||
break;
|
break;
|
||||||
case RPN_ADD:
|
case RPN_ADD:
|
||||||
expr->nVal = uleft + uright;
|
expr->val = uleft + uright;
|
||||||
break;
|
break;
|
||||||
case RPN_SUB:
|
case RPN_SUB:
|
||||||
expr->nVal = uleft - uright;
|
expr->val = uleft - uright;
|
||||||
break;
|
break;
|
||||||
case RPN_XOR:
|
case RPN_XOR:
|
||||||
expr->nVal = src1->nVal ^ src2->nVal;
|
expr->val = src1->val ^ src2->val;
|
||||||
break;
|
break;
|
||||||
case RPN_OR:
|
case RPN_OR:
|
||||||
expr->nVal = src1->nVal | src2->nVal;
|
expr->val = src1->val | src2->val;
|
||||||
break;
|
break;
|
||||||
case RPN_AND:
|
case RPN_AND:
|
||||||
expr->nVal = src1->nVal & src2->nVal;
|
expr->val = src1->val & src2->val;
|
||||||
break;
|
break;
|
||||||
case RPN_SHL:
|
case RPN_SHL:
|
||||||
if (src2->nVal < 0)
|
if (src2->val < 0)
|
||||||
warning(WARNING_SHIFT_AMOUNT,
|
warning(WARNING_SHIFT_AMOUNT,
|
||||||
"Shifting left by negative amount %" PRId32 "\n",
|
"Shifting left by negative amount %" PRId32 "\n",
|
||||||
src2->nVal);
|
src2->val);
|
||||||
|
|
||||||
expr->nVal = shift(src1->nVal, src2->nVal);
|
if (src2->val >= 32)
|
||||||
|
warning(WARNING_SHIFT_AMOUNT,
|
||||||
|
"Shifting left by large amount %" PRId32 "\n", src2->val);
|
||||||
|
|
||||||
|
expr->val = op_shift_left(src1->val, src2->val);
|
||||||
break;
|
break;
|
||||||
case RPN_SHR:
|
case RPN_SHR:
|
||||||
if (src1->nVal < 0)
|
if (src1->val < 0)
|
||||||
warning(WARNING_SHIFT, "Shifting negative value %" PRId32 "\n",
|
warning(WARNING_SHIFT,
|
||||||
src1->nVal);
|
"Shifting right negative value %" PRId32 "\n", src1->val);
|
||||||
|
|
||||||
if (src2->nVal < 0)
|
if (src2->val < 0)
|
||||||
warning(WARNING_SHIFT_AMOUNT,
|
warning(WARNING_SHIFT_AMOUNT,
|
||||||
"Shifting right by negative amount %" PRId32 "\n",
|
"Shifting right by negative amount %" PRId32 "\n",
|
||||||
src2->nVal);
|
src2->val);
|
||||||
|
|
||||||
expr->nVal = shift(src1->nVal, -src2->nVal);
|
if (src2->val >= 32)
|
||||||
|
warning(WARNING_SHIFT_AMOUNT,
|
||||||
|
"Shifting right by large amount %" PRId32 "\n",
|
||||||
|
src2->val);
|
||||||
|
|
||||||
|
expr->val = op_shift_right(src1->val, src2->val);
|
||||||
|
break;
|
||||||
|
case RPN_USHR:
|
||||||
|
if (src2->val < 0)
|
||||||
|
warning(WARNING_SHIFT_AMOUNT,
|
||||||
|
"Shifting right by negative amount %" PRId32 "\n",
|
||||||
|
src2->val);
|
||||||
|
|
||||||
|
if (src2->val >= 32)
|
||||||
|
warning(WARNING_SHIFT_AMOUNT,
|
||||||
|
"Shifting right by large amount %" PRId32 "\n",
|
||||||
|
src2->val);
|
||||||
|
|
||||||
|
expr->val = op_shift_right_unsigned(src1->val, src2->val);
|
||||||
break;
|
break;
|
||||||
case RPN_MUL:
|
case RPN_MUL:
|
||||||
expr->nVal = uleft * uright;
|
expr->val = uleft * uright;
|
||||||
break;
|
break;
|
||||||
case RPN_DIV:
|
case RPN_DIV:
|
||||||
if (src2->nVal == 0)
|
if (src2->val == 0)
|
||||||
fatalerror("Division by zero\n");
|
fatalerror("Division by zero\n");
|
||||||
|
|
||||||
if (src1->nVal == INT32_MIN && src2->nVal == -1) {
|
if (src1->val == INT32_MIN && src2->val == -1) {
|
||||||
warning(WARNING_DIV, "Division of %" PRId32 " by -1 yields %"
|
warning(WARNING_DIV,
|
||||||
PRId32 "\n", INT32_MIN, INT32_MIN);
|
"Division of %" PRId32 " by -1 yields %" PRId32 "\n",
|
||||||
expr->nVal = INT32_MIN;
|
INT32_MIN, INT32_MIN);
|
||||||
|
expr->val = INT32_MIN;
|
||||||
} else {
|
} else {
|
||||||
expr->nVal = src1->nVal / src2->nVal;
|
expr->val = op_divide(src1->val, src2->val);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case RPN_MOD:
|
case RPN_MOD:
|
||||||
if (src2->nVal == 0)
|
if (src2->val == 0)
|
||||||
fatalerror("Division by zero\n");
|
fatalerror("Modulo by zero\n");
|
||||||
|
|
||||||
if (src1->nVal == INT32_MIN && src2->nVal == -1)
|
if (src1->val == INT32_MIN && src2->val == -1)
|
||||||
expr->nVal = 0;
|
expr->val = 0;
|
||||||
else
|
else
|
||||||
expr->nVal = src1->nVal % src2->nVal;
|
expr->val = op_modulo(src1->val, src2->val);
|
||||||
|
break;
|
||||||
|
case RPN_EXP:
|
||||||
|
if (src2->val < 0)
|
||||||
|
fatalerror("Exponentiation by negative power\n");
|
||||||
|
|
||||||
|
if (src1->val == INT32_MIN && src2->val == -1)
|
||||||
|
expr->val = 0;
|
||||||
|
else
|
||||||
|
expr->val = op_exponent(src1->val, src2->val);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RPN_UNSUB:
|
case RPN_UNSUB:
|
||||||
@@ -422,6 +516,8 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
|||||||
case RPN_BANK_SYM:
|
case RPN_BANK_SYM:
|
||||||
case RPN_BANK_SECT:
|
case RPN_BANK_SECT:
|
||||||
case RPN_BANK_SELF:
|
case RPN_BANK_SELF:
|
||||||
|
case RPN_SIZEOF_SECT:
|
||||||
|
case RPN_STARTOF_SECT:
|
||||||
case RPN_HRAM:
|
case RPN_HRAM:
|
||||||
case RPN_RST:
|
case RPN_RST:
|
||||||
case RPN_CONST:
|
case RPN_CONST:
|
||||||
@@ -433,20 +529,23 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
|||||||
struct Symbol const *symbol1 = rpn_SymbolOf(src1);
|
struct Symbol const *symbol1 = rpn_SymbolOf(src1);
|
||||||
struct Symbol const *symbol2 = rpn_SymbolOf(src2);
|
struct Symbol const *symbol2 = rpn_SymbolOf(src2);
|
||||||
|
|
||||||
expr->nVal = sym_GetValue(symbol1) - sym_GetValue(symbol2);
|
expr->val = sym_GetValue(symbol1) - sym_GetValue(symbol2);
|
||||||
|
expr->isKnown = true;
|
||||||
|
} else if (op == RPN_AND && (constMaskVal = tryConstMask(src1, src2)) != -1) {
|
||||||
|
expr->val = constMaskVal;
|
||||||
expr->isKnown = true;
|
expr->isKnown = true;
|
||||||
} else {
|
} else {
|
||||||
/* If it's not known, start computing the RPN expression */
|
/* If it's not known, start computing the RPN expression */
|
||||||
|
|
||||||
/* Convert the left-hand expression if it's constant */
|
/* Convert the left-hand expression if it's constant */
|
||||||
if (src1->isKnown) {
|
if (src1->isKnown) {
|
||||||
uint32_t lval = src1->nVal;
|
uint32_t lval = src1->val;
|
||||||
uint8_t bytes[] = {RPN_CONST, lval, lval >> 8,
|
uint8_t bytes[] = {RPN_CONST, lval, lval >> 8,
|
||||||
lval >> 16, lval >> 24};
|
lval >> 16, lval >> 24};
|
||||||
expr->nRPNPatchSize = sizeof(bytes);
|
expr->rpnPatchSize = sizeof(bytes);
|
||||||
expr->tRPN = NULL;
|
expr->rpn = NULL;
|
||||||
expr->nRPNCapacity = 0;
|
expr->rpnCapacity = 0;
|
||||||
expr->nRPNLength = 0;
|
expr->rpnLength = 0;
|
||||||
memcpy(reserveSpace(expr, sizeof(bytes)), bytes,
|
memcpy(reserveSpace(expr, sizeof(bytes)), bytes,
|
||||||
sizeof(bytes));
|
sizeof(bytes));
|
||||||
|
|
||||||
@@ -455,21 +554,21 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
|||||||
free(src1->reason);
|
free(src1->reason);
|
||||||
} else {
|
} else {
|
||||||
/* Otherwise just reuse its RPN buffer */
|
/* Otherwise just reuse its RPN buffer */
|
||||||
expr->nRPNPatchSize = src1->nRPNPatchSize;
|
expr->rpnPatchSize = src1->rpnPatchSize;
|
||||||
expr->tRPN = src1->tRPN;
|
expr->rpn = src1->rpn;
|
||||||
expr->nRPNCapacity = src1->nRPNCapacity;
|
expr->rpnCapacity = src1->rpnCapacity;
|
||||||
expr->nRPNLength = src1->nRPNLength;
|
expr->rpnLength = src1->rpnLength;
|
||||||
expr->reason = src1->reason;
|
expr->reason = src1->reason;
|
||||||
free(src2->reason);
|
free(src2->reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now, merge the right expression into the left one */
|
/* Now, merge the right expression into the left one */
|
||||||
uint8_t *ptr = src2->tRPN; /* Pointer to the right RPN */
|
uint8_t *ptr = src2->rpn; /* Pointer to the right RPN */
|
||||||
uint32_t len = src2->nRPNLength; /* Size of the right RPN */
|
uint32_t len = src2->rpnLength; /* Size of the right RPN */
|
||||||
uint32_t patchSize = src2->nRPNPatchSize;
|
uint32_t patchSize = src2->rpnPatchSize;
|
||||||
|
|
||||||
/* If the right expression is constant, merge a shim instead */
|
/* If the right expression is constant, merge a shim instead */
|
||||||
uint32_t rval = src2->nVal;
|
uint32_t rval = src2->val;
|
||||||
uint8_t bytes[] = {RPN_CONST, rval, rval >> 8, rval >> 16,
|
uint8_t bytes[] = {RPN_CONST, rval, rval >> 8, rval >> 16,
|
||||||
rval >> 24};
|
rval >> 24};
|
||||||
if (src2->isKnown) {
|
if (src2->isKnown) {
|
||||||
@@ -483,8 +582,8 @@ void rpn_BinaryOp(enum RPNCommand op, struct Expression *expr,
|
|||||||
memcpy(buf, ptr, len);
|
memcpy(buf, ptr, len);
|
||||||
buf[len] = op;
|
buf[len] = op;
|
||||||
|
|
||||||
free(src2->tRPN); /* If there was none, this is `free(NULL)` */
|
free(src2->rpn); /* If there was none, this is `free(NULL)` */
|
||||||
expr->nRPNPatchSize += patchSize + 1;
|
expr->rpnPatchSize += patchSize + 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -494,11 +593,11 @@ void rpn_HIGH(struct Expression *expr, const struct Expression *src)
|
|||||||
expr->isSymbol = false;
|
expr->isSymbol = false;
|
||||||
|
|
||||||
if (rpn_isKnown(expr)) {
|
if (rpn_isKnown(expr)) {
|
||||||
expr->nVal = (uint32_t)expr->nVal >> 8 & 0xFF;
|
expr->val = (uint32_t)expr->val >> 8 & 0xFF;
|
||||||
} else {
|
} else {
|
||||||
uint8_t bytes[] = {RPN_CONST, 8, 0, 0, 0, RPN_SHR,
|
uint8_t bytes[] = {RPN_CONST, 8, 0, 0, 0, RPN_SHR,
|
||||||
RPN_CONST, 0xFF, 0, 0, 0, RPN_AND};
|
RPN_CONST, 0xFF, 0, 0, 0, RPN_AND};
|
||||||
expr->nRPNPatchSize += sizeof(bytes);
|
expr->rpnPatchSize += sizeof(bytes);
|
||||||
memcpy(reserveSpace(expr, sizeof(bytes)), bytes, sizeof(bytes));
|
memcpy(reserveSpace(expr, sizeof(bytes)), bytes, sizeof(bytes));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -509,11 +608,11 @@ void rpn_LOW(struct Expression *expr, const struct Expression *src)
|
|||||||
expr->isSymbol = false;
|
expr->isSymbol = false;
|
||||||
|
|
||||||
if (rpn_isKnown(expr)) {
|
if (rpn_isKnown(expr)) {
|
||||||
expr->nVal = expr->nVal & 0xFF;
|
expr->val = expr->val & 0xFF;
|
||||||
} else {
|
} else {
|
||||||
uint8_t bytes[] = {RPN_CONST, 0xFF, 0, 0, 0, RPN_AND};
|
uint8_t bytes[] = {RPN_CONST, 0xFF, 0, 0, 0, RPN_AND};
|
||||||
|
|
||||||
expr->nRPNPatchSize += sizeof(bytes);
|
expr->rpnPatchSize += sizeof(bytes);
|
||||||
memcpy(reserveSpace(expr, sizeof(bytes)), bytes, sizeof(bytes));
|
memcpy(reserveSpace(expr, sizeof(bytes)), bytes, sizeof(bytes));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -521,7 +620,7 @@ void rpn_LOW(struct Expression *expr, const struct Expression *src)
|
|||||||
void rpn_ISCONST(struct Expression *expr, const struct Expression *src)
|
void rpn_ISCONST(struct Expression *expr, const struct Expression *src)
|
||||||
{
|
{
|
||||||
rpn_Init(expr);
|
rpn_Init(expr);
|
||||||
expr->nVal = rpn_isKnown(src);
|
expr->val = rpn_isKnown(src);
|
||||||
expr->isKnown = true;
|
expr->isKnown = true;
|
||||||
expr->isSymbol = false;
|
expr->isSymbol = false;
|
||||||
}
|
}
|
||||||
@@ -532,9 +631,9 @@ void rpn_UNNEG(struct Expression *expr, const struct Expression *src)
|
|||||||
expr->isSymbol = false;
|
expr->isSymbol = false;
|
||||||
|
|
||||||
if (rpn_isKnown(expr)) {
|
if (rpn_isKnown(expr)) {
|
||||||
expr->nVal = -(uint32_t)expr->nVal;
|
expr->val = -(uint32_t)expr->val;
|
||||||
} else {
|
} else {
|
||||||
expr->nRPNPatchSize++;
|
expr->rpnPatchSize++;
|
||||||
*reserveSpace(expr, 1) = RPN_UNSUB;
|
*reserveSpace(expr, 1) = RPN_UNSUB;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -545,9 +644,9 @@ void rpn_UNNOT(struct Expression *expr, const struct Expression *src)
|
|||||||
expr->isSymbol = false;
|
expr->isSymbol = false;
|
||||||
|
|
||||||
if (rpn_isKnown(expr)) {
|
if (rpn_isKnown(expr)) {
|
||||||
expr->nVal = ~expr->nVal;
|
expr->val = ~expr->val;
|
||||||
} else {
|
} else {
|
||||||
expr->nRPNPatchSize++;
|
expr->rpnPatchSize++;
|
||||||
*reserveSpace(expr, 1) = RPN_UNNOT;
|
*reserveSpace(expr, 1) = RPN_UNNOT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+543
-330
File diff suppressed because it is too large
Load Diff
+295
-168
@@ -13,24 +13,23 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
#include <limits.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
|
||||||
#include "asm/asm.h"
|
#include "asm/fixpoint.h"
|
||||||
#include "asm/fstack.h"
|
#include "asm/fstack.h"
|
||||||
#include "asm/macro.h"
|
#include "asm/macro.h"
|
||||||
#include "asm/main.h"
|
#include "asm/main.h"
|
||||||
#include "asm/mymath.h"
|
|
||||||
#include "asm/output.h"
|
#include "asm/output.h"
|
||||||
#include "asm/section.h"
|
#include "asm/section.h"
|
||||||
#include "asm/symbol.h"
|
#include "asm/symbol.h"
|
||||||
#include "asm/util.h"
|
#include "asm/util.h"
|
||||||
#include "asm/warning.h"
|
#include "asm/warning.h"
|
||||||
|
|
||||||
#include "extern/err.h"
|
#include "error.h"
|
||||||
|
|
||||||
#include "hashmap.h"
|
#include "hashmap.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
@@ -43,12 +42,6 @@ static char savedTIME[256];
|
|||||||
static char savedDATE[256];
|
static char savedDATE[256];
|
||||||
static char savedTIMESTAMP_ISO8601_LOCAL[256];
|
static char savedTIMESTAMP_ISO8601_LOCAL[256];
|
||||||
static char savedTIMESTAMP_ISO8601_UTC[256];
|
static char savedTIMESTAMP_ISO8601_UTC[256];
|
||||||
static char savedDAY[3];
|
|
||||||
static char savedMONTH[3];
|
|
||||||
static char savedYEAR[20];
|
|
||||||
static char savedHOUR[3];
|
|
||||||
static char savedMINUTE[3];
|
|
||||||
static char savedSECOND[3];
|
|
||||||
static bool exportall;
|
static bool exportall;
|
||||||
|
|
||||||
bool sym_IsPC(struct Symbol const *sym)
|
bool sym_IsPC(struct Symbol const *sym)
|
||||||
@@ -57,16 +50,16 @@ bool sym_IsPC(struct Symbol const *sym)
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct ForEachArgs {
|
struct ForEachArgs {
|
||||||
void (*func)(struct Symbol *symbol, void *arg);
|
void (*func)(struct Symbol *sym, void *arg);
|
||||||
void *arg;
|
void *arg;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void forEachWrapper(void *_symbol, void *_argWrapper)
|
static void forEachWrapper(void *_sym, void *_argWrapper)
|
||||||
{
|
{
|
||||||
struct ForEachArgs *argWrapper = _argWrapper;
|
struct ForEachArgs *argWrapper = _argWrapper;
|
||||||
struct Symbol *symbol = _symbol;
|
struct Symbol *sym = _sym;
|
||||||
|
|
||||||
argWrapper->func(symbol, argWrapper->arg);
|
argWrapper->func(sym, argWrapper->arg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void sym_ForEach(void (*func)(struct Symbol *, void *), void *arg)
|
void sym_ForEach(void (*func)(struct Symbol *, void *), void *arg)
|
||||||
@@ -102,7 +95,6 @@ static char const *Callback__FILE__(void)
|
|||||||
char const *fileName = fstk_GetFileName();
|
char const *fileName = fstk_GetFileName();
|
||||||
size_t j = 1;
|
size_t j = 1;
|
||||||
|
|
||||||
/* TODO: is there a way for a file name to be empty? */
|
|
||||||
assert(fileName[0]);
|
assert(fileName[0]);
|
||||||
/* The assertion above ensures the loop runs at least once */
|
/* The assertion above ensures the loop runs at least once */
|
||||||
for (size_t i = 0; fileName[i]; i++, j++) {
|
for (size_t i = 0; fileName[i]; i++, j++) {
|
||||||
@@ -153,10 +145,12 @@ int32_t sym_GetValue(struct Symbol const *sym)
|
|||||||
|
|
||||||
static void dumpFilename(struct Symbol const *sym)
|
static void dumpFilename(struct Symbol const *sym)
|
||||||
{
|
{
|
||||||
if (!sym->src)
|
if (sym->src)
|
||||||
fputs("<builtin>", stderr);
|
|
||||||
else
|
|
||||||
fstk_Dump(sym->src, sym->fileLine);
|
fstk_Dump(sym->src, sym->fileLine);
|
||||||
|
else if (sym->fileLine == 0)
|
||||||
|
fputs("<command-line>", stderr);
|
||||||
|
else
|
||||||
|
fputs("<builtin>", stderr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -165,7 +159,7 @@ static void dumpFilename(struct Symbol const *sym)
|
|||||||
static void setSymbolFilename(struct Symbol *sym)
|
static void setSymbolFilename(struct Symbol *sym)
|
||||||
{
|
{
|
||||||
sym->src = fstk_GetFileStack();
|
sym->src = fstk_GetFileStack();
|
||||||
sym->fileLine = lexer_GetLineNo();
|
sym->fileLine = sym->src ? lexer_GetLineNo() : 0; // This is (NULL, 1) for built-ins
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -177,34 +171,34 @@ static void updateSymbolFilename(struct Symbol *sym)
|
|||||||
|
|
||||||
setSymbolFilename(sym);
|
setSymbolFilename(sym);
|
||||||
/* If the old node was referenced, ensure the new one is */
|
/* If the old node was referenced, ensure the new one is */
|
||||||
if (oldSrc->referenced && oldSrc->ID != -1)
|
if (oldSrc && oldSrc->referenced && oldSrc->ID != (uint32_t)-1)
|
||||||
out_RegisterNode(sym->src);
|
out_RegisterNode(sym->src);
|
||||||
/* TODO: unref the old node, and use `out_ReplaceNode` instead if deleting it */
|
/* TODO: unref the old node, and use `out_ReplaceNode` instead of deleting it */
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create a new symbol by name
|
* Create a new symbol by name
|
||||||
*/
|
*/
|
||||||
static struct Symbol *createsymbol(char const *s)
|
static struct Symbol *createsymbol(char const *symName)
|
||||||
{
|
{
|
||||||
struct Symbol *symbol = malloc(sizeof(*symbol));
|
struct Symbol *sym = malloc(sizeof(*sym));
|
||||||
|
|
||||||
if (!symbol)
|
if (!sym)
|
||||||
fatalerror("Failed to create symbol '%s': %s\n", s, strerror(errno));
|
fatalerror("Failed to create symbol '%s': %s\n", symName, strerror(errno));
|
||||||
|
|
||||||
if (snprintf(symbol->name, MAXSYMLEN + 1, "%s", s) > MAXSYMLEN)
|
if (snprintf(sym->name, MAXSYMLEN + 1, "%s", symName) > MAXSYMLEN)
|
||||||
warning(WARNING_LONG_STR, "Symbol name is too long: '%s'\n", s);
|
warning(WARNING_LONG_STR, "Symbol name is too long: '%s'\n", symName);
|
||||||
|
|
||||||
symbol->isExported = false;
|
sym->isExported = false;
|
||||||
symbol->isBuiltin = false;
|
sym->isBuiltin = false;
|
||||||
symbol->hasCallback = false;
|
sym->hasCallback = false;
|
||||||
symbol->section = NULL;
|
sym->section = NULL;
|
||||||
setSymbolFilename(symbol);
|
setSymbolFilename(sym);
|
||||||
symbol->ID = -1;
|
sym->ID = -1;
|
||||||
symbol->next = NULL;
|
sym->next = NULL;
|
||||||
|
|
||||||
hash_AddElement(symbols, symbol->name, symbol);
|
hash_AddElement(symbols, sym->name, sym);
|
||||||
return symbol;
|
return sym;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -222,37 +216,49 @@ static void fullSymbolName(char *output, size_t outputSize,
|
|||||||
fatalerror("Symbol name is too long: '%s%s'\n", scopeName, localName);
|
fatalerror("Symbol name is too long: '%s%s'\n", scopeName, localName);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Symbol *sym_FindExactSymbol(char const *name)
|
static void assignStringSymbol(struct Symbol *sym, char const *value)
|
||||||
{
|
{
|
||||||
return hash_GetElement(symbols, name);
|
char *string = strdup(value);
|
||||||
|
|
||||||
|
if (!string)
|
||||||
|
fatalerror("No memory for string equate: %s\n", strerror(errno));
|
||||||
|
|
||||||
|
sym->type = SYM_EQUS;
|
||||||
|
sym->macro = string;
|
||||||
|
sym->macroSize = strlen(string);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Symbol *sym_FindUnscopedSymbol(char const *name)
|
struct Symbol *sym_FindExactSymbol(char const *symName)
|
||||||
{
|
{
|
||||||
if (strchr(name, '.')) {
|
return hash_GetElement(symbols, symName);
|
||||||
error("Expected non-scoped symbol name, not \"%s\"\n", name);
|
}
|
||||||
|
|
||||||
|
struct Symbol *sym_FindUnscopedSymbol(char const *symName)
|
||||||
|
{
|
||||||
|
if (strchr(symName, '.')) {
|
||||||
|
error("Expected non-scoped symbol name, not \"%s\"\n", symName);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return sym_FindExactSymbol(name);
|
return sym_FindExactSymbol(symName);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Symbol *sym_FindScopedSymbol(char const *name)
|
struct Symbol *sym_FindScopedSymbol(char const *symName)
|
||||||
{
|
{
|
||||||
char const *dotPtr = strchr(name, '.');
|
char const *localName = strchr(symName, '.');
|
||||||
|
|
||||||
if (dotPtr) {
|
if (localName) {
|
||||||
if (strchr(dotPtr + 1, '.'))
|
if (strchr(localName + 1, '.'))
|
||||||
fatalerror("'%s' is a nonsensical reference to a nested local symbol\n",
|
fatalerror("'%s' is a nonsensical reference to a nested local symbol\n",
|
||||||
name);
|
symName);
|
||||||
/* If auto-scoped local label, expand the name */
|
/* If auto-scoped local label, expand the name */
|
||||||
if (dotPtr == name) { /* Meaning, the name begins with the dot */
|
if (localName == symName) { /* Meaning, the name begins with the dot */
|
||||||
char fullname[MAXSYMLEN + 1];
|
char fullName[MAXSYMLEN + 1];
|
||||||
|
|
||||||
fullSymbolName(fullname, sizeof(fullname), name, labelScope);
|
fullSymbolName(fullName, sizeof(fullName), symName, labelScope);
|
||||||
return sym_FindExactSymbol(fullname);
|
return sym_FindExactSymbol(fullName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return sym_FindExactSymbol(name);
|
return sym_FindExactSymbol(symName);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Symbol const *sym_GetPC(void)
|
struct Symbol const *sym_GetPC(void)
|
||||||
@@ -260,9 +266,9 @@ struct Symbol const *sym_GetPC(void)
|
|||||||
return PCSymbol;
|
return PCSymbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool isReferenced(struct Symbol const *sym)
|
static bool isReferenced(struct Symbol const *sym)
|
||||||
{
|
{
|
||||||
return sym->ID != -1;
|
return sym->ID != (uint32_t)-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -270,22 +276,26 @@ static inline bool isReferenced(struct Symbol const *sym)
|
|||||||
*/
|
*/
|
||||||
void sym_Purge(char const *symName)
|
void sym_Purge(char const *symName)
|
||||||
{
|
{
|
||||||
struct Symbol *symbol = sym_FindScopedSymbol(symName);
|
struct Symbol *sym = sym_FindScopedSymbol(symName);
|
||||||
|
|
||||||
if (!symbol) {
|
if (!sym) {
|
||||||
error("'%s' not defined\n", symName);
|
error("'%s' not defined\n", symName);
|
||||||
} else if (symbol->isBuiltin) {
|
} else if (sym->isBuiltin) {
|
||||||
error("Built-in symbol '%s' cannot be purged\n", symName);
|
error("Built-in symbol '%s' cannot be purged\n", symName);
|
||||||
} else if (isReferenced(symbol)) {
|
} else if (isReferenced(sym)) {
|
||||||
error("Symbol \"%s\" is referenced and thus cannot be purged\n", symName);
|
error("Symbol \"%s\" is referenced and thus cannot be purged\n", symName);
|
||||||
} else {
|
} else {
|
||||||
/* Do not keep a reference to the label's name after purging it */
|
/* Do not keep a reference to the label's name after purging it */
|
||||||
if (symbol->name == labelScope)
|
if (sym->name == labelScope)
|
||||||
labelScope = NULL;
|
sym_SetCurrentSymbolScope(NULL);
|
||||||
|
|
||||||
hash_RemoveElement(symbols, symbol->name);
|
/*
|
||||||
|
* FIXME: this leaks sym->macro for SYM_EQUS and SYM_MACRO, but this can't
|
||||||
|
* free(sym->macro) because the expansion may be purging itself.
|
||||||
|
*/
|
||||||
|
hash_RemoveElement(symbols, sym->name);
|
||||||
/* TODO: ideally, also unref the file stack nodes */
|
/* TODO: ideally, also unref the file stack nodes */
|
||||||
free(symbol);
|
free(sym);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,7 +305,7 @@ uint32_t sym_GetPCValue(void)
|
|||||||
|
|
||||||
if (!sect)
|
if (!sect)
|
||||||
error("PC has no value outside a section\n");
|
error("PC has no value outside a section\n");
|
||||||
else if (sect->org == -1)
|
else if (sect->org == (uint32_t)-1)
|
||||||
error("Expected constant PC but section is not fixed\n");
|
error("Expected constant PC but section is not fixed\n");
|
||||||
else
|
else
|
||||||
return CallbackPC();
|
return CallbackPC();
|
||||||
@@ -320,12 +330,12 @@ uint32_t sym_GetConstantSymValue(struct Symbol const *sym)
|
|||||||
/*
|
/*
|
||||||
* Return a constant symbol's value
|
* Return a constant symbol's value
|
||||||
*/
|
*/
|
||||||
uint32_t sym_GetConstantValue(char const *s)
|
uint32_t sym_GetConstantValue(char const *symName)
|
||||||
{
|
{
|
||||||
struct Symbol const *sym = sym_FindScopedSymbol(s);
|
struct Symbol const *sym = sym_FindScopedSymbol(symName);
|
||||||
|
|
||||||
if (sym == NULL)
|
if (!sym)
|
||||||
error("'%s' not defined\n", s);
|
error("'%s' not defined\n", symName);
|
||||||
else
|
else
|
||||||
return sym_GetConstantSymValue(sym);
|
return sym_GetConstantSymValue(sym);
|
||||||
|
|
||||||
@@ -346,20 +356,29 @@ void sym_SetCurrentSymbolScope(char const *newScope)
|
|||||||
* Create a symbol that will be non-relocatable and ensure that it
|
* Create a symbol that will be non-relocatable and ensure that it
|
||||||
* hasn't already been defined or referenced in a context that would
|
* hasn't already been defined or referenced in a context that would
|
||||||
* require that it be relocatable
|
* require that it be relocatable
|
||||||
|
* @param symName The name of the symbol to create
|
||||||
|
* @param numeric If false, the symbol may not have been referenced earlier
|
||||||
*/
|
*/
|
||||||
static struct Symbol *createNonrelocSymbol(char const *symbolName)
|
static struct Symbol *createNonrelocSymbol(char const *symName, bool numeric)
|
||||||
{
|
{
|
||||||
struct Symbol *symbol = sym_FindExactSymbol(symbolName);
|
struct Symbol *sym = sym_FindExactSymbol(symName);
|
||||||
|
|
||||||
if (!symbol) {
|
if (!sym) {
|
||||||
symbol = createsymbol(symbolName);
|
sym = createsymbol(symName);
|
||||||
} else if (sym_IsDefined(symbol)) {
|
} else if (sym_IsDefined(sym)) {
|
||||||
error("'%s' already defined at ", symbolName);
|
error("'%s' already defined at ", symName);
|
||||||
dumpFilename(symbol);
|
dumpFilename(sym);
|
||||||
putc('\n', stderr);
|
putc('\n', stderr);
|
||||||
|
return NULL; // Don't allow overriding the symbol, that'd be bad!
|
||||||
|
} else if (!numeric) {
|
||||||
|
// The symbol has already been referenced, but it's not allowed
|
||||||
|
error("'%s' already referenced at ", symName);
|
||||||
|
dumpFilename(sym);
|
||||||
|
putc('\n', stderr);
|
||||||
|
return NULL; // Don't allow overriding the symbol, that'd be bad!
|
||||||
}
|
}
|
||||||
|
|
||||||
return symbol;
|
return sym;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -367,8 +386,35 @@ static struct Symbol *createNonrelocSymbol(char const *symbolName)
|
|||||||
*/
|
*/
|
||||||
struct Symbol *sym_AddEqu(char const *symName, int32_t value)
|
struct Symbol *sym_AddEqu(char const *symName, int32_t value)
|
||||||
{
|
{
|
||||||
struct Symbol *sym = createNonrelocSymbol(symName);
|
struct Symbol *sym = createNonrelocSymbol(symName, true);
|
||||||
|
|
||||||
|
if (!sym)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
sym->type = SYM_EQU;
|
||||||
|
sym->value = value;
|
||||||
|
|
||||||
|
return sym;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Symbol *sym_RedefEqu(char const *symName, int32_t value)
|
||||||
|
{
|
||||||
|
struct Symbol *sym = sym_FindExactSymbol(symName);
|
||||||
|
|
||||||
|
if (!sym)
|
||||||
|
return sym_AddEqu(symName, value);
|
||||||
|
|
||||||
|
if (sym_IsDefined(sym) && sym->type != SYM_EQU) {
|
||||||
|
error("'%s' already defined as non-EQU at ", symName);
|
||||||
|
dumpFilename(sym);
|
||||||
|
putc('\n', stderr);
|
||||||
|
return NULL;
|
||||||
|
} else if (sym->isBuiltin) {
|
||||||
|
error("Built-in symbol '%s' cannot be redefined\n", symName);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSymbolFilename(sym);
|
||||||
sym->type = SYM_EQU;
|
sym->type = SYM_EQU;
|
||||||
sym->value = value;
|
sym->value = value;
|
||||||
|
|
||||||
@@ -389,32 +435,55 @@ struct Symbol *sym_AddEqu(char const *symName, int32_t value)
|
|||||||
*/
|
*/
|
||||||
struct Symbol *sym_AddString(char const *symName, char const *value)
|
struct Symbol *sym_AddString(char const *symName, char const *value)
|
||||||
{
|
{
|
||||||
struct Symbol *sym = createNonrelocSymbol(symName);
|
struct Symbol *sym = createNonrelocSymbol(symName, false);
|
||||||
size_t len = strlen(value);
|
|
||||||
char *string = malloc(len + 1);
|
|
||||||
|
|
||||||
if (string == NULL)
|
if (!sym)
|
||||||
fatalerror("No memory for string equate: %s\n", strerror(errno));
|
return NULL;
|
||||||
strcpy(string, value);
|
|
||||||
|
|
||||||
sym->type = SYM_EQUS;
|
assignStringSymbol(sym, value);
|
||||||
/* TODO: use other fields */
|
return sym;
|
||||||
sym->macroSize = len;
|
}
|
||||||
sym->macro = string;
|
|
||||||
|
struct Symbol *sym_RedefString(char const *symName, char const *value)
|
||||||
|
{
|
||||||
|
struct Symbol *sym = sym_FindExactSymbol(symName);
|
||||||
|
|
||||||
|
if (!sym)
|
||||||
|
return sym_AddString(symName, value);
|
||||||
|
|
||||||
|
if (sym->type != SYM_EQUS) {
|
||||||
|
if (sym_IsDefined(sym))
|
||||||
|
error("'%s' already defined as non-EQUS at ", symName);
|
||||||
|
else
|
||||||
|
error("'%s' already referenced at ", symName);
|
||||||
|
dumpFilename(sym);
|
||||||
|
putc('\n', stderr);
|
||||||
|
return NULL;
|
||||||
|
} else if (sym->isBuiltin) {
|
||||||
|
error("Built-in symbol '%s' cannot be redefined\n", symName);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateSymbolFilename(sym);
|
||||||
|
/*
|
||||||
|
* FIXME: this leaks the previous sym->macro value, but this can't
|
||||||
|
* free(sym->macro) because the expansion may be redefining itself.
|
||||||
|
*/
|
||||||
|
assignStringSymbol(sym, value);
|
||||||
|
|
||||||
return sym;
|
return sym;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Alter a SET symbols value
|
* Alter a mutable symbol's value
|
||||||
*/
|
*/
|
||||||
struct Symbol *sym_AddSet(char const *symName, int32_t value)
|
struct Symbol *sym_AddVar(char const *symName, int32_t value)
|
||||||
{
|
{
|
||||||
struct Symbol *sym = sym_FindExactSymbol(symName);
|
struct Symbol *sym = sym_FindExactSymbol(symName);
|
||||||
|
|
||||||
if (sym == NULL) {
|
if (!sym) {
|
||||||
sym = createsymbol(symName);
|
sym = createsymbol(symName);
|
||||||
} else if (sym_IsDefined(sym) && sym->type != SYM_SET) {
|
} else if (sym_IsDefined(sym) && sym->type != SYM_VAR) {
|
||||||
error("'%s' already defined as %s at ",
|
error("'%s' already defined as %s at ",
|
||||||
symName, sym->type == SYM_LABEL ? "label" : "constant");
|
symName, sym->type == SYM_LABEL ? "label" : "constant");
|
||||||
dumpFilename(sym);
|
dumpFilename(sym);
|
||||||
@@ -424,7 +493,7 @@ struct Symbol *sym_AddSet(char const *symName, int32_t value)
|
|||||||
updateSymbolFilename(sym);
|
updateSymbolFilename(sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
sym->type = SYM_SET;
|
sym->type = SYM_VAR;
|
||||||
sym->value = value;
|
sym->value = value;
|
||||||
|
|
||||||
return sym;
|
return sym;
|
||||||
@@ -432,18 +501,18 @@ struct Symbol *sym_AddSet(char const *symName, int32_t value)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Add a label (aka "relocatable symbol")
|
* Add a label (aka "relocatable symbol")
|
||||||
* @param name The label's full name (so `.name` is invalid)
|
* @param symName The label's full name (so `.name` is invalid)
|
||||||
* @return The created symbol
|
* @return The created symbol
|
||||||
*/
|
*/
|
||||||
static struct Symbol *addLabel(char const *name)
|
static struct Symbol *addLabel(char const *symName)
|
||||||
{
|
{
|
||||||
assert(name[0] != '.'); /* The symbol name must have been expanded prior */
|
assert(symName[0] != '.'); /* The symbol name must have been expanded prior */
|
||||||
struct Symbol *sym = sym_FindExactSymbol(name);
|
struct Symbol *sym = sym_FindExactSymbol(symName);
|
||||||
|
|
||||||
if (!sym) {
|
if (!sym) {
|
||||||
sym = createsymbol(name);
|
sym = createsymbol(symName);
|
||||||
} else if (sym_IsDefined(sym)) {
|
} else if (sym_IsDefined(sym)) {
|
||||||
error("'%s' already defined at ", name);
|
error("'%s' already defined at ", symName);
|
||||||
dumpFilename(sym);
|
dumpFilename(sym);
|
||||||
putc('\n', stderr);
|
putc('\n', stderr);
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -458,68 +527,125 @@ static struct Symbol *addLabel(char const *name)
|
|||||||
sym->section = sect_GetSymbolSection();
|
sym->section = sect_GetSymbolSection();
|
||||||
|
|
||||||
if (sym && !sym->section)
|
if (sym && !sym->section)
|
||||||
error("Label \"%s\" created outside of a SECTION\n", name);
|
error("Label \"%s\" created outside of a SECTION\n", symName);
|
||||||
return sym;
|
return sym;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add a local (.name or Parent.name) relocatable symbol
|
* Add a local (`.name` or `Parent.name`) relocatable symbol
|
||||||
*/
|
*/
|
||||||
struct Symbol *sym_AddLocalLabel(char const *name)
|
struct Symbol *sym_AddLocalLabel(char const *symName)
|
||||||
{
|
{
|
||||||
if (!labelScope) {
|
if (!labelScope) {
|
||||||
error("Local label '%s' in main scope\n", name);
|
error("Local label '%s' in main scope\n", symName);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
assert(!strchr(labelScope, '.')); /* Assuming no dots in `labelScope` */
|
||||||
|
|
||||||
char fullname[MAXSYMLEN + 1];
|
char fullName[MAXSYMLEN + 1];
|
||||||
|
char const *localName = strchr(symName, '.');
|
||||||
|
|
||||||
if (name[0] == '.') {
|
assert(localName); /* There should be at least one dot in `symName` */
|
||||||
/* If symbol is of the form `.name`, expand to the full `Parent.name` name */
|
/* Check for something after the dot in `localName` */
|
||||||
fullSymbolName(fullname, sizeof(fullname), name, labelScope);
|
if (localName[1] == '\0') {
|
||||||
name = fullname; /* Use the expanded name instead */
|
fatalerror("'%s' is a nonsensical reference to an empty local label\n",
|
||||||
|
symName);
|
||||||
|
}
|
||||||
|
/* Check for more than one dot in `localName` */
|
||||||
|
if (strchr(localName + 1, '.'))
|
||||||
|
fatalerror("'%s' is a nonsensical reference to a nested local label\n",
|
||||||
|
symName);
|
||||||
|
|
||||||
|
if (localName == symName) {
|
||||||
|
/* Expand `symName` to the full `labelScope.symName` name */
|
||||||
|
fullSymbolName(fullName, sizeof(fullName), symName, labelScope);
|
||||||
|
symName = fullName;
|
||||||
} else {
|
} else {
|
||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
|
|
||||||
/* Otherwise, check that `Parent` is in fact the current scope */
|
/* Find where `labelScope` and `symName` first differ */
|
||||||
while (labelScope[i] && name[i] == labelScope[i])
|
while (labelScope[i] && symName[i] == labelScope[i])
|
||||||
i++;
|
i++;
|
||||||
/* Assuming no dots in `labelScope` */
|
|
||||||
assert(strchr(&name[i], '.')); /* There should be at least one dot, though */
|
|
||||||
size_t parentLen = i + (strchr(&name[i], '.') - name);
|
|
||||||
|
|
||||||
/*
|
/* Check that `symName` starts with `labelScope` and then a '.' */
|
||||||
* Check that `labelScope[i]` ended the check, guaranteeing that `name` is at least
|
if (labelScope[i] != '\0' || symName[i] != '.') {
|
||||||
* as long, and then that this was the entirety of the `Parent` part of `name`.
|
size_t parentLen = localName - symName;
|
||||||
*/
|
|
||||||
if (labelScope[i] != '\0' || name[i] != '.')
|
assert(parentLen <= INT_MAX);
|
||||||
error("Not currently in the scope of '%.*s'\n", parentLen, name);
|
error("Not currently in the scope of '%.*s'\n", (int)parentLen, symName);
|
||||||
if (strchr(&name[parentLen + 1], '.')) /* There will at least be a terminator */
|
}
|
||||||
fatalerror("'%s' is a nonsensical reference to a nested local label\n",
|
|
||||||
name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return addLabel(name);
|
return addLabel(symName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Add a relocatable symbol
|
* Add a relocatable symbol
|
||||||
*/
|
*/
|
||||||
struct Symbol *sym_AddLabel(char const *name)
|
struct Symbol *sym_AddLabel(char const *symName)
|
||||||
{
|
{
|
||||||
struct Symbol *sym = addLabel(name);
|
struct Symbol *sym = addLabel(symName);
|
||||||
|
|
||||||
/* Set the symbol as the new scope */
|
/* Set the symbol as the new scope */
|
||||||
if (sym)
|
if (sym)
|
||||||
labelScope = sym->name;
|
sym_SetCurrentSymbolScope(sym->name);
|
||||||
return sym;
|
return sym;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint32_t anonLabelID;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Add an anonymous label
|
||||||
|
*/
|
||||||
|
struct Symbol *sym_AddAnonLabel(void)
|
||||||
|
{
|
||||||
|
if (anonLabelID == UINT32_MAX) {
|
||||||
|
error("Only %" PRIu32 " anonymous labels can be created!", anonLabelID);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
char name[MAXSYMLEN + 1];
|
||||||
|
|
||||||
|
sym_WriteAnonLabelName(name, 0, true); // The direction is important!!
|
||||||
|
anonLabelID++;
|
||||||
|
return addLabel(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write an anonymous label's name to a buffer
|
||||||
|
*/
|
||||||
|
void sym_WriteAnonLabelName(char buf[MIN_NB_ELMS(MAXSYMLEN + 1)], uint32_t ofs, bool neg)
|
||||||
|
{
|
||||||
|
uint32_t id = 0;
|
||||||
|
|
||||||
|
if (neg) {
|
||||||
|
if (ofs > anonLabelID)
|
||||||
|
error("Reference to anonymous label %" PRIu32 " before, when only %" PRIu32
|
||||||
|
" ha%s been created so far\n",
|
||||||
|
ofs, anonLabelID, anonLabelID == 1 ? "s" : "ve");
|
||||||
|
else
|
||||||
|
id = anonLabelID - ofs;
|
||||||
|
} else {
|
||||||
|
ofs--; // We're referencing symbols that haven't been created yet...
|
||||||
|
if (ofs > UINT32_MAX - anonLabelID)
|
||||||
|
error("Reference to anonymous label %" PRIu32 " after, when only %" PRIu32
|
||||||
|
" may still be created\n", ofs + 1, UINT32_MAX - anonLabelID);
|
||||||
|
else
|
||||||
|
id = anonLabelID + ofs;
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf(buf, "!%u", id);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Export a symbol
|
* Export a symbol
|
||||||
*/
|
*/
|
||||||
void sym_Export(char const *symName)
|
void sym_Export(char const *symName)
|
||||||
{
|
{
|
||||||
|
if (symName[0] == '!') {
|
||||||
|
error("Anonymous labels cannot be exported\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
struct Symbol *sym = sym_FindScopedSymbol(symName);
|
struct Symbol *sym = sym_FindScopedSymbol(symName);
|
||||||
|
|
||||||
/* If the symbol doesn't exist, create a ref that can be purged */
|
/* If the symbol doesn't exist, create a ref that can be purged */
|
||||||
@@ -533,7 +659,10 @@ void sym_Export(char const *symName)
|
|||||||
*/
|
*/
|
||||||
struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char *body, size_t size)
|
struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char *body, size_t size)
|
||||||
{
|
{
|
||||||
struct Symbol *sym = createNonrelocSymbol(symName);
|
struct Symbol *sym = createNonrelocSymbol(symName, false);
|
||||||
|
|
||||||
|
if (!sym)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
sym->type = SYM_MACRO;
|
sym->type = SYM_MACRO;
|
||||||
sym->macroSize = size;
|
sym->macroSize = size;
|
||||||
@@ -554,9 +683,9 @@ struct Symbol *sym_AddMacro(char const *symName, int32_t defLineNo, char *body,
|
|||||||
*/
|
*/
|
||||||
struct Symbol *sym_Ref(char const *symName)
|
struct Symbol *sym_Ref(char const *symName)
|
||||||
{
|
{
|
||||||
struct Symbol *nsym = sym_FindScopedSymbol(symName);
|
struct Symbol *sym = sym_FindScopedSymbol(symName);
|
||||||
|
|
||||||
if (nsym == NULL) {
|
if (!sym) {
|
||||||
char fullname[MAXSYMLEN + 1];
|
char fullname[MAXSYMLEN + 1];
|
||||||
|
|
||||||
if (symName[0] == '.') {
|
if (symName[0] == '.') {
|
||||||
@@ -566,11 +695,11 @@ struct Symbol *sym_Ref(char const *symName)
|
|||||||
symName = fullname;
|
symName = fullname;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsym = createsymbol(symName);
|
sym = createsymbol(symName);
|
||||||
nsym->type = SYM_REF;
|
sym->type = SYM_REF;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nsym;
|
return sym;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -581,32 +710,21 @@ void sym_SetExportAll(bool set)
|
|||||||
exportall = set;
|
exportall = set;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static struct Symbol *createBuiltinSymbol(char const *symName)
|
||||||
* Returns a pointer to the first non-zero character in a string
|
|
||||||
* Non-'0', not non-'\0'.
|
|
||||||
*/
|
|
||||||
static inline char const *removeLeadingZeros(char const *ptr)
|
|
||||||
{
|
{
|
||||||
while (*ptr == '0')
|
struct Symbol *sym = createsymbol(symName);
|
||||||
ptr++;
|
|
||||||
return ptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline struct Symbol *createBuiltinSymbol(char const *name)
|
|
||||||
{
|
|
||||||
struct Symbol *sym = createsymbol(name);
|
|
||||||
|
|
||||||
sym->isBuiltin = true;
|
sym->isBuiltin = true;
|
||||||
sym->hasCallback = true;
|
sym->hasCallback = true;
|
||||||
sym->src = NULL;
|
sym->src = NULL;
|
||||||
sym->fileLine = 0;
|
sym->fileLine = 1; // This is 0 for CLI-defined symbols
|
||||||
return sym;
|
return sym;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize the symboltable
|
* Initialize the symboltable
|
||||||
*/
|
*/
|
||||||
void sym_Init(void)
|
void sym_Init(time_t now)
|
||||||
{
|
{
|
||||||
PCSymbol = createBuiltinSymbol("@");
|
PCSymbol = createBuiltinSymbol("@");
|
||||||
struct Symbol *_NARGSymbol = createBuiltinSymbol("_NARG");
|
struct Symbol *_NARGSymbol = createBuiltinSymbol("_NARG");
|
||||||
@@ -622,13 +740,19 @@ void sym_Init(void)
|
|||||||
__LINE__Symbol->numCallback = Callback__LINE__;
|
__LINE__Symbol->numCallback = Callback__LINE__;
|
||||||
__FILE__Symbol->type = SYM_EQUS;
|
__FILE__Symbol->type = SYM_EQUS;
|
||||||
__FILE__Symbol->strCallback = Callback__FILE__;
|
__FILE__Symbol->strCallback = Callback__FILE__;
|
||||||
sym_AddSet("_RS", 0)->isBuiltin = true;
|
|
||||||
|
|
||||||
sym_AddEqu("__RGBDS_MAJOR__", PACKAGE_VERSION_MAJOR)->isBuiltin = true;
|
sym_AddVar("_RS", 0)->isBuiltin = true;
|
||||||
sym_AddEqu("__RGBDS_MINOR__", PACKAGE_VERSION_MINOR)->isBuiltin = true;
|
|
||||||
sym_AddEqu("__RGBDS_PATCH__", PACKAGE_VERSION_PATCH)->isBuiltin = true;
|
|
||||||
|
|
||||||
time_t now = time(NULL);
|
#define addNumber(name, val) sym_AddEqu(name, val)->isBuiltin = true
|
||||||
|
#define addString(name, val) sym_AddString(name, val)->isBuiltin = true
|
||||||
|
|
||||||
|
addString("__RGBDS_VERSION__", get_package_version_string());
|
||||||
|
addNumber("__RGBDS_MAJOR__", PACKAGE_VERSION_MAJOR);
|
||||||
|
addNumber("__RGBDS_MINOR__", PACKAGE_VERSION_MINOR);
|
||||||
|
addNumber("__RGBDS_PATCH__", PACKAGE_VERSION_PATCH);
|
||||||
|
#ifdef PACKAGE_VERSION_RC
|
||||||
|
addNumber("__RGBDS_RC__", PACKAGE_VERSION_RC);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (now == (time_t)-1) {
|
if (now == (time_t)-1) {
|
||||||
warn("Couldn't determine current time");
|
warn("Couldn't determine current time");
|
||||||
@@ -636,7 +760,6 @@ void sym_Init(void)
|
|||||||
now = 0;
|
now = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct tm *time_utc = gmtime(&now);
|
|
||||||
const struct tm *time_local = localtime(&now);
|
const struct tm *time_local = localtime(&now);
|
||||||
|
|
||||||
strftime(savedTIME, sizeof(savedTIME), "\"%H:%M:%S\"", time_local);
|
strftime(savedTIME, sizeof(savedTIME), "\"%H:%M:%S\"", time_local);
|
||||||
@@ -645,32 +768,36 @@ void sym_Init(void)
|
|||||||
sizeof(savedTIMESTAMP_ISO8601_LOCAL), "\"%Y-%m-%dT%H:%M:%S%z\"",
|
sizeof(savedTIMESTAMP_ISO8601_LOCAL), "\"%Y-%m-%dT%H:%M:%S%z\"",
|
||||||
time_local);
|
time_local);
|
||||||
|
|
||||||
|
const struct tm *time_utc = gmtime(&now);
|
||||||
|
|
||||||
strftime(savedTIMESTAMP_ISO8601_UTC,
|
strftime(savedTIMESTAMP_ISO8601_UTC,
|
||||||
sizeof(savedTIMESTAMP_ISO8601_UTC), "\"%Y-%m-%dT%H:%M:%SZ\"",
|
sizeof(savedTIMESTAMP_ISO8601_UTC), "\"%Y-%m-%dT%H:%M:%SZ\"",
|
||||||
time_utc);
|
time_utc);
|
||||||
|
|
||||||
strftime(savedYEAR, sizeof(savedYEAR), "%Y", time_utc);
|
|
||||||
strftime(savedMONTH, sizeof(savedMONTH), "%m", time_utc);
|
|
||||||
strftime(savedDAY, sizeof(savedDAY), "%d", time_utc);
|
|
||||||
strftime(savedHOUR, sizeof(savedHOUR), "%H", time_utc);
|
|
||||||
strftime(savedMINUTE, sizeof(savedMINUTE), "%M", time_utc);
|
|
||||||
strftime(savedSECOND, sizeof(savedSECOND), "%S", time_utc);
|
|
||||||
|
|
||||||
#define addString(name, val) sym_AddString(name, val)->isBuiltin = true
|
|
||||||
addString("__TIME__", savedTIME);
|
addString("__TIME__", savedTIME);
|
||||||
addString("__DATE__", savedDATE);
|
addString("__DATE__", savedDATE);
|
||||||
addString("__ISO_8601_LOCAL__", savedTIMESTAMP_ISO8601_LOCAL);
|
addString("__ISO_8601_LOCAL__", savedTIMESTAMP_ISO8601_LOCAL);
|
||||||
addString("__ISO_8601_UTC__", savedTIMESTAMP_ISO8601_UTC);
|
addString("__ISO_8601_UTC__", savedTIMESTAMP_ISO8601_UTC);
|
||||||
/* This cannot start with zeros */
|
|
||||||
addString("__UTC_YEAR__", savedYEAR);
|
addNumber("__UTC_YEAR__", time_utc->tm_year + 1900);
|
||||||
addString("__UTC_MONTH__", removeLeadingZeros(savedMONTH));
|
addNumber("__UTC_MONTH__", time_utc->tm_mon + 1);
|
||||||
addString("__UTC_DAY__", removeLeadingZeros(savedDAY));
|
addNumber("__UTC_DAY__", time_utc->tm_mday);
|
||||||
addString("__UTC_HOUR__", removeLeadingZeros(savedHOUR));
|
addNumber("__UTC_HOUR__", time_utc->tm_hour);
|
||||||
addString("__UTC_MINUTE__", removeLeadingZeros(savedMINUTE));
|
addNumber("__UTC_MINUTE__", time_utc->tm_min);
|
||||||
addString("__UTC_SECOND__", removeLeadingZeros(savedSECOND));
|
addNumber("__UTC_SECOND__", time_utc->tm_sec);
|
||||||
|
|
||||||
|
#undef addNumber
|
||||||
#undef addString
|
#undef addString
|
||||||
|
|
||||||
labelScope = NULL;
|
sym_SetCurrentSymbolScope(NULL);
|
||||||
|
anonLabelID = 0;
|
||||||
|
|
||||||
math_DefinePI();
|
/* _PI is deprecated */
|
||||||
|
struct Symbol *_PISymbol = createBuiltinSymbol("_PI");
|
||||||
|
|
||||||
|
_PISymbol->type = SYM_EQU;
|
||||||
|
_PISymbol->src = NULL;
|
||||||
|
_PISymbol->fileLine = 0;
|
||||||
|
_PISymbol->hasCallback = true;
|
||||||
|
_PISymbol->numCallback = fix_Callback_PI;
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-25
@@ -15,50 +15,45 @@
|
|||||||
|
|
||||||
#include "extern/utf8decoder.h"
|
#include "extern/utf8decoder.h"
|
||||||
|
|
||||||
/*
|
char const *printChar(int c)
|
||||||
* Calculate the hash value for a string
|
|
||||||
*/
|
|
||||||
uint32_t calchash(const char *s)
|
|
||||||
{
|
{
|
||||||
uint32_t hash = 5381;
|
// "'A'" + '\0': 4 bytes
|
||||||
|
// "'\\n'" + '\0': 5 bytes
|
||||||
while (*s != 0)
|
// "0xFF" + '\0': 5 bytes
|
||||||
hash = (hash * 33) ^ (*s++);
|
static char buf[5];
|
||||||
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
char const *print(int c)
|
|
||||||
{
|
|
||||||
static char buf[5]; /* '\xNN' + '\0' */
|
|
||||||
|
|
||||||
if (c == EOF)
|
if (c == EOF)
|
||||||
return "EOF";
|
return "EOF";
|
||||||
|
|
||||||
if (isprint(c)) {
|
if (isprint(c)) {
|
||||||
buf[0] = c;
|
buf[0] = '\'';
|
||||||
buf[1] = '\0';
|
buf[1] = c;
|
||||||
|
buf[2] = '\'';
|
||||||
|
buf[3] = '\0';
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
buf[0] = '\\';
|
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case '\n':
|
case '\n':
|
||||||
buf[1] = 'n';
|
buf[2] = 'n';
|
||||||
break;
|
break;
|
||||||
case '\r':
|
case '\r':
|
||||||
buf[1] = 'r';
|
buf[2] = 'r';
|
||||||
break;
|
break;
|
||||||
case '\t':
|
case '\t':
|
||||||
buf[1] = 't';
|
buf[2] = 't';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default: /* Print as hex */
|
default: /* Print as hex */
|
||||||
|
buf[0] = '0';
|
||||||
buf[1] = 'x';
|
buf[1] = 'x';
|
||||||
sprintf(&buf[2], "%02hhx", c);
|
snprintf(&buf[2], 3, "%02hhX", (uint8_t)c); // includes the '\0'
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
buf[2] = '\0';
|
buf[0] = '\'';
|
||||||
|
buf[1] = '\\';
|
||||||
|
buf[3] = '\'';
|
||||||
|
buf[4] = '\0';
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,9 +65,10 @@ size_t readUTF8Char(uint8_t *dest, char const *src)
|
|||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (decode(&state, &codep, src[i]) == 1)
|
if (decode(&state, &codep, src[i]) == 1)
|
||||||
fatalerror("invalid UTF-8 character\n");
|
return 0;
|
||||||
|
|
||||||
dest[i] = src[i];
|
if (dest)
|
||||||
|
dest[i] = src[i];
|
||||||
i++;
|
i++;
|
||||||
|
|
||||||
if (state == 0)
|
if (state == 0)
|
||||||
|
|||||||
+162
-43
@@ -6,47 +6,49 @@
|
|||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#include "asm/fstack.h"
|
#include "asm/fstack.h"
|
||||||
#include "asm/main.h"
|
#include "asm/main.h"
|
||||||
#include "asm/warning.h"
|
#include "asm/warning.h"
|
||||||
|
|
||||||
#include "extern/err.h"
|
#include "error.h"
|
||||||
|
|
||||||
unsigned int nbErrors = 0;
|
unsigned int nbErrors = 0;
|
||||||
|
|
||||||
enum WarningState {
|
static const enum WarningState defaultWarnings[ARRAY_SIZE(warningStates)] = {
|
||||||
WARNING_DEFAULT,
|
|
||||||
WARNING_DISABLED,
|
|
||||||
WARNING_ENABLED,
|
|
||||||
WARNING_ERROR
|
|
||||||
};
|
|
||||||
|
|
||||||
static enum WarningState const defaultWarnings[NB_WARNINGS] = {
|
|
||||||
[WARNING_ASSERT] = WARNING_ENABLED,
|
[WARNING_ASSERT] = WARNING_ENABLED,
|
||||||
|
[WARNING_BACKWARDS_FOR] = WARNING_DISABLED,
|
||||||
[WARNING_BUILTIN_ARG] = WARNING_DISABLED,
|
[WARNING_BUILTIN_ARG] = WARNING_DISABLED,
|
||||||
[WARNING_CHARMAP_REDEF] = WARNING_DISABLED,
|
[WARNING_CHARMAP_REDEF] = WARNING_DISABLED,
|
||||||
[WARNING_DIV] = WARNING_DISABLED,
|
[WARNING_DIV] = WARNING_DISABLED,
|
||||||
[WARNING_EMPTY_DATA_DIRECTIVE] = WARNING_DISABLED,
|
[WARNING_EMPTY_DATA_DIRECTIVE] = WARNING_DISABLED,
|
||||||
[WARNING_EMPTY_ENTRY] = WARNING_DISABLED,
|
[WARNING_EMPTY_MACRO_ARG] = WARNING_DISABLED,
|
||||||
|
[WARNING_EMPTY_STRRPL] = WARNING_DISABLED,
|
||||||
[WARNING_LARGE_CONSTANT] = WARNING_DISABLED,
|
[WARNING_LARGE_CONSTANT] = WARNING_DISABLED,
|
||||||
[WARNING_LONG_STR] = WARNING_DISABLED,
|
[WARNING_LONG_STR] = WARNING_DISABLED,
|
||||||
|
[WARNING_MACRO_SHIFT] = WARNING_DISABLED,
|
||||||
[WARNING_NESTED_COMMENT] = WARNING_ENABLED,
|
[WARNING_NESTED_COMMENT] = WARNING_ENABLED,
|
||||||
[WARNING_OBSOLETE] = WARNING_ENABLED,
|
[WARNING_OBSOLETE] = WARNING_ENABLED,
|
||||||
[WARNING_SHIFT] = WARNING_DISABLED,
|
[WARNING_SHIFT] = WARNING_DISABLED,
|
||||||
[WARNING_SHIFT_AMOUNT] = WARNING_DISABLED,
|
[WARNING_SHIFT_AMOUNT] = WARNING_DISABLED,
|
||||||
[WARNING_TRUNCATION] = WARNING_ENABLED,
|
|
||||||
[WARNING_USER] = WARNING_ENABLED,
|
[WARNING_USER] = WARNING_ENABLED,
|
||||||
|
|
||||||
|
[WARNING_NUMERIC_STRING_1] = WARNING_ENABLED,
|
||||||
|
[WARNING_NUMERIC_STRING_2] = WARNING_DISABLED,
|
||||||
|
[WARNING_TRUNCATION_1] = WARNING_ENABLED,
|
||||||
|
[WARNING_TRUNCATION_2] = WARNING_DISABLED,
|
||||||
};
|
};
|
||||||
|
|
||||||
static enum WarningState warningStates[NB_WARNINGS];
|
enum WarningState warningStates[ARRAY_SIZE(warningStates)];
|
||||||
|
|
||||||
static bool warningsAreErrors; /* Set if `-Werror` was specified */
|
bool warningsAreErrors; /* Set if `-Werror` was specified */
|
||||||
|
|
||||||
static enum WarningState warningState(enum WarningID id)
|
static enum WarningState warningState(enum WarningID id)
|
||||||
{
|
{
|
||||||
@@ -67,62 +69,132 @@ static enum WarningState warningState(enum WarningID id)
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
static char const *warningFlags[NB_WARNINGS_ALL] = {
|
static const char * const warningFlags[NB_WARNINGS] = {
|
||||||
"assert",
|
"assert",
|
||||||
|
"backwards-for",
|
||||||
"builtin-args",
|
"builtin-args",
|
||||||
"charmap-redef",
|
"charmap-redef",
|
||||||
"div",
|
"div",
|
||||||
"empty-data-directive",
|
"empty-data-directive",
|
||||||
"empty-entry",
|
"empty-macro-arg",
|
||||||
|
"empty-strrpl",
|
||||||
"large-constant",
|
"large-constant",
|
||||||
"long-string",
|
"long-string",
|
||||||
|
"macro-shift",
|
||||||
"nested-comment",
|
"nested-comment",
|
||||||
"obsolete",
|
"obsolete",
|
||||||
"shift",
|
"shift",
|
||||||
"shift-amount",
|
"shift-amount",
|
||||||
"truncation",
|
|
||||||
"user",
|
"user",
|
||||||
|
|
||||||
|
// Parametric warnings
|
||||||
|
"numeric-string",
|
||||||
|
"numeric-string",
|
||||||
|
"truncation",
|
||||||
|
"truncation",
|
||||||
|
|
||||||
/* Meta warnings */
|
/* Meta warnings */
|
||||||
"all",
|
"all",
|
||||||
"extra",
|
"extra",
|
||||||
"everything" /* Especially useful for testing */
|
"everything", /* Especially useful for testing */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct {
|
||||||
|
char const *name;
|
||||||
|
uint8_t nbLevels;
|
||||||
|
uint8_t defaultLevel;
|
||||||
|
} paramWarnings[] = {
|
||||||
|
{ "numeric-string", 2, 1 },
|
||||||
|
{ "truncation", 2, 2 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool tryProcessParamWarning(char const *flag, uint8_t param, enum WarningState state)
|
||||||
|
{
|
||||||
|
enum WarningID baseID = PARAM_WARNINGS_START;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < ARRAY_SIZE(paramWarnings); i++) {
|
||||||
|
uint8_t maxParam = paramWarnings[i].nbLevels;
|
||||||
|
|
||||||
|
if (!strcmp(paramWarnings[i].name, flag)) { // Match!
|
||||||
|
// If making the warning an error but param is 0, set to the maximum
|
||||||
|
// This accommodates `-Werror=flag`, but also `-Werror=flag=0`, which is
|
||||||
|
// thus filtered out by the caller.
|
||||||
|
// A param of 0 makes sense for disabling everything, but neither for
|
||||||
|
// enabling nor "erroring". Use the default for those.
|
||||||
|
if (param == 0 && state != WARNING_DISABLED) {
|
||||||
|
param = paramWarnings[i].defaultLevel;
|
||||||
|
} else if (param > maxParam) {
|
||||||
|
if (param != 255) // Don't warn if already capped
|
||||||
|
warnx("Got parameter %" PRIu8
|
||||||
|
" for warning flag \"%s\", but the maximum is %"
|
||||||
|
PRIu8 "; capping.\n",
|
||||||
|
param, flag, maxParam);
|
||||||
|
param = maxParam;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the first <param> to enabled/error, and disable the rest
|
||||||
|
for (uint8_t ofs = 0; ofs < maxParam; ofs++) {
|
||||||
|
warningStates[baseID + ofs] =
|
||||||
|
ofs < param ? state : WARNING_DISABLED;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
baseID += maxParam;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
enum MetaWarningCommand {
|
enum MetaWarningCommand {
|
||||||
META_WARNING_DONE = NB_WARNINGS
|
META_WARNING_DONE = NB_WARNINGS
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Warnings that probably indicate an error */
|
/* Warnings that probably indicate an error */
|
||||||
static uint8_t const _wallCommands[] = {
|
static uint8_t const _wallCommands[] = {
|
||||||
|
WARNING_BACKWARDS_FOR,
|
||||||
WARNING_BUILTIN_ARG,
|
WARNING_BUILTIN_ARG,
|
||||||
WARNING_CHARMAP_REDEF,
|
WARNING_CHARMAP_REDEF,
|
||||||
WARNING_EMPTY_DATA_DIRECTIVE,
|
WARNING_EMPTY_DATA_DIRECTIVE,
|
||||||
|
WARNING_EMPTY_STRRPL,
|
||||||
WARNING_LARGE_CONSTANT,
|
WARNING_LARGE_CONSTANT,
|
||||||
WARNING_LONG_STR,
|
WARNING_LONG_STR,
|
||||||
|
WARNING_NESTED_COMMENT,
|
||||||
|
WARNING_OBSOLETE,
|
||||||
|
WARNING_NUMERIC_STRING_1,
|
||||||
META_WARNING_DONE
|
META_WARNING_DONE
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Warnings that are less likely to indicate an error */
|
/* Warnings that are less likely to indicate an error */
|
||||||
static uint8_t const _wextraCommands[] = {
|
static uint8_t const _wextraCommands[] = {
|
||||||
WARNING_EMPTY_ENTRY,
|
WARNING_EMPTY_MACRO_ARG,
|
||||||
|
WARNING_MACRO_SHIFT,
|
||||||
WARNING_NESTED_COMMENT,
|
WARNING_NESTED_COMMENT,
|
||||||
|
WARNING_OBSOLETE,
|
||||||
|
WARNING_NUMERIC_STRING_2,
|
||||||
|
WARNING_TRUNCATION_1,
|
||||||
|
WARNING_TRUNCATION_2,
|
||||||
META_WARNING_DONE
|
META_WARNING_DONE
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Literally everything. Notably useful for testing */
|
/* Literally everything. Notably useful for testing */
|
||||||
static uint8_t const _weverythingCommands[] = {
|
static uint8_t const _weverythingCommands[] = {
|
||||||
|
WARNING_BACKWARDS_FOR,
|
||||||
WARNING_BUILTIN_ARG,
|
WARNING_BUILTIN_ARG,
|
||||||
WARNING_DIV,
|
WARNING_DIV,
|
||||||
WARNING_EMPTY_DATA_DIRECTIVE,
|
WARNING_EMPTY_DATA_DIRECTIVE,
|
||||||
WARNING_EMPTY_ENTRY,
|
WARNING_EMPTY_MACRO_ARG,
|
||||||
|
WARNING_EMPTY_STRRPL,
|
||||||
WARNING_LARGE_CONSTANT,
|
WARNING_LARGE_CONSTANT,
|
||||||
WARNING_LONG_STR,
|
WARNING_LONG_STR,
|
||||||
|
WARNING_MACRO_SHIFT,
|
||||||
WARNING_NESTED_COMMENT,
|
WARNING_NESTED_COMMENT,
|
||||||
WARNING_OBSOLETE,
|
WARNING_OBSOLETE,
|
||||||
WARNING_SHIFT,
|
WARNING_SHIFT,
|
||||||
WARNING_SHIFT_AMOUNT,
|
WARNING_SHIFT_AMOUNT,
|
||||||
/* WARNING_TRUNCATION, */
|
WARNING_NUMERIC_STRING_1,
|
||||||
|
WARNING_NUMERIC_STRING_2,
|
||||||
|
WARNING_TRUNCATION_1,
|
||||||
|
WARNING_TRUNCATION_2,
|
||||||
/* WARNING_USER, */
|
/* WARNING_USER, */
|
||||||
META_WARNING_DONE
|
META_WARNING_DONE
|
||||||
};
|
};
|
||||||
@@ -133,37 +205,33 @@ static uint8_t const *metaWarningCommands[NB_META_WARNINGS] = {
|
|||||||
_weverythingCommands
|
_weverythingCommands
|
||||||
};
|
};
|
||||||
|
|
||||||
void processWarningFlag(char const *flag)
|
void processWarningFlag(char *flag)
|
||||||
{
|
{
|
||||||
static bool setError = false;
|
static bool setError = false;
|
||||||
|
|
||||||
/* First, try to match against a "meta" warning */
|
/* First, try to match against a "meta" warning */
|
||||||
for (enum WarningID id = NB_WARNINGS; id < NB_WARNINGS_ALL; id++) {
|
for (enum WarningID id = META_WARNINGS_START; id < NB_WARNINGS; id++) {
|
||||||
/* TODO: improve the matching performance? */
|
/* TODO: improve the matching performance? */
|
||||||
if (!strcmp(flag, warningFlags[id])) {
|
if (!strcmp(flag, warningFlags[id])) {
|
||||||
/* We got a match! */
|
/* We got a match! */
|
||||||
if (setError)
|
if (setError)
|
||||||
errx(1, "Cannot make meta warning \"%s\" into an error",
|
errx("Cannot make meta warning \"%s\" into an error",
|
||||||
flag);
|
flag);
|
||||||
|
|
||||||
uint8_t const *ptr =
|
for (uint8_t const *ptr = metaWarningCommands[id - META_WARNINGS_START];
|
||||||
metaWarningCommands[id - NB_WARNINGS];
|
*ptr != META_WARNING_DONE; ptr++) {
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
if (*ptr == META_WARNING_DONE)
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* Warning flag, set without override */
|
/* Warning flag, set without override */
|
||||||
if (warningStates[*ptr] == WARNING_DEFAULT)
|
if (warningStates[*ptr] == WARNING_DEFAULT)
|
||||||
warningStates[*ptr] = WARNING_ENABLED;
|
warningStates[*ptr] = WARNING_ENABLED;
|
||||||
ptr++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If it's not a meta warning, specially check against `-Werror` */
|
/* If it's not a meta warning, specially check against `-Werror` */
|
||||||
if (!strncmp(flag, "error", strlen("error"))) {
|
if (!strncmp(flag, "error", strlen("error"))) {
|
||||||
char const *errorFlag = flag + strlen("error");
|
char *errorFlag = flag + strlen("error");
|
||||||
|
|
||||||
switch (*errorFlag) {
|
switch (*errorFlag) {
|
||||||
case '\0':
|
case '\0':
|
||||||
@@ -172,7 +240,7 @@ void processWarningFlag(char const *flag)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
case '=':
|
case '=':
|
||||||
/* `-Werror=XXX */
|
/* `-Werror=XXX` */
|
||||||
setError = true;
|
setError = true;
|
||||||
processWarningFlag(errorFlag + 1); /* Skip the `=` */
|
processWarningFlag(errorFlag + 1); /* Skip the `=` */
|
||||||
setError = false;
|
setError = false;
|
||||||
@@ -184,14 +252,60 @@ void processWarningFlag(char const *flag)
|
|||||||
|
|
||||||
/* Well, it's either a normal warning or a mistake */
|
/* Well, it's either a normal warning or a mistake */
|
||||||
|
|
||||||
/* Check if this is a negation */
|
|
||||||
bool isNegation = !strncmp(flag, "no-", strlen("no-")) && !setError;
|
|
||||||
char const *rootFlag = isNegation ? flag + strlen("no-") : flag;
|
|
||||||
enum WarningState state = setError ? WARNING_ERROR :
|
enum WarningState state = setError ? WARNING_ERROR :
|
||||||
isNegation ? WARNING_DISABLED : WARNING_ENABLED;
|
/* Not an error, then check if this is a negation */
|
||||||
|
strncmp(flag, "no-", strlen("no-")) ? WARNING_ENABLED
|
||||||
|
: WARNING_DISABLED;
|
||||||
|
char const *rootFlag = state == WARNING_DISABLED ? flag + strlen("no-") : flag;
|
||||||
|
|
||||||
|
// Is this a "parametric" warning?
|
||||||
|
if (state != WARNING_DISABLED) { // The `no-` form cannot be parametrized
|
||||||
|
// First, check if there is an "equals" sign followed by a decimal number
|
||||||
|
char *equals = strchr(rootFlag, '=');
|
||||||
|
|
||||||
|
if (equals && equals[1] != '\0') { // Ignore an equal sign at the very end as well
|
||||||
|
// Is the rest of the string a decimal number?
|
||||||
|
// We want to avoid `strtoul`'s whitespace and sign, so we parse manually
|
||||||
|
uint8_t param = 0;
|
||||||
|
char const *ptr = equals + 1;
|
||||||
|
bool warned = false;
|
||||||
|
|
||||||
|
// The `if`'s condition above ensures that this will run at least once
|
||||||
|
do {
|
||||||
|
// If we don't have a digit, bail
|
||||||
|
if (*ptr < '0' || *ptr > '9')
|
||||||
|
break;
|
||||||
|
// Avoid overflowing!
|
||||||
|
if (param > UINT8_MAX - (*ptr - '0')) {
|
||||||
|
if (!warned)
|
||||||
|
warnx("Invalid warning flag \"%s\": capping parameter at 255\n",
|
||||||
|
flag);
|
||||||
|
warned = true; // Only warn once, cap always
|
||||||
|
param = 255;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
param = param * 10 + (*ptr - '0');
|
||||||
|
|
||||||
|
ptr++;
|
||||||
|
} while (*ptr);
|
||||||
|
|
||||||
|
// If we managed to the end of the string, check that the warning indeed
|
||||||
|
// accepts a parameter
|
||||||
|
if (*ptr == '\0') {
|
||||||
|
if (setError && param == 0) {
|
||||||
|
warnx("Ignoring nonsensical warning flag \"%s\"\n", flag);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
*equals = '\0'; // Truncate the param at the '='
|
||||||
|
if (tryProcessParamWarning(rootFlag, param,
|
||||||
|
param == 0 ? WARNING_DISABLED : state))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Try to match the flag against a "normal" flag */
|
/* Try to match the flag against a "normal" flag */
|
||||||
for (enum WarningID id = 0; id < NB_WARNINGS; id++) {
|
for (enum WarningID id = 0; id < NB_PLAIN_WARNINGS; id++) {
|
||||||
if (!strcmp(rootFlag, warningFlags[id])) {
|
if (!strcmp(rootFlag, warningFlags[id])) {
|
||||||
/* We got a match! */
|
/* We got a match! */
|
||||||
warningStates[id] = state;
|
warningStates[id] = state;
|
||||||
@@ -199,10 +313,15 @@ void processWarningFlag(char const *flag)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Lastly, this might be a "parametric" warning without an equals sign
|
||||||
|
// If it is, treat the param as 1 if enabling, or 0 if disabling
|
||||||
|
if (tryProcessParamWarning(rootFlag, 0, state))
|
||||||
|
return;
|
||||||
|
|
||||||
warnx("Unknown warning `%s`", flag);
|
warnx("Unknown warning `%s`", flag);
|
||||||
}
|
}
|
||||||
|
|
||||||
void printDiag(const char *fmt, va_list args, char const *type,
|
void printDiag(char const *fmt, va_list args, char const *type,
|
||||||
char const *flagfmt, char const *flag)
|
char const *flagfmt, char const *flag)
|
||||||
{
|
{
|
||||||
fputs(type, stderr);
|
fputs(type, stderr);
|
||||||
@@ -212,17 +331,17 @@ void printDiag(const char *fmt, va_list args, char const *type,
|
|||||||
lexer_DumpStringExpansions();
|
lexer_DumpStringExpansions();
|
||||||
}
|
}
|
||||||
|
|
||||||
void error(const char *fmt, ...)
|
void error(char const *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list args;
|
va_list args;
|
||||||
|
|
||||||
va_start(args, fmt);
|
va_start(args, fmt);
|
||||||
printDiag(fmt, args, "ERROR: ", ":\n ", NULL);
|
printDiag(fmt, args, "error: ", ":\n ", NULL);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
nbErrors++;
|
nbErrors++;
|
||||||
}
|
}
|
||||||
|
|
||||||
_Noreturn void fatalerror(const char *fmt, ...)
|
_Noreturn void fatalerror(char const *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list args;
|
va_list args;
|
||||||
|
|
||||||
@@ -245,7 +364,7 @@ void warning(enum WarningID id, char const *fmt, ...)
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
case WARNING_ERROR:
|
case WARNING_ERROR:
|
||||||
printDiag(fmt, args, "ERROR: ", ": [-Werror=%s]\n ", flag);
|
printDiag(fmt, args, "error: ", ": [-Werror=%s]\n ", flag);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
Executable
+7
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
bison -V | awk -v major="$1" -v minor="$2" '
|
||||||
|
/^bison.*[0-9]+(\.[0-9]+)(\.[0-9]+)?$/ {
|
||||||
|
match($0, /[0-9]+(\.[0-9]+)(\.[0-9]+)?$/);
|
||||||
|
split(substr($0, RSTART), ver, ".");
|
||||||
|
if (ver[1] == major && ver[2] >= minor) { exit 0 } else { exit 1 }
|
||||||
|
}'
|
||||||
+87
@@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of RGBDS.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2005-2021, Rich Felker and RGBDS contributors.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include "error.h"
|
||||||
|
#include "platform.h"
|
||||||
|
|
||||||
|
static void vwarn(char const NONNULL(fmt), va_list ap)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "warning: ");
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
fputs(": ", stderr);
|
||||||
|
perror(NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void vwarnx(char const NONNULL(fmt), va_list ap)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "warning");
|
||||||
|
fputs(": ", stderr);
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
putc('\n', stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
_Noreturn static void verr(char const NONNULL(fmt), va_list ap)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "error: ");
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
fputs(": ", stderr);
|
||||||
|
fputs(strerror(errno), stderr);
|
||||||
|
putc('\n', stderr);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
_Noreturn static void verrx(char const NONNULL(fmt), va_list ap)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "error");
|
||||||
|
fputs(": ", stderr);
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
putc('\n', stderr);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void warn(char const NONNULL(fmt), ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vwarn(fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
void warnx(char const NONNULL(fmt), ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vwarnx(fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
_Noreturn void err(char const NONNULL(fmt), ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
verr(fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
_Noreturn void errx(char const NONNULL(fmt), ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
va_start(ap, fmt);
|
||||||
|
verrx(fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
}
|
||||||
Vendored
-94
@@ -1,94 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of RGBDS.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2005-2018, Rich Felker and RGBDS contributors.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: MIT
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <errno.h>
|
|
||||||
#include <stdarg.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
|
|
||||||
#include "extern/err.h"
|
|
||||||
|
|
||||||
void rgbds_vwarn(const char *fmt, va_list ap)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "warning: ");
|
|
||||||
if (fmt) {
|
|
||||||
vfprintf(stderr, fmt, ap);
|
|
||||||
fputs(": ", stderr);
|
|
||||||
}
|
|
||||||
perror(NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void rgbds_vwarnx(const char *fmt, va_list ap)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "warning");
|
|
||||||
if (fmt) {
|
|
||||||
fputs(": ", stderr);
|
|
||||||
vfprintf(stderr, fmt, ap);
|
|
||||||
}
|
|
||||||
putc('\n', stderr);
|
|
||||||
}
|
|
||||||
|
|
||||||
_Noreturn void rgbds_verr(int status, const char *fmt, va_list ap)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "error: ");
|
|
||||||
if (fmt) {
|
|
||||||
vfprintf(stderr, fmt, ap);
|
|
||||||
fputs(": ", stderr);
|
|
||||||
}
|
|
||||||
fputs(strerror(errno), stderr);
|
|
||||||
putc('\n', stderr);
|
|
||||||
exit(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
_Noreturn void rgbds_verrx(int status, const char *fmt, va_list ap)
|
|
||||||
{
|
|
||||||
fprintf(stderr, "error");
|
|
||||||
if (fmt) {
|
|
||||||
fputs(": ", stderr);
|
|
||||||
vfprintf(stderr, fmt, ap);
|
|
||||||
}
|
|
||||||
putc('\n', stderr);
|
|
||||||
exit(status);
|
|
||||||
}
|
|
||||||
|
|
||||||
void rgbds_warn(const char *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
vwarn(fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
void rgbds_warnx(const char *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
vwarnx(fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
_Noreturn void rgbds_err(int status, const char *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
verr(status, fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
|
|
||||||
_Noreturn void rgbds_errx(int status, const char *fmt, ...)
|
|
||||||
{
|
|
||||||
va_list ap;
|
|
||||||
|
|
||||||
va_start(ap, fmt);
|
|
||||||
verrx(status, fmt, ap);
|
|
||||||
va_end(ap);
|
|
||||||
}
|
|
||||||
Vendored
+122
-99
@@ -26,23 +26,18 @@
|
|||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#ifndef _MSC_VER
|
|
||||||
# include <unistd.h>
|
|
||||||
#endif
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <wchar.h>
|
#include <wchar.h>
|
||||||
|
|
||||||
#include "extern/getopt.h"
|
#include "extern/getopt.h"
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
char *musl_optarg;
|
||||||
char *optarg;
|
int musl_optind = 1, musl_opterr = 1, musl_optopt;
|
||||||
int optind=1, opterr=1, optopt;
|
int musl_optreset = 0;
|
||||||
#endif
|
static int musl_optpos;
|
||||||
int optreset=0;
|
|
||||||
static int optpos;
|
|
||||||
|
|
||||||
static void musl_getopt_msg(const char *a, const char *b, const char *c, size_t l)
|
static void musl_getopt_msg(char const *a, char const *b, char const *c, size_t l)
|
||||||
{
|
{
|
||||||
FILE *f = stderr;
|
FILE *f = stderr;
|
||||||
|
|
||||||
@@ -52,48 +47,49 @@ static void musl_getopt_msg(const char *a, const char *b, const char *c, size_t
|
|||||||
putc('\n', f);
|
putc('\n', f);
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
static int getopt(int argc, char *argv[], char const *optstring)
|
||||||
static int getopt(int argc, char *argv[], const char *optstring)
|
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
wchar_t c, d;
|
wchar_t c, d;
|
||||||
int k, l;
|
int k, l;
|
||||||
char *optchar;
|
char *optchar;
|
||||||
|
|
||||||
if (!optind || optreset) {
|
if (!musl_optind || musl_optreset) {
|
||||||
optreset = 0;
|
musl_optreset = 0;
|
||||||
optpos = 0;
|
musl_optpos = 0;
|
||||||
optind = 1;
|
musl_optind = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (optind >= argc || !argv[optind])
|
if (musl_optind >= argc || !argv[musl_optind])
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (argv[optind][0] != '-') {
|
if (argv[musl_optind][0] != '-') {
|
||||||
if (optstring[0] == '-') {
|
if (optstring[0] == '-') {
|
||||||
optarg = argv[optind++];
|
musl_optarg = argv[musl_optind++];
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!argv[optind][1])
|
if (!argv[musl_optind][1])
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (argv[optind][1] == '-' && !argv[optind][2])
|
if (argv[musl_optind][1] == '-' && !argv[musl_optind][2])
|
||||||
return optind++, -1;
|
return musl_optind++, -1;
|
||||||
|
|
||||||
if (!optpos) optpos++;
|
if (!musl_optpos)
|
||||||
if ((k = mbtowc(&c, argv[optind]+optpos, MB_LEN_MAX)) < 0) {
|
musl_optpos++;
|
||||||
|
k = mbtowc(&c, argv[musl_optind] + musl_optpos, MB_LEN_MAX);
|
||||||
|
if (k < 0) {
|
||||||
k = 1;
|
k = 1;
|
||||||
c = 0xfffd; /* replacement char */
|
c = 0xfffd; /* replacement char */
|
||||||
}
|
}
|
||||||
optchar = argv[optind]+optpos;
|
optchar = argv[musl_optind] + musl_optpos;
|
||||||
optpos += k;
|
musl_optpos += k;
|
||||||
|
|
||||||
if (!argv[optind][optpos]) {
|
if (!argv[musl_optind][musl_optpos]) {
|
||||||
optind++;
|
musl_optind++;
|
||||||
optpos = 0;
|
musl_optpos = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (optstring[0] == '-' || optstring[0] == '+')
|
if (optstring[0] == '-' || optstring[0] == '+')
|
||||||
@@ -103,91 +99,110 @@ static int getopt(int argc, char *argv[], const char *optstring)
|
|||||||
d = 0;
|
d = 0;
|
||||||
do {
|
do {
|
||||||
l = mbtowc(&d, optstring+i, MB_LEN_MAX);
|
l = mbtowc(&d, optstring+i, MB_LEN_MAX);
|
||||||
if (l>0) i+=l; else i++;
|
if (l > 0)
|
||||||
|
i += l;
|
||||||
|
else
|
||||||
|
i++;
|
||||||
} while (l && d != c);
|
} while (l && d != c);
|
||||||
|
|
||||||
if (d != c || c == ':') {
|
if (d != c || c == ':') {
|
||||||
optopt = c;
|
musl_optopt = c;
|
||||||
if (optstring[0] != ':' && opterr)
|
if (optstring[0] != ':' && musl_opterr)
|
||||||
musl_getopt_msg(argv[0], ": unrecognized option: ", optchar, k);
|
musl_getopt_msg(argv[0], ": unrecognized option: ", optchar, k);
|
||||||
return '?';
|
return '?';
|
||||||
}
|
}
|
||||||
if (optstring[i] == ':') {
|
if (optstring[i] == ':') {
|
||||||
optarg = 0;
|
musl_optarg = 0;
|
||||||
if (optstring[i+1] != ':' || optpos) {
|
if (optstring[i + 1] != ':' || musl_optpos) {
|
||||||
optarg = argv[optind++] + optpos;
|
musl_optarg = argv[musl_optind++] + musl_optpos;
|
||||||
optpos = 0;
|
musl_optpos = 0;
|
||||||
}
|
}
|
||||||
if (optind > argc) {
|
if (musl_optind > argc) {
|
||||||
optopt = c;
|
musl_optopt = c;
|
||||||
if (optstring[0] == ':') return ':';
|
if (optstring[0] == ':')
|
||||||
if (opterr) musl_getopt_msg(argv[0],
|
return ':';
|
||||||
": option requires an argument: ",
|
if (musl_opterr)
|
||||||
optchar, k);
|
musl_getopt_msg(argv[0], ": option requires an argument: ",
|
||||||
|
optchar, k);
|
||||||
return '?';
|
return '?';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
#endif /* _MSC_VER */
|
|
||||||
|
|
||||||
static void permute(char **argv, int dest, int src)
|
static void permute(char **argv, int dest, int src)
|
||||||
{
|
{
|
||||||
char *tmp = argv[src];
|
char *tmp = argv[src];
|
||||||
int i;
|
int i;
|
||||||
for (i=src; i>dest; i--)
|
|
||||||
|
for (i = src; i > dest; i--)
|
||||||
argv[i] = argv[i-1];
|
argv[i] = argv[i-1];
|
||||||
argv[dest] = tmp;
|
argv[dest] = tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int musl_getopt_long_core(int argc, char **argv, const char *optstring, const struct option *longopts, int *idx, int longonly);
|
static int musl_getopt_long_core(int argc, char **argv, char const *optstring,
|
||||||
|
const struct option *longopts, int *idx, int longonly);
|
||||||
|
|
||||||
static int musl_getopt_long(int argc, char **argv, const char *optstring, const struct option *longopts, int *idx, int longonly)
|
static int musl_getopt_long(int argc, char **argv, char const *optstring,
|
||||||
|
const struct option *longopts, int *idx, int longonly)
|
||||||
{
|
{
|
||||||
int ret, skipped, resumed;
|
int ret, skipped, resumed;
|
||||||
if (!optind || optreset) {
|
|
||||||
optreset = 0;
|
if (!musl_optind || musl_optreset) {
|
||||||
optpos = 0;
|
musl_optreset = 0;
|
||||||
optind = 1;
|
musl_optpos = 0;
|
||||||
|
musl_optind = 1;
|
||||||
}
|
}
|
||||||
if (optind >= argc || !argv[optind]) return -1;
|
|
||||||
skipped = optind;
|
if (musl_optind >= argc || !argv[musl_optind])
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
skipped = musl_optind;
|
||||||
if (optstring[0] != '+' && optstring[0] != '-') {
|
if (optstring[0] != '+' && optstring[0] != '-') {
|
||||||
int i;
|
int i;
|
||||||
for (i=optind; ; i++) {
|
for (i = musl_optind; ; i++) {
|
||||||
if (i >= argc || !argv[i]) return -1;
|
if (i >= argc || !argv[i])
|
||||||
if (argv[i][0] == '-' && argv[i][1]) break;
|
return -1;
|
||||||
|
if (argv[i][0] == '-' && argv[i][1])
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
optind = i;
|
musl_optind = i;
|
||||||
}
|
}
|
||||||
resumed = optind;
|
resumed = musl_optind;
|
||||||
ret = musl_getopt_long_core(argc, argv, optstring, longopts, idx, longonly);
|
ret = musl_getopt_long_core(argc, argv, optstring, longopts, idx, longonly);
|
||||||
if (resumed > skipped) {
|
if (resumed > skipped) {
|
||||||
int i, cnt = optind-resumed;
|
int i, cnt = musl_optind - resumed;
|
||||||
for (i=0; i<cnt; i++)
|
|
||||||
permute(argv, skipped, optind-1);
|
for (i = 0; i < cnt; i++)
|
||||||
optind = skipped + cnt;
|
permute(argv, skipped, musl_optind - 1);
|
||||||
|
musl_optind = skipped + cnt;
|
||||||
}
|
}
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int musl_getopt_long_core(int argc, char **argv, const char *optstring, const struct option *longopts, int *idx, int longonly)
|
static int musl_getopt_long_core(int argc, char **argv, char const *optstring,
|
||||||
|
const struct option *longopts, int *idx, int longonly)
|
||||||
{
|
{
|
||||||
optarg = 0;
|
musl_optarg = 0;
|
||||||
if (longopts && argv[optind][0] == '-' &&
|
if (longopts && argv[musl_optind][0] == '-' &&
|
||||||
((longonly && argv[optind][1] && argv[optind][1] != '-') ||
|
((longonly && argv[musl_optind][1] && argv[musl_optind][1] != '-') ||
|
||||||
(argv[optind][1] == '-' && argv[optind][2])))
|
(argv[musl_optind][1] == '-' && argv[musl_optind][2]))) {
|
||||||
{
|
int colon = optstring[optstring[0] == '+' || optstring[0] == '-'] == ':';
|
||||||
int colon = optstring[optstring[0]=='+'||optstring[0]=='-']==':';
|
|
||||||
int i, cnt, match = 0;
|
int i, cnt, match = 0;
|
||||||
char *arg = 0, *opt, *start = argv[optind]+1;
|
char *arg = 0, *opt, *start = argv[musl_optind] + 1;
|
||||||
for (cnt=i=0; longopts[i].name; i++) {
|
|
||||||
const char *name = longopts[i].name;
|
for (cnt = i = 0; longopts[i].name; i++) {
|
||||||
|
char const *name = longopts[i].name;
|
||||||
|
|
||||||
opt = start;
|
opt = start;
|
||||||
if (*opt == '-') opt++;
|
if (*opt == '-')
|
||||||
while (*opt && *opt != '=' && *opt == *name)
|
opt++;
|
||||||
name++, opt++;
|
while (*opt && *opt != '=' && *opt == *name) {
|
||||||
if (*opt && *opt != '=') continue;
|
name++;
|
||||||
|
opt++;
|
||||||
|
}
|
||||||
|
if (*opt && *opt != '=')
|
||||||
|
continue;
|
||||||
arg = opt;
|
arg = opt;
|
||||||
match = i;
|
match = i;
|
||||||
if (!*name) {
|
if (!*name) {
|
||||||
@@ -196,25 +211,28 @@ static int musl_getopt_long_core(int argc, char **argv, const char *optstring, c
|
|||||||
}
|
}
|
||||||
cnt++;
|
cnt++;
|
||||||
}
|
}
|
||||||
if (cnt==1 && longonly && arg-start == mblen(start, MB_LEN_MAX)) {
|
if (cnt == 1 && longonly && arg - start == mblen(start, MB_LEN_MAX)) {
|
||||||
int l = arg-start;
|
int l = arg - start;
|
||||||
for (i=0; optstring[i]; i++) {
|
|
||||||
int j;
|
for (i = 0; optstring[i]; i++) {
|
||||||
for (j=0; j<l && start[j]==optstring[i+j]; j++);
|
int j = 0;
|
||||||
if (j==l) {
|
|
||||||
|
while (j < l && start[j] == optstring[i + j])
|
||||||
|
j++;
|
||||||
|
if (j == l) {
|
||||||
cnt++;
|
cnt++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (cnt==1) {
|
if (cnt == 1) {
|
||||||
i = match;
|
i = match;
|
||||||
opt = arg;
|
opt = arg;
|
||||||
optind++;
|
musl_optind++;
|
||||||
if (*opt == '=') {
|
if (*opt == '=') {
|
||||||
if (!longopts[i].has_arg) {
|
if (!longopts[i].has_arg) {
|
||||||
optopt = longopts[i].val;
|
musl_optopt = longopts[i].val;
|
||||||
if (colon || !opterr)
|
if (colon || !musl_opterr)
|
||||||
return '?';
|
return '?';
|
||||||
musl_getopt_msg(argv[0],
|
musl_getopt_msg(argv[0],
|
||||||
": option does not take an argument: ",
|
": option does not take an argument: ",
|
||||||
@@ -222,43 +240,48 @@ static int musl_getopt_long_core(int argc, char **argv, const char *optstring, c
|
|||||||
strlen(longopts[i].name));
|
strlen(longopts[i].name));
|
||||||
return '?';
|
return '?';
|
||||||
}
|
}
|
||||||
optarg = opt+1;
|
musl_optarg = opt + 1;
|
||||||
} else if (longopts[i].has_arg == required_argument) {
|
} else if (longopts[i].has_arg == required_argument) {
|
||||||
if (!(optarg = argv[optind])) {
|
musl_optarg = argv[musl_optind];
|
||||||
optopt = longopts[i].val;
|
if (!musl_optarg) {
|
||||||
if (colon) return ':';
|
musl_optopt = longopts[i].val;
|
||||||
if (!opterr) return '?';
|
if (colon)
|
||||||
|
return ':';
|
||||||
|
if (!musl_opterr)
|
||||||
|
return '?';
|
||||||
musl_getopt_msg(argv[0],
|
musl_getopt_msg(argv[0],
|
||||||
": option requires an argument: ",
|
": option requires an argument: ",
|
||||||
longopts[i].name,
|
longopts[i].name,
|
||||||
strlen(longopts[i].name));
|
strlen(longopts[i].name));
|
||||||
return '?';
|
return '?';
|
||||||
}
|
}
|
||||||
optind++;
|
musl_optind++;
|
||||||
}
|
}
|
||||||
if (idx) *idx = i;
|
if (idx)
|
||||||
|
*idx = i;
|
||||||
if (longopts[i].flag) {
|
if (longopts[i].flag) {
|
||||||
*longopts[i].flag = longopts[i].val;
|
*longopts[i].flag = longopts[i].val;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
return longopts[i].val;
|
return longopts[i].val;
|
||||||
}
|
}
|
||||||
if (argv[optind][1] == '-') {
|
if (argv[musl_optind][1] == '-') {
|
||||||
optopt = 0;
|
musl_optopt = 0;
|
||||||
if (!colon && opterr)
|
if (!colon && musl_opterr)
|
||||||
musl_getopt_msg(argv[0], cnt ?
|
musl_getopt_msg(argv[0], cnt ?
|
||||||
": option is ambiguous: " :
|
": option is ambiguous: " :
|
||||||
": unrecognized option: ",
|
": unrecognized option: ",
|
||||||
argv[optind]+2,
|
argv[musl_optind] + 2,
|
||||||
strlen(argv[optind]+2));
|
strlen(argv[musl_optind] + 2));
|
||||||
optind++;
|
musl_optind++;
|
||||||
return '?';
|
return '?';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return getopt(argc, argv, optstring);
|
return getopt(argc, argv, optstring);
|
||||||
}
|
}
|
||||||
|
|
||||||
int musl_getopt_long_only(int argc, char **argv, const char *optstring, const struct option *longopts, int *idx)
|
int musl_getopt_long_only(int argc, char **argv, char const *optstring,
|
||||||
|
const struct option *longopts, int *idx)
|
||||||
{
|
{
|
||||||
return musl_getopt_long(argc, argv, optstring, longopts, idx, 1);
|
return musl_getopt_long(argc, argv, optstring, longopts, idx, 1);
|
||||||
}
|
}
|
||||||
|
|||||||
+1347
-438
File diff suppressed because it is too large
Load Diff
+119
-52
@@ -1,11 +1,11 @@
|
|||||||
.\"
|
.\"
|
||||||
.\" This file is part of RGBDS.
|
.\" This file is part of RGBDS.
|
||||||
.\"
|
.\"
|
||||||
.\" Copyright (c) 2010-2017, Anthony J. Bentley and RGBDS contributors.
|
.\" Copyright (c) 2010-2021, Anthony J. Bentley and RGBDS contributors.
|
||||||
.\"
|
.\"
|
||||||
.\" SPDX-License-Identifier: MIT
|
.\" SPDX-License-Identifier: MIT
|
||||||
.\"
|
.\"
|
||||||
.Dd December 5, 2019
|
.Dd March 28, 2021
|
||||||
.Dt RGBFIX 1
|
.Dt RGBFIX 1
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
.Nd Game Boy header utility and checksum fixer
|
.Nd Game Boy header utility and checksum fixer
|
||||||
.Sh SYNOPSIS
|
.Sh SYNOPSIS
|
||||||
.Nm
|
.Nm
|
||||||
.Op Fl jsVv
|
.Op Fl jOsVv
|
||||||
.Op Fl C | c
|
.Op Fl C | c
|
||||||
.Op Fl f Ar fix_spec
|
.Op Fl f Ar fix_spec
|
||||||
.Op Fl i Ar game_id
|
.Op Fl i Ar game_id
|
||||||
@@ -24,39 +24,48 @@
|
|||||||
.Op Fl p Ar pad_value
|
.Op Fl p Ar pad_value
|
||||||
.Op Fl r Ar ram_size
|
.Op Fl r Ar ram_size
|
||||||
.Op Fl t Ar title_str
|
.Op Fl t Ar title_str
|
||||||
.Ar file
|
.Op Ar
|
||||||
.Sh DESCRIPTION
|
.Sh DESCRIPTION
|
||||||
The
|
The
|
||||||
.Nm
|
.Nm
|
||||||
program changes headers of Game Boy ROM images.
|
program changes headers of Game Boy ROM images, typically generated by
|
||||||
|
.Xr rgblink 1 ,
|
||||||
|
though it will work with
|
||||||
|
.Em any
|
||||||
|
Game Boy ROM.
|
||||||
It also performs other correctness operations, such as padding.
|
It also performs other correctness operations, such as padding.
|
||||||
|
.Nm
|
||||||
|
only changes the fields for which it has values specified.
|
||||||
|
Developers are advised to fill those fields with 0x00 bytes in their source code before running
|
||||||
|
.Nm ,
|
||||||
|
and to have already populated whichever fields they don't specify using
|
||||||
|
.Nm .
|
||||||
.Pp
|
.Pp
|
||||||
Note that options can be abbreviated as long as the abbreviation is unambiguous:
|
Note that options can be abbreviated as long as the abbreviation is unambiguous:
|
||||||
.Fl Fl verb
|
.Fl Fl color-o
|
||||||
is
|
is
|
||||||
.Fl Fl verbose ,
|
.Fl Fl color-only ,
|
||||||
but
|
but
|
||||||
.Fl Fl ver
|
.Fl Fl color
|
||||||
is invalid because it could also be
|
is invalid because it could also be
|
||||||
.Fl Fl version .
|
.Fl Fl color-compatible .
|
||||||
The arguments are as follows:
|
Options later in the command line override those set earlier.
|
||||||
|
Accepted options are as follows:
|
||||||
.Bl -tag -width Ds
|
.Bl -tag -width Ds
|
||||||
.It Fl C , Fl Fl color-only
|
.It Fl C , Fl Fl color-only
|
||||||
Set the Game Boy Color\(enonly flag:
|
Set the Game Boy Color\(enonly flag
|
||||||
.Ad 0x143
|
.Pq Ad 0x143
|
||||||
= 0xC0.
|
to 0xC0.
|
||||||
If both this and the
|
This overrides
|
||||||
.Fl c
|
.Fl c
|
||||||
flag are set, this takes precedence.
|
if it was set prior.
|
||||||
.It Fl c , Fl Fl color-compatible
|
.It Fl c , Fl Fl color-compatible
|
||||||
Set the Game Boy Color\(encompatible flag:
|
Set the Game Boy Color\(encompatible flag:
|
||||||
.Ad 0x143
|
.Pq Ad 0x143
|
||||||
= 0x80.
|
to 0x80.
|
||||||
If both this and the
|
This overrides
|
||||||
.Fl C
|
.Fl c
|
||||||
flag are set,
|
if it was set prior.
|
||||||
.Fl C
|
|
||||||
takes precedence.
|
|
||||||
.It Fl f Ar fix_spec , Fl Fl fix-spec Ar fix_spec
|
.It Fl f Ar fix_spec , Fl Fl fix-spec Ar fix_spec
|
||||||
Fix certain header values that the Game Boy checks for correctness.
|
Fix certain header values that the Game Boy checks for correctness.
|
||||||
Alternatively, intentionally trash these values by writing their binary inverse instead.
|
Alternatively, intentionally trash these values by writing their binary inverse instead.
|
||||||
@@ -83,54 +92,73 @@ Trash the global checksum.
|
|||||||
.It Fl i Ar game_id , Fl Fl game-id Ar game_id
|
.It Fl i Ar game_id , Fl Fl game-id Ar game_id
|
||||||
Set the game ID string
|
Set the game ID string
|
||||||
.Pq Ad 0x13F Ns \(en Ns Ad 0x142
|
.Pq Ad 0x13F Ns \(en Ns Ad 0x142
|
||||||
to a given string of exactly 4 characters.
|
to a given string.
|
||||||
If both this and the title are set, the game ID will overwrite the overlapping portion of the title.
|
If it's longer than 4 chars, it will be truncated, and a warning emitted.
|
||||||
.It Fl j , Fl Fl non-japanese
|
.It Fl j , Fl Fl non-japanese
|
||||||
Set the non-Japanese region flag:
|
Set the non-Japanese region flag
|
||||||
.Ad 0x14A
|
.Pq Ad 0x14A
|
||||||
= 1.
|
to 0x01.
|
||||||
.It Fl k Ar licensee_str , Fl Fl new-licensee Ar licensee_str
|
.It Fl k Ar licensee_str , Fl Fl new-licensee Ar licensee_str
|
||||||
Set the new licensee string
|
Set the new licensee string
|
||||||
.Pq Ad 0x144 Ns \(en Ns Ad 0x145
|
.Pq Ad 0x144 Ns \(en Ns Ad 0x145
|
||||||
to a given string, truncated to at most two characters.
|
to a given string.
|
||||||
|
If it's longer than 2 chars, it will be truncated, and a warning emitted.
|
||||||
.It Fl l Ar licensee_id , Fl Fl old-licensee Ar licensee_id
|
.It Fl l Ar licensee_id , Fl Fl old-licensee Ar licensee_id
|
||||||
Set the old licensee code,
|
Set the old licensee code
|
||||||
.Ad 0x14B ,
|
.Pq Ad 0x14B
|
||||||
to a given value from 0 to 0xFF.
|
to a given value from 0 to 0xFF.
|
||||||
This value is deprecated and should be set to 0x33 in all new software.
|
This value is deprecated and should be set to 0x33 in all new software.
|
||||||
.It Fl m Ar mbc_type , Fl Fl mbc-type Ar mbc_type
|
.It Fl m Ar mbc_type , Fl Fl mbc-type Ar mbc_type
|
||||||
Set the MBC type,
|
Set the MBC type
|
||||||
.Ad 0x147 ,
|
.Pq Ad 0x147
|
||||||
to a given value from 0 to 0xFF.
|
to a given value from 0 to 0xFF.
|
||||||
|
.Pp
|
||||||
|
This value may also be an MBC name.
|
||||||
|
The list of accepted names can be obtained by passing
|
||||||
|
.Ql Cm help
|
||||||
|
as the argument.
|
||||||
|
Any amount of whitespace (space and tabs) is allowed around plus signs, and the order of "components" is free, as long as the MBC name is first.
|
||||||
|
There are special considerations to take for the TPP1 mapper; see the
|
||||||
|
.Sx TPP1
|
||||||
|
section below.
|
||||||
.It Fl n Ar rom_version , Fl Fl rom-version Ar rom_version
|
.It Fl n Ar rom_version , Fl Fl rom-version Ar rom_version
|
||||||
Set the ROM version,
|
Set the ROM version
|
||||||
.Ad 0x14C ,
|
.Pq Ad 0x14C
|
||||||
to a given value from 0 to 0xFF.
|
to a given value from 0 to 0xFF.
|
||||||
|
.It Fl O , Fl Fl overwrite
|
||||||
|
Allow overwriting different non-zero bytes in the header without a warning being emitted.
|
||||||
.It Fl p Ar pad_value , Fl Fl pad-value Ar pad_value
|
.It Fl p Ar pad_value , Fl Fl pad-value Ar pad_value
|
||||||
Pad the image to a valid size with a given pad value from 0 to 0xFF.
|
Pad the ROM image to a valid size with a given pad value from 0 to 255 (0xFF).
|
||||||
.Nm
|
.Nm
|
||||||
will automatically pick a size from 32 KiB, 64 KiB, 128 KiB, ..., 8192 KiB.
|
will automatically pick a size from 32 KiB, 64 KiB, 128 KiB, ..., 8192 KiB.
|
||||||
The cartridge size byte
|
The cartridge size byte
|
||||||
.Pq Ad 0x148
|
.Pq Ad 0x148
|
||||||
will be changed to reflect this new size.
|
will be changed to reflect this new size.
|
||||||
|
The recommended padding value is 0xFF, to speed up writing the ROM to flash chips, and to avoid "nop slides" into VRAM.
|
||||||
.It Fl r Ar ram_size , Fl Fl ram-size Ar ram_size
|
.It Fl r Ar ram_size , Fl Fl ram-size Ar ram_size
|
||||||
Set the RAM size,
|
Set the RAM size
|
||||||
.Ad 0x149 ,
|
.Pq Ad 0x149
|
||||||
to a given value from 0 to 0xFF.
|
to a given value from 0 to 0xFF.
|
||||||
.It Fl s , Fl Fl sgb-compatible
|
.It Fl s , Fl Fl sgb-compatible
|
||||||
Set the SGB flag:
|
Set the SGB flag
|
||||||
.Ad 0x146
|
.Pq Ad 0x146
|
||||||
= 3. This flag will be ignored by the SGB unless the old licensee code is 0x33!
|
to 0x03.
|
||||||
|
This flag will be ignored by the SGB unless the old licensee code is 0x33!
|
||||||
|
If this is given as well as
|
||||||
|
.Fl l ,
|
||||||
|
but is not set to 0x33, a warning will be printed.
|
||||||
.It Fl t Ar title , Fl Fl title Ar title
|
.It Fl t Ar title , Fl Fl title Ar title
|
||||||
Set the title string
|
Set the title string
|
||||||
.Pq Ad 0x134 Ns \(en Ns Ad 0x143
|
.Pq Ad 0x134 Ns \(en Ns Ad 0x143
|
||||||
to a given string, truncated to at most 16 characters.
|
to a given string.
|
||||||
It is recommended to use 15 characters instead, to avoid clashing with the CGB flag
|
If the title is longer than the max length, it will be truncated, and a warning emitted.
|
||||||
.Po Fl c
|
The max length is 11 characters if the game ID
|
||||||
|
.Pq Fl i
|
||||||
|
is specified, 15 characters if the CGB flag
|
||||||
|
.Fl ( c
|
||||||
or
|
or
|
||||||
.Fl C
|
.Fl C )
|
||||||
.Pc .
|
is specified but the game ID is not, and 16 characters otherwise.
|
||||||
If both this and the game ID are set, the game ID will overwrite the overlapping portion of the title.
|
|
||||||
.It Fl V , Fl Fl version
|
.It Fl V , Fl Fl version
|
||||||
Print the version of the program and exit.
|
Print the version of the program and exit.
|
||||||
.It Fl v , Fl Fl validate
|
.It Fl v , Fl Fl validate
|
||||||
@@ -138,7 +166,7 @@ Equivalent to
|
|||||||
.Fl f Cm lhg .
|
.Fl f Cm lhg .
|
||||||
.El
|
.El
|
||||||
.Sh EXAMPLES
|
.Sh EXAMPLES
|
||||||
Most values in the ROM header are only cosmetic.
|
Most values in the ROM header do not matter to the actual console, and most are seldom useful anyway.
|
||||||
The bare minimum requirements for a workable program are the header checksum, the Nintendo logo, and (if needed) the CGB/SGB flags.
|
The bare minimum requirements for a workable program are the header checksum, the Nintendo logo, and (if needed) the CGB/SGB flags.
|
||||||
It is a good idea to pad the image to a valid size as well
|
It is a good idea to pad the image to a valid size as well
|
||||||
.Pq Do valid Dc meaning a power of 2, times 32 KiB .
|
.Pq Do valid Dc meaning a power of 2, times 32 KiB .
|
||||||
@@ -151,17 +179,56 @@ a valid size:
|
|||||||
The following will make a SGB-enabled, color-enabled game with a title of
|
The following will make a SGB-enabled, color-enabled game with a title of
|
||||||
.Dq foobar ,
|
.Dq foobar ,
|
||||||
and pad it to a valid size.
|
and pad it to a valid size.
|
||||||
.Po
|
.Pq The Game Boy itself does not use the title, but some emulators or ROM managers do.
|
||||||
The Game Boy itself does not use the title, but some emulators or ROM managers do.
|
|
||||||
.Pc
|
|
||||||
.Pp
|
.Pp
|
||||||
.D1 $ rgbfix -vcs -l 0x33 -p 255 -t foobar baz.gb
|
.D1 $ rgbfix -vcs -l 0x33 -p 255 -t foobar baz.gb
|
||||||
.Pp
|
.Pp
|
||||||
The following will duplicate the header (sans global checksum) of the game
|
The following will duplicate the header of the game
|
||||||
.Dq Survival Kids :
|
.Dq Survival Kids ,
|
||||||
|
sans global checksum:
|
||||||
.Pp
|
.Pp
|
||||||
.D1 $ rgbfix -cjsv -k A4 -l 0x33 -m 0x1B -p 0xFF -r 3 -t SURVIVALKIDAVKE \
|
.D1 $ rgbfix -cjsv -k A4 -l 0x33 -m 0x1B -p 0xFF -r 3 -t SURVIVALKIDAVKE \
|
||||||
SurvivalKids.gbc
|
SurvivalKids.gbc
|
||||||
|
.Sh TPP1
|
||||||
|
TPP1 is a homebrew mapper designed as a functional superset of the common traditional MBCs, allowing larger ROM and RAM sizes combined with other hardware features.
|
||||||
|
Its specification, as well as more resources, can be found online at
|
||||||
|
.Lk https://github.com/TwitchPlaysPokemon/tpp1 .
|
||||||
|
.Ss MBC name
|
||||||
|
The MBC name for TPP1 is more complex than standard mappers.
|
||||||
|
It must be followed with the revision number, of the form
|
||||||
|
.Ql major.minor ,
|
||||||
|
where both
|
||||||
|
.Ql major
|
||||||
|
and
|
||||||
|
.Ql minor
|
||||||
|
are decimal, 8-bit integers.
|
||||||
|
There may be any amount of spaces or underscores between
|
||||||
|
.Ql TPP1
|
||||||
|
and the revision number.
|
||||||
|
.Nm
|
||||||
|
only supports 1.x revisions, and will reject everything else.
|
||||||
|
.Pp
|
||||||
|
Like other mappers, the name may be followed with a list of optional,
|
||||||
|
.Ql + Ns
|
||||||
|
-separated features; however,
|
||||||
|
.Ql RAM
|
||||||
|
should not be specified, as the TPP1 mapper implicitly requests RAM if a non-zero RAM size is specified.
|
||||||
|
Therefore,
|
||||||
|
.Nm
|
||||||
|
will ignore the
|
||||||
|
.Ql RAM
|
||||||
|
feature on a TPP1 mapper with a warning.
|
||||||
|
.Ss Special considerations
|
||||||
|
TPP1 overwrites the byte at
|
||||||
|
.Ad 0x14A ,
|
||||||
|
usually indicating the region destination
|
||||||
|
.Pq see Fl j ,
|
||||||
|
with one of its three identification bytes.
|
||||||
|
Therefore,
|
||||||
|
.Nm
|
||||||
|
will warn about and ignore
|
||||||
|
.Fl j
|
||||||
|
if used in combination with TPP1.
|
||||||
.Sh BUGS
|
.Sh BUGS
|
||||||
Please report bugs on
|
Please report bugs on
|
||||||
.Lk https://github.com/gbdev/rgbds/issues GitHub .
|
.Lk https://github.com/gbdev/rgbds/issues GitHub .
|
||||||
|
|||||||
+413
-357
File diff suppressed because it is too large
Load Diff
+20
-17
@@ -20,7 +20,7 @@ void transpose_tiles(struct GBImage *gb, int width)
|
|||||||
|
|
||||||
newdata = calloc(gb->size, 1);
|
newdata = calloc(gb->size, 1);
|
||||||
if (!newdata)
|
if (!newdata)
|
||||||
err(1, "%s: Failed to allocate memory for new data", __func__);
|
err("%s: Failed to allocate memory for new data", __func__);
|
||||||
|
|
||||||
for (i = 0; i < gb->size; i++) {
|
for (i = 0; i < gb->size; i++) {
|
||||||
newbyte = i / (8 * depth) * width * 8 * depth;
|
newbyte = i / (8 * depth) * width * 8 * depth;
|
||||||
@@ -37,15 +37,14 @@ void transpose_tiles(struct GBImage *gb, int width)
|
|||||||
|
|
||||||
void raw_to_gb(const struct RawIndexedImage *raw_image, struct GBImage *gb)
|
void raw_to_gb(const struct RawIndexedImage *raw_image, struct GBImage *gb)
|
||||||
{
|
{
|
||||||
int x, y, byte;
|
|
||||||
uint8_t index;
|
uint8_t index;
|
||||||
|
|
||||||
for (y = 0; y < raw_image->height; y++) {
|
for (unsigned int y = 0; y < raw_image->height; y++) {
|
||||||
for (x = 0; x < raw_image->width; x++) {
|
for (unsigned int x = 0; x < raw_image->width; x++) {
|
||||||
index = raw_image->data[y][x];
|
index = raw_image->data[y][x];
|
||||||
index &= (1 << depth) - 1;
|
index &= (1 << depth) - 1;
|
||||||
|
|
||||||
byte = y * depth
|
unsigned int byte = y * depth
|
||||||
+ x / 8 * raw_image->height / 8 * 8 * depth;
|
+ x / 8 * raw_image->height / 8 * 8 * depth;
|
||||||
gb->data[byte] |= (index & 1) << (7 - x % 8);
|
gb->data[byte] |= (index & 1) << (7 - x % 8);
|
||||||
if (depth == 2) {
|
if (depth == 2) {
|
||||||
@@ -65,7 +64,7 @@ void output_file(const struct Options *opts, const struct GBImage *gb)
|
|||||||
|
|
||||||
f = fopen(opts->outfile, "wb");
|
f = fopen(opts->outfile, "wb");
|
||||||
if (!f)
|
if (!f)
|
||||||
err(1, "%s: Opening output file '%s' failed", __func__,
|
err("%s: Opening output file '%s' failed", __func__,
|
||||||
opts->outfile);
|
opts->outfile);
|
||||||
|
|
||||||
fwrite(gb->data, 1, gb->size - gb->trim * 8 * depth, f);
|
fwrite(gb->data, 1, gb->size - gb->trim * 8 * depth, f);
|
||||||
@@ -141,7 +140,7 @@ int get_mirrored_tile_index(uint8_t *tile, uint8_t **tiles, int num_tiles,
|
|||||||
|
|
||||||
tile_yflip = malloc(tile_size);
|
tile_yflip = malloc(tile_size);
|
||||||
if (!tile_yflip)
|
if (!tile_yflip)
|
||||||
err(1, "%s: Failed to allocate memory for Y flip of tile",
|
err("%s: Failed to allocate memory for Y flip of tile",
|
||||||
__func__);
|
__func__);
|
||||||
yflip(tile, tile_yflip, tile_size);
|
yflip(tile, tile_yflip, tile_size);
|
||||||
index = get_tile_index(tile_yflip, tiles, num_tiles, tile_size);
|
index = get_tile_index(tile_yflip, tiles, num_tiles, tile_size);
|
||||||
@@ -153,7 +152,7 @@ int get_mirrored_tile_index(uint8_t *tile, uint8_t **tiles, int num_tiles,
|
|||||||
|
|
||||||
tile_xflip = malloc(tile_size);
|
tile_xflip = malloc(tile_size);
|
||||||
if (!tile_xflip)
|
if (!tile_xflip)
|
||||||
err(1, "%s: Failed to allocate memory for X flip of tile",
|
err("%s: Failed to allocate memory for X flip of tile",
|
||||||
__func__);
|
__func__);
|
||||||
xflip(tile, tile_xflip, tile_size);
|
xflip(tile, tile_xflip, tile_size);
|
||||||
index = get_tile_index(tile_xflip, tiles, num_tiles, tile_size);
|
index = get_tile_index(tile_xflip, tiles, num_tiles, tile_size);
|
||||||
@@ -198,13 +197,13 @@ void create_mapfiles(const struct Options *opts, struct GBImage *gb,
|
|||||||
|
|
||||||
tiles = calloc(max_tiles, sizeof(*tiles));
|
tiles = calloc(max_tiles, sizeof(*tiles));
|
||||||
if (!tiles)
|
if (!tiles)
|
||||||
err(1, "%s: Failed to allocate memory for tiles", __func__);
|
err("%s: Failed to allocate memory for tiles", __func__);
|
||||||
num_tiles = 0;
|
num_tiles = 0;
|
||||||
|
|
||||||
if (*opts->tilemapfile) {
|
if (*opts->tilemapfile) {
|
||||||
tilemap->data = calloc(max_tiles, sizeof(*tilemap->data));
|
tilemap->data = calloc(max_tiles, sizeof(*tilemap->data));
|
||||||
if (!tilemap->data)
|
if (!tilemap->data)
|
||||||
err(1, "%s: Failed to allocate memory for tilemap data",
|
err("%s: Failed to allocate memory for tilemap data",
|
||||||
__func__);
|
__func__);
|
||||||
tilemap->size = 0;
|
tilemap->size = 0;
|
||||||
}
|
}
|
||||||
@@ -212,7 +211,7 @@ void create_mapfiles(const struct Options *opts, struct GBImage *gb,
|
|||||||
if (*opts->attrmapfile) {
|
if (*opts->attrmapfile) {
|
||||||
attrmap->data = calloc(max_tiles, sizeof(*attrmap->data));
|
attrmap->data = calloc(max_tiles, sizeof(*attrmap->data));
|
||||||
if (!attrmap->data)
|
if (!attrmap->data)
|
||||||
err(1, "%s: Failed to allocate memory for attrmap data",
|
err("%s: Failed to allocate memory for attrmap data",
|
||||||
__func__);
|
__func__);
|
||||||
attrmap->size = 0;
|
attrmap->size = 0;
|
||||||
}
|
}
|
||||||
@@ -222,9 +221,13 @@ void create_mapfiles(const struct Options *opts, struct GBImage *gb,
|
|||||||
flags = 0;
|
flags = 0;
|
||||||
tile = malloc(tile_size);
|
tile = malloc(tile_size);
|
||||||
if (!tile)
|
if (!tile)
|
||||||
err(1, "%s: Failed to allocate memory for tile",
|
err("%s: Failed to allocate memory for tile",
|
||||||
__func__);
|
__func__);
|
||||||
for (i = 0; i < tile_size; i++) {
|
/*
|
||||||
|
* If the input image doesn't fill the last tile,
|
||||||
|
* `gb_i` will reach `gb_size`.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < tile_size && gb_i < gb_size; i++) {
|
||||||
tile[i] = gb->data[gb_i];
|
tile[i] = gb->data[gb_i];
|
||||||
gb_i++;
|
gb_i++;
|
||||||
}
|
}
|
||||||
@@ -265,7 +268,7 @@ void create_mapfiles(const struct Options *opts, struct GBImage *gb,
|
|||||||
free(gb->data);
|
free(gb->data);
|
||||||
gb->data = malloc(tile_size * num_tiles);
|
gb->data = malloc(tile_size * num_tiles);
|
||||||
if (!gb->data)
|
if (!gb->data)
|
||||||
err(1, "%s: Failed to allocate memory for tile data",
|
err("%s: Failed to allocate memory for tile data",
|
||||||
__func__);
|
__func__);
|
||||||
for (i = 0; i < num_tiles; i++) {
|
for (i = 0; i < num_tiles; i++) {
|
||||||
tile = tiles[i];
|
tile = tiles[i];
|
||||||
@@ -288,7 +291,7 @@ void output_tilemap_file(const struct Options *opts,
|
|||||||
|
|
||||||
f = fopen(opts->tilemapfile, "wb");
|
f = fopen(opts->tilemapfile, "wb");
|
||||||
if (!f)
|
if (!f)
|
||||||
err(1, "%s: Opening tilemap file '%s' failed", __func__,
|
err("%s: Opening tilemap file '%s' failed", __func__,
|
||||||
opts->tilemapfile);
|
opts->tilemapfile);
|
||||||
|
|
||||||
fwrite(tilemap->data, 1, tilemap->size, f);
|
fwrite(tilemap->data, 1, tilemap->size, f);
|
||||||
@@ -305,7 +308,7 @@ void output_attrmap_file(const struct Options *opts,
|
|||||||
|
|
||||||
f = fopen(opts->attrmapfile, "wb");
|
f = fopen(opts->attrmapfile, "wb");
|
||||||
if (!f)
|
if (!f)
|
||||||
err(1, "%s: Opening attrmap file '%s' failed", __func__,
|
err("%s: Opening attrmap file '%s' failed", __func__,
|
||||||
opts->attrmapfile);
|
opts->attrmapfile);
|
||||||
|
|
||||||
fwrite(attrmap->data, 1, attrmap->size, f);
|
fwrite(attrmap->data, 1, attrmap->size, f);
|
||||||
@@ -348,7 +351,7 @@ void output_palette_file(const struct Options *opts,
|
|||||||
|
|
||||||
f = fopen(opts->palfile, "wb");
|
f = fopen(opts->palfile, "wb");
|
||||||
if (!f)
|
if (!f)
|
||||||
err(1, "%s: Opening palette file '%s' failed", __func__,
|
err("%s: Opening palette file '%s' failed", __func__,
|
||||||
opts->palfile);
|
opts->palfile);
|
||||||
|
|
||||||
for (i = 0; i < raw_image->num_colors; i++) {
|
for (i = 0; i < raw_image->num_colors; i++) {
|
||||||
|
|||||||
+12
-12
@@ -96,7 +96,7 @@ int main(int argc, char *argv[])
|
|||||||
opts.attrmapout = true;
|
opts.attrmapout = true;
|
||||||
break;
|
break;
|
||||||
case 'a':
|
case 'a':
|
||||||
opts.attrmapfile = optarg;
|
opts.attrmapfile = musl_optarg;
|
||||||
break;
|
break;
|
||||||
case 'C':
|
case 'C':
|
||||||
opts.colorcurve = true;
|
opts.colorcurve = true;
|
||||||
@@ -105,7 +105,7 @@ int main(int argc, char *argv[])
|
|||||||
opts.debug = true;
|
opts.debug = true;
|
||||||
break;
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
depth = strtoul(optarg, NULL, 0);
|
depth = strtoul(musl_optarg, NULL, 0);
|
||||||
break;
|
break;
|
||||||
case 'F':
|
case 'F':
|
||||||
opts.hardfix = true;
|
opts.hardfix = true;
|
||||||
@@ -121,19 +121,19 @@ int main(int argc, char *argv[])
|
|||||||
opts.unique = true;
|
opts.unique = true;
|
||||||
break;
|
break;
|
||||||
case 'o':
|
case 'o':
|
||||||
opts.outfile = optarg;
|
opts.outfile = musl_optarg;
|
||||||
break;
|
break;
|
||||||
case 'P':
|
case 'P':
|
||||||
opts.palout = true;
|
opts.palout = true;
|
||||||
break;
|
break;
|
||||||
case 'p':
|
case 'p':
|
||||||
opts.palfile = optarg;
|
opts.palfile = musl_optarg;
|
||||||
break;
|
break;
|
||||||
case 'T':
|
case 'T':
|
||||||
opts.tilemapout = true;
|
opts.tilemapout = true;
|
||||||
break;
|
break;
|
||||||
case 't':
|
case 't':
|
||||||
opts.tilemapfile = optarg;
|
opts.tilemapfile = musl_optarg;
|
||||||
break;
|
break;
|
||||||
case 'u':
|
case 'u':
|
||||||
opts.unique = true;
|
opts.unique = true;
|
||||||
@@ -145,15 +145,15 @@ int main(int argc, char *argv[])
|
|||||||
opts.verbose = true;
|
opts.verbose = true;
|
||||||
break;
|
break;
|
||||||
case 'x':
|
case 'x':
|
||||||
opts.trim = strtoul(optarg, NULL, 0);
|
opts.trim = strtoul(musl_optarg, NULL, 0);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
print_usage();
|
print_usage();
|
||||||
/* NOTREACHED */
|
/* NOTREACHED */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
argc -= optind;
|
argc -= musl_optind;
|
||||||
argv += optind;
|
argv += musl_optind;
|
||||||
|
|
||||||
if (argc == 0) {
|
if (argc == 0) {
|
||||||
fputs("FATAL: no input files\n", stderr);
|
fputs("FATAL: no input files\n", stderr);
|
||||||
@@ -167,7 +167,7 @@ int main(int argc, char *argv[])
|
|||||||
opts.infile = argv[argc - 1];
|
opts.infile = argv[argc - 1];
|
||||||
|
|
||||||
if (depth != 1 && depth != 2)
|
if (depth != 1 && depth != 2)
|
||||||
errx(1, "Depth option must be either 1 or 2.");
|
errx("Depth option must be either 1 or 2.");
|
||||||
|
|
||||||
colors = 1 << depth;
|
colors = 1 << depth;
|
||||||
|
|
||||||
@@ -200,17 +200,17 @@ int main(int argc, char *argv[])
|
|||||||
opts.trim = png_options.trim;
|
opts.trim = png_options.trim;
|
||||||
|
|
||||||
if (raw_image->width % 8) {
|
if (raw_image->width % 8) {
|
||||||
errx(1, "Input PNG file %s not sized correctly. The image's width must be a multiple of 8.",
|
errx("Input PNG file %s not sized correctly. The image's width must be a multiple of 8.",
|
||||||
opts.infile);
|
opts.infile);
|
||||||
}
|
}
|
||||||
if (raw_image->width / 8 > 1 && raw_image->height % 8) {
|
if (raw_image->width / 8 > 1 && raw_image->height % 8) {
|
||||||
errx(1, "Input PNG file %s not sized correctly. If the image is more than 1 tile wide, its height must be a multiple of 8.",
|
errx("Input PNG file %s not sized correctly. If the image is more than 1 tile wide, its height must be a multiple of 8.",
|
||||||
opts.infile);
|
opts.infile);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opts.trim &&
|
if (opts.trim &&
|
||||||
opts.trim > (raw_image->width / 8) * (raw_image->height / 8) - 1) {
|
opts.trim > (raw_image->width / 8) * (raw_image->height / 8) - 1) {
|
||||||
errx(1, "Trim (%d) for input raw_image file '%s' too large (max: %u)",
|
errx("Trim (%d) for input raw_image file '%s' too large (max: %u)",
|
||||||
opts.trim, opts.infile,
|
opts.trim, opts.infile,
|
||||||
(raw_image->width / 8) * (raw_image->height / 8) - 1);
|
(raw_image->width / 8) * (raw_image->height / 8) - 1);
|
||||||
}
|
}
|
||||||
|
|||||||
+43
-44
@@ -32,7 +32,7 @@ struct RawIndexedImage *input_png_file(const struct Options *opts,
|
|||||||
|
|
||||||
f = fopen(opts->infile, "rb");
|
f = fopen(opts->infile, "rb");
|
||||||
if (!f)
|
if (!f)
|
||||||
err(1, "Opening input png file '%s' failed", opts->infile);
|
err("Opening input png file '%s' failed", opts->infile);
|
||||||
|
|
||||||
initialize_png(&img, f);
|
initialize_png(&img, f);
|
||||||
|
|
||||||
@@ -54,7 +54,7 @@ struct RawIndexedImage *input_png_file(const struct Options *opts,
|
|||||||
raw_image = truecolor_png_to_raw(&img); break;
|
raw_image = truecolor_png_to_raw(&img); break;
|
||||||
default:
|
default:
|
||||||
/* Shouldn't happen, but might as well handle just in case. */
|
/* Shouldn't happen, but might as well handle just in case. */
|
||||||
errx(1, "Input PNG file is of invalid color type.");
|
errx("Input PNG file is of invalid color type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
get_text(&img, png_options);
|
get_text(&img, png_options);
|
||||||
@@ -83,7 +83,7 @@ void output_png_file(const struct Options *opts,
|
|||||||
if (opts->debug) {
|
if (opts->debug) {
|
||||||
outfile = malloc(strlen(opts->infile) + 5);
|
outfile = malloc(strlen(opts->infile) + 5);
|
||||||
if (!outfile)
|
if (!outfile)
|
||||||
err(1, "%s: Failed to allocate memory for outfile",
|
err("%s: Failed to allocate memory for outfile",
|
||||||
__func__);
|
__func__);
|
||||||
strcpy(outfile, opts->infile);
|
strcpy(outfile, opts->infile);
|
||||||
strcat(outfile, ".out");
|
strcat(outfile, ".out");
|
||||||
@@ -93,16 +93,19 @@ void output_png_file(const struct Options *opts,
|
|||||||
|
|
||||||
f = fopen(outfile, "wb");
|
f = fopen(outfile, "wb");
|
||||||
if (!f)
|
if (!f)
|
||||||
err(1, "Opening output png file '%s' failed", outfile);
|
err("Opening output png file '%s' failed", outfile);
|
||||||
|
|
||||||
|
if (opts->debug)
|
||||||
|
free(outfile);
|
||||||
|
|
||||||
img.png = png_create_write_struct(PNG_LIBPNG_VER_STRING,
|
img.png = png_create_write_struct(PNG_LIBPNG_VER_STRING,
|
||||||
NULL, NULL, NULL);
|
NULL, NULL, NULL);
|
||||||
if (!img.png)
|
if (!img.png)
|
||||||
errx(1, "Creating png structure failed");
|
errx("Creating png structure failed");
|
||||||
|
|
||||||
img.info = png_create_info_struct(img.png);
|
img.info = png_create_info_struct(img.png);
|
||||||
if (!img.info)
|
if (!img.info)
|
||||||
errx(1, "Creating png info structure failed");
|
errx("Creating png info structure failed");
|
||||||
|
|
||||||
if (setjmp(png_jmpbuf(img.png)))
|
if (setjmp(png_jmpbuf(img.png)))
|
||||||
exit(1);
|
exit(1);
|
||||||
@@ -115,7 +118,7 @@ void output_png_file(const struct Options *opts,
|
|||||||
|
|
||||||
png_palette = malloc(sizeof(*png_palette) * raw_image->num_colors);
|
png_palette = malloc(sizeof(*png_palette) * raw_image->num_colors);
|
||||||
if (!png_palette)
|
if (!png_palette)
|
||||||
err(1, "%s: Failed to allocate memory for PNG palette",
|
err("%s: Failed to allocate memory for PNG palette",
|
||||||
__func__);
|
__func__);
|
||||||
for (i = 0; i < raw_image->num_colors; i++) {
|
for (i = 0; i < raw_image->num_colors; i++) {
|
||||||
png_palette[i].red = raw_image->palette[i].red;
|
png_palette[i].red = raw_image->palette[i].red;
|
||||||
@@ -135,17 +138,13 @@ void output_png_file(const struct Options *opts,
|
|||||||
|
|
||||||
png_destroy_write_struct(&img.png, &img.info);
|
png_destroy_write_struct(&img.png, &img.info);
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
if (opts->debug)
|
|
||||||
free(outfile);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void destroy_raw_image(struct RawIndexedImage **raw_image_ptr_ptr)
|
void destroy_raw_image(struct RawIndexedImage **raw_image_ptr_ptr)
|
||||||
{
|
{
|
||||||
int y;
|
|
||||||
struct RawIndexedImage *raw_image = *raw_image_ptr_ptr;
|
struct RawIndexedImage *raw_image = *raw_image_ptr_ptr;
|
||||||
|
|
||||||
for (y = 0; y < raw_image->height; y++)
|
for (unsigned int y = 0; y < raw_image->height; y++)
|
||||||
free(raw_image->data[y]);
|
free(raw_image->data[y]);
|
||||||
|
|
||||||
free(raw_image->data);
|
free(raw_image->data);
|
||||||
@@ -159,11 +158,11 @@ static void initialize_png(struct PNGImage *img, FILE *f)
|
|||||||
img->png = png_create_read_struct(PNG_LIBPNG_VER_STRING,
|
img->png = png_create_read_struct(PNG_LIBPNG_VER_STRING,
|
||||||
NULL, NULL, NULL);
|
NULL, NULL, NULL);
|
||||||
if (!img->png)
|
if (!img->png)
|
||||||
errx(1, "Creating png structure failed");
|
errx("Creating png structure failed");
|
||||||
|
|
||||||
img->info = png_create_info_struct(img->png);
|
img->info = png_create_info_struct(img->png);
|
||||||
if (!img->info)
|
if (!img->info)
|
||||||
errx(1, "Creating png info structure failed");
|
errx("Creating png info structure failed");
|
||||||
|
|
||||||
if (setjmp(png_jmpbuf(img->png)))
|
if (setjmp(png_jmpbuf(img->png)))
|
||||||
exit(1);
|
exit(1);
|
||||||
@@ -182,7 +181,7 @@ static void read_png(struct PNGImage *img);
|
|||||||
static struct RawIndexedImage *create_raw_image(int width, int height,
|
static struct RawIndexedImage *create_raw_image(int width, int height,
|
||||||
int num_colors);
|
int num_colors);
|
||||||
static void set_raw_image_palette(struct RawIndexedImage *raw_image,
|
static void set_raw_image_palette(struct RawIndexedImage *raw_image,
|
||||||
const png_color *palette, int num_colors);
|
png_color const *palette, int num_colors);
|
||||||
|
|
||||||
static struct RawIndexedImage *indexed_png_to_raw(struct PNGImage *img)
|
static struct RawIndexedImage *indexed_png_to_raw(struct PNGImage *img)
|
||||||
{
|
{
|
||||||
@@ -215,13 +214,13 @@ static struct RawIndexedImage *indexed_png_to_raw(struct PNGImage *img)
|
|||||||
original_palette = palette;
|
original_palette = palette;
|
||||||
palette = malloc(sizeof(*palette) * colors_in_PLTE);
|
palette = malloc(sizeof(*palette) * colors_in_PLTE);
|
||||||
if (!palette)
|
if (!palette)
|
||||||
err(1, "%s: Failed to allocate memory for palette",
|
err("%s: Failed to allocate memory for palette",
|
||||||
__func__);
|
__func__);
|
||||||
colors_in_new_palette = 0;
|
colors_in_new_palette = 0;
|
||||||
old_to_new_palette = malloc(sizeof(*old_to_new_palette)
|
old_to_new_palette = malloc(sizeof(*old_to_new_palette)
|
||||||
* colors_in_PLTE);
|
* colors_in_PLTE);
|
||||||
if (!old_to_new_palette)
|
if (!old_to_new_palette)
|
||||||
err(1, "%s: Failed to allocate memory for new palette",
|
err("%s: Failed to allocate memory for new palette",
|
||||||
__func__);
|
__func__);
|
||||||
|
|
||||||
for (i = 0; i < num_trans; i++) {
|
for (i = 0; i < num_trans; i++) {
|
||||||
@@ -243,7 +242,7 @@ static struct RawIndexedImage *indexed_png_to_raw(struct PNGImage *img)
|
|||||||
sizeof(*palette) *
|
sizeof(*palette) *
|
||||||
colors_in_new_palette);
|
colors_in_new_palette);
|
||||||
if (!palette)
|
if (!palette)
|
||||||
err(1, "%s: Failed to allocate memory for palette",
|
err("%s: Failed to allocate memory for palette",
|
||||||
__func__);
|
__func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,7 +290,7 @@ static void rgba_png_palette(struct PNGImage *img,
|
|||||||
png_color **palette_ptr_ptr, int *num_colors);
|
png_color **palette_ptr_ptr, int *num_colors);
|
||||||
static struct RawIndexedImage
|
static struct RawIndexedImage
|
||||||
*processed_rgba_png_to_raw(const struct PNGImage *img,
|
*processed_rgba_png_to_raw(const struct PNGImage *img,
|
||||||
const png_color *palette,
|
png_color const *palette,
|
||||||
int colors_in_palette);
|
int colors_in_palette);
|
||||||
|
|
||||||
static struct RawIndexedImage *truecolor_png_to_raw(struct PNGImage *img)
|
static struct RawIndexedImage *truecolor_png_to_raw(struct PNGImage *img)
|
||||||
@@ -352,7 +351,7 @@ static void rgba_PLTE_palette(struct PNGImage *img,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void update_built_palette(png_color *palette,
|
static void update_built_palette(png_color *palette,
|
||||||
const png_color *pixel_color, png_byte alpha,
|
png_color const *pixel_color, png_byte alpha,
|
||||||
int *num_colors, bool *only_grayscale);
|
int *num_colors, bool *only_grayscale);
|
||||||
static int fit_grayscale_palette(png_color *palette, int *num_colors);
|
static int fit_grayscale_palette(png_color *palette, int *num_colors);
|
||||||
static void order_color_palette(png_color *palette, int num_colors);
|
static void order_color_palette(png_color *palette, int num_colors);
|
||||||
@@ -372,7 +371,7 @@ static void rgba_build_palette(struct PNGImage *img,
|
|||||||
*/
|
*/
|
||||||
*palette_ptr_ptr = calloc(colors, sizeof(**palette_ptr_ptr));
|
*palette_ptr_ptr = calloc(colors, sizeof(**palette_ptr_ptr));
|
||||||
if (!*palette_ptr_ptr)
|
if (!*palette_ptr_ptr)
|
||||||
err(1, "%s: Failed to allocate memory for palette", __func__);
|
err("%s: Failed to allocate memory for palette", __func__);
|
||||||
palette = *palette_ptr_ptr;
|
palette = *palette_ptr_ptr;
|
||||||
*num_colors = 0;
|
*num_colors = 0;
|
||||||
|
|
||||||
@@ -398,7 +397,7 @@ static void rgba_build_palette(struct PNGImage *img,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void update_built_palette(png_color *palette,
|
static void update_built_palette(png_color *palette,
|
||||||
const png_color *pixel_color, png_byte alpha,
|
png_color const *pixel_color, png_byte alpha,
|
||||||
int *num_colors, bool *only_grayscale)
|
int *num_colors, bool *only_grayscale)
|
||||||
{
|
{
|
||||||
bool color_exists;
|
bool color_exists;
|
||||||
@@ -429,7 +428,7 @@ static void update_built_palette(png_color *palette,
|
|||||||
}
|
}
|
||||||
if (!color_exists) {
|
if (!color_exists) {
|
||||||
if (*num_colors == colors) {
|
if (*num_colors == colors) {
|
||||||
errx(1, "Too many colors in input PNG file to fit into a %d-bit palette (max %d).",
|
errx("Too many colors in input PNG file to fit into a %d-bit palette (max %d).",
|
||||||
depth, colors);
|
depth, colors);
|
||||||
}
|
}
|
||||||
palette[*num_colors] = *pixel_color;
|
palette[*num_colors] = *pixel_color;
|
||||||
@@ -445,9 +444,9 @@ static int fit_grayscale_palette(png_color *palette, int *num_colors)
|
|||||||
int i, shade_index;
|
int i, shade_index;
|
||||||
|
|
||||||
if (!fitted_palette)
|
if (!fitted_palette)
|
||||||
err(1, "%s: Failed to allocate memory for palette", __func__);
|
err("%s: Failed to allocate memory for palette", __func__);
|
||||||
if (!set_indices)
|
if (!set_indices)
|
||||||
err(1, "%s: Failed to allocate memory for indices", __func__);
|
err("%s: Failed to allocate memory for indices", __func__);
|
||||||
|
|
||||||
fitted_palette[0].red = 0xFF;
|
fitted_palette[0].red = 0xFF;
|
||||||
fitted_palette[0].green = 0xFF;
|
fitted_palette[0].green = 0xFF;
|
||||||
@@ -491,7 +490,7 @@ struct ColorWithLuminance {
|
|||||||
int luminance;
|
int luminance;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int compare_luminance(const void *a, const void *b)
|
static int compare_luminance(void const *a, void const *b)
|
||||||
{
|
{
|
||||||
const struct ColorWithLuminance *x, *y;
|
const struct ColorWithLuminance *x, *y;
|
||||||
|
|
||||||
@@ -508,7 +507,7 @@ static void order_color_palette(png_color *palette, int num_colors)
|
|||||||
malloc(sizeof(*palette_with_luminance) * num_colors);
|
malloc(sizeof(*palette_with_luminance) * num_colors);
|
||||||
|
|
||||||
if (!palette_with_luminance)
|
if (!palette_with_luminance)
|
||||||
err(1, "%s: Failed to allocate memory for palette", __func__);
|
err("%s: Failed to allocate memory for palette", __func__);
|
||||||
|
|
||||||
for (i = 0; i < num_colors; i++) {
|
for (i = 0; i < num_colors; i++) {
|
||||||
/*
|
/*
|
||||||
@@ -531,12 +530,12 @@ static void order_color_palette(png_color *palette, int num_colors)
|
|||||||
static void put_raw_image_pixel(struct RawIndexedImage *raw_image,
|
static void put_raw_image_pixel(struct RawIndexedImage *raw_image,
|
||||||
const struct PNGImage *img,
|
const struct PNGImage *img,
|
||||||
int *value_index, int x, int y,
|
int *value_index, int x, int y,
|
||||||
const png_color *palette,
|
png_color const *palette,
|
||||||
int colors_in_palette);
|
int colors_in_palette);
|
||||||
|
|
||||||
static struct RawIndexedImage
|
static struct RawIndexedImage
|
||||||
*processed_rgba_png_to_raw(const struct PNGImage *img,
|
*processed_rgba_png_to_raw(const struct PNGImage *img,
|
||||||
const png_color *palette,
|
png_color const *palette,
|
||||||
int colors_in_palette)
|
int colors_in_palette)
|
||||||
{
|
{
|
||||||
struct RawIndexedImage *raw_image;
|
struct RawIndexedImage *raw_image;
|
||||||
@@ -561,13 +560,13 @@ static struct RawIndexedImage
|
|||||||
return raw_image;
|
return raw_image;
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t palette_index_of(const png_color *palette,
|
static uint8_t palette_index_of(png_color const *palette,
|
||||||
int num_colors, const png_color *color);
|
int num_colors, png_color const *color);
|
||||||
|
|
||||||
static void put_raw_image_pixel(struct RawIndexedImage *raw_image,
|
static void put_raw_image_pixel(struct RawIndexedImage *raw_image,
|
||||||
const struct PNGImage *img,
|
const struct PNGImage *img,
|
||||||
int *value_index, int x, int y,
|
int *value_index, int x, int y,
|
||||||
const png_color *palette,
|
png_color const *palette,
|
||||||
int colors_in_palette)
|
int colors_in_palette)
|
||||||
{
|
{
|
||||||
png_color pixel_color;
|
png_color pixel_color;
|
||||||
@@ -588,8 +587,8 @@ static void put_raw_image_pixel(struct RawIndexedImage *raw_image,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t palette_index_of(const png_color *palette,
|
static uint8_t palette_index_of(png_color const *palette,
|
||||||
int num_colors, const png_color *color)
|
int num_colors, png_color const *color)
|
||||||
{
|
{
|
||||||
uint8_t i;
|
uint8_t i;
|
||||||
|
|
||||||
@@ -600,7 +599,7 @@ static uint8_t palette_index_of(const png_color *palette,
|
|||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
errx(1, "The input PNG file contains colors that don't appear in its embedded palette.");
|
errx("The input PNG file contains colors that don't appear in its embedded palette.");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void read_png(struct PNGImage *img)
|
static void read_png(struct PNGImage *img)
|
||||||
@@ -611,12 +610,12 @@ static void read_png(struct PNGImage *img)
|
|||||||
|
|
||||||
img->data = malloc(sizeof(*img->data) * img->height);
|
img->data = malloc(sizeof(*img->data) * img->height);
|
||||||
if (!img->data)
|
if (!img->data)
|
||||||
err(1, "%s: Failed to allocate memory for image data",
|
err("%s: Failed to allocate memory for image data",
|
||||||
__func__);
|
__func__);
|
||||||
for (y = 0; y < img->height; y++) {
|
for (y = 0; y < img->height; y++) {
|
||||||
img->data[y] = malloc(png_get_rowbytes(img->png, img->info));
|
img->data[y] = malloc(png_get_rowbytes(img->png, img->info));
|
||||||
if (!img->data[y])
|
if (!img->data[y])
|
||||||
err(1, "%s: Failed to allocate memory for image data",
|
err("%s: Failed to allocate memory for image data",
|
||||||
__func__);
|
__func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -632,7 +631,7 @@ static struct RawIndexedImage *create_raw_image(int width, int height,
|
|||||||
|
|
||||||
raw_image = malloc(sizeof(*raw_image));
|
raw_image = malloc(sizeof(*raw_image));
|
||||||
if (!raw_image)
|
if (!raw_image)
|
||||||
err(1, "%s: Failed to allocate memory for raw image",
|
err("%s: Failed to allocate memory for raw image",
|
||||||
__func__);
|
__func__);
|
||||||
|
|
||||||
raw_image->width = width;
|
raw_image->width = width;
|
||||||
@@ -641,18 +640,18 @@ static struct RawIndexedImage *create_raw_image(int width, int height,
|
|||||||
|
|
||||||
raw_image->palette = malloc(sizeof(*raw_image->palette) * num_colors);
|
raw_image->palette = malloc(sizeof(*raw_image->palette) * num_colors);
|
||||||
if (!raw_image->palette)
|
if (!raw_image->palette)
|
||||||
err(1, "%s: Failed to allocate memory for raw image palette",
|
err("%s: Failed to allocate memory for raw image palette",
|
||||||
__func__);
|
__func__);
|
||||||
|
|
||||||
raw_image->data = malloc(sizeof(*raw_image->data) * height);
|
raw_image->data = malloc(sizeof(*raw_image->data) * height);
|
||||||
if (!raw_image->data)
|
if (!raw_image->data)
|
||||||
err(1, "%s: Failed to allocate memory for raw image data",
|
err("%s: Failed to allocate memory for raw image data",
|
||||||
__func__);
|
__func__);
|
||||||
for (y = 0; y < height; y++) {
|
for (y = 0; y < height; y++) {
|
||||||
raw_image->data[y] = malloc(sizeof(*raw_image->data[y])
|
raw_image->data[y] = malloc(sizeof(*raw_image->data[y])
|
||||||
* width);
|
* width);
|
||||||
if (!raw_image->data[y])
|
if (!raw_image->data[y])
|
||||||
err(1, "%s: Failed to allocate memory for raw image data",
|
err("%s: Failed to allocate memory for raw image data",
|
||||||
__func__);
|
__func__);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -660,12 +659,12 @@ static struct RawIndexedImage *create_raw_image(int width, int height,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void set_raw_image_palette(struct RawIndexedImage *raw_image,
|
static void set_raw_image_palette(struct RawIndexedImage *raw_image,
|
||||||
const png_color *palette, int num_colors)
|
png_color const *palette, int num_colors)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
if (num_colors > raw_image->num_colors) {
|
if (num_colors > raw_image->num_colors) {
|
||||||
errx(1, "Too many colors in input PNG file's palette to fit into a %d-bit palette (%d in input palette, max %d).",
|
errx("Too many colors in input PNG file's palette to fit into a %d-bit palette (%d in input palette, max %d).",
|
||||||
raw_image->num_colors >> 1,
|
raw_image->num_colors >> 1,
|
||||||
num_colors, raw_image->num_colors);
|
num_colors, raw_image->num_colors);
|
||||||
}
|
}
|
||||||
@@ -740,7 +739,7 @@ static void set_text(const struct PNGImage *img,
|
|||||||
|
|
||||||
text = malloc(sizeof(*text));
|
text = malloc(sizeof(*text));
|
||||||
if (!text)
|
if (!text)
|
||||||
err(1, "%s: Failed to allocate memory for PNG text",
|
err("%s: Failed to allocate memory for PNG text",
|
||||||
__func__);
|
__func__);
|
||||||
|
|
||||||
if (png_options->horizontal) {
|
if (png_options->horizontal) {
|
||||||
|
|||||||
+4
-3
@@ -1,11 +1,11 @@
|
|||||||
.\"
|
.\"
|
||||||
.\" This file is part of RGBDS.
|
.\" This file is part of RGBDS.
|
||||||
.\"
|
.\"
|
||||||
.\" Copyright (c) 2013-2018, stag019 and RGBDS contributors.
|
.\" Copyright (c) 2013-2021, stag019 and RGBDS contributors.
|
||||||
.\"
|
.\"
|
||||||
.\" SPDX-License-Identifier: MIT
|
.\" SPDX-License-Identifier: MIT
|
||||||
.\"
|
.\"
|
||||||
.Dd December 5, 2019
|
.Dd March 28, 2021
|
||||||
.Dt RGBGFX 1
|
.Dt RGBGFX 1
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -78,7 +78,8 @@ Same as
|
|||||||
.Fl f ,
|
.Fl f ,
|
||||||
but additionally, the supplied command line parameters are saved within the PNG and will be loaded and automatically used next time.
|
but additionally, the supplied command line parameters are saved within the PNG and will be loaded and automatically used next time.
|
||||||
.It Fl h , Fl Fl horizontal
|
.It Fl h , Fl Fl horizontal
|
||||||
Lay out tiles horizontally rather than vertically.
|
Lay out tiles in column-major order (column by column), instead of the default row-major order (line by line).
|
||||||
|
Especially useful for "8x16" OBJ mode, if the input image is 16 pixels tall.
|
||||||
.It Fl m , Fl Fl mirror-tiles
|
.It Fl m , Fl Fl mirror-tiles
|
||||||
Truncate tiles by checking for tiles that are mirrored versions of others and omitting these from the output file.
|
Truncate tiles by checking for tiles that are mirrored versions of others and omitting these from the output file.
|
||||||
Useful with tilemaps and attrmaps together to keep track of the duplicated tiles and the dimension mirrored.
|
Useful with tilemaps and attrmaps together to keep track of the duplicated tiles and the dimension mirrored.
|
||||||
|
|||||||
+13
-22
@@ -12,8 +12,8 @@
|
|||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
|
#include "error.h"
|
||||||
#include "hashmap.h"
|
#include "hashmap.h"
|
||||||
#include "extern/err.h"
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The lower half of the hash is used to index the "master" table,
|
* The lower half of the hash is used to index the "master" table,
|
||||||
@@ -46,14 +46,14 @@ static HashType hash(char const *str)
|
|||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hash_AddElement(HashMap map, char const *key, void *element)
|
void **hash_AddElement(HashMap map, char const *key, void *element)
|
||||||
{
|
{
|
||||||
HashType hashedKey = hash(key);
|
HashType hashedKey = hash(key);
|
||||||
HalfHashType index = hashedKey;
|
HalfHashType index = hashedKey;
|
||||||
struct HashMapEntry *newEntry = malloc(sizeof(*newEntry));
|
struct HashMapEntry *newEntry = malloc(sizeof(*newEntry));
|
||||||
|
|
||||||
if (!newEntry)
|
if (!newEntry)
|
||||||
err(1, "%s: Failed to allocate new entry", __func__);
|
err("%s: Failed to allocate new entry", __func__);
|
||||||
|
|
||||||
newEntry->hash = hashedKey >> HALF_HASH_NB_BITS;
|
newEntry->hash = hashedKey >> HALF_HASH_NB_BITS;
|
||||||
newEntry->key = key;
|
newEntry->key = key;
|
||||||
@@ -61,23 +61,7 @@ bool hash_AddElement(HashMap map, char const *key, void *element)
|
|||||||
newEntry->next = map[index];
|
newEntry->next = map[index];
|
||||||
map[index] = newEntry;
|
map[index] = newEntry;
|
||||||
|
|
||||||
return newEntry->next != NULL;
|
return &newEntry->content;
|
||||||
}
|
|
||||||
|
|
||||||
bool hash_ReplaceElement(HashMap const map, char const *key, void *element)
|
|
||||||
{
|
|
||||||
HashType hashedKey = hash(key);
|
|
||||||
struct HashMapEntry *ptr = map[(HalfHashType)hashedKey];
|
|
||||||
|
|
||||||
while (ptr) {
|
|
||||||
if (hashedKey >> HALF_HASH_NB_BITS == ptr->hash
|
|
||||||
&& !strcmp(ptr->key, key)) {
|
|
||||||
ptr->content = element;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
ptr = ptr->next;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hash_RemoveElement(HashMap map, char const *key)
|
bool hash_RemoveElement(HashMap map, char const *key)
|
||||||
@@ -99,7 +83,7 @@ bool hash_RemoveElement(HashMap map, char const *key)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void *hash_GetElement(HashMap const map, char const *key)
|
void **hash_GetNode(HashMap const map, char const *key)
|
||||||
{
|
{
|
||||||
HashType hashedKey = hash(key);
|
HashType hashedKey = hash(key);
|
||||||
struct HashMapEntry *ptr = map[(HalfHashType)hashedKey];
|
struct HashMapEntry *ptr = map[(HalfHashType)hashedKey];
|
||||||
@@ -107,13 +91,20 @@ void *hash_GetElement(HashMap const map, char const *key)
|
|||||||
while (ptr) {
|
while (ptr) {
|
||||||
if (hashedKey >> HALF_HASH_NB_BITS == ptr->hash
|
if (hashedKey >> HALF_HASH_NB_BITS == ptr->hash
|
||||||
&& !strcmp(ptr->key, key)) {
|
&& !strcmp(ptr->key, key)) {
|
||||||
return ptr->content;
|
return &ptr->content;
|
||||||
}
|
}
|
||||||
ptr = ptr->next;
|
ptr = ptr->next;
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void *hash_GetElement(HashMap const map, char const *key)
|
||||||
|
{
|
||||||
|
void **node = hash_GetNode(map, key);
|
||||||
|
|
||||||
|
return node ? *node : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
void hash_ForEach(HashMap const map, void (*func)(void *, void *), void *arg)
|
void hash_ForEach(HashMap const map, void (*func)(void *, void *), void *arg)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < HASHMAP_NB_BUCKETS; i++) {
|
for (size_t i = 0; i < HASHMAP_NB_BUCKETS; i++) {
|
||||||
|
|||||||
+39
-14
@@ -19,7 +19,7 @@
|
|||||||
#include "link/script.h"
|
#include "link/script.h"
|
||||||
#include "link/output.h"
|
#include "link/output.h"
|
||||||
|
|
||||||
#include "extern/err.h"
|
#include "error.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
|
|
||||||
struct MemoryLocation {
|
struct MemoryLocation {
|
||||||
@@ -46,13 +46,13 @@ static void initFreeSpace(void)
|
|||||||
for (enum SectionType type = 0; type < SECTTYPE_INVALID; type++) {
|
for (enum SectionType type = 0; type < SECTTYPE_INVALID; type++) {
|
||||||
memory[type] = malloc(sizeof(*memory[type]) * nbbanks(type));
|
memory[type] = malloc(sizeof(*memory[type]) * nbbanks(type));
|
||||||
if (!memory[type])
|
if (!memory[type])
|
||||||
err(1, "Failed to init free space for region %d", type);
|
err("Failed to init free space for region %d", type);
|
||||||
|
|
||||||
for (uint32_t bank = 0; bank < nbbanks(type); bank++) {
|
for (uint32_t bank = 0; bank < nbbanks(type); bank++) {
|
||||||
memory[type][bank].next =
|
memory[type][bank].next =
|
||||||
malloc(sizeof(*memory[type][0].next));
|
malloc(sizeof(*memory[type][0].next));
|
||||||
if (!memory[type][bank].next)
|
if (!memory[type][bank].next)
|
||||||
err(1, "Failed to init free space for region %d bank %" PRIu32,
|
err("Failed to init free space for region %d bank %" PRIu32,
|
||||||
type, bank);
|
type, bank);
|
||||||
memory[type][bank].next->address = startaddr[type];
|
memory[type][bank].next->address = startaddr[type];
|
||||||
memory[type][bank].next->size = maxsize[type];
|
memory[type][bank].next->size = maxsize[type];
|
||||||
@@ -106,12 +106,18 @@ static void processLinkerScript(void)
|
|||||||
* @param section The section to assign
|
* @param section The section to assign
|
||||||
* @param location The location to assign the section to
|
* @param location The location to assign the section to
|
||||||
*/
|
*/
|
||||||
static inline void assignSection(struct Section *section,
|
static void assignSection(struct Section *section, struct MemoryLocation const *location)
|
||||||
struct MemoryLocation const *location)
|
|
||||||
{
|
{
|
||||||
section->org = location->address;
|
section->org = location->address;
|
||||||
section->bank = location->bank;
|
section->bank = location->bank;
|
||||||
|
|
||||||
|
// Propagate the assigned location to all UNIONs/FRAGMENTs
|
||||||
|
// so `jr` patches in them will have the correct offset
|
||||||
|
for (struct Section *next = section->nextu; next != NULL; next = next->nextu) {
|
||||||
|
next->org = section->org;
|
||||||
|
next->bank = section->bank;
|
||||||
|
}
|
||||||
|
|
||||||
nbSectionsToAssign--;
|
nbSectionsToAssign--;
|
||||||
|
|
||||||
out_AddSection(section);
|
out_AddSection(section);
|
||||||
@@ -153,9 +159,28 @@ static bool isLocationSuitable(struct Section const *section,
|
|||||||
static struct FreeSpace *getPlacement(struct Section const *section,
|
static struct FreeSpace *getPlacement(struct Section const *section,
|
||||||
struct MemoryLocation *location)
|
struct MemoryLocation *location)
|
||||||
{
|
{
|
||||||
location->bank = section->isBankFixed
|
static uint16_t curScrambleROM = 1;
|
||||||
? section->bank
|
static uint8_t curScrambleWRAM = 1;
|
||||||
: bankranges[section->type][0];
|
static uint8_t curScrambleSRAM = 1;
|
||||||
|
|
||||||
|
// Determine which bank we should start searching in
|
||||||
|
if (section->isBankFixed) {
|
||||||
|
location->bank = section->bank;
|
||||||
|
} else if (scrambleROMX && section->type == SECTTYPE_ROMX) {
|
||||||
|
location->bank = curScrambleROM++;
|
||||||
|
if (curScrambleROM > scrambleROMX)
|
||||||
|
curScrambleROM = 1;
|
||||||
|
} else if (scrambleWRAMX && section->type == SECTTYPE_WRAMX) {
|
||||||
|
location->bank = curScrambleWRAM++;
|
||||||
|
if (curScrambleWRAM > scrambleWRAMX)
|
||||||
|
curScrambleWRAM = 1;
|
||||||
|
} else if (scrambleSRAM && section->type == SECTTYPE_SRAM) {
|
||||||
|
location->bank = curScrambleSRAM++;
|
||||||
|
if (curScrambleSRAM > scrambleSRAM)
|
||||||
|
curScrambleSRAM = 0;
|
||||||
|
} else {
|
||||||
|
location->bank = bankranges[section->type][0];
|
||||||
|
}
|
||||||
struct FreeSpace *space;
|
struct FreeSpace *space;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
@@ -276,7 +301,7 @@ static void placeSection(struct Section *section)
|
|||||||
struct FreeSpace *newSpace = malloc(sizeof(*newSpace));
|
struct FreeSpace *newSpace = malloc(sizeof(*newSpace));
|
||||||
|
|
||||||
if (!newSpace)
|
if (!newSpace)
|
||||||
err(1, "Failed to split new free space");
|
err("Failed to split new free space");
|
||||||
/* Append the new space after the chosen one */
|
/* Append the new space after the chosen one */
|
||||||
newSpace->prev = freeSpace;
|
newSpace->prev = freeSpace;
|
||||||
newSpace->next = freeSpace->next;
|
newSpace->next = freeSpace->next;
|
||||||
@@ -327,16 +352,16 @@ static void placeSection(struct Section *section)
|
|||||||
|
|
||||||
/* If a section failed to go to several places, nothing we can report */
|
/* If a section failed to go to several places, nothing we can report */
|
||||||
if (!section->isBankFixed || !section->isAddressFixed)
|
if (!section->isBankFixed || !section->isAddressFixed)
|
||||||
errx(1, "Unable to place \"%s\" (%s section) %s",
|
errx("Unable to place \"%s\" (%s section) %s",
|
||||||
section->name, typeNames[section->type], where);
|
section->name, typeNames[section->type], where);
|
||||||
/* If the section just can't fit the bank, report that */
|
/* If the section just can't fit the bank, report that */
|
||||||
else if (section->org + section->size > endaddr(section->type) + 1)
|
else if (section->org + section->size > endaddr(section->type) + 1)
|
||||||
errx(1, "Unable to place \"%s\" (%s section) %s: section runs past end of region ($%04" PRIx16 " > $%04" PRIx16 ")",
|
errx("Unable to place \"%s\" (%s section) %s: section runs past end of region ($%04x > $%04x)",
|
||||||
section->name, typeNames[section->type], where,
|
section->name, typeNames[section->type], where,
|
||||||
section->org + section->size, endaddr(section->type) + 1);
|
section->org + section->size, endaddr(section->type) + 1);
|
||||||
/* Otherwise there is overlap with another section */
|
/* Otherwise there is overlap with another section */
|
||||||
else
|
else
|
||||||
errx(1, "Unable to place \"%s\" (%s section) %s: section overlaps with \"%s\"",
|
errx("Unable to place \"%s\" (%s section) %s: section overlaps with \"%s\"",
|
||||||
section->name, typeNames[section->type], where,
|
section->name, typeNames[section->type], where,
|
||||||
out_OverlappingSection(section)->name);
|
out_OverlappingSection(section)->name);
|
||||||
}
|
}
|
||||||
@@ -393,7 +418,7 @@ void assign_AssignSections(void)
|
|||||||
/* Generate linked lists of sections to assign */
|
/* Generate linked lists of sections to assign */
|
||||||
sections = malloc(sizeof(*sections) * nbSectionsToAssign + 1);
|
sections = malloc(sizeof(*sections) * nbSectionsToAssign + 1);
|
||||||
if (!sections)
|
if (!sections)
|
||||||
err(1, "Failed to allocate memory for section assignment");
|
err("Failed to allocate memory for section assignment");
|
||||||
|
|
||||||
initFreeSpace();
|
initFreeSpace();
|
||||||
|
|
||||||
@@ -422,7 +447,7 @@ void assign_AssignSections(void)
|
|||||||
/* Overlaying requires only fully-constrained sections */
|
/* Overlaying requires only fully-constrained sections */
|
||||||
verbosePrint("Assigning other sections...\n");
|
verbosePrint("Assigning other sections...\n");
|
||||||
if (overlayFileName)
|
if (overlayFileName)
|
||||||
errx(1, "All sections must be fixed when using an overlay file; %" PRIu64 " %sn't",
|
errx("All sections must be fixed when using an overlay file; %" PRIu64 " %sn't",
|
||||||
nbSectionsToAssign, nbSectionsToAssign == 1 ? "is" : "are");
|
nbSectionsToAssign, nbSectionsToAssign == 1 ? "is" : "are");
|
||||||
|
|
||||||
/* Assign all remaining sections by decreasing constraint order */
|
/* Assign all remaining sections by decreasing constraint order */
|
||||||
|
|||||||
+185
-35
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
#include <limits.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
@@ -24,8 +25,10 @@
|
|||||||
#include "link/patch.h"
|
#include "link/patch.h"
|
||||||
#include "link/output.h"
|
#include "link/output.h"
|
||||||
|
|
||||||
#include "extern/err.h"
|
|
||||||
#include "extern/getopt.h"
|
#include "extern/getopt.h"
|
||||||
|
|
||||||
|
#include "error.h"
|
||||||
|
#include "platform.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
bool isDmgMode; /* -d */
|
bool isDmgMode; /* -d */
|
||||||
@@ -35,6 +38,10 @@ char const *symFileName; /* -n */
|
|||||||
char const *overlayFileName; /* -O */
|
char const *overlayFileName; /* -O */
|
||||||
char const *outputFileName; /* -o */
|
char const *outputFileName; /* -o */
|
||||||
uint8_t padValue; /* -p */
|
uint8_t padValue; /* -p */
|
||||||
|
// Setting these three to 0 disables the functionality
|
||||||
|
uint16_t scrambleROMX = 0; /* -S */
|
||||||
|
uint8_t scrambleWRAMX = 0;
|
||||||
|
uint8_t scrambleSRAM = 0;
|
||||||
bool is32kMode; /* -t */
|
bool is32kMode; /* -t */
|
||||||
bool beVerbose; /* -v */
|
bool beVerbose; /* -v */
|
||||||
bool isWRA0Mode; /* -w */
|
bool isWRA0Mode; /* -w */
|
||||||
@@ -100,11 +107,25 @@ void error(struct FileStackNode const *where, uint32_t lineNo, char const *fmt,
|
|||||||
nbErrors++;
|
nbErrors++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void argErr(char flag, char const *fmt, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
fprintf(stderr, "error: Invalid argument for option '%c': ", flag);
|
||||||
|
va_start(ap, fmt);
|
||||||
|
vfprintf(stderr, fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
putc('\n', stderr);
|
||||||
|
|
||||||
|
if (nbErrors != UINT32_MAX)
|
||||||
|
nbErrors++;
|
||||||
|
}
|
||||||
|
|
||||||
_Noreturn void fatal(struct FileStackNode const *where, uint32_t lineNo, char const *fmt, ...)
|
_Noreturn void fatal(struct FileStackNode const *where, uint32_t lineNo, char const *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
fputs("fatal: ", stderr);
|
fputs("FATAL: ", stderr);
|
||||||
if (where) {
|
if (where) {
|
||||||
dumpFileStack(where);
|
dumpFileStack(where);
|
||||||
fprintf(stderr, "(%" PRIu32 "): ", lineNo);
|
fprintf(stderr, "(%" PRIu32 "): ", lineNo);
|
||||||
@@ -118,7 +139,7 @@ _Noreturn void fatal(struct FileStackNode const *where, uint32_t lineNo, char co
|
|||||||
nbErrors++;
|
nbErrors++;
|
||||||
|
|
||||||
fprintf(stderr, "Linking aborted after %" PRIu32 " error%s\n", nbErrors,
|
fprintf(stderr, "Linking aborted after %" PRIu32 " error%s\n", nbErrors,
|
||||||
nbErrors != 1 ? "s" : "");
|
nbErrors == 1 ? "" : "s");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,13 +157,13 @@ FILE *openFile(char const *fileName, char const *mode)
|
|||||||
file = fdopen(1, mode);
|
file = fdopen(1, mode);
|
||||||
|
|
||||||
if (!file)
|
if (!file)
|
||||||
err(1, "Could not open file \"%s\"", fileName);
|
err("Could not open file \"%s\"", fileName);
|
||||||
|
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Short options */
|
/* Short options */
|
||||||
static char const *optstring = "dl:m:n:O:o:p:s:tVvwx";
|
static const char *optstring = "dl:m:n:O:o:p:S:s:tVvWwx";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Equivalent long options
|
* Equivalent long options
|
||||||
@@ -155,20 +176,21 @@ static char const *optstring = "dl:m:n:O:o:p:s:tVvwx";
|
|||||||
* over short opt matching
|
* over short opt matching
|
||||||
*/
|
*/
|
||||||
static struct option const longopts[] = {
|
static struct option const longopts[] = {
|
||||||
{ "dmg", no_argument, NULL, 'd' },
|
{ "dmg", no_argument, NULL, 'd' },
|
||||||
{ "linkerscript", required_argument, NULL, 'l' },
|
{ "linkerscript", required_argument, NULL, 'l' },
|
||||||
{ "map", required_argument, NULL, 'm' },
|
{ "map", required_argument, NULL, 'm' },
|
||||||
{ "sym", required_argument, NULL, 'n' },
|
{ "sym", required_argument, NULL, 'n' },
|
||||||
{ "overlay", required_argument, NULL, 'O' },
|
{ "overlay", required_argument, NULL, 'O' },
|
||||||
{ "output", required_argument, NULL, 'o' },
|
{ "output", required_argument, NULL, 'o' },
|
||||||
{ "pad", required_argument, NULL, 'p' },
|
{ "pad", required_argument, NULL, 'p' },
|
||||||
{ "smart", required_argument, NULL, 's' },
|
{ "scramble", required_argument, NULL, 'S' },
|
||||||
{ "tiny", no_argument, NULL, 't' },
|
{ "smart", required_argument, NULL, 's' },
|
||||||
{ "version", no_argument, NULL, 'V' },
|
{ "tiny", no_argument, NULL, 't' },
|
||||||
{ "verbose", no_argument, NULL, 'v' },
|
{ "version", no_argument, NULL, 'V' },
|
||||||
{ "wramx", no_argument, NULL, 'w' },
|
{ "verbose", no_argument, NULL, 'v' },
|
||||||
{ "nopad", no_argument, NULL, 'x' },
|
{ "wramx", no_argument, NULL, 'w' },
|
||||||
{ NULL, no_argument, NULL, 0 }
|
{ "nopad", no_argument, NULL, 'x' },
|
||||||
|
{ NULL, no_argument, NULL, 0 }
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -178,8 +200,8 @@ static void printUsage(void)
|
|||||||
{
|
{
|
||||||
fputs(
|
fputs(
|
||||||
"Usage: rgblink [-dtVvwx] [-l script] [-m map_file] [-n sym_file]\n"
|
"Usage: rgblink [-dtVvwx] [-l script] [-m map_file] [-n sym_file]\n"
|
||||||
" [-O overlay_file] [-o out_file] [-p pad_value] [-s symbol]\n"
|
" [-O overlay_file] [-o out_file] [-p pad_value]\n"
|
||||||
" <file> ...\n"
|
" [-S spec] [-s symbol] <file> ...\n"
|
||||||
"Useful options:\n"
|
"Useful options:\n"
|
||||||
" -l, --linkerscript <path> set the input linker script\n"
|
" -l, --linkerscript <path> set the input linker script\n"
|
||||||
" -m, --map <path> set the output map file\n"
|
" -m, --map <path> set the output map file\n"
|
||||||
@@ -202,10 +224,135 @@ static void cleanup(void)
|
|||||||
obj_Cleanup();
|
obj_Cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum ScrambledRegion {
|
||||||
|
SCRAMBLE_ROMX,
|
||||||
|
SCRAMBLE_SRAM,
|
||||||
|
SCRAMBLE_WRAMX,
|
||||||
|
|
||||||
|
SCRAMBLE_UNK, // Used for errors
|
||||||
|
};
|
||||||
|
|
||||||
|
struct {
|
||||||
|
char const *name;
|
||||||
|
uint16_t max;
|
||||||
|
} scrambleSpecs[SCRAMBLE_UNK] = {
|
||||||
|
[SCRAMBLE_ROMX] = { "romx", 65535 },
|
||||||
|
[SCRAMBLE_SRAM] = { "sram", 255 },
|
||||||
|
[SCRAMBLE_WRAMX] = { "wramx", 7},
|
||||||
|
};
|
||||||
|
|
||||||
|
static void parseScrambleSpec(char const *spec)
|
||||||
|
{
|
||||||
|
// Skip any leading whitespace
|
||||||
|
spec += strspn(spec, " \t");
|
||||||
|
|
||||||
|
// The argument to `-S` should be a comma-separated list of sections followed by an '='
|
||||||
|
// indicating their scramble limit.
|
||||||
|
while (spec) {
|
||||||
|
// Invariant: we should not be pointing at whitespace at this point
|
||||||
|
assert(*spec != ' ' && *spec != '\t');
|
||||||
|
|
||||||
|
// Remember where the region's name begins and ends
|
||||||
|
char const *regionName = spec;
|
||||||
|
size_t regionNameLen = strcspn(spec, "=, \t");
|
||||||
|
// Length of region name string slice for printing, truncated if too long
|
||||||
|
int regionNamePrintLen = regionNameLen > INT_MAX ? INT_MAX : (int)regionNameLen;
|
||||||
|
|
||||||
|
// If this trips, `spec` must be pointing at a ',' or '=' (or NUL) due to the assert
|
||||||
|
if (regionNameLen == 0) {
|
||||||
|
argErr('S', "Missing region name");
|
||||||
|
|
||||||
|
if (*spec == '\0')
|
||||||
|
break;
|
||||||
|
if (*spec == '=') // Skip the limit, too
|
||||||
|
spec = strchr(&spec[1], ','); // Skip to next comma, if any
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find the next non-blank char after the region name's end
|
||||||
|
spec += regionNameLen + strspn(&spec[regionNameLen], " \t");
|
||||||
|
if (*spec != '\0' && *spec != ',' && *spec != '=') {
|
||||||
|
argErr('S', "Unexpected '%c' after region name \"%.*s\"",
|
||||||
|
regionNamePrintLen, regionName);
|
||||||
|
// Skip to next ',' or '=' (or NUL) and keep parsing
|
||||||
|
spec += 1 + strcspn(&spec[1], ",=");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now, determine which region type this is
|
||||||
|
enum ScrambledRegion region = 0;
|
||||||
|
|
||||||
|
for (; region < SCRAMBLE_UNK; region++) {
|
||||||
|
// If the strings match (case-insensitively), we got it!
|
||||||
|
// `strncasecmp` must be used here since `regionName` points
|
||||||
|
// to the entire remaining argument.
|
||||||
|
if (!strncasecmp(scrambleSpecs[region].name, regionName, regionNameLen))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (region == SCRAMBLE_UNK)
|
||||||
|
argErr('S', "Unknown region \"%.*s\"", regionNamePrintLen, regionName);
|
||||||
|
|
||||||
|
if (*spec == '=') {
|
||||||
|
spec++; // `strtoul` will skip the whitespace on its own
|
||||||
|
unsigned long limit;
|
||||||
|
char *endptr;
|
||||||
|
|
||||||
|
if (*spec == '\0' || *spec == ',') {
|
||||||
|
argErr('S', "Empty limit for region \"%.*s\"",
|
||||||
|
regionNamePrintLen, regionName);
|
||||||
|
goto next;
|
||||||
|
}
|
||||||
|
limit = strtoul(spec, &endptr, 10);
|
||||||
|
endptr += strspn(endptr, " \t");
|
||||||
|
if (*endptr != '\0' && *endptr != ',') {
|
||||||
|
argErr('S', "Invalid non-numeric limit for region \"%.*s\"",
|
||||||
|
regionNamePrintLen, regionName);
|
||||||
|
endptr = strchr(endptr, ',');
|
||||||
|
}
|
||||||
|
spec = endptr;
|
||||||
|
|
||||||
|
if (region != SCRAMBLE_UNK && limit >= scrambleSpecs[region].max) {
|
||||||
|
argErr('S', "Limit for region \"%.*s\" may not exceed %" PRIu16,
|
||||||
|
regionNamePrintLen, regionName, scrambleSpecs[region].max);
|
||||||
|
limit = scrambleSpecs[region].max;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (region) {
|
||||||
|
case SCRAMBLE_ROMX:
|
||||||
|
scrambleROMX = limit;
|
||||||
|
break;
|
||||||
|
case SCRAMBLE_SRAM:
|
||||||
|
scrambleSRAM = limit;
|
||||||
|
break;
|
||||||
|
case SCRAMBLE_WRAMX:
|
||||||
|
scrambleWRAMX = limit;
|
||||||
|
break;
|
||||||
|
case SCRAMBLE_UNK: // The error has already been reported, do nothing
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (region == SCRAMBLE_WRAMX) {
|
||||||
|
// Only WRAMX can be implied, since ROMX and SRAM size may vary
|
||||||
|
scrambleWRAMX = 7;
|
||||||
|
} else {
|
||||||
|
argErr('S', "Cannot imply limit for region \"%.*s\"",
|
||||||
|
regionNamePrintLen, regionName);
|
||||||
|
}
|
||||||
|
|
||||||
|
next:
|
||||||
|
if (spec) {
|
||||||
|
assert(*spec == ',' || *spec == '\0');
|
||||||
|
if (*spec == ',')
|
||||||
|
spec += 1 + strspn(&spec[1], " \t");
|
||||||
|
if (*spec == '\0')
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
int optionChar;
|
int optionChar;
|
||||||
char *endptr; /* For error checking with `strtol` */
|
char *endptr; /* For error checking with `strtoul` */
|
||||||
unsigned long value; /* For storing `strtoul`'s return value */
|
unsigned long value; /* For storing `strtoul`'s return value */
|
||||||
|
|
||||||
/* Parse options */
|
/* Parse options */
|
||||||
@@ -217,35 +364,38 @@ int main(int argc, char *argv[])
|
|||||||
isWRA0Mode = true;
|
isWRA0Mode = true;
|
||||||
break;
|
break;
|
||||||
case 'l':
|
case 'l':
|
||||||
linkerScriptName = optarg;
|
linkerScriptName = musl_optarg;
|
||||||
break;
|
break;
|
||||||
case 'm':
|
case 'm':
|
||||||
mapFileName = optarg;
|
mapFileName = musl_optarg;
|
||||||
break;
|
break;
|
||||||
case 'n':
|
case 'n':
|
||||||
symFileName = optarg;
|
symFileName = musl_optarg;
|
||||||
break;
|
break;
|
||||||
case 'O':
|
case 'O':
|
||||||
overlayFileName = optarg;
|
overlayFileName = musl_optarg;
|
||||||
break;
|
break;
|
||||||
case 'o':
|
case 'o':
|
||||||
outputFileName = optarg;
|
outputFileName = musl_optarg;
|
||||||
break;
|
break;
|
||||||
case 'p':
|
case 'p':
|
||||||
value = strtoul(optarg, &endptr, 0);
|
value = strtoul(musl_optarg, &endptr, 0);
|
||||||
if (optarg[0] == '\0' || *endptr != '\0') {
|
if (musl_optarg[0] == '\0' || *endptr != '\0') {
|
||||||
error(NULL, 0, "Invalid argument for option 'p'");
|
argErr('p', "");
|
||||||
value = 0xFF;
|
value = 0xFF;
|
||||||
}
|
}
|
||||||
if (value > 0xFF) {
|
if (value > 0xFF) {
|
||||||
error(NULL, 0, "Argument for 'p' must be a byte (between 0 and 0xFF)");
|
argErr('p', "Argument for 'p' must be a byte (between 0 and 0xFF)");
|
||||||
value = 0xFF;
|
value = 0xFF;
|
||||||
}
|
}
|
||||||
padValue = value;
|
padValue = value;
|
||||||
break;
|
break;
|
||||||
|
case 'S':
|
||||||
|
parseScrambleSpec(musl_optarg);
|
||||||
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
/* FIXME: nobody knows what this does, figure it out */
|
/* FIXME: nobody knows what this does, figure it out */
|
||||||
(void)optarg;
|
(void)musl_optarg;
|
||||||
warning(NULL, 0, "Nobody has any idea what `-s` does");
|
warning(NULL, 0, "Nobody has any idea what `-s` does");
|
||||||
break;
|
break;
|
||||||
case 't':
|
case 't':
|
||||||
@@ -271,11 +421,11 @@ int main(int argc, char *argv[])
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int curArgIndex = optind;
|
int curArgIndex = musl_optind;
|
||||||
|
|
||||||
/* If no input files were specified, the user must have screwed up */
|
/* If no input files were specified, the user must have screwed up */
|
||||||
if (curArgIndex == argc) {
|
if (curArgIndex == argc) {
|
||||||
fputs("fatal: no input files\n", stderr);
|
fputs("FATAL: no input files\n", stderr);
|
||||||
printUsage();
|
printUsage();
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
@@ -304,7 +454,7 @@ int main(int argc, char *argv[])
|
|||||||
patch_ApplyPatches();
|
patch_ApplyPatches();
|
||||||
if (nbErrors) {
|
if (nbErrors) {
|
||||||
fprintf(stderr, "Linking failed with %" PRIu32 " error%s\n",
|
fprintf(stderr, "Linking failed with %" PRIu32 " error%s\n",
|
||||||
nbErrors, nbErrors != 1 ? "s" : "");
|
nbErrors, nbErrors == 1 ? "" : "s");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
out_WriteFiles();
|
out_WriteFiles();
|
||||||
|
|||||||
+83
-60
@@ -21,7 +21,7 @@
|
|||||||
#include "link/section.h"
|
#include "link/section.h"
|
||||||
#include "link/symbol.h"
|
#include "link/symbol.h"
|
||||||
|
|
||||||
#include "extern/err.h"
|
#include "error.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "linkdefs.h"
|
#include "linkdefs.h"
|
||||||
|
|
||||||
@@ -48,8 +48,9 @@ static struct Assertion *assertions;
|
|||||||
do { \
|
do { \
|
||||||
FILE *tmpFile = file; \
|
FILE *tmpFile = file; \
|
||||||
type tmpVal = func(tmpFile); \
|
type tmpVal = func(tmpFile); \
|
||||||
|
/* TODO: maybe mark the condition as `unlikely`; how to do that portably? */ \
|
||||||
if (tmpVal == (errval)) { \
|
if (tmpVal == (errval)) { \
|
||||||
errx(1, __VA_ARGS__, feof(tmpFile) \
|
errx(__VA_ARGS__, feof(tmpFile) \
|
||||||
? "Unexpected end of file" \
|
? "Unexpected end of file" \
|
||||||
: strerror(errno)); \
|
: strerror(errno)); \
|
||||||
} \
|
} \
|
||||||
@@ -86,7 +87,6 @@ static int64_t readlong(FILE *file)
|
|||||||
/**
|
/**
|
||||||
* Helper macro for reading longs from a file, and errors out if it fails to.
|
* Helper macro for reading longs from a file, and errors out if it fails to.
|
||||||
* Not as a function to avoid overhead in the general case.
|
* Not as a function to avoid overhead in the general case.
|
||||||
* TODO: maybe mark the condition as `unlikely`; how to do that portably?
|
|
||||||
* @param var The variable to stash the number into
|
* @param var The variable to stash the number into
|
||||||
* @param file The file to read from. Its position will be advanced
|
* @param file The file to read from. Its position will be advanced
|
||||||
* @param ... A format string and related arguments; note that an extra string
|
* @param ... A format string and related arguments; note that an extra string
|
||||||
@@ -101,7 +101,6 @@ static int64_t readlong(FILE *file)
|
|||||||
* Helper macro for reading bytes from a file, and errors out if it fails to.
|
* Helper macro for reading bytes from a file, and errors out if it fails to.
|
||||||
* Differs from `tryGetc` in that the backing function is fgetc(1).
|
* Differs from `tryGetc` in that the backing function is fgetc(1).
|
||||||
* Not as a function to avoid overhead in the general case.
|
* Not as a function to avoid overhead in the general case.
|
||||||
* TODO: maybe mark the condition as `unlikely`; how to do that portably?
|
|
||||||
* @param var The variable to stash the number into
|
* @param var The variable to stash the number into
|
||||||
* @param file The file to read from. Its position will be advanced
|
* @param file The file to read from. Its position will be advanced
|
||||||
* @param ... A format string and related arguments; note that an extra string
|
* @param ... A format string and related arguments; note that an extra string
|
||||||
@@ -114,7 +113,6 @@ static int64_t readlong(FILE *file)
|
|||||||
* Helper macro for reading bytes from a file, and errors out if it fails to.
|
* Helper macro for reading bytes from a file, and errors out if it fails to.
|
||||||
* Differs from `tryGetc` in that the backing function is fgetc(1).
|
* Differs from `tryGetc` in that the backing function is fgetc(1).
|
||||||
* Not as a function to avoid overhead in the general case.
|
* Not as a function to avoid overhead in the general case.
|
||||||
* TODO: maybe mark the condition as `unlikely`; how to do that portably?
|
|
||||||
* @param var The variable to stash the number into
|
* @param var The variable to stash the number into
|
||||||
* @param file The file to read from. Its position will be advanced
|
* @param file The file to read from. Its position will be advanced
|
||||||
* @param ... A format string and related arguments; note that an extra string
|
* @param ... A format string and related arguments; note that an extra string
|
||||||
@@ -163,7 +161,6 @@ static char *readstr(FILE *file)
|
|||||||
/**
|
/**
|
||||||
* Helper macro for reading bytes from a file, and errors out if it fails to.
|
* Helper macro for reading bytes from a file, and errors out if it fails to.
|
||||||
* Not as a function to avoid overhead in the general case.
|
* Not as a function to avoid overhead in the general case.
|
||||||
* TODO: maybe mark the condition as `unlikely`; how to do that portably?
|
|
||||||
* @param var The variable to stash the string into
|
* @param var The variable to stash the string into
|
||||||
* @param file The file to read from. Its position will be advanced
|
* @param file The file to read from. Its position will be advanced
|
||||||
* @param ... A format string and related arguments; note that an extra string
|
* @param ... A format string and related arguments; note that an extra string
|
||||||
@@ -188,7 +185,7 @@ static void readFileStackNode(FILE *file, struct FileStackNode fileNodes[], uint
|
|||||||
|
|
||||||
tryReadlong(parentID, file,
|
tryReadlong(parentID, file,
|
||||||
"%s: Cannot read node #%" PRIu32 "'s parent ID: %s", fileName, i);
|
"%s: Cannot read node #%" PRIu32 "'s parent ID: %s", fileName, i);
|
||||||
fileNodes[i].parent = parentID == -1 ? NULL : &fileNodes[parentID];
|
fileNodes[i].parent = parentID == (uint32_t)-1 ? NULL : &fileNodes[parentID];
|
||||||
tryReadlong(fileNodes[i].lineNo, file,
|
tryReadlong(fileNodes[i].lineNo, file,
|
||||||
"%s: Cannot read node #%" PRIu32 "'s line number: %s", fileName, i);
|
"%s: Cannot read node #%" PRIu32 "'s line number: %s", fileName, i);
|
||||||
tryGetc(fileNodes[i].type, file, "%s: Cannot read node #%" PRIu32 "'s type: %s",
|
tryGetc(fileNodes[i].type, file, "%s: Cannot read node #%" PRIu32 "'s type: %s",
|
||||||
@@ -260,11 +257,11 @@ static void readSymbol(FILE *file, struct Symbol *symbol,
|
|||||||
* @param fileName The filename to report in errors
|
* @param fileName The filename to report in errors
|
||||||
* @param i The number of the patch to report in errors
|
* @param i The number of the patch to report in errors
|
||||||
*/
|
*/
|
||||||
static void readPatch(FILE *file, struct Patch *patch, char const *fileName,
|
static void readPatch(FILE *file, struct Patch *patch, char const *fileName, char const *sectName,
|
||||||
char const *sectName, uint32_t i,
|
uint32_t i, struct FileStackNode fileNodes[])
|
||||||
struct Section *fileSections[], struct FileStackNode fileNodes[])
|
|
||||||
{
|
{
|
||||||
uint32_t nodeID;
|
uint32_t nodeID;
|
||||||
|
uint8_t type;
|
||||||
|
|
||||||
tryReadlong(nodeID, file,
|
tryReadlong(nodeID, file,
|
||||||
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s node ID: %s",
|
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s node ID: %s",
|
||||||
@@ -279,31 +276,40 @@ static void readPatch(FILE *file, struct Patch *patch, char const *fileName,
|
|||||||
tryReadlong(patch->pcSectionID, file,
|
tryReadlong(patch->pcSectionID, file,
|
||||||
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
|
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
|
||||||
fileName, sectName, i);
|
fileName, sectName, i);
|
||||||
patch->pcSection = patch->pcSectionID == -1 ? NULL
|
|
||||||
: fileSections[patch->pcSectionID];
|
|
||||||
tryReadlong(patch->pcOffset, file,
|
tryReadlong(patch->pcOffset, file,
|
||||||
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
|
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s PC offset: %s",
|
||||||
fileName, sectName, i);
|
fileName, sectName, i);
|
||||||
tryGetc(patch->type, file,
|
tryGetc(type, file,
|
||||||
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s type: %s",
|
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s type: %s",
|
||||||
fileName, sectName, i);
|
fileName, sectName, i);
|
||||||
|
patch->type = type;
|
||||||
tryReadlong(patch->rpnSize, file,
|
tryReadlong(patch->rpnSize, file,
|
||||||
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s RPN size: %s",
|
"%s: Unable to read \"%s\"'s patch #%" PRIu32 "'s RPN size: %s",
|
||||||
fileName, sectName, i);
|
fileName, sectName, i);
|
||||||
|
|
||||||
patch->rpnExpression = malloc(sizeof(*patch->rpnExpression) * patch->rpnSize);
|
patch->rpnExpression = malloc(sizeof(*patch->rpnExpression) * patch->rpnSize);
|
||||||
if (!patch->rpnExpression)
|
if (!patch->rpnExpression)
|
||||||
err(1, "%s: Failed to alloc \"%s\"'s patch #%" PRIu32 "'s RPN expression",
|
err("%s: Failed to alloc \"%s\"'s patch #%" PRIu32 "'s RPN expression",
|
||||||
fileName, sectName, i);
|
fileName, sectName, i);
|
||||||
size_t nbElementsRead = fread(patch->rpnExpression, sizeof(*patch->rpnExpression),
|
size_t nbElementsRead = fread(patch->rpnExpression, sizeof(*patch->rpnExpression),
|
||||||
patch->rpnSize, file);
|
patch->rpnSize, file);
|
||||||
|
|
||||||
if (nbElementsRead != patch->rpnSize)
|
if (nbElementsRead != patch->rpnSize)
|
||||||
errx(1, "%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s RPN expression: %s",
|
errx("%s: Cannot read \"%s\"'s patch #%" PRIu32 "'s RPN expression: %s",
|
||||||
fileName, sectName, i,
|
fileName, sectName, i,
|
||||||
feof(file) ? "Unexpected end of file" : strerror(errno));
|
feof(file) ? "Unexpected end of file" : strerror(errno));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a patch's pcSection from its pcSectionID.
|
||||||
|
* @param patch The struct to fix
|
||||||
|
*/
|
||||||
|
static void linkPatchToPCSect(struct Patch *patch, struct Section *fileSections[])
|
||||||
|
{
|
||||||
|
patch->pcSection = patch->pcSectionID == (uint32_t)-1 ? NULL
|
||||||
|
: fileSections[patch->pcSectionID];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads a section from a file.
|
* Reads a section from a file.
|
||||||
* @param file The file to read from
|
* @param file The file to read from
|
||||||
@@ -311,7 +317,7 @@ static void readPatch(FILE *file, struct Patch *patch, char const *fileName,
|
|||||||
* @param fileName The filename to report in errors
|
* @param fileName The filename to report in errors
|
||||||
*/
|
*/
|
||||||
static void readSection(FILE *file, struct Section *section, char const *fileName,
|
static void readSection(FILE *file, struct Section *section, char const *fileName,
|
||||||
struct Section *fileSections[], struct FileStackNode fileNodes[])
|
struct FileStackNode fileNodes[])
|
||||||
{
|
{
|
||||||
int32_t tmp;
|
int32_t tmp;
|
||||||
uint8_t byte;
|
uint8_t byte;
|
||||||
@@ -321,7 +327,7 @@ static void readSection(FILE *file, struct Section *section, char const *fileNam
|
|||||||
tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s' size: %s",
|
tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s' size: %s",
|
||||||
fileName, section->name);
|
fileName, section->name);
|
||||||
if (tmp < 0 || tmp > UINT16_MAX)
|
if (tmp < 0 || tmp > UINT16_MAX)
|
||||||
errx(1, "\"%s\"'s section size (%" PRId32 ") is invalid",
|
errx("\"%s\"'s section size (%" PRId32 ") is invalid",
|
||||||
section->name, tmp);
|
section->name, tmp);
|
||||||
section->size = tmp;
|
section->size = tmp;
|
||||||
section->offset = 0;
|
section->offset = 0;
|
||||||
@@ -349,6 +355,8 @@ static void readSection(FILE *file, struct Section *section, char const *fileNam
|
|||||||
section->bank = tmp;
|
section->bank = tmp;
|
||||||
tryGetc(byte, file, "%s: Cannot read \"%s\"'s alignment: %s",
|
tryGetc(byte, file, "%s: Cannot read \"%s\"'s alignment: %s",
|
||||||
fileName, section->name);
|
fileName, section->name);
|
||||||
|
if (byte > 16)
|
||||||
|
byte = 16;
|
||||||
section->isAlignFixed = byte != 0;
|
section->isAlignFixed = byte != 0;
|
||||||
section->alignMask = (1 << byte) - 1;
|
section->alignMask = (1 << byte) - 1;
|
||||||
tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s alignment offset: %s",
|
tryReadlong(tmp, file, "%s: Cannot read \"%s\"'s alignment offset: %s",
|
||||||
@@ -365,13 +373,13 @@ static void readSection(FILE *file, struct Section *section, char const *fileNam
|
|||||||
uint8_t *data = malloc(sizeof(*data) * section->size + 1);
|
uint8_t *data = malloc(sizeof(*data) * section->size + 1);
|
||||||
|
|
||||||
if (!data)
|
if (!data)
|
||||||
err(1, "%s: Unable to read \"%s\"'s data", fileName,
|
err("%s: Unable to read \"%s\"'s data", fileName,
|
||||||
section->name);
|
section->name);
|
||||||
if (section->size) {
|
if (section->size) {
|
||||||
size_t nbElementsRead = fread(data, sizeof(*data),
|
size_t nbElementsRead = fread(data, sizeof(*data),
|
||||||
section->size, file);
|
section->size, file);
|
||||||
if (nbElementsRead != section->size)
|
if (nbElementsRead != section->size)
|
||||||
errx(1, "%s: Cannot read \"%s\"'s data: %s",
|
errx("%s: Cannot read \"%s\"'s data: %s",
|
||||||
fileName, section->name,
|
fileName, section->name,
|
||||||
feof(file) ? "Unexpected end of file"
|
feof(file) ? "Unexpected end of file"
|
||||||
: strerror(errno));
|
: strerror(errno));
|
||||||
@@ -386,12 +394,9 @@ static void readSection(FILE *file, struct Section *section, char const *fileNam
|
|||||||
malloc(sizeof(*patches) * section->nbPatches + 1);
|
malloc(sizeof(*patches) * section->nbPatches + 1);
|
||||||
|
|
||||||
if (!patches)
|
if (!patches)
|
||||||
err(1, "%s: Unable to read \"%s\"'s patches", fileName,
|
err("%s: Unable to read \"%s\"'s patches", fileName, section->name);
|
||||||
section->name);
|
for (uint32_t i = 0; i < section->nbPatches; i++)
|
||||||
for (uint32_t i = 0; i < section->nbPatches; i++) {
|
readPatch(file, &patches[i], fileName, section->name, i, fileNodes);
|
||||||
readPatch(file, &patches[i], fileName, section->name,
|
|
||||||
i, fileSections, fileNodes);
|
|
||||||
}
|
|
||||||
section->patches = patches;
|
section->patches = patches;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -433,18 +438,18 @@ static void linkSymToSect(struct Symbol const *symbol, struct Section *section)
|
|||||||
*/
|
*/
|
||||||
static void readAssertion(FILE *file, struct Assertion *assert,
|
static void readAssertion(FILE *file, struct Assertion *assert,
|
||||||
char const *fileName, uint32_t i,
|
char const *fileName, uint32_t i,
|
||||||
struct Section *fileSections[], struct FileStackNode fileNodes[])
|
struct FileStackNode fileNodes[])
|
||||||
{
|
{
|
||||||
char assertName[sizeof("Assertion #" EXPAND_AND_STR(UINT32_MAX))];
|
char assertName[sizeof("Assertion #4294967295")]; // UINT32_MAX
|
||||||
|
|
||||||
snprintf(assertName, sizeof(assertName), "Assertion #%" PRIu32, i);
|
snprintf(assertName, sizeof(assertName), "Assertion #%" PRIu32, i);
|
||||||
|
|
||||||
readPatch(file, &assert->patch, fileName, assertName, 0, fileSections, fileNodes);
|
readPatch(file, &assert->patch, fileName, assertName, 0, fileNodes);
|
||||||
tryReadstr(assert->message, file, "%s: Cannot read assertion's message: %s",
|
tryReadstr(assert->message, file, "%s: Cannot read assertion's message: %s",
|
||||||
fileName);
|
fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline struct Section *getMainSection(struct Section *section)
|
static struct Section *getMainSection(struct Section *section)
|
||||||
{
|
{
|
||||||
if (section->modifier != SECTION_NORMAL)
|
if (section->modifier != SECTION_NORMAL)
|
||||||
section = sect_GetSection(section->name);
|
section = sect_GetSection(section->name);
|
||||||
@@ -457,7 +462,7 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
|
|||||||
FILE *file = strcmp("-", fileName) ? fopen(fileName, "rb") : stdin;
|
FILE *file = strcmp("-", fileName) ? fopen(fileName, "rb") : stdin;
|
||||||
|
|
||||||
if (!file)
|
if (!file)
|
||||||
err(1, "Could not open file %s", fileName);
|
err("Could not open file %s", fileName);
|
||||||
|
|
||||||
/* Begin by reading the magic bytes and version number */
|
/* Begin by reading the magic bytes and version number */
|
||||||
unsigned versionNumber;
|
unsigned versionNumber;
|
||||||
@@ -465,13 +470,13 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
|
|||||||
&versionNumber);
|
&versionNumber);
|
||||||
|
|
||||||
if (matchedElems != 1)
|
if (matchedElems != 1)
|
||||||
errx(1, "\"%s\" is not a RGBDS object file", fileName);
|
errx("\"%s\" is not a RGBDS object file", fileName);
|
||||||
|
|
||||||
verbosePrint("Reading object file %s, version %u\n",
|
verbosePrint("Reading object file %s, version %u\n",
|
||||||
fileName, versionNumber);
|
fileName, versionNumber);
|
||||||
|
|
||||||
if (versionNumber != RGBDS_OBJECT_VERSION_NUMBER)
|
if (versionNumber != RGBDS_OBJECT_VERSION_NUMBER)
|
||||||
errx(1, "\"%s\" is an incompatible version %u object file",
|
errx("\"%s\" is an incompatible version %u object file",
|
||||||
fileName, versionNumber);
|
fileName, versionNumber);
|
||||||
|
|
||||||
uint32_t revNum;
|
uint32_t revNum;
|
||||||
@@ -479,7 +484,7 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
|
|||||||
tryReadlong(revNum, file, "%s: Cannot read revision number: %s",
|
tryReadlong(revNum, file, "%s: Cannot read revision number: %s",
|
||||||
fileName);
|
fileName);
|
||||||
if (revNum != RGBDS_OBJECT_REV)
|
if (revNum != RGBDS_OBJECT_REV)
|
||||||
errx(1, "%s is a revision 0x%04" PRIx32 " object file; only 0x%04x is supported",
|
errx("%s is a revision 0x%04" PRIx32 " object file; only 0x%04x is supported",
|
||||||
fileName, revNum, RGBDS_OBJECT_REV);
|
fileName, revNum, RGBDS_OBJECT_REV);
|
||||||
|
|
||||||
uint32_t nbSymbols;
|
uint32_t nbSymbols;
|
||||||
@@ -495,7 +500,7 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
|
|||||||
tryReadlong(nodes[fileID].nbNodes, file, "%s: Cannot read number of nodes: %s", fileName);
|
tryReadlong(nodes[fileID].nbNodes, file, "%s: Cannot read number of nodes: %s", fileName);
|
||||||
nodes[fileID].nodes = calloc(nodes[fileID].nbNodes, sizeof(nodes[fileID].nodes[0]));
|
nodes[fileID].nodes = calloc(nodes[fileID].nbNodes, sizeof(nodes[fileID].nodes[0]));
|
||||||
if (!nodes[fileID].nodes)
|
if (!nodes[fileID].nodes)
|
||||||
err(1, "Failed to get memory for %s's nodes", fileName);
|
err("Failed to get memory for %s's nodes", fileName);
|
||||||
verbosePrint("Reading %u nodes...\n", nodes[fileID].nbNodes);
|
verbosePrint("Reading %u nodes...\n", nodes[fileID].nbNodes);
|
||||||
for (uint32_t i = nodes[fileID].nbNodes; i--; )
|
for (uint32_t i = nodes[fileID].nbNodes; i--; )
|
||||||
readFileStackNode(file, nodes[fileID].nodes, i, fileName);
|
readFileStackNode(file, nodes[fileID].nodes, i, fileName);
|
||||||
@@ -505,12 +510,12 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
|
|||||||
malloc(sizeof(*fileSymbols) * nbSymbols + 1);
|
malloc(sizeof(*fileSymbols) * nbSymbols + 1);
|
||||||
|
|
||||||
if (!fileSymbols)
|
if (!fileSymbols)
|
||||||
err(1, "Failed to get memory for %s's symbols", fileName);
|
err("Failed to get memory for %s's symbols", fileName);
|
||||||
|
|
||||||
struct SymbolList *symbolList = malloc(sizeof(*symbolList));
|
struct SymbolList *symbolList = malloc(sizeof(*symbolList));
|
||||||
|
|
||||||
if (!symbolList)
|
if (!symbolList)
|
||||||
err(1, "Failed to register %s's symbol list", fileName);
|
err("Failed to register %s's symbol list", fileName);
|
||||||
symbolList->symbolList = fileSymbols;
|
symbolList->symbolList = fileSymbols;
|
||||||
symbolList->nbSymbols = nbSymbols;
|
symbolList->nbSymbols = nbSymbols;
|
||||||
symbolList->next = symbolLists;
|
symbolList->next = symbolLists;
|
||||||
@@ -525,7 +530,7 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
|
|||||||
struct Symbol *symbol = malloc(sizeof(*symbol));
|
struct Symbol *symbol = malloc(sizeof(*symbol));
|
||||||
|
|
||||||
if (!symbol)
|
if (!symbol)
|
||||||
err(1, "%s: Couldn't create new symbol", fileName);
|
err("%s: Couldn't create new symbol", fileName);
|
||||||
readSymbol(file, symbol, fileName, nodes[fileID].nodes);
|
readSymbol(file, symbol, fileName, nodes[fileID].nodes);
|
||||||
|
|
||||||
fileSymbols[i] = symbol;
|
fileSymbols[i] = symbol;
|
||||||
@@ -544,16 +549,16 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
|
|||||||
/* Read section */
|
/* Read section */
|
||||||
fileSections[i] = malloc(sizeof(*fileSections[i]));
|
fileSections[i] = malloc(sizeof(*fileSections[i]));
|
||||||
if (!fileSections[i])
|
if (!fileSections[i])
|
||||||
err(1, "%s: Couldn't create new section", fileName);
|
err("%s: Couldn't create new section", fileName);
|
||||||
|
|
||||||
fileSections[i]->nextu = NULL;
|
fileSections[i]->nextu = NULL;
|
||||||
readSection(file, fileSections[i], fileName, fileSections, nodes[fileID].nodes);
|
readSection(file, fileSections[i], fileName, nodes[fileID].nodes);
|
||||||
fileSections[i]->fileSymbols = fileSymbols;
|
fileSections[i]->fileSymbols = fileSymbols;
|
||||||
if (nbSymPerSect[i]) {
|
if (nbSymPerSect[i]) {
|
||||||
fileSections[i]->symbols = malloc(nbSymPerSect[i]
|
fileSections[i]->symbols = malloc(nbSymPerSect[i]
|
||||||
* sizeof(*fileSections[i]->symbols));
|
* sizeof(*fileSections[i]->symbols));
|
||||||
if (!fileSections[i]->symbols)
|
if (!fileSections[i]->symbols)
|
||||||
err(1, "%s: Couldn't link to symbols",
|
err("%s: Couldn't link to symbols",
|
||||||
fileName);
|
fileName);
|
||||||
} else {
|
} else {
|
||||||
fileSections[i]->symbols = NULL;
|
fileSections[i]->symbols = NULL;
|
||||||
@@ -565,7 +570,15 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
|
|||||||
|
|
||||||
free(nbSymPerSect);
|
free(nbSymPerSect);
|
||||||
|
|
||||||
/* Give symbols pointers to their sections */
|
/* Give patches' PC section pointers to their sections */
|
||||||
|
for (uint32_t i = 0; i < nbSections; i++) {
|
||||||
|
if (sect_HasData(fileSections[i]->type)) {
|
||||||
|
for (uint32_t j = 0; j < fileSections[i]->nbPatches; j++)
|
||||||
|
linkPatchToPCSect(&fileSections[i]->patches[j], fileSections);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Give symbols' section pointers to their sections */
|
||||||
for (uint32_t i = 0; i < nbSymbols; i++) {
|
for (uint32_t i = 0; i < nbSymbols; i++) {
|
||||||
int32_t sectionID = fileSymbols[i]->sectionID;
|
int32_t sectionID = fileSymbols[i]->sectionID;
|
||||||
|
|
||||||
@@ -596,8 +609,9 @@ void obj_ReadFile(char const *fileName, unsigned int fileID)
|
|||||||
struct Assertion *assertion = malloc(sizeof(*assertion));
|
struct Assertion *assertion = malloc(sizeof(*assertion));
|
||||||
|
|
||||||
if (!assertion)
|
if (!assertion)
|
||||||
err(1, "%s: Couldn't create new assertion", fileName);
|
err("%s: Couldn't create new assertion", fileName);
|
||||||
readAssertion(file, assertion, fileName, i, fileSections, nodes[fileID].nodes);
|
readAssertion(file, assertion, fileName, i, nodes[fileID].nodes);
|
||||||
|
linkPatchToPCSect(&assertion->patch, fileSections);
|
||||||
assertion->fileSymbols = fileSymbols;
|
assertion->fileSymbols = fileSymbols;
|
||||||
assertion->next = assertions;
|
assertion->next = assertions;
|
||||||
assertions = assertion;
|
assertions = assertion;
|
||||||
@@ -626,19 +640,33 @@ void obj_Setup(unsigned int nbFiles)
|
|||||||
nodes = malloc(sizeof(*nodes) * nbFiles);
|
nodes = malloc(sizeof(*nodes) * nbFiles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void freeNode(struct FileStackNode *node)
|
||||||
|
{
|
||||||
|
if (node->type == NODE_REPT)
|
||||||
|
free(node->iters);
|
||||||
|
else
|
||||||
|
free(node->name);
|
||||||
|
}
|
||||||
|
|
||||||
static void freeSection(struct Section *section, void *arg)
|
static void freeSection(struct Section *section, void *arg)
|
||||||
{
|
{
|
||||||
(void)arg;
|
(void)arg;
|
||||||
|
|
||||||
free(section->name);
|
do {
|
||||||
if (sect_HasData(section->type)) {
|
struct Section *next = section->nextu;
|
||||||
free(section->data);
|
|
||||||
for (int32_t i = 0; i < section->nbPatches; i++)
|
free(section->name);
|
||||||
free(section->patches[i].rpnExpression);
|
if (sect_HasData(section->type)) {
|
||||||
free(section->patches);
|
free(section->data);
|
||||||
}
|
for (uint32_t i = 0; i < section->nbPatches; i++)
|
||||||
free(section->symbols);
|
free(section->patches[i].rpnExpression);
|
||||||
free(section);
|
free(section->patches);
|
||||||
|
}
|
||||||
|
free(section->symbols);
|
||||||
|
free(section);
|
||||||
|
|
||||||
|
section = next;
|
||||||
|
} while (section);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void freeSymbol(struct Symbol *symbol)
|
static void freeSymbol(struct Symbol *symbol)
|
||||||
@@ -651,8 +679,7 @@ void obj_Cleanup(void)
|
|||||||
{
|
{
|
||||||
for (unsigned int i = 0; i < nbObjFiles; i++) {
|
for (unsigned int i = 0; i < nbObjFiles; i++) {
|
||||||
for (uint32_t j = 0; j < nodes[i].nbNodes; j++) {
|
for (uint32_t j = 0; j < nodes[i].nbNodes; j++) {
|
||||||
if (nodes[i].nodes[j].type == NODE_REPT)
|
freeNode(&nodes[i].nodes[j]);
|
||||||
free(nodes[i].nodes[j].iters);
|
|
||||||
}
|
}
|
||||||
free(nodes[i].nodes);
|
free(nodes[i].nodes);
|
||||||
}
|
}
|
||||||
@@ -663,16 +690,12 @@ void obj_Cleanup(void)
|
|||||||
sect_ForEach(freeSection, NULL);
|
sect_ForEach(freeSection, NULL);
|
||||||
sect_CleanupSections();
|
sect_CleanupSections();
|
||||||
|
|
||||||
struct SymbolList *list = symbolLists;
|
for (struct SymbolList *list = symbolLists, *next; list; list = next) {
|
||||||
|
next = list->next;
|
||||||
|
|
||||||
while (list) {
|
|
||||||
for (size_t i = 0; i < list->nbSymbols; i++)
|
for (size_t i = 0; i < list->nbSymbols; i++)
|
||||||
freeSymbol(list->symbolList[i]);
|
freeSymbol(list->symbolList[i]);
|
||||||
free(list->symbolList);
|
free(list->symbolList);
|
||||||
|
|
||||||
struct SymbolList *next = list->next;
|
|
||||||
|
|
||||||
free(list);
|
free(list);
|
||||||
list = next;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+201
-108
@@ -6,6 +6,7 @@
|
|||||||
* SPDX-License-Identifier: MIT
|
* SPDX-License-Identifier: MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -15,9 +16,13 @@
|
|||||||
#include "link/section.h"
|
#include "link/section.h"
|
||||||
#include "link/symbol.h"
|
#include "link/symbol.h"
|
||||||
|
|
||||||
#include "extern/err.h"
|
#include "error.h"
|
||||||
|
#include "linkdefs.h"
|
||||||
|
#include "platform.h" // MIN_NB_ELMS
|
||||||
|
|
||||||
FILE * outputFile;
|
#define BANK_SIZE 0x4000
|
||||||
|
|
||||||
|
FILE *outputFile;
|
||||||
FILE *overlayFile;
|
FILE *overlayFile;
|
||||||
FILE *symFile;
|
FILE *symFile;
|
||||||
FILE *mapFile;
|
FILE *mapFile;
|
||||||
@@ -27,6 +32,12 @@ struct SortedSection {
|
|||||||
struct SortedSection *next;
|
struct SortedSection *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SortedSymbol {
|
||||||
|
struct Symbol const *sym;
|
||||||
|
uint32_t idx;
|
||||||
|
uint16_t addr;
|
||||||
|
};
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
uint32_t nbBanks;
|
uint32_t nbBanks;
|
||||||
struct SortedSections {
|
struct SortedSections {
|
||||||
@@ -35,6 +46,18 @@ static struct {
|
|||||||
} *banks;
|
} *banks;
|
||||||
} sections[SECTTYPE_INVALID];
|
} sections[SECTTYPE_INVALID];
|
||||||
|
|
||||||
|
/* Defines the order in which types are output to the sym and map files */
|
||||||
|
static enum SectionType typeMap[SECTTYPE_INVALID] = {
|
||||||
|
SECTTYPE_ROM0,
|
||||||
|
SECTTYPE_ROMX,
|
||||||
|
SECTTYPE_VRAM,
|
||||||
|
SECTTYPE_SRAM,
|
||||||
|
SECTTYPE_WRAM0,
|
||||||
|
SECTTYPE_WRAMX,
|
||||||
|
SECTTYPE_OAM,
|
||||||
|
SECTTYPE_HRAM
|
||||||
|
};
|
||||||
|
|
||||||
void out_AddSection(struct Section const *section)
|
void out_AddSection(struct Section const *section)
|
||||||
{
|
{
|
||||||
static uint32_t maxNbBanks[] = {
|
static uint32_t maxNbBanks[] = {
|
||||||
@@ -52,7 +75,7 @@ void out_AddSection(struct Section const *section)
|
|||||||
uint32_t minNbBanks = targetBank + 1;
|
uint32_t minNbBanks = targetBank + 1;
|
||||||
|
|
||||||
if (minNbBanks > maxNbBanks[section->type])
|
if (minNbBanks > maxNbBanks[section->type])
|
||||||
errx(1, "Section \"%s\" has an invalid bank range (%" PRIu32 " > %" PRIu32 ")",
|
errx("Section \"%s\" has an invalid bank range (%" PRIu32 " > %" PRIu32 ")",
|
||||||
section->name, section->bank,
|
section->name, section->bank,
|
||||||
maxNbBanks[section->type] - 1);
|
maxNbBanks[section->type] - 1);
|
||||||
|
|
||||||
@@ -60,15 +83,14 @@ void out_AddSection(struct Section const *section)
|
|||||||
sections[section->type].banks =
|
sections[section->type].banks =
|
||||||
realloc(sections[section->type].banks,
|
realloc(sections[section->type].banks,
|
||||||
sizeof(*sections[0].banks) * minNbBanks);
|
sizeof(*sections[0].banks) * minNbBanks);
|
||||||
for (uint32_t i = sections[section->type].nbBanks;
|
for (uint32_t i = sections[section->type].nbBanks; i < minNbBanks; i++) {
|
||||||
i < minNbBanks; i++) {
|
|
||||||
sections[section->type].banks[i].sections = NULL;
|
sections[section->type].banks[i].sections = NULL;
|
||||||
sections[section->type].banks[i].zeroLenSections = NULL;
|
sections[section->type].banks[i].zeroLenSections = NULL;
|
||||||
}
|
}
|
||||||
sections[section->type].nbBanks = minNbBanks;
|
sections[section->type].nbBanks = minNbBanks;
|
||||||
}
|
}
|
||||||
if (!sections[section->type].banks)
|
if (!sections[section->type].banks)
|
||||||
err(1, "Failed to realloc banks");
|
err("Failed to realloc banks");
|
||||||
|
|
||||||
struct SortedSection *newSection = malloc(sizeof(*newSection));
|
struct SortedSection *newSection = malloc(sizeof(*newSection));
|
||||||
struct SortedSection **ptr = section->size
|
struct SortedSection **ptr = section->size
|
||||||
@@ -76,7 +98,7 @@ void out_AddSection(struct Section const *section)
|
|||||||
: §ions[section->type].banks[targetBank].zeroLenSections;
|
: §ions[section->type].banks[targetBank].zeroLenSections;
|
||||||
|
|
||||||
if (!newSection)
|
if (!newSection)
|
||||||
err(1, "Failed to add new section \"%s\"", section->name);
|
err("Failed to add new section \"%s\"", section->name);
|
||||||
newSection->section = section;
|
newSection->section = section;
|
||||||
|
|
||||||
while (*ptr && (*ptr)->section->org < section->org)
|
while (*ptr && (*ptr)->section->org < section->org)
|
||||||
@@ -103,43 +125,63 @@ struct Section const *out_OverlappingSection(struct Section const *section)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs sanity checks on the overlay file.
|
* Performs sanity checks on the overlay file.
|
||||||
|
* @return The number of ROM banks in the overlay file
|
||||||
*/
|
*/
|
||||||
static void checkOverlay(void)
|
static uint32_t checkOverlaySize(void)
|
||||||
{
|
{
|
||||||
if (!overlayFile)
|
if (!overlayFile)
|
||||||
return;
|
return 0;
|
||||||
|
|
||||||
if (fseek(overlayFile, 0, SEEK_END) != 0) {
|
if (fseek(overlayFile, 0, SEEK_END) != 0) {
|
||||||
warnx("Overlay file is not seekable, cannot check if properly formed");
|
warnx("Overlay file is not seekable, cannot check if properly formed");
|
||||||
return;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
long overlaySize = ftell(overlayFile);
|
long overlaySize = ftell(overlayFile);
|
||||||
|
|
||||||
if (overlaySize % 0x4000)
|
|
||||||
errx(1, "Overlay file must have a size multiple of 0x4000");
|
|
||||||
|
|
||||||
/* Reset back to beginning */
|
/* Reset back to beginning */
|
||||||
fseek(overlayFile, 0, SEEK_SET);
|
fseek(overlayFile, 0, SEEK_SET);
|
||||||
|
|
||||||
uint32_t nbOverlayBanks = overlaySize / 0x4000 - 1;
|
if (overlaySize % BANK_SIZE)
|
||||||
|
errx("Overlay file must have a size multiple of 0x4000");
|
||||||
|
|
||||||
if (nbOverlayBanks < 1)
|
uint32_t nbOverlayBanks = overlaySize / BANK_SIZE;
|
||||||
errx(1, "Overlay must be at least 0x8000 bytes large");
|
|
||||||
|
|
||||||
if (nbOverlayBanks > sections[SECTTYPE_ROMX].nbBanks) {
|
if (is32kMode && nbOverlayBanks != 2)
|
||||||
|
errx("Overlay must be exactly 0x8000 bytes large");
|
||||||
|
|
||||||
|
if (nbOverlayBanks < 2)
|
||||||
|
errx("Overlay must be at least 0x8000 bytes large");
|
||||||
|
|
||||||
|
return nbOverlayBanks;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Expand sections[SECTTYPE_ROMX].banks to cover all the overlay banks.
|
||||||
|
* This ensures that writeROM will output each bank, even if some are not
|
||||||
|
* covered by any sections.
|
||||||
|
* @param nbOverlayBanks The number of banks in the overlay file
|
||||||
|
*/
|
||||||
|
static void coverOverlayBanks(uint32_t nbOverlayBanks)
|
||||||
|
{
|
||||||
|
/* 2 if is32kMode, 1 otherwise */
|
||||||
|
uint32_t nbRom0Banks = maxsize[SECTTYPE_ROM0] / BANK_SIZE;
|
||||||
|
/* Discount ROM0 banks to avoid outputting too much */
|
||||||
|
uint32_t nbUncoveredBanks = nbOverlayBanks - nbRom0Banks > sections[SECTTYPE_ROMX].nbBanks
|
||||||
|
? nbOverlayBanks - nbRom0Banks
|
||||||
|
: 0;
|
||||||
|
|
||||||
|
if (nbUncoveredBanks > sections[SECTTYPE_ROMX].nbBanks) {
|
||||||
sections[SECTTYPE_ROMX].banks =
|
sections[SECTTYPE_ROMX].banks =
|
||||||
realloc(sections[SECTTYPE_ROMX].banks,
|
realloc(sections[SECTTYPE_ROMX].banks,
|
||||||
sizeof(*sections[SECTTYPE_ROMX].banks) *
|
sizeof(*sections[SECTTYPE_ROMX].banks) * nbUncoveredBanks);
|
||||||
nbOverlayBanks);
|
|
||||||
if (!sections[SECTTYPE_ROMX].banks)
|
if (!sections[SECTTYPE_ROMX].banks)
|
||||||
err(1, "Failed to realloc banks for overlay");
|
err("Failed to realloc banks for overlay");
|
||||||
for (uint32_t i = sections[SECTTYPE_ROMX].nbBanks;
|
for (uint32_t i = sections[SECTTYPE_ROMX].nbBanks; i < nbUncoveredBanks; i++) {
|
||||||
i < nbOverlayBanks; i++) {
|
|
||||||
sections[SECTTYPE_ROMX].banks[i].sections = NULL;
|
sections[SECTTYPE_ROMX].banks[i].sections = NULL;
|
||||||
sections[SECTTYPE_ROMX].banks[i].zeroLenSections = NULL;
|
sections[SECTTYPE_ROMX].banks[i].zeroLenSections = NULL;
|
||||||
}
|
}
|
||||||
sections[SECTTYPE_ROMX].nbBanks = nbOverlayBanks;
|
sections[SECTTYPE_ROMX].nbBanks = nbUncoveredBanks;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,16 +237,19 @@ static void writeROM(void)
|
|||||||
outputFile = openFile(outputFileName, "wb");
|
outputFile = openFile(outputFileName, "wb");
|
||||||
overlayFile = openFile(overlayFileName, "rb");
|
overlayFile = openFile(overlayFileName, "rb");
|
||||||
|
|
||||||
checkOverlay();
|
uint32_t nbOverlayBanks = checkOverlaySize();
|
||||||
|
|
||||||
|
if (nbOverlayBanks > 0)
|
||||||
|
coverOverlayBanks(nbOverlayBanks);
|
||||||
|
|
||||||
if (outputFile) {
|
if (outputFile) {
|
||||||
if (sections[SECTTYPE_ROM0].nbBanks > 0)
|
if (sections[SECTTYPE_ROM0].nbBanks > 0)
|
||||||
writeBank(sections[SECTTYPE_ROM0].banks[0].sections,
|
writeBank(sections[SECTTYPE_ROM0].banks[0].sections,
|
||||||
0x0000, 0x4000);
|
startaddr[SECTTYPE_ROM0], maxsize[SECTTYPE_ROM0]);
|
||||||
|
|
||||||
for (uint32_t i = 0 ; i < sections[SECTTYPE_ROMX].nbBanks; i++)
|
for (uint32_t i = 0 ; i < sections[SECTTYPE_ROMX].nbBanks; i++)
|
||||||
writeBank(sections[SECTTYPE_ROMX].banks[i].sections,
|
writeBank(sections[SECTTYPE_ROMX].banks[i].sections,
|
||||||
0x4000, 0x4000);
|
startaddr[SECTTYPE_ROMX], maxsize[SECTTYPE_ROMX]);
|
||||||
}
|
}
|
||||||
|
|
||||||
closeFile(outputFile);
|
closeFile(outputFile);
|
||||||
@@ -228,80 +273,98 @@ static struct SortedSection const **nextSection(struct SortedSection const **s1,
|
|||||||
return (*s1)->section->org < (*s2)->section->org ? s1 : s2;
|
return (*s1)->section->org < (*s2)->section->org ? s1 : s2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Comparator function for `qsort` to sort symbols
|
||||||
|
* Symbols are ordered by address, or else by original index for a stable sort
|
||||||
|
*/
|
||||||
|
static int compareSymbols(void const *a, void const *b)
|
||||||
|
{
|
||||||
|
struct SortedSymbol const *sym1 = (struct SortedSymbol const *)a;
|
||||||
|
struct SortedSymbol const *sym2 = (struct SortedSymbol const *)b;
|
||||||
|
|
||||||
|
if (sym1->addr != sym2->addr)
|
||||||
|
return sym1->addr < sym2->addr ? -1 : 1;
|
||||||
|
|
||||||
|
return sym1->idx < sym2->idx ? -1 : sym1->idx > sym2->idx ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write a bank's contents to the sym file
|
* Write a bank's contents to the sym file
|
||||||
* @param bankSections The bank's sections
|
* @param bankSections The bank's sections
|
||||||
*/
|
*/
|
||||||
static void writeSymBank(struct SortedSections const *bankSections)
|
static void writeSymBank(struct SortedSections const *bankSections,
|
||||||
|
enum SectionType type, uint32_t bank)
|
||||||
{
|
{
|
||||||
if (!symFile)
|
if (!symFile)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
struct {
|
uint32_t nbSymbols = 0;
|
||||||
struct SortedSection const *sections;
|
|
||||||
#define sect sections->section /* Fake member as a shortcut */
|
|
||||||
uint32_t i;
|
|
||||||
struct Symbol const *sym;
|
|
||||||
uint16_t addr;
|
|
||||||
} sectList = { .sections = bankSections->sections, .i = 0 },
|
|
||||||
zlSectList = { .sections = bankSections->zeroLenSections, .i = 0 },
|
|
||||||
*minSectList;
|
|
||||||
|
|
||||||
for (;;) {
|
for (struct SortedSection const *ptr = bankSections->zeroLenSections; ptr; ptr = ptr->next) {
|
||||||
while (sectList.sections
|
for (struct Section const *sect = ptr->section; sect; sect = sect->nextu)
|
||||||
&& sectList.i == sectList.sect->nbSymbols) {
|
nbSymbols += sect->nbSymbols;
|
||||||
sectList.sections = sectList.sections->next;
|
|
||||||
sectList.i = 0;
|
|
||||||
}
|
|
||||||
while (zlSectList.sections
|
|
||||||
&& zlSectList.i == zlSectList.sect->nbSymbols) {
|
|
||||||
zlSectList.sections = zlSectList.sections->next;
|
|
||||||
zlSectList.i = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sectList.sections && !zlSectList.sections) {
|
|
||||||
break;
|
|
||||||
} else if (sectList.sections && zlSectList.sections) {
|
|
||||||
sectList.sym = sectList.sect->symbols[sectList.i];
|
|
||||||
zlSectList.sym = zlSectList.sect->symbols[zlSectList.i];
|
|
||||||
sectList.addr =
|
|
||||||
sectList.sym->offset + sectList.sect->org;
|
|
||||||
zlSectList.addr =
|
|
||||||
zlSectList.sym->offset + zlSectList.sect->org;
|
|
||||||
|
|
||||||
minSectList = sectList.addr < zlSectList.addr
|
|
||||||
? §List
|
|
||||||
: &zlSectList;
|
|
||||||
} else if (sectList.sections) {
|
|
||||||
sectList.sym = sectList.sect->symbols[sectList.i];
|
|
||||||
sectList.addr =
|
|
||||||
sectList.sym->offset + sectList.sect->org;
|
|
||||||
|
|
||||||
minSectList = §List;
|
|
||||||
} else {
|
|
||||||
zlSectList.sym = zlSectList.sect->symbols[zlSectList.i];
|
|
||||||
zlSectList.addr =
|
|
||||||
zlSectList.sym->offset + zlSectList.sect->org;
|
|
||||||
|
|
||||||
minSectList = &zlSectList;
|
|
||||||
}
|
|
||||||
fprintf(symFile, "%02" PRIx32 ":%04" PRIx16 " %s\n",
|
|
||||||
minSectList->sect->bank, minSectList->addr,
|
|
||||||
minSectList->sym->name);
|
|
||||||
minSectList->i++;
|
|
||||||
}
|
}
|
||||||
#undef sect
|
for (struct SortedSection const *ptr = bankSections->sections; ptr; ptr = ptr->next) {
|
||||||
|
for (struct Section const *sect = ptr->section; sect; sect = sect->nextu)
|
||||||
|
nbSymbols += sect->nbSymbols;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nbSymbols)
|
||||||
|
return;
|
||||||
|
|
||||||
|
struct SortedSymbol *symList = malloc(sizeof(*symList) * nbSymbols);
|
||||||
|
|
||||||
|
if (!symList)
|
||||||
|
err("Failed to allocate symbol list");
|
||||||
|
|
||||||
|
uint32_t idx = 0;
|
||||||
|
|
||||||
|
for (struct SortedSection const *ptr = bankSections->zeroLenSections; ptr; ptr = ptr->next) {
|
||||||
|
for (struct Section const *sect = ptr->section; sect; sect = sect->nextu) {
|
||||||
|
for (uint32_t i = 0; i < sect->nbSymbols; i++) {
|
||||||
|
symList[idx].idx = idx;
|
||||||
|
symList[idx].sym = sect->symbols[i];
|
||||||
|
symList[idx].addr = symList[idx].sym->offset + sect->org;
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (struct SortedSection const *ptr = bankSections->sections; ptr; ptr = ptr->next) {
|
||||||
|
for (struct Section const *sect = ptr->section; sect; sect = sect->nextu) {
|
||||||
|
for (uint32_t i = 0; i < sect->nbSymbols; i++) {
|
||||||
|
symList[idx].idx = idx;
|
||||||
|
symList[idx].sym = sect->symbols[i];
|
||||||
|
symList[idx].addr = symList[idx].sym->offset + sect->org;
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(idx == nbSymbols);
|
||||||
|
|
||||||
|
qsort(symList, nbSymbols, sizeof(*symList), compareSymbols);
|
||||||
|
|
||||||
|
uint32_t symBank = bank + bankranges[type][0];
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < nbSymbols; i++) {
|
||||||
|
struct SortedSymbol *sym = &symList[i];
|
||||||
|
|
||||||
|
fprintf(symFile, "%02" PRIx32 ":%04" PRIx16 " %s\n",
|
||||||
|
symBank, sym->addr, sym->sym->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(symList);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Write a bank's contents to the map file
|
* Write a bank's contents to the map file
|
||||||
* @param bankSections The bank's sections
|
* @param bankSections The bank's sections
|
||||||
|
* @return The bank's used space
|
||||||
*/
|
*/
|
||||||
static void writeMapBank(struct SortedSections const *sectList,
|
static uint16_t writeMapBank(struct SortedSections const *sectList,
|
||||||
enum SectionType type, uint32_t bank)
|
enum SectionType type, uint32_t bank)
|
||||||
{
|
{
|
||||||
if (!mapFile)
|
if (!mapFile)
|
||||||
return;
|
return 0;
|
||||||
|
|
||||||
struct SortedSection const *section = sectList->sections;
|
struct SortedSection const *section = sectList->sections;
|
||||||
struct SortedSection const *zeroLenSection = sectList->zeroLenSections;
|
struct SortedSection const *zeroLenSection = sectList->zeroLenSections;
|
||||||
@@ -309,17 +372,18 @@ static void writeMapBank(struct SortedSections const *sectList,
|
|||||||
fprintf(mapFile, "%s bank #%" PRIu32 ":\n", typeNames[type],
|
fprintf(mapFile, "%s bank #%" PRIu32 ":\n", typeNames[type],
|
||||||
bank + bankranges[type][0]);
|
bank + bankranges[type][0]);
|
||||||
|
|
||||||
uint16_t slack = maxsize[type];
|
uint16_t used = 0;
|
||||||
|
|
||||||
while (section || zeroLenSection) {
|
while (section || zeroLenSection) {
|
||||||
struct SortedSection const **pickedSection =
|
struct SortedSection const **pickedSection =
|
||||||
nextSection(§ion, &zeroLenSection);
|
nextSection(§ion, &zeroLenSection);
|
||||||
struct Section const *sect = (*pickedSection)->section;
|
struct Section const *sect = (*pickedSection)->section;
|
||||||
|
|
||||||
slack -= sect->size;
|
used += sect->size;
|
||||||
|
|
||||||
if (sect->size != 0)
|
if (sect->size != 0)
|
||||||
fprintf(mapFile, " SECTION: $%04" PRIx16 "-$%04" PRIx16 " ($%04" PRIx16 " byte%s) [\"%s\"]\n",
|
fprintf(mapFile, " SECTION: $%04" PRIx16 "-$%04x ($%04" PRIx16
|
||||||
|
" byte%s) [\"%s\"]\n",
|
||||||
sect->org, sect->org + sect->size - 1,
|
sect->org, sect->org + sect->size - 1,
|
||||||
sect->size, sect->size == 1 ? "" : "s",
|
sect->size, sect->size == 1 ? "" : "s",
|
||||||
sect->name);
|
sect->name);
|
||||||
@@ -327,19 +391,57 @@ static void writeMapBank(struct SortedSections const *sectList,
|
|||||||
fprintf(mapFile, " SECTION: $%04" PRIx16 " (0 bytes) [\"%s\"]\n",
|
fprintf(mapFile, " SECTION: $%04" PRIx16 " (0 bytes) [\"%s\"]\n",
|
||||||
sect->org, sect->name);
|
sect->org, sect->name);
|
||||||
|
|
||||||
for (size_t i = 0; i < sect->nbSymbols; i++)
|
uint16_t org = sect->org;
|
||||||
fprintf(mapFile, " $%04" PRIx32 " = %s\n",
|
|
||||||
sect->symbols[i]->offset + sect->org,
|
while (sect) {
|
||||||
sect->symbols[i]->name);
|
for (size_t i = 0; i < sect->nbSymbols; i++)
|
||||||
|
fprintf(mapFile, " $%04" PRIx32 " = %s\n",
|
||||||
|
sect->symbols[i]->offset + org,
|
||||||
|
sect->symbols[i]->name);
|
||||||
|
|
||||||
|
sect = sect->nextu; // Also print symbols in the following "pieces"
|
||||||
|
}
|
||||||
|
|
||||||
*pickedSection = (*pickedSection)->next;
|
*pickedSection = (*pickedSection)->next;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (slack == maxsize[type])
|
|
||||||
|
if (used == 0) {
|
||||||
fputs(" EMPTY\n\n", mapFile);
|
fputs(" EMPTY\n\n", mapFile);
|
||||||
else
|
} else {
|
||||||
|
uint16_t slack = maxsize[type] - used;
|
||||||
|
|
||||||
fprintf(mapFile, " SLACK: $%04" PRIx16 " byte%s\n\n", slack,
|
fprintf(mapFile, " SLACK: $%04" PRIx16 " byte%s\n\n", slack,
|
||||||
slack == 1 ? "" : "s");
|
slack == 1 ? "" : "s");
|
||||||
|
}
|
||||||
|
|
||||||
|
return used;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write the total used space by section type to the map file
|
||||||
|
* @param usedMap The total used space by section type
|
||||||
|
*/
|
||||||
|
static void writeMapUsed(uint32_t usedMap[MIN_NB_ELMS(SECTTYPE_INVALID)])
|
||||||
|
{
|
||||||
|
if (!mapFile)
|
||||||
|
return;
|
||||||
|
|
||||||
|
fputs("USED:\n", mapFile);
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < SECTTYPE_INVALID; i++) {
|
||||||
|
enum SectionType type = typeMap[i];
|
||||||
|
|
||||||
|
// Do not output used space for VRAM or OAM
|
||||||
|
if (type == SECTTYPE_VRAM || type == SECTTYPE_OAM)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (sections[type].nbBanks > 0) {
|
||||||
|
fprintf(mapFile, " %s: $%04" PRIx32 " byte%s in %" PRIu32 " bank%s\n",
|
||||||
|
typeNames[type], usedMap[type], usedMap[type] == 1 ? "" : "s",
|
||||||
|
sections[type].nbBanks, sections[type].nbBanks == 1 ? "" : "s");
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -350,16 +452,7 @@ static void writeSymAndMap(void)
|
|||||||
if (!symFileName && !mapFileName)
|
if (!symFileName && !mapFileName)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
enum SectionType typeMap[SECTTYPE_INVALID] = {
|
uint32_t usedMap[SECTTYPE_INVALID] = {0};
|
||||||
SECTTYPE_ROM0,
|
|
||||||
SECTTYPE_ROMX,
|
|
||||||
SECTTYPE_VRAM,
|
|
||||||
SECTTYPE_SRAM,
|
|
||||||
SECTTYPE_WRAM0,
|
|
||||||
SECTTYPE_WRAMX,
|
|
||||||
SECTTYPE_OAM,
|
|
||||||
SECTTYPE_HRAM
|
|
||||||
};
|
|
||||||
|
|
||||||
symFile = openFile(symFileName, "w");
|
symFile = openFile(symFileName, "w");
|
||||||
mapFile = openFile(mapFileName, "w");
|
mapFile = openFile(mapFileName, "w");
|
||||||
@@ -370,16 +463,16 @@ static void writeSymAndMap(void)
|
|||||||
for (uint8_t i = 0; i < SECTTYPE_INVALID; i++) {
|
for (uint8_t i = 0; i < SECTTYPE_INVALID; i++) {
|
||||||
enum SectionType type = typeMap[i];
|
enum SectionType type = typeMap[i];
|
||||||
|
|
||||||
if (sections[type].nbBanks > 0) {
|
for (uint32_t bank = 0; bank < sections[type].nbBanks; bank++) {
|
||||||
for (uint32_t bank = 0; bank < sections[type].nbBanks;
|
struct SortedSections const *sect = §ions[type].banks[bank];
|
||||||
bank++) {
|
|
||||||
writeSymBank(§ions[type].banks[bank]);
|
writeSymBank(sect, type, bank);
|
||||||
writeMapBank(§ions[type].banks[bank],
|
usedMap[type] += writeMapBank(sect, type, bank);
|
||||||
type, bank);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
writeMapUsed(usedMap);
|
||||||
|
|
||||||
closeFile(symFile);
|
closeFile(symFile);
|
||||||
closeFile(mapFile);
|
closeFile(mapFile);
|
||||||
}
|
}
|
||||||
|
|||||||
+78
-57
@@ -17,50 +17,9 @@
|
|||||||
#include "link/section.h"
|
#include "link/section.h"
|
||||||
#include "link/symbol.h"
|
#include "link/symbol.h"
|
||||||
|
|
||||||
|
#include "error.h"
|
||||||
#include "linkdefs.h"
|
#include "linkdefs.h"
|
||||||
|
#include "opmath.h"
|
||||||
#include "extern/err.h"
|
|
||||||
|
|
||||||
static int32_t asl(int32_t value, int32_t shiftamt); // Forward decl for below
|
|
||||||
static int32_t asr(int32_t value, int32_t shiftamt)
|
|
||||||
{
|
|
||||||
uint32_t uvalue = value;
|
|
||||||
|
|
||||||
// Get the easy cases out of the way
|
|
||||||
if (shiftamt == 0)
|
|
||||||
return value;
|
|
||||||
if (value == 0 || shiftamt <= -32)
|
|
||||||
return 0;
|
|
||||||
if (shiftamt > 31)
|
|
||||||
return (value < 0) ? -1 : 0;
|
|
||||||
if (shiftamt < 0)
|
|
||||||
return asl(value, -shiftamt);
|
|
||||||
if (value > 0)
|
|
||||||
return uvalue >> shiftamt;
|
|
||||||
|
|
||||||
{
|
|
||||||
// Calculate an OR mask for sign extension
|
|
||||||
// 1->0x80000000, 2->0xC0000000, ..., 31->0xFFFFFFFE
|
|
||||||
uint32_t shiftamt_high_bits = -((uint32_t)1 << (32 - shiftamt));
|
|
||||||
|
|
||||||
return (uvalue >> shiftamt) | shiftamt_high_bits;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int32_t asl(int32_t value, int32_t shiftamt)
|
|
||||||
{
|
|
||||||
// Repeat the easy cases here to avoid INT_MIN funny business
|
|
||||||
if (shiftamt == 0)
|
|
||||||
return value;
|
|
||||||
if (value == 0 || shiftamt >= 32)
|
|
||||||
return 0;
|
|
||||||
if (shiftamt < -31)
|
|
||||||
return (value < 0) ? -1 : 0;
|
|
||||||
if (shiftamt < 0)
|
|
||||||
return asr(value, -shiftamt);
|
|
||||||
|
|
||||||
return (uint32_t)value << shiftamt;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This is an "empty"-type stack. Apart from the actual values, we also remember
|
* This is an "empty"-type stack. Apart from the actual values, we also remember
|
||||||
@@ -77,16 +36,16 @@ struct RPNStack {
|
|||||||
size_t capacity;
|
size_t capacity;
|
||||||
} stack;
|
} stack;
|
||||||
|
|
||||||
static inline void initRPNStack(void)
|
static void initRPNStack(void)
|
||||||
{
|
{
|
||||||
stack.capacity = 64;
|
stack.capacity = 64;
|
||||||
stack.values = malloc(sizeof(*stack.values) * stack.capacity);
|
stack.values = malloc(sizeof(*stack.values) * stack.capacity);
|
||||||
stack.errorFlags = malloc(sizeof(*stack.errorFlags) * stack.capacity);
|
stack.errorFlags = malloc(sizeof(*stack.errorFlags) * stack.capacity);
|
||||||
if (!stack.values || !stack.errorFlags)
|
if (!stack.values || !stack.errorFlags)
|
||||||
err(1, "Failed to init RPN stack");
|
err("Failed to init RPN stack");
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void clearRPNStack(void)
|
static void clearRPNStack(void)
|
||||||
{
|
{
|
||||||
stack.size = 0;
|
stack.size = 0;
|
||||||
}
|
}
|
||||||
@@ -97,7 +56,7 @@ static void pushRPN(int32_t value, bool comesFromError)
|
|||||||
static const size_t increase_factor = 2;
|
static const size_t increase_factor = 2;
|
||||||
|
|
||||||
if (stack.capacity > SIZE_MAX / increase_factor)
|
if (stack.capacity > SIZE_MAX / increase_factor)
|
||||||
errx(1, "Overflow in RPN stack resize");
|
errx("Overflow in RPN stack resize");
|
||||||
|
|
||||||
stack.capacity *= increase_factor;
|
stack.capacity *= increase_factor;
|
||||||
stack.values =
|
stack.values =
|
||||||
@@ -110,7 +69,7 @@ static void pushRPN(int32_t value, bool comesFromError)
|
|||||||
* the overflow check above. Hence the stringent check below.
|
* the overflow check above. Hence the stringent check below.
|
||||||
*/
|
*/
|
||||||
if (!stack.values || !stack.errorFlags || !stack.capacity)
|
if (!stack.values || !stack.errorFlags || !stack.capacity)
|
||||||
err(1, "Failed to resize RPN stack");
|
err("Failed to resize RPN stack");
|
||||||
}
|
}
|
||||||
|
|
||||||
stack.values[stack.size] = value;
|
stack.values[stack.size] = value;
|
||||||
@@ -132,7 +91,7 @@ static int32_t popRPN(struct FileStackNode const *node, uint32_t lineNo)
|
|||||||
return stack.values[stack.size];
|
return stack.values[stack.size];
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void freeRPNStack(void)
|
static void freeRPNStack(void)
|
||||||
{
|
{
|
||||||
free(stack.values);
|
free(stack.values);
|
||||||
free(stack.errorFlags);
|
free(stack.errorFlags);
|
||||||
@@ -152,7 +111,7 @@ static uint32_t getRPNByte(uint8_t const **expression, int32_t *size,
|
|||||||
static struct Symbol const *getSymbol(struct Symbol const * const *symbolList,
|
static struct Symbol const *getSymbol(struct Symbol const * const *symbolList,
|
||||||
uint32_t index)
|
uint32_t index)
|
||||||
{
|
{
|
||||||
assert(index != -1); /* PC needs to be handled specially, not here */
|
assert(index != (uint32_t)-1); /* PC needs to be handled specially, not here */
|
||||||
struct Symbol const *symbol = symbolList[index];
|
struct Symbol const *symbol = symbolList[index];
|
||||||
|
|
||||||
/* If the symbol is defined elsewhere... */
|
/* If the symbol is defined elsewhere... */
|
||||||
@@ -219,7 +178,7 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
|||||||
popRPN();
|
popRPN();
|
||||||
value = INT32_MAX;
|
value = INT32_MAX;
|
||||||
} else {
|
} else {
|
||||||
value = popRPN() / value;
|
value = op_divide(popRPN(), value);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case RPN_MOD:
|
case RPN_MOD:
|
||||||
@@ -231,12 +190,24 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
|||||||
popRPN();
|
popRPN();
|
||||||
value = 0;
|
value = 0;
|
||||||
} else {
|
} else {
|
||||||
value = popRPN() % value;
|
value = op_modulo(popRPN(), value);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case RPN_UNSUB:
|
case RPN_UNSUB:
|
||||||
value = -popRPN();
|
value = -popRPN();
|
||||||
break;
|
break;
|
||||||
|
case RPN_EXP:
|
||||||
|
value = popRPN();
|
||||||
|
if (value < 0) {
|
||||||
|
if (!isError)
|
||||||
|
error(patch->src, patch->lineNo, "Exponent by negative");
|
||||||
|
isError = true;
|
||||||
|
popRPN();
|
||||||
|
value = 0;
|
||||||
|
} else {
|
||||||
|
value = op_exponent(popRPN(), value);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case RPN_OR:
|
case RPN_OR:
|
||||||
value = popRPN() | popRPN();
|
value = popRPN() | popRPN();
|
||||||
@@ -288,11 +259,15 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
|||||||
|
|
||||||
case RPN_SHL:
|
case RPN_SHL:
|
||||||
value = popRPN();
|
value = popRPN();
|
||||||
value = asl(popRPN(), value);
|
value = op_shift_left(popRPN(), value);
|
||||||
break;
|
break;
|
||||||
case RPN_SHR:
|
case RPN_SHR:
|
||||||
value = popRPN();
|
value = popRPN();
|
||||||
value = asr(popRPN(), value);
|
value = op_shift_right(popRPN(), value);
|
||||||
|
break;
|
||||||
|
case RPN_USHR:
|
||||||
|
value = popRPN();
|
||||||
|
value = op_shift_right_unsigned(popRPN(), value);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RPN_BANK_SYM:
|
case RPN_BANK_SYM:
|
||||||
@@ -320,6 +295,11 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case RPN_BANK_SECT:
|
case RPN_BANK_SECT:
|
||||||
|
/*
|
||||||
|
* `expression` is not guaranteed to be '\0'-terminated. If it is not,
|
||||||
|
* `getRPNByte` will have a fatal internal error.
|
||||||
|
* In either case, `getRPNByte` will not free `expression`.
|
||||||
|
*/
|
||||||
name = (char const *)expression;
|
name = (char const *)expression;
|
||||||
while (getRPNByte(&expression, &size, patch->src, patch->lineNo))
|
while (getRPNByte(&expression, &size, patch->src, patch->lineNo))
|
||||||
;
|
;
|
||||||
@@ -348,6 +328,44 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case RPN_SIZEOF_SECT:
|
||||||
|
/* This has assumptions commented in the `RPN_BANK_SECT` case above. */
|
||||||
|
name = (char const *)expression;
|
||||||
|
while (getRPNByte(&expression, &size, patch->src, patch->lineNo))
|
||||||
|
;
|
||||||
|
|
||||||
|
sect = sect_GetSection(name);
|
||||||
|
|
||||||
|
if (!sect) {
|
||||||
|
error(patch->src, patch->lineNo,
|
||||||
|
"Requested SIZEOF() of section \"%s\", which was not found",
|
||||||
|
name);
|
||||||
|
isError = true;
|
||||||
|
value = 1;
|
||||||
|
} else {
|
||||||
|
value = sect->size;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RPN_STARTOF_SECT:
|
||||||
|
/* This has assumptions commented in the `RPN_BANK_SECT` case above. */
|
||||||
|
name = (char const *)expression;
|
||||||
|
while (getRPNByte(&expression, &size, patch->src, patch->lineNo))
|
||||||
|
;
|
||||||
|
|
||||||
|
sect = sect_GetSection(name);
|
||||||
|
|
||||||
|
if (!sect) {
|
||||||
|
error(patch->src, patch->lineNo,
|
||||||
|
"Requested STARTOF() of section \"%s\", which was not found",
|
||||||
|
name);
|
||||||
|
isError = true;
|
||||||
|
value = 1;
|
||||||
|
} else {
|
||||||
|
value = sect->org;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case RPN_HRAM:
|
case RPN_HRAM:
|
||||||
value = popRPN();
|
value = popRPN();
|
||||||
if (!isError && (value < 0
|
if (!isError && (value < 0
|
||||||
@@ -428,7 +446,7 @@ static int32_t computeRPNExpr(struct Patch const *patch,
|
|||||||
|
|
||||||
void patch_CheckAssertions(struct Assertion *assert)
|
void patch_CheckAssertions(struct Assertion *assert)
|
||||||
{
|
{
|
||||||
verbosePrint("Checking assertions...");
|
verbosePrint("Checking assertions...\n");
|
||||||
initRPNStack();
|
initRPNStack();
|
||||||
|
|
||||||
while (assert) {
|
while (assert) {
|
||||||
@@ -463,6 +481,8 @@ void patch_CheckAssertions(struct Assertion *assert)
|
|||||||
}
|
}
|
||||||
struct Assertion *next = assert->next;
|
struct Assertion *next = assert->next;
|
||||||
|
|
||||||
|
free(assert->patch.rpnExpression);
|
||||||
|
free(assert->message);
|
||||||
free(assert);
|
free(assert);
|
||||||
assert = next;
|
assert = next;
|
||||||
}
|
}
|
||||||
@@ -490,9 +510,10 @@ static void applyFilePatches(struct Section *section, struct Section *dataSectio
|
|||||||
|
|
||||||
/* `jr` is quite unlike the others... */
|
/* `jr` is quite unlike the others... */
|
||||||
if (patch->type == PATCHTYPE_JR) {
|
if (patch->type == PATCHTYPE_JR) {
|
||||||
/* Target is relative to the byte *after* the operand */
|
// Offset is relative to the byte *after* the operand
|
||||||
|
// PC as operand to `jr` is lower than reference PC by 2
|
||||||
uint16_t address = patch->pcSection->org
|
uint16_t address = patch->pcSection->org
|
||||||
+ patch->pcOffset + 1;
|
+ patch->pcOffset + 2;
|
||||||
int16_t jumpOffset = value - address;
|
int16_t jumpOffset = value - address;
|
||||||
|
|
||||||
if (!isError && (jumpOffset < -128 || jumpOffset > 127))
|
if (!isError && (jumpOffset < -128 || jumpOffset > 127))
|
||||||
|
|||||||
+61
-2
@@ -1,11 +1,11 @@
|
|||||||
.\"
|
.\"
|
||||||
.\" This file is part of RGBDS.
|
.\" This file is part of RGBDS.
|
||||||
.\"
|
.\"
|
||||||
.\" Copyright (c) 2010-2019, Anthony J. Bentley and RGBDS contributors.
|
.\" Copyright (c) 2010-2021, Anthony J. Bentley and RGBDS contributors.
|
||||||
.\"
|
.\"
|
||||||
.\" SPDX-License-Identifier: MIT
|
.\" SPDX-License-Identifier: MIT
|
||||||
.\"
|
.\"
|
||||||
.Dd November 26, 2019
|
.Dd March 28, 2021
|
||||||
.Dt RGBLINK 1
|
.Dt RGBLINK 1
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
.Op Fl O Ar overlay_file
|
.Op Fl O Ar overlay_file
|
||||||
.Op Fl o Ar out_file
|
.Op Fl o Ar out_file
|
||||||
.Op Fl p Ar pad_value
|
.Op Fl p Ar pad_value
|
||||||
|
.Op Fl S Ar spec
|
||||||
.Op Fl s Ar symbol
|
.Op Fl s Ar symbol
|
||||||
.Ar
|
.Ar
|
||||||
.Sh DESCRIPTION
|
.Sh DESCRIPTION
|
||||||
@@ -89,6 +90,14 @@ Has no effect if
|
|||||||
.Fl O
|
.Fl O
|
||||||
is specified.
|
is specified.
|
||||||
The default is 0.
|
The default is 0.
|
||||||
|
.It Fl S Ar spec , Fl Fl scramble Ar spec
|
||||||
|
Enables a different
|
||||||
|
.Dq scrambling
|
||||||
|
algorithm for placing sections.
|
||||||
|
See
|
||||||
|
.Sx Scrambling algorithm
|
||||||
|
below for an explanation and a description of
|
||||||
|
.Ar spec .
|
||||||
.It Fl s Ar symbol , Fl Fl smart Ar symbol
|
.It Fl s Ar symbol , Fl Fl smart Ar symbol
|
||||||
This option is ignored.
|
This option is ignored.
|
||||||
It was supposed to perform smart linking but fell into disrepair, and so has been removed.
|
It was supposed to perform smart linking but fell into disrepair, and so has been removed.
|
||||||
@@ -113,6 +122,56 @@ When making a ROM, be careful that not using this is not a replacement for
|
|||||||
.Xr rgbfix 1 Ap s Fl p
|
.Xr rgbfix 1 Ap s Fl p
|
||||||
option!
|
option!
|
||||||
.El
|
.El
|
||||||
|
.Ss Scrambling algorithm
|
||||||
|
The default section placement algorithm tries to minimize the number of banks used;
|
||||||
|
.Dq scrambling
|
||||||
|
instead places sections into a given pool of banks, trying to minimize the number of sections sharing a given bank.
|
||||||
|
This is useful to catch broken bank assumptions, such as expecting two different sections to land in the same bank (that is not guaranteed unless both are manually assigned the same bank number).
|
||||||
|
.Pp
|
||||||
|
A scrambling spec is a comma-separated list of region specs.
|
||||||
|
A trailing comma is allowed, as well as whitespace between all specs and their components.
|
||||||
|
Each region spec has the following form:
|
||||||
|
.D1 Ar region Ns Op = Ns Ar size
|
||||||
|
.Ar region
|
||||||
|
must be one of the following (case-insensitive), while
|
||||||
|
.Ar size
|
||||||
|
must be a positive decimal integer between 1 and the corresponding maximum.
|
||||||
|
Certain regions allow omitting the size, in which case it defaults to its max value.
|
||||||
|
.Bl -column "Region name" "Max value" "Size optional"
|
||||||
|
Region name Ta Max size Ta Size optional
|
||||||
|
.Cm romx Ta 65535 Ta \&No
|
||||||
|
.Cm sram Ta 255 Ta \&No
|
||||||
|
.Cm wramx Ta 7 Ta Yes
|
||||||
|
.El
|
||||||
|
.Pp
|
||||||
|
A
|
||||||
|
.Ar size
|
||||||
|
of 0 disables scrambling for that region.
|
||||||
|
.Pp
|
||||||
|
For example,
|
||||||
|
.Ql romx=64,wramx=4
|
||||||
|
will scramble
|
||||||
|
.Ic ROMX
|
||||||
|
sections among ROM banks 1 to 64,
|
||||||
|
.Ic WRAMX
|
||||||
|
sections among RAM banks 1 to 4, and will not scramble
|
||||||
|
.Ic SRAM
|
||||||
|
sections.
|
||||||
|
.Pp
|
||||||
|
Later region specs override earlier ones; for example,
|
||||||
|
.Ql romx=42, Romx=0
|
||||||
|
disables scrambling for
|
||||||
|
.Cm romx .
|
||||||
|
.Pp
|
||||||
|
.Cm wramx
|
||||||
|
scrambling is silently ignored if
|
||||||
|
.Fl w
|
||||||
|
is passed (including if implied by
|
||||||
|
.Fl d ) ,
|
||||||
|
as
|
||||||
|
.Ic WRAMX
|
||||||
|
sections will be treated as
|
||||||
|
.Ic WRAM0 .
|
||||||
.Sh EXAMPLES
|
.Sh EXAMPLES
|
||||||
All you need for a basic ROM is an object file, which can be made into a ROM image like so:
|
All you need for a basic ROM is an object file, which can be made into a ROM image like so:
|
||||||
.Pp
|
.Pp
|
||||||
|
|||||||
+2
-2
@@ -1,11 +1,11 @@
|
|||||||
.\"
|
.\"
|
||||||
.\" This file is part of RGBDS.
|
.\" This file is part of RGBDS.
|
||||||
.\"
|
.\"
|
||||||
.\" Copyright (c) 2017-2018, Antonio Nino Diaz and RGBDS contributors.
|
.\" Copyright (c) 2017-2021, Antonio Nino Diaz and RGBDS contributors.
|
||||||
.\"
|
.\"
|
||||||
.\" SPDX-License-Identifier: MIT
|
.\" SPDX-License-Identifier: MIT
|
||||||
.\"
|
.\"
|
||||||
.Dd November 26, 2019
|
.Dd March 28, 2021
|
||||||
.Dt RGBLINK 5
|
.Dt RGBLINK 5
|
||||||
.Os
|
.Os
|
||||||
.Sh NAME
|
.Sh NAME
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user