mirror of
https://github.com/gbdev/rgbds.git
synced 2026-09-18 03:37:06 +00:00
Compare commits
472
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 | ||
|
|
ede982b50a | ||
|
|
319d775c13 | ||
|
|
44124319a6 | ||
|
|
462fd7539c | ||
|
|
f16e34b804 | ||
|
|
c3ccdc548e | ||
|
|
fd721ca480 | ||
|
|
eac365aef0 | ||
|
|
4de6266442 | ||
|
|
213d985e17 | ||
|
|
de76dcb8fb | ||
|
|
30fb6bde5e | ||
|
|
4f842a1248 | ||
|
|
cc4d455b8a | ||
|
|
0bb5efebfd | ||
|
|
b6bf7ae620 | ||
|
|
dc96cc6d1e | ||
|
|
642daf1a76 | ||
|
|
84edfb3d88 | ||
|
|
7e620bff81 | ||
|
|
0c55703438 | ||
|
|
6c57ad2226 | ||
|
|
9028fb5391 | ||
|
|
12dc49b60a | ||
|
|
7e1d20acdf | ||
|
|
5230104852 | ||
|
|
55be77be69 | ||
|
|
42b3a17356 | ||
|
|
4e1d79081c | ||
|
|
4ce4fdec71 | ||
|
|
05256946ac | ||
|
|
73396166aa | ||
|
|
4c5d5c7085 | ||
|
|
045a9e8b93 | ||
|
|
4419f0d54f | ||
|
|
b07aa00d5c | ||
|
|
e4f5df1306 | ||
|
|
dc62d60e9b | ||
|
|
71d8aeb4c2 | ||
|
|
0836f67d42 | ||
|
|
176a57a1e9 | ||
|
|
0d02355dbf | ||
|
|
6767d11c23 | ||
|
|
2dd9015dc6 | ||
|
|
217c10ddac | ||
|
|
822e4e7c44 | ||
|
|
0b1d01792d | ||
|
|
01637768cf | ||
|
|
6a8ae643d5 | ||
|
|
fd83d46ba0 | ||
|
|
914856342c | ||
|
|
91889fc14a | ||
|
|
7c8ec5a5ed | ||
|
|
effc6788eb |
@@ -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
|
||||||
@@ -26,14 +27,12 @@ while getopts ":hr" opt; do
|
|||||||
;;
|
;;
|
||||||
\?)
|
\?)
|
||||||
echo "Unknown option '$OPTARG'"
|
echo "Unknown option '$OPTARG'"
|
||||||
if [ $bad_usage -eq 0 ]; then
|
bad_usage=1
|
||||||
usage
|
|
||||||
bad_usage=1
|
|
||||||
fi
|
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
if [ $bad_usage -ne 0 ]; then
|
if [ $bad_usage -ne 0 ]; then
|
||||||
|
usage
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
shift $(($OPTIND - 1))
|
shift $(($OPTIND - 1))
|
||||||
@@ -52,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
|
||||||
@@ -66,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]
|
||||||
@@ -77,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}/
|
||||||
@@ -89,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/
|
||||||
@@ -98,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 }}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
name: "Create release docs"
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types:
|
||||||
|
- released # This avoids triggering on pre-releases
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
if: github.repository_owner == 'gbdev'
|
||||||
|
runs-on: ubuntu-18.04
|
||||||
|
steps:
|
||||||
|
- name: Checkout rgbds@release
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
path: rgbds
|
||||||
|
- name: Checkout rgbds-www@master
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
repository: ${{ github.repository_owner }}/rgbds-www
|
||||||
|
path: rgbds-www
|
||||||
|
# `-O toc` was added in 1.14.5, but the repos only have 1.14.4
|
||||||
|
- name: Build and install mandoc + install groff
|
||||||
|
run: |
|
||||||
|
sudo apt-get -qq update
|
||||||
|
sudo apt-get install -yq groff zlib1g-dev
|
||||||
|
wget 'http://mandoc.bsd.lv/snapshots/mandoc-1.14.5.tar.gz'
|
||||||
|
tar xf mandoc-1.14.5.tar.gz
|
||||||
|
cd mandoc-1.14.5
|
||||||
|
./configure
|
||||||
|
make
|
||||||
|
sudo make install
|
||||||
|
- name: Update pages
|
||||||
|
working-directory: rgbds
|
||||||
|
run: | # The ref appears to be in the format "refs/tags/<version>", so strip that
|
||||||
|
./.github/actions/get-pages.sh -r ../rgbds-www ${GITHUB_REF##*/}
|
||||||
|
- name: Push new pages
|
||||||
|
working-directory: rgbds-www
|
||||||
|
run: |
|
||||||
|
mkdir -p -m 700 ~/.ssh
|
||||||
|
cat > ~/.ssh/id_ed25519 <<<"${{ secrets.SSH_KEY_SECRET }}"
|
||||||
|
chmod 0600 ~/.ssh/id_ed25519
|
||||||
|
eval $(ssh-agent -s)
|
||||||
|
ssh-add ~/.ssh/id_ed25519
|
||||||
|
git config --global user.name "GitHub Action"
|
||||||
|
git config --global user.email "[email protected]"
|
||||||
|
git add -A
|
||||||
|
git commit -m "Create RGBDS ${GITHUB_REF##*/} documentation"
|
||||||
|
if git remote | grep -q origin; then
|
||||||
|
git remote set-url origin [email protected]:${{ github.repository_owner }}/rgbds-www.git
|
||||||
|
else
|
||||||
|
git remote add origin [email protected]:${{ github.repository_owner }}/rgbds-www.git
|
||||||
|
fi
|
||||||
|
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,7 +17,7 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
if: secrets.SSH_KEY_SECRET != ''
|
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
|
||||||
@@ -32,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
|
||||||
@@ -45,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
|
|
||||||
|
|||||||
+49
-21
@@ -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,49 +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)
|
|
||||||
|
|
||||||
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)
|
||||||
|
|
||||||
|
# By default, build in Release mode; Debug mode must be explicitly requested
|
||||||
|
# (You may want to augment it with the options above)
|
||||||
|
if(CMAKE_BUILD_TYPE STREQUAL "Release")
|
||||||
|
message(CHECK_START "Checking if LTO is supported")
|
||||||
|
include(CheckIPOSupported)
|
||||||
|
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()
|
||||||
|
|||||||
+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
-2
@@ -7,8 +7,7 @@
|
|||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
RUN apk add --update \
|
RUN apk add --update \
|
||||||
build-base \
|
build-base \
|
||||||
byacc \
|
bison \
|
||||||
flex \
|
|
||||||
libpng-dev
|
libpng-dev
|
||||||
COPY . /rgbds
|
COPY . /rgbds
|
||||||
WORKDIR /rgbds
|
WORKDIR /rgbds
|
||||||
|
|||||||
@@ -30,49 +30,56 @@ 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/asmy.o \
|
|
||||||
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/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/asmy.h
|
src/asm/lexer.o src/asm/main.o: src/asm/parser.h
|
||||||
|
|
||||||
rgblink_obj := \
|
rgblink_obj := \
|
||||||
src/link/assign.o \
|
src/link/assign.o \
|
||||||
@@ -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 $@ $<
|
||||||
@@ -136,7 +156,7 @@ clean:
|
|||||||
$Q${RM} rgbgfx rgbgfx.exe
|
$Q${RM} rgbgfx rgbgfx.exe
|
||||||
$Qfind src/ -name "*.o" -exec rm {} \;
|
$Qfind src/ -name "*.o" -exec rm {} \;
|
||||||
$Q${RM} rgbshim.sh
|
$Q${RM} rgbshim.sh
|
||||||
$Q${RM} src/asm/asmy.c src/asm/asmy.h
|
$Q${RM} src/asm/parser.c src/asm/parser.h
|
||||||
|
|
||||||
# Target used to install the binaries and man pages.
|
# Target used to install the binaries and man pages.
|
||||||
|
|
||||||
@@ -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:
|
||||||
|
|||||||
+37
-20
@@ -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
|
||||||
-------------------
|
-------------------
|
||||||
|
|
||||||
@@ -32,7 +34,7 @@ is possible using ``make`` or ``cmake``; follow the link for more detailed instr
|
|||||||
|
|
||||||
.. code:: sh
|
.. code:: sh
|
||||||
|
|
||||||
cmake -S . -B build
|
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
|
||||||
cmake --build build
|
cmake --build build
|
||||||
cmake --install build
|
cmake --install build
|
||||||
|
|
||||||
@@ -45,40 +47,53 @@ The RGBDS source code file structure somewhat resembles the following:
|
|||||||
|
|
||||||
.
|
.
|
||||||
├── .github/
|
├── .github/
|
||||||
│ ├── actions/
|
│ ├── actions/
|
||||||
│ │ └── ...
|
│ │ └── ...
|
||||||
│ └── workflows/
|
│ └── workflows/
|
||||||
│ └── ...
|
│ └── ...
|
||||||
├── contrib/
|
├── contrib/
|
||||||
│ └── ...
|
│ ├── 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.
|
||||||
|
|
||||||
|
* ``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.
|
||||||
@@ -88,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
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
#compdef rgbasm
|
||||||
|
|
||||||
|
_rgbasm_warnings() {
|
||||||
|
local warnings=(
|
||||||
|
'error:Turn all warnings into errors'
|
||||||
|
|
||||||
|
'all:Enable most warning messages'
|
||||||
|
'extra:Enable extra, possibly unwanted, messages'
|
||||||
|
'everything:Enable literally everything'
|
||||||
|
|
||||||
|
'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'
|
||||||
|
'charmap-redef:Warn when redefining a charmap mapping'
|
||||||
|
'div:Warn when dividing the smallest int by -1'
|
||||||
|
'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'
|
||||||
|
'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'
|
||||||
|
'shift:Warn when shifting negative values'
|
||||||
|
'shift-amount:Warn when a shift'\''s operand it negative or \> 32'
|
||||||
|
'truncation:Warn when implicit truncation loses bits'
|
||||||
|
'user:Warn when executing the WARN built-in'
|
||||||
|
)
|
||||||
|
# TODO: handle `no-` and `error=` somehow?
|
||||||
|
# TODO: handle `=0|1|2` levels for `numeric-string` and `truncation`?
|
||||||
|
_describe warning warnings
|
||||||
|
}
|
||||||
|
|
||||||
|
local args=(
|
||||||
|
# Arguments are listed here in the same order as in the manual, except for the version
|
||||||
|
'(- : * options)'{-V,--version}'[Print version number]'
|
||||||
|
|
||||||
|
'(-E --export-all)'{-E,--export-all}'[Export all symbols]'
|
||||||
|
'(-h --halt-without-nop)'{-h,--halt-without-nop}'[Avoid outputting a `nop` after `halt`]'
|
||||||
|
'(-L ---preserve-ld)'{-L,--preserve-ld}'[Prevent auto-optimizing `ld` into `ldh`]'
|
||||||
|
'(-v --verbose)'{-v,--verbose}'[Print additional messages regarding progression]'
|
||||||
|
-w'[Disable all warnings]'
|
||||||
|
|
||||||
|
'(-b --binary-digits)'{-b,--binary-digits}'+[Change chars for binary constants]:digit spec:'
|
||||||
|
'*'{-D,--define}'+[Define a string symbol]:name + value (default 1):'
|
||||||
|
'(-g --gfx-chars)'{-g,--gfx-chars}'+[Change chars for gfx constants]:chars spec:'
|
||||||
|
'(-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}'"
|
||||||
|
-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'
|
||||||
|
'(-p --pad-value)'{-p,--pad-value}'+[Set padding byte]:padding byte:'
|
||||||
|
'(-r --recursion-depth)'{-r,--recursion-depth}'+[Set maximum recursion depth]:depth:'
|
||||||
|
'(-W --warning)'{-W,--warning}'+[Toggle warning flags]:warning flag:_rgbasm_warnings'
|
||||||
|
|
||||||
|
":assembly sources:_files -g '*.asm'"
|
||||||
|
)
|
||||||
|
_arguments -s -S : $args
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
#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=(
|
||||||
|
# Arguments are listed here in the same order as in the manual, except for the version
|
||||||
|
'(- : * options)'{-V,--version}'[Print version number]'
|
||||||
|
|
||||||
|
'(-C --color-only -c --color-compatible)'{-C,--color-only}'[Mark ROM as GBC-only]'
|
||||||
|
'(-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]'
|
||||||
|
'(-O --overwrite)'{-O,--overwrite}'[Allow overwriting non-zero bytes]'
|
||||||
|
'(-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)'{-f,--fix-spec}'+[Fix or trash some header values]:fix spec:'
|
||||||
|
'(-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:'
|
||||||
|
'(-l --old-licensee)'{-l,--old-licensee}'+[Set old licensee ID]:licensee number:'
|
||||||
|
'(-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:'
|
||||||
|
'(-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:'
|
||||||
|
'(-t --title)'{-t,--title}'+[Set title string]:11-char title string:'
|
||||||
|
|
||||||
|
'*'":ROM files:_files -g '*.{gb,sgb,gbc}'"
|
||||||
|
)
|
||||||
|
_arguments -s -S : $args
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
#compdef rgbgfx
|
||||||
|
|
||||||
|
_depths() {
|
||||||
|
local depths=(
|
||||||
|
'1:1bpp'
|
||||||
|
'2:2bpp (native)'
|
||||||
|
)
|
||||||
|
|
||||||
|
_describe 'bit depth' depths
|
||||||
|
}
|
||||||
|
|
||||||
|
local args=(
|
||||||
|
# Arguments are listed here in the same order as in the manual, except for the version
|
||||||
|
'(- : * options)'{-V,--version}'[Print version number]'
|
||||||
|
|
||||||
|
'(-a --attr-map -A --output-attr-map)'{-A,--output-attr-map}'[Shortcut for -a <file>.attrmap]'
|
||||||
|
'(-C --color-curve)'{-C,--color-curve}'[Generate palettes using GBC color curve]'
|
||||||
|
'(-D --debug)'{-D,--debug}'[Enable debug features]'
|
||||||
|
'(-f --fix -F --fix-and-save)'{-f,--fix}'[Fix input PNG into an indexed image]'
|
||||||
|
'(-f --fix -F --fix-and-save)'{-F,--fix-and-save}'[Like -f but also save CLI params within the PNG]'
|
||||||
|
'(-h --horizontal)'{-h,--horizontal}'[Lay out tiles horizontally instead of vertically]'
|
||||||
|
'(-m --mirror-tiles)'{-m,--mirror-tiles}'[Eliminate mirrored tiles from output]'
|
||||||
|
'(-p --palette -P --output-palette)'{-P,--output-palette}'[Shortcut for -p <file>.pal]'
|
||||||
|
'(-t --tilemap -T --output-tilemap)'{-T,--output-tilemap}'[Shortcut for -t <file>.tilemap]'
|
||||||
|
'(-u --unique-tiles)'{-u,--unique-tiles}'[Eliminate redundant tiles]'
|
||||||
|
'(-v --verbose)'{-v,--verbose}'[Enable verbose output]'
|
||||||
|
|
||||||
|
'(-a --attr-map -A --output-attr-map)'{-a,--attr-map}'+[Generate a map of tile attributes (mirroring)]:attrmap file:_files'
|
||||||
|
'(-d --depth)'{-d,--depth}'+[Set bit depth]:bit depth:_depths'
|
||||||
|
'(-o --output)'{-o,--output}'+[Set output file]:output file:_files'
|
||||||
|
'(-p --palette -P --output-palette)'{-p,--palette}"+[Output the image's palette in little-endian native RGB555 format]:palette 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:'
|
||||||
|
|
||||||
|
":input png file:_files -g '*.png'"
|
||||||
|
)
|
||||||
|
_arguments -s -S : $args
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
#compdef rgblink
|
||||||
|
|
||||||
|
local args=(
|
||||||
|
# Arguments are listed here in the same order as in the manual, except for the version
|
||||||
|
'(- : * options)'{-V,--version}'[Print version number]'
|
||||||
|
|
||||||
|
'(-d --dmg)'{-d,--dmg}'[Enable DMG mode (-w + no VRAM banking)]'
|
||||||
|
'(-t --tiny)'{-t,--tiny}'[Enable tiny mode, disabling ROM banking]'
|
||||||
|
'(-v --verbose)'{-v,--verbose}'[Enable verbose output]'
|
||||||
|
'(-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'"
|
||||||
|
'(-m --map)'{-m,--map}"+[Produce a map file]:map file:_files -g '*.map'"
|
||||||
|
'(-n --sym)'(-n,--sym)"+[Produce a symbol file]:sym file:_files -g '*.sym'"
|
||||||
|
'(-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}'"
|
||||||
|
'(-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:'
|
||||||
|
|
||||||
|
'*'":object files:_files -g '*.o'"
|
||||||
|
)
|
||||||
|
_arguments -s -S : $args
|
||||||
@@ -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 */
|
|
||||||
+3
-1
@@ -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,12 +25,13 @@ 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);
|
||||||
void macro_SetUniqueID(uint32_t id);
|
void macro_SetUniqueID(uint32_t id);
|
||||||
uint32_t macro_UseNewUniqueID(void);
|
uint32_t macro_UseNewUniqueID(void);
|
||||||
void macro_ShiftCurrentArgs(void);
|
void macro_ShiftCurrentArgs(int32_t count);
|
||||||
uint32_t macro_NbArgs(void);
|
uint32_t macro_NbArgs(void);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
+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 `asmy.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 *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
|
||||||
|
|||||||
+30
-16
@@ -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,19 +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);
|
||||||
struct Symbol *sym_FindSymbol(char const *symName);
|
/*
|
||||||
|
* Find a symbol by exact name, bypassing expansion checks
|
||||||
|
*/
|
||||||
|
struct Symbol *sym_FindExactSymbol(char const *symName);
|
||||||
|
/*
|
||||||
|
* Find a symbol by exact name; may not be scoped, produces an error if it is
|
||||||
|
*/
|
||||||
|
struct Symbol *sym_FindUnscopedSymbol(char const *symName);
|
||||||
|
/*
|
||||||
|
* Find a symbol, possibly scoped, by name
|
||||||
|
*/
|
||||||
|
struct Symbol *sym_FindScopedSymbol(char const *symName);
|
||||||
|
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);
|
||||||
uint32_t sym_GetDefinedValue(char const *s);
|
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
-22
@@ -13,39 +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_OBSOLETE, /* Obsolete things */
|
WARNING_LARGE_CONSTANT, // Constants too large
|
||||||
WARNING_SHIFT, /* Shifting undefined behavior */
|
WARNING_LONG_STR, // String too long for internal buffers
|
||||||
WARNING_SHIFT_AMOUNT, /* Strange shift amount */
|
WARNING_MACRO_SHIFT, // Shift past available arguments in macro
|
||||||
WARNING_TRUNCATION, /* Implicit truncation loses some bits */
|
WARNING_NESTED_COMMENT, // Comment-start delimiter in a block comment
|
||||||
WARNING_USER, /* User warnings */
|
WARNING_OBSOLETE, // Obsolete things
|
||||||
|
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
|
||||||
@@ -54,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
|
||||||
@@ -62,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
|
||||||
|
|||||||
Vendored
+1
-1
@@ -9,6 +9,6 @@
|
|||||||
#ifndef EXTERN_UTF8DECODER_H
|
#ifndef EXTERN_UTF8DECODER_H
|
||||||
#define EXTERN_UTF8DECODER_H
|
#define EXTERN_UTF8DECODER_H
|
||||||
|
|
||||||
uint32_t decode(uint32_t *state, uint32_t *codep, uint32_t byte);
|
uint32_t decode(uint32_t *state, uint32_t *codep, uint8_t byte);
|
||||||
|
|
||||||
#endif /* EXTERN_UTF8DECODER_H */
|
#endif /* EXTERN_UTF8DECODER_H */
|
||||||
|
|||||||
+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
-2
@@ -29,9 +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);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes an element from a hashmap.
|
* Removes an element from a hashmap.
|
||||||
@@ -41,6 +41,14 @@ bool hash_AddElement(HashMap 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
|
||||||
|
|||||||
+79
-13
@@ -9,22 +9,88 @@
|
|||||||
#ifndef HELPERS_H
|
#ifndef HELPERS_H
|
||||||
#define HELPERS_H
|
#define HELPERS_H
|
||||||
|
|
||||||
#ifdef __GNUC__
|
// Of course, MSVC does not support C11, so no _Noreturn there...
|
||||||
/* GCC or compatible */
|
#ifdef _MSC_VER
|
||||||
#define format_(archetype, str_index, first_arg) \
|
#define _Noreturn __declspec(noreturn)
|
||||||
__attribute__ ((format (archetype, str_index, first_arg)))
|
|
||||||
#define noreturn_ __attribute__ ((noreturn))
|
|
||||||
#define trap_ __builtin_trap()
|
|
||||||
#else
|
|
||||||
/* Unsupported, but no need to throw a fit */
|
|
||||||
#define format_(archetype, str_index, first_arg)
|
|
||||||
#define noreturn_
|
|
||||||
#define unused_
|
|
||||||
#define trap_
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Macros for stringification */
|
// Ideally, we'd use `__has_attribute` and `__has_builtin`, but these were only introduced in GCC 9
|
||||||
|
#ifdef __GNUC__ // GCC or compatible
|
||||||
|
#define 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
|
||||||
|
#ifdef NDEBUG
|
||||||
|
#define unreachable_ __builtin_unreachable
|
||||||
|
#else
|
||||||
|
#define unreachable_ __builtin_trap
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
// Unsupported, but no need to throw a fit
|
||||||
|
#define format_(archetype, str_index, first_arg)
|
||||||
|
#define attr_(...)
|
||||||
|
// This seems to generate similar code to __builtin_unreachable, despite different semantics
|
||||||
|
// Note that executing this is undefined behavior (declared _Noreturn, but does return)
|
||||||
|
static inline _Noreturn unreachable_(void) {}
|
||||||
|
#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
|
||||||
#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 */
|
||||||
|
|||||||
+4
-1
@@ -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;
|
||||||
@@ -66,7 +69,7 @@ void warning(struct FileStackNode const *where, uint32_t lineNo,
|
|||||||
void error(struct FileStackNode const *where, uint32_t lineNo,
|
void error(struct FileStackNode const *where, uint32_t lineNo,
|
||||||
char const *fmt, ...) format_(printf, 3, 4);
|
char const *fmt, ...) format_(printf, 3, 4);
|
||||||
|
|
||||||
noreturn_ void fatal(struct FileStackNode const *where, uint32_t lineNo,
|
_Noreturn void fatal(struct FileStackNode const *where, uint32_t lineNo,
|
||||||
char const *fmt, ...) format_(printf, 3, 4);
|
char const *fmt, ...) format_(printf, 3, 4);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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 (1)
|
#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
|
||||||
+30
-10
@@ -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()
|
||||||
|
|
||||||
BISON_TARGET(ASMy "asm/asmy.y"
|
find_package(BISON REQUIRED)
|
||||||
"${PROJECT_SOURCE_DIR}/src/asm/asmy.c"
|
set(BISON_FLAGS "-Wall")
|
||||||
DEFINES_FILE "${PROJECT_SOURCE_DIR}/src/asm/asmy.h"
|
# 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"
|
||||||
|
"${PROJECT_SOURCE_DIR}/src/asm/parser.c"
|
||||||
|
COMPILE_FLAGS "${BISON_FLAGS}"
|
||||||
|
DEFINES_FILE "${PROJECT_SOURCE_DIR}/src/asm/parser.h"
|
||||||
)
|
)
|
||||||
|
|
||||||
set(rgbasm_src
|
set(rgbasm_src
|
||||||
"${BISON_ASMy_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")
|
||||||
|
|||||||
+2
-2
@@ -1,2 +1,2 @@
|
|||||||
asmy.c
|
/parser.c
|
||||||
asmy.h
|
/parser.h
|
||||||
|
|||||||
-1631
File diff suppressed because it is too large
Load Diff
+73
-51
@@ -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,36 +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);
|
||||||
|
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.
|
||||||
@@ -196,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';
|
||||||
|
}
|
||||||
+143
-48
@@ -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,9 +345,7 @@ 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_FindSymbol(macroName);
|
|
||||||
|
|
||||||
if (!macro) {
|
if (!macro) {
|
||||||
error("Macro \"%s\" not defined\n", macroName);
|
error("Macro \"%s\" not defined\n", macroName);
|
||||||
@@ -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);
|
||||||
|
|||||||
+1408
-925
File diff suppressed because it is too large
Load Diff
+59
-9
@@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -128,10 +166,22 @@ uint32_t macro_UseNewUniqueID(void)
|
|||||||
return maxUniqueID;
|
return maxUniqueID;
|
||||||
}
|
}
|
||||||
|
|
||||||
void macro_ShiftCurrentArgs(void)
|
void macro_ShiftCurrentArgs(int32_t count)
|
||||||
{
|
{
|
||||||
if (macroArgs->shift != macroArgs->nbArgs)
|
if (!macroArgs) {
|
||||||
macroArgs->shift++;
|
error("Cannot shift macro arguments outside of a macro\n");
|
||||||
|
} else if (count > 0 && ((uint32_t)count > macroArgs->nbArgs
|
||||||
|
|| macroArgs->shift > macroArgs->nbArgs - count)) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t macro_NbArgs(void)
|
uint32_t macro_NbArgs(void)
|
||||||
|
|||||||
+144
-315
@@ -19,204 +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 "asmy.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"
|
||||||
|
|
||||||
size_t cldefines_index;
|
#ifdef __clang__
|
||||||
size_t cldefines_numindices;
|
#if __has_feature(address_sanitizer) && !defined(__SANITIZE_ADDRESS__)
|
||||||
size_t cldefines_bufsize;
|
#define __SANITIZE_ADDRESS__
|
||||||
const size_t cldefine_entrysize = 2 * sizeof(void *);
|
#endif /* __has_feature(address_sanitizer) && !defined(__SANITIZE_ADDRESS__) */
|
||||||
char **cldefines;
|
#endif /* __clang__ */
|
||||||
|
|
||||||
clock_t nStartClock, nEndClock;
|
#ifdef __SANITIZE_ADDRESS__
|
||||||
uint32_t nTotalLines, nIFDepth;
|
// There are known, non-trivial to fix leaks. We would still like to have `make develop'
|
||||||
|
// detect memory corruption, though.
|
||||||
#if defined(YYDEBUG) && YYDEBUG
|
char const *__asan_default_options(void) { return "detect_leaks=0"; }
|
||||||
extern int yydebug;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// 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...
|
||||||
|
int yyparse(void);
|
||||||
|
|
||||||
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 */
|
||||||
@@ -230,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` */
|
||||||
@@ -272,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"
|
||||||
@@ -288,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;
|
||||||
@@ -413,40 +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);
|
||||||
*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;
|
||||||
@@ -457,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);
|
||||||
|
}
|
||||||
+174
-113
@@ -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++]
|
||||||
@@ -296,6 +307,11 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
|
|||||||
uint8_t rpndata = popbyte();
|
uint8_t rpndata = popbyte();
|
||||||
|
|
||||||
switch (rpndata) {
|
switch (rpndata) {
|
||||||
|
struct Symbol *sym;
|
||||||
|
uint32_t value;
|
||||||
|
uint8_t b;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
case RPN_CONST:
|
case RPN_CONST:
|
||||||
writebyte(RPN_CONST);
|
writebyte(RPN_CONST);
|
||||||
writebyte(popbyte());
|
writebyte(popbyte());
|
||||||
@@ -303,32 +319,38 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
|
|||||||
writebyte(popbyte());
|
writebyte(popbyte());
|
||||||
writebyte(popbyte());
|
writebyte(popbyte());
|
||||||
break;
|
break;
|
||||||
case RPN_SYM:
|
|
||||||
{
|
|
||||||
for (unsigned int i = -1; (tzSym[++i] = popbyte()); )
|
|
||||||
;
|
|
||||||
struct Symbol *sym = sym_FindSymbol(tzSym);
|
|
||||||
uint32_t value;
|
|
||||||
|
|
||||||
|
case RPN_SYM:
|
||||||
|
i = 0;
|
||||||
|
do {
|
||||||
|
symName[i] = popbyte();
|
||||||
|
} while (symName[i++]);
|
||||||
|
|
||||||
|
// The symbol name is always written expanded
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
writebyte(value & 0xFF);
|
writebyte(value & 0xFF);
|
||||||
writebyte(value >> 8);
|
writebyte(value >> 8);
|
||||||
writebyte(value >> 16);
|
writebyte(value >> 16);
|
||||||
writebyte(value >> 24);
|
writebyte(value >> 24);
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case RPN_BANK_SYM:
|
case RPN_BANK_SYM:
|
||||||
{
|
i = 0;
|
||||||
for (unsigned int i = -1; (tzSym[++i] = popbyte()); )
|
do {
|
||||||
;
|
symName[i] = popbyte();
|
||||||
struct Symbol *sym = sym_FindSymbol(tzSym);
|
} while (symName[i++]);
|
||||||
uint32_t value = getSymbolID(sym);
|
|
||||||
|
// The symbol name is always written expanded
|
||||||
|
sym = sym_FindExactSymbol(symName);
|
||||||
|
value = getSymbolID(sym);
|
||||||
|
|
||||||
writebyte(RPN_BANK_SYM);
|
writebyte(RPN_BANK_SYM);
|
||||||
writebyte(value & 0xFF);
|
writebyte(value & 0xFF);
|
||||||
@@ -336,18 +358,31 @@ static void writerpn(uint8_t *rpnexpr, uint32_t *rpnptr, uint8_t *rpn,
|
|||||||
writebyte(value >> 16);
|
writebyte(value >> 16);
|
||||||
writebyte(value >> 24);
|
writebyte(value >> 24);
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
case RPN_BANK_SECT:
|
|
||||||
{
|
|
||||||
uint8_t b;
|
|
||||||
|
|
||||||
|
case RPN_BANK_SECT:
|
||||||
writebyte(RPN_BANK_SECT);
|
writebyte(RPN_BANK_SECT);
|
||||||
do {
|
do {
|
||||||
b = popbyte();
|
b = popbyte();
|
||||||
writebyte(b);
|
writebyte(b);
|
||||||
} 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;
|
||||||
@@ -364,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;
|
||||||
}
|
}
|
||||||
@@ -403,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -438,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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -471,21 +520,25 @@ static void registerExportedSymbol(struct Symbol *symbol, void *arg)
|
|||||||
*/
|
*/
|
||||||
void out_WriteObject(void)
|
void out_WriteObject(void)
|
||||||
{
|
{
|
||||||
FILE *f = fopen(tzObjectname, "wb");
|
FILE *f;
|
||||||
|
if (strcmp(objectName, "-") != 0)
|
||||||
|
f = fopen(objectName, "wb");
|
||||||
|
else
|
||||||
|
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)
|
||||||
@@ -497,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);
|
||||||
}
|
}
|
||||||
@@ -513,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);
|
||||||
}
|
}
|
||||||
|
|||||||
+2267
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
|
||||||
|
|||||||
+911
-398
File diff suppressed because it is too large
Load Diff
+283
-182
@@ -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,45 +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 */
|
||||||
else if (expr->nRPNCapacity >= MAXRPNLEN)
|
while (expr->rpnCapacity - expr->rpnLength < size) {
|
||||||
/*
|
if (expr->rpnCapacity >= MAXRPNLEN)
|
||||||
* To avoid generating humongous object files, cap the
|
/*
|
||||||
* size of RPN expressions
|
* To avoid generating humongous object files, cap the
|
||||||
*/
|
* size of RPN expressions
|
||||||
fatalerror("RPN expression cannot grow larger than "
|
*/
|
||||||
EXPAND_AND_STR(MAXRPNLEN) " bytes\n");
|
fatalerror("RPN expression cannot grow larger than "
|
||||||
else if (expr->nRPNCapacity > MAXRPNLEN / 2)
|
EXPAND_AND_STR(MAXRPNLEN) " bytes\n");
|
||||||
expr->nRPNCapacity = MAXRPNLEN;
|
else if (expr->rpnCapacity > MAXRPNLEN / 2)
|
||||||
else
|
expr->rpnCapacity = MAXRPNLEN;
|
||||||
expr->nRPNCapacity *= 2;
|
else
|
||||||
expr->tRPN = realloc(expr->tRPN, expr->nRPNCapacity);
|
expr->rpnCapacity *= 2;
|
||||||
|
}
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -88,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);
|
||||||
}
|
}
|
||||||
@@ -99,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 *tzSym)
|
void rpn_Symbol(struct Expression *expr, char const *symName)
|
||||||
{
|
{
|
||||||
struct Symbol *sym = sym_FindSymbol(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");
|
||||||
@@ -113,27 +119,17 @@ void rpn_Symbol(struct Expression *expr, char *tzSym)
|
|||||||
rpn_Init(expr);
|
rpn_Init(expr);
|
||||||
expr->isSymbol = true;
|
expr->isSymbol = true;
|
||||||
|
|
||||||
sym_Ref(tzSym);
|
|
||||||
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);
|
||||||
expr->nRPNPatchSize += 5; /* 1-byte opcode + 4-byte symbol ID */
|
sym = sym_Ref(symName);
|
||||||
|
expr->rpnPatchSize += 5; /* 1-byte opcode + 4-byte symbol ID */
|
||||||
|
|
||||||
size_t nameLen = strlen(tzSym) + 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, tzSym, 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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -141,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_FindSymbol(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)) {
|
||||||
@@ -167,44 +163,81 @@ 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_Ref(tzSym);
|
sym = sym_Ref(symName);
|
||||||
if (!sym)
|
assert(sym); // If the symbol didn't exist, it should have been created
|
||||||
/* If the symbol didn't exist, `sym_Ref` created it */
|
|
||||||
sym = sym_FindSymbol(tzSym);
|
|
||||||
|
|
||||||
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(tzSym) + 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);
|
||||||
*ptr++ = RPN_BANK_SYM;
|
*ptr++ = RPN_BANK_SYM;
|
||||||
memcpy(ptr, tzSym, nameLen);
|
memcpy(ptr, sym->name, nameLen);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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_FindSymbol((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)
|
||||||
@@ -307,7 +328,9 @@ bool rpn_IsDiffConstant(struct Expression const *src, struct Symbol const *sym)
|
|||||||
if (!sym1 || !sym || sym1->type != SYM_LABEL || sym->type != SYM_LABEL)
|
if (!sym1 || !sym || sym1->type != SYM_LABEL || sym->type != SYM_LABEL)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return sym_GetSection(sym1) == sym_GetSection(sym);
|
struct Section const *section1 = sym_GetSection(sym1);
|
||||||
|
struct Section const *section2 = sym_GetSection(sym);
|
||||||
|
return section1 && (section1 == section2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool isDiffConstant(struct Expression const *src1,
|
static bool isDiffConstant(struct Expression const *src1,
|
||||||
@@ -316,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;
|
||||||
@@ -327,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:
|
||||||
@@ -420,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:
|
||||||
@@ -431,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));
|
||||||
|
|
||||||
@@ -453,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) {
|
||||||
@@ -481,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -492,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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -507,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));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -519,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;
|
||||||
}
|
}
|
||||||
@@ -530,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -543,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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+545
-331
File diff suppressed because it is too large
Load Diff
+312
-192
@@ -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,59 @@ 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
static void assignStringSymbol(struct Symbol *sym, char const *value)
|
||||||
* Find a symbol by name and scope
|
|
||||||
*/
|
|
||||||
static struct Symbol *findsymbol(char const *s, char const *scope)
|
|
||||||
{
|
{
|
||||||
char fullname[MAXSYMLEN + 1];
|
char *string = strdup(value);
|
||||||
|
|
||||||
if (s[0] == '.' && scope) {
|
if (!string)
|
||||||
fullSymbolName(fullname, sizeof(fullname), s, scope);
|
fatalerror("No memory for string equate: %s\n", strerror(errno));
|
||||||
s = fullname;
|
|
||||||
|
sym->type = SYM_EQUS;
|
||||||
|
sym->macro = string;
|
||||||
|
sym->macroSize = strlen(string);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Symbol *sym_FindExactSymbol(char const *symName)
|
||||||
|
{
|
||||||
|
return hash_GetElement(symbols, symName);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Symbol *sym_FindUnscopedSymbol(char const *symName)
|
||||||
|
{
|
||||||
|
if (strchr(symName, '.')) {
|
||||||
|
error("Expected non-scoped symbol name, not \"%s\"\n", symName);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
return sym_FindExactSymbol(symName);
|
||||||
char const *separator = strchr(s, '.');
|
|
||||||
|
|
||||||
if (separator && strchr(separator + 1, '.'))
|
|
||||||
fatalerror("'%s' is a nonsensical reference to a nested local symbol\n", s);
|
|
||||||
|
|
||||||
return hash_GetElement(symbols, s);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
struct Symbol *sym_FindScopedSymbol(char const *symName)
|
||||||
* Find a symbol by name, with automatically determined scope
|
|
||||||
*/
|
|
||||||
struct Symbol *sym_FindSymbol(char const *symName)
|
|
||||||
{
|
{
|
||||||
return findsymbol(symName, symName[0] == '.' ? labelScope : NULL);
|
char const *localName = strchr(symName, '.');
|
||||||
|
|
||||||
|
if (localName) {
|
||||||
|
if (strchr(localName + 1, '.'))
|
||||||
|
fatalerror("'%s' is a nonsensical reference to a nested local symbol\n",
|
||||||
|
symName);
|
||||||
|
/* If auto-scoped local label, expand the name */
|
||||||
|
if (localName == symName) { /* Meaning, the name begins with the dot */
|
||||||
|
char fullName[MAXSYMLEN + 1];
|
||||||
|
|
||||||
|
fullSymbolName(fullName, sizeof(fullName), symName, labelScope);
|
||||||
|
return sym_FindExactSymbol(fullName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sym_FindExactSymbol(symName);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline bool isReferenced(struct Symbol const *sym)
|
struct Symbol const *sym_GetPC(void)
|
||||||
{
|
{
|
||||||
return sym->ID != -1;
|
return PCSymbol;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isReferenced(struct Symbol const *sym)
|
||||||
|
{
|
||||||
|
return sym->ID != (uint32_t)-1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -260,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_FindSymbol(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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,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();
|
||||||
@@ -310,35 +330,18 @@ 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_FindSymbol(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);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Return a defined symbols value... aborts if not defined yet
|
|
||||||
*/
|
|
||||||
uint32_t sym_GetDefinedValue(char const *s)
|
|
||||||
{
|
|
||||||
struct Symbol const *sym = sym_FindSymbol(s);
|
|
||||||
|
|
||||||
if (sym == NULL || !sym_IsDefined(sym))
|
|
||||||
error("'%s' not defined\n", s);
|
|
||||||
else if (!sym_IsNumeric(sym))
|
|
||||||
error("'%s' is a macro or string symbol\n", s);
|
|
||||||
else
|
|
||||||
return sym_GetValue(sym);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
char const *sym_GetCurrentSymbolScope(void)
|
char const *sym_GetCurrentSymbolScope(void)
|
||||||
{
|
{
|
||||||
return labelScope;
|
return labelScope;
|
||||||
@@ -353,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 = findsymbol(symbolName, NULL);
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -374,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;
|
||||||
|
|
||||||
@@ -396,42 +435,65 @@ 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 = findsymbol(symName, NULL);
|
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);
|
||||||
putc('\n', stderr);
|
putc('\n', stderr);
|
||||||
|
return sym;
|
||||||
} else {
|
} else {
|
||||||
/* TODO: can the scope be incorrect when taking over refs? */
|
|
||||||
updateSymbolFilename(sym);
|
updateSymbolFilename(sym);
|
||||||
}
|
}
|
||||||
|
|
||||||
sym->type = SYM_SET;
|
sym->type = SYM_VAR;
|
||||||
sym->value = value;
|
sym->value = value;
|
||||||
|
|
||||||
return sym;
|
return sym;
|
||||||
@@ -439,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 = findsymbol(name, NULL); /* Due to this, don't look for expansions */
|
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;
|
||||||
@@ -465,69 +527,126 @@ 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)
|
||||||
{
|
{
|
||||||
struct Symbol *sym = sym_FindSymbol(symName);
|
if (symName[0] == '!') {
|
||||||
|
error("Anonymous labels cannot be exported\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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 */
|
||||||
if (!sym)
|
if (!sym)
|
||||||
@@ -540,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;
|
||||||
@@ -561,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_FindSymbol(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] == '.') {
|
||||||
@@ -573,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -588,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");
|
||||||
@@ -629,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");
|
||||||
@@ -643,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);
|
||||||
@@ -652,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;
|
||||||
}
|
}
|
||||||
|
|||||||
+22
-26
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,10 +64,11 @@ size_t readUTF8Char(uint8_t *dest, char const *src)
|
|||||||
size_t i = 0;
|
size_t i = 0;
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
if (decode(&state, &codep, (uint8_t)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)
|
||||||
|
|||||||
+167
-44
@@ -6,46 +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_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)
|
||||||
{
|
{
|
||||||
@@ -66,59 +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",
|
||||||
"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_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_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
|
||||||
};
|
};
|
||||||
@@ -129,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':
|
||||||
@@ -168,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;
|
||||||
@@ -180,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;
|
||||||
@@ -195,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);
|
||||||
@@ -208,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;
|
||||||
|
|
||||||
@@ -241,12 +364,12 @@ 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;
|
||||||
|
|
||||||
case WARNING_DEFAULT:
|
case WARNING_DEFAULT:
|
||||||
trap_;
|
unreachable_();
|
||||||
/* Not reached */
|
/* Not reached */
|
||||||
|
|
||||||
case WARNING_ENABLED:
|
case WARNING_ENABLED:
|
||||||
|
|||||||
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);
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+1
-1
@@ -38,7 +38,7 @@ static const uint8_t utf8d[] = {
|
|||||||
1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* s8 */
|
1, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* s8 */
|
||||||
};
|
};
|
||||||
|
|
||||||
uint32_t decode(uint32_t *state, uint32_t *codep, uint32_t byte)
|
uint32_t decode(uint32_t *state, uint32_t *codep, uint8_t byte)
|
||||||
{
|
{
|
||||||
uint32_t type = utf8d[byte];
|
uint32_t type = utf8d[byte];
|
||||||
|
|
||||||
|
|||||||
+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
-359
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) {
|
||||||
|
|||||||
+7
-6
@@ -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
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
The
|
The
|
||||||
.Nm
|
.Nm
|
||||||
program converts PNG images into the Nintendo Game Boy's planar tile format.
|
program converts PNG images into the Nintendo Game Boy's planar tile format.
|
||||||
|
.Pp
|
||||||
The resulting colors and their palette indices are determined differently depending on the input PNG file:
|
The resulting colors and their palette indices are determined differently depending on the input PNG file:
|
||||||
.Bl -dash -width Ds
|
.Bl -dash -width Ds
|
||||||
.It
|
.It
|
||||||
@@ -39,7 +39,7 @@ If the image has multiple shades that map to the same index, the palette is inst
|
|||||||
.It
|
.It
|
||||||
If the image has color (or the grayscale method failed), the colors are sorted from lightest to darkest.
|
If the image has color (or the grayscale method failed), the colors are sorted from lightest to darkest.
|
||||||
.El
|
.El
|
||||||
|
.Pp
|
||||||
The input image may not contain more colors than the selected bit depth allows.
|
The input image may not contain more colors than the selected bit depth allows.
|
||||||
Transparent pixels are set to palette index 0.
|
Transparent pixels are set to palette index 0.
|
||||||
.Sh ARGUMENTS
|
.Sh ARGUMENTS
|
||||||
@@ -53,7 +53,7 @@ is invalid because it could also be
|
|||||||
.Fl Fl version .
|
.Fl Fl version .
|
||||||
The arguments are as follows:
|
The arguments are as follows:
|
||||||
.Bl -tag -width Ds
|
.Bl -tag -width Ds
|
||||||
.It Fl a Ar attrmap, Fl Fl attr-map Ar attrmap
|
.It Fl a Ar attrmap , Fl Fl attr-map Ar attrmap
|
||||||
Generate a file of tile mirroring attributes for OAM or (CGB-only) background tiles.
|
Generate a file of tile mirroring attributes for OAM or (CGB-only) background tiles.
|
||||||
For each tile in the input file, a byte is written representing the dimensions that the associated tile in the output file should be mirrored.
|
For each tile in the input file, a byte is written representing the dimensions that the associated tile in the output file should be mirrored.
|
||||||
Useful in combination with
|
Useful in combination with
|
||||||
@@ -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
-6
@@ -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,7 +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_RemoveElement(HashMap map, char const *key)
|
bool hash_RemoveElement(HashMap map, char const *key)
|
||||||
@@ -83,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];
|
||||||
@@ -91,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++) {
|
||||||
|
|||||||
+40
-15
@@ -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 */
|
||||||
@@ -439,7 +464,7 @@ void assign_AssignSections(void)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
trap_;
|
unreachable_();
|
||||||
}
|
}
|
||||||
|
|
||||||
void assign_Cleanup(void)
|
void assign_Cleanup(void)
|
||||||
|
|||||||
+194
-37
@@ -8,11 +8,13 @@
|
|||||||
|
|
||||||
#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>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
|
||||||
@@ -23,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 */
|
||||||
@@ -34,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 */
|
||||||
@@ -99,11 +107,25 @@ void error(struct FileStackNode const *where, uint32_t lineNo, char const *fmt,
|
|||||||
nbErrors++;
|
nbErrors++;
|
||||||
}
|
}
|
||||||
|
|
||||||
noreturn_ void fatal(struct FileStackNode const *where, uint32_t lineNo, char const *fmt, ...)
|
void argErr(char flag, char const *fmt, ...)
|
||||||
{
|
{
|
||||||
va_list ap;
|
va_list ap;
|
||||||
|
|
||||||
fputs("fatal: ", stderr);
|
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, ...)
|
||||||
|
{
|
||||||
|
va_list ap;
|
||||||
|
|
||||||
|
fputs("FATAL: ", stderr);
|
||||||
if (where) {
|
if (where) {
|
||||||
dumpFileStack(where);
|
dumpFileStack(where);
|
||||||
fprintf(stderr, "(%" PRIu32 "): ", lineNo);
|
fprintf(stderr, "(%" PRIu32 "): ", lineNo);
|
||||||
@@ -117,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,16 +148,22 @@ FILE *openFile(char const *fileName, char const *mode)
|
|||||||
if (!fileName)
|
if (!fileName)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
FILE *file = fopen(fileName, mode);
|
FILE *file;
|
||||||
|
if (strcmp(fileName, "-") != 0)
|
||||||
|
file = fopen(fileName, mode);
|
||||||
|
else if (mode[0] == 'r')
|
||||||
|
file = fdopen(0, mode);
|
||||||
|
else
|
||||||
|
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
|
||||||
@@ -148,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 }
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -171,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"
|
||||||
@@ -195,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 */
|
||||||
@@ -210,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':
|
||||||
@@ -264,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);
|
||||||
}
|
}
|
||||||
@@ -297,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();
|
||||||
|
|||||||
+84
-61
@@ -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,9 +500,9 @@ 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 = 0; 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);
|
||||||
|
|
||||||
/* This file's symbols, kept to link sections to them */
|
/* This file's symbols, kept to link sections to them */
|
||||||
@@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user