├── .github
└── FUNDING.yml
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── docs
├── GBCribSheet000129.pdf
├── GB_CPU_Manual.pdf
└── pandocs.htm
├── gb
├── core
│ └── core.go
├── cpu
│ ├── cb_opcodes.go
│ ├── common.go
│ ├── cpu.go
│ ├── functors.go
│ ├── normal_opcodes.go
│ ├── registers.go
│ ├── serial.go
│ ├── timers.go
│ └── timing.go
├── input
│ └── input.go
├── mapper
│ └── mapper.go
├── mbcs
│ ├── common.go
│ ├── io.go
│ ├── iovalues.go
│ ├── mbc1.go
│ └── romonly.go
├── util
│ ├── constants.go
│ ├── functions.go
│ └── types.go
└── video
│ ├── background.go
│ ├── sprites.go
│ ├── video.go
│ └── window.go
├── glfw
└── glfw.go
├── main.go
├── opengl
└── opengl.go
├── screenshots
├── screenshot1.png
└── screenshot2.png
├── slides
└── Golang Emus.pdf
└── test_roms
├── cpu_instrs
├── cpu_instrs.gb
├── individual
│ ├── 01-special.gb
│ ├── 02-interrupts.gb
│ ├── 03-op sp,hl.gb
│ ├── 04-op r,imm.gb
│ ├── 05-op rp.gb
│ ├── 06-ld r,r.gb
│ ├── 07-jr,jp,call,ret,rst.gb
│ ├── 08-misc instrs.gb
│ ├── 09-op r,r.gb
│ ├── 10-bit ops.gb
│ └── 11-op a,(hl).gb
└── readme.txt
├── instr_timing
├── instr_timing.gb
└── readme.txt
├── testgb
├── PUZZLE.GB
├── RPN.GB
├── SOUND.GB
├── SPACE.GB
├── SPRITE.GB
└── TEST.GB
└── workshop.rom
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: drhelius
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Compiled Object files, Static and Dynamic libs (Shared Objects)
2 | *.o
3 | *.a
4 | *.so
5 |
6 | # Folders
7 | _obj
8 | _test
9 |
10 | # Architecture specific extensions/prefixes
11 | *.[568vq]
12 | [568vq].out
13 |
14 | *.cgo1.go
15 | *.cgo2.c
16 | _cgo_defun.c
17 | _cgo_gotypes.go
18 | _cgo_export.*
19 |
20 | _testmain.go
21 |
22 | *.exe
23 | *.test
24 | *.prof
25 | *.zip
26 | *.gb
27 |
28 | debug
29 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: go
2 |
3 | before_install:
4 | - sudo apt-get -qq update
5 | - sudo apt-get install -y libgl1-mesa-dev xorg-dev
6 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 | {one line to give the program's name and a brief idea of what it does.}
635 | Copyright (C) {year} {name of author}
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | {project} Copyright (C) {year} {fullname}
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | demo-emulator [](https://travis-ci.org/drhelius/demo-emulator)
2 | =======
3 | Copyright © 2016 by Ignacio Sanchez
4 |
5 | Nintendo Game Boy emulator written in Go to be used in workshops about emulator programming.
6 |
7 | Follow me on Twitter for updates: http://twitter.com/drhelius
8 |
9 |  
10 |
11 | Presentation
12 | ------------
13 |
14 | https://speakerdeck.com/drhelius/8-bit-emulator-programming-with-go
15 |
16 | Requirements
17 | ------------
18 |
19 | Before you start, make sure you have Go installed and ready to build applications: https://golang.org/doc/install
20 |
21 | Once you have a working Go environment you'll need to install the following dependecies:
22 |
23 | #### Windows
24 |
25 | - GCC 64 bit installed: http://tdm-gcc.tdragon.net/download
26 |
27 | #### Linux
28 |
29 | - Ubuntu: sudo apt-get install build-essential libgl1-mesa-dev xorg-dev
30 | - Fedora: sudo dnf install @development-tools libX11-devel libXcursor-devel libXrandr-devel libXinerama-devel mesa-libGL-devel libXi-devel
31 |
32 | #### Mac OS X
33 |
34 | - You need Xcode or Command Line Tools for Xcode (xcode-select --install
) for required headers and libraries.
35 |
36 | Building
37 | --------
38 | Run this command to let Go download and build the sources. You don't even need to clone this repo, Go will do it for you:
39 |
40 | ```
41 | go get -u github.com/drhelius/demo-emulator
42 | ```
43 |
44 | Running
45 | -------
46 | Once built you can find the emulator binary in $GOPATH/bin
. Use it with the -rom
argument in order to load a Game Boy ROM file:
47 |
48 | ```
49 | $GOPATH/bin/demo-emulator -rom path/to/your_rom.gb
50 | ```
51 |
52 | Controls
53 | --------
54 | ```
55 | START = Enter
56 | SELECT = Space
57 | A = S
58 | B = A
59 | Pad = Cursors
60 | ```
61 |
62 |
63 | License
64 | -------
65 |
66 | This program is free software: you can redistribute it and/or modify
67 | it under the terms of the GNU General Public License as published by
68 | the Free Software Foundation, either version 3 of the License, or
69 | any later version.
70 |
71 | This program is distributed in the hope that it will be useful,
72 | but WITHOUT ANY WARRANTY; without even the implied warranty of
73 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
74 | GNU General Public License for more details.
75 |
76 | You should have received a copy of the GNU General Public License
77 | along with this program. If not, see http://www.gnu.org/licenses/
78 |
--------------------------------------------------------------------------------
/docs/GBCribSheet000129.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drhelius/demo-emulator/20fbe5669ec274ea9fdca7eb805a5c789519a93c/docs/GBCribSheet000129.pdf
--------------------------------------------------------------------------------
/docs/GB_CPU_Manual.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drhelius/demo-emulator/20fbe5669ec274ea9fdca7eb805a5c789519a93c/docs/GB_CPU_Manual.pdf
--------------------------------------------------------------------------------
/gb/core/core.go:
--------------------------------------------------------------------------------
1 | package core
2 |
3 | import (
4 | "fmt"
5 | "io/ioutil"
6 |
7 | "github.com/drhelius/demo-emulator/gb/cpu"
8 | "github.com/drhelius/demo-emulator/gb/input"
9 | "github.com/drhelius/demo-emulator/gb/mapper"
10 | "github.com/drhelius/demo-emulator/gb/mbcs"
11 | "github.com/drhelius/demo-emulator/gb/util"
12 | "github.com/drhelius/demo-emulator/gb/video"
13 | )
14 |
15 | var pallete = [12]uint8{
16 | // R G B
17 | 0x87, 0x96, 0x03, // color 0
18 | 0x4d, 0x6b, 0x03, // color 1
19 | 0x2b, 0x55, 0x03, // color 2
20 | 0x14, 0x44, 0x03} // color 3
21 |
22 | // RunToVBlank runs a single frame of the emulator
23 | // The emulator must run at 60fps
24 | func RunToVBlank(colorFrameBuffer []uint8) {
25 |
26 | // keep updating each system
27 | // until the vblank is reached
28 | for vblank := false; !vblank; {
29 | clockCycles := cpu.Tick()
30 | vblank = video.Tick(clockCycles)
31 | input.Tick(clockCycles)
32 | }
33 |
34 | // the frame buffer of the Game Boy encodes color
35 | // as a four shades of gray (or green)
36 | // we have to transform these 4 colors to RGB by using
37 | // a predefined pallete
38 | for i, pixelCount := 0, util.GbWidth*util.GbHeight; i < pixelCount; i++ {
39 | colorFrameBuffer[i*4] = pallete[video.GbFrameBuffer[i]*3] // red
40 | colorFrameBuffer[(i*4)+1] = pallete[(video.GbFrameBuffer[i]*3)+1] // green
41 | colorFrameBuffer[(i*4)+2] = pallete[(video.GbFrameBuffer[i]*3)+2] // blue
42 | }
43 | }
44 |
45 | // LoadROM loads a new rom into the emulator
46 | // this fucntion must be called before running RunToVBlank
47 | func LoadROM(filePath string) {
48 |
49 | fmt.Printf("loading ROM \"%s\"...\n", filePath)
50 |
51 | data, err := ioutil.ReadFile(filePath)
52 |
53 | if err != nil {
54 | panic(err)
55 | }
56 |
57 | cartType := data[0x147]
58 | var m mapper.Mapper
59 |
60 | // check the memory mapper of the ROM
61 | switch cartType {
62 | case 0x00:
63 | fmt.Println("found ROM")
64 | m = new(mbcs.RomOnly)
65 | case 0x08:
66 | fmt.Println("found ROM + SRAM")
67 | m = new(mbcs.RomOnly)
68 | case 0x09:
69 | fmt.Println("found ROM + SRAM + BATT")
70 | m = new(mbcs.RomOnly)
71 | case 0x01:
72 | fmt.Println("found MBC1")
73 | m = new(mbcs.MBC1)
74 | case 0x02:
75 | fmt.Println("found MBC1 + SRAM")
76 | m = new(mbcs.MBC1)
77 | case 0x03:
78 | fmt.Println("found MBC1 + SRAM + BATT")
79 | m = new(mbcs.MBC1)
80 | default:
81 | // MBC1 and ROM are the only cartridges supported
82 | panic(fmt.Sprintf("cartridge type not supported: %d", cartType))
83 | }
84 |
85 | // once we know which is the correct memory mapper
86 | // we inject it into the systems that use the memory
87 | m.Setup(data)
88 | cpu.SetMapper(m)
89 | video.SetMapper(m)
90 | }
91 |
92 | // ButtonPressed tells the emulator that a button has been pressed
93 | func ButtonPressed(button util.GameboyButton) {
94 | input.ButtonPressed(button)
95 | }
96 |
97 | // ButtonReleased tells the emulator that a button has been released
98 | func ButtonReleased(button util.GameboyButton) {
99 | input.ButtonReleased(button)
100 | }
101 |
--------------------------------------------------------------------------------
/gb/cpu/cb_opcodes.go:
--------------------------------------------------------------------------------
1 | package cpu
2 |
3 | func opcodeCB0x00() {
4 | // RLC B
5 | opcodesRLC(bc.GetHighReg())
6 | }
7 |
8 | func opcodeCB0x01() {
9 | // RLC C
10 | opcodesRLC(bc.GetLowReg())
11 | }
12 |
13 | func opcodeCB0x02() {
14 | // RLC D
15 | opcodesRLC(de.GetHighReg())
16 | }
17 |
18 | func opcodeCB0x03() {
19 | // RLC E
20 | opcodesRLC(de.GetLowReg())
21 | }
22 |
23 | func opcodeCB0x04() {
24 | // RLC H
25 | opcodesRLC(hl.GetHighReg())
26 | }
27 |
28 | func opcodeCB0x05() {
29 | // RLC L
30 | opcodesRLC(hl.GetLowReg())
31 | }
32 |
33 | func opcodeCB0x06() {
34 | // RLC (HL)
35 | opcodesRLCHL()
36 | }
37 |
38 | func opcodeCB0x07() {
39 | // RLC A
40 | opcodesRLC(af.GetHighReg())
41 | }
42 |
43 | func opcodeCB0x08() {
44 | // RRC B
45 | opcodesRRC(bc.GetHighReg())
46 | }
47 |
48 | func opcodeCB0x09() {
49 | // RRC C
50 | opcodesRRC(bc.GetLowReg())
51 | }
52 |
53 | func opcodeCB0x0A() {
54 | // RRC D
55 | opcodesRRC(de.GetHighReg())
56 | }
57 |
58 | func opcodeCB0x0B() {
59 | // RRC E
60 | opcodesRRC(de.GetLowReg())
61 | }
62 |
63 | func opcodeCB0x0C() {
64 | // RRC H
65 | opcodesRRC(hl.GetHighReg())
66 | }
67 |
68 | func opcodeCB0x0D() {
69 | // RRC L
70 | opcodesRRC(hl.GetLowReg())
71 | }
72 |
73 | func opcodeCB0x0E() {
74 | // RRC (HL)
75 | opcodesRRCHL()
76 | }
77 |
78 | func opcodeCB0x0F() {
79 | // RRC A
80 | opcodesRRC(af.GetHighReg())
81 | }
82 |
83 | func opcodeCB0x10() {
84 | // RL B
85 | opcodesRL(bc.GetHighReg())
86 | }
87 |
88 | func opcodeCB0x11() {
89 | // RL C
90 | opcodesRL(bc.GetLowReg())
91 | }
92 |
93 | func opcodeCB0x12() {
94 | // RL D
95 | opcodesRL(de.GetHighReg())
96 | }
97 |
98 | func opcodeCB0x13() {
99 | // RL E
100 | opcodesRL(de.GetLowReg())
101 | }
102 |
103 | func opcodeCB0x14() {
104 | // RL H
105 | opcodesRL(hl.GetHighReg())
106 | }
107 |
108 | func opcodeCB0x15() {
109 | // RL L
110 | opcodesRL(hl.GetLowReg())
111 | }
112 |
113 | func opcodeCB0x16() {
114 | // RL (HL)
115 | opcodesRLHL()
116 | }
117 |
118 | func opcodeCB0x17() {
119 | // RL A
120 | opcodesRL(af.GetHighReg())
121 | }
122 |
123 | func opcodeCB0x18() {
124 | // RR B
125 | opcodesRR(bc.GetHighReg())
126 | }
127 |
128 | func opcodeCB0x19() {
129 | // RR C
130 | opcodesRR(bc.GetLowReg())
131 | }
132 |
133 | func opcodeCB0x1A() {
134 | // RR D
135 | opcodesRR(de.GetHighReg())
136 | }
137 |
138 | func opcodeCB0x1B() {
139 | // RR E
140 | opcodesRR(de.GetLowReg())
141 | }
142 |
143 | func opcodeCB0x1C() {
144 | // RR H
145 | opcodesRR(hl.GetHighReg())
146 | }
147 |
148 | func opcodeCB0x1D() {
149 | // RR L
150 | opcodesRR(hl.GetLowReg())
151 | }
152 |
153 | func opcodeCB0x1E() {
154 | // RR (HL)
155 | opcodesRRHL()
156 | }
157 |
158 | func opcodeCB0x1F() {
159 | // RR A
160 | opcodesRR(af.GetHighReg())
161 | }
162 |
163 | func opcodeCB0x20() {
164 | // SLA B
165 | opcodesSLA(bc.GetHighReg())
166 | }
167 |
168 | func opcodeCB0x21() {
169 | // SLA C
170 | opcodesSLA(bc.GetLowReg())
171 | }
172 |
173 | func opcodeCB0x22() {
174 | // SLA D
175 | opcodesSLA(de.GetHighReg())
176 | }
177 |
178 | func opcodeCB0x23() {
179 | // SLA E
180 | opcodesSLA(de.GetLowReg())
181 | }
182 |
183 | func opcodeCB0x24() {
184 | // SLA H
185 | opcodesSLA(hl.GetHighReg())
186 | }
187 |
188 | func opcodeCB0x25() {
189 | // SLA L
190 | opcodesSLA(hl.GetLowReg())
191 | }
192 |
193 | func opcodeCB0x26() {
194 | // SLA (HL)
195 | opcodesSLAHL()
196 | }
197 |
198 | func opcodeCB0x27() {
199 | // SLA A
200 | opcodesSLA(af.GetHighReg())
201 | }
202 |
203 | func opcodeCB0x28() {
204 | // SRA B
205 | opcodesSRA(bc.GetHighReg())
206 | }
207 |
208 | func opcodeCB0x29() {
209 | // SRA C
210 | opcodesSRA(bc.GetLowReg())
211 | }
212 |
213 | func opcodeCB0x2A() {
214 | // SRA D
215 | opcodesSRA(de.GetHighReg())
216 | }
217 |
218 | func opcodeCB0x2B() {
219 | // SRA E
220 | opcodesSRA(de.GetLowReg())
221 | }
222 |
223 | func opcodeCB0x2C() {
224 | // SRA H
225 | opcodesSRA(hl.GetHighReg())
226 | }
227 |
228 | func opcodeCB0x2D() {
229 | // SRA L
230 | opcodesSRA(hl.GetLowReg())
231 | }
232 |
233 | func opcodeCB0x2E() {
234 | // SRA (HL)
235 | opcodesSRAHL()
236 | }
237 |
238 | func opcodeCB0x2F() {
239 | // SRA A
240 | opcodesSRA(af.GetHighReg())
241 | }
242 |
243 | func opcodeCB0x30() {
244 | // SWAP B
245 | opcodesSWAPReg(bc.GetHighReg())
246 | }
247 |
248 | func opcodeCB0x31() {
249 | // SWAP C
250 | opcodesSWAPReg(bc.GetLowReg())
251 | }
252 |
253 | func opcodeCB0x32() {
254 | // SWAP D
255 | opcodesSWAPReg(de.GetHighReg())
256 | }
257 |
258 | func opcodeCB0x33() {
259 | // SWAP E
260 | opcodesSWAPReg(de.GetLowReg())
261 | }
262 |
263 | func opcodeCB0x34() {
264 | // SWAP H
265 | opcodesSWAPReg(hl.GetHighReg())
266 | }
267 |
268 | func opcodeCB0x35() {
269 | // SWAP L
270 | opcodesSWAPReg(hl.GetLowReg())
271 | }
272 |
273 | func opcodeCB0x36() {
274 | // SWAP (HL)
275 | opcodesSWAPHL()
276 | }
277 |
278 | func opcodeCB0x37() {
279 | // SWAP A
280 | opcodesSWAPReg(af.GetHighReg())
281 | }
282 |
283 | func opcodeCB0x38() {
284 | // SRL B
285 | opcodesSRL(bc.GetHighReg())
286 | }
287 |
288 | func opcodeCB0x39() {
289 | // SRL C
290 | opcodesSRL(bc.GetLowReg())
291 | }
292 |
293 | func opcodeCB0x3A() {
294 | // SRL D
295 | opcodesSRL(de.GetHighReg())
296 | }
297 |
298 | func opcodeCB0x3B() {
299 | // SRL E
300 | opcodesSRL(de.GetLowReg())
301 | }
302 |
303 | func opcodeCB0x3C() {
304 | // SRL H
305 | opcodesSRL(hl.GetHighReg())
306 | }
307 |
308 | func opcodeCB0x3D() {
309 | // SRL L
310 | opcodesSRL(hl.GetLowReg())
311 | }
312 |
313 | func opcodeCB0x3E() {
314 | // SRL (HL)
315 | opcodesSRLHL()
316 | }
317 |
318 | func opcodeCB0x3F() {
319 | // SRL A
320 | opcodesSRL(af.GetHighReg())
321 | }
322 |
323 | func opcodeCB0x40() {
324 | // BIT 0 B
325 | opcodesBIT(bc.GetHighReg(), 0)
326 | }
327 |
328 | func opcodeCB0x41() {
329 | // BIT 0 C
330 | opcodesBIT(bc.GetLowReg(), 0)
331 | }
332 |
333 | func opcodeCB0x42() {
334 | // BIT 0 D
335 | opcodesBIT(de.GetHighReg(), 0)
336 | }
337 |
338 | func opcodeCB0x43() {
339 | // BIT 0 E
340 | opcodesBIT(de.GetLowReg(), 0)
341 | }
342 |
343 | func opcodeCB0x44() {
344 | // BIT 0 H
345 | opcodesBIT(hl.GetHighReg(), 0)
346 | }
347 |
348 | func opcodeCB0x45() {
349 | // BIT 0 L
350 | opcodesBIT(hl.GetLowReg(), 0)
351 | }
352 |
353 | func opcodeCB0x46() {
354 | // BIT 0 (HL)
355 | opcodesBITHL(0)
356 | }
357 |
358 | func opcodeCB0x47() {
359 | // BIT 0 A
360 | opcodesBIT(af.GetHighReg(), 0)
361 | }
362 |
363 | func opcodeCB0x48() {
364 | // BIT 1 B
365 | opcodesBIT(bc.GetHighReg(), 1)
366 | }
367 |
368 | func opcodeCB0x49() {
369 | // BIT 1 C
370 | opcodesBIT(bc.GetLowReg(), 1)
371 | }
372 |
373 | func opcodeCB0x4A() {
374 | // BIT 1 D
375 | opcodesBIT(de.GetHighReg(), 1)
376 | }
377 |
378 | func opcodeCB0x4B() {
379 | // BIT 1 E
380 | opcodesBIT(de.GetLowReg(), 1)
381 | }
382 |
383 | func opcodeCB0x4C() {
384 | // BIT 1 H
385 | opcodesBIT(hl.GetHighReg(), 1)
386 | }
387 |
388 | func opcodeCB0x4D() {
389 | // BIT 1 L
390 | opcodesBIT(hl.GetLowReg(), 1)
391 | }
392 |
393 | func opcodeCB0x4E() {
394 | // BIT 1 (HL)
395 | opcodesBITHL(1)
396 | }
397 |
398 | func opcodeCB0x4F() {
399 | // BIT 1 A
400 | opcodesBIT(af.GetHighReg(), 1)
401 | }
402 |
403 | func opcodeCB0x50() {
404 | // BIT 2 B
405 | opcodesBIT(bc.GetHighReg(), 2)
406 | }
407 |
408 | func opcodeCB0x51() {
409 | // BIT 2 C
410 | opcodesBIT(bc.GetLowReg(), 2)
411 | }
412 |
413 | func opcodeCB0x52() {
414 | // BIT 2 D
415 | opcodesBIT(de.GetHighReg(), 2)
416 | }
417 |
418 | func opcodeCB0x53() {
419 | // BIT 2 E
420 | opcodesBIT(de.GetLowReg(), 2)
421 | }
422 |
423 | func opcodeCB0x54() {
424 | // BIT 2 H
425 | opcodesBIT(hl.GetHighReg(), 2)
426 | }
427 |
428 | func opcodeCB0x55() {
429 | // BIT 2 L
430 | opcodesBIT(hl.GetLowReg(), 2)
431 | }
432 |
433 | func opcodeCB0x56() {
434 | // BIT 2 (HL)
435 | opcodesBITHL(2)
436 | }
437 |
438 | func opcodeCB0x57() {
439 | // BIT 2 A
440 | opcodesBIT(af.GetHighReg(), 2)
441 | }
442 |
443 | func opcodeCB0x58() {
444 | // BIT 3 B
445 | opcodesBIT(bc.GetHighReg(), 3)
446 | }
447 |
448 | func opcodeCB0x59() {
449 | // BIT 3 C
450 | opcodesBIT(bc.GetLowReg(), 3)
451 | }
452 |
453 | func opcodeCB0x5A() {
454 | // BIT 3 D
455 | opcodesBIT(de.GetHighReg(), 3)
456 | }
457 |
458 | func opcodeCB0x5B() {
459 | // BIT 3 E
460 | opcodesBIT(de.GetLowReg(), 3)
461 | }
462 |
463 | func opcodeCB0x5C() {
464 | // BIT 3 H
465 | opcodesBIT(hl.GetHighReg(), 3)
466 | }
467 |
468 | func opcodeCB0x5D() {
469 | // BIT 3 L
470 | opcodesBIT(hl.GetLowReg(), 3)
471 | }
472 |
473 | func opcodeCB0x5E() {
474 | // BIT 3 (HL)
475 | opcodesBITHL(3)
476 | }
477 |
478 | func opcodeCB0x5F() {
479 | // BIT 3 A
480 | opcodesBIT(af.GetHighReg(), 3)
481 | }
482 |
483 | func opcodeCB0x60() {
484 | // BIT 4 B
485 | opcodesBIT(bc.GetHighReg(), 4)
486 | }
487 |
488 | func opcodeCB0x61() {
489 | // BIT 4 C
490 | opcodesBIT(bc.GetLowReg(), 4)
491 | }
492 |
493 | func opcodeCB0x62() {
494 | // BIT 4 D
495 | opcodesBIT(de.GetHighReg(), 4)
496 | }
497 |
498 | func opcodeCB0x63() {
499 | // BIT 4 E
500 | opcodesBIT(de.GetLowReg(), 4)
501 | }
502 |
503 | func opcodeCB0x64() {
504 | // BIT 4 H
505 | opcodesBIT(hl.GetHighReg(), 4)
506 | }
507 |
508 | func opcodeCB0x65() {
509 | // BIT 4 L
510 | opcodesBIT(hl.GetLowReg(), 4)
511 | }
512 |
513 | func opcodeCB0x66() {
514 | // BIT 4 (HL)
515 | opcodesBITHL(4)
516 | }
517 |
518 | func opcodeCB0x67() {
519 | // BIT 4 A
520 | opcodesBIT(af.GetHighReg(), 4)
521 | }
522 |
523 | func opcodeCB0x68() {
524 | // BIT 5 B
525 | opcodesBIT(bc.GetHighReg(), 5)
526 | }
527 |
528 | func opcodeCB0x69() {
529 | // BIT 5 C
530 | opcodesBIT(bc.GetLowReg(), 5)
531 | }
532 |
533 | func opcodeCB0x6A() {
534 | // BIT 5 D
535 | opcodesBIT(de.GetHighReg(), 5)
536 | }
537 |
538 | func opcodeCB0x6B() {
539 | // BIT 5 E
540 | opcodesBIT(de.GetLowReg(), 5)
541 | }
542 |
543 | func opcodeCB0x6C() {
544 | // BIT 5 H
545 | opcodesBIT(hl.GetHighReg(), 5)
546 | }
547 |
548 | func opcodeCB0x6D() {
549 | // BIT 5 L
550 | opcodesBIT(hl.GetLowReg(), 5)
551 | }
552 |
553 | func opcodeCB0x6E() {
554 | // BIT 5 (HL)
555 | opcodesBITHL(5)
556 | }
557 |
558 | func opcodeCB0x6F() {
559 | // BIT 5 A
560 | opcodesBIT(af.GetHighReg(), 5)
561 | }
562 |
563 | func opcodeCB0x70() {
564 | // BIT 6 B
565 | opcodesBIT(bc.GetHighReg(), 6)
566 | }
567 |
568 | func opcodeCB0x71() {
569 | // BIT 6 C
570 | opcodesBIT(bc.GetLowReg(), 6)
571 | }
572 |
573 | func opcodeCB0x72() {
574 | // BIT 6 D
575 | opcodesBIT(de.GetHighReg(), 6)
576 | }
577 |
578 | func opcodeCB0x73() {
579 | // BIT 6 E
580 | opcodesBIT(de.GetLowReg(), 6)
581 | }
582 |
583 | func opcodeCB0x74() {
584 | // BIT 6 H
585 | opcodesBIT(hl.GetHighReg(), 6)
586 | }
587 |
588 | func opcodeCB0x75() {
589 | // BIT 6 L
590 | opcodesBIT(hl.GetLowReg(), 6)
591 | }
592 |
593 | func opcodeCB0x76() {
594 | // BIT 6 (HL)
595 | opcodesBITHL(6)
596 | }
597 |
598 | func opcodeCB0x77() {
599 | // BIT 6 A
600 | opcodesBIT(af.GetHighReg(), 6)
601 | }
602 |
603 | func opcodeCB0x78() {
604 | // BIT 7 B
605 | opcodesBIT(bc.GetHighReg(), 7)
606 | }
607 |
608 | func opcodeCB0x79() {
609 | // BIT 7 C
610 | opcodesBIT(bc.GetLowReg(), 7)
611 | }
612 |
613 | func opcodeCB0x7A() {
614 | // BIT 7 D
615 | opcodesBIT(de.GetHighReg(), 7)
616 | }
617 |
618 | func opcodeCB0x7B() {
619 | // BIT 7 E
620 | opcodesBIT(de.GetLowReg(), 7)
621 | }
622 |
623 | func opcodeCB0x7C() {
624 | // BIT 7 H
625 | opcodesBIT(hl.GetHighReg(), 7)
626 | }
627 |
628 | func opcodeCB0x7D() {
629 | // BIT 7 L
630 | opcodesBIT(hl.GetLowReg(), 7)
631 | }
632 |
633 | func opcodeCB0x7E() {
634 | // BIT 7 (HL)
635 | opcodesBITHL(7)
636 | }
637 |
638 | func opcodeCB0x7F() {
639 | // BIT 7 A
640 | opcodesBIT(af.GetHighReg(), 7)
641 | }
642 |
643 | func opcodeCB0x80() {
644 | // RES 0 B
645 | opcodesRES(bc.GetHighReg(), 0)
646 | }
647 |
648 | func opcodeCB0x81() {
649 | // RES 0 C
650 | opcodesRES(bc.GetLowReg(), 0)
651 | }
652 |
653 | func opcodeCB0x82() {
654 | // RES 0 D
655 | opcodesRES(de.GetHighReg(), 0)
656 | }
657 |
658 | func opcodeCB0x83() {
659 | // RES 0 E
660 | opcodesRES(de.GetLowReg(), 0)
661 | }
662 |
663 | func opcodeCB0x84() {
664 | // RES 0 H
665 | opcodesRES(hl.GetHighReg(), 0)
666 | }
667 |
668 | func opcodeCB0x85() {
669 | // RES 0 L
670 | opcodesRES(hl.GetLowReg(), 0)
671 | }
672 |
673 | func opcodeCB0x86() {
674 | // RES 0 (HL)
675 | opcodesRESHL(0)
676 | }
677 |
678 | func opcodeCB0x87() {
679 | // RES 0 A
680 | opcodesRES(af.GetHighReg(), 0)
681 | }
682 |
683 | func opcodeCB0x88() {
684 | // RES 1 B
685 | opcodesRES(bc.GetHighReg(), 1)
686 | }
687 |
688 | func opcodeCB0x89() {
689 | // RES 1 C
690 | opcodesRES(bc.GetLowReg(), 1)
691 | }
692 |
693 | func opcodeCB0x8A() {
694 | // RES 1 D
695 | opcodesRES(de.GetHighReg(), 1)
696 | }
697 |
698 | func opcodeCB0x8B() {
699 | // RES 1 E
700 | opcodesRES(de.GetLowReg(), 1)
701 | }
702 |
703 | func opcodeCB0x8C() {
704 | // RES 1 H
705 | opcodesRES(hl.GetHighReg(), 1)
706 | }
707 |
708 | func opcodeCB0x8D() {
709 | // RES 1 L
710 | opcodesRES(hl.GetLowReg(), 1)
711 | }
712 |
713 | func opcodeCB0x8E() {
714 | // RES 1 (HL)
715 | opcodesRESHL(1)
716 | }
717 |
718 | func opcodeCB0x8F() {
719 | // RES 1 A
720 | opcodesRES(af.GetHighReg(), 1)
721 | }
722 |
723 | func opcodeCB0x90() {
724 | // RES 2 B
725 | opcodesRES(bc.GetHighReg(), 2)
726 | }
727 |
728 | func opcodeCB0x91() {
729 | // RES 2 C
730 | opcodesRES(bc.GetLowReg(), 2)
731 | }
732 |
733 | func opcodeCB0x92() {
734 | // RES 2 D
735 | opcodesRES(de.GetHighReg(), 2)
736 | }
737 |
738 | func opcodeCB0x93() {
739 | // RES 2 E
740 | opcodesRES(de.GetLowReg(), 2)
741 | }
742 |
743 | func opcodeCB0x94() {
744 | // RES 2 H
745 | opcodesRES(hl.GetHighReg(), 2)
746 | }
747 |
748 | func opcodeCB0x95() {
749 | // RES 2 L
750 | opcodesRES(hl.GetLowReg(), 2)
751 | }
752 |
753 | func opcodeCB0x96() {
754 | // RES 2 (HL)
755 | opcodesRESHL(2)
756 | }
757 |
758 | func opcodeCB0x97() {
759 | // RES 2 A
760 | opcodesRES(af.GetHighReg(), 2)
761 | }
762 |
763 | func opcodeCB0x98() {
764 | // RES 3 B
765 | opcodesRES(bc.GetHighReg(), 3)
766 | }
767 |
768 | func opcodeCB0x99() {
769 | // RES 3 C
770 | opcodesRES(bc.GetLowReg(), 3)
771 | }
772 |
773 | func opcodeCB0x9A() {
774 | // RES 3 D
775 | opcodesRES(de.GetHighReg(), 3)
776 | }
777 |
778 | func opcodeCB0x9B() {
779 | // RES 3 E
780 | opcodesRES(de.GetLowReg(), 3)
781 | }
782 |
783 | func opcodeCB0x9C() {
784 | // RES 3 H
785 | opcodesRES(hl.GetHighReg(), 3)
786 | }
787 |
788 | func opcodeCB0x9D() {
789 | // RES 3 L
790 | opcodesRES(hl.GetLowReg(), 3)
791 | }
792 |
793 | func opcodeCB0x9E() {
794 | // RES 3 (HL)
795 | opcodesRESHL(3)
796 | }
797 |
798 | func opcodeCB0x9F() {
799 | // RES 3 A
800 | opcodesRES(af.GetHighReg(), 3)
801 | }
802 |
803 | func opcodeCB0xA0() {
804 | // RES 4 B
805 | opcodesRES(bc.GetHighReg(), 4)
806 | }
807 |
808 | func opcodeCB0xA1() {
809 | // RES 4 C
810 | opcodesRES(bc.GetLowReg(), 4)
811 | }
812 |
813 | func opcodeCB0xA2() {
814 | // RES 4 D
815 | opcodesRES(de.GetHighReg(), 4)
816 | }
817 |
818 | func opcodeCB0xA3() {
819 | // RES 4 E
820 | opcodesRES(de.GetLowReg(), 4)
821 | }
822 |
823 | func opcodeCB0xA4() {
824 | // RES 4 H
825 | opcodesRES(hl.GetHighReg(), 4)
826 | }
827 |
828 | func opcodeCB0xA5() {
829 | // RES 4 L
830 | opcodesRES(hl.GetLowReg(), 4)
831 | }
832 |
833 | func opcodeCB0xA6() {
834 | // RES 4 (HL)
835 | opcodesRESHL(4)
836 | }
837 |
838 | func opcodeCB0xA7() {
839 | // RES 4 A
840 | opcodesRES(af.GetHighReg(), 4)
841 | }
842 |
843 | func opcodeCB0xA8() {
844 | // RES 5 B
845 | opcodesRES(bc.GetHighReg(), 5)
846 | }
847 |
848 | func opcodeCB0xA9() {
849 | // RES 5 C
850 | opcodesRES(bc.GetLowReg(), 5)
851 | }
852 |
853 | func opcodeCB0xAA() {
854 | // RES 5 D
855 | opcodesRES(de.GetHighReg(), 5)
856 | }
857 |
858 | func opcodeCB0xAB() {
859 | // RES 5 E
860 | opcodesRES(de.GetLowReg(), 5)
861 | }
862 |
863 | func opcodeCB0xAC() {
864 | // RES 5 H
865 | opcodesRES(hl.GetHighReg(), 5)
866 | }
867 |
868 | func opcodeCB0xAD() {
869 | // RES 5 L
870 | opcodesRES(hl.GetLowReg(), 5)
871 | }
872 |
873 | func opcodeCB0xAE() {
874 | // RES 5 (HL)
875 | opcodesRESHL(5)
876 | }
877 |
878 | func opcodeCB0xAF() {
879 | // RES 5 A
880 | opcodesRES(af.GetHighReg(), 5)
881 | }
882 |
883 | func opcodeCB0xB0() {
884 | // RES 6 B
885 | opcodesRES(bc.GetHighReg(), 6)
886 | }
887 |
888 | func opcodeCB0xB1() {
889 | // RES 6 C
890 | opcodesRES(bc.GetLowReg(), 6)
891 | }
892 |
893 | func opcodeCB0xB2() {
894 | // RES 6 D
895 | opcodesRES(de.GetHighReg(), 6)
896 | }
897 |
898 | func opcodeCB0xB3() {
899 | // RES 6 E
900 | opcodesRES(de.GetLowReg(), 6)
901 | }
902 |
903 | func opcodeCB0xB4() {
904 | // RES 6 H
905 | opcodesRES(hl.GetHighReg(), 6)
906 | }
907 |
908 | func opcodeCB0xB5() {
909 | // RES 6 L
910 | opcodesRES(hl.GetLowReg(), 6)
911 | }
912 |
913 | func opcodeCB0xB6() {
914 | // RES 6 (HL)
915 | opcodesRESHL(6)
916 | }
917 |
918 | func opcodeCB0xB7() {
919 | // RES 6 A
920 | opcodesRES(af.GetHighReg(), 6)
921 | }
922 |
923 | func opcodeCB0xB8() {
924 | // RES 7 B
925 | opcodesRES(bc.GetHighReg(), 7)
926 | }
927 |
928 | func opcodeCB0xB9() {
929 | // RES 7 C
930 | opcodesRES(bc.GetLowReg(), 7)
931 | }
932 |
933 | func opcodeCB0xBA() {
934 | // RES 7 D
935 | opcodesRES(de.GetHighReg(), 7)
936 | }
937 |
938 | func opcodeCB0xBB() {
939 | // RES 7 E
940 | opcodesRES(de.GetLowReg(), 7)
941 | }
942 |
943 | func opcodeCB0xBC() {
944 | // RES 7 H
945 | opcodesRES(hl.GetHighReg(), 7)
946 | }
947 |
948 | func opcodeCB0xBD() {
949 | // RES 7 L
950 | opcodesRES(hl.GetLowReg(), 7)
951 | }
952 |
953 | func opcodeCB0xBE() {
954 | // RES 7 (HL)
955 | opcodesRESHL(7)
956 | }
957 |
958 | func opcodeCB0xBF() {
959 | // RES 7 A
960 | opcodesRES(af.GetHighReg(), 7)
961 | }
962 |
963 | func opcodeCB0xC0() {
964 | // SET 0 B
965 | opcodesSET(bc.GetHighReg(), 0)
966 | }
967 |
968 | func opcodeCB0xC1() {
969 | // SET 0 C
970 | opcodesSET(bc.GetLowReg(), 0)
971 | }
972 |
973 | func opcodeCB0xC2() {
974 | // SET 0 D
975 | opcodesSET(de.GetHighReg(), 0)
976 | }
977 |
978 | func opcodeCB0xC3() {
979 | // SET 0 E
980 | opcodesSET(de.GetLowReg(), 0)
981 | }
982 |
983 | func opcodeCB0xC4() {
984 | // SET 0 H
985 | opcodesSET(hl.GetHighReg(), 0)
986 | }
987 |
988 | func opcodeCB0xC5() {
989 | // SET 0 L
990 | opcodesSET(hl.GetLowReg(), 0)
991 | }
992 |
993 | func opcodeCB0xC6() {
994 | // SET 0 (HL)
995 | opcodesSETHL(0)
996 | }
997 |
998 | func opcodeCB0xC7() {
999 | // SET 0 A
1000 | opcodesSET(af.GetHighReg(), 0)
1001 | }
1002 |
1003 | func opcodeCB0xC8() {
1004 | // SET 1 B
1005 | opcodesSET(bc.GetHighReg(), 1)
1006 | }
1007 |
1008 | func opcodeCB0xC9() {
1009 | // SET 1 C
1010 | opcodesSET(bc.GetLowReg(), 1)
1011 | }
1012 |
1013 | func opcodeCB0xCA() {
1014 | // SET 1 D
1015 | opcodesSET(de.GetHighReg(), 1)
1016 | }
1017 |
1018 | func opcodeCB0xCB() {
1019 | // SET 1 E
1020 | opcodesSET(de.GetLowReg(), 1)
1021 | }
1022 |
1023 | func opcodeCB0xCC() {
1024 | // SET 1 H
1025 | opcodesSET(hl.GetHighReg(), 1)
1026 | }
1027 |
1028 | func opcodeCB0xCD() {
1029 | // SET 1 L
1030 | opcodesSET(hl.GetLowReg(), 1)
1031 | }
1032 |
1033 | func opcodeCB0xCE() {
1034 | // SET 1 (HL)
1035 | opcodesSETHL(1)
1036 | }
1037 |
1038 | func opcodeCB0xCF() {
1039 | // SET 1 A
1040 | opcodesSET(af.GetHighReg(), 1)
1041 | }
1042 |
1043 | func opcodeCB0xD0() {
1044 | // SET 2 B
1045 | opcodesSET(bc.GetHighReg(), 2)
1046 | }
1047 |
1048 | func opcodeCB0xD1() {
1049 | // SET 2 C
1050 | opcodesSET(bc.GetLowReg(), 2)
1051 | }
1052 |
1053 | func opcodeCB0xD2() {
1054 | // SET 2 D
1055 | opcodesSET(de.GetHighReg(), 2)
1056 | }
1057 |
1058 | func opcodeCB0xD3() {
1059 | // SET 2 E
1060 | opcodesSET(de.GetLowReg(), 2)
1061 | }
1062 |
1063 | func opcodeCB0xD4() {
1064 | // SET 2 H
1065 | opcodesSET(hl.GetHighReg(), 2)
1066 | }
1067 |
1068 | func opcodeCB0xD5() {
1069 | // SET 2 L
1070 | opcodesSET(hl.GetLowReg(), 2)
1071 | }
1072 |
1073 | func opcodeCB0xD6() {
1074 | // SET 2 (HL)
1075 | opcodesSETHL(2)
1076 | }
1077 |
1078 | func opcodeCB0xD7() {
1079 | // SET 2 A
1080 | opcodesSET(af.GetHighReg(), 2)
1081 | }
1082 |
1083 | func opcodeCB0xD8() {
1084 | // SET 3 B
1085 | opcodesSET(bc.GetHighReg(), 3)
1086 | }
1087 |
1088 | func opcodeCB0xD9() {
1089 | // SET 3 C
1090 | opcodesSET(bc.GetLowReg(), 3)
1091 | }
1092 |
1093 | func opcodeCB0xDA() {
1094 | // SET 3 D
1095 | opcodesSET(de.GetHighReg(), 3)
1096 | }
1097 |
1098 | func opcodeCB0xDB() {
1099 | // SET 3 E
1100 | opcodesSET(de.GetLowReg(), 3)
1101 | }
1102 |
1103 | func opcodeCB0xDC() {
1104 | // SET 3 H
1105 | opcodesSET(hl.GetHighReg(), 3)
1106 | }
1107 |
1108 | func opcodeCB0xDD() {
1109 | // SET 3 L
1110 | opcodesSET(hl.GetLowReg(), 3)
1111 | }
1112 |
1113 | func opcodeCB0xDE() {
1114 | // SET 3 (HL)
1115 | opcodesSETHL(3)
1116 | }
1117 |
1118 | func opcodeCB0xDF() {
1119 | // SET 3 A
1120 | opcodesSET(af.GetHighReg(), 3)
1121 | }
1122 |
1123 | func opcodeCB0xE0() {
1124 | // SET 4 B
1125 | opcodesSET(bc.GetHighReg(), 4)
1126 | }
1127 |
1128 | func opcodeCB0xE1() {
1129 | // SET 4 C
1130 | opcodesSET(bc.GetLowReg(), 4)
1131 | }
1132 |
1133 | func opcodeCB0xE2() {
1134 | // SET 4 D
1135 | opcodesSET(de.GetHighReg(), 4)
1136 | }
1137 |
1138 | func opcodeCB0xE3() {
1139 | // SET 4 E
1140 | opcodesSET(de.GetLowReg(), 4)
1141 | }
1142 |
1143 | func opcodeCB0xE4() {
1144 | // SET 4 H
1145 | opcodesSET(hl.GetHighReg(), 4)
1146 | }
1147 |
1148 | func opcodeCB0xE5() {
1149 | // SET 4 L
1150 | opcodesSET(hl.GetLowReg(), 4)
1151 | }
1152 |
1153 | func opcodeCB0xE6() {
1154 | // SET 4 (HL)
1155 | opcodesSETHL(4)
1156 | }
1157 |
1158 | func opcodeCB0xE7() {
1159 | // SET 4 A
1160 | opcodesSET(af.GetHighReg(), 4)
1161 |
1162 | }
1163 |
1164 | func opcodeCB0xE8() {
1165 | // SET 5 B
1166 | opcodesSET(bc.GetHighReg(), 5)
1167 | }
1168 |
1169 | func opcodeCB0xE9() {
1170 | // SET 5 C
1171 | opcodesSET(bc.GetLowReg(), 5)
1172 | }
1173 |
1174 | func opcodeCB0xEA() {
1175 | // SET 5 D
1176 | opcodesSET(de.GetHighReg(), 5)
1177 | }
1178 |
1179 | func opcodeCB0xEB() {
1180 | // SET 5 E
1181 | opcodesSET(de.GetLowReg(), 5)
1182 | }
1183 |
1184 | func opcodeCB0xEC() {
1185 | // SET 5 H
1186 | opcodesSET(hl.GetHighReg(), 5)
1187 | }
1188 |
1189 | func opcodeCB0xED() {
1190 | // SET 5 L
1191 | opcodesSET(hl.GetLowReg(), 5)
1192 | }
1193 |
1194 | func opcodeCB0xEE() {
1195 | // SET 5 (HL)
1196 | opcodesSETHL(5)
1197 | }
1198 |
1199 | func opcodeCB0xEF() {
1200 | // SET 5 A
1201 | opcodesSET(af.GetHighReg(), 5)
1202 | }
1203 |
1204 | func opcodeCB0xF0() {
1205 | // SET 6 B
1206 | opcodesSET(bc.GetHighReg(), 6)
1207 | }
1208 |
1209 | func opcodeCB0xF1() {
1210 | // SET 6 C
1211 | opcodesSET(bc.GetLowReg(), 6)
1212 | }
1213 |
1214 | func opcodeCB0xF2() {
1215 | // SET 6 D
1216 | opcodesSET(de.GetHighReg(), 6)
1217 | }
1218 |
1219 | func opcodeCB0xF3() {
1220 | // SET 6 E
1221 | opcodesSET(de.GetLowReg(), 6)
1222 | }
1223 |
1224 | func opcodeCB0xF4() {
1225 | // SET 6 H
1226 | opcodesSET(hl.GetHighReg(), 6)
1227 | }
1228 |
1229 | func opcodeCB0xF5() {
1230 | // SET 6 L
1231 | opcodesSET(hl.GetLowReg(), 6)
1232 | }
1233 |
1234 | func opcodeCB0xF6() {
1235 | // SET 6 (HL)
1236 | opcodesSETHL(6)
1237 | }
1238 |
1239 | func opcodeCB0xF7() {
1240 | // SET 6 A
1241 | opcodesSET(af.GetHighReg(), 6)
1242 | }
1243 |
1244 | func opcodeCB0xF8() {
1245 | // SET 7 B
1246 | opcodesSET(bc.GetHighReg(), 7)
1247 | }
1248 |
1249 | func opcodeCB0xF9() {
1250 | // SET 7 C
1251 | opcodesSET(bc.GetLowReg(), 7)
1252 | }
1253 |
1254 | func opcodeCB0xFA() {
1255 | // SET 7 D
1256 | opcodesSET(de.GetHighReg(), 7)
1257 | }
1258 |
1259 | func opcodeCB0xFB() {
1260 | // SET 7 E
1261 | opcodesSET(de.GetLowReg(), 7)
1262 | }
1263 |
1264 | func opcodeCB0xFC() {
1265 | // SET 7 H
1266 | opcodesSET(hl.GetHighReg(), 7)
1267 | }
1268 |
1269 | func opcodeCB0xFD() {
1270 | // SET 7 L
1271 | opcodesSET(hl.GetLowReg(), 7)
1272 | }
1273 |
1274 | func opcodeCB0xFE() {
1275 | // SET 7 (HL)
1276 | opcodesSETHL(7)
1277 | }
1278 |
1279 | func opcodeCB0xFF() {
1280 | // SET 7 A
1281 | opcodesSET(af.GetHighReg(), 7)
1282 | }
1283 |
--------------------------------------------------------------------------------
/gb/cpu/common.go:
--------------------------------------------------------------------------------
1 | package cpu
2 |
3 | import "fmt"
4 |
5 | func clearAllFlags() {
6 | initFlagReg(flagNone)
7 | }
8 |
9 | func setZeroFlagFromResult(result uint8) {
10 | if result == 0 {
11 | setFlag(flagZero)
12 | }
13 | }
14 |
15 | func initFlagReg(flag uint8) {
16 | af.SetLow(flag)
17 | }
18 |
19 | func flipFlag(flag uint8) {
20 | af.SetLow(af.GetLow() ^ flag)
21 | }
22 |
23 | func setFlag(flag uint8) {
24 | af.SetLow(af.GetLow() | flag)
25 | }
26 |
27 | func resetFlag(flag uint8) {
28 | af.SetLow(af.GetLow() &^ flag)
29 | }
30 |
31 | func isSetFlag(flag uint8) bool {
32 | return (af.GetLow() & flag) != 0
33 | }
34 |
35 | func stackPush(reg *SixteenBitReg) {
36 | sp.Decrement()
37 | mem.Write(sp.GetValue(), reg.GetHigh())
38 | sp.Decrement()
39 | mem.Write(sp.GetValue(), reg.GetLow())
40 | }
41 |
42 | func stackPop(reg *SixteenBitReg) {
43 | reg.SetLow(mem.Read(sp.GetValue()))
44 | sp.Increment()
45 | reg.SetHigh(mem.Read(sp.GetValue()))
46 | sp.Increment()
47 | }
48 |
49 | func invalidOPCode() {
50 | fmt.Println("INVALID opcode")
51 | }
52 |
53 | func opcodesLDValueToReg(reg1 *EightBitReg, value uint8) {
54 | reg1.SetValue(value)
55 | }
56 |
57 | func opcodesLDAddrToReg(reg *EightBitReg, address uint16) {
58 | reg.SetValue(mem.Read(address))
59 | }
60 |
61 | func opcodesLDValueToAddr(address uint16, value uint8) {
62 | mem.Write(address, value)
63 | }
64 |
65 | func opcodesOR(number uint8) {
66 | result := af.GetHigh() | number
67 | af.SetHigh(result)
68 | clearAllFlags()
69 | setZeroFlagFromResult(result)
70 | }
71 |
72 | func opcodesXOR(number uint8) {
73 | result := af.GetHigh() ^ number
74 | af.SetHigh(result)
75 | clearAllFlags()
76 | setZeroFlagFromResult(result)
77 | }
78 |
79 | func opcodesAND(number uint8) {
80 | result := af.GetHigh() & number
81 | af.SetHigh(result)
82 | if result == 0 {
83 | setFlag(flagZero)
84 | } else {
85 | resetFlag(flagZero)
86 | }
87 | resetFlag(flagNegative)
88 | setFlag(flagHalf)
89 | resetFlag(flagCarry)
90 | }
91 |
92 | func opcodesCP(number uint8) {
93 | initFlagReg(flagNegative)
94 | if af.GetHigh() < number {
95 | setFlag(flagCarry)
96 | } else if af.GetHigh() == number {
97 | setFlag(flagZero)
98 | }
99 | if ((af.GetHigh() - number) & 0xF) > (af.GetHigh() & 0xF) {
100 | setFlag(flagHalf)
101 | }
102 | }
103 |
104 | func opcodesINC(reg *EightBitReg) {
105 | result := reg.GetValue() + 1
106 | reg.SetValue(result)
107 | if result == 0 {
108 | setFlag(flagZero)
109 | } else {
110 | resetFlag(flagZero)
111 | }
112 | resetFlag(flagNegative)
113 | if (result & 0x0F) == 0 {
114 | setFlag(flagHalf)
115 | } else {
116 | resetFlag(flagHalf)
117 | }
118 | }
119 |
120 | func opcodesINCHL() {
121 | address := hl.GetValue()
122 | result := mem.Read(address)
123 | result++
124 | mem.Write(address, result)
125 | if isSetFlag(flagCarry) {
126 | initFlagReg(flagCarry)
127 | } else {
128 | clearAllFlags()
129 | }
130 | setZeroFlagFromResult(result)
131 | if (result & 0x0F) == 0x00 {
132 | setFlag(flagHalf)
133 | }
134 | }
135 |
136 | func opcodesDEC(reg *EightBitReg) {
137 | result := reg.GetValue()
138 | result--
139 | reg.SetValue(result)
140 | if isSetFlag(flagCarry) {
141 | initFlagReg(flagCarry)
142 | } else {
143 | clearAllFlags()
144 | }
145 | setFlag(flagNegative)
146 | setZeroFlagFromResult(result)
147 | if (result & 0x0F) == 0x0F {
148 | setFlag(flagHalf)
149 | }
150 | }
151 |
152 | func opcodesDECHL() {
153 | address := hl.GetValue()
154 | result := mem.Read(address)
155 | result--
156 | mem.Write(address, result)
157 | if isSetFlag(flagCarry) {
158 | initFlagReg(flagCarry)
159 | } else {
160 | clearAllFlags()
161 | }
162 | setFlag(flagNegative)
163 | setZeroFlagFromResult(result)
164 | if (result & 0x0F) == 0x0F {
165 | setFlag(flagHalf)
166 | }
167 | }
168 |
169 | func opcodesADD(number uint8) {
170 | result := uint(af.GetHigh()) + uint(number)
171 | carrybits := uint(af.GetHigh()) ^ uint(number) ^ result
172 | af.SetHigh(uint8(result))
173 | clearAllFlags()
174 | setZeroFlagFromResult(uint8(result))
175 | if (carrybits & 0x100) != 0 {
176 | setFlag(flagCarry)
177 | }
178 | if (carrybits & 0x10) != 0 {
179 | setFlag(flagHalf)
180 | }
181 | }
182 |
183 | func opcodesADC(number uint8) {
184 | var carry uint
185 | if isSetFlag(flagCarry) {
186 | carry = 1
187 | } else {
188 | carry = 0
189 | }
190 | result := uint(af.GetHigh()) + uint(number) + carry
191 | if uint8(result) == 0 {
192 | setFlag(flagZero)
193 | } else {
194 | resetFlag(flagZero)
195 | }
196 | resetFlag(flagNegative)
197 | if result > 0xFF {
198 | setFlag(flagCarry)
199 | } else {
200 | resetFlag(flagCarry)
201 | }
202 | if ((uint(af.GetHigh()) & 0x0F) + (uint(number) & 0x0F) + carry) > 0x0F {
203 | setFlag(flagHalf)
204 | } else {
205 | resetFlag(flagHalf)
206 | }
207 | af.SetHigh(uint8(result))
208 | }
209 |
210 | func opcodesSUB(number uint8) {
211 | result := int(af.GetHigh()) - int(number)
212 | carrybits := int(af.GetHigh()) ^ int(number) ^ result
213 | af.SetHigh(uint8(result))
214 | initFlagReg(flagNegative)
215 | setZeroFlagFromResult(uint8(result))
216 | if (carrybits & 0x100) != 0 {
217 | setFlag(flagCarry)
218 | }
219 | if (carrybits & 0x10) != 0 {
220 | setFlag(flagHalf)
221 | }
222 | }
223 |
224 | func opcodesSBC(number uint8) {
225 | var carry int
226 | if isSetFlag(flagCarry) {
227 | carry = 1
228 | } else {
229 | carry = 0
230 | }
231 | result := int(af.GetHigh()) - int(number) - carry
232 | initFlagReg(flagNegative)
233 | setZeroFlagFromResult(uint8(result))
234 | if result < 0 {
235 | setFlag(flagCarry)
236 | }
237 | if ((int(af.GetHigh()) & 0x0F) - (int(number) & 0x0F) - carry) < 0 {
238 | setFlag(flagHalf)
239 | }
240 | af.SetHigh(uint8(result))
241 | }
242 |
243 | func opcodesADDHL(number uint16) {
244 | result := uint(hl.GetValue()) + uint(number)
245 | if isSetFlag(flagZero) {
246 | initFlagReg(flagZero)
247 | } else {
248 | clearAllFlags()
249 | }
250 | if (result & 0x10000) != 0 {
251 | setFlag(flagCarry)
252 | }
253 | if ((uint(hl.GetValue()) ^ uint(number) ^ (result & 0xFFFF)) & 0x1000) != 0 {
254 | setFlag(flagHalf)
255 | }
256 | hl.SetValue(uint16(result))
257 | }
258 |
259 | func opcodesADDSP(number int8) {
260 | result := int(sp.GetValue()) + int(number)
261 | clearAllFlags()
262 | carrybits := int(sp.GetValue()) ^ int(number) ^ (result & 0xFFFF)
263 | if (carrybits & 0x100) == 0x100 {
264 | setFlag(flagCarry)
265 | }
266 | if (carrybits & 0x10) == 0x10 {
267 | setFlag(flagHalf)
268 | }
269 | sp.SetValue(uint16(result))
270 | }
271 |
272 | func opcodesSWAPReg(reg *EightBitReg) {
273 | lowHalf := reg.GetValue() & 0x0F
274 | highHalf := (reg.GetValue() >> 4) & 0x0F
275 | reg.SetValue((lowHalf << 4) + highHalf)
276 | clearAllFlags()
277 | setZeroFlagFromResult(reg.GetValue())
278 | }
279 |
280 | func opcodesSWAPHL() {
281 | address := hl.GetValue()
282 | result := mem.Read(address)
283 | lowHalf := result & 0x0F
284 | highHalf := (result >> 4) & 0x0F
285 | result = (lowHalf << 4) + highHalf
286 | mem.Write(address, result)
287 | clearAllFlags()
288 | setZeroFlagFromResult(result)
289 | }
290 |
291 | func opcodesSLA(reg *EightBitReg) {
292 | if (reg.GetValue() & 0x80) != 0 {
293 | initFlagReg(flagCarry)
294 | } else {
295 | clearAllFlags()
296 | }
297 | result := reg.GetValue() << 1
298 | reg.SetValue(result)
299 | setZeroFlagFromResult(result)
300 | }
301 |
302 | func opcodesSLAHL() {
303 | address := hl.GetValue()
304 | result := mem.Read(address)
305 | if (result & 0x80) != 0 {
306 | initFlagReg(flagCarry)
307 | } else {
308 | clearAllFlags()
309 | }
310 | result <<= 1
311 | mem.Write(address, result)
312 | setZeroFlagFromResult(result)
313 | }
314 |
315 | func opcodesSRA(reg *EightBitReg) {
316 | value := reg.GetValue()
317 | if (value & 0x01) != 0 {
318 | initFlagReg(flagCarry)
319 | } else {
320 | clearAllFlags()
321 | }
322 | result := value >> 1
323 | if (value & 0x80) != 0 {
324 | result |= 0x80
325 | }
326 | reg.SetValue(result)
327 | setZeroFlagFromResult(result)
328 | }
329 |
330 | func opcodesSRAHL() {
331 | address := hl.GetValue()
332 | value := mem.Read(address)
333 | if (value & 0x01) != 0 {
334 | initFlagReg(flagCarry)
335 | } else {
336 | clearAllFlags()
337 | }
338 | result := value >> 1
339 | if (value & 0x80) != 0 {
340 | result |= 0x80
341 | }
342 | mem.Write(address, result)
343 | setZeroFlagFromResult(result)
344 | }
345 |
346 | func opcodesSRL(reg *EightBitReg) {
347 | result := reg.GetValue()
348 | if (result & 0x01) != 0 {
349 | initFlagReg(flagCarry)
350 | } else {
351 | clearAllFlags()
352 | }
353 | result >>= 1
354 | reg.SetValue(result)
355 | setZeroFlagFromResult(result)
356 | }
357 |
358 | func opcodesSRLHL() {
359 | address := hl.GetValue()
360 | result := mem.Read(address)
361 | if (result & 0x01) != 0 {
362 | initFlagReg(flagCarry)
363 | } else {
364 | clearAllFlags()
365 | }
366 | result >>= 1
367 | mem.Write(address, result)
368 | setZeroFlagFromResult(result)
369 | }
370 |
371 | func opcodesRLC(reg *EightBitReg) {
372 | opcodesRLCA(reg)
373 | setZeroFlagFromResult(reg.GetValue())
374 | }
375 |
376 | func opcodesRLCA(reg *EightBitReg) {
377 | value := reg.GetValue()
378 | result := value << 1
379 | if (value & 0x80) != 0 {
380 | initFlagReg(flagCarry)
381 | result |= 0x01
382 | } else {
383 | clearAllFlags()
384 | }
385 | reg.SetValue(result)
386 | }
387 |
388 | func opcodesRLCHL() {
389 | address := hl.GetValue()
390 | value := mem.Read(address)
391 | result := value << 1
392 | if (value & 0x80) != 0 {
393 | initFlagReg(flagCarry)
394 | result |= 0x01
395 | } else {
396 | clearAllFlags()
397 | }
398 | mem.Write(address, result)
399 | setZeroFlagFromResult(result)
400 | }
401 |
402 | func opcodesRL(reg *EightBitReg) {
403 | opcodesRLA(reg)
404 | setZeroFlagFromResult(reg.GetValue())
405 | }
406 |
407 | func opcodesRLA(reg *EightBitReg) {
408 | var carry uint8
409 | if isSetFlag(flagCarry) {
410 | carry = 0x01
411 | } else {
412 | carry = 0x00
413 | }
414 | value := reg.GetValue()
415 | if (value & 0x80) != 0 {
416 | initFlagReg(flagCarry)
417 | } else {
418 | clearAllFlags()
419 | }
420 | result := (value << 1) | carry
421 | reg.SetValue(result)
422 | }
423 |
424 | func opcodesRLHL() {
425 | var carry uint8
426 | if isSetFlag(flagCarry) {
427 | carry = 0x01
428 | } else {
429 | carry = 0x00
430 | }
431 | address := hl.GetValue()
432 | value := mem.Read(address)
433 | if (value & 0x80) != 0 {
434 | initFlagReg(flagCarry)
435 | } else {
436 | clearAllFlags()
437 | }
438 | result := (value << 1) | carry
439 | mem.Write(address, result)
440 | setZeroFlagFromResult(result)
441 | }
442 |
443 | func opcodesRRC(reg *EightBitReg) {
444 | opcodesRRCA(reg)
445 | setZeroFlagFromResult(reg.GetValue())
446 | }
447 |
448 | func opcodesRRCA(reg *EightBitReg) {
449 | value := reg.GetValue()
450 | result := value >> 1
451 | if (value & 0x01) != 0 {
452 | initFlagReg(flagCarry)
453 | result |= 0x80
454 | } else {
455 | clearAllFlags()
456 | }
457 | reg.SetValue(result)
458 | }
459 |
460 | func opcodesRRCHL() {
461 | address := hl.GetValue()
462 | value := mem.Read(address)
463 | result := value >> 1
464 | if (value & 0x01) != 0 {
465 | initFlagReg(flagCarry)
466 | result |= 0x80
467 | } else {
468 | clearAllFlags()
469 | }
470 | mem.Write(address, result)
471 | setZeroFlagFromResult(result)
472 | }
473 |
474 | func opcodesRR(reg *EightBitReg) {
475 | opcodesRRA(reg)
476 | setZeroFlagFromResult(reg.GetValue())
477 | }
478 |
479 | func opcodesRRA(reg *EightBitReg) {
480 | var carry uint8
481 | if isSetFlag(flagCarry) {
482 | carry = 0x80
483 | } else {
484 | carry = 0x00
485 | }
486 | value := reg.GetValue()
487 | if (value & 0x01) != 0 {
488 | initFlagReg(flagCarry)
489 | } else {
490 | clearAllFlags()
491 | }
492 | result := (value >> 1) | carry
493 | reg.SetValue(result)
494 | }
495 |
496 | func opcodesRRHL() {
497 | var carry uint8
498 | if isSetFlag(flagCarry) {
499 | carry = 0x80
500 | } else {
501 | carry = 0x00
502 | }
503 | address := hl.GetValue()
504 | value := mem.Read(address)
505 | if (value & 0x01) != 0 {
506 | initFlagReg(flagCarry)
507 | } else {
508 | clearAllFlags()
509 | }
510 | result := (value >> 1) | carry
511 | mem.Write(address, result)
512 | setZeroFlagFromResult(result)
513 | }
514 |
515 | func opcodesBIT(reg *EightBitReg, bit uint) {
516 | if ((reg.GetValue() >> bit) & 0x01) == 0 {
517 | setFlag(flagZero)
518 | } else {
519 | resetFlag(flagZero)
520 | }
521 | setFlag(flagHalf)
522 | resetFlag(flagNegative)
523 | }
524 |
525 | func opcodesBITHL(bit uint) {
526 | if ((mem.Read(hl.GetValue()) >> bit) & 0x01) == 0 {
527 | setFlag(flagZero)
528 | } else {
529 | resetFlag(flagZero)
530 | }
531 | setFlag(flagHalf)
532 | resetFlag(flagNegative)
533 | }
534 |
535 | func opcodesSET(reg *EightBitReg, bit uint) {
536 | reg.SetValue(reg.GetValue() | (0x01 << bit))
537 | }
538 |
539 | func opcodesSETHL(bit uint) {
540 | address := hl.GetValue()
541 | result := mem.Read(address)
542 | result |= (0x01 << bit)
543 | mem.Write(address, result)
544 | }
545 |
546 | func opcodesRES(reg *EightBitReg, bit uint) {
547 | reg.SetValue(reg.GetValue() &^ (0x01 << bit))
548 | }
549 |
550 | func opcodesRESHL(bit uint) {
551 | address := hl.GetValue()
552 | result := mem.Read(address)
553 | result &= ^(0x01 << bit)
554 | mem.Write(address, result)
555 | }
556 |
--------------------------------------------------------------------------------
/gb/cpu/cpu.go:
--------------------------------------------------------------------------------
1 | package cpu
2 |
3 | import "github.com/drhelius/demo-emulator/gb/mapper"
4 |
5 | // Interrupt types
6 | const (
7 | InterruptNone uint8 = 0x00
8 | InterruptVBlank uint8 = 0x01
9 | InterruptLCDSTAT uint8 = 0x02
10 | InterruptTimer uint8 = 0x04
11 | InterruptSerial uint8 = 0x08
12 | InterruptJoypad uint8 = 0x10
13 | )
14 |
15 | // All the flags in the F register
16 | const (
17 | flagZero uint8 = 0x80
18 | flagNegative uint8 = 0x40
19 | flagHalf uint8 = 0x20
20 | flagCarry uint8 = 0x10
21 | flagNone uint8 = 0x00
22 | )
23 |
24 | var (
25 | af SixteenBitReg
26 | bc SixteenBitReg
27 | de SixteenBitReg
28 | hl SixteenBitReg
29 | sp SixteenBitReg
30 | pc SixteenBitReg
31 | mem mapper.Mapper
32 | ime bool
33 | halted bool
34 | branchTaken bool
35 | clockCycles uint
36 | imeCycles int
37 | skipPCBug bool
38 | )
39 |
40 | func init() {
41 | pc.SetValue(0x0100)
42 | sp.SetValue(0xFFFE)
43 | af.SetValue(0x01B0)
44 | bc.SetValue(0x0013)
45 | de.SetValue(0x00D8)
46 | hl.SetValue(0x014D)
47 | }
48 |
49 | // SetMapper injects the memory impl
50 | func SetMapper(m mapper.Mapper) {
51 | mem = m
52 | }
53 |
54 | // Tick runs a single instruction of the processor
55 | // Then returns the number of cycles used
56 | func Tick() uint {
57 | clockCycles = 0
58 |
59 | if halted {
60 | // if an interrupt is pending leave halt
61 | if interruptPending() != InterruptNone {
62 | halted = false
63 | } else {
64 | clockCycles += 4
65 | }
66 | }
67 |
68 | if !halted {
69 | // acknowledge any pending interrupt s
70 | serveInterrupt(interruptPending())
71 |
72 | // fetch the next opcode and execute it
73 | runOpcode(fetchOpcode())
74 | }
75 |
76 | updateTimers()
77 | updateSerial()
78 |
79 | // this is in order to delay the activation
80 | // of ima one instruction
81 | if imeCycles > 0 {
82 | imeCycles -= int(clockCycles)
83 | if imeCycles <= 0 {
84 | imeCycles = 0
85 | ime = true
86 | }
87 | }
88 |
89 | return clockCycles
90 | }
91 |
92 | // RequestInterrupt is used to raise a new interrupt
93 | func RequestInterrupt(interrupt uint8) {
94 | mem.Write(0xFF0F, mem.Read(0xFF0F)|interrupt)
95 | }
96 |
97 | func fetchOpcode() uint8 {
98 | opcode := mem.Read(pc.GetValue())
99 |
100 | // if there is an interrupt pending and
101 | // the cpu is halted it fails to advance the PC register
102 | // once the cpu resumes operation
103 | // this bug is present in all the original DMGs
104 | if skipPCBug {
105 | skipPCBug = false
106 | } else {
107 | pc.Increment()
108 | }
109 |
110 | return opcode
111 | }
112 |
113 | func runOpcode(opcode uint8) {
114 | if opcode == 0xCB {
115 | opcode = fetchOpcode()
116 | opcodeCBArray[opcode]()
117 | clockCycles += machineCyclesCB[opcode] * 4
118 | } else {
119 | opcodeArray[opcode]()
120 | if branchTaken {
121 | branchTaken = false
122 | clockCycles += machineCyclesBranched[opcode] * 4
123 | } else {
124 | clockCycles += machineCycles[opcode] * 4
125 | }
126 | }
127 | }
128 |
129 | func interruptIsAboutToRaise() bool {
130 | ieReg := mem.Read(0xFFFF)
131 | ifReg := mem.Read(0xFF0F)
132 | return (ifReg & ieReg & 0x1F) != 0
133 | }
134 |
135 | func interruptPending() uint8 {
136 | ieReg := mem.Read(0xFFFF)
137 | ifReg := mem.Read(0xFF0F)
138 | ieIf := ieReg & ifReg
139 |
140 | switch {
141 | case (ieIf & 0x01) != 0:
142 | return InterruptVBlank
143 | case (ieIf & 0x02) != 0:
144 | return InterruptLCDSTAT
145 | case (ieIf & 0x04) != 0:
146 | return InterruptTimer
147 | case (ieIf & 0x08) != 0:
148 | return InterruptSerial
149 | case (ieIf & 0x10) != 0:
150 | return InterruptJoypad
151 | }
152 |
153 | return InterruptNone
154 | }
155 |
156 | func serveInterrupt(interrupt uint8) {
157 | if ime {
158 | ifReg := mem.Read(0xFF0F)
159 | switch interrupt {
160 | case InterruptVBlank:
161 | mem.Write(0xFF0F, ifReg&0xFE)
162 | ime = false
163 | stackPush(&pc)
164 | pc.SetValue(0x0040)
165 | clockCycles += 20
166 | case InterruptLCDSTAT:
167 | mem.Write(0xFF0F, ifReg&0xFD)
168 | ime = false
169 | stackPush(&pc)
170 | pc.SetValue(0x0048)
171 | clockCycles += 20
172 | case InterruptTimer:
173 | mem.Write(0xFF0F, ifReg&0xFB)
174 | ime = false
175 | stackPush(&pc)
176 | pc.SetValue(0x0050)
177 | clockCycles += 20
178 | case InterruptSerial:
179 | mem.Write(0xFF0F, ifReg&0xF7)
180 | ime = false
181 | stackPush(&pc)
182 | pc.SetValue(0x0058)
183 | clockCycles += 20
184 | case InterruptJoypad:
185 | mem.Write(0xFF0F, ifReg&0xEF)
186 | ime = false
187 | stackPush(&pc)
188 | pc.SetValue(0x0060)
189 | clockCycles += 20
190 | }
191 | }
192 | }
193 |
--------------------------------------------------------------------------------
/gb/cpu/functors.go:
--------------------------------------------------------------------------------
1 | package cpu
2 |
3 | type opcodeFunc func()
4 | type opcodeFuncArray [256]opcodeFunc
5 |
6 | var opcodeArray opcodeFuncArray
7 | var opcodeCBArray opcodeFuncArray
8 |
9 | func init() {
10 |
11 | // this two arrays store the poiters to
12 | // all the opcodes functions
13 |
14 | opcodeArray[0x00] = opcode0x00
15 | opcodeArray[0x01] = opcode0x01
16 | opcodeArray[0x02] = opcode0x02
17 | opcodeArray[0x03] = opcode0x03
18 | opcodeArray[0x04] = opcode0x04
19 | opcodeArray[0x05] = opcode0x05
20 | opcodeArray[0x06] = opcode0x06
21 | opcodeArray[0x07] = opcode0x07
22 | opcodeArray[0x08] = opcode0x08
23 | opcodeArray[0x09] = opcode0x09
24 | opcodeArray[0x0A] = opcode0x0A
25 | opcodeArray[0x0B] = opcode0x0B
26 | opcodeArray[0x0C] = opcode0x0C
27 | opcodeArray[0x0D] = opcode0x0D
28 | opcodeArray[0x0E] = opcode0x0E
29 | opcodeArray[0x0F] = opcode0x0F
30 |
31 | opcodeArray[0x10] = opcode0x10
32 | opcodeArray[0x11] = opcode0x11
33 | opcodeArray[0x12] = opcode0x12
34 | opcodeArray[0x13] = opcode0x13
35 | opcodeArray[0x14] = opcode0x14
36 | opcodeArray[0x15] = opcode0x15
37 | opcodeArray[0x16] = opcode0x16
38 | opcodeArray[0x17] = opcode0x17
39 | opcodeArray[0x18] = opcode0x18
40 | opcodeArray[0x19] = opcode0x19
41 | opcodeArray[0x1A] = opcode0x1A
42 | opcodeArray[0x1B] = opcode0x1B
43 | opcodeArray[0x1C] = opcode0x1C
44 | opcodeArray[0x1D] = opcode0x1D
45 | opcodeArray[0x1E] = opcode0x1E
46 | opcodeArray[0x1F] = opcode0x1F
47 |
48 | opcodeArray[0x20] = opcode0x20
49 | opcodeArray[0x21] = opcode0x21
50 | opcodeArray[0x22] = opcode0x22
51 | opcodeArray[0x23] = opcode0x23
52 | opcodeArray[0x24] = opcode0x24
53 | opcodeArray[0x25] = opcode0x25
54 | opcodeArray[0x26] = opcode0x26
55 | opcodeArray[0x27] = opcode0x27
56 | opcodeArray[0x28] = opcode0x28
57 | opcodeArray[0x29] = opcode0x29
58 | opcodeArray[0x2A] = opcode0x2A
59 | opcodeArray[0x2B] = opcode0x2B
60 | opcodeArray[0x2C] = opcode0x2C
61 | opcodeArray[0x2D] = opcode0x2D
62 | opcodeArray[0x2E] = opcode0x2E
63 | opcodeArray[0x2F] = opcode0x2F
64 |
65 | opcodeArray[0x30] = opcode0x30
66 | opcodeArray[0x31] = opcode0x31
67 | opcodeArray[0x32] = opcode0x32
68 | opcodeArray[0x33] = opcode0x33
69 | opcodeArray[0x34] = opcode0x34
70 | opcodeArray[0x35] = opcode0x35
71 | opcodeArray[0x36] = opcode0x36
72 | opcodeArray[0x37] = opcode0x37
73 | opcodeArray[0x38] = opcode0x38
74 | opcodeArray[0x39] = opcode0x39
75 | opcodeArray[0x3A] = opcode0x3A
76 | opcodeArray[0x3B] = opcode0x3B
77 | opcodeArray[0x3C] = opcode0x3C
78 | opcodeArray[0x3D] = opcode0x3D
79 | opcodeArray[0x3E] = opcode0x3E
80 | opcodeArray[0x3F] = opcode0x3F
81 |
82 | opcodeArray[0x40] = opcode0x40
83 | opcodeArray[0x41] = opcode0x41
84 | opcodeArray[0x42] = opcode0x42
85 | opcodeArray[0x43] = opcode0x43
86 | opcodeArray[0x44] = opcode0x44
87 | opcodeArray[0x45] = opcode0x45
88 | opcodeArray[0x46] = opcode0x46
89 | opcodeArray[0x47] = opcode0x47
90 | opcodeArray[0x48] = opcode0x48
91 | opcodeArray[0x49] = opcode0x49
92 | opcodeArray[0x4A] = opcode0x4A
93 | opcodeArray[0x4B] = opcode0x4B
94 | opcodeArray[0x4C] = opcode0x4C
95 | opcodeArray[0x4D] = opcode0x4D
96 | opcodeArray[0x4E] = opcode0x4E
97 | opcodeArray[0x4F] = opcode0x4F
98 |
99 | opcodeArray[0x50] = opcode0x50
100 | opcodeArray[0x51] = opcode0x51
101 | opcodeArray[0x52] = opcode0x52
102 | opcodeArray[0x53] = opcode0x53
103 | opcodeArray[0x54] = opcode0x54
104 | opcodeArray[0x55] = opcode0x55
105 | opcodeArray[0x56] = opcode0x56
106 | opcodeArray[0x57] = opcode0x57
107 | opcodeArray[0x58] = opcode0x58
108 | opcodeArray[0x59] = opcode0x59
109 | opcodeArray[0x5A] = opcode0x5A
110 | opcodeArray[0x5B] = opcode0x5B
111 | opcodeArray[0x5C] = opcode0x5C
112 | opcodeArray[0x5D] = opcode0x5D
113 | opcodeArray[0x5E] = opcode0x5E
114 | opcodeArray[0x5F] = opcode0x5F
115 |
116 | opcodeArray[0x60] = opcode0x60
117 | opcodeArray[0x61] = opcode0x61
118 | opcodeArray[0x62] = opcode0x62
119 | opcodeArray[0x63] = opcode0x63
120 | opcodeArray[0x64] = opcode0x64
121 | opcodeArray[0x65] = opcode0x65
122 | opcodeArray[0x66] = opcode0x66
123 | opcodeArray[0x67] = opcode0x67
124 | opcodeArray[0x68] = opcode0x68
125 | opcodeArray[0x69] = opcode0x69
126 | opcodeArray[0x6A] = opcode0x6A
127 | opcodeArray[0x6B] = opcode0x6B
128 | opcodeArray[0x6C] = opcode0x6C
129 | opcodeArray[0x6D] = opcode0x6D
130 | opcodeArray[0x6E] = opcode0x6E
131 | opcodeArray[0x6F] = opcode0x6F
132 |
133 | opcodeArray[0x70] = opcode0x70
134 | opcodeArray[0x71] = opcode0x71
135 | opcodeArray[0x72] = opcode0x72
136 | opcodeArray[0x73] = opcode0x73
137 | opcodeArray[0x74] = opcode0x74
138 | opcodeArray[0x75] = opcode0x75
139 | opcodeArray[0x76] = opcode0x76
140 | opcodeArray[0x77] = opcode0x77
141 | opcodeArray[0x78] = opcode0x78
142 | opcodeArray[0x79] = opcode0x79
143 | opcodeArray[0x7A] = opcode0x7A
144 | opcodeArray[0x7B] = opcode0x7B
145 | opcodeArray[0x7C] = opcode0x7C
146 | opcodeArray[0x7D] = opcode0x7D
147 | opcodeArray[0x7E] = opcode0x7E
148 | opcodeArray[0x7F] = opcode0x7F
149 |
150 | opcodeArray[0x80] = opcode0x80
151 | opcodeArray[0x81] = opcode0x81
152 | opcodeArray[0x82] = opcode0x82
153 | opcodeArray[0x83] = opcode0x83
154 | opcodeArray[0x84] = opcode0x84
155 | opcodeArray[0x85] = opcode0x85
156 | opcodeArray[0x86] = opcode0x86
157 | opcodeArray[0x87] = opcode0x87
158 | opcodeArray[0x88] = opcode0x88
159 | opcodeArray[0x89] = opcode0x89
160 | opcodeArray[0x8A] = opcode0x8A
161 | opcodeArray[0x8B] = opcode0x8B
162 | opcodeArray[0x8C] = opcode0x8C
163 | opcodeArray[0x8D] = opcode0x8D
164 | opcodeArray[0x8E] = opcode0x8E
165 | opcodeArray[0x8F] = opcode0x8F
166 |
167 | opcodeArray[0x90] = opcode0x90
168 | opcodeArray[0x91] = opcode0x91
169 | opcodeArray[0x92] = opcode0x92
170 | opcodeArray[0x93] = opcode0x93
171 | opcodeArray[0x94] = opcode0x94
172 | opcodeArray[0x95] = opcode0x95
173 | opcodeArray[0x96] = opcode0x96
174 | opcodeArray[0x97] = opcode0x97
175 | opcodeArray[0x98] = opcode0x98
176 | opcodeArray[0x99] = opcode0x99
177 | opcodeArray[0x9A] = opcode0x9A
178 | opcodeArray[0x9B] = opcode0x9B
179 | opcodeArray[0x9C] = opcode0x9C
180 | opcodeArray[0x9D] = opcode0x9D
181 | opcodeArray[0x9E] = opcode0x9E
182 | opcodeArray[0x9F] = opcode0x9F
183 |
184 | opcodeArray[0xA0] = opcode0xA0
185 | opcodeArray[0xA1] = opcode0xA1
186 | opcodeArray[0xA2] = opcode0xA2
187 | opcodeArray[0xA3] = opcode0xA3
188 | opcodeArray[0xA4] = opcode0xA4
189 | opcodeArray[0xA5] = opcode0xA5
190 | opcodeArray[0xA6] = opcode0xA6
191 | opcodeArray[0xA7] = opcode0xA7
192 | opcodeArray[0xA8] = opcode0xA8
193 | opcodeArray[0xA9] = opcode0xA9
194 | opcodeArray[0xAA] = opcode0xAA
195 | opcodeArray[0xAB] = opcode0xAB
196 | opcodeArray[0xAC] = opcode0xAC
197 | opcodeArray[0xAD] = opcode0xAD
198 | opcodeArray[0xAE] = opcode0xAE
199 | opcodeArray[0xAF] = opcode0xAF
200 |
201 | opcodeArray[0xB0] = opcode0xB0
202 | opcodeArray[0xB1] = opcode0xB1
203 | opcodeArray[0xB2] = opcode0xB2
204 | opcodeArray[0xB3] = opcode0xB3
205 | opcodeArray[0xB4] = opcode0xB4
206 | opcodeArray[0xB5] = opcode0xB5
207 | opcodeArray[0xB6] = opcode0xB6
208 | opcodeArray[0xB7] = opcode0xB7
209 | opcodeArray[0xB8] = opcode0xB8
210 | opcodeArray[0xB9] = opcode0xB9
211 | opcodeArray[0xBA] = opcode0xBA
212 | opcodeArray[0xBB] = opcode0xBB
213 | opcodeArray[0xBC] = opcode0xBC
214 | opcodeArray[0xBD] = opcode0xBD
215 | opcodeArray[0xBE] = opcode0xBE
216 | opcodeArray[0xBF] = opcode0xBF
217 |
218 | opcodeArray[0xC0] = opcode0xC0
219 | opcodeArray[0xC1] = opcode0xC1
220 | opcodeArray[0xC2] = opcode0xC2
221 | opcodeArray[0xC3] = opcode0xC3
222 | opcodeArray[0xC4] = opcode0xC4
223 | opcodeArray[0xC5] = opcode0xC5
224 | opcodeArray[0xC6] = opcode0xC6
225 | opcodeArray[0xC7] = opcode0xC7
226 | opcodeArray[0xC8] = opcode0xC8
227 | opcodeArray[0xC9] = opcode0xC9
228 | opcodeArray[0xCA] = opcode0xCA
229 | opcodeArray[0xCB] = opcode0xCB
230 | opcodeArray[0xCC] = opcode0xCC
231 | opcodeArray[0xCD] = opcode0xCD
232 | opcodeArray[0xCE] = opcode0xCE
233 | opcodeArray[0xCF] = opcode0xCF
234 |
235 | opcodeArray[0xD0] = opcode0xD0
236 | opcodeArray[0xD1] = opcode0xD1
237 | opcodeArray[0xD2] = opcode0xD2
238 | opcodeArray[0xD3] = opcode0xD3
239 | opcodeArray[0xD4] = opcode0xD4
240 | opcodeArray[0xD5] = opcode0xD5
241 | opcodeArray[0xD6] = opcode0xD6
242 | opcodeArray[0xD7] = opcode0xD7
243 | opcodeArray[0xD8] = opcode0xD8
244 | opcodeArray[0xD9] = opcode0xD9
245 | opcodeArray[0xDA] = opcode0xDA
246 | opcodeArray[0xDB] = opcode0xDB
247 | opcodeArray[0xDC] = opcode0xDC
248 | opcodeArray[0xDD] = opcode0xDD
249 | opcodeArray[0xDE] = opcode0xDE
250 | opcodeArray[0xDF] = opcode0xDF
251 |
252 | opcodeArray[0xE0] = opcode0xE0
253 | opcodeArray[0xE1] = opcode0xE1
254 | opcodeArray[0xE2] = opcode0xE2
255 | opcodeArray[0xE3] = opcode0xE3
256 | opcodeArray[0xE4] = opcode0xE4
257 | opcodeArray[0xE5] = opcode0xE5
258 | opcodeArray[0xE6] = opcode0xE6
259 | opcodeArray[0xE7] = opcode0xE7
260 | opcodeArray[0xE8] = opcode0xE8
261 | opcodeArray[0xE9] = opcode0xE9
262 | opcodeArray[0xEA] = opcode0xEA
263 | opcodeArray[0xEB] = opcode0xEB
264 | opcodeArray[0xEC] = opcode0xEC
265 | opcodeArray[0xED] = opcode0xED
266 | opcodeArray[0xEE] = opcode0xEE
267 | opcodeArray[0xEF] = opcode0xEF
268 |
269 | opcodeArray[0xF0] = opcode0xF0
270 | opcodeArray[0xF1] = opcode0xF1
271 | opcodeArray[0xF2] = opcode0xF2
272 | opcodeArray[0xF3] = opcode0xF3
273 | opcodeArray[0xF4] = opcode0xF4
274 | opcodeArray[0xF5] = opcode0xF5
275 | opcodeArray[0xF6] = opcode0xF6
276 | opcodeArray[0xF7] = opcode0xF7
277 | opcodeArray[0xF8] = opcode0xF8
278 | opcodeArray[0xF9] = opcode0xF9
279 | opcodeArray[0xFA] = opcode0xFA
280 | opcodeArray[0xFB] = opcode0xFB
281 | opcodeArray[0xFC] = opcode0xFC
282 | opcodeArray[0xFD] = opcode0xFD
283 | opcodeArray[0xFE] = opcode0xFE
284 | opcodeArray[0xFF] = opcode0xFF
285 |
286 | opcodeCBArray[0x00] = opcodeCB0x00
287 | opcodeCBArray[0x01] = opcodeCB0x01
288 | opcodeCBArray[0x02] = opcodeCB0x02
289 | opcodeCBArray[0x03] = opcodeCB0x03
290 | opcodeCBArray[0x04] = opcodeCB0x04
291 | opcodeCBArray[0x05] = opcodeCB0x05
292 | opcodeCBArray[0x06] = opcodeCB0x06
293 | opcodeCBArray[0x07] = opcodeCB0x07
294 | opcodeCBArray[0x08] = opcodeCB0x08
295 | opcodeCBArray[0x09] = opcodeCB0x09
296 | opcodeCBArray[0x0A] = opcodeCB0x0A
297 | opcodeCBArray[0x0B] = opcodeCB0x0B
298 | opcodeCBArray[0x0C] = opcodeCB0x0C
299 | opcodeCBArray[0x0D] = opcodeCB0x0D
300 | opcodeCBArray[0x0E] = opcodeCB0x0E
301 | opcodeCBArray[0x0F] = opcodeCB0x0F
302 |
303 | opcodeCBArray[0x10] = opcodeCB0x10
304 | opcodeCBArray[0x11] = opcodeCB0x11
305 | opcodeCBArray[0x12] = opcodeCB0x12
306 | opcodeCBArray[0x13] = opcodeCB0x13
307 | opcodeCBArray[0x14] = opcodeCB0x14
308 | opcodeCBArray[0x15] = opcodeCB0x15
309 | opcodeCBArray[0x16] = opcodeCB0x16
310 | opcodeCBArray[0x17] = opcodeCB0x17
311 | opcodeCBArray[0x18] = opcodeCB0x18
312 | opcodeCBArray[0x19] = opcodeCB0x19
313 | opcodeCBArray[0x1A] = opcodeCB0x1A
314 | opcodeCBArray[0x1B] = opcodeCB0x1B
315 | opcodeCBArray[0x1C] = opcodeCB0x1C
316 | opcodeCBArray[0x1D] = opcodeCB0x1D
317 | opcodeCBArray[0x1E] = opcodeCB0x1E
318 | opcodeCBArray[0x1F] = opcodeCB0x1F
319 |
320 | opcodeCBArray[0x20] = opcodeCB0x20
321 | opcodeCBArray[0x21] = opcodeCB0x21
322 | opcodeCBArray[0x22] = opcodeCB0x22
323 | opcodeCBArray[0x23] = opcodeCB0x23
324 | opcodeCBArray[0x24] = opcodeCB0x24
325 | opcodeCBArray[0x25] = opcodeCB0x25
326 | opcodeCBArray[0x26] = opcodeCB0x26
327 | opcodeCBArray[0x27] = opcodeCB0x27
328 | opcodeCBArray[0x28] = opcodeCB0x28
329 | opcodeCBArray[0x29] = opcodeCB0x29
330 | opcodeCBArray[0x2A] = opcodeCB0x2A
331 | opcodeCBArray[0x2B] = opcodeCB0x2B
332 | opcodeCBArray[0x2C] = opcodeCB0x2C
333 | opcodeCBArray[0x2D] = opcodeCB0x2D
334 | opcodeCBArray[0x2E] = opcodeCB0x2E
335 | opcodeCBArray[0x2F] = opcodeCB0x2F
336 |
337 | opcodeCBArray[0x30] = opcodeCB0x30
338 | opcodeCBArray[0x31] = opcodeCB0x31
339 | opcodeCBArray[0x32] = opcodeCB0x32
340 | opcodeCBArray[0x33] = opcodeCB0x33
341 | opcodeCBArray[0x34] = opcodeCB0x34
342 | opcodeCBArray[0x35] = opcodeCB0x35
343 | opcodeCBArray[0x36] = opcodeCB0x36
344 | opcodeCBArray[0x37] = opcodeCB0x37
345 | opcodeCBArray[0x38] = opcodeCB0x38
346 | opcodeCBArray[0x39] = opcodeCB0x39
347 | opcodeCBArray[0x3A] = opcodeCB0x3A
348 | opcodeCBArray[0x3B] = opcodeCB0x3B
349 | opcodeCBArray[0x3C] = opcodeCB0x3C
350 | opcodeCBArray[0x3D] = opcodeCB0x3D
351 | opcodeCBArray[0x3E] = opcodeCB0x3E
352 | opcodeCBArray[0x3F] = opcodeCB0x3F
353 |
354 | opcodeCBArray[0x40] = opcodeCB0x40
355 | opcodeCBArray[0x41] = opcodeCB0x41
356 | opcodeCBArray[0x42] = opcodeCB0x42
357 | opcodeCBArray[0x43] = opcodeCB0x43
358 | opcodeCBArray[0x44] = opcodeCB0x44
359 | opcodeCBArray[0x45] = opcodeCB0x45
360 | opcodeCBArray[0x46] = opcodeCB0x46
361 | opcodeCBArray[0x47] = opcodeCB0x47
362 | opcodeCBArray[0x48] = opcodeCB0x48
363 | opcodeCBArray[0x49] = opcodeCB0x49
364 | opcodeCBArray[0x4A] = opcodeCB0x4A
365 | opcodeCBArray[0x4B] = opcodeCB0x4B
366 | opcodeCBArray[0x4C] = opcodeCB0x4C
367 | opcodeCBArray[0x4D] = opcodeCB0x4D
368 | opcodeCBArray[0x4E] = opcodeCB0x4E
369 | opcodeCBArray[0x4F] = opcodeCB0x4F
370 |
371 | opcodeCBArray[0x50] = opcodeCB0x50
372 | opcodeCBArray[0x51] = opcodeCB0x51
373 | opcodeCBArray[0x52] = opcodeCB0x52
374 | opcodeCBArray[0x53] = opcodeCB0x53
375 | opcodeCBArray[0x54] = opcodeCB0x54
376 | opcodeCBArray[0x55] = opcodeCB0x55
377 | opcodeCBArray[0x56] = opcodeCB0x56
378 | opcodeCBArray[0x57] = opcodeCB0x57
379 | opcodeCBArray[0x58] = opcodeCB0x58
380 | opcodeCBArray[0x59] = opcodeCB0x59
381 | opcodeCBArray[0x5A] = opcodeCB0x5A
382 | opcodeCBArray[0x5B] = opcodeCB0x5B
383 | opcodeCBArray[0x5C] = opcodeCB0x5C
384 | opcodeCBArray[0x5D] = opcodeCB0x5D
385 | opcodeCBArray[0x5E] = opcodeCB0x5E
386 | opcodeCBArray[0x5F] = opcodeCB0x5F
387 |
388 | opcodeCBArray[0x60] = opcodeCB0x60
389 | opcodeCBArray[0x61] = opcodeCB0x61
390 | opcodeCBArray[0x62] = opcodeCB0x62
391 | opcodeCBArray[0x63] = opcodeCB0x63
392 | opcodeCBArray[0x64] = opcodeCB0x64
393 | opcodeCBArray[0x65] = opcodeCB0x65
394 | opcodeCBArray[0x66] = opcodeCB0x66
395 | opcodeCBArray[0x67] = opcodeCB0x67
396 | opcodeCBArray[0x68] = opcodeCB0x68
397 | opcodeCBArray[0x69] = opcodeCB0x69
398 | opcodeCBArray[0x6A] = opcodeCB0x6A
399 | opcodeCBArray[0x6B] = opcodeCB0x6B
400 | opcodeCBArray[0x6C] = opcodeCB0x6C
401 | opcodeCBArray[0x6D] = opcodeCB0x6D
402 | opcodeCBArray[0x6E] = opcodeCB0x6E
403 | opcodeCBArray[0x6F] = opcodeCB0x6F
404 |
405 | opcodeCBArray[0x70] = opcodeCB0x70
406 | opcodeCBArray[0x71] = opcodeCB0x71
407 | opcodeCBArray[0x72] = opcodeCB0x72
408 | opcodeCBArray[0x73] = opcodeCB0x73
409 | opcodeCBArray[0x74] = opcodeCB0x74
410 | opcodeCBArray[0x75] = opcodeCB0x75
411 | opcodeCBArray[0x76] = opcodeCB0x76
412 | opcodeCBArray[0x77] = opcodeCB0x77
413 | opcodeCBArray[0x78] = opcodeCB0x78
414 | opcodeCBArray[0x79] = opcodeCB0x79
415 | opcodeCBArray[0x7A] = opcodeCB0x7A
416 | opcodeCBArray[0x7B] = opcodeCB0x7B
417 | opcodeCBArray[0x7C] = opcodeCB0x7C
418 | opcodeCBArray[0x7D] = opcodeCB0x7D
419 | opcodeCBArray[0x7E] = opcodeCB0x7E
420 | opcodeCBArray[0x7F] = opcodeCB0x7F
421 |
422 | opcodeCBArray[0x80] = opcodeCB0x80
423 | opcodeCBArray[0x81] = opcodeCB0x81
424 | opcodeCBArray[0x82] = opcodeCB0x82
425 | opcodeCBArray[0x83] = opcodeCB0x83
426 | opcodeCBArray[0x84] = opcodeCB0x84
427 | opcodeCBArray[0x85] = opcodeCB0x85
428 | opcodeCBArray[0x86] = opcodeCB0x86
429 | opcodeCBArray[0x87] = opcodeCB0x87
430 | opcodeCBArray[0x88] = opcodeCB0x88
431 | opcodeCBArray[0x89] = opcodeCB0x89
432 | opcodeCBArray[0x8A] = opcodeCB0x8A
433 | opcodeCBArray[0x8B] = opcodeCB0x8B
434 | opcodeCBArray[0x8C] = opcodeCB0x8C
435 | opcodeCBArray[0x8D] = opcodeCB0x8D
436 | opcodeCBArray[0x8E] = opcodeCB0x8E
437 | opcodeCBArray[0x8F] = opcodeCB0x8F
438 |
439 | opcodeCBArray[0x90] = opcodeCB0x90
440 | opcodeCBArray[0x91] = opcodeCB0x91
441 | opcodeCBArray[0x92] = opcodeCB0x92
442 | opcodeCBArray[0x93] = opcodeCB0x93
443 | opcodeCBArray[0x94] = opcodeCB0x94
444 | opcodeCBArray[0x95] = opcodeCB0x95
445 | opcodeCBArray[0x96] = opcodeCB0x96
446 | opcodeCBArray[0x97] = opcodeCB0x97
447 | opcodeCBArray[0x98] = opcodeCB0x98
448 | opcodeCBArray[0x99] = opcodeCB0x99
449 | opcodeCBArray[0x9A] = opcodeCB0x9A
450 | opcodeCBArray[0x9B] = opcodeCB0x9B
451 | opcodeCBArray[0x9C] = opcodeCB0x9C
452 | opcodeCBArray[0x9D] = opcodeCB0x9D
453 | opcodeCBArray[0x9E] = opcodeCB0x9E
454 | opcodeCBArray[0x9F] = opcodeCB0x9F
455 |
456 | opcodeCBArray[0xA0] = opcodeCB0xA0
457 | opcodeCBArray[0xA1] = opcodeCB0xA1
458 | opcodeCBArray[0xA2] = opcodeCB0xA2
459 | opcodeCBArray[0xA3] = opcodeCB0xA3
460 | opcodeCBArray[0xA4] = opcodeCB0xA4
461 | opcodeCBArray[0xA5] = opcodeCB0xA5
462 | opcodeCBArray[0xA6] = opcodeCB0xA6
463 | opcodeCBArray[0xA7] = opcodeCB0xA7
464 | opcodeCBArray[0xA8] = opcodeCB0xA8
465 | opcodeCBArray[0xA9] = opcodeCB0xA9
466 | opcodeCBArray[0xAA] = opcodeCB0xAA
467 | opcodeCBArray[0xAB] = opcodeCB0xAB
468 | opcodeCBArray[0xAC] = opcodeCB0xAC
469 | opcodeCBArray[0xAD] = opcodeCB0xAD
470 | opcodeCBArray[0xAE] = opcodeCB0xAE
471 | opcodeCBArray[0xAF] = opcodeCB0xAF
472 |
473 | opcodeCBArray[0xB0] = opcodeCB0xB0
474 | opcodeCBArray[0xB1] = opcodeCB0xB1
475 | opcodeCBArray[0xB2] = opcodeCB0xB2
476 | opcodeCBArray[0xB3] = opcodeCB0xB3
477 | opcodeCBArray[0xB4] = opcodeCB0xB4
478 | opcodeCBArray[0xB5] = opcodeCB0xB5
479 | opcodeCBArray[0xB6] = opcodeCB0xB6
480 | opcodeCBArray[0xB7] = opcodeCB0xB7
481 | opcodeCBArray[0xB8] = opcodeCB0xB8
482 | opcodeCBArray[0xB9] = opcodeCB0xB9
483 | opcodeCBArray[0xBA] = opcodeCB0xBA
484 | opcodeCBArray[0xBB] = opcodeCB0xBB
485 | opcodeCBArray[0xBC] = opcodeCB0xBC
486 | opcodeCBArray[0xBD] = opcodeCB0xBD
487 | opcodeCBArray[0xBE] = opcodeCB0xBE
488 | opcodeCBArray[0xBF] = opcodeCB0xBF
489 |
490 | opcodeCBArray[0xC0] = opcodeCB0xC0
491 | opcodeCBArray[0xC1] = opcodeCB0xC1
492 | opcodeCBArray[0xC2] = opcodeCB0xC2
493 | opcodeCBArray[0xC3] = opcodeCB0xC3
494 | opcodeCBArray[0xC4] = opcodeCB0xC4
495 | opcodeCBArray[0xC5] = opcodeCB0xC5
496 | opcodeCBArray[0xC6] = opcodeCB0xC6
497 | opcodeCBArray[0xC7] = opcodeCB0xC7
498 | opcodeCBArray[0xC8] = opcodeCB0xC8
499 | opcodeCBArray[0xC9] = opcodeCB0xC9
500 | opcodeCBArray[0xCA] = opcodeCB0xCA
501 | opcodeCBArray[0xCB] = opcodeCB0xCB
502 | opcodeCBArray[0xCC] = opcodeCB0xCC
503 | opcodeCBArray[0xCD] = opcodeCB0xCD
504 | opcodeCBArray[0xCE] = opcodeCB0xCE
505 | opcodeCBArray[0xCF] = opcodeCB0xCF
506 |
507 | opcodeCBArray[0xD0] = opcodeCB0xD0
508 | opcodeCBArray[0xD1] = opcodeCB0xD1
509 | opcodeCBArray[0xD2] = opcodeCB0xD2
510 | opcodeCBArray[0xD3] = opcodeCB0xD3
511 | opcodeCBArray[0xD4] = opcodeCB0xD4
512 | opcodeCBArray[0xD5] = opcodeCB0xD5
513 | opcodeCBArray[0xD6] = opcodeCB0xD6
514 | opcodeCBArray[0xD7] = opcodeCB0xD7
515 | opcodeCBArray[0xD8] = opcodeCB0xD8
516 | opcodeCBArray[0xD9] = opcodeCB0xD9
517 | opcodeCBArray[0xDA] = opcodeCB0xDA
518 | opcodeCBArray[0xDB] = opcodeCB0xDB
519 | opcodeCBArray[0xDC] = opcodeCB0xDC
520 | opcodeCBArray[0xDD] = opcodeCB0xDD
521 | opcodeCBArray[0xDE] = opcodeCB0xDE
522 | opcodeCBArray[0xDF] = opcodeCB0xDF
523 |
524 | opcodeCBArray[0xE0] = opcodeCB0xE0
525 | opcodeCBArray[0xE1] = opcodeCB0xE1
526 | opcodeCBArray[0xE2] = opcodeCB0xE2
527 | opcodeCBArray[0xE3] = opcodeCB0xE3
528 | opcodeCBArray[0xE4] = opcodeCB0xE4
529 | opcodeCBArray[0xE5] = opcodeCB0xE5
530 | opcodeCBArray[0xE6] = opcodeCB0xE6
531 | opcodeCBArray[0xE7] = opcodeCB0xE7
532 | opcodeCBArray[0xE8] = opcodeCB0xE8
533 | opcodeCBArray[0xE9] = opcodeCB0xE9
534 | opcodeCBArray[0xEA] = opcodeCB0xEA
535 | opcodeCBArray[0xEB] = opcodeCB0xEB
536 | opcodeCBArray[0xEC] = opcodeCB0xEC
537 | opcodeCBArray[0xED] = opcodeCB0xED
538 | opcodeCBArray[0xEE] = opcodeCB0xEE
539 | opcodeCBArray[0xEF] = opcodeCB0xEF
540 |
541 | opcodeCBArray[0xF0] = opcodeCB0xF0
542 | opcodeCBArray[0xF1] = opcodeCB0xF1
543 | opcodeCBArray[0xF2] = opcodeCB0xF2
544 | opcodeCBArray[0xF3] = opcodeCB0xF3
545 | opcodeCBArray[0xF4] = opcodeCB0xF4
546 | opcodeCBArray[0xF5] = opcodeCB0xF5
547 | opcodeCBArray[0xF6] = opcodeCB0xF6
548 | opcodeCBArray[0xF7] = opcodeCB0xF7
549 | opcodeCBArray[0xF8] = opcodeCB0xF8
550 | opcodeCBArray[0xF9] = opcodeCB0xF9
551 | opcodeCBArray[0xFA] = opcodeCB0xFA
552 | opcodeCBArray[0xFB] = opcodeCB0xFB
553 | opcodeCBArray[0xFC] = opcodeCB0xFC
554 | opcodeCBArray[0xFD] = opcodeCB0xFD
555 | opcodeCBArray[0xFE] = opcodeCB0xFE
556 | opcodeCBArray[0xFF] = opcodeCB0xFF
557 | }
558 |
--------------------------------------------------------------------------------
/gb/cpu/normal_opcodes.go:
--------------------------------------------------------------------------------
1 | package cpu
2 |
3 | func opcode0x00() {
4 | // NOP
5 | }
6 |
7 | func opcode0x01() {
8 | // LD BC,nn
9 | opcodesLDAddrToReg(bc.GetLowReg(), pc.GetValue())
10 | pc.Increment()
11 | opcodesLDAddrToReg(bc.GetHighReg(), pc.GetValue())
12 | pc.Increment()
13 | }
14 |
15 | func opcode0x02() {
16 | // LD (BC),A
17 | opcodesLDValueToAddr(bc.GetValue(), af.GetHigh())
18 | }
19 |
20 | func opcode0x03() {
21 | // INC BC
22 | bc.Increment()
23 | }
24 |
25 | func opcode0x04() {
26 | // INC B
27 | opcodesINC(bc.GetHighReg())
28 | }
29 |
30 | func opcode0x05() {
31 | // DEC B
32 | opcodesDEC(bc.GetHighReg())
33 | }
34 |
35 | func opcode0x06() {
36 | // LD B,n
37 | opcodesLDAddrToReg(bc.GetHighReg(), pc.GetValue())
38 | pc.Increment()
39 | }
40 |
41 | func opcode0x07() {
42 | // RLCA
43 | opcodesRLCA(af.GetHighReg())
44 | }
45 |
46 | func opcode0x08() {
47 | // LD (nn),SP
48 | l := uint16(mem.Read(pc.GetValue()))
49 | pc.Increment()
50 | h := uint16(mem.Read(pc.GetValue()))
51 | pc.Increment()
52 | address := ((h << 8) + l)
53 | mem.Write(address, sp.GetLow())
54 | mem.Write(address+1, sp.GetHigh())
55 | }
56 |
57 | func opcode0x09() {
58 | // ADD HL,BC
59 | opcodesADDHL(bc.GetValue())
60 | }
61 |
62 | func opcode0x0A() {
63 | // LD A,(BC)
64 | opcodesLDAddrToReg(af.GetHighReg(), bc.GetValue())
65 | }
66 |
67 | func opcode0x0B() {
68 | // DEC BC
69 | bc.Decrement()
70 | }
71 |
72 | func opcode0x0C() {
73 | // INC C
74 | opcodesINC(bc.GetLowReg())
75 | }
76 |
77 | func opcode0x0D() {
78 | // DEC C
79 | opcodesDEC(bc.GetLowReg())
80 | }
81 |
82 | func opcode0x0E() {
83 | // LD C,n
84 | opcodesLDAddrToReg(bc.GetLowReg(), pc.GetValue())
85 | pc.Increment()
86 | }
87 |
88 | func opcode0x0F() {
89 | // RRCA
90 | opcodesRRCA(af.GetHighReg())
91 | }
92 |
93 | func opcode0x10() {
94 | // STOP
95 | pc.Increment()
96 | }
97 |
98 | func opcode0x11() {
99 | // LD DE,nn
100 | opcodesLDAddrToReg(de.GetLowReg(), pc.GetValue())
101 | pc.Increment()
102 | opcodesLDAddrToReg(de.GetHighReg(), pc.GetValue())
103 | pc.Increment()
104 | }
105 |
106 | func opcode0x12() {
107 | // LD (DE),A
108 | opcodesLDValueToAddr(de.GetValue(), af.GetHigh())
109 | }
110 |
111 | func opcode0x13() {
112 | // INC DE
113 | de.Increment()
114 | }
115 |
116 | func opcode0x14() {
117 | // INC D
118 | opcodesINC(de.GetHighReg())
119 | }
120 |
121 | func opcode0x15() {
122 | // DEC D
123 | opcodesDEC(de.GetHighReg())
124 | }
125 |
126 | func opcode0x16() {
127 | // LD D,n
128 | opcodesLDAddrToReg(de.GetHighReg(), pc.GetValue())
129 | pc.Increment()
130 | }
131 |
132 | func opcode0x17() {
133 | // RLA
134 | opcodesRLA(af.GetHighReg())
135 | }
136 |
137 | func opcode0x18() {
138 | // JR n
139 | offset := int8(mem.Read(pc.GetValue()))
140 | value := int(pc.GetValue()) + 1 + int(offset)
141 | pc.SetValue(uint16(value))
142 | }
143 |
144 | func opcode0x19() {
145 | // ADD HL,DE
146 | opcodesADDHL(de.GetValue())
147 | }
148 |
149 | func opcode0x1A() {
150 | // LD A,(DE)
151 | opcodesLDAddrToReg(af.GetHighReg(), de.GetValue())
152 | }
153 |
154 | func opcode0x1B() {
155 | // DEC DE
156 | de.Decrement()
157 | }
158 |
159 | func opcode0x1C() {
160 | // INC E
161 | opcodesINC(de.GetLowReg())
162 | }
163 |
164 | func opcode0x1D() {
165 | // DEC E
166 | opcodesDEC(de.GetLowReg())
167 | }
168 |
169 | func opcode0x1E() {
170 | // LD E,n
171 | opcodesLDAddrToReg(de.GetLowReg(), pc.GetValue())
172 | pc.Increment()
173 | }
174 |
175 | func opcode0x1F() {
176 | // RRA
177 | opcodesRRA(af.GetHighReg())
178 | }
179 |
180 | func opcode0x20() {
181 | // JR NZ,n
182 | if !isSetFlag(flagZero) {
183 | offset := int8(mem.Read(pc.GetValue()))
184 | value := int(pc.GetValue()) + 1 + int(offset)
185 | pc.SetValue(uint16(value))
186 | branchTaken = true
187 | } else {
188 | pc.Increment()
189 | }
190 | }
191 |
192 | func opcode0x21() {
193 | // LD HL,nn
194 | opcodesLDAddrToReg(hl.GetLowReg(), pc.GetValue())
195 | pc.Increment()
196 | opcodesLDAddrToReg(hl.GetHighReg(), pc.GetValue())
197 | pc.Increment()
198 | }
199 |
200 | func opcode0x22() {
201 | // LD (HLI),A
202 | opcodesLDValueToAddr(hl.GetValue(), af.GetHigh())
203 | hl.Increment()
204 | }
205 |
206 | func opcode0x23() {
207 | // INC HL
208 | hl.Increment()
209 | }
210 |
211 | func opcode0x24() {
212 | // INC H
213 | opcodesINC(hl.GetHighReg())
214 | }
215 |
216 | func opcode0x25() {
217 | // DEC H
218 | opcodesDEC(hl.GetHighReg())
219 | }
220 |
221 | func opcode0x26() {
222 | // LD H,n
223 | opcodesLDAddrToReg(hl.GetHighReg(), pc.GetValue())
224 | pc.Increment()
225 | }
226 |
227 | func opcode0x27() {
228 | // DAA
229 | a := uint16(af.GetHigh())
230 |
231 | if !isSetFlag(flagNegative) {
232 | if isSetFlag(flagHalf) || ((a & 0xF) > 9) {
233 | a += 0x06
234 | }
235 | if isSetFlag(flagCarry) || (a > 0x9F) {
236 | a += 0x60
237 | }
238 | } else {
239 | if isSetFlag(flagHalf) {
240 | a = (a - 6) & 0xFF
241 | }
242 | if isSetFlag(flagCarry) {
243 | a -= 0x60
244 | }
245 | }
246 |
247 | resetFlag(flagHalf)
248 | resetFlag(flagZero)
249 |
250 | if (a & 0x100) == 0x100 {
251 | setFlag(flagCarry)
252 | }
253 |
254 | a &= 0xFF
255 |
256 | setZeroFlagFromResult(uint8(a))
257 |
258 | af.SetHigh(uint8(a))
259 | }
260 |
261 | func opcode0x28() {
262 | // JR Z,n
263 | if isSetFlag(flagZero) {
264 | offset := int8(mem.Read(pc.GetValue()))
265 | value := int(pc.GetValue()) + 1 + int(offset)
266 | pc.SetValue(uint16(value))
267 | branchTaken = true
268 | } else {
269 | pc.Increment()
270 | }
271 | }
272 |
273 | func opcode0x29() {
274 | // ADD HL,HL
275 | opcodesADDHL(hl.GetValue())
276 | }
277 |
278 | func opcode0x2A() {
279 | // LD A,(HLI)
280 | opcodesLDAddrToReg(af.GetHighReg(), hl.GetValue())
281 | hl.Increment()
282 | }
283 |
284 | func opcode0x2B() {
285 | // DEC HL
286 | hl.Decrement()
287 | }
288 |
289 | func opcode0x2C() {
290 | // INC L
291 | opcodesINC(hl.GetLowReg())
292 | }
293 |
294 | func opcode0x2D() {
295 | // DEC L
296 | opcodesDEC(hl.GetLowReg())
297 | }
298 |
299 | func opcode0x2E() {
300 | // LD L,n
301 | opcodesLDAddrToReg(hl.GetLowReg(), pc.GetValue())
302 | pc.Increment()
303 | }
304 |
305 | func opcode0x2F() {
306 | // CPL
307 | af.SetHigh(^af.GetHigh())
308 | setFlag(flagHalf)
309 | setFlag(flagNegative)
310 | }
311 |
312 | func opcode0x30() {
313 | // JR NC,n
314 | if !isSetFlag(flagCarry) {
315 | offset := int8(mem.Read(pc.GetValue()))
316 | value := int(pc.GetValue()) + 1 + int(offset)
317 | pc.SetValue(uint16(value))
318 | branchTaken = true
319 | } else {
320 | pc.Increment()
321 | }
322 | }
323 |
324 | func opcode0x31() {
325 | // LD SP,nn
326 | sp.SetLow(mem.Read(pc.GetValue()))
327 | pc.Increment()
328 | sp.SetHigh(mem.Read(pc.GetValue()))
329 | pc.Increment()
330 | }
331 |
332 | func opcode0x32() {
333 | // LD (HLD), A
334 | opcodesLDValueToAddr(hl.GetValue(), af.GetHigh())
335 | hl.Decrement()
336 | }
337 |
338 | func opcode0x33() {
339 | // INC SP
340 | sp.Increment()
341 | }
342 |
343 | func opcode0x34() {
344 | // INC (HL)
345 | opcodesINCHL()
346 | }
347 |
348 | func opcode0x35() {
349 | // DEC (HL)
350 | opcodesDECHL()
351 | }
352 |
353 | func opcode0x36() {
354 | // LD (HL),n
355 | mem.Write(hl.GetValue(), mem.Read(pc.GetValue()))
356 | pc.Increment()
357 | }
358 |
359 | func opcode0x37() {
360 | // SCF
361 | setFlag(flagCarry)
362 | resetFlag(flagHalf)
363 | resetFlag(flagNegative)
364 | }
365 |
366 | func opcode0x38() {
367 | // JR C,n
368 | if isSetFlag(flagCarry) {
369 | offset := int8(mem.Read(pc.GetValue()))
370 | value := int(pc.GetValue()) + 1 + int(offset)
371 | pc.SetValue(uint16(value))
372 | branchTaken = true
373 | } else {
374 | pc.Increment()
375 | }
376 | }
377 |
378 | func opcode0x39() {
379 | // ADD HL,SP
380 | opcodesADDHL(sp.GetValue())
381 | }
382 |
383 | func opcode0x3A() {
384 | // LD A,(HLD)
385 | opcodesLDAddrToReg(af.GetHighReg(), hl.GetValue())
386 | hl.Decrement()
387 | }
388 |
389 | func opcode0x3B() {
390 | // DEC SP
391 | sp.Decrement()
392 | }
393 |
394 | func opcode0x3C() {
395 | // INC A
396 | opcodesINC(af.GetHighReg())
397 | }
398 |
399 | func opcode0x3D() {
400 | // DEC A
401 | opcodesDEC(af.GetHighReg())
402 |
403 | }
404 |
405 | func opcode0x3E() {
406 | // LD A,n
407 | opcodesLDAddrToReg(af.GetHighReg(), pc.GetValue())
408 | pc.Increment()
409 | }
410 |
411 | func opcode0x3F() {
412 | // CCF
413 | flipFlag(flagCarry)
414 | resetFlag(flagHalf)
415 | resetFlag(flagNegative)
416 | }
417 |
418 | func opcode0x40() {
419 | // LD B,B
420 | opcodesLDValueToReg(bc.GetHighReg(), bc.GetHigh())
421 | }
422 |
423 | func opcode0x41() {
424 | // LD B,C
425 | opcodesLDValueToReg(bc.GetHighReg(), bc.GetLow())
426 | }
427 |
428 | func opcode0x42() {
429 | // LD B,D
430 | opcodesLDValueToReg(bc.GetHighReg(), de.GetHigh())
431 | }
432 |
433 | func opcode0x43() {
434 | // LD B,E
435 | opcodesLDValueToReg(bc.GetHighReg(), de.GetLow())
436 | }
437 |
438 | func opcode0x44() {
439 | // LD B,H
440 | opcodesLDValueToReg(bc.GetHighReg(), hl.GetHigh())
441 | }
442 |
443 | func opcode0x45() {
444 | // LD B,L
445 | opcodesLDValueToReg(bc.GetHighReg(), hl.GetLow())
446 | }
447 |
448 | func opcode0x46() {
449 | // LD B,(HL)
450 | opcodesLDAddrToReg(bc.GetHighReg(), hl.GetValue())
451 | }
452 |
453 | func opcode0x47() {
454 | // LD B,A
455 | opcodesLDValueToReg(bc.GetHighReg(), af.GetHigh())
456 | }
457 |
458 | func opcode0x48() {
459 | // LD C,B
460 | opcodesLDValueToReg(bc.GetLowReg(), bc.GetHigh())
461 | }
462 |
463 | func opcode0x49() {
464 | // LD C,C
465 | opcodesLDValueToReg(bc.GetLowReg(), bc.GetLow())
466 | }
467 |
468 | func opcode0x4A() {
469 | // LD C,D
470 | opcodesLDValueToReg(bc.GetLowReg(), de.GetHigh())
471 | }
472 |
473 | func opcode0x4B() {
474 | // LD C,E
475 | opcodesLDValueToReg(bc.GetLowReg(), de.GetLow())
476 | }
477 |
478 | func opcode0x4C() {
479 | // LD C,H
480 | opcodesLDValueToReg(bc.GetLowReg(), hl.GetHigh())
481 | }
482 |
483 | func opcode0x4D() {
484 | // LD C,L
485 | opcodesLDValueToReg(bc.GetLowReg(), hl.GetLow())
486 | }
487 |
488 | func opcode0x4E() {
489 | // LD C,(HL)
490 | opcodesLDAddrToReg(bc.GetLowReg(), hl.GetValue())
491 | }
492 |
493 | func opcode0x4F() {
494 | // LD C,A
495 | opcodesLDValueToReg(bc.GetLowReg(), af.GetHigh())
496 | }
497 |
498 | func opcode0x50() {
499 | // LD D,B
500 | opcodesLDValueToReg(de.GetHighReg(), bc.GetHigh())
501 | }
502 |
503 | func opcode0x51() {
504 | // LD D,C
505 | opcodesLDValueToReg(de.GetHighReg(), bc.GetLow())
506 | }
507 |
508 | func opcode0x52() {
509 | // LD D,D
510 | opcodesLDValueToReg(de.GetHighReg(), de.GetHigh())
511 | }
512 |
513 | func opcode0x53() {
514 | // LD D,E
515 | opcodesLDValueToReg(de.GetHighReg(), de.GetLow())
516 | }
517 |
518 | func opcode0x54() {
519 | // LD D,H
520 | opcodesLDValueToReg(de.GetHighReg(), hl.GetHigh())
521 | }
522 |
523 | func opcode0x55() {
524 | // LD D,L
525 | opcodesLDValueToReg(de.GetHighReg(), hl.GetLow())
526 | }
527 |
528 | func opcode0x56() {
529 | // LD D,(HL)
530 | opcodesLDAddrToReg(de.GetHighReg(), hl.GetValue())
531 | }
532 |
533 | func opcode0x57() {
534 | // LD D,A
535 | opcodesLDValueToReg(de.GetHighReg(), af.GetHigh())
536 | }
537 |
538 | func opcode0x58() {
539 | // LD E,B
540 | opcodesLDValueToReg(de.GetLowReg(), bc.GetHigh())
541 | }
542 |
543 | func opcode0x59() {
544 | // LD E,C
545 | opcodesLDValueToReg(de.GetLowReg(), bc.GetLow())
546 | }
547 |
548 | func opcode0x5A() {
549 | // LD E,D
550 | opcodesLDValueToReg(de.GetLowReg(), de.GetHigh())
551 | }
552 |
553 | func opcode0x5B() {
554 | // LD E,E
555 | opcodesLDValueToReg(de.GetLowReg(), de.GetLow())
556 | }
557 |
558 | func opcode0x5C() {
559 | // LD E,H
560 | opcodesLDValueToReg(de.GetLowReg(), hl.GetHigh())
561 | }
562 |
563 | func opcode0x5D() {
564 | // LD E,L
565 | opcodesLDValueToReg(de.GetLowReg(), hl.GetLow())
566 | }
567 |
568 | func opcode0x5E() {
569 | // LD E,(HL)
570 | opcodesLDAddrToReg(de.GetLowReg(), hl.GetValue())
571 | }
572 |
573 | func opcode0x5F() {
574 | // LD E,A
575 | opcodesLDValueToReg(de.GetLowReg(), af.GetHigh())
576 | }
577 |
578 | func opcode0x60() {
579 | // LD H,B
580 | opcodesLDValueToReg(hl.GetHighReg(), bc.GetHigh())
581 | }
582 |
583 | func opcode0x61() {
584 | // LD H,C
585 | opcodesLDValueToReg(hl.GetHighReg(), bc.GetLow())
586 | }
587 |
588 | func opcode0x62() {
589 | // LD H,D
590 | opcodesLDValueToReg(hl.GetHighReg(), de.GetHigh())
591 | }
592 |
593 | func opcode0x63() {
594 | // LD H,E
595 | opcodesLDValueToReg(hl.GetHighReg(), de.GetLow())
596 | }
597 |
598 | func opcode0x64() {
599 | // LD H,H
600 | opcodesLDValueToReg(hl.GetHighReg(), hl.GetHigh())
601 | }
602 |
603 | func opcode0x65() {
604 | // LD H,L
605 | opcodesLDValueToReg(hl.GetHighReg(), hl.GetLow())
606 | }
607 |
608 | func opcode0x66() {
609 | // LD H,(HL)
610 | opcodesLDAddrToReg(hl.GetHighReg(), hl.GetValue())
611 | }
612 |
613 | func opcode0x67() {
614 | // LD H,A
615 | opcodesLDValueToReg(hl.GetHighReg(), af.GetHigh())
616 | }
617 |
618 | func opcode0x68() {
619 | // LD L,B
620 | opcodesLDValueToReg(hl.GetLowReg(), bc.GetHigh())
621 | }
622 |
623 | func opcode0x69() {
624 | // LD L,C
625 | opcodesLDValueToReg(hl.GetLowReg(), bc.GetLow())
626 | }
627 |
628 | func opcode0x6A() {
629 | // LD L,D
630 | opcodesLDValueToReg(hl.GetLowReg(), de.GetHigh())
631 | }
632 |
633 | func opcode0x6B() {
634 | // LD L,E
635 | opcodesLDValueToReg(hl.GetLowReg(), de.GetLow())
636 | }
637 |
638 | func opcode0x6C() {
639 | // LD L,H
640 | opcodesLDValueToReg(hl.GetLowReg(), hl.GetHigh())
641 | }
642 |
643 | func opcode0x6D() {
644 | // LD L,L
645 | opcodesLDValueToReg(hl.GetLowReg(), hl.GetLow())
646 | }
647 |
648 | func opcode0x6E() {
649 | // LD L,(HL)
650 | opcodesLDAddrToReg(hl.GetLowReg(), hl.GetValue())
651 | }
652 |
653 | func opcode0x6F() {
654 | // LD L,A
655 | opcodesLDValueToReg(hl.GetLowReg(), af.GetHigh())
656 | }
657 |
658 | func opcode0x70() {
659 | // LD (HL),B
660 | opcodesLDValueToAddr(hl.GetValue(), bc.GetHigh())
661 | }
662 |
663 | func opcode0x71() {
664 | // LD (HL),C
665 | opcodesLDValueToAddr(hl.GetValue(), bc.GetLow())
666 | }
667 |
668 | func opcode0x72() {
669 | // LD (HL),D
670 | opcodesLDValueToAddr(hl.GetValue(), de.GetHigh())
671 | }
672 |
673 | func opcode0x73() {
674 | // LD (HL),E
675 | opcodesLDValueToAddr(hl.GetValue(), de.GetLow())
676 | }
677 |
678 | func opcode0x74() {
679 | // LD (HL),H
680 | opcodesLDValueToAddr(hl.GetValue(), hl.GetHigh())
681 | }
682 |
683 | func opcode0x75() {
684 | // LD (HL),L
685 | opcodesLDValueToAddr(hl.GetValue(), hl.GetLow())
686 | }
687 |
688 | func opcode0x76() {
689 | // HALT
690 | if imeCycles > 0 {
691 | // If EI is pending interrupts are triggered before Halt
692 | imeCycles = 0
693 | ime = true
694 | pc.Decrement()
695 | } else {
696 | ifreg := mem.Read(0xFF0F)
697 | iereg := mem.Read(0xFFFF)
698 |
699 | halted = true
700 |
701 | // if there is an interrupt pending and
702 | // the cpu is halted it fails to advance the PC register
703 | // once the cpu resumes operation
704 | // this bug is present in all the original DMGs
705 | if !ime && ((ifreg & iereg & 0x1F) != 0) {
706 | skipPCBug = true
707 | }
708 | }
709 | }
710 |
711 | func opcode0x77() {
712 | // LD (HL),A
713 | opcodesLDValueToAddr(hl.GetValue(), af.GetHigh())
714 | }
715 |
716 | func opcode0x78() {
717 | // LD A,B
718 | opcodesLDValueToReg(af.GetHighReg(), bc.GetHigh())
719 | }
720 |
721 | func opcode0x79() {
722 | // LD A,C
723 | opcodesLDValueToReg(af.GetHighReg(), bc.GetLow())
724 | }
725 |
726 | func opcode0x7A() {
727 | // LD A,D
728 | opcodesLDValueToReg(af.GetHighReg(), de.GetHigh())
729 | }
730 |
731 | func opcode0x7B() {
732 | // LD A,E
733 | opcodesLDValueToReg(af.GetHighReg(), de.GetLow())
734 | }
735 |
736 | func opcode0x7C() {
737 | // LD A,H
738 | opcodesLDValueToReg(af.GetHighReg(), hl.GetHigh())
739 | }
740 |
741 | func opcode0x7D() {
742 | // LD A,L
743 | opcodesLDValueToReg(af.GetHighReg(), hl.GetLow())
744 | }
745 |
746 | func opcode0x7E() {
747 | // LD A,(HL)
748 | opcodesLDAddrToReg(af.GetHighReg(), hl.GetValue())
749 | }
750 |
751 | func opcode0x7F() {
752 | // LD A,A
753 | opcodesLDValueToReg(af.GetHighReg(), af.GetHigh())
754 | }
755 |
756 | func opcode0x80() {
757 | // ADD A,B
758 | opcodesADD(bc.GetHigh())
759 | }
760 |
761 | func opcode0x81() {
762 | // ADD A,C
763 | opcodesADD(bc.GetLow())
764 | }
765 |
766 | func opcode0x82() {
767 | // ADD A,D
768 | opcodesADD(de.GetHigh())
769 | }
770 |
771 | func opcode0x83() {
772 | // ADD A,E
773 | opcodesADD(de.GetLow())
774 | }
775 |
776 | func opcode0x84() {
777 | // ADD A,H
778 | opcodesADD(hl.GetHigh())
779 | }
780 |
781 | func opcode0x85() {
782 | // ADD A,L
783 | opcodesADD(hl.GetLow())
784 | }
785 |
786 | func opcode0x86() {
787 | // ADD A,(HL)
788 | opcodesADD(mem.Read(hl.GetValue()))
789 | }
790 |
791 | func opcode0x87() {
792 | // ADD A,A
793 | opcodesADD(af.GetHigh())
794 | }
795 |
796 | func opcode0x88() {
797 | // ADC A,B
798 | opcodesADC(bc.GetHigh())
799 | }
800 |
801 | func opcode0x89() {
802 | // ADC A,C
803 | opcodesADC(bc.GetLow())
804 | }
805 |
806 | func opcode0x8A() {
807 | // ADC A,D
808 | opcodesADC(de.GetHigh())
809 | }
810 |
811 | func opcode0x8B() {
812 | // ADC A,E
813 | opcodesADC(de.GetLow())
814 | }
815 |
816 | func opcode0x8C() {
817 | // ADC A,H
818 | opcodesADC(hl.GetHigh())
819 | }
820 |
821 | func opcode0x8D() {
822 | // ADC A,L
823 | opcodesADC(hl.GetLow())
824 | }
825 |
826 | func opcode0x8E() {
827 | // ADC A,(HL)
828 | opcodesADC(mem.Read(hl.GetValue()))
829 | }
830 |
831 | func opcode0x8F() {
832 | // ADC A,A
833 | opcodesADC(af.GetHigh())
834 | }
835 |
836 | func opcode0x90() {
837 | // SUB B
838 | opcodesSUB(bc.GetHigh())
839 | }
840 |
841 | func opcode0x91() {
842 | // SUB C
843 | opcodesSUB(bc.GetLow())
844 | }
845 |
846 | func opcode0x92() {
847 | // SUB D
848 | opcodesSUB(de.GetHigh())
849 | }
850 |
851 | func opcode0x93() {
852 | // SUB E
853 | opcodesSUB(de.GetLow())
854 | }
855 |
856 | func opcode0x94() {
857 | // SUB H
858 | opcodesSUB(hl.GetHigh())
859 | }
860 |
861 | func opcode0x95() {
862 | // SUB L
863 | opcodesSUB(hl.GetLow())
864 | }
865 |
866 | func opcode0x96() {
867 | // SUB (HL)
868 | opcodesSUB(mem.Read(hl.GetValue()))
869 | }
870 |
871 | func opcode0x97() {
872 | // SUB A
873 | opcodesSUB(af.GetHigh())
874 | }
875 |
876 | func opcode0x98() {
877 | // SBC B
878 | opcodesSBC(bc.GetHigh())
879 | }
880 |
881 | func opcode0x99() {
882 | // SBC C
883 | opcodesSBC(bc.GetLow())
884 | }
885 |
886 | func opcode0x9A() {
887 | // SBC D
888 | opcodesSBC(de.GetHigh())
889 | }
890 |
891 | func opcode0x9B() {
892 | // SBC E
893 | opcodesSBC(de.GetLow())
894 | }
895 |
896 | func opcode0x9C() {
897 | // SBC H
898 | opcodesSBC(hl.GetHigh())
899 | }
900 |
901 | func opcode0x9D() {
902 | // SBC L
903 | opcodesSBC(hl.GetLow())
904 | }
905 |
906 | func opcode0x9E() {
907 | // SBC (HL)
908 | opcodesSBC(mem.Read(hl.GetValue()))
909 | }
910 |
911 | func opcode0x9F() {
912 | // SBC A
913 | opcodesSBC(af.GetHigh())
914 | }
915 |
916 | func opcode0xA0() {
917 | // AND B
918 | opcodesAND(bc.GetHigh())
919 | }
920 |
921 | func opcode0xA1() {
922 | // AND C
923 | opcodesAND(bc.GetLow())
924 | }
925 |
926 | func opcode0xA2() {
927 | // AND D
928 | opcodesAND(de.GetHigh())
929 | }
930 |
931 | func opcode0xA3() {
932 | // AND E
933 | opcodesAND(de.GetLow())
934 | }
935 |
936 | func opcode0xA4() {
937 | // AND H
938 | opcodesAND(hl.GetHigh())
939 | }
940 |
941 | func opcode0xA5() {
942 | // AND L
943 | opcodesAND(hl.GetLow())
944 | }
945 |
946 | func opcode0xA6() {
947 | // AND (HL)
948 | opcodesAND(mem.Read(hl.GetValue()))
949 | }
950 |
951 | func opcode0xA7() {
952 | // AND A
953 | opcodesAND(af.GetHigh())
954 | }
955 |
956 | func opcode0xA8() {
957 | // XOR B
958 | opcodesXOR(bc.GetHigh())
959 | }
960 |
961 | func opcode0xA9() {
962 | // XOR C
963 | opcodesXOR(bc.GetLow())
964 | }
965 |
966 | func opcode0xAA() {
967 | // XOR D
968 | opcodesXOR(de.GetHigh())
969 | }
970 |
971 | func opcode0xAB() {
972 | // XOR E
973 | opcodesXOR(de.GetLow())
974 | }
975 |
976 | func opcode0xAC() {
977 | // XOR H
978 | opcodesXOR(hl.GetHigh())
979 | }
980 |
981 | func opcode0xAD() {
982 | // XOR L
983 | opcodesXOR(hl.GetLow())
984 | }
985 |
986 | func opcode0xAE() {
987 | // XOR (HL)
988 | opcodesXOR(mem.Read(hl.GetValue()))
989 | }
990 |
991 | func opcode0xAF() {
992 | // XOR A
993 | opcodesXOR(af.GetHigh())
994 | }
995 |
996 | func opcode0xB0() {
997 | // OR B
998 | opcodesOR(bc.GetHigh())
999 | }
1000 |
1001 | func opcode0xB1() {
1002 | // OR C
1003 | opcodesOR(bc.GetLow())
1004 | }
1005 |
1006 | func opcode0xB2() {
1007 | // OR D
1008 | opcodesOR(de.GetHigh())
1009 | }
1010 |
1011 | func opcode0xB3() {
1012 | // OR E
1013 | opcodesOR(de.GetLow())
1014 | }
1015 |
1016 | func opcode0xB4() {
1017 | // OR H
1018 | opcodesOR(hl.GetHigh())
1019 | }
1020 |
1021 | func opcode0xB5() {
1022 | // OR L
1023 | opcodesOR(hl.GetLow())
1024 | }
1025 |
1026 | func opcode0xB6() {
1027 | // OR (HL)
1028 | opcodesOR(mem.Read(hl.GetValue()))
1029 | }
1030 |
1031 | func opcode0xB7() {
1032 | // OR A
1033 | opcodesOR(af.GetHigh())
1034 | }
1035 |
1036 | func opcode0xB8() {
1037 | // CP B
1038 | opcodesCP(bc.GetHigh())
1039 | }
1040 |
1041 | func opcode0xB9() {
1042 | // CP C
1043 | opcodesCP(bc.GetLow())
1044 | }
1045 |
1046 | func opcode0xBA() {
1047 | // CP D
1048 | opcodesCP(de.GetHigh())
1049 | }
1050 |
1051 | func opcode0xBB() {
1052 | // CP E
1053 | opcodesCP(de.GetLow())
1054 | }
1055 |
1056 | func opcode0xBC() {
1057 | // CP H
1058 | opcodesCP(hl.GetHigh())
1059 | }
1060 |
1061 | func opcode0xBD() {
1062 | // CP L
1063 | opcodesCP(hl.GetLow())
1064 | }
1065 |
1066 | func opcode0xBE() {
1067 | // CP (HL)
1068 | opcodesCP(mem.Read(hl.GetValue()))
1069 | }
1070 |
1071 | func opcode0xBF() {
1072 | // CP A
1073 | opcodesCP(af.GetHigh())
1074 | }
1075 |
1076 | func opcode0xC0() {
1077 | // RET NZ
1078 | if !isSetFlag(flagZero) {
1079 | stackPop(&pc)
1080 | branchTaken = true
1081 | }
1082 | }
1083 |
1084 | func opcode0xC1() {
1085 | // POP BC
1086 | stackPop(&bc)
1087 | }
1088 |
1089 | func opcode0xC2() {
1090 | // JP NZ,nn
1091 | if !isSetFlag(flagZero) {
1092 | l := mem.Read(pc.GetValue())
1093 | pc.Increment()
1094 | h := mem.Read(pc.GetValue())
1095 | pc.SetHigh(h)
1096 | pc.SetLow(l)
1097 | branchTaken = true
1098 | } else {
1099 | pc.Increment()
1100 | pc.Increment()
1101 | }
1102 | }
1103 |
1104 | func opcode0xC3() {
1105 | // JP nn
1106 | l := mem.Read(pc.GetValue())
1107 | pc.Increment()
1108 | h := mem.Read(pc.GetValue())
1109 | pc.SetHigh(h)
1110 | pc.SetLow(l)
1111 | }
1112 |
1113 | func opcode0xC4() {
1114 | // CALL NZ,nn
1115 | if !isSetFlag(flagZero) {
1116 | l := mem.Read(pc.GetValue())
1117 | pc.Increment()
1118 | h := mem.Read(pc.GetValue())
1119 | pc.Increment()
1120 | stackPush(&pc)
1121 | pc.SetHigh(h)
1122 | pc.SetLow(l)
1123 | branchTaken = true
1124 | } else {
1125 | pc.Increment()
1126 | pc.Increment()
1127 | }
1128 | }
1129 |
1130 | func opcode0xC5() {
1131 | // PUSH BC
1132 | stackPush(&bc)
1133 | }
1134 |
1135 | func opcode0xC6() {
1136 | // ADD A,n
1137 | opcodesADD(mem.Read(pc.GetValue()))
1138 | pc.Increment()
1139 | }
1140 |
1141 | func opcode0xC7() {
1142 | // RST 00H
1143 | stackPush(&pc)
1144 | pc.SetValue(0x0000)
1145 | }
1146 |
1147 | func opcode0xC8() {
1148 | // RET Z
1149 | if isSetFlag(flagZero) {
1150 | stackPop(&pc)
1151 | branchTaken = true
1152 | }
1153 | }
1154 |
1155 | func opcode0xC9() {
1156 | // RET
1157 | stackPop(&pc)
1158 | }
1159 |
1160 | func opcode0xCA() {
1161 | // JP Z,nn
1162 | if isSetFlag(flagZero) {
1163 | l := mem.Read(pc.GetValue())
1164 | pc.Increment()
1165 | h := mem.Read(pc.GetValue())
1166 | pc.SetHigh(h)
1167 | pc.SetLow(l)
1168 | branchTaken = true
1169 | } else {
1170 | pc.Increment()
1171 | pc.Increment()
1172 | }
1173 | }
1174 |
1175 | func opcode0xCB() {
1176 | // CB prefixed instruction
1177 | }
1178 |
1179 | func opcode0xCC() {
1180 | // CALL Z,nn
1181 | if isSetFlag(flagZero) {
1182 | l := mem.Read(pc.GetValue())
1183 | pc.Increment()
1184 | h := mem.Read(pc.GetValue())
1185 | pc.Increment()
1186 | stackPush(&pc)
1187 | pc.SetHigh(h)
1188 | pc.SetLow(l)
1189 | branchTaken = true
1190 | } else {
1191 | pc.Increment()
1192 | pc.Increment()
1193 | }
1194 | }
1195 |
1196 | func opcode0xCD() {
1197 | // CALL nn
1198 | l := mem.Read(pc.GetValue())
1199 | pc.Increment()
1200 | h := mem.Read(pc.GetValue())
1201 | pc.Increment()
1202 | stackPush(&pc)
1203 | pc.SetHigh(h)
1204 | pc.SetLow(l)
1205 | }
1206 |
1207 | func opcode0xCE() {
1208 | // ADC A,n
1209 | opcodesADC(mem.Read(pc.GetValue()))
1210 | pc.Increment()
1211 | }
1212 |
1213 | func opcode0xCF() {
1214 | // RST 08H
1215 | stackPush(&pc)
1216 | pc.SetValue(0x0008)
1217 | }
1218 |
1219 | func opcode0xD0() {
1220 | // RET NC
1221 | if !isSetFlag(flagCarry) {
1222 | stackPop(&pc)
1223 | branchTaken = true
1224 | }
1225 | }
1226 |
1227 | func opcode0xD1() {
1228 | // POP DE
1229 | stackPop(&de)
1230 | }
1231 |
1232 | func opcode0xD2() {
1233 | // JP NC,nn
1234 | if !isSetFlag(flagCarry) {
1235 | l := mem.Read(pc.GetValue())
1236 | pc.Increment()
1237 | h := mem.Read(pc.GetValue())
1238 | pc.SetHigh(h)
1239 | pc.SetLow(l)
1240 | branchTaken = true
1241 | } else {
1242 | pc.Increment()
1243 | pc.Increment()
1244 | }
1245 | }
1246 |
1247 | func opcode0xD3() {
1248 | invalidOPCode()
1249 | }
1250 |
1251 | func opcode0xD4() {
1252 | // CALL NC,nn
1253 | if !isSetFlag(flagCarry) {
1254 | l := mem.Read(pc.GetValue())
1255 | pc.Increment()
1256 | h := mem.Read(pc.GetValue())
1257 | pc.Increment()
1258 | stackPush(&pc)
1259 | pc.SetHigh(h)
1260 | pc.SetLow(l)
1261 | branchTaken = true
1262 | } else {
1263 | pc.Increment()
1264 | pc.Increment()
1265 | }
1266 | }
1267 |
1268 | func opcode0xD5() {
1269 | // PUSH DE
1270 | stackPush(&de)
1271 | }
1272 |
1273 | func opcode0xD6() {
1274 | // SUB n
1275 | opcodesSUB(mem.Read(pc.GetValue()))
1276 | pc.Increment()
1277 | }
1278 |
1279 | func opcode0xD7() {
1280 | // RST 10H
1281 | stackPush(&pc)
1282 | pc.SetValue(0x0010)
1283 | }
1284 |
1285 | func opcode0xD8() {
1286 | // RET C
1287 | if isSetFlag(flagCarry) {
1288 | stackPop(&pc)
1289 | branchTaken = true
1290 | }
1291 | }
1292 |
1293 | func opcode0xD9() {
1294 | // RETI
1295 | stackPop(&pc)
1296 | ime = true
1297 | }
1298 |
1299 | func opcode0xDA() {
1300 | // JP C,nn
1301 | if isSetFlag(flagCarry) {
1302 | l := mem.Read(pc.GetValue())
1303 | pc.Increment()
1304 | h := mem.Read(pc.GetValue())
1305 | pc.SetHigh(h)
1306 | pc.SetLow(l)
1307 | branchTaken = true
1308 | } else {
1309 | pc.Increment()
1310 | pc.Increment()
1311 | }
1312 | }
1313 |
1314 | func opcode0xDB() {
1315 | invalidOPCode()
1316 | }
1317 |
1318 | func opcode0xDC() {
1319 | // CALL C,nn
1320 | if isSetFlag(flagCarry) {
1321 | l := mem.Read(pc.GetValue())
1322 | pc.Increment()
1323 | h := mem.Read(pc.GetValue())
1324 | pc.Increment()
1325 | stackPush(&pc)
1326 | pc.SetHigh(h)
1327 | pc.SetLow(l)
1328 | branchTaken = true
1329 | } else {
1330 | pc.Increment()
1331 | pc.Increment()
1332 | }
1333 | }
1334 |
1335 | func opcode0xDD() {
1336 | invalidOPCode()
1337 | }
1338 |
1339 | func opcode0xDE() {
1340 | // SBC n
1341 | opcodesSBC(mem.Read(pc.GetValue()))
1342 | pc.Increment()
1343 | }
1344 |
1345 | func opcode0xDF() {
1346 | // RST 18H
1347 | stackPush(&pc)
1348 | pc.SetValue(0x0018)
1349 | }
1350 |
1351 | func opcode0xE0() {
1352 | // LD (0xFF00+n),A
1353 | opcodesLDValueToAddr(0xFF00+uint16(mem.Read(pc.GetValue())), af.GetHigh())
1354 | pc.Increment()
1355 | }
1356 |
1357 | func opcode0xE1() {
1358 | // POP HL
1359 | stackPop(&hl)
1360 | }
1361 |
1362 | func opcode0xE2() {
1363 | // LD (0xFF00+C),A
1364 | opcodesLDValueToAddr(0xFF00+uint16(bc.GetLow()), af.GetHigh())
1365 | }
1366 |
1367 | func opcode0xE3() {
1368 | invalidOPCode()
1369 | }
1370 |
1371 | func opcode0xE4() {
1372 | invalidOPCode()
1373 | }
1374 |
1375 | func opcode0xE5() {
1376 | // PUSH HL
1377 | stackPush(&hl)
1378 | }
1379 |
1380 | func opcode0xE6() {
1381 | // AND n
1382 | opcodesAND(mem.Read(pc.GetValue()))
1383 | pc.Increment()
1384 | }
1385 |
1386 | func opcode0xE7() {
1387 | // RST 20H
1388 | stackPush(&pc)
1389 | pc.SetValue(0x0020)
1390 | }
1391 |
1392 | func opcode0xE8() {
1393 | // ADD SP,n
1394 | opcodesADDSP(int8(mem.Read(pc.GetValue())))
1395 | pc.Increment()
1396 | }
1397 |
1398 | func opcode0xE9() {
1399 | // JP (HL)
1400 | pc.SetValue(hl.GetValue())
1401 | }
1402 |
1403 | func opcode0xEA() {
1404 | // LD (nn),A
1405 | var tmp SixteenBitReg
1406 | tmp.SetLow(mem.Read(pc.GetValue()))
1407 | pc.Increment()
1408 | tmp.SetHigh(mem.Read(pc.GetValue()))
1409 | pc.Increment()
1410 | opcodesLDValueToAddr(tmp.GetValue(), af.GetHigh())
1411 | }
1412 |
1413 | func opcode0xEB() {
1414 | invalidOPCode()
1415 | }
1416 |
1417 | func opcode0xEC() {
1418 | invalidOPCode()
1419 | }
1420 |
1421 | func opcode0xED() {
1422 | invalidOPCode()
1423 | }
1424 |
1425 | func opcode0xEE() {
1426 | // XOR n
1427 | opcodesXOR(mem.Read(pc.GetValue()))
1428 | pc.Increment()
1429 | }
1430 |
1431 | func opcode0xEF() {
1432 | // RST 28H
1433 | stackPush(&pc)
1434 | pc.SetValue(0x28)
1435 | }
1436 |
1437 | func opcode0xF0() {
1438 | // LD A,(0xFF00+n)
1439 | opcodesLDAddrToReg(af.GetHighReg(), 0xFF00+uint16(mem.Read(pc.GetValue())))
1440 | pc.Increment()
1441 | }
1442 |
1443 | func opcode0xF1() {
1444 | // POP AF
1445 | stackPop(&af)
1446 | af.SetLow(af.GetLow() & 0xF0)
1447 | }
1448 |
1449 | func opcode0xF2() {
1450 | // LD A,(C)
1451 | opcodesLDAddrToReg(af.GetHighReg(), 0xFF00+uint16(bc.GetLow()))
1452 | }
1453 |
1454 | func opcode0xF3() {
1455 | // DI
1456 | ime = false
1457 | imeCycles = 0
1458 | }
1459 |
1460 | func opcode0xF4() {
1461 | invalidOPCode()
1462 | }
1463 |
1464 | func opcode0xF5() {
1465 | // PUSH AF
1466 | stackPush(&af)
1467 | }
1468 |
1469 | func opcode0xF6() {
1470 | // OR n
1471 | opcodesOR(mem.Read(pc.GetValue()))
1472 | pc.Increment()
1473 | }
1474 |
1475 | func opcode0xF7() {
1476 | // RST 30H
1477 | stackPush(&pc)
1478 | pc.SetValue(0x0030)
1479 | }
1480 |
1481 | func opcode0xF8() {
1482 | // LD HL,SP+n
1483 | offset := int8(mem.Read(pc.GetValue()))
1484 | value := int(sp.GetValue()) + int(offset)
1485 | result := uint16(value)
1486 | clearAllFlags()
1487 | if ((sp.GetValue() ^ uint16(offset) ^ result) & 0x100) == 0x100 {
1488 | setFlag(flagCarry)
1489 | }
1490 | if ((sp.GetValue() ^ uint16(offset) ^ result) & 0x10) == 0x10 {
1491 | setFlag(flagHalf)
1492 | }
1493 | hl.SetValue(result)
1494 | pc.Increment()
1495 | }
1496 |
1497 | func opcode0xF9() {
1498 | // LD SP,HL
1499 | sp.SetValue(hl.GetValue())
1500 | }
1501 |
1502 | func opcode0xFA() {
1503 | // LD A,(nn)
1504 | var tmp SixteenBitReg
1505 | tmp.SetLow(mem.Read(pc.GetValue()))
1506 | pc.Increment()
1507 | tmp.SetHigh(mem.Read(pc.GetValue()))
1508 | pc.Increment()
1509 | opcodesLDAddrToReg(af.GetHighReg(), tmp.GetValue())
1510 | }
1511 |
1512 | func opcode0xFB() {
1513 | // EI
1514 | imeCycles = (int(machineCycles[0xFB]) * 4) + 1
1515 | }
1516 |
1517 | func opcode0xFC() {
1518 | invalidOPCode()
1519 | }
1520 |
1521 | func opcode0xFD() {
1522 | invalidOPCode()
1523 | }
1524 |
1525 | func opcode0xFE() {
1526 | // CP n
1527 | opcodesCP(mem.Read(pc.GetValue()))
1528 | pc.Increment()
1529 | }
1530 |
1531 | func opcode0xFF() {
1532 | // RST 38H
1533 | stackPush(&pc)
1534 | pc.SetValue(0x0038)
1535 | }
1536 |
--------------------------------------------------------------------------------
/gb/cpu/registers.go:
--------------------------------------------------------------------------------
1 | package cpu
2 |
3 | // EightBitReg models an 8 bit processor register
4 | type EightBitReg struct {
5 | value uint8
6 | }
7 |
8 | // SixteenBitReg models a 16 bit processor register
9 | type SixteenBitReg struct {
10 | high EightBitReg
11 | low EightBitReg
12 | }
13 |
14 | // SetValue sets the 8 bit value of the register
15 | func (reg *EightBitReg) SetValue(value uint8) {
16 | reg.value = value
17 | }
18 |
19 | // GetValue returns the 8 bit value of the register
20 | func (reg *EightBitReg) GetValue() uint8 {
21 | return reg.value
22 | }
23 |
24 | // Increment increments (++) the 8 bit value of the register
25 | func (reg *EightBitReg) Increment() {
26 | reg.value++
27 | }
28 |
29 | // Decrement decrements (--) the 8 bit value of the register
30 | func (reg *EightBitReg) Decrement() {
31 | reg.value--
32 | }
33 |
34 | // SetHigh sets the 8 bit value of the 16 bit higher part
35 | func (reg *SixteenBitReg) SetHigh(value uint8) {
36 | reg.high.value = value
37 | }
38 |
39 | // GetHigh returns the 8 bit value of the 16 bit higher part
40 | func (reg *SixteenBitReg) GetHigh() uint8 {
41 | return reg.high.value
42 | }
43 |
44 | // GetHighReg returns the 8 bit register of the 16 bit higher part
45 | func (reg *SixteenBitReg) GetHighReg() *EightBitReg {
46 | return ®.high
47 | }
48 |
49 | // SetLow sets the 8 bit value of the 16 bit lower part
50 | func (reg *SixteenBitReg) SetLow(value uint8) {
51 | reg.low.value = value
52 | }
53 |
54 | // GetLow returns the 8 bit value of the 16 bit lower part
55 | func (reg *SixteenBitReg) GetLow() uint8 {
56 | return reg.low.value
57 | }
58 |
59 | // GetLowReg returns the 8 bit register of the 16 bit lower part
60 | func (reg *SixteenBitReg) GetLowReg() *EightBitReg {
61 | return ®.low
62 | }
63 |
64 | // SetValue sets the 16 bit value of the register
65 | func (reg *SixteenBitReg) SetValue(value uint16) {
66 | reg.low.SetValue(uint8(value & 0xFF))
67 | reg.high.SetValue(uint8((value >> 8) & 0xFF))
68 | }
69 |
70 | // GetValue returns the 16 bit value of the register
71 | func (reg *SixteenBitReg) GetValue() uint16 {
72 | high := uint16(reg.high.GetValue())
73 | low := uint16(reg.low.GetValue())
74 | return (high << 8) | low
75 | }
76 |
77 | // Increment increments (++) the 16 bit value of the register
78 | func (reg *SixteenBitReg) Increment() {
79 | value := reg.GetValue()
80 | value++
81 | reg.SetValue(value)
82 | }
83 |
84 | // Decrement decrements (--) the 16 bit value of the register
85 | func (reg *SixteenBitReg) Decrement() {
86 | value := reg.GetValue()
87 | value--
88 | reg.SetValue(value)
89 | }
90 |
--------------------------------------------------------------------------------
/gb/cpu/serial.go:
--------------------------------------------------------------------------------
1 | package cpu
2 |
3 | import "github.com/drhelius/demo-emulator/gb/util"
4 |
5 | var (
6 | serialBit int
7 | serialCycles uint
8 | )
9 |
10 | func updateSerial() {
11 | sc := mem.Read(0xFF02)
12 |
13 | if util.IsSetBit(sc, 7) && util.IsSetBit(sc, 0) {
14 | serialCycles += clockCycles
15 |
16 | if serialBit < 0 {
17 | serialBit = 0
18 | serialCycles = 0
19 | return
20 | }
21 |
22 | if serialCycles >= 512 {
23 | if serialBit > 7 {
24 | mem.Write(0xFF02, sc&0x7F)
25 | RequestInterrupt(InterruptSerial)
26 | serialBit = -1
27 | return
28 | }
29 |
30 | sb := mem.Read(0xFF01)
31 | sb <<= 1
32 | sb |= 0x01
33 | mem.Write(0xFF01, sb)
34 |
35 | serialCycles -= 512
36 | serialBit++
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/gb/cpu/timers.go:
--------------------------------------------------------------------------------
1 | package cpu
2 |
3 | import "github.com/drhelius/demo-emulator/gb/util"
4 |
5 | var (
6 | divCycles uint
7 | timaCycles uint
8 | )
9 |
10 | // ResetDivCycles sets divCycles to 0
11 | func ResetDivCycles() {
12 | divCycles = 0
13 | }
14 |
15 | // ResetTimaCycles sets timaCycles to 0
16 | func ResetTimaCycles() {
17 | timaCycles = 0
18 | }
19 |
20 | func updateTimers() {
21 | divCycles += clockCycles
22 |
23 | for divCycles >= 256 {
24 | divCycles -= 256
25 | div := mem.Read(0xFF04)
26 | div++
27 | mem.Write(0xFF04, div)
28 | }
29 |
30 | tac := mem.Read(0xFF07)
31 |
32 | // if tima is running
33 | if util.IsSetBit(tac, 2) {
34 | timaCycles += clockCycles
35 |
36 | var freq uint
37 |
38 | switch tac & 0x03 {
39 | case 0:
40 | freq = 1024
41 | case 1:
42 | freq = 16
43 | case 2:
44 | freq = 64
45 | case 3:
46 | freq = 256
47 | }
48 |
49 | for timaCycles >= freq {
50 | timaCycles -= freq
51 | tima := mem.Read(0xFF05)
52 |
53 | if tima == 0xFF {
54 | tima = mem.Read(0xFF06)
55 | RequestInterrupt(InterruptTimer)
56 | } else {
57 | tima++
58 | }
59 |
60 | mem.Write(0xFF05, tima)
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/gb/cpu/timing.go:
--------------------------------------------------------------------------------
1 | package cpu
2 |
3 | // machine cycles for normal opcodes
4 | var machineCycles = [256]uint{
5 | 1, 3, 2, 2, 1, 1, 2, 1, 5, 2, 2, 2, 1, 1, 2, 1,
6 | 1, 3, 2, 2, 1, 1, 2, 1, 3, 2, 2, 2, 1, 1, 2, 1,
7 | 2, 3, 2, 2, 1, 1, 2, 1, 2, 2, 2, 2, 1, 1, 2, 1,
8 | 2, 3, 2, 2, 3, 3, 3, 1, 2, 2, 2, 2, 1, 1, 2, 1,
9 | 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1,
10 | 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1,
11 | 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1,
12 | 2, 2, 2, 2, 2, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1,
13 | 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1,
14 | 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1,
15 | 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1,
16 | 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1,
17 | 2, 3, 3, 4, 3, 4, 2, 4, 2, 4, 3, 0, 3, 6, 2, 4,
18 | 2, 3, 3, 0, 3, 4, 2, 4, 2, 4, 3, 0, 3, 0, 2, 4,
19 | 3, 3, 2, 0, 0, 4, 2, 4, 4, 1, 4, 0, 0, 0, 2, 4,
20 | 3, 3, 2, 1, 0, 4, 2, 4, 3, 2, 4, 1, 0, 0, 2, 4}
21 |
22 | // machine cycles for normal opcodes plus cycles used in branches
23 | var machineCyclesBranched = [256]uint{
24 | 1, 3, 2, 2, 1, 1, 2, 1, 5, 2, 2, 2, 1, 1, 2, 1,
25 | 1, 3, 2, 2, 1, 1, 2, 1, 3, 2, 2, 2, 1, 1, 2, 1,
26 | 3, 3, 2, 2, 1, 1, 2, 1, 3, 2, 2, 2, 1, 1, 2, 1,
27 | 3, 3, 2, 2, 3, 3, 3, 1, 3, 2, 2, 2, 1, 1, 2, 1,
28 | 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1,
29 | 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1,
30 | 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1,
31 | 2, 2, 2, 2, 2, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1,
32 | 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1,
33 | 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1,
34 | 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1,
35 | 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1,
36 | 5, 3, 4, 4, 6, 4, 2, 4, 5, 4, 4, 0, 6, 6, 2, 4,
37 | 5, 3, 4, 0, 6, 4, 2, 4, 5, 4, 4, 0, 6, 0, 2, 4,
38 | 3, 3, 2, 0, 0, 4, 2, 4, 4, 1, 4, 0, 0, 0, 2, 4,
39 | 3, 3, 2, 1, 0, 4, 2, 4, 3, 2, 4, 1, 0, 0, 2, 4}
40 |
41 | // machine cycles for CB opcodes
42 | var machineCyclesCB = [256]uint{
43 | 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2,
44 | 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2,
45 | 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2,
46 | 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2,
47 | 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2,
48 | 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2,
49 | 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2,
50 | 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2, 2, 2, 2, 3, 2,
51 | 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2,
52 | 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2,
53 | 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2,
54 | 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2,
55 | 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2,
56 | 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2,
57 | 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2,
58 | 2, 2, 2, 2, 2, 2, 4, 2, 2, 2, 2, 2, 2, 2, 4, 2}
59 |
--------------------------------------------------------------------------------
/gb/input/input.go:
--------------------------------------------------------------------------------
1 | package input
2 |
3 | import (
4 | "github.com/drhelius/demo-emulator/gb/cpu"
5 | "github.com/drhelius/demo-emulator/gb/util"
6 | )
7 |
8 | var (
9 | joypadState uint8
10 | p1 uint8
11 | inputCycles uint
12 | )
13 |
14 | func init() {
15 | joypadState = 0xFF
16 | p1 = 0xFF
17 | }
18 |
19 | // Tick runs the input eumulation n cycles
20 | func Tick(cycles uint) {
21 | inputCycles += cycles
22 |
23 | // Joypad Poll Speed (64 Hz)
24 | if inputCycles >= 65536 {
25 | inputCycles -= 65536
26 | update()
27 | }
28 | }
29 |
30 | // Read returns the P1 register
31 | func Read() uint8 {
32 | return p1
33 | }
34 |
35 | // Write stores the P1 register
36 | func Write(value uint8) {
37 | p1 = (p1 & 0xCF) | (value & 0x30)
38 | update()
39 | }
40 |
41 | // ButtonPressed tells the input system that a button has been pressed
42 | func ButtonPressed(button util.GameboyButton) {
43 | joypadState = util.UnsetBit(joypadState, uint8(button))
44 | }
45 |
46 | // ButtonReleased tells the input system that a button has been released
47 | func ButtonReleased(button util.GameboyButton) {
48 | joypadState = util.SetBit(joypadState, uint8(button))
49 | }
50 |
51 | func update() {
52 | current := p1 & 0xF0
53 |
54 | switch current & 0x30 {
55 | case 0x10:
56 | topJoypad := (joypadState >> 4) & 0x0F
57 | current |= topJoypad
58 | case 0x20:
59 | bottomJoypad := joypadState & 0x0F
60 | current |= bottomJoypad
61 | case 0x30:
62 | current |= 0x0F
63 | }
64 |
65 | if (p1 & ^current & 0x0F) != 0 {
66 | cpu.RequestInterrupt(cpu.InterruptJoypad)
67 | }
68 |
69 | p1 = current
70 | }
71 |
--------------------------------------------------------------------------------
/gb/mapper/mapper.go:
--------------------------------------------------------------------------------
1 | package mapper
2 |
3 | // Mapper is the interface to implement MBCs
4 | type Mapper interface {
5 | GetMemoryMap() []uint8
6 | GetROM() []uint8
7 | Setup([]uint8)
8 | Read(uint16) uint8
9 | Write(uint16, uint8)
10 | }
11 |
--------------------------------------------------------------------------------
/gb/mbcs/common.go:
--------------------------------------------------------------------------------
1 | package mbcs
2 |
3 | // WriteCommon emulates the internal RAM 0xC000-0xD000
4 | // and the mirror of the internal RAM 0xE000-0xFE00
5 | func WriteCommon(addr uint16, value uint8, mem []uint8) {
6 | switch {
7 | case (addr >= 0xC000) && (addr < 0xDE00):
8 | mem[addr] = value
9 | mem[addr+0x2000] = value
10 | case (addr >= 0xE000) && (addr < 0xFE00):
11 | mem[addr] = value
12 | mem[addr-0x2000] = value
13 | default:
14 | mem[addr] = value
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/gb/mbcs/io.go:
--------------------------------------------------------------------------------
1 | package mbcs
2 |
3 | import (
4 | "github.com/drhelius/demo-emulator/gb/cpu"
5 | "github.com/drhelius/demo-emulator/gb/input"
6 | "github.com/drhelius/demo-emulator/gb/mapper"
7 | "github.com/drhelius/demo-emulator/gb/util"
8 | "github.com/drhelius/demo-emulator/gb/video"
9 | )
10 |
11 | // ReadIO returns the values of the special IO registers
12 | func ReadIO(addr uint16, mem []uint8) uint8 {
13 | switch addr {
14 | case 0xFF00:
15 | // P1
16 | return input.Read()
17 | case 0xFF07:
18 | // TAC
19 | return mem[addr] | 0xF8
20 | case 0xFF0F:
21 | // IF
22 | return mem[addr] | 0xE0
23 | case 0xFF41:
24 | // STAT
25 | return mem[addr] | 0x80
26 | case 0xFF44:
27 | // LY
28 | if video.ScreenEnabled {
29 | return mem[0xFF44]
30 | }
31 | return 0x00
32 | case 0xFF4F:
33 | // VBK
34 | return mem[addr] | 0xFE
35 | }
36 |
37 | return mem[addr]
38 | }
39 |
40 | // WriteIO stores the values of the special IO registers
41 | func WriteIO(addr uint16, value uint8, mem []uint8, m mapper.Mapper) {
42 | switch addr {
43 | case 0xFF00:
44 | // P1
45 | input.Write(value)
46 | case 0xFF04:
47 | // DIV
48 | cpu.ResetDivCycles()
49 | mem[addr] = 0x00
50 | case 0xFF07:
51 | // TAC
52 | value &= 0x07
53 | currentTac := mem[addr]
54 | if (currentTac & 0x03) != (value & 0x03) {
55 | cpu.ResetTimaCycles()
56 | mem[0xFF05] = mem[0xFF06]
57 | }
58 | mem[addr] = value
59 | case 0xFF0F:
60 | // IF
61 | mem[addr] = value & 0x1F
62 | case 0xFF40:
63 | // LCDC
64 | currentLcdc := mem[addr]
65 | newLcdc := value
66 | mem[addr] = newLcdc
67 | if !util.IsSetBit(currentLcdc, 5) && util.IsSetBit(newLcdc, 5) {
68 | video.ResetWindowLine()
69 | }
70 | if util.IsSetBit(value, 7) {
71 | video.EnableScreen()
72 | } else {
73 | video.DisableScreen()
74 | }
75 | case 0xFF41:
76 | // STAT
77 | currentStat := mem[addr] & 0x07
78 | newStat := (value & 0x78) | (currentStat & 0x07)
79 | mem[addr] = newStat
80 | lcdc := mem[0xFF40]
81 | if util.IsSetBit(lcdc, 7) {
82 | video.CompareLYToLYC()
83 | }
84 | mem[addr] = value
85 | case 0xFF44:
86 | // LY
87 | currentLy := mem[addr]
88 | if util.IsSetBit(currentLy, 7) && !util.IsSetBit(value, 7) {
89 | video.DisableScreen()
90 | }
91 | mem[addr] = value
92 | case 0xFF45:
93 | // LYC
94 | currentLyc := mem[addr]
95 | if currentLyc != value {
96 | mem[addr] = value
97 | lcdc := mem[0xFF40]
98 | if util.IsSetBit(lcdc, 7) {
99 | video.CompareLYToLYC()
100 | }
101 | }
102 | case 0xFF46:
103 | // DMA
104 | mem[addr] = value
105 | address := uint16(value) << 8
106 | if address >= 0x8000 && address < 0xE000 {
107 | for i := uint16(0x0000); i < 0x00A0; i++ {
108 | m.Write(0xFE00+i, m.Read(address+i))
109 | }
110 | }
111 | case 0xFFFF:
112 | // IE
113 | mem[addr] = value & 0x1F
114 | default:
115 | mem[addr] = value
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/gb/mbcs/iovalues.go:
--------------------------------------------------------------------------------
1 | package mbcs
2 |
3 | // the initial values observed on a real Game Boy
4 | // for the IO registers 0xFF00-0xFF
5 | var initialValuesForFFXX = [256]uint8{
6 | 0xCF, 0x00, 0x7E, 0xFF, 0xD3, 0x00, 0x00, 0xF8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xE1,
7 | 0x80, 0xBF, 0xF3, 0xFF, 0xBF, 0xFF, 0x3F, 0x00, 0xFF, 0xBF, 0x7F, 0xFF, 0x9F, 0xFF, 0xBF, 0xFF,
8 | 0xFF, 0x00, 0x00, 0xBF, 0x77, 0xF3, 0xF1, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
9 | 0x71, 0x72, 0xD5, 0x91, 0x58, 0xBB, 0x2A, 0xFA, 0xCF, 0x3C, 0x54, 0x75, 0x48, 0xCF, 0x8F, 0xD9,
10 | 0x91, 0x80, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFC, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
11 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
12 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
13 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
14 | 0x2B, 0x0B, 0x64, 0x2F, 0xAF, 0x15, 0x60, 0x6D, 0x61, 0x4E, 0xAC, 0x45, 0x0F, 0xDA, 0x92, 0xF3,
15 | 0x83, 0x38, 0xE4, 0x4E, 0xA7, 0x6C, 0x38, 0x58, 0xBE, 0xEA, 0xE5, 0x81, 0xB4, 0xCB, 0xBF, 0x7B,
16 | 0x59, 0xAD, 0x50, 0x13, 0x5E, 0xF6, 0xB3, 0xC1, 0xDC, 0xDF, 0x9E, 0x68, 0xD7, 0x59, 0x26, 0xF3,
17 | 0x62, 0x54, 0xF8, 0x36, 0xB7, 0x78, 0x6A, 0x22, 0xA7, 0xDD, 0x88, 0x15, 0xCA, 0x96, 0x39, 0xD3,
18 | 0xE6, 0x55, 0x6E, 0xEA, 0x90, 0x76, 0xB8, 0xFF, 0x50, 0xCD, 0xB5, 0x1B, 0x1F, 0xA5, 0x4D, 0x2E,
19 | 0xB4, 0x09, 0x47, 0x8A, 0xC4, 0x5A, 0x8C, 0x4E, 0xE7, 0x29, 0x50, 0x88, 0xA8, 0x66, 0x85, 0x4B,
20 | 0xAA, 0x38, 0xE7, 0x6B, 0x45, 0x3E, 0x30, 0x37, 0xBA, 0xC5, 0x31, 0xF2, 0x71, 0xB4, 0xCF, 0x29,
21 | 0xBC, 0x7F, 0x7E, 0xD0, 0xC7, 0xC3, 0xBD, 0xCF, 0x59, 0xEA, 0x39, 0x01, 0x2E, 0x00, 0x69, 0x00}
22 |
--------------------------------------------------------------------------------
/gb/mbcs/mbc1.go:
--------------------------------------------------------------------------------
1 | package mbcs
2 |
3 | import "fmt"
4 |
5 | // MBC1 is a mapper implementation to emulate
6 | // cartridges with a MBC1 memory bank controller
7 | type MBC1 struct {
8 | memoryMap []uint8
9 | rom []uint8
10 | ram []uint8
11 | mode uint8
12 | romBank uint
13 | ramBank uint16
14 | ramEnabled bool
15 | romBankHighBits uint
16 | higherROMBank uint
17 | higherRAMBank uint16
18 | ramSize uint8
19 | }
20 |
21 | // GetMemoryMap returns the memory array
22 | func (m *MBC1) GetMemoryMap() []uint8 {
23 | return m.memoryMap
24 | }
25 |
26 | // GetROM returns the rom array
27 | func (m *MBC1) GetROM() []uint8 {
28 | return m.rom
29 | }
30 |
31 | // Setup Receives the rom data and intializes memory
32 | func (m *MBC1) Setup(r []uint8) {
33 | m.rom = r
34 | m.romBank = 1
35 |
36 | m.memoryMap = make([]uint8, 0x10000)
37 | m.ram = make([]uint8, 0x8000)
38 |
39 | for i := 0; i < 0x100; i++ {
40 | m.memoryMap[0xFF00+i] = initialValuesForFFXX[i]
41 | }
42 |
43 | m.ramSize = m.rom[0x149]
44 | ramBanks := 0
45 |
46 | switch m.ramSize {
47 | case 0x00:
48 | ramBanks = 0
49 | m.higherRAMBank = 0x00
50 | case 0x01:
51 | fallthrough
52 | case 0x02:
53 | ramBanks = 1
54 | m.higherRAMBank = 0x00
55 | default:
56 | ramBanks = 4
57 | m.higherRAMBank = 0x03
58 | break
59 | }
60 |
61 | m.higherROMBank = uint(max(pow2Ceil(len(m.rom)/0x4000), 2) - 1)
62 |
63 | fmt.Printf("%d ROM banks\n", m.higherROMBank+1)
64 | fmt.Printf("%d RAM banks\n", ramBanks)
65 | }
66 |
67 | // Read returns the 8 bit value at the 16 bit address of the memory
68 | func (m *MBC1) Read(addr uint16) uint8 {
69 | switch {
70 | case (addr >= 0x0000) && (addr < 0x4000):
71 | // ROM bank 0
72 | return m.rom[addr]
73 | case (addr >= 0x4000) && (addr < 0x8000):
74 | // ROM bank X
75 | return m.rom[(uint(addr)-0x4000)+(m.romBank*0x4000)]
76 | case (addr >= 0xA000) && (addr < 0xC000):
77 | // RAM bank
78 | if m.ramEnabled {
79 | if m.mode == 0 {
80 | return m.ram[addr-0xA000]
81 | }
82 | return m.ram[(addr-0xA000)+(m.ramBank*0x2000)]
83 | }
84 | fmt.Printf("*** attempting to read from disabled RAM %X\n", addr)
85 | return 0xFF
86 | case addr >= 0xFF00:
87 | // IO registers
88 | return ReadIO(addr, m.memoryMap)
89 | }
90 | return m.memoryMap[addr]
91 | }
92 |
93 | // Write stores the 8 bit value at the 16 bit address of the memory
94 | func (m *MBC1) Write(addr uint16, value uint8) {
95 | switch {
96 | case (addr >= 0x0000) && (addr < 0x2000):
97 | // enable / disable RAM
98 | if m.ramSize > 0 {
99 | m.ramEnabled = ((value & 0x0F) == 0x0A)
100 | }
101 | case (addr >= 0x2000) && (addr < 0x4000):
102 | // select ROM bank
103 | if m.mode == 0 {
104 | m.romBank = uint(value&0x1F) | (m.romBankHighBits << 5)
105 | } else {
106 | m.romBank = uint(value & 0x1F)
107 | }
108 | if m.romBank == 0x00 || m.romBank == 0x20 || m.romBank == 0x40 || m.romBank == 0x60 {
109 | m.romBank++
110 | }
111 | m.romBank &= m.higherROMBank
112 | case (addr >= 0x4000) && (addr < 0x6000):
113 | if m.mode == 1 {
114 | // select RAM bank
115 | m.ramBank = uint16(value & 0x03)
116 | m.ramBank &= m.higherRAMBank
117 | } else {
118 | // select high bits of ROM bank
119 | m.romBankHighBits = uint(value & 0x03)
120 | m.romBank = (m.romBank & 0x1F) | (m.romBankHighBits << 5)
121 | if m.romBank == 0x00 || m.romBank == 0x20 || m.romBank == 0x40 || m.romBank == 0x60 {
122 | m.romBank++
123 | }
124 | m.romBank &= m.higherROMBank
125 | }
126 | case (addr >= 0x6000) && (addr < 0x8000):
127 | // operation mode
128 | if (m.ramSize != 3) && ((value & 0x01) != 0) {
129 | fmt.Printf("*** attempting to change MBC1 to mode 1 with incorrect RAM banks %X %X\n", addr, value)
130 | } else {
131 | m.mode = value & 0x01
132 | }
133 | case (addr >= 0xA000) && (addr < 0xC000):
134 | // cartridge RAM
135 | if m.ramEnabled {
136 | if m.mode == 0 {
137 | m.ram[addr-0xA000] = value
138 | } else {
139 | m.ram[(addr-0xA000)+(m.ramBank*0x2000)] = value
140 | }
141 | } else {
142 | fmt.Printf("*** attempting to write to disabled RAM %X %X\n", addr, value)
143 | }
144 | case (addr >= 0xC000) && (addr < 0xFE00):
145 | // internal RAM
146 | WriteCommon(addr, value, m.memoryMap)
147 | case addr >= 0xFF00:
148 | // IO registers
149 | WriteIO(addr, value, m.memoryMap, m)
150 | default:
151 | m.memoryMap[addr] = value
152 | }
153 | }
154 |
155 | func max(a, b int) int {
156 | if a < b {
157 | return b
158 | }
159 | return a
160 | }
161 |
162 | func pow2Ceil(n int) int {
163 | n--
164 | n |= n >> 1
165 | n |= n >> 2
166 | n |= n >> 4
167 | n |= n >> 8
168 | n++
169 | return n
170 | }
171 |
--------------------------------------------------------------------------------
/gb/mbcs/romonly.go:
--------------------------------------------------------------------------------
1 | package mbcs
2 |
3 | import "fmt"
4 |
5 | // RomOnly is a mapper implementation to emulate
6 | // cartridges with only a ROM chip
7 | type RomOnly struct {
8 | memoryMap []uint8
9 | rom []uint8
10 | }
11 |
12 | // GetMemoryMap returns the memory array
13 | func (m *RomOnly) GetMemoryMap() []uint8 {
14 | return m.memoryMap
15 | }
16 |
17 | // GetROM returns the rom array
18 | func (m *RomOnly) GetROM() []uint8 {
19 | return m.rom
20 | }
21 |
22 | // Setup Receives the rom data and intializes memory
23 | func (m *RomOnly) Setup(r []uint8) {
24 | m.rom = r
25 | m.memoryMap = make([]uint8, 0x10000)
26 |
27 | for i := 0; i < 0x100; i++ {
28 | m.memoryMap[0xFF00+i] = initialValuesForFFXX[i]
29 | }
30 | }
31 |
32 | // Read returns the 8 bit value at the 16 bit address of the memory
33 | func (m *RomOnly) Read(addr uint16) uint8 {
34 | switch {
35 | case addr < 0x8000:
36 | // ROM
37 | return m.rom[addr]
38 | case addr >= 0xFF00:
39 | // IO Registers
40 | return ReadIO(addr, m.memoryMap)
41 | }
42 | return m.memoryMap[addr]
43 | }
44 |
45 | // Write stores the 8 bit value at the 16 bit address of the memory
46 | func (m *RomOnly) Write(addr uint16, value uint8) {
47 | switch {
48 | case addr < 0x8000:
49 | // ROM
50 | fmt.Printf("*** attempting to write on ROM address %X %X\n", addr, value)
51 | case (addr >= 0xC000) && (addr < 0xFE00):
52 | // Internal RAM
53 | WriteCommon(addr, value, m.memoryMap)
54 | case addr >= 0xFF00:
55 | // IO Registers
56 | WriteIO(addr, value, m.memoryMap, m)
57 | default:
58 | m.memoryMap[addr] = value
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/gb/util/constants.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | // GbWidth is the original Game Boy LCD Width in pixels
4 | const GbWidth = 160
5 |
6 | // GbHeight is the original Game Boy LCD Height in pixels
7 | const GbHeight = 144
8 |
--------------------------------------------------------------------------------
/gb/util/functions.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | // SetBit sets the selected bit to 1 inside a byte and returns the new byte
4 | func SetBit(value uint8, bit uint8) uint8 {
5 | return value | (0x01 << bit)
6 | }
7 |
8 | // UnsetBit clears the selected bit to 0 inside a byte and returns the new byte
9 | func UnsetBit(value uint8, bit uint8) uint8 {
10 | return value & (^(0x01 << bit))
11 | }
12 |
13 | // IsSetBit returns true if the selected bit inside a byte is 1
14 | func IsSetBit(value uint8, bit uint8) bool {
15 | return (value & (0x01 << bit)) != 0
16 | }
17 |
--------------------------------------------------------------------------------
/gb/util/types.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | // GameboyButton is the type representing the buttons of the Game Boy
4 | type GameboyButton int
5 |
6 | // These are all the buttons on the Game Boy
7 | const (
8 | AButton GameboyButton = 4
9 | BButton GameboyButton = 5
10 | StartButton GameboyButton = 7
11 | SelectButton GameboyButton = 6
12 | RightButton GameboyButton = 0
13 | LeftButton GameboyButton = 1
14 | UpButton GameboyButton = 2
15 | DownButton GameboyButton = 3
16 | )
17 |
--------------------------------------------------------------------------------
/gb/video/background.go:
--------------------------------------------------------------------------------
1 | package video
2 |
3 | import "github.com/drhelius/demo-emulator/gb/util"
4 |
5 | func renderBG(line uint8) {
6 | lcdc := mem.GetMemoryMap()[0xFF40]
7 | lineWidth := uint(line) * uint(util.GbWidth)
8 |
9 | if util.IsSetBit(lcdc, 0) {
10 | var tiles uint = 0x8800
11 | if util.IsSetBit(lcdc, 4) {
12 | tiles = 0x8000
13 | }
14 | var maploc uint = 0x9800
15 | if util.IsSetBit(lcdc, 3) {
16 | maploc = 0x9C00
17 | }
18 |
19 | scx := mem.GetMemoryMap()[0xFF43]
20 | scy := mem.GetMemoryMap()[0xFF42]
21 | lineAdjusted := line + scy
22 | y32 := (uint(lineAdjusted) / 8) * 32
23 | pixely := uint(lineAdjusted) % 8
24 | pixely2 := pixely * 2
25 |
26 | var x uint
27 | for ; x < 32; x++ {
28 | var tile uint8
29 |
30 | if tiles == 0x8800 {
31 | tile = uint8(int(int8(mem.GetMemoryMap()[maploc+y32+x])) + 128)
32 | } else {
33 | tile = mem.Read(uint16(maploc + y32 + x))
34 | }
35 |
36 | mapOffsetX := x * 8
37 | tile16 := uint(tile) * 16
38 | tileAddress := tiles + tile16 + pixely2
39 |
40 | byte1 := mem.Read(uint16(tileAddress))
41 | byte2 := mem.Read(uint16(tileAddress) + 1)
42 |
43 | var pixelx uint8
44 | for ; pixelx < 8; pixelx++ {
45 | bufferX := uint8(mapOffsetX) + pixelx - scx
46 |
47 | if bufferX >= util.GbWidth {
48 | continue
49 | }
50 | var pixel uint8
51 | if (byte1 & (0x1 << (7 - pixelx))) != 0 {
52 | pixel = 1
53 | }
54 | if (byte2 & (0x1 << (7 - pixelx))) != 0 {
55 | pixel |= 2
56 | }
57 |
58 | position := lineWidth + uint(bufferX)
59 |
60 | colorCacheBuffer[position] = pixel & 0x03
61 |
62 | palette := mem.GetMemoryMap()[0xFF47]
63 | color := (palette >> (pixel * 2)) & 0x03
64 | GbFrameBuffer[position] = color
65 | }
66 | }
67 | } else {
68 | var x uint
69 | for ; x < util.GbWidth; x++ {
70 | position := lineWidth + x
71 | GbFrameBuffer[position] = 0
72 | colorCacheBuffer[position] = 0
73 | }
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/gb/video/sprites.go:
--------------------------------------------------------------------------------
1 | package video
2 |
3 | import "github.com/drhelius/demo-emulator/gb/util"
4 |
5 | var (
6 | spriteCacheBuffer [util.GbWidth * util.GbHeight]int
7 | )
8 |
9 | func renderSprites(line uint8) {
10 |
11 | lcdc := mem.GetMemoryMap()[0xFF40]
12 |
13 | if !util.IsSetBit(lcdc, 1) {
14 | return
15 | }
16 |
17 | spriteHeight := 8
18 | var spriteHeightMask uint8 = 0xFF
19 | if util.IsSetBit(lcdc, 2) {
20 | spriteHeight = 16
21 | spriteHeightMask = 0xFE
22 | }
23 |
24 | lineWidth := int(line) * util.GbWidth
25 |
26 | sprite := 39
27 | for ; sprite >= 0; sprite-- {
28 | sprite4 := sprite * 4
29 | spriteY := int(mem.GetMemoryMap()[0xFE00+sprite4]) - 16
30 |
31 | if (spriteY > int(line)) || ((spriteY + spriteHeight) <= int(line)) {
32 | continue
33 | }
34 |
35 | spriteX := int(mem.GetMemoryMap()[0xFE00+sprite4+1]) - 8
36 |
37 | if (spriteX < -7) || (spriteX >= util.GbWidth) {
38 | continue
39 | }
40 |
41 | spriteTile16 := int(mem.GetMemoryMap()[0xFE00+sprite4+2]&spriteHeightMask) * 16
42 | spriteFlags := mem.GetMemoryMap()[0xFE00+sprite4+3]
43 |
44 | spritePallete := util.IsSetBit(spriteFlags, 4)
45 | xflip := util.IsSetBit(spriteFlags, 5)
46 | yflip := util.IsSetBit(spriteFlags, 6)
47 | aboveBG := !util.IsSetBit(spriteFlags, 7)
48 |
49 | tiles := 0x8000
50 | pixelY := int(line) - spriteY
51 | if yflip {
52 | pixelY = (spriteHeight - 1) - (int(line) - spriteY)
53 | }
54 |
55 | pixelY2 := 0
56 | offset := 0
57 |
58 | if (spriteHeight == 16) && (pixelY >= 8) {
59 | pixelY2 = (pixelY - 8) * 2
60 | offset = 16
61 | } else {
62 | pixelY2 = pixelY * 2
63 | }
64 |
65 | tileAddress := tiles + spriteTile16 + pixelY2 + offset
66 |
67 | byte1 := mem.GetMemoryMap()[tileAddress]
68 | byte2 := mem.GetMemoryMap()[tileAddress+1]
69 |
70 | var pixelx int
71 | for ; pixelx < 8; pixelx++ {
72 |
73 | var pixelxFlipped = 7 - uint(pixelx)
74 | if xflip {
75 | pixelxFlipped = uint(pixelx)
76 | }
77 |
78 | var pixel uint8
79 |
80 | if (byte1 & (0x01 << pixelxFlipped)) != 0 {
81 | pixel = 0x01
82 | }
83 | if (byte2 & (0x01 << pixelxFlipped)) != 0 {
84 | pixel |= 0x02
85 | }
86 |
87 | if pixel == 0 {
88 | continue
89 | }
90 |
91 | bufferX := spriteX + pixelx
92 |
93 | if (bufferX < 0) || (bufferX >= util.GbWidth) {
94 | continue
95 | }
96 |
97 | position := lineWidth + bufferX
98 |
99 | colorCache := colorCacheBuffer[position]
100 |
101 | spriteCache := spriteCacheBuffer[position]
102 | if util.IsSetBit(colorCache, 3) && (spriteCache < spriteX) {
103 | continue
104 | }
105 |
106 | if !aboveBG && ((colorCache & 0x03) != 0) {
107 | continue
108 | }
109 |
110 | colorCacheBuffer[position] = util.SetBit(colorCache, 3)
111 | spriteCacheBuffer[position] = spriteX
112 |
113 | var paletteAddr uint16 = 0xFF48
114 | if spritePallete {
115 | paletteAddr = 0xFF49
116 | }
117 |
118 | palette := mem.GetMemoryMap()[paletteAddr]
119 | color := (palette >> (pixel * 2)) & 0x03
120 | GbFrameBuffer[position] = color
121 | }
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/gb/video/video.go:
--------------------------------------------------------------------------------
1 | package video
2 |
3 | import (
4 | "github.com/drhelius/demo-emulator/gb/cpu"
5 | "github.com/drhelius/demo-emulator/gb/mapper"
6 | "github.com/drhelius/demo-emulator/gb/util"
7 | )
8 |
9 | var (
10 | // GbFrameBuffer is the internal Game Boy frame buffer
11 | GbFrameBuffer [util.GbWidth * util.GbHeight]uint8
12 | // ScreenEnabled keeps track of the screen state
13 | ScreenEnabled bool
14 | statusMode uint8
15 | statusModeCycles uint
16 | subStatusModeCycles uint
17 | lyCounter uint8
18 | vblankLine uint8
19 | mem mapper.Mapper
20 | colorCacheBuffer [util.GbWidth * util.GbHeight]uint8
21 | )
22 |
23 | func init() {
24 | // the Game Boy starts on V-BLANK
25 | statusMode = 1
26 | lyCounter = 144
27 | ScreenEnabled = true
28 | }
29 |
30 | // SetMapper injects the memory impl
31 | func SetMapper(m mapper.Mapper) {
32 | mem = m
33 | }
34 |
35 | // Tick runs the video eumulation n cycles
36 | // Then updates the frameBuffer and returns true if the simulation reached the vblank
37 | func Tick(cycles uint) bool {
38 | vblank := false
39 | statusModeCycles += cycles
40 |
41 | if ScreenEnabled {
42 | switch statusMode {
43 | // During H-BLANK
44 | case 0:
45 | if statusModeCycles >= 204 {
46 | statusModeCycles -= 204
47 | statusMode = 2
48 | lyCounter++
49 | mem.GetMemoryMap()[0xFF44] = lyCounter
50 | CompareLYToLYC()
51 |
52 | // if last visible line, change to vblank
53 | if lyCounter == 144 {
54 | statusMode = 1
55 | vblankLine = 0
56 | subStatusModeCycles = statusModeCycles
57 | cpu.RequestInterrupt(cpu.InterruptVBlank)
58 | stat := mem.GetMemoryMap()[0xFF41]
59 | if util.IsSetBit(stat, 4) {
60 | cpu.RequestInterrupt(cpu.InterruptLCDSTAT)
61 | }
62 | vblank = true
63 | windowLine = 0
64 | } else {
65 | stat := mem.GetMemoryMap()[0xFF41]
66 | if util.IsSetBit(stat, 5) {
67 | cpu.RequestInterrupt(cpu.InterruptLCDSTAT)
68 | }
69 | }
70 |
71 | updateStatRegister()
72 | }
73 | // During V-BLANK
74 | case 1:
75 | subStatusModeCycles += cycles
76 |
77 | // advance a line each 456 cycles
78 | if subStatusModeCycles >= 456 {
79 | subStatusModeCycles -= 456
80 | vblankLine++
81 |
82 | if vblankLine <= 9 {
83 | lyCounter++
84 | mem.GetMemoryMap()[0xFF44] = lyCounter
85 | CompareLYToLYC()
86 | }
87 | }
88 |
89 | // line 0 starts one line before the end of V-BLANK
90 | if (statusModeCycles >= 4104) && (subStatusModeCycles >= 4) && (lyCounter == 153) {
91 | lyCounter = 0
92 | mem.GetMemoryMap()[0xFF44] = lyCounter
93 | CompareLYToLYC()
94 | }
95 |
96 | // end of V-BLANK
97 | if statusModeCycles >= 4560 {
98 | statusModeCycles -= 4560
99 | statusMode = 2
100 | updateStatRegister()
101 | stat := mem.GetMemoryMap()[0xFF41]
102 | if util.IsSetBit(stat, 5) {
103 | cpu.RequestInterrupt(cpu.InterruptLCDSTAT)
104 | }
105 | }
106 | // During searching OAM RAM
107 | case 2:
108 | if statusModeCycles >= 80 {
109 | statusModeCycles -= 80
110 | statusMode = 3
111 | updateStatRegister()
112 | }
113 | // During transfering data to LCD driver
114 | case 3:
115 | if statusModeCycles >= 172 {
116 | statusModeCycles -= 172
117 | statusMode = 0
118 | scanLine(lyCounter)
119 | updateStatRegister()
120 | stat := mem.GetMemoryMap()[0xFF41]
121 | if util.IsSetBit(stat, 3) {
122 | cpu.RequestInterrupt(cpu.InterruptLCDSTAT)
123 | }
124 | }
125 | }
126 | // if ScreenEnabled == false
127 | } else {
128 | // force a vblank each frame even if
129 | // the screen is disabled
130 | if statusModeCycles >= 70224 {
131 | statusModeCycles -= 70224
132 | vblank = true
133 | }
134 | }
135 |
136 | return vblank
137 | }
138 |
139 | // EnableScreen enables the screen
140 | func EnableScreen() {
141 | if !ScreenEnabled {
142 | ScreenEnabled = true
143 | statusMode = 0
144 | statusModeCycles = 0
145 | subStatusModeCycles = 0
146 | lyCounter = 0
147 | vblankLine = 0
148 | windowLine = 0
149 |
150 | mem.GetMemoryMap()[0xFF44] = lyCounter
151 |
152 | stat := mem.GetMemoryMap()[0xFF41]
153 | if util.IsSetBit(stat, 5) {
154 | cpu.RequestInterrupt(cpu.InterruptLCDSTAT)
155 | }
156 |
157 | CompareLYToLYC()
158 | }
159 | }
160 |
161 | // DisableScreen disables the screen
162 | func DisableScreen() {
163 | ScreenEnabled = false
164 | mem.GetMemoryMap()[0xFF44] = 0x00
165 | stat := mem.GetMemoryMap()[0xFF41]
166 | stat &= 0x7C
167 | mem.GetMemoryMap()[0xFF41] = stat
168 | statusMode = 0
169 | statusModeCycles = 0
170 | subStatusModeCycles = 0
171 | lyCounter = 0
172 | }
173 |
174 | // CompareLYToLYC compares LY counter with LYC register
175 | func CompareLYToLYC() {
176 | if ScreenEnabled {
177 | lyc := mem.GetMemoryMap()[0xFF45]
178 | stat := mem.GetMemoryMap()[0xFF41]
179 |
180 | if lyc == lyCounter {
181 | stat = util.SetBit(stat, 2)
182 | if util.IsSetBit(stat, 6) {
183 | cpu.RequestInterrupt(cpu.InterruptLCDSTAT)
184 | }
185 | } else {
186 | stat = util.UnsetBit(stat, 2)
187 | }
188 |
189 | mem.GetMemoryMap()[0xFF41] = stat
190 | }
191 | }
192 |
193 | func scanLine(line uint8) {
194 | lcdc := mem.GetMemoryMap()[0xFF40]
195 |
196 | if ScreenEnabled && util.IsSetBit(lcdc, 7) {
197 | renderBG(line)
198 | renderWindow(line)
199 | renderSprites(line)
200 | } else {
201 | var x uint8
202 | for ; x < util.GbWidth; x++ {
203 | GbFrameBuffer[(line*util.GbWidth)+x] = 0
204 | }
205 | }
206 | }
207 |
208 | func updateStatRegister() {
209 | // Updates the STAT register with current mode
210 | stat := mem.GetMemoryMap()[0xFF41]
211 | mem.GetMemoryMap()[0xFF41] = (stat & 0xFC) | (statusMode & 0x3)
212 | }
213 |
--------------------------------------------------------------------------------
/gb/video/window.go:
--------------------------------------------------------------------------------
1 | package video
2 |
3 | import "github.com/drhelius/demo-emulator/gb/util"
4 |
5 | var (
6 | windowLine uint
7 | )
8 |
9 | // ResetWindowLine resetes the current line of the window
10 | func ResetWindowLine() {
11 | wy := mem.GetMemoryMap()[0xFF4A]
12 |
13 | if (windowLine == 0) && (lyCounter < 144) && (lyCounter > wy) {
14 | windowLine = 144
15 | }
16 | }
17 |
18 | func renderWindow(line uint8) {
19 | if windowLine > 143 {
20 | return
21 | }
22 |
23 | lcdc := mem.GetMemoryMap()[0xFF40]
24 | if !util.IsSetBit(lcdc, 5) {
25 | return
26 | }
27 |
28 | wx := int(mem.GetMemoryMap()[0xFF4B]) - 7
29 | if wx > 159 {
30 | return
31 | }
32 |
33 | wy := mem.GetMemoryMap()[0xFF4A]
34 | if (wy > 143) || (wy > line) {
35 | return
36 | }
37 |
38 | var tilesAddr uint = 0x8800
39 | if util.IsSetBit(lcdc, 4) {
40 | tilesAddr = 0x8000
41 | }
42 | var mapAddr uint = 0x9800
43 | if util.IsSetBit(lcdc, 6) {
44 | mapAddr = 0x9C00
45 | }
46 |
47 | lineAdjusted := windowLine
48 | y32 := (lineAdjusted / 8) * 32
49 | pixely := lineAdjusted % 8
50 | pixely2 := pixely * 2
51 | lineWidth := uint(line) * util.GbWidth
52 |
53 | var x uint
54 | for ; x < 32; x++ {
55 | var tile int
56 |
57 | if tilesAddr == 0x8800 {
58 | tile = int(int8(mem.GetMemoryMap()[mapAddr+y32+x]))
59 | tile += 128
60 | } else {
61 | tile = int(mem.GetMemoryMap()[mapAddr+y32+x])
62 | }
63 |
64 | mapOffsetX := x * 8
65 | tile16 := uint(tile) * 16
66 | tileAddress := tilesAddr + tile16 + pixely2
67 |
68 | byte1 := mem.GetMemoryMap()[tileAddress]
69 | byte2 := mem.GetMemoryMap()[tileAddress+1]
70 |
71 | var pixelx uint8
72 | for ; pixelx < 8; pixelx++ {
73 | bufferX := (int(mapOffsetX) + int(pixelx) + wx)
74 |
75 | if (bufferX < 0) || (bufferX >= util.GbWidth) {
76 | continue
77 | }
78 |
79 | var pixel uint8
80 | if (byte1 & (0x1 << (7 - pixelx))) != 0 {
81 | pixel = 1
82 | }
83 | if (byte2 & (0x1 << (7 - pixelx))) != 0 {
84 | pixel |= 2
85 | }
86 |
87 | position := lineWidth + uint(bufferX)
88 | colorCacheBuffer[position] = pixel & 0x03
89 |
90 | palette := mem.GetMemoryMap()[0xFF47]
91 | color := (palette >> (pixel * 2)) & 0x03
92 | GbFrameBuffer[position] = color
93 |
94 | }
95 | }
96 | windowLine++
97 | }
98 |
--------------------------------------------------------------------------------
/glfw/glfw.go:
--------------------------------------------------------------------------------
1 | package glfw
2 |
3 | import (
4 | "fmt"
5 | "log"
6 | "runtime"
7 |
8 | "github.com/drhelius/demo-emulator/gb/core"
9 | "github.com/drhelius/demo-emulator/gb/util"
10 | "github.com/go-gl/glfw/v3.2/glfw"
11 | )
12 |
13 | // multiplier to control the size of the Window
14 | // Width = GbWidth * zoom, Height = GbHeight * zoom
15 | const zoom = 5
16 |
17 | var window *glfw.Window
18 |
19 | func init() {
20 | // GLFW event handling must run on the main OS thread
21 | runtime.LockOSThread()
22 | }
23 |
24 | // Setup initializes the windowing system
25 | func Setup() {
26 | if err := glfw.Init(); err != nil {
27 | log.Fatalln("failed to initialize glfw:", err)
28 | }
29 |
30 | fmt.Println("glfw init ok")
31 |
32 | // fixed size window
33 | glfw.WindowHint(glfw.Resizable, glfw.False)
34 |
35 | // sets the OpenGL version to 2.1
36 | glfw.WindowHint(glfw.ContextVersionMajor, 2)
37 | glfw.WindowHint(glfw.ContextVersionMinor, 1)
38 |
39 | var err error
40 | window, err = glfw.CreateWindow(util.GbWidth*zoom, util.GbHeight*zoom, "Hack & Sangria - GB Emu", nil, nil)
41 | if err != nil {
42 | panic(err)
43 | }
44 |
45 | // sets the callback to receive input events
46 | window.SetKeyCallback(onKey)
47 |
48 | // move the rendering context to this thread
49 | window.MakeContextCurrent()
50 |
51 | // enable vsync, forces 60fps on most displays
52 | glfw.SwapInterval(1)
53 |
54 | fmt.Println("window ok")
55 | }
56 |
57 | // Update swaps the buffers and updates the input
58 | func Update() {
59 | window.SwapBuffers()
60 | glfw.PollEvents()
61 | }
62 |
63 | // WindowClosed checks if the window has been closed by the user
64 | func WindowClosed() bool {
65 | return window.ShouldClose()
66 | }
67 |
68 | // Teardown shut downs the windowing system
69 | func Teardown() {
70 | glfw.Terminate()
71 | }
72 |
73 | func onKey(window *glfw.Window, k glfw.Key, s int, action glfw.Action, mods glfw.ModifierKey) {
74 |
75 | // check for key press / release events
76 | // and notifiy the emulator accordingly
77 |
78 | switch glfw.Key(k) {
79 | case glfw.KeyEnter:
80 | if action == glfw.Press {
81 | core.ButtonPressed(util.StartButton)
82 | } else if action == glfw.Release {
83 | core.ButtonReleased(util.StartButton)
84 | }
85 | case glfw.KeySpace:
86 | if action == glfw.Press {
87 | core.ButtonPressed(util.SelectButton)
88 | } else if action == glfw.Release {
89 | core.ButtonReleased(util.SelectButton)
90 | }
91 | case glfw.KeyS:
92 | if action == glfw.Press {
93 | core.ButtonPressed(util.AButton)
94 | } else if action == glfw.Release {
95 | core.ButtonReleased(util.AButton)
96 | }
97 | case glfw.KeyA:
98 | if action == glfw.Press {
99 | core.ButtonPressed(util.BButton)
100 | } else if action == glfw.Release {
101 | core.ButtonReleased(util.BButton)
102 | }
103 | case glfw.KeyUp:
104 | if action == glfw.Press {
105 | core.ButtonPressed(util.UpButton)
106 | } else if action == glfw.Release {
107 | core.ButtonReleased(util.UpButton)
108 | }
109 | case glfw.KeyDown:
110 | if action == glfw.Press {
111 | core.ButtonPressed(util.DownButton)
112 | } else if action == glfw.Release {
113 | core.ButtonReleased(util.DownButton)
114 | }
115 | case glfw.KeyLeft:
116 | if action == glfw.Press {
117 | core.ButtonPressed(util.LeftButton)
118 | } else if action == glfw.Release {
119 | core.ButtonReleased(util.LeftButton)
120 | }
121 | case glfw.KeyRight:
122 | if action == glfw.Press {
123 | core.ButtonPressed(util.RightButton)
124 | } else if action == glfw.Release {
125 | core.ButtonReleased(util.RightButton)
126 | }
127 | default:
128 | return
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "flag"
5 | "fmt"
6 |
7 | "github.com/drhelius/demo-emulator/gb/core"
8 | "github.com/drhelius/demo-emulator/gb/util"
9 | "github.com/drhelius/demo-emulator/glfw"
10 | "github.com/drhelius/demo-emulator/opengl"
11 | )
12 |
13 | var colorFrameBuffer [util.GbWidth * util.GbHeight * 4]uint8
14 |
15 | func main() {
16 | // init windowing system
17 | glfw.Setup()
18 |
19 | // init rendering system
20 | opengl.Setup(colorFrameBuffer[:])
21 |
22 | loadROM()
23 |
24 | loop()
25 |
26 | opengl.Teardown()
27 | glfw.Teardown()
28 | }
29 |
30 | func loadROM() {
31 | // get the rom path from a command line argument
32 | romPathPtr := flag.String("rom", "", "rom path")
33 | flag.Parse()
34 |
35 | // tell the emulator to load the rom from a path
36 | core.LoadROM(*romPathPtr)
37 | }
38 |
39 | func loop() {
40 | fmt.Println("starting loop...")
41 |
42 | // while the user doesn't close the window
43 | for !glfw.WindowClosed() {
44 |
45 | // run the emulation one frame
46 | // it should be called 60 times in a second
47 | core.RunToVBlank(colorFrameBuffer[:])
48 |
49 | // render de results
50 | opengl.Render()
51 |
52 | // present the rendering in a window
53 | // and update input
54 | glfw.Update()
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/opengl/opengl.go:
--------------------------------------------------------------------------------
1 | package opengl
2 |
3 | import (
4 | "fmt"
5 |
6 | "github.com/drhelius/demo-emulator/gb/util"
7 | "github.com/go-gl/gl/v2.1/gl"
8 | )
9 |
10 | var (
11 | texture uint32
12 | colorFrameBuffer []uint8
13 | )
14 |
15 | // Setup initializes OpenGL
16 | func Setup(fb []uint8) {
17 |
18 | // a reference to the array of pixels generated by the
19 | // emulator that will be presented on the screen
20 | colorFrameBuffer = fb
21 |
22 | if err := gl.Init(); err != nil {
23 | panic(err)
24 | }
25 |
26 | fmt.Println("gl init ok")
27 |
28 | // enable textures and create one texture to dump the contents
29 | // of the emulator frame buffer
30 | // this texture has the same size as the Game Boy screen
31 | gl.Enable(gl.TEXTURE_2D)
32 | gl.GenTextures(1, &texture)
33 | gl.BindTexture(gl.TEXTURE_2D, texture)
34 | gl.TexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, util.GbWidth, util.GbHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(&colorFrameBuffer[0]))
35 |
36 | fmt.Println("gl setup ok")
37 | }
38 |
39 | // Render draws the current frame
40 | func Render() {
41 |
42 | // bind the texture we create before and transfer
43 | // the contents of the emulator frame buffer to it
44 | // this way the texture will store the updated pixels
45 | // of the frame buffer
46 | gl.BindTexture(gl.TEXTURE_2D, texture)
47 | gl.TexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, util.GbWidth, util.GbHeight, gl.RGBA, gl.UNSIGNED_BYTE, gl.Ptr(&colorFrameBuffer[0]))
48 |
49 | // disable any filtering to avoid bluring the texture
50 | gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
51 | gl.TexParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
52 |
53 | // set an orthogonal projection (2D) with the size of
54 | // the Game Boy Screen
55 | gl.MatrixMode(gl.PROJECTION)
56 | gl.LoadIdentity()
57 | gl.Ortho(0.0, util.GbWidth, 0.0, util.GbHeight, -1.0, 1.0)
58 | gl.MatrixMode(gl.MODELVIEW)
59 |
60 | // render a single quad with the size of the Game Boy
61 | // screen and with the contents of the emulator
62 | // frame buffer (already in the texture)
63 | gl.Begin(gl.QUADS)
64 | gl.TexCoord2d(0.0, 1.0)
65 | gl.Vertex2d(0.0, 0.0)
66 | gl.TexCoord2d(1.0, 1.0)
67 | gl.Vertex2d(util.GbWidth, 0.0)
68 | gl.TexCoord2d(1.0, 0.0)
69 | gl.Vertex2d(util.GbWidth, util.GbHeight)
70 | gl.TexCoord2d(0.0, 0.0)
71 | gl.Vertex2d(0.0, util.GbHeight)
72 | gl.End()
73 | }
74 |
75 | // Teardown shut downs OpenGL
76 | func Teardown() {
77 | gl.DeleteTextures(1, &texture)
78 | }
79 |
--------------------------------------------------------------------------------
/screenshots/screenshot1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drhelius/demo-emulator/20fbe5669ec274ea9fdca7eb805a5c789519a93c/screenshots/screenshot1.png
--------------------------------------------------------------------------------
/screenshots/screenshot2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drhelius/demo-emulator/20fbe5669ec274ea9fdca7eb805a5c789519a93c/screenshots/screenshot2.png
--------------------------------------------------------------------------------
/slides/Golang Emus.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drhelius/demo-emulator/20fbe5669ec274ea9fdca7eb805a5c789519a93c/slides/Golang Emus.pdf
--------------------------------------------------------------------------------
/test_roms/cpu_instrs/cpu_instrs.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drhelius/demo-emulator/20fbe5669ec274ea9fdca7eb805a5c789519a93c/test_roms/cpu_instrs/cpu_instrs.gb
--------------------------------------------------------------------------------
/test_roms/cpu_instrs/individual/01-special.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drhelius/demo-emulator/20fbe5669ec274ea9fdca7eb805a5c789519a93c/test_roms/cpu_instrs/individual/01-special.gb
--------------------------------------------------------------------------------
/test_roms/cpu_instrs/individual/02-interrupts.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drhelius/demo-emulator/20fbe5669ec274ea9fdca7eb805a5c789519a93c/test_roms/cpu_instrs/individual/02-interrupts.gb
--------------------------------------------------------------------------------
/test_roms/cpu_instrs/individual/03-op sp,hl.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drhelius/demo-emulator/20fbe5669ec274ea9fdca7eb805a5c789519a93c/test_roms/cpu_instrs/individual/03-op sp,hl.gb
--------------------------------------------------------------------------------
/test_roms/cpu_instrs/individual/04-op r,imm.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drhelius/demo-emulator/20fbe5669ec274ea9fdca7eb805a5c789519a93c/test_roms/cpu_instrs/individual/04-op r,imm.gb
--------------------------------------------------------------------------------
/test_roms/cpu_instrs/individual/05-op rp.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drhelius/demo-emulator/20fbe5669ec274ea9fdca7eb805a5c789519a93c/test_roms/cpu_instrs/individual/05-op rp.gb
--------------------------------------------------------------------------------
/test_roms/cpu_instrs/individual/06-ld r,r.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drhelius/demo-emulator/20fbe5669ec274ea9fdca7eb805a5c789519a93c/test_roms/cpu_instrs/individual/06-ld r,r.gb
--------------------------------------------------------------------------------
/test_roms/cpu_instrs/individual/07-jr,jp,call,ret,rst.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drhelius/demo-emulator/20fbe5669ec274ea9fdca7eb805a5c789519a93c/test_roms/cpu_instrs/individual/07-jr,jp,call,ret,rst.gb
--------------------------------------------------------------------------------
/test_roms/cpu_instrs/individual/08-misc instrs.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drhelius/demo-emulator/20fbe5669ec274ea9fdca7eb805a5c789519a93c/test_roms/cpu_instrs/individual/08-misc instrs.gb
--------------------------------------------------------------------------------
/test_roms/cpu_instrs/individual/09-op r,r.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drhelius/demo-emulator/20fbe5669ec274ea9fdca7eb805a5c789519a93c/test_roms/cpu_instrs/individual/09-op r,r.gb
--------------------------------------------------------------------------------
/test_roms/cpu_instrs/individual/10-bit ops.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drhelius/demo-emulator/20fbe5669ec274ea9fdca7eb805a5c789519a93c/test_roms/cpu_instrs/individual/10-bit ops.gb
--------------------------------------------------------------------------------
/test_roms/cpu_instrs/individual/11-op a,(hl).gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drhelius/demo-emulator/20fbe5669ec274ea9fdca7eb805a5c789519a93c/test_roms/cpu_instrs/individual/11-op a,(hl).gb
--------------------------------------------------------------------------------
/test_roms/cpu_instrs/readme.txt:
--------------------------------------------------------------------------------
1 | Game Boy CPU Instruction Behavior Test
2 | --------------------------------------
3 | This ROM tests the behavior of all CPU instructions except STOP and the
4 | 11 illegal opcodes. The tests are fairly thorough, running instructions
5 | with boundary data and verifying both the result and that other
6 | registers are not modified. Instructions which perform the same
7 | operation on different registers are each tested just as thoroughly, in
8 | case an emulator implements each independently. Some sub-tests take half
9 | minute to complete.
10 |
11 | Failed instructions are listed as
12 |
13 | [CB] opcode
14 |
15 | Some errors cannot of course be diagnosed properly, since the test
16 | framework itself relies on basic instruction behavior being correct.
17 |
18 |
19 | Internal operation
20 | ------------------
21 | The main tests use a framework that runs each instruction in a loop,
22 | varying the register values on input and examining them on output.
23 | Rather than keep a table of correct values, it simply calculates a
24 | CRC-32 checksum of all the output, then compares this with the correct
25 | value. Instructions are divided into several groups, each with a
26 | different set of input values suited for their behavior; for example,
27 | the bit test instructions are fed $01, $02, $04 ... $40, $80, to ensure
28 | each bit is handled properly, while the arithmetic instructions are fed
29 | $01, $0F, $10, $7F, $FF, to exercise carry and half-carry. A few
30 | instructions require a custom test due to their uniqueness.
31 |
32 |
33 | Multi-ROM
34 | ---------
35 | In the main directory is a single ROM which runs all the tests. It
36 | prints a test's number, runs the test, then "ok" if it passes, otherwise
37 | a failure code. Once all tests have completed it either reports that all
38 | tests passed, or prints the number of failed tests. Finally, it makes
39 | several beeps. If a test fails, it can be run on its own by finding the
40 | corresponding ROM in individual/.
41 |
42 | Ths compact format on screen is to avoid having the results scroll off
43 | the top, so the test can be started and allowed to run without having to
44 | constantly monitor the display.
45 |
46 | Currently there is no well-defined way for an emulator test rig to
47 | programatically find the result of the test; contact me if you're trying
48 | to do completely automated testing of your emulator. One simple approach
49 | is to take a screenshot after all tests have run, or even just a
50 | checksum of one, and compare this with a previous run.
51 |
52 |
53 | Failure codes
54 | -------------
55 | Failed tests may print a failure code, and also short description of the
56 | problem. For more information about a failure code, look in the
57 | corresponding source file in source/; the point in the code where
58 | "set_test n" occurs is where that failure code will be generated.
59 | Failure code 1 is a general failure of the test; any further information
60 | will be printed.
61 |
62 | Note that once a sub-test fails, no further tests for that file are run.
63 |
64 |
65 | Console output
66 | --------------
67 | Information is printed on screen in a way that needs only minimum LCD
68 | support, and won't hang if LCD output isn't supported at all.
69 | Specifically, while polling LY to wait for vblank, it will time out if
70 | it takes too long, so LY always reading back as the same value won't
71 | hang the test. It's also OK if scrolling isn't supported; in this case,
72 | text will appear starting at the top of the screen.
73 |
74 | Everything printed on screen is also sent to the game link port by
75 | writing the character to SB, then writing $81 to SC. This is useful for
76 | tests which print lots of information that scrolls off screen.
77 |
78 |
79 | Source code
80 | -----------
81 | Source code is included for all tests, in source/. It can be used to
82 | build the individual test ROMs. Code for the multi test isn't included
83 | due to the complexity of putting everything together.
84 |
85 | Code is written for the wla-dx assembler. To assemble a particular test,
86 | execute
87 |
88 | wla -o "source_filename.s" test.o
89 | wlalink linkfile test.gb
90 |
91 | Test code uses a common shell framework contained in common/.
92 |
93 |
94 | Internal framework operation
95 | ----------------------------
96 | Tests use a common framework for setting things up, reporting results,
97 | and ending. All files first include "shell.inc", which sets up the ROM
98 | header and shell code, and includes other commonly-used modules.
99 |
100 | One oddity is that test code is first copied to internal RAM at $D000,
101 | then executed there. This allows self-modification, and ensures the code
102 | is executed the same way it is on my devcart, which doesn't have a
103 | rewritable ROM as most do.
104 |
105 | Some macros are used to simplify common tasks:
106 |
107 | Macro Behavior
108 | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
109 | wreg addr,data Writes data to addr using LDH
110 | lda addr Loads byte from addr into A using LDH
111 | sta addr Stores A at addr using LDH
112 | delay n Delays n cycles, where NOP = 1 cycle
113 | delay_msec n Delays n milliseconds
114 | set_test n,"Cause" Sets failure code and optional string
115 |
116 | Routines and macros are documented where they are defined.
117 |
118 | --
119 | Shay Green
120 |
--------------------------------------------------------------------------------
/test_roms/instr_timing/instr_timing.gb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drhelius/demo-emulator/20fbe5669ec274ea9fdca7eb805a5c789519a93c/test_roms/instr_timing/instr_timing.gb
--------------------------------------------------------------------------------
/test_roms/instr_timing/readme.txt:
--------------------------------------------------------------------------------
1 | Game Boy CPU Instruction Timing Test
2 | ------------------------------------
3 | This ROM tests the timings of all CPU instructions except HALT, STOP,
4 | and the 11 illegal opcodes. For conditional instructions, it tests taken
5 | and not taken timings. This test requires proper timer operation (TAC,
6 | TIMA, TMA).
7 |
8 | Failed instructions are listed as
9 |
10 | [CB] opcode:measured time-correct time
11 |
12 | Times are in terms of instruction cycles, where NOP takes one cycle.
13 |
14 |
15 | Verified cycle timing tables
16 | ----------------------------
17 | The test internally uses a table of proper cycle times, which can be
18 | used in an emulator to ensure proper timing. The only changes below are
19 | removal of the .byte prefixes, and addition of commas at the ends, so
20 | that they can be used without changes in most programming languages. For
21 | added correctness assurance, the original tables can be found at the end
22 | of the source code.
23 |
24 | Normal instructions:
25 |
26 | 1,3,2,2,1,1,2,1,5,2,2,2,1,1,2,1,
27 | 0,3,2,2,1,1,2,1,3,2,2,2,1,1,2,1,
28 | 2,3,2,2,1,1,2,1,2,2,2,2,1,1,2,1,
29 | 2,3,2,2,3,3,3,1,2,2,2,2,1,1,2,1,
30 | 1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,
31 | 1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,
32 | 1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,
33 | 2,2,2,2,2,2,0,2,1,1,1,1,1,1,2,1,
34 | 1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,
35 | 1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,
36 | 1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,
37 | 1,1,1,1,1,1,2,1,1,1,1,1,1,1,2,1,
38 | 2,3,3,4,3,4,2,4,2,4,3,0,3,6,2,4,
39 | 2,3,3,0,3,4,2,4,2,4,3,0,3,0,2,4,
40 | 3,3,2,0,0,4,2,4,4,1,4,0,0,0,2,4,
41 | 3,3,2,1,0,4,2,4,3,2,4,1,0,0,2,4
42 |
43 | CB-prefixed instructions:
44 |
45 | 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2,
46 | 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2,
47 | 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2,
48 | 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2,
49 | 2,2,2,2,2,2,3,2,2,2,2,2,2,2,3,2,
50 | 2,2,2,2,2,2,3,2,2,2,2,2,2,2,3,2,
51 | 2,2,2,2,2,2,3,2,2,2,2,2,2,2,3,2,
52 | 2,2,2,2,2,2,3,2,2,2,2,2,2,2,3,2,
53 | 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2,
54 | 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2,
55 | 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2,
56 | 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2,
57 | 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2,
58 | 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2,
59 | 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2,
60 | 2,2,2,2,2,2,4,2,2,2,2,2,2,2,4,2
61 |
62 |
63 | Internal operation
64 | ------------------
65 | Before each instruction is executed, the test sets up registers and
66 | memory in such a way that the instruction will cleanly execute and then
67 | end up at a common destination, without trashing anything important. The
68 | timing itself is done by first synchronizing to the timer via a loop,
69 | executing the instruction, then using a similar loop to determine how
70 | many clocks elapsed.
71 |
72 |
73 | Failure codes
74 | -------------
75 | Failed tests may print a failure code, and also short description of the
76 | problem. For more information about a failure code, look in the
77 | corresponding source file in source/; the point in the code where
78 | "set_test n" occurs is where that failure code will be generated.
79 | Failure code 1 is a general failure of the test; any further information
80 | will be printed.
81 |
82 | Note that once a sub-test fails, no further tests for that file are run.
83 |
84 |
85 | Console output
86 | --------------
87 | Information is printed on screen in a way that needs only minimum LCD
88 | support, and won't hang if LCD output isn't supported at all.
89 | Specifically, while polling LY to wait for vblank, it will time out if
90 | it takes too long, so LY always reading back as the same value won't
91 | hang the test. It's also OK if scrolling isn't supported; in this case,
92 | text will appear starting at the top of the screen.
93 |
94 | Everything printed on screen is also sent to the game link port by
95 | writing the character to SB, then writing $81 to SC. This is useful for
96 | tests which print lots of information that scrolls off screen.
97 |
98 |
99 | Source code
100 | -----------
101 | Source code is included for all tests, in source/. It can be used to
102 | build the individual test ROMs. Code for the multi test isn't included
103 | due to the complexity of putting everything together.
104 |
105 | Code is written for the wla-dx assembler. To assemble a particular test,
106 | execute
107 |
108 | wla -o "source_filename.s" test.o
109 | wlalink linkfile test.gb
110 |
111 | Test code uses a common shell framework contained in common/.
112 |
113 |
114 | Internal framework operation
115 | ----------------------------
116 | Tests use a common framework for setting things up, reporting results,
117 | and ending. All files first include "shell.inc", which sets up the ROM
118 | header and shell code, and includes other commonly-used modules.
119 |
120 | One oddity is that test code is first copied to internal RAM at $D000,
121 | then executed there. This allows self-modification, and ensures the code
122 | is executed the same way it is on my devcart, which doesn't have a
123 | rewritable ROM as most do.
124 |
125 | Some macros are used to simplify common tasks:
126 |
127 | Macro Behavior
128 | - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
129 | wreg addr,data Writes data to addr using LDH
130 | lda addr Loads byte from addr into A using LDH
131 | sta addr Stores A at addr using LDH
132 | delay n Delays n cycles, where NOP = 1 cycle
133 | delay_msec n Delays n milliseconds
134 | set_test n,"Cause" Sets failure code and optional string
135 |
136 | Routines and macros are documented where they are defined.
137 |
138 | --
139 | Shay Green
140 |
--------------------------------------------------------------------------------
/test_roms/testgb/PUZZLE.GB:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drhelius/demo-emulator/20fbe5669ec274ea9fdca7eb805a5c789519a93c/test_roms/testgb/PUZZLE.GB
--------------------------------------------------------------------------------
/test_roms/testgb/RPN.GB:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drhelius/demo-emulator/20fbe5669ec274ea9fdca7eb805a5c789519a93c/test_roms/testgb/RPN.GB
--------------------------------------------------------------------------------
/test_roms/testgb/SOUND.GB:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drhelius/demo-emulator/20fbe5669ec274ea9fdca7eb805a5c789519a93c/test_roms/testgb/SOUND.GB
--------------------------------------------------------------------------------
/test_roms/testgb/SPACE.GB:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drhelius/demo-emulator/20fbe5669ec274ea9fdca7eb805a5c789519a93c/test_roms/testgb/SPACE.GB
--------------------------------------------------------------------------------
/test_roms/testgb/SPRITE.GB:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drhelius/demo-emulator/20fbe5669ec274ea9fdca7eb805a5c789519a93c/test_roms/testgb/SPRITE.GB
--------------------------------------------------------------------------------
/test_roms/testgb/TEST.GB:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drhelius/demo-emulator/20fbe5669ec274ea9fdca7eb805a5c789519a93c/test_roms/testgb/TEST.GB
--------------------------------------------------------------------------------
/test_roms/workshop.rom:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/drhelius/demo-emulator/20fbe5669ec274ea9fdca7eb805a5c789519a93c/test_roms/workshop.rom
--------------------------------------------------------------------------------