├── .gitignore
├── LICENSE
├── README.md
├── bundle
├── cgx-legacy.bat
├── cgx-legacy.sh
├── cgx-lite.bat
├── cgx-lite.sh
├── cgx.bat
├── cgx.sh
├── colors.csv
└── survivors
│ ├── bimp1
│ ├── bimp2
│ ├── shooterA1
│ ├── shooterA2
│ ├── shooterB
│ └── shooterC
├── pom.xml
└── src
└── main
└── java
└── il
└── co
└── codeguru
└── corewars8086
├── CoreWarsEngine.java
├── cli
├── HeadlessCompetitionRunner.java
└── Options.java
├── cpu
├── Cpu.java
├── CpuException.java
├── CpuState.java
├── DivisionException.java
├── IndirectAddressingDecoder.java
├── IntOpcodeException.java
├── InvalidOpcodeException.java
├── OpcodeFetcher.java
├── RegisterIndexingDecoder.java
├── UnimplementedOpcodeException.java
└── UnsupportedOpcodeException.java
├── gui
├── Canvas.java
├── ColorHolder.java
├── ColumnGraph.java
├── CompetitionWindow.java
├── CpuFrame.java
├── FlagFields.java
├── MemoryFrame.java
├── MouseAddressRequest.java
├── RegisterField.java
├── TeamColorHolder.java
└── WarFrame.java
├── memory
├── AbstractRealModeMemory.java
├── MemoryEventListener.java
├── MemoryException.java
├── RealModeAddress.java
├── RealModeMemory.java
├── RealModeMemoryImpl.java
├── RealModeMemoryRegion.java
└── RestrictedAccessRealModeMemory.java
├── utils
├── Disassembler.java
├── EventMulticaster.java
└── Unsigned.java
└── war
├── Competition.java
├── CompetitionEventListener.java
├── CompetitionIterator.java
├── ScoreEventListener.java
├── War.java
├── Warrior.java
├── WarriorData.java
├── WarriorFilenameFilter.java
├── WarriorGroup.java
├── WarriorRepository.java
└── WarriorType.java
/.gitignore:
--------------------------------------------------------------------------------
1 | target/
2 | *.iml
3 | .idea/**
4 | survivors/
5 | zombies/
6 | *.csv
7 | !bundle/**
8 | !bundle/*.csv
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
203 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | corewars8086
2 | ============
3 |
4 | Core Wars for standard 8086 assembly.
5 |
--------------------------------------------------------------------------------
/bundle/cgx-legacy.bat:
--------------------------------------------------------------------------------
1 | @java -jar bin\corewars8086-5.0.1.jar --noparallel --zombieSpeed 1
--------------------------------------------------------------------------------
/bundle/cgx-legacy.sh:
--------------------------------------------------------------------------------
1 | java -jar bin/corewars8086-5.0.1.jar --noparallel --zombieSpeed 2
--------------------------------------------------------------------------------
/bundle/cgx-lite.bat:
--------------------------------------------------------------------------------
1 | @java -jar bin\corewars8086-5.0.1.jar --noparallel
--------------------------------------------------------------------------------
/bundle/cgx-lite.sh:
--------------------------------------------------------------------------------
1 | java -jar bin/corewars8086-5.0.1.jar --noparallel
2 |
--------------------------------------------------------------------------------
/bundle/cgx.bat:
--------------------------------------------------------------------------------
1 | @java -jar bin\corewars8086-5.0.1.jar
--------------------------------------------------------------------------------
/bundle/cgx.sh:
--------------------------------------------------------------------------------
1 | java -jar bin/corewars8086-5.0.1.jar
2 |
--------------------------------------------------------------------------------
/bundle/colors.csv:
--------------------------------------------------------------------------------
1 | GSA,#00B000
--------------------------------------------------------------------------------
/bundle/survivors/bimp1:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codeguru-il/corewars8086/999071591366e6943780943a077b63b2b7ff6242/bundle/survivors/bimp1
--------------------------------------------------------------------------------
/bundle/survivors/bimp2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codeguru-il/corewars8086/999071591366e6943780943a077b63b2b7ff6242/bundle/survivors/bimp2
--------------------------------------------------------------------------------
/bundle/survivors/shooterA1:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codeguru-il/corewars8086/999071591366e6943780943a077b63b2b7ff6242/bundle/survivors/shooterA1
--------------------------------------------------------------------------------
/bundle/survivors/shooterA2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codeguru-il/corewars8086/999071591366e6943780943a077b63b2b7ff6242/bundle/survivors/shooterA2
--------------------------------------------------------------------------------
/bundle/survivors/shooterB:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codeguru-il/corewars8086/999071591366e6943780943a077b63b2b7ff6242/bundle/survivors/shooterB
--------------------------------------------------------------------------------
/bundle/survivors/shooterC:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/codeguru-il/corewars8086/999071591366e6943780943a077b63b2b7ff6242/bundle/survivors/shooterC
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
Due to a limitation with the Google Options library, the parameter fields
9 | * cannot be set as {@code final}, however please treat them so.
10 | *
11 | * @author RM
12 | */
13 | public class Options extends OptionsBase {
14 | @Option(
15 | name = "headless",
16 | abbrev = 'h',
17 | help = "Run the engine in headless mode",
18 | category = "Startup",
19 | defaultValue = "false"
20 | )
21 | public boolean headless;
22 |
23 | @Option(
24 | name = "comboSize",
25 | abbrev = 'c',
26 | help = "The size of each group combination",
27 | category = "Gameplay",
28 | defaultValue = "4"
29 | )
30 | public int combinationSize;
31 |
32 | @Option(
33 | name = "battlesPerCombo",
34 | abbrev = 'b',
35 | help = "Battles per group combination",
36 | category = "Gameplay",
37 | defaultValue = "100"
38 | )
39 | public int battlesPerCombo;
40 |
41 | @Option(
42 | name = "seed",
43 | abbrev = 's',
44 | help = "Starting seed for the game",
45 | category = "Gameplay",
46 | defaultValue = "guru"
47 | )
48 | public String seed;
49 |
50 | @Option(
51 | name = "zombieSpeed",
52 | help = "Number of turns zombies play per round",
53 | category = "Gameplay",
54 | defaultValue = "2"
55 | )
56 | public int zombieSpeed;
57 |
58 | @Option(
59 | name = "parallel",
60 | abbrev = 'p',
61 | help = "Run multiple battles concurrently - cancel for (pre-)cgx2022 result emulation",
62 | category = "Concurrency",
63 | defaultValue = "true"
64 | )
65 | public boolean parallel;
66 |
67 | @Option(
68 | name = "threads",
69 | abbrev = 't',
70 | help = "Number of threads for parallel mode",
71 | category = "Concurrency",
72 | defaultValue = "4"
73 | )
74 | public int threads;
75 |
76 | @Option(
77 | name = "warriorsDir",
78 | abbrev = 'w',
79 | help = "Directory for warrior files",
80 | category = "Data",
81 | defaultValue = "survivors"
82 | )
83 | public String warriorsDir;
84 |
85 | @Option(
86 | name = "zombiesDir",
87 | abbrev = 'z',
88 | help = "Directory for zombie files",
89 | category = "Data",
90 | defaultValue = "zombies"
91 | )
92 | public String zombiesDir;
93 |
94 | @Option(
95 | name = "outputFile",
96 | abbrev = 'o',
97 | help = "Path to scores output file",
98 | category = "Data",
99 | defaultValue = "scores.csv"
100 | )
101 | public String outputFile;
102 |
103 | @Option(
104 | name = "colorsFile",
105 | help = "Path to color holder file",
106 | category = "Data",
107 | defaultValue = "colors.csv"
108 | )
109 | public String colorsFile;
110 | }
111 |
--------------------------------------------------------------------------------
/src/main/java/il/co/codeguru/corewars8086/cpu/CpuException.java:
--------------------------------------------------------------------------------
1 | package il.co.codeguru.corewars8086.cpu;
2 |
3 | /**
4 | * Base class for all Exceptions thrown by the Cpu class.
5 | *
6 | * @author DL
7 | */
8 | public abstract class CpuException extends Exception {
9 | private static final long serialVersionUID = 1L;
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/il/co/codeguru/corewars8086/cpu/CpuState.java:
--------------------------------------------------------------------------------
1 | package il.co.codeguru.corewars8086.cpu;
2 |
3 | import il.co.codeguru.corewars8086.utils.Unsigned;
4 |
5 | /**
6 | * Wrapper class for CPU state (registers & flags).
7 | *
8 | * @author DL
9 | */
10 | public class CpuState {
11 |
12 | /** Accessors for the 16bit registers */
13 | public short getAX() {
14 | return m_ax;
15 | }
16 | public short getBX() {
17 | return m_bx;
18 | }
19 | public short getCX() {
20 | return m_cx;
21 | }
22 | public short getDX() {
23 | return m_dx;
24 | }
25 | public short getDS() {
26 | return m_ds;
27 | }
28 | public short getES() {
29 | return m_es;
30 | }
31 | public short getSI() {
32 | return m_si;
33 | }
34 | public short getDI() {
35 | return m_di;
36 | }
37 | public short getSS() {
38 | return m_ss;
39 | }
40 | public short getBP() {
41 | return m_bp;
42 | }
43 | public short getSP() {
44 | return m_sp;
45 | }
46 | public short getCS() {
47 | return m_cs;
48 | }
49 | public short getIP() {
50 | return m_ip;
51 | }
52 | public short getFlags() {
53 | return m_flags;
54 | }
55 |
56 | public void setAX(short value) {
57 | m_ax = value;
58 | }
59 | public void setBX(short value) {
60 | m_bx = value;
61 | }
62 | public void setCX(short value) {
63 | m_cx = value;
64 | }
65 | public void setDX(short value) {
66 | m_dx = value;
67 | }
68 | public void setDS(short value) {
69 | m_ds = value;
70 | }
71 | public void setES(short value) {
72 | m_es = value;
73 | }
74 | public void setSI(short value) {
75 | m_si = value;
76 | }
77 | public void setDI(short value) {
78 | m_di = value;
79 | }
80 | public void setSS(short value) {
81 | m_ss = value;
82 | }
83 | public void setBP(short value) {
84 | m_bp = value;
85 | }
86 | public void setSP(short value) {
87 | m_sp = value;
88 | }
89 | public void setCS(short value) {
90 | m_cs = value;
91 | }
92 | public void setIP(short value) {
93 | m_ip = value;
94 | }
95 | public void setFlags(short value) {
96 | m_flags = value;
97 | }
98 |
99 | /** Accessors for the 8bit registers */
100 | public byte getAL() {
101 | return (byte)m_ax;
102 | }
103 | public byte getBL() {
104 | return (byte)m_bx;
105 | }
106 | public byte getCL() {
107 | return (byte)m_cx;
108 | }
109 | public byte getDL() {
110 | return (byte)m_dx;
111 | }
112 | public byte getAH() {
113 | return (byte)(m_ax >> 8);
114 | }
115 | public byte getBH() {
116 | return (byte)(m_bx >> 8);
117 | }
118 | public byte getCH() {
119 | return (byte)(m_cx >> 8);
120 | }
121 | public byte getDH() {
122 | return (byte)(m_dx >> 8);
123 | }
124 |
125 | public void setAL(byte value) {
126 | m_ax &= 0xFF00;
127 | m_ax |= Unsigned.unsignedByte(value);
128 | }
129 | public void setBL(byte value) {
130 | m_bx &= 0xFF00;
131 | m_bx |= Unsigned.unsignedByte(value);
132 | }
133 | public void setCL(byte value) {
134 | m_cx &= 0xFF00;
135 | m_cx |= Unsigned.unsignedByte(value);
136 | }
137 | public void setDL(byte value) {
138 | m_dx &= 0xFF00;
139 | m_dx |= Unsigned.unsignedByte(value);
140 | }
141 | public void setAH(byte value) {
142 | m_ax &= 0x00FF;
143 | m_ax |= (Unsigned.unsignedByte(value) << 8);
144 | }
145 | public void setBH(byte value) {
146 | m_bx &= 0x00FF;
147 | m_bx |= (Unsigned.unsignedByte(value) << 8);
148 | }
149 | public void setCH(byte value) {
150 | m_cx &= 0x00FF;
151 | m_cx |= (Unsigned.unsignedByte(value) << 8);
152 | }
153 | public void setDH(byte value) {
154 | m_dx &= 0x00FF;
155 | m_dx |= (Unsigned.unsignedByte(value) << 8);
156 | }
157 |
158 | /** Accessors for the virtual Energy register. */
159 | public short getEnergy() {
160 | return m_energy;
161 | }
162 | public void setEnergy(short value) {
163 | m_energy = value;
164 | }
165 |
166 | /** Accessors for the virtual bomb count registers. */
167 | public byte getBomb1Count() {
168 | return m_bomb1count;
169 | }
170 | public void setBomb1Count(byte value) {
171 | m_bomb1count = value;
172 | }
173 | public byte getBomb2Count() {
174 | return m_bomb2count;
175 | }
176 | public void setBomb2Count(byte value) {
177 | m_bomb2count = value;
178 | }
179 |
180 | /**
181 | * 'get' accessor methods for the various fields of the flags register.
182 | * @return whether or not the requested flags field is set.
183 | */
184 | public boolean getCarryFlag() {
185 | return ((m_flags & FLAGS_MASK_CARRY) == FLAGS_MASK_CARRY);
186 | }
187 | public boolean getParityFlag() {
188 | return ((m_flags & FLAGS_MASK_PARITY) == FLAGS_MASK_PARITY);
189 | }
190 | public boolean getAuxFlag() {
191 | return ((m_flags & FLAGS_MASK_AUX) == FLAGS_MASK_AUX);
192 | }
193 | public boolean getZeroFlag() {
194 | return ((m_flags & FLAGS_MASK_ZERO) == FLAGS_MASK_ZERO);
195 | }
196 | public boolean getSignFlag() {
197 | return ((m_flags & FLAGS_MASK_SIGN) == FLAGS_MASK_SIGN);
198 | }
199 | public boolean getTrapFlag() {
200 | return ((m_flags & FLAGS_MASK_TRAP) == FLAGS_MASK_TRAP);
201 | }
202 | public boolean getInterruptFlag() {
203 | return ((m_flags & FLAGS_MASK_INTERRUPT) == FLAGS_MASK_INTERRUPT);
204 | }
205 | public boolean getDirectionFlag() {
206 | return ((m_flags & FLAGS_MASK_DIRECTION) == FLAGS_MASK_DIRECTION);
207 | }
208 | public boolean getOverflowFlag() {
209 | return ((m_flags & FLAGS_MASK_OVERFLOW) == FLAGS_MASK_OVERFLOW);
210 | }
211 |
212 | /**
213 | * 'set' accessor methods for the various fields of the flags register.
214 | * @param newValue whether or not the requested flags field should be set.
215 | */
216 | public void setCarryFlag(boolean newValue) {
217 | if (newValue) {
218 | m_flags |= FLAGS_MASK_CARRY;
219 | } else {
220 | m_flags &= (~FLAGS_MASK_CARRY);
221 | }
222 | }
223 | public void setParityFlag(boolean newValue) {
224 | if (newValue) {
225 | m_flags |= FLAGS_MASK_PARITY;
226 | } else {
227 | m_flags &= (~FLAGS_MASK_PARITY);
228 | }
229 | }
230 | public void setAuxFlag(boolean newValue) {
231 | if (newValue) {
232 | m_flags |= FLAGS_MASK_AUX;
233 | } else {
234 | m_flags &= (~FLAGS_MASK_AUX);
235 | }
236 | }
237 | public void setZeroFlag(boolean newValue) {
238 | if (newValue) {
239 | m_flags |= FLAGS_MASK_ZERO;
240 | } else {
241 | m_flags &= (~FLAGS_MASK_ZERO);
242 | }
243 | }
244 | public void setSignFlag(boolean newValue) {
245 | if (newValue) {
246 | m_flags |= FLAGS_MASK_SIGN;
247 | } else {
248 | m_flags &= (~FLAGS_MASK_SIGN);
249 | }
250 | }
251 | public void setTrapFlag(boolean newValue) {
252 | if (newValue) {
253 | m_flags |= FLAGS_MASK_TRAP;
254 | } else {
255 | m_flags &= (~FLAGS_MASK_TRAP);
256 | }
257 | }
258 | public void setInterruptFlag(boolean newValue) {
259 | if (newValue) {
260 | m_flags |= FLAGS_MASK_INTERRUPT;
261 | } else {
262 | m_flags &= (~FLAGS_MASK_INTERRUPT);
263 | }
264 | }
265 | public void setDirectionFlag(boolean newValue) {
266 | if (newValue) {
267 | m_flags |= FLAGS_MASK_DIRECTION;
268 | } else {
269 | m_flags &= (~FLAGS_MASK_DIRECTION);
270 | }
271 | }
272 | public void setOverflowFlag(boolean newValue) {
273 | if (newValue) {
274 | m_flags |= FLAGS_MASK_OVERFLOW;
275 | } else {
276 | m_flags &= (~FLAGS_MASK_OVERFLOW);
277 | }
278 | }
279 |
280 | /** CPU registers */
281 | private short m_ax;
282 | private short m_bx;
283 | private short m_cx;
284 | private short m_dx;
285 |
286 | private short m_ds;
287 | private short m_es;
288 | private short m_si;
289 | private short m_di;
290 |
291 | private short m_ss;
292 | private short m_bp;
293 | private short m_sp;
294 |
295 | private short m_cs;
296 | private short m_ip;
297 | private short m_flags;
298 |
299 | /** The virtual Energy register (used to calculate the warrior's speed). */
300 | private short m_energy;
301 |
302 | /** The virtual bomb count registers (used for INT 0x86, INT 0x87 opcodes). */
303 | private byte m_bomb1count;
304 | private byte m_bomb2count;
305 |
306 |
307 | /**
308 | * Masks for the various 'flags' fields.
309 | */
310 | private static final short FLAGS_MASK_CARRY = 0x0001;
311 | private static final short FLAGS_MASK_PARITY = 0x0004;
312 | private static final short FLAGS_MASK_AUX = 0x0010;
313 | private static final short FLAGS_MASK_ZERO = 0x0040;
314 | private static final short FLAGS_MASK_SIGN = 0x0080;
315 | private static final short FLAGS_MASK_TRAP = 0x0100;
316 | private static final short FLAGS_MASK_INTERRUPT = 0x0200;
317 | private static final short FLAGS_MASK_DIRECTION = 0x0400;
318 | private static final short FLAGS_MASK_OVERFLOW = 0x0800;
319 | }
--------------------------------------------------------------------------------
/src/main/java/il/co/codeguru/corewars8086/cpu/DivisionException.java:
--------------------------------------------------------------------------------
1 | package il.co.codeguru.corewars8086.cpu;
2 |
3 | /**
4 | * Thrown by DIV/IDIV opcodes when the division overflows, or when dividing by zero.
5 | *
6 | * @author DL
7 | */
8 | public class DivisionException extends CpuException {
9 | private static final long serialVersionUID = 1L;
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/il/co/codeguru/corewars8086/cpu/IndirectAddressingDecoder.java:
--------------------------------------------------------------------------------
1 | package il.co.codeguru.corewars8086.cpu;
2 |
3 | import il.co.codeguru.corewars8086.memory.MemoryException;
4 | import il.co.codeguru.corewars8086.memory.RealModeAddress;
5 | import il.co.codeguru.corewars8086.memory.RealModeMemory;
6 |
7 | /**
8 | * Decodes indirect-addressing opcodes (translates between the CPU's internal
9 | * representation of indirect-addressing, to the actual real-mode address).
10 | *
11 | * The CPU supports four indirect addressing modes:
12 | * (0) [BX+SI] indirect
13 | * (1) [BX+SI+12h] indirect + imm8
14 | * (2) [BX+SI+1234h] indirect + imm16
15 | * (3) AX direct register mode
16 | *
17 | * Each indirect-addressing opcode has two operands: a register, and one of the
18 | * above. e.g:
19 | * ADD [BX+SI], AX
20 | *
21 | * @author DL
22 | */
23 | public class IndirectAddressingDecoder {
24 |
25 | /**
26 | * Constructor.
27 | * @param state CPU registers.
28 | * @param memory Memory.
29 | * @param fetcher Used to fetch additional opcode bytes.
30 | */
31 | public IndirectAddressingDecoder(
32 | CpuState state, RealModeMemory memory, OpcodeFetcher fetcher) {
33 |
34 | m_state = state;
35 | m_memory = memory;
36 | m_fetcher = fetcher;
37 | m_regs = new RegisterIndexingDecoder(m_state);
38 |
39 | m_regIndex = 0;
40 | m_memIndex = 0;
41 | m_memAddress = null;
42 | }
43 |
44 | /**
45 | * Fetches & decodes the bytes currently pointed by the OpcodeFetcher.
46 | * @throws MemoryException on any error while reading from memory.
47 | */
48 | public void reset() throws MemoryException {
49 |
50 | // read the 'mode' byte (MM RRR III)
51 | // M - indirect addressing mode mux
52 | // R - register indexing
53 | // I - indirect addressing indexing
54 | byte modeByte = m_fetcher.nextByte();
55 |
56 | byte mode = (byte)((modeByte >> 6) & 0x03);
57 | m_regIndex = (byte)((modeByte >> 3) & 0x07);
58 | m_memIndex = (byte)(modeByte & 0x07);
59 |
60 | // decode the opcode according to the indirect-addressing mode, and
61 | // retrieve the address operand
62 | switch (mode) {
63 | case 0:
64 | m_memAddress = getMode0Address();
65 | break;
66 | case 1:
67 | m_memAddress = getMode1Address();
68 | break;
69 | case 2:
70 | m_memAddress = getMode2Address();
71 | break;
72 | case 3:
73 | m_memAddress = getMode3Address();
74 | break;
75 | default:
76 | throw new RuntimeException();
77 | }
78 | }
79 |
80 | /**
81 | * @return 3 bits representing the internal register indexing.
82 | */
83 | public byte getRegIndex() {
84 | return m_regIndex;
85 | }
86 |
87 | /**
88 | * @return The indirect memory operand's address (or null if the latter
89 | * refers to a register).
90 | */
91 | public RealModeAddress getMemAddress() {
92 | return m_memAddress;
93 | }
94 |
95 | /**
96 | * Assuming the opcode operand referred to an 8bit register, returns the
97 | * corresponding register's value.
98 | * @return 8bit register value.
99 | */
100 | public byte getReg8() {
101 | return m_regs.getReg8(m_regIndex);
102 | }
103 |
104 | /**
105 | * Returns the 8bit value pointed by the indirect memory operand (or register,
106 | * depands on the indirect-addressing mode).
107 | * @return Indirect address (or register) 8bit value.
108 | */
109 | public byte getMem8() throws MemoryException {
110 | if (m_memAddress != null) {
111 | return m_memory.readByte(m_memAddress);
112 | }
113 | return m_regs.getReg8(m_memIndex);
114 | }
115 |
116 | /**
117 | * Assuming the opcode operand referred to a 16bit register, returns the
118 | * corresponding register's value.
119 | * @return 16bit register value.
120 | */
121 | public short getReg16() {
122 | return m_regs.getReg16(m_regIndex);
123 | }
124 |
125 | /**
126 | * Assuming the opcode operand referred to a segment register, returns the
127 | * corresponding register's value.
128 | * @return segment register value.
129 | */
130 | public short getSeg() {
131 | return m_regs.getSeg(m_regIndex);
132 | }
133 |
134 | /**
135 | * Returns the 16bit value pointed by the indirect memory operand (or register,
136 | * depands on the indirect-addressing mode).
137 | * @return Indirect address (or register) 16bit value.
138 | */
139 | public short getMem16() throws MemoryException {
140 | if (m_memAddress != null) {
141 | return m_memory.readWord(m_memAddress);
142 | }
143 | return m_regs.getReg16(m_memIndex);
144 | }
145 |
146 | /**
147 | * Assuming the opcode operand referred to an 8bit register, sets the
148 | * corresponding register's value.
149 | * @param value New value for the 8bit register.
150 | */
151 | public void setReg8(byte value) {
152 | m_regs.setReg8(m_regIndex, value);
153 | }
154 |
155 | /**
156 | * Sets the 8bit value pointed by the indirect memory operand (or register,
157 | * depands on the indirect-addressing mode).
158 | * @param value Value to set.
159 | */
160 | public void setMem8(byte value) throws MemoryException {
161 | if (m_memAddress != null) {
162 | m_memory.writeByte(m_memAddress, value);
163 | } else {
164 | m_regs.setReg8(m_memIndex, value);
165 | }
166 | }
167 |
168 | /**
169 | * Assuming the opcode operand referred to a 16bit register, sets the
170 | * corresponding register's value.
171 | * @param value New value for the segment register.
172 | */
173 | public void setReg16(short value) {
174 | m_regs.setReg16(m_regIndex, value);
175 | }
176 |
177 | /**
178 | * Assuming the opcode operand referred to a segment register, sets the
179 | * corresponding register's value.
180 | * @param value New value for the segment register.
181 | */
182 | public void setSeg(short value) {
183 | m_regs.setSeg(m_regIndex, value);
184 | }
185 |
186 | /**
187 | * Sets the 16bit value pointed by the indirect memory operand (or register,
188 | * depands on the indirect-addressing mode).
189 | * @param value Value to set.
190 | */
191 | public void setMem16(short value) throws MemoryException {
192 | if (m_memAddress != null) {
193 | m_memory.writeWord(m_memAddress, value);
194 | } else {
195 | m_regs.setReg16(m_memIndex, value);
196 | }
197 | }
198 |
199 | /**
200 | * Decodes the indirect-memory operand corresponding to mode #0.
201 | * @return the real-mode address to which the indirect-memory operand
202 | * refers to.
203 | * @throws MemoryException on any error while reading from memory.
204 | */
205 | private RealModeAddress getMode0Address() throws MemoryException {
206 | switch (m_memIndex) {
207 | case 0:
208 | return new RealModeAddress(
209 | m_state.getDS(), (short)(m_state.getBX() + m_state.getSI()));
210 | case 1:
211 | return new RealModeAddress(
212 | m_state.getDS(), (short)(m_state.getBX() + m_state.getDI()));
213 | case 2:
214 | return new RealModeAddress(
215 | m_state.getSS(), (short)(m_state.getBP() + m_state.getSI()));
216 | case 3:
217 | return new RealModeAddress(
218 | m_state.getSS(), (short)(m_state.getBP() + m_state.getDI()));
219 | case 4:
220 | return new RealModeAddress(m_state.getDS(), m_state.getSI());
221 | case 5:
222 | return new RealModeAddress(m_state.getDS(), m_state.getDI());
223 | case 6:
224 | return new RealModeAddress(m_state.getDS(), m_fetcher.nextWord());
225 | case 7:
226 | return new RealModeAddress(m_state.getDS(), m_state.getBX());
227 | default:
228 | throw new RuntimeException();
229 | }
230 | }
231 |
232 | /**
233 | * Decodes the indirect-memory operand corresponding to mode #1.
234 | * @return the real-mode address to which the indirect-memory operand
235 | * refers to.
236 | * @throws MemoryException on any error while reading from memory.
237 | */
238 | private RealModeAddress getMode1Address() throws MemoryException {
239 | switch (m_memIndex) {
240 | case 0:
241 | return new RealModeAddress(m_state.getDS(),
242 | (short)(m_state.getBX() + m_state.getSI() + m_fetcher.nextByte()));
243 | case 1:
244 | return new RealModeAddress(m_state.getDS(),
245 | (short)(m_state.getBX() + m_state.getDI() + m_fetcher.nextByte()));
246 | case 2:
247 | return new RealModeAddress(m_state.getSS(),
248 | (short)(m_state.getBP() + m_state.getSI() + m_fetcher.nextByte()));
249 | case 3:
250 | return new RealModeAddress(m_state.getSS(),
251 | (short)(m_state.getBP() + m_state.getDI() + m_fetcher.nextByte()));
252 | case 4:
253 | return new RealModeAddress(m_state.getDS(),
254 | (short)(m_state.getSI() + m_fetcher.nextByte()));
255 | case 5:
256 | return new RealModeAddress(m_state.getDS(),
257 | (short)(m_state.getDI() + m_fetcher.nextByte()));
258 | case 6:
259 | return new RealModeAddress(m_state.getSS(),
260 | (short)(m_state.getBP() + m_fetcher.nextByte()));
261 | case 7:
262 | return new RealModeAddress(m_state.getDS(),
263 | (short)(m_state.getBX() + m_fetcher.nextByte()));
264 | default:
265 | throw new RuntimeException();
266 | }
267 | }
268 |
269 | /**
270 | * Decodes the indirect-memory operand corresponding to mode #2.
271 | * @return the real-mode address to which the indirect-memory operand
272 | * refers to.
273 | * @throws MemoryException on any error while reading from memory.
274 | */
275 | private RealModeAddress getMode2Address() throws MemoryException {
276 | switch (m_memIndex) {
277 | case 0:
278 | return new RealModeAddress(m_state.getDS(),
279 | (short)(m_state.getBX() + m_state.getSI() + m_fetcher.nextWord()));
280 | case 1:
281 | return new RealModeAddress(m_state.getDS(),
282 | (short)(m_state.getBX() + m_state.getDI() + m_fetcher.nextWord()));
283 | case 2:
284 | return new RealModeAddress(m_state.getSS(),
285 | (short)(m_state.getBP() + m_state.getSI() + m_fetcher.nextWord()));
286 | case 3:
287 | return new RealModeAddress(m_state.getSS(),
288 | (short)(m_state.getBP() + m_state.getDI() + m_fetcher.nextWord()));
289 | case 4:
290 | return new RealModeAddress(m_state.getDS(),
291 | (short)(m_state.getSI() + m_fetcher.nextWord()));
292 | case 5:
293 | return new RealModeAddress(m_state.getDS(),
294 | (short)(m_state.getDI() + m_fetcher.nextWord()));
295 | case 6:
296 | return new RealModeAddress(m_state.getSS(),
297 | (short)(m_state.getBP() + m_fetcher.nextWord()));
298 | case 7:
299 | return new RealModeAddress(m_state.getDS(),
300 | (short)(m_state.getBX() + m_fetcher.nextWord()));
301 | default:
302 | throw new RuntimeException();
303 | }
304 | }
305 |
306 | /**
307 | * Decodes the indirect-memory operand corresponding to mode #3.
308 | * Since in this mode the indirect-memory operand actually referes to one
309 | * of the registers, the method simply returns 'null'.
310 | * @return null (meaning the indirect operand refers to a register).
311 | */
312 | private RealModeAddress getMode3Address() {
313 | return null;
314 | }
315 |
316 | /** CPU registers */
317 | private final CpuState m_state;
318 |
319 | /** Memory */
320 | private final RealModeMemory m_memory;
321 |
322 | /** Used to fetch additional opcode bytes. */
323 | private final OpcodeFetcher m_fetcher;
324 |
325 | /** Used to decode the non-memory part of the opcode */
326 | private final RegisterIndexingDecoder m_regs;
327 |
328 | private byte m_regIndex;
329 | private byte m_memIndex;
330 | private RealModeAddress m_memAddress;
331 | }
332 |
--------------------------------------------------------------------------------
/src/main/java/il/co/codeguru/corewars8086/cpu/IntOpcodeException.java:
--------------------------------------------------------------------------------
1 | package il.co.codeguru.corewars8086.cpu;
2 |
3 | /**
4 | * Thrown on various interrupt opcodes.
5 | *
6 | * @author DL
7 | */
8 | public class IntOpcodeException extends CpuException {
9 | private static final long serialVersionUID = 1L;
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/il/co/codeguru/corewars8086/cpu/InvalidOpcodeException.java:
--------------------------------------------------------------------------------
1 | package il.co.codeguru.corewars8086.cpu;
2 |
3 | /**
4 | * Thrown when attempting to execute an invalid opcode.
5 | *
6 | * @author DL
7 | */
8 | public class InvalidOpcodeException extends CpuException {
9 | private static final long serialVersionUID = 1L;
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/il/co/codeguru/corewars8086/cpu/OpcodeFetcher.java:
--------------------------------------------------------------------------------
1 | package il.co.codeguru.corewars8086.cpu;
2 |
3 | import il.co.codeguru.corewars8086.memory.MemoryException;
4 | import il.co.codeguru.corewars8086.memory.RealModeAddress;
5 | import il.co.codeguru.corewars8086.memory.RealModeMemory;
6 |
7 | /**
8 | * Wraps opcode fetching from CS:IP.
9 | *
10 | * @author DL
11 | */
12 | public class OpcodeFetcher {
13 |
14 | /**
15 | * Constructor.
16 | * @param state Used to read & update CS:IP.
17 | * @param memory Used to actually read the fetched bytes.
18 | */
19 | public OpcodeFetcher(CpuState state, RealModeMemory memory) {
20 | m_state = state;
21 | m_memory = memory;
22 | }
23 |
24 | /**
25 | * @return the next byte pointed by CS:IP (and advances IP).
26 | * @throws MemoryException on any error.
27 | */
28 | public byte nextByte() throws MemoryException {
29 | RealModeAddress address = new RealModeAddress(
30 | m_state.getCS(), m_state.getIP());
31 | m_state.setIP((short)(m_state.getIP() + 1));
32 | return m_memory.readExecuteByte(address);
33 | }
34 |
35 | /**
36 | * @return the next word pointed by CS:IP (and advances IP).
37 | * @throws MemoryException on any error.
38 | */
39 | public short nextWord() throws MemoryException {
40 | RealModeAddress address = new RealModeAddress(
41 | m_state.getCS(), m_state.getIP());
42 | m_state.setIP((short)(m_state.getIP() + 2));
43 | return m_memory.readExecuteWord(address);
44 | }
45 |
46 | /** Used to read & update CS:IP. */
47 | private final CpuState m_state;
48 |
49 | /** Used to actually read the fetched bytes. */
50 | private final RealModeMemory m_memory;
51 | }
--------------------------------------------------------------------------------
/src/main/java/il/co/codeguru/corewars8086/cpu/RegisterIndexingDecoder.java:
--------------------------------------------------------------------------------
1 | package il.co.codeguru.corewars8086.cpu;
2 |
3 | /**
4 | * Decodes the CPU's internal register indexing to the actual registers.
5 | *
6 | * @author DL
7 | */
8 | public class RegisterIndexingDecoder {
9 |
10 | /**
11 | * Constructor.
12 | *
13 | * @param state CPU Registers.
14 | */
15 | RegisterIndexingDecoder(CpuState state) {
16 | m_state = state;
17 | }
18 |
19 | /**
20 | * Returns the value of the 8bit register whose index is given.
21 | * @param index CPU's internal register index.
22 | * @return the value of the 8bit register whose index is given.
23 | */
24 | public byte getReg8(byte index) {
25 | switch (index) {
26 | case 0:
27 | return m_state.getAL();
28 | case 1:
29 | return m_state.getCL();
30 | case 2:
31 | return m_state.getDL();
32 | case 3:
33 | return m_state.getBL();
34 | case 4:
35 | return m_state.getAH();
36 | case 5:
37 | return m_state.getCH();
38 | case 6:
39 | return m_state.getDH();
40 | case 7:
41 | return m_state.getBH();
42 | default:
43 | throw new RuntimeException();
44 | }
45 | }
46 |
47 | /**
48 | * Sets the value of the 8bit register whose index is given.
49 | * @param index CPU's internal register index.
50 | * @param value New value for above register.
51 | */
52 | public void setReg8(byte index, byte value) {
53 | switch (index) {
54 | case 0:
55 | m_state.setAL(value);
56 | break;
57 | case 1:
58 | m_state.setCL(value);
59 | break;
60 | case 2:
61 | m_state.setDL(value);
62 | break;
63 | case 3:
64 | m_state.setBL(value);
65 | break;
66 | case 4:
67 | m_state.setAH(value);
68 | break;
69 | case 5:
70 | m_state.setCH(value);
71 | break;
72 | case 6:
73 | m_state.setDH(value);
74 | break;
75 | case 7:
76 | m_state.setBH(value);
77 | break;
78 | default:
79 | throw new RuntimeException();
80 | }
81 | }
82 |
83 | /**
84 | * Returns the value of the 16bit register whose index is given.
85 | * @param index CPU's internal register index.
86 | * @return the value of the 16bit register whose index is given.
87 | */
88 | public short getReg16(byte index) {
89 | switch (index) {
90 | case 0:
91 | return m_state.getAX();
92 | case 1:
93 | return m_state.getCX();
94 | case 2:
95 | return m_state.getDX();
96 | case 3:
97 | return m_state.getBX();
98 | case 4:
99 | return m_state.getSP();
100 | case 5:
101 | return m_state.getBP();
102 | case 6:
103 | return m_state.getSI();
104 | case 7:
105 | return m_state.getDI();
106 | default:
107 | throw new RuntimeException();
108 | }
109 | }
110 |
111 | /**
112 | * Sets the value of the 16bit register whose index is given.
113 | * @param index CPU's internal register index.
114 | * @param value New value for above register.
115 | */
116 | public void setReg16(byte index, short value) {
117 | switch (index) {
118 | case 0:
119 | m_state.setAX(value);
120 | break;
121 | case 1:
122 | m_state.setCX(value);
123 | break;
124 | case 2:
125 | m_state.setDX(value);
126 | break;
127 | case 3:
128 | m_state.setBX(value);
129 | break;
130 | case 4:
131 | m_state.setSP(value);
132 | break;
133 | case 5:
134 | m_state.setBP(value);
135 | break;
136 | case 6:
137 | m_state.setSI(value);
138 | break;
139 | case 7:
140 | m_state.setDI(value);
141 | break;
142 | default:
143 | throw new RuntimeException();
144 | }
145 | }
146 |
147 | /**
148 | * Returns the value of the segment register whose index is given.
149 | * @param index CPU's internal register index.
150 | * @return the value of the segment register whose index is given.
151 | */
152 | public short getSeg(byte index) {
153 | switch (index) {
154 | case 0:
155 | return m_state.getES();
156 | case 1:
157 | return m_state.getCS();
158 | case 2:
159 | return m_state.getSS();
160 | case 3:
161 | return m_state.getDS();
162 | case 4:
163 | return m_state.getES();
164 | case 5:
165 | return m_state.getCS();
166 | case 6:
167 | return m_state.getSS();
168 | case 7:
169 | return m_state.getDS();
170 | default:
171 | throw new RuntimeException();
172 | }
173 | }
174 |
175 | /**
176 | * Sets the value of the segment register whose index is given.
177 | * @param index CPU's internal register index.
178 | * @param value New value for above register.
179 | */
180 | public void setSeg(byte index, short value) {
181 | switch (index) {
182 | case 0:
183 | m_state.setES(value);
184 | break;
185 | case 1:
186 | m_state.setCS(value);
187 | break;
188 | case 2:
189 | m_state.setSS(value);
190 | break;
191 | case 3:
192 | m_state.setDS(value);
193 | break;
194 | case 4:
195 | m_state.setES(value);
196 | break;
197 | case 5:
198 | m_state.setCS(value);
199 | break;
200 | case 6:
201 | m_state.setSS(value);
202 | break;
203 | case 7:
204 | m_state.setDS(value);
205 | break;
206 | default:
207 | throw new RuntimeException();
208 | }
209 | }
210 |
211 | /** Used to access the actual registers */
212 | private final CpuState m_state;
213 | }
214 |
--------------------------------------------------------------------------------
/src/main/java/il/co/codeguru/corewars8086/cpu/UnimplementedOpcodeException.java:
--------------------------------------------------------------------------------
1 | package il.co.codeguru.corewars8086.cpu;
2 |
3 | /**
4 | * Thrown when attempting to execute an unimplemented opcode.
5 | *
6 | * @author DL
7 | */
8 | public class UnimplementedOpcodeException extends CpuException {
9 | private static final long serialVersionUID = 1L;
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/il/co/codeguru/corewars8086/cpu/UnsupportedOpcodeException.java:
--------------------------------------------------------------------------------
1 | package il.co.codeguru.corewars8086.cpu;
2 |
3 | /**
4 | * Thrown when attempting to execute an unsupported opcode.
5 | *
6 | * @author DL
7 | */
8 | public class UnsupportedOpcodeException extends CpuException {
9 | private static final long serialVersionUID = 1L;
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/il/co/codeguru/corewars8086/gui/Canvas.java:
--------------------------------------------------------------------------------
1 | package il.co.codeguru.corewars8086.gui;
2 |
3 | import il.co.codeguru.corewars8086.utils.EventMulticaster;
4 |
5 | import java.awt.Color;
6 | import java.awt.Dimension;
7 | import java.awt.Graphics;
8 | import java.awt.event.MouseEvent;
9 |
10 | import javax.swing.JComponent;
11 | import javax.swing.event.MouseInputListener;
12 |
13 |
14 | /**
15 | * @author BS
16 | */
17 | public class Canvas extends JComponent implements MouseInputListener {
18 | private static final long serialVersionUID = 1L;
19 |
20 | public static final int BOARD_SIZE = 256;
21 | public static final int DOT_SIZE = 3;
22 | public static final byte EMPTY = -1;
23 |
24 | private byte[][] data;
25 |
26 | private boolean[][] pointer;
27 |
28 | private EventMulticaster eventCaster;
29 | private MouseAddressRequest eventHandler;
30 |
31 | private int MouseX, MouseY;
32 |
33 | public Canvas() {
34 | eventCaster = new EventMulticaster(MouseAddressRequest.class);
35 | eventHandler = (MouseAddressRequest) eventCaster.getProxy();
36 | this.addMouseMotionListener(this);
37 | this.addMouseListener(this);
38 | this.MouseX = 0;
39 | this.MouseY = 0;
40 | clear();
41 | }
42 |
43 | @Override
44 | public Dimension getMinimumSize() {
45 | return new Dimension(BOARD_SIZE * DOT_SIZE, BOARD_SIZE * DOT_SIZE);
46 | }
47 |
48 | @Override
49 | public Dimension getPreferredSize() {
50 | return getMinimumSize();
51 | }
52 |
53 | public void paintPixel(int number, byte color) {
54 | paintPixel(number % BOARD_SIZE, number / BOARD_SIZE, color);
55 | }
56 |
57 | public void paintPixel(int x, int y, byte color) {
58 | data[x][y] = color;
59 | Graphics g = getGraphics();
60 | if (g != null) {
61 | g.setColor(ColorHolder.getInstance().getColor(color,false));
62 | g.fillRect(x * DOT_SIZE, y * DOT_SIZE, DOT_SIZE, DOT_SIZE);
63 | }
64 | }
65 |
66 | /**
67 | * Get the color of warrior id
68 | */
69 | public Color getColorForWarrior(int id) {
70 | return ColorHolder.getInstance().getColor(id,false);
71 | }
72 |
73 |
74 | public void paintPointer(int x, int y, Color color) {
75 | pointer[x][y] = true;
76 | Graphics g = getGraphics();
77 | if (g != null) {
78 | g.setColor(color);
79 | g.fillRect(x * DOT_SIZE, y * DOT_SIZE, DOT_SIZE, DOT_SIZE);
80 | }
81 | }
82 |
83 | public void paintPointer(int x, int y, byte color) {
84 | this.paintPointer(x, y, ColorHolder.getInstance().getColor(color, true));
85 | }
86 |
87 | public void paintPointer(int number, byte color) {
88 | this.paintPointer(number % BOARD_SIZE, number / BOARD_SIZE, color);
89 | }
90 |
91 | /**
92 | * Clears the entire canvas
93 | */
94 | public void clear() {
95 | data = new byte[BOARD_SIZE][BOARD_SIZE];
96 | pointer = new boolean[BOARD_SIZE][BOARD_SIZE];
97 | for (int i = 0; i < BOARD_SIZE; i++)
98 | for (int j = 0; j < BOARD_SIZE; j++) {
99 | data[i][j] = EMPTY;
100 | pointer[i][j] = false;
101 | }
102 | repaint();
103 | }
104 |
105 | /**
106 | * When we have to - repaint the entire canvas
107 | */
108 | @Override
109 | public void paint(Graphics g) {
110 | g.fillRect(0,0, BOARD_SIZE * DOT_SIZE, BOARD_SIZE * DOT_SIZE);
111 |
112 | for (int y = 0; y < BOARD_SIZE; y++) {
113 | for (int x = 0; x < BOARD_SIZE; x++) {
114 | int cellVal = data[x][y];
115 | if (cellVal == EMPTY) {
116 | continue;
117 | }
118 |
119 | g.setColor(ColorHolder.getInstance().getColor(cellVal,false));
120 | g.fillRect(x*DOT_SIZE, y*DOT_SIZE, DOT_SIZE, DOT_SIZE);
121 | }
122 | }
123 | }
124 |
125 | @Override
126 | public void mouseMoved(MouseEvent e) {
127 |
128 | Graphics g = this.getGraphics();
129 |
130 | if (g != null) {
131 | // delete Mouse
132 | this.clearMousePointer(g);
133 |
134 | if (true) {
135 | MouseX = e.getX() / DOT_SIZE;
136 | MouseY = e.getY() / DOT_SIZE;
137 |
138 | // draw new Mouse
139 | g.setColor(Color.WHITE);
140 |
141 | g.fillRect(MouseX * DOT_SIZE, MouseY * DOT_SIZE, DOT_SIZE,
142 | DOT_SIZE);
143 | }
144 | }
145 | }
146 |
147 | private void clearMousePointer(Graphics g) {
148 | try {
149 | g.setColor(ColorHolder.getInstance()
150 | .getColor(data[MouseX][MouseY],false));
151 | } catch (Exception ex) {
152 | // TODO the true variable of the color
153 | g.setColor(new Color(51, 51, 51));
154 | }
155 | g.fillRect(MouseX * DOT_SIZE, MouseY * DOT_SIZE, DOT_SIZE, DOT_SIZE);
156 | }
157 |
158 | @Override
159 | public void mouseClicked(MouseEvent arg0) {
160 | eventHandler.addressAtMouseLocationRequested(this.MouseX + BOARD_SIZE* this.MouseY);
161 | }
162 |
163 | @Override
164 | public void mouseExited(MouseEvent arg0) {
165 | this.clearMousePointer(this.getGraphics());
166 | }
167 |
168 | public void addListener(MouseAddressRequest l) {
169 | eventCaster.add(l);
170 | }
171 |
172 | public void deletePointers() {
173 | for (int i = 0; i < BOARD_SIZE; i++)
174 | for (int j = 0; j < BOARD_SIZE; j++) {
175 | if (pointer[i][j] == true && data[i][j] != EMPTY) {
176 | pointer[i][j] = false;
177 | paintPixel(i, j, data[i][j]);
178 | }
179 | }
180 | }
181 |
182 | @Override
183 | public void mouseEntered(MouseEvent arg0) {
184 | // TODO Auto-generated method stub
185 |
186 | }
187 |
188 | @Override
189 | public void mousePressed(MouseEvent arg0) {
190 | // TODO Auto-generated method stub
191 |
192 | }
193 |
194 | @Override
195 | public void mouseReleased(MouseEvent arg0) {
196 | // TODO Auto-generated method stub
197 |
198 | }
199 |
200 | @Override
201 | public void mouseDragged(MouseEvent arg0) {
202 | // TODO Auto-generated method stub
203 |
204 | }
205 |
206 | }
207 |
--------------------------------------------------------------------------------
/src/main/java/il/co/codeguru/corewars8086/gui/ColorHolder.java:
--------------------------------------------------------------------------------
1 | package il.co.codeguru.corewars8086.gui;
2 |
3 | import java.awt.Color;
4 |
5 | /**
6 | * @author BS
7 | */
8 | public class ColorHolder {
9 | private Color colors[];
10 | private Color darkColors[];
11 | public static final int MAX_COLORS = 360;
12 | private static ColorHolder ins = new ColorHolder(MAX_COLORS);
13 |
14 | private ColorHolder(int numPlayers) {
15 | // see http://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/
16 |
17 | colors = new Color[numPlayers];
18 | float golden_ratio_conjugate = 0.618033988749895f;
19 | float x = 0;
20 | for (int i = 0; i < MAX_COLORS; i++) {
21 | colors[i] = Color.getHSBColor(x % 1, 0.8f, 0.95f);
22 | x += golden_ratio_conjugate;
23 | }
24 |
25 | darkColors = new Color[colors.length];
26 | for (int i = 0; i < colors.length; i++) {
27 | darkColors[i] = colors[i].darker();
28 | }
29 | }
30 |
31 | public static ColorHolder getInstance() {
32 | return ins;
33 | }
34 |
35 | public Color getColor(int pos, boolean darker) {
36 | if (darker) {
37 | return darkColors[pos];
38 | } else {
39 | return colors[pos];
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/main/java/il/co/codeguru/corewars8086/gui/ColumnGraph.java:
--------------------------------------------------------------------------------
1 | package il.co.codeguru.corewars8086.gui;
2 |
3 | import il.co.codeguru.corewars8086.cli.Options;
4 |
5 | import java.awt.Color;
6 | import java.awt.Dimension;
7 | import java.awt.Font;
8 | import java.awt.Graphics;
9 | import java.awt.Graphics2D;
10 |
11 | import javax.swing.JComponent;
12 |
13 | /**
14 | * @author BS
15 | */
16 | public class ColumnGraph extends JComponent {
17 | private static final long serialVersionUID = 1L;
18 |
19 | private float[][] values;
20 | private String[] names;
21 | private float maxValue;
22 | private double reduceFactor;
23 | private long numTopTeams;
24 | private TeamColorHolder teamColorHolder;
25 |
26 | private static final int NAME_HEIGHT = 14;
27 | // We assume the teams' first 3 characters are the school name
28 | private static final int SCHOOL_PREFIX_LEN = 3;
29 |
30 | public ColumnGraph(String[] names, Options options) {
31 | super();
32 | this.names = new String[names.length];
33 | // the first element holds the sum of all the other values
34 | values = new float[names.length][3];
35 | System.arraycopy(names, 0, this.names, 0, names.length);
36 | maxValue = 0;
37 | reduceFactor = 5;
38 | numTopTeams = Math.min(names.length / 2, 10); // If you are in the top half you count as top team
39 | // Give out colors by the team name first 3 characters
40 | // So teams from the same school have the same color
41 | teamColorHolder = new TeamColorHolder(names, SCHOOL_PREFIX_LEN, options);
42 | }
43 |
44 | @Override
45 | public Dimension getMinimumSize() {
46 | return new Dimension(500,500);
47 | }
48 |
49 | @Override
50 | public Dimension getPreferredSize() {
51 | return getMinimumSize();
52 | }
53 |
54 | public void addToValue(int pos, int subIndex, float value) {
55 | values[pos][0]+= value;
56 | values[pos][subIndex+1]+= value;
57 |
58 | if (values[pos][0] > maxValue) {
59 | // reset graph factor by half to make more room
60 | maxValue = values[pos][0];
61 | if (maxValue * reduceFactor > getSize().height-10) {
62 | reduceFactor *= 0.5;
63 | }
64 | }
65 | repaint();
66 | }
67 |
68 | /* (non-Javadoc)
69 | * @see javax.swing.JComponent#paintComponent(java.awt.Graphics)
70 | */
71 | protected void paintComponent(Graphics g) {
72 | Dimension d = getSize();
73 | g.setColor(Color.BLACK);
74 | g.fillRect(0,0,d.width, d.height);
75 | d.setSize(d.width, d.height - NAME_HEIGHT);
76 | g.drawRect(0,0,d.width, d.height);
77 | final int numPlayers = names.length;
78 | int columnWidth = d.width / numPlayers;
79 |
80 | ColorHolder colorHolder= ColorHolder.getInstance();
81 | for (int i = 0; i < numPlayers; i++) {
82 | paintColumn(g, i, columnWidth, d.height, colorHolder);
83 | g.setColor(getTeamColor(i, false));
84 | g.drawString(names[i], i*columnWidth+5, d.height+NAME_HEIGHT-2);
85 | }
86 | }
87 |
88 | private int getTeamRating(int teamCol) {
89 | int rating = 1;
90 | for (int i = 0; i < values.length; i++) {
91 | // For every different team that has a higher score, this teams ranks one less
92 | // For a tie, both teams lose one rating score
93 | if (teamCol != i && values[teamCol][0] <= values[i][0]) {
94 | rating++;
95 | }
96 | }
97 | return rating;
98 | }
99 |
100 | private boolean isTopTeam(int teamCol) {
101 | return getTeamRating(teamCol) <= numTopTeams;
102 | }
103 |
104 | private Color getTeamColor(int teamIndex, boolean darker) {
105 | return teamColorHolder.getColor(names[teamIndex], darker);
106 | }
107 |
108 | // Copied from this stackover flow question - https://stackoverflow.com/questions/10083913/how-to-rotate-text-with-graphics2d-in-java
109 | public static void drawRotatedString(Graphics g, double x, double y, int angle, String text) {
110 | Graphics2D g2d = (Graphics2D)g;
111 | g2d.translate((float)x,(float)y);
112 | g2d.rotate(Math.toRadians(angle));
113 | g2d.drawString(text,0,0);
114 | g2d.rotate(-Math.toRadians(angle));
115 | g2d.translate(-(float)x,-(float)y);
116 | }
117 |
118 | public static Color invertColor(Color c) {
119 | int r = 255 - c.getRed();
120 | int g = 255 - c.getGreen();
121 | int b = 255 - c.getBlue();
122 | return new Color(r, g, b, c.getAlpha());
123 | }
124 |
125 | private void paintColumn(Graphics g, int col, int width, int startHeight, ColorHolder colorHolder) {
126 | g.setColor(getTeamColor(col, false));
127 | int height1 = (int) (reduceFactor*values[col][1]);
128 | g.fill3DRect(col*width, startHeight - height1, width, height1, true);
129 | g.setColor(getTeamColor(col, true));
130 | int height2 = (int) (reduceFactor*values[col][2]);
131 | int boxTopY = startHeight - height1 - height2;
132 | g.fill3DRect(col*width, boxTopY, width, height2, false);
133 | g.drawString("" + (int)(values[col][0] * 10)/10.0f, col*width, boxTopY - 5);
134 |
135 |
136 | if (isTopTeam(col)) {
137 | Font origFontBackup = g.getFont();
138 | // For top teams, draw their initials on top of the rectangle
139 | // This should be their school initials - for example,
140 | // "OST" for Ostrovski or "GBA" for GreenBlitz Academy
141 | String teamInitials = names[col].substring(0, Math.min(SCHOOL_PREFIX_LEN, names[col].length())).toUpperCase();
142 |
143 | // About font size - the number specified is the font's "em height"
144 | // See this stackoverflow question - https://graphicdesign.stackexchange.com/questions/4035/what-does-the-size-of-the-font-translate-to-exactly
145 | // We treat as approximately the max height of a character
146 | // wild guess - The width of the common character will be around the same
147 | int teamInitialsFontSize = Math.min(width/2, 100);
148 | g.setFont(new Font(Font.MONOSPACED, Font.BOLD, teamInitialsFontSize));
149 | g.drawString(teamInitials, col*width, boxTopY - 25);
150 |
151 |
152 |
153 | Color bakColor = g.getColor();
154 | Color gold = new Color(255, 215, 0);
155 | g.setColor(gold);
156 | // Draw the rating (e.g "1") on top of the box, in gold
157 | int ratingFontSize = (int)Math.min(width*1.3, 180);
158 | g.setFont(new Font(Font.MONOSPACED, Font.BOLD, ratingFontSize));
159 | g.drawString(String.valueOf(getTeamRating(col)), (int)((col + 0.1)*(width)), boxTopY - teamInitialsFontSize - 35);
160 | g.setColor(bakColor);
161 |
162 |
163 |
164 | // Here the font size should match:
165 | // The font height is the width because it's horizontal
166 | int rotatedNameFontSize = (int)Math.min(width * 0.6, 140);
167 | g.setFont(new Font(Font.MONOSPACED, Font.BOLD, rotatedNameFontSize));
168 | int charwidth = g.getFontMetrics().charWidth('A'); // monospaced so all char widths are the same
169 | int lettersCanFitInRect = (int)((height1 + height2) / charwidth);
170 | String nameCanFit = names[col].substring(0, Math.min(lettersCanFitInRect, names[col].length()));
171 |
172 | // Invert the color so we can see best
173 | Color curColor = g.getColor();
174 | g.setColor(invertColor(curColor));
175 | drawRotatedString(g, col*width + g.getFontMetrics().getAscent()/2, boxTopY + 5, 90, nameCanFit);
176 | g.setColor(curColor);
177 |
178 |
179 | g.setFont(origFontBackup);
180 | }
181 | }
182 | }
183 |
--------------------------------------------------------------------------------
/src/main/java/il/co/codeguru/corewars8086/gui/CompetitionWindow.java:
--------------------------------------------------------------------------------
1 | package il.co.codeguru.corewars8086.gui;
2 |
3 | import il.co.codeguru.corewars8086.cli.Options;
4 | import il.co.codeguru.corewars8086.war.Competition;
5 | import il.co.codeguru.corewars8086.war.CompetitionEventListener;
6 | import il.co.codeguru.corewars8086.war.ScoreEventListener;
7 | import il.co.codeguru.corewars8086.war.WarriorRepository;
8 |
9 | import javax.swing.*;
10 | import java.awt.*;
11 | import java.awt.event.ActionEvent;
12 | import java.awt.event.ActionListener;
13 | import java.awt.event.WindowEvent;
14 | import java.awt.event.WindowListener;
15 | import java.io.IOException;
16 |
17 | /**
18 | * @author BS
19 | */
20 | public class CompetitionWindow extends JFrame
21 | implements ScoreEventListener, ActionListener, CompetitionEventListener {
22 | private static final long serialVersionUID = 1L;
23 |
24 | private final Competition competition;
25 | private final ColumnGraph columnGraph;
26 |
27 | // widgets
28 | private final JButton runWarButton;
29 | private final JLabel warCounterDisplay;
30 | private final JCheckBox showBattleCheckBox;
31 | private final JTextField battlesPerGroupField;
32 | private final JTextField warriorsPerGroupField;
33 | private WarFrame battleFrame;
34 |
35 | private int warCounter;
36 | private int totalWars;
37 | private Thread warThread;
38 | private boolean competitionRunning;
39 |
40 | private static final String SEED_PREFIX = "SEED!@#=";
41 | private final JTextField seed;
42 |
43 | private final JCheckBox startPausedCheckBox;
44 |
45 | private final Options options;
46 |
47 | public CompetitionWindow(Options options) throws IOException {
48 | super("CodeGuru Extreme - Competition Viewer");
49 | this.options = options;
50 | getContentPane().setLayout(new BorderLayout());
51 | setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
52 | competition = new Competition(options);
53 | competition.addCompetitionEventListener(this);
54 | WarriorRepository warriorRepository = competition.getWarriorRepository();
55 | warriorRepository.addScoreEventListener(this);
56 | columnGraph = new ColumnGraph(warriorRepository.getGroupNames(), options);
57 | getContentPane().add(columnGraph, BorderLayout.CENTER);
58 | // -------------
59 | JPanel controlArea = new JPanel();
60 | controlArea.setLayout(new BoxLayout(controlArea, BoxLayout.Y_AXIS));
61 | // -------------- Button Panel
62 | JPanel buttonPanel = new JPanel();
63 | runWarButton = new JButton("Start!");
64 | runWarButton.addActionListener(this);
65 | buttonPanel.add(runWarButton);
66 | warCounterDisplay = new JLabel("");
67 | buttonPanel.add(warCounterDisplay);
68 | buttonPanel.add(Box.createHorizontalStrut(30));
69 | showBattleCheckBox = new JCheckBox("Show war on start");
70 | buttonPanel.add(showBattleCheckBox);
71 |
72 | startPausedCheckBox = new JCheckBox("Start Paused");
73 | startPausedCheckBox.setEnabled(!options.parallel); // TODO enable functionality in 5.0.1
74 | startPausedCheckBox.addActionListener(event -> {
75 | if (startPausedCheckBox.isSelected())
76 | showBattleCheckBox.setSelected(true);
77 | });
78 | buttonPanel.add(startPausedCheckBox);
79 |
80 | controlArea.add(buttonPanel);
81 | // -------------
82 | controlArea.add(new JSeparator(JSeparator.HORIZONTAL));
83 | // ------------ Control panel
84 | JPanel controlPanel = new JPanel();
85 | controlPanel.setLayout(new FlowLayout());
86 | controlPanel.add(new JLabel("Survivor groups per war:"));
87 |
88 | // If total number of teams is less then four, make it the defauld number
89 | int numberOfGroups = Math.min(4,
90 | competition.getWarriorRepository().getNumberOfGroups());
91 |
92 | warriorsPerGroupField = new JTextField(String.format("%d", numberOfGroups), 3);
93 | controlPanel.add(warriorsPerGroupField);
94 | controlPanel.add(new JLabel("Wars per groups combination:"));
95 | battlesPerGroupField = new JTextField("100", 4);
96 | controlPanel.add(battlesPerGroupField);
97 | seed = new JTextField(15);
98 | seed.setText("guru");
99 | controlPanel.add(new JLabel("seed:"));
100 | controlPanel.add(seed);
101 |
102 | controlArea.add(controlPanel);
103 |
104 | // ------------
105 | getContentPane().add(controlArea, BorderLayout.SOUTH);
106 |
107 | addWindowListener(new WindowListener() {
108 | public void windowOpened(WindowEvent e) {
109 | }
110 |
111 | public void windowClosing(WindowEvent e) {
112 | if (warThread != null) {
113 | competition.setAbort(true);
114 | }
115 | }
116 |
117 | public void windowClosed(WindowEvent e) {
118 | }
119 |
120 | public void windowIconified(WindowEvent e) {
121 | }
122 |
123 | public void windowDeiconified(WindowEvent e) {
124 | }
125 |
126 | public void windowActivated(WindowEvent e) {
127 | }
128 |
129 | public void windowDeactivated(WindowEvent e) {
130 | }
131 | });
132 | }
133 |
134 | /**
135 | * Starts a new war.
136 | *
137 | * @return whether or not a new war was started.
138 | */
139 | public boolean runWar() {
140 | try {
141 | long seedValue;
142 | if (seed.getText().startsWith(SEED_PREFIX)) {
143 | seedValue = Long.parseLong(seed.getText().substring(SEED_PREFIX.length()));
144 | } else {
145 | seedValue = seed.getText().hashCode();
146 | }
147 | competition.setSeed(seedValue);
148 | final int battlesPerGroup = Integer.parseInt(
149 | battlesPerGroupField.getText().trim());
150 | final int warriorsPerGroup = Integer.parseInt(
151 | warriorsPerGroupField.getText().trim());
152 | if (competition.getWarriorRepository().getNumberOfGroups() < warriorsPerGroup) {
153 | JOptionPane.showMessageDialog(this,
154 | "Not enough survivors (got " +
155 | competition.getWarriorRepository().getNumberOfGroups() +
156 | " but " + warriorsPerGroup + " are needed)");
157 | return false;
158 | }
159 | warThread = new Thread("CompetitionThread") {
160 | @Override
161 | public void run() {
162 | try {
163 | if (options.parallel) {
164 | competition.runCompetitionInParallel(battlesPerGroup, warriorsPerGroup, options.threads);
165 | } else {
166 | competition.runCompetition(battlesPerGroup, warriorsPerGroup, startPausedCheckBox.isSelected());
167 | }
168 | } catch (Exception e) {
169 | e.printStackTrace();
170 | }
171 | }
172 | };
173 | if (!competitionRunning) {
174 | warThread.start();
175 | return true;
176 | }
177 | } catch (NumberFormatException e2) {
178 | JOptionPane.showMessageDialog(this, "Error in configuration");
179 | }
180 | return false;
181 | }
182 |
183 | public void scoreChanged(String name, float addedValue, int groupIndex, int subIndex) {
184 | SwingUtilities.invokeLater(() -> columnGraph.addToValue(groupIndex, subIndex, addedValue));
185 | }
186 |
187 | public void actionPerformed(ActionEvent e) {
188 | if (e.getSource() == runWarButton) {
189 | showBattleFrameIfNeeded();
190 | switch (runWarButton.getText().trim()) {
191 | case "Start!":
192 | if (runWar()) {
193 | competitionRunning = true;
194 |
195 | if (options.parallel) {
196 | showBattleCheckBox.setEnabled(false); // TODO enable functionality in 5.0.1
197 | }
198 | }
199 | break;
200 | case ("Stop!"):
201 | competition.setAbort(true);
202 | competitionRunning = false;
203 | break;
204 | default:
205 | break;
206 | }
207 | }
208 | }
209 |
210 |
211 | @Override
212 | public void onWarStart(long seed) {
213 | SwingUtilities.invokeLater(() -> {
214 | this.seed.setText(SEED_PREFIX + seed);
215 | showBattleFrameIfNeeded();
216 | });
217 | }
218 |
219 | private void showBattleFrameIfNeeded() {
220 | if (showBattleCheckBox.isSelected() && battleFrame == null) {
221 | showBattleRoom();
222 | showBattleCheckBox.setSelected(false);
223 |
224 | if (options.parallel) {
225 | showBattleCheckBox.setEnabled(false);
226 | }
227 | }
228 | }
229 |
230 | private void showBattleRoom() {
231 | competition.setSpeed(5);
232 | battleFrame = new WarFrame(competition);
233 | battleFrame.addWindowListener(new WindowListener() {
234 | public void windowOpened(WindowEvent e) {
235 | }
236 |
237 | public void windowClosing(WindowEvent e) {
238 | }
239 |
240 | public void windowClosed(WindowEvent e) {
241 | //System.out.println("BattleFrame=null");
242 | battleFrame = null;
243 | competition.setSpeed(Competition.MAXIMUM_SPEED);
244 | }
245 |
246 | public void windowIconified(WindowEvent e) {
247 | }
248 |
249 | public void windowDeiconified(WindowEvent e) {
250 | }
251 |
252 | public void windowActivated(WindowEvent e) {
253 | }
254 |
255 | public void windowDeactivated(WindowEvent e) {
256 | }
257 | });
258 |
259 | competition.addMemoryEventLister(battleFrame);
260 | competition.addCompetitionEventListener(battleFrame);
261 | Rectangle battleFrameRect = new Rectangle(0, getY(), 750, 700);
262 | Rectangle screen = getGraphicsConfiguration().getBounds(); //for multiple monitors
263 |
264 | if (getX() + getWidth() <= screen.getX() + screen.getWidth()
265 | - battleFrameRect.width) {
266 | battleFrameRect.x = getX() + getWidth();
267 | } else if (screen.getX() + screen.getWidth() - battleFrameRect.width
268 | - getWidth() >= screen.getX()) {
269 | setLocation((int) (screen.getX() + screen.getWidth() - battleFrameRect.width
270 | - getWidth()), getY());
271 | battleFrameRect.x = getX() + getWidth();
272 | } else {
273 | setLocation((int) screen.getX(), getY());
274 | battleFrameRect.x = getWidth();
275 | }
276 |
277 | battleFrame.setBounds(battleFrameRect);
278 | battleFrame.setVisible(true);
279 | }
280 |
281 | public void onWarEnd(int reason, String winners) {
282 | warCounter++;
283 | seed.setText(SEED_PREFIX + competition.getSeed());
284 | SwingUtilities.invokeLater(
285 | () -> warCounterDisplay.setText("Wars so far:" + warCounter + " (out of " + totalWars + ")")
286 | );
287 | }
288 |
289 | public void onRound(int round) {
290 | }
291 |
292 | public void onWarriorBirth(String warriorName) {
293 | }
294 |
295 | public void onWarriorDeath(String warriorName, String reason) {
296 | }
297 |
298 | public void onCompetitionStart() {
299 | warCounter = 0;
300 | totalWars = competition.getTotalNumberOfWars();
301 | competition.setAbort(false);
302 | runWarButton.setText("Stop!");
303 | }
304 |
305 | public void onCompetitionEnd() {
306 | SwingUtilities.invokeLater(() -> {
307 | warCounterDisplay.setText(String.format("The competition is over. %d wars were run.", warCounter));
308 | runWarButton.setText("Start!");
309 | showBattleCheckBox.setEnabled(true);
310 | });
311 | warThread = null;
312 | competitionRunning = false;
313 | }
314 |
315 | @Override
316 | public void onEndRound() {
317 | }
318 |
319 | }
--------------------------------------------------------------------------------
/src/main/java/il/co/codeguru/corewars8086/gui/CpuFrame.java:
--------------------------------------------------------------------------------
1 | package il.co.codeguru.corewars8086.gui;
2 |
3 | import il.co.codeguru.corewars8086.memory.RealModeAddress;
4 | import il.co.codeguru.corewars8086.utils.Disassembler;
5 | import il.co.codeguru.corewars8086.war.Competition;
6 | import il.co.codeguru.corewars8086.war.CompetitionEventListener;
7 | import il.co.codeguru.corewars8086.war.War;
8 |
9 | import java.awt.Font;
10 | import java.awt.GridLayout;
11 | import java.awt.event.ActionEvent;
12 | import java.awt.event.ActionListener;
13 |
14 | import javax.swing.JButton;
15 | import javax.swing.JComboBox;
16 | import javax.swing.JFrame;
17 | import javax.swing.JOptionPane;
18 | import javax.swing.JPanel;
19 | import javax.swing.JTextArea;
20 |
21 | public class CpuFrame extends JFrame implements CompetitionEventListener {
22 |
23 | private War currentWar;
24 |
25 | private Competition competition;
26 |
27 | private JPanel menuPanel;
28 |
29 | private JComboBox
18 | *
24 | *
25 | * @author BS
26 | */
27 | public class WarFrame extends JFrame
28 | implements MemoryEventListener, CompetitionEventListener, MouseAddressRequest{
29 | private static final long serialVersionUID = 1L;
30 |
31 | /** the canvas which show the core war memory area */
32 | private Canvas warCanvas;
33 |
34 | /** the message area show misc. information about the current fight */
35 | private JTextArea messagesArea;
36 |
37 | /** list of warrior names */
38 | private JList nameList;
39 |
40 | /** Model for the name list */
41 | private DefaultListModel nameListModel;
42 |
43 | /** Holds the current round number */
44 | private int nRoundNumber;
45 |
46 | /** A text field showing the current round number */
47 | private JTextField roundNumber;
48 |
49 | // Debugger
50 | private JLabel addressFiled;
51 | private JButton btnCpuState;
52 | private CpuFrame cpuFrame;
53 | private JButton btnPause;
54 | private JButton btnSingleRound;
55 |
56 |
57 | private JSlider speedSlider;
58 |
59 | private final Competition competition;
60 |
61 | private MemoryFrame memoryFrame;
62 |
63 | public WarFrame(final Competition competition) {
64 | super("CodeGuru Extreme - Session Viewer");
65 | setDefaultCloseOperation(DISPOSE_ON_CLOSE);
66 | this.competition = competition;
67 | getContentPane().setLayout(new BorderLayout());
68 |
69 | // build widgets
70 | JPanel mainPanel = new JPanel(new BorderLayout());
71 |
72 | // build war zone (canvas + title)
73 | JPanel warZone = new JPanel(new BorderLayout());
74 | warZone.setBackground(Color.BLACK);
75 |
76 | JPanel canvasPanel = new JPanel();
77 | canvasPanel.setBorder(BorderFactory.createCompoundBorder(
78 | BorderFactory.createLineBorder(new Color(169,154,133),3),
79 | BorderFactory.createEmptyBorder(10,10,20,10)));
80 | canvasPanel.setBackground(Color.BLACK);
81 | warCanvas = new Canvas();
82 | canvasPanel.add(warCanvas);
83 | warZone.add(canvasPanel, BorderLayout.CENTER);
84 |
85 | //warZone.add(new JLabel(new ImageIcon("images/warzone.jpg")), BorderLayout.NORTH);
86 | mainPanel.add(warZone, BorderLayout.CENTER);
87 |
88 | // build info zone (message area + buttons)
89 | JPanel infoZone = new JPanel(new BorderLayout());
90 | messagesArea = new JTextArea(5, 60);
91 | messagesArea.setFont(new Font("Tahoma", Font.PLAIN, 12));
92 |
93 | infoZone.add(new JScrollPane(messagesArea), BorderLayout.CENTER);
94 |
95 | JPanel buttonPanel = new JPanel();
96 |
97 | buttonPanel.add(new JLabel("Round:"));
98 | roundNumber = new JTextField(4);
99 | roundNumber.setEditable(false);
100 | buttonPanel.add(roundNumber);
101 | buttonPanel.add(Box.createHorizontalStrut(20));
102 | JButton closeButton = new JButton("Close");
103 | closeButton.addActionListener(e -> dispose());
104 | buttonPanel.add(closeButton);
105 | buttonPanel.add(Box.createHorizontalStrut(20));
106 | buttonPanel.add(new JLabel("Speed:"));
107 | speedSlider = new JSlider(1,100,competition.getSpeed());
108 | speedSlider.addChangeListener(e -> {
109 | WarFrame.this.competition.setSpeed((int) Math.pow(1.2, speedSlider.getValue()) ); //exponential speed slider
110 | });
111 | buttonPanel.add(speedSlider);
112 | nRoundNumber = 0;
113 | infoZone.add(buttonPanel, BorderLayout.SOUTH);
114 | infoZone.setBackground(Color.black);
115 |
116 | // Debugger
117 | addressFiled = new JLabel("Click on the arena to see the memory");
118 | warCanvas.addListener(this);
119 |
120 | btnCpuState = new JButton("View CPU");
121 | btnCpuState.setEnabled(false);
122 | btnCpuState.addActionListener(event -> {
123 | cpuFrame = new CpuFrame(competition);
124 | WarFrame.this.competition.addCompetitionEventListener(cpuFrame);
125 | });
126 |
127 | competition.addCompetitionEventListener(this);
128 |
129 | btnPause = new JButton("Pause");
130 | btnPause.setEnabled(false);
131 | btnPause.addActionListener(event -> {
132 | if (competition.getCurrentWar().isPaused()) {
133 | competition.getCurrentWar().resume();
134 | btnPause.setText("Pause");
135 | btnSingleRound.setEnabled(false);
136 | } else {
137 | competition.getCurrentWar().pause();
138 | btnPause.setText("Resume");
139 | btnSingleRound.setEnabled(true);
140 | }
141 |
142 | });
143 |
144 | btnSingleRound = new JButton("Single Round");
145 | btnSingleRound.setEnabled(false);
146 | btnSingleRound.addActionListener(event -> competition.getCurrentWar().runSingleRound());
147 |
148 | buttonPanel.add(btnCpuState);
149 | buttonPanel.add(btnPause);
150 | buttonPanel.add(btnSingleRound);
151 | buttonPanel.add(addressFiled);
152 |
153 | // build warrior zone (warrior list + title)
154 | JPanel warriorZone = new JPanel(new BorderLayout());
155 | warriorZone.setBackground(Color.BLACK);
156 | nameListModel = new DefaultListModel();
157 | nameList = new JList(nameListModel);
158 | nameList.setPreferredSize(new Dimension(200,0));
159 | nameList.setCellRenderer(new NameCellRenderer());
160 | nameList.setOpaque(false);
161 | nameList.setBorder(BorderFactory.createCompoundBorder(
162 | BorderFactory.createLineBorder(new Color(169,154,133),3),
163 | BorderFactory.createEmptyBorder(10,10,20,10)));
164 | nameList.repaint();
165 | warriorZone.add(nameList, BorderLayout.CENTER);
166 | //warriorZone.add(new JLabel(new ImageIcon("images/warriors.jpg")), BorderLayout.NORTH);
167 | warriorZone.add(Box.createHorizontalStrut(20), BorderLayout.WEST);
168 | mainPanel.add(warriorZone, BorderLayout.EAST);
169 |
170 | setDefaultCloseOperation(DISPOSE_ON_CLOSE);
171 | getContentPane().setBackground(Color.BLACK);
172 | getContentPane().add(mainPanel, BorderLayout.CENTER);
173 | //getContentPane().add(new JLabel(new ImageIcon("images/title2.png")), BorderLayout.EAST);
174 | getContentPane().add(infoZone, BorderLayout.SOUTH);
175 | }
176 |
177 | /** Add a message to the message zone */
178 | public void addMessage(String message) {
179 | messagesArea.append(message + "\n");
180 | SwingUtilities.invokeLater(new Runnable() {
181 | public void run() {
182 | messagesArea.setCaretPosition(messagesArea.getDocument().getLength());
183 | }
184 | });
185 | }
186 |
187 | /** Add a message to the message zone (with round number) */
188 | public void addMessage(int round, String message) {
189 | addMessage("[" + round + "] "+ message);
190 | }
191 |
192 | /** @see MemoryEventListener#onMemoryWrite(RealModeAddress) */
193 | public void onMemoryWrite(RealModeAddress address) {
194 | int ipInsideArena = address.getLinearAddress() - 0x1000 *0x10; // arena * paragraph
195 |
196 | if ( address.getLinearAddress() >= War.ARENA_SEGMENT*0x10 && address.getLinearAddress() < 2*War.ARENA_SEGMENT*0x10 ) {
197 | warCanvas.paintPixel(
198 | Unsigned.unsignedShort(ipInsideArena),
199 | (byte)competition.getCurrentWarrior());
200 | }
201 | }
202 |
203 | /** @see CompetitionEventListener#onWarStart(long) */
204 | public void onWarStart(long seed) {
205 | addMessage("=== Session started ===");
206 | nameListModel.clear();
207 | warCanvas.clear();
208 | if (competition.getCurrentWar().isPaused()){
209 | btnPause.setText("Resume");
210 | btnSingleRound.setEnabled(true);
211 | }
212 | }
213 |
214 | /** @see CompetitionEventListener#onWarEnd(int, String) */
215 | public void onWarEnd(int reason, String winners) {
216 | roundNumber.setText(Integer.toString(nRoundNumber));
217 | roundNumber.repaint();
218 |
219 | switch (reason) {
220 | case SINGLE_WINNER:
221 | addMessage(nRoundNumber,
222 | "Session over: The winner is " + winners + "!");
223 | break;
224 | case MAX_ROUND_REACHED:
225 | addMessage(nRoundNumber,
226 | "Maximum round reached: The winners are " + winners + "!");
227 | break;
228 | case ABORTED:
229 | addMessage(nRoundNumber,
230 | "Session aborted: The winners are " + winners + "!");
231 | break;
232 | default:
233 | throw new RuntimeException();
234 | }
235 | }
236 |
237 | /** @see CompetitionEventListener#onRound(int) */
238 | public void onRound(int round) {
239 | nRoundNumber = round;
240 | if ((nRoundNumber % 1000) == 0) {
241 | roundNumber.setText(Integer.toString(nRoundNumber));
242 | roundNumber.repaint();
243 | }
244 | btnCpuState.setEnabled(true); //in case we open the window during a match
245 | btnPause.setEnabled(true);
246 | }
247 |
248 | /** @see CompetitionEventListener#onWarriorBirth(String) */
249 | public void onWarriorBirth(String warriorName) {
250 | addMessage(nRoundNumber, warriorName + " enters the arena.");
251 | nameListModel.addElement(new WarriorInfo(warriorName));
252 | }
253 |
254 | /** @see CompetitionEventListener#onWarriorDeath(String) */
255 | public void onWarriorDeath(String warriorName, String reason) {
256 | addMessage(nRoundNumber, warriorName + " died due to " + reason + ".");
257 | Enumeration namesListElements = nameListModel.elements();
258 | while(namesListElements.hasMoreElements()) {
259 | WarriorInfo info = (WarriorInfo) namesListElements.nextElement();
260 | if (info.name.equals(warriorName)) {
261 | info.alive = false;
262 | break;
263 | }
264 | }
265 |
266 | // a bit bogus... just to make the list refresh and show the new status.
267 | SwingUtilities.invokeLater(new Runnable() {
268 | public void run() {
269 | nameList.repaint();
270 | }
271 | });
272 | }
273 |
274 | /**
275 | * A renderer for the names on the warrior list.
276 | * Paints each warrior with its color and uses strikeout to show
277 | * dead warriors.
278 | */
279 | class NameCellRenderer extends JLabel implements ListCellRenderer {
280 | private static final long serialVersionUID = 1L;
281 |
282 | private static final int FONT_SIZE = 20;
283 |
284 | /**
285 | * Construct a name cell renderer
286 | * Set font size to FONT_SIZE.
287 | */
288 | public NameCellRenderer() {
289 | setFont(new Font("Tahoma", Font.PLAIN, FONT_SIZE));
290 | }
291 |
292 | /**
293 | * @see javax.swing.ListCellRenderer#getListCellRendererComponent(javax.swing.JList, java.lang.Object, int, boolean, boolean)
294 | */
295 | public Component getListCellRendererComponent(JList list, Object value,
296 | int index, boolean isSelected, boolean cellHasFocus) {
297 | WarriorInfo info = (WarriorInfo)value;
298 | /*
299 | float warriorScore = m_warSession.m_scoreBoard.getScore(warriorName);
300 | warriorScore = (float)((int)(warriorScore * 100)) / 100;
301 | */
302 | String text = info.name;// + " (" + warriorScore + ")";
303 | if (!info.alive) {
304 | // strike out dead warriors
305 | text = "" + text + "";
306 | }
307 | setText(text);
308 | setForeground(warCanvas.getColorForWarrior(index));
309 | return this;
310 | }
311 | }
312 |
313 | public void onCompetitionStart() {
314 | btnCpuState.setEnabled(true);
315 | btnPause.setEnabled(true);
316 | }
317 |
318 | public void onCompetitionEnd() {
319 | btnCpuState.setEnabled(false);
320 | btnPause.setEnabled(false);
321 | }
322 |
323 | class WarriorInfo {
324 | String name;
325 | boolean alive;
326 |
327 | public WarriorInfo(String name) {
328 | this.name= name;
329 | this.alive = true;
330 | }
331 |
332 | @Override
333 | public String toString() {
334 | return name;
335 | }
336 |
337 | @Override
338 | public boolean equals(Object obj) {
339 | return (obj!=null) && (obj instanceof String) &&
340 | (((String)obj).equals(name));
341 | }
342 | }
343 |
344 | @Override
345 | public void onEndRound() {
346 | this.warCanvas.deletePointers();
347 | for (int i = 0; i < this.competition.getCurrentWar().getNumWarriors(); i++)
348 | if (this.competition.getCurrentWar().getWarrior(i).isAlive()) {
349 | short ip = this.competition.getCurrentWar().getWarrior(i).getCpuState().getIP();
350 | short cs = this.competition.getCurrentWar().getWarrior(i).getCpuState().getCS();
351 |
352 | int ipInsideArena = new RealModeAddress(cs, ip).getLinearAddress() - 0x10000;
353 |
354 | this.warCanvas.paintPointer((char) ipInsideArena,(byte) i);
355 | }
356 | }
357 |
358 | @Override
359 | public void dispose() {
360 |
361 | // bug fix - event casted while window is being disposed FIXME find a
362 | // better solution
363 | this.competition.getCurrentWar().pause();
364 | try {
365 | Thread.sleep(300);
366 | } catch (Exception e) {
367 |
368 | }
369 | this.competition.removeCompetitionEventListener(this);
370 | this.competition.removeMemoryEventLister(this);
371 | this.competition.getCurrentWar().resume();
372 |
373 | try {
374 | this.cpuFrame.dispose();
375 | } catch (Exception e) {
376 | }
377 | // restoring maximum speed
378 | competition.getCurrentWar().resume();
379 | competition.setSpeed(Competition.MAXIMUM_SPEED);
380 | super.dispose();
381 | }
382 |
383 | @Override
384 | public void addressAtMouseLocationRequested(int address) {
385 | RealModeAddress tmp = new RealModeAddress(War.ARENA_SEGMENT, (short) address);
386 | byte data = this.competition.getCurrentWar().getMemory().readByte(tmp);
387 |
388 | // Warrior w = this.competition.getCurrentWar().getNumWarriors()
389 |
390 | this.addressFiled.setText(Integer.toHexString(address).toUpperCase()
391 | + ": " + String.format("%02X", data).toUpperCase());
392 |
393 | if (memoryFrame == null || !memoryFrame.isVisible()) {
394 | memoryFrame = new MemoryFrame(competition, tmp.getLinearAddress());
395 | this.competition.addCompetitionEventListener(memoryFrame);
396 | }
397 | else
398 | memoryFrame.refresh(tmp.getLinearAddress());
399 | }
400 |
401 | }
402 |
--------------------------------------------------------------------------------
/src/main/java/il/co/codeguru/corewars8086/memory/AbstractRealModeMemory.java:
--------------------------------------------------------------------------------
1 | package il.co.codeguru.corewars8086.memory;
2 |
3 | import il.co.codeguru.corewars8086.utils.Unsigned;
4 |
5 | /**
6 | * Base class for classes implementing the RealModeMemory interface, which
7 | * provides simple implementation of the 'word' methods using the 'byte' methods.
8 | *
9 | * @author DL
10 | */
11 | public abstract class AbstractRealModeMemory implements RealModeMemory {
12 |
13 | /**
14 | * Reads a single byte from the specified address.
15 | *
16 | * @param address Real-mode address to read from.
17 | * @return the read byte.
18 | *
19 | * @throws MemoryException on any error.
20 | */
21 | public abstract byte readByte(RealModeAddress address) throws MemoryException;
22 |
23 | /**
24 | * Reads a single word from the specified address.
25 | *
26 | * @param address Real-mode address to read from.
27 | * @return the read word.
28 | *
29 | * @throws MemoryException on any error.
30 | */
31 | public short readWord(RealModeAddress address) throws MemoryException {
32 | // read low word
33 | byte low = readByte(address);
34 |
35 | // read high word
36 | RealModeAddress nextAddress = new RealModeAddress(
37 | address.getSegment(), (short)(address.getOffset() + 1));
38 | byte high = readByte(nextAddress);
39 |
40 | return (short)((Unsigned.unsignedByte(high) << 8) |
41 | Unsigned.unsignedByte(low));
42 | }
43 |
44 | /**
45 | * Writes a single byte to the specified address.
46 | *
47 | * @param address Real-mode address to write to.
48 | * @param value Data to write.
49 | *
50 | * @throws MemoryException on any error.
51 | */
52 | public abstract void writeByte(RealModeAddress address, byte value)
53 | throws MemoryException;
54 |
55 | /**
56 | * Writes a single word to the specified address.
57 | *
58 | * @param address Real-mode address to write to.
59 | * @param value Data to write.
60 | *
61 | * @throws MemoryException on any error.
62 | */
63 | public void writeWord(RealModeAddress address, short value)
64 | throws MemoryException {
65 |
66 | byte low = (byte)value;
67 | byte high = (byte)(value >> 8);
68 |
69 | // write low byte
70 | writeByte(address, low);
71 |
72 | // write high byte
73 | RealModeAddress nextAddress = new RealModeAddress(
74 | address.getSegment(), (short)(address.getOffset() + 1));
75 | writeByte(nextAddress, high);
76 | }
77 |
78 | /**
79 | * Reads a single byte from the specified address, in order to execute it.
80 | *
81 | * @param address Real-mode address to read from.
82 | * @return the read byte.
83 | *
84 | * @throws MemoryException on any error.
85 | */
86 | public abstract byte readExecuteByte(RealModeAddress address)
87 | throws MemoryException;
88 |
89 | /**
90 | * Reads a single word from the specified address, in order to execute it.
91 | *
92 | * @param address Real-mode address to read from.
93 | * @return the read word.
94 | *
95 | * @throws MemoryException on any error.
96 | */
97 | public short readExecuteWord(RealModeAddress address) throws MemoryException {
98 | // read low word
99 | byte low = readExecuteByte(address);
100 |
101 | // read high word
102 | RealModeAddress nextAddress = new RealModeAddress(
103 | address.getSegment(), (short)(address.getOffset() + 1));
104 | byte high = readExecuteByte(nextAddress);
105 |
106 | return (short)((Unsigned.unsignedByte(high) << 8) |
107 | Unsigned.unsignedByte(low));
108 | }
109 | }
--------------------------------------------------------------------------------
/src/main/java/il/co/codeguru/corewars8086/memory/MemoryEventListener.java:
--------------------------------------------------------------------------------
1 | package il.co.codeguru.corewars8086.memory;
2 |
3 | import java.util.EventListener;
4 |
5 | /**
6 | * Defines an interface for memory listeners
7 | *
8 | * @author BS
9 | */
10 | public interface MemoryEventListener extends EventListener {
11 | /**
12 | * Called when a byte is written to memory
13 | * @param address
14 | */
15 | void onMemoryWrite(RealModeAddress address);
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/java/il/co/codeguru/corewars8086/memory/MemoryException.java:
--------------------------------------------------------------------------------
1 | package il.co.codeguru.corewars8086.memory;
2 |
3 | /**
4 | * Base class for all Exceptions thrown by the RealModeMemory classes.
5 | *
6 | * @author DL
7 | */
8 | public class MemoryException extends Exception {
9 | private static final long serialVersionUID = 1L;
10 | }
11 |
--------------------------------------------------------------------------------
/src/main/java/il/co/codeguru/corewars8086/memory/RealModeAddress.java:
--------------------------------------------------------------------------------
1 | package il.co.codeguru.corewars8086.memory;
2 |
3 | import il.co.codeguru.corewars8086.utils.Unsigned;
4 |
5 | /**
6 | * Wrapper class for a Real-Mode segment:offset address.
7 | *
8 | * @author DL
9 | */
10 | public class RealModeAddress {
11 |
12 | /**
13 | * Constructor from segment:offset.
14 | *
15 | * @param segment 16bit Real-mode segment.
16 | * @param offset 16bit Real-mode offset.
17 | */
18 | public RealModeAddress(short segment, short offset) {
19 | m_segment = segment;
20 | m_offset = offset;
21 |
22 | int unsignedSegment = Unsigned.unsignedShort(m_segment);
23 | int unsignedOffset = Unsigned.unsignedShort(m_offset);
24 |
25 | int linearAddressFull = unsignedSegment * PARAGRAPH_SIZE + unsignedOffset;
26 | m_linearAddress = linearAddressFull % MEMORY_SIZE;
27 | }
28 |
29 | /**
30 | * Constructor from linear address.
31 | *
32 | * The 'segment' part will be the highest possible, e.g.:
33 | * 12345h -> 1234:0005h
34 | *
35 | * @param linearAddress 32bit linear address.
36 | */
37 | public RealModeAddress(int linearAddress) {
38 | linearAddress %= MEMORY_SIZE;
39 |
40 | int unsignedSegment = Unsigned.unsignedShort(linearAddress / PARAGRAPH_SIZE);
41 | int unsignedOffset = Unsigned.unsignedShort(
42 | (linearAddress - (unsignedSegment*PARAGRAPH_SIZE)));
43 |
44 | m_segment = (short)unsignedSegment;
45 | m_offset = (short)unsignedOffset;
46 |
47 | m_linearAddress = linearAddress;
48 | }
49 |
50 | /**
51 | * @return 16bit Real-Mode segment.
52 | */
53 | public short getSegment() {
54 | return m_segment;
55 | }
56 |
57 | /**
58 | * @return 16bit Real-Mode offset.
59 | */
60 | public short getOffset() {
61 | return m_offset;
62 | }
63 |
64 | /**
65 | * @return 32bit linear address.
66 | */
67 | public int getLinearAddress() {
68 | return m_linearAddress;
69 | }
70 |
71 | /** Various real-mode memory constants. */
72 | public static final int NUM_PARAGRAPHS = 64 * 1024;
73 | public static final int PARAGRAPH_SIZE = 0x10;
74 | public static final int PARAGRAPHS_IN_SEGMENT = 0x1000;
75 | public static final int MEMORY_SIZE = NUM_PARAGRAPHS * PARAGRAPH_SIZE;
76 |
77 | /** 16bit Real-Mode segment. */
78 | private final short m_segment;
79 |
80 | /** 16bit Real-Mode offset. */
81 | private final short m_offset;
82 |
83 | /** cached linear representation of segment and offset */
84 | private int m_linearAddress;
85 | }
--------------------------------------------------------------------------------
/src/main/java/il/co/codeguru/corewars8086/memory/RealModeMemory.java:
--------------------------------------------------------------------------------
1 | package il.co.codeguru.corewars8086.memory;
2 |
3 | /**
4 | * Interface for 16bit Real-Mode memory.
5 | *
6 | * @author DL
7 | */
8 | public interface RealModeMemory {
9 |
10 | /**
11 | * Reads a single byte from the specified address.
12 | *
13 | * @param address Real-mode address to read from.
14 | * @return the read byte.
15 | *
16 | * @throws MemoryException on any error.
17 | */
18 | public abstract byte readByte(RealModeAddress address) throws MemoryException;
19 |
20 | /**
21 | * Reads a single word from the specified address.
22 | *
23 | * @param address Real-mode address to read from.
24 | * @return the read word.
25 | *
26 | * @throws MemoryException on any error.
27 | */
28 | public abstract short readWord(RealModeAddress address) throws MemoryException;
29 |
30 | /**
31 | * Writes a single byte to the specified address.
32 | *
33 | * @param address Real-mode address to write to.
34 | * @param value Data to write.
35 | *
36 | * @throws MemoryException on any error.
37 | */
38 | public abstract void writeByte(RealModeAddress address, byte value)
39 | throws MemoryException;
40 |
41 | /**
42 | * Writes a single word to the specified address.
43 | *
44 | * @param address Real-mode address to write to.
45 | * @param value Data to write.
46 | *
47 | * @throws MemoryException on any error.
48 | */
49 | public abstract void writeWord(RealModeAddress address, short value)
50 | throws MemoryException;
51 |
52 | /**
53 | * Reads a single byte from the specified address, in order to execute it.
54 | *
55 | * @param address Real-mode address to read from.
56 | * @return the read byte.
57 | *
58 | * @throws MemoryException on any error.
59 | */
60 | public abstract byte readExecuteByte(RealModeAddress address)
61 | throws MemoryException;
62 |
63 | /**
64 | * Reads a single word from the specified address, in order to execute it.
65 | *
66 | * @param address Real-mode address to read from.
67 | * @return the read word.
68 | *
69 | * @throws MemoryException on any error.
70 | */
71 | public abstract short readExecuteWord(RealModeAddress address)
72 | throws MemoryException;
73 | }
74 |
--------------------------------------------------------------------------------
/src/main/java/il/co/codeguru/corewars8086/memory/RealModeMemoryImpl.java:
--------------------------------------------------------------------------------
1 | package il.co.codeguru.corewars8086.memory;
2 |
3 | /**
4 | * Implements the RealModeMemory interface using a buffer.
5 | *
6 | * @author DL
7 | */
8 | public class RealModeMemoryImpl extends AbstractRealModeMemory {
9 |
10 | /** Listener to memory events */
11 | private MemoryEventListener listener;
12 |
13 | /** Actual memory data */
14 | private byte[] m_data;
15 |
16 | /**
17 | * Constructor.
18 | */
19 | public RealModeMemoryImpl() {
20 | m_data = new byte[RealModeAddress.MEMORY_SIZE];
21 | }
22 |
23 | /**
24 | * Reads a single byte from the specified address.
25 | *
26 | * @param address Real-mode address to read from.
27 | * @return the read byte.
28 | *
29 | * @throws MemoryException on any error.
30 | */
31 | public byte readByte(RealModeAddress address) {
32 | return m_data[address.getLinearAddress()];
33 | }
34 |
35 | /**
36 | * Writes a single byte to the specified address.
37 | *
38 | * @param address Real-mode address to write to.
39 | * @param value Data to write.
40 | *
41 | * @throws MemoryException on any error.
42 | */
43 | public void writeByte(RealModeAddress address, byte value) {
44 | m_data[address.getLinearAddress()] = value;
45 | if (listener != null) {
46 | listener.onMemoryWrite(address);
47 | }
48 | }
49 |
50 | /**
51 | * Reads a single byte from the specified address, in order to execute it.
52 | *
53 | * @param address Real-mode address to read from.
54 | * @return the read byte.
55 | *
56 | * @throws MemoryException on any error.
57 | */
58 | public byte readExecuteByte(RealModeAddress address) {
59 | return m_data[address.getLinearAddress()];
60 | }
61 |
62 | /**
63 | * @return Returns the listener.
64 | */
65 | public MemoryEventListener getListener() {
66 | return listener;
67 | }
68 | /**
69 | * @param listener The listener to set.
70 | */
71 | public void setListener(MemoryEventListener listener) {
72 | this.listener = listener;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/main/java/il/co/codeguru/corewars8086/memory/RealModeMemoryRegion.java:
--------------------------------------------------------------------------------
1 | package il.co.codeguru.corewars8086.memory;
2 |
3 | /**
4 | * Memory region (start address, end address)
5 | *
6 | * @author DL
7 | */
8 | public class RealModeMemoryRegion {
9 |
10 | /**
11 | * Constructor.
12 | *
13 | * @param start Region's start address.
14 | * @param end Region's end address.
15 | */
16 | public RealModeMemoryRegion(RealModeAddress start, RealModeAddress end) {
17 | m_start = start;
18 | m_end = end;
19 | }
20 |
21 | /**
22 | * Returns whether or not a given address is within the region.
23 | *
24 | * @param address Address to check.
25 | * @return whether or not the given address is within the region.
26 | */
27 | public boolean isInRegion(RealModeAddress address) {
28 | final int start = m_start.getLinearAddress();
29 | final int end = m_end.getLinearAddress();
30 | final int asked = address.getLinearAddress();
31 |
32 | return ((asked >= start) && (asked <= end));
33 | }
34 |
35 | /** Region's start address */
36 | private final RealModeAddress m_start;
37 | /** Region's end address */
38 | private final RealModeAddress m_end;
39 | }
--------------------------------------------------------------------------------
/src/main/java/il/co/codeguru/corewars8086/memory/RestrictedAccessRealModeMemory.java:
--------------------------------------------------------------------------------
1 | package il.co.codeguru.corewars8086.memory;
2 |
3 | /**
4 | * Implementation of the RealModeMemory interface which limits memory access
5 | * to given regions of the memory.
6 | *
7 | * @author DL
8 | */
9 | public class RestrictedAccessRealModeMemory extends AbstractRealModeMemory {
10 |
11 | /**
12 | * Constructor.
13 | *
14 | * @param memory Wrapped RealModeMemory implementation.
15 | * @param readAccessRegions Reading from these regions is allowed.
16 | * @param writeAccessRegions Writing to these regions is allowed.
17 | * @param executeAccessRegions Executing these regions is allowed.
18 | */
19 | public RestrictedAccessRealModeMemory(
20 | RealModeMemory memory,
21 | RealModeMemoryRegion[] readAccessRegions,
22 | RealModeMemoryRegion[] writeAccessRegions,
23 | RealModeMemoryRegion[] executeAccessRegions) {
24 |
25 | m_memory = memory;
26 | m_readAccessRegions = readAccessRegions;
27 | m_writeAccessRegions = writeAccessRegions;
28 | m_executeAccessRegions = executeAccessRegions;
29 | }
30 |
31 | /**
32 | * Reads a single byte from the specified address.
33 | *
34 | * @param address Real-mode address to read from.
35 | * @return the read byte.
36 | *
37 | * @throws MemoryException if reading is not allowed from this address.
38 | */
39 | public byte readByte(RealModeAddress address) throws MemoryException {
40 | // is reading allowed from this address ?
41 | if (!isAddressInRegions(m_readAccessRegions, address)) {
42 | throw new MemoryException();
43 | }
44 |
45 | return m_memory.readByte(address);
46 | }
47 |
48 | /**
49 | * Writes a single byte to the specified address.
50 | *
51 | * @param address Real-mode address to write to.
52 | * @param value Data to write.
53 | *
54 | * @throws MemoryException if writing is not allowed to this address.
55 | */
56 | public void writeByte(RealModeAddress address, byte value) throws MemoryException {
57 | // is writing allowed to this address ?
58 | if (!isAddressInRegions(m_writeAccessRegions, address)) {
59 | throw new MemoryException();
60 | }
61 |
62 | m_memory.writeByte(address, value);
63 | }
64 |
65 | /**
66 | * Reads a single byte from the specified address, in order to execute it.
67 | *
68 | * @param address Real-mode address to read from.
69 | * @return the read byte.
70 | *
71 | * @throws MemoryException if reading is not allowed from this address.
72 | */
73 | public byte readExecuteByte(RealModeAddress address) throws MemoryException {
74 | // is reading allowed from this address ?
75 | if (!isAddressInRegions(m_executeAccessRegions, address)) {
76 | throw new MemoryException();
77 | }
78 |
79 | return m_memory.readExecuteByte(address);
80 | }
81 |
82 | /**
83 | * Checks whether or not a given address is within at least a single
84 | * region in an array of regions.
85 | *
86 | * @param regions Regions array to match address against.
87 | * @param address Address to check.
88 | * @return whether or not the address is within at least one of the regions.
89 | */
90 | private boolean isAddressInRegions(
91 | RealModeMemoryRegion[] regions, RealModeAddress address) {
92 |
93 | // iterate all regions, attempt to match address
94 | boolean found = false;
95 | for (int i = 0; i < regions.length; ++i) {
96 | if (regions[i].isInRegion(address)) {
97 | found = true;
98 | break;
99 | }
100 | }
101 |
102 | return found;
103 | }
104 |
105 | /** Wrapped RealModeMemory implementation */
106 | private final RealModeMemory m_memory;
107 | /** Reading from these regions is allowed */
108 | private final RealModeMemoryRegion[] m_readAccessRegions;
109 | /** Writing to these regions is allowed */
110 | private final RealModeMemoryRegion[] m_writeAccessRegions;
111 | /** Executing these regions is allowed */
112 | private final RealModeMemoryRegion[] m_executeAccessRegions;
113 | }
--------------------------------------------------------------------------------
/src/main/java/il/co/codeguru/corewars8086/utils/Disassembler.java:
--------------------------------------------------------------------------------
1 | package il.co.codeguru.corewars8086.utils;
2 |
3 | import java.io.BufferedOutputStream;
4 | import java.io.BufferedReader;
5 | import java.io.File;
6 | import java.io.FileOutputStream;
7 | import java.io.IOException;
8 | import java.io.InputStream;
9 | import java.io.InputStreamReader;
10 | import java.nio.file.Files;
11 |
12 | public class Disassembler {
13 |
14 | public static String disassembler(byte[] bytes) throws Exception {
15 |
16 | File root = new File(Disassembler.class.getProtectionDomain().getCodeSource().getLocation().getPath()).getParentFile();
17 | File tempfile = new File( root + "\\temp_disassemblr");
18 | BufferedOutputStream bos = new BufferedOutputStream(Files.newOutputStream(tempfile.toPath()));
19 | bos.write(bytes);
20 | bos.flush();
21 | bos.close();
22 |
23 | ProcessBuilder pb = new ProcessBuilder("ndisasm", "-b 16","-pintel" , tempfile + "");
24 | pb.redirectOutput();
25 | Process p = pb.start();
26 |
27 | String error = getStringFromInput(p.getErrorStream());
28 | if(error == null)
29 | throw new Exception(error);
30 |
31 | String result = getStringFromInput(p.getInputStream());
32 |
33 | StringBuilder sb = new StringBuilder();
34 | String[] lines = result.split("\n");
35 | for (String line : lines) {
36 | String args[] = line.split("\\s\\s+");
37 | StringBuffer opcode = new StringBuffer(args[1]);
38 | while(opcode.length() < 10) opcode.append(" ");
39 | sb.append(opcode).append(args[2]).append("\n");
40 |
41 | }
42 |
43 | return sb.toString();
44 | }
45 |
46 | private static String getStringFromInput(InputStream s) throws IOException {
47 | BufferedReader br = new BufferedReader(new InputStreamReader(s));
48 | StringBuilder builder = new StringBuilder();
49 | String line = null;
50 | while ( (line = br.readLine()) != null) {
51 | builder.append(line);
52 | builder.append(System.getProperty("line.separator"));
53 | }
54 | String result = builder.toString();
55 | return result;
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/src/main/java/il/co/codeguru/corewars8086/utils/EventMulticaster.java:
--------------------------------------------------------------------------------
1 | package il.co.codeguru.corewars8086.utils;
2 |
3 | import java.lang.reflect.InvocationHandler;
4 | import java.lang.reflect.Method;
5 | import java.lang.reflect.Proxy;
6 | import java.util.*;
7 |
8 | /**
9 | * An event multicaster which broadcasts Events to a number of listeners.
10 | * @author BS
11 | */
12 | public class EventMulticaster {
13 |
14 | private Class mListenerInterface;
15 | private EventListener mProxy;
16 | private Set