From d8fd36f22d88dc875db3bf4e2a313fff42c4069d Mon Sep 17 00:00:00 2001 From: alloncm Date: Sun, 25 Apr 2021 02:02:57 +0300 Subject: [PATCH 001/136] Make the keyboard more responsive --- gb/src/sdl_joypad_provider.rs | 15 ++++++++++++--- lib_gb/src/keypad/joypad_provider.rs | 2 +- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/gb/src/sdl_joypad_provider.rs b/gb/src/sdl_joypad_provider.rs index 9deaf697..3aafa762 100644 --- a/gb/src/sdl_joypad_provider.rs +++ b/gb/src/sdl_joypad_provider.rs @@ -5,22 +5,31 @@ use lib_gb::keypad::{ button::Button }; +const PUMP_THRESHOLD:u32 = 1000; + pub struct SdlJoypadProviderSDL_Scancode>{ - mapper: F + mapper: F, + pump_counter:u32, } implSDL_Scancode> SdlJoypadProvider{ pub fn new(mapper:F)->Self{ SdlJoypadProvider{ - mapper + mapper, + pump_counter:PUMP_THRESHOLD } } } implSDL_Scancode> JoypadProvider for SdlJoypadProvider{ - fn provide(&self, joypad:&mut Joypad) { + fn provide(&mut self, joypad:&mut Joypad) { let mapper = &(self.mapper); unsafe{ + self.pump_counter = (self.pump_counter + 1) % PUMP_THRESHOLD; + if self.pump_counter == 0{ + SDL_PumpEvents(); + } + let keyborad_state:*const u8 = SDL_GetKeyboardState(std::ptr::null_mut()); joypad.buttons[Button::A as usize] = *keyborad_state.offset(mapper(Button::A) as isize) != 0; diff --git a/lib_gb/src/keypad/joypad_provider.rs b/lib_gb/src/keypad/joypad_provider.rs index 276411bc..1fe9f2e6 100644 --- a/lib_gb/src/keypad/joypad_provider.rs +++ b/lib_gb/src/keypad/joypad_provider.rs @@ -1,5 +1,5 @@ use super::joypad::Joypad; pub trait JoypadProvider{ - fn provide(&self, joypad:&mut Joypad); + fn provide(&mut self, joypad:&mut Joypad); } \ No newline at end of file From bd2c86e1d1184bb8093c0f04e5403db811bb9cf7 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 1 May 2021 19:03:16 +0300 Subject: [PATCH 002/136] Decrease the threshold to every 500 intrs --- gb/src/sdl_joypad_provider.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gb/src/sdl_joypad_provider.rs b/gb/src/sdl_joypad_provider.rs index 3aafa762..0ace7ebe 100644 --- a/gb/src/sdl_joypad_provider.rs +++ b/gb/src/sdl_joypad_provider.rs @@ -5,7 +5,7 @@ use lib_gb::keypad::{ button::Button }; -const PUMP_THRESHOLD:u32 = 1000; +const PUMP_THRESHOLD:u32 = 500; pub struct SdlJoypadProviderSDL_Scancode>{ mapper: F, From 8d974cc77e9ecb1d88e13f5c177d015399bc1eaa Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 1 May 2021 19:06:59 +0300 Subject: [PATCH 003/136] Move getting to keyboard state to the ctor --- gb/src/sdl_joypad_provider.rs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/gb/src/sdl_joypad_provider.rs b/gb/src/sdl_joypad_provider.rs index 0ace7ebe..b6d931d8 100644 --- a/gb/src/sdl_joypad_provider.rs +++ b/gb/src/sdl_joypad_provider.rs @@ -10,13 +10,15 @@ const PUMP_THRESHOLD:u32 = 500; pub struct SdlJoypadProviderSDL_Scancode>{ mapper: F, pump_counter:u32, + keyborad_state: *const u8 } implSDL_Scancode> SdlJoypadProvider{ pub fn new(mapper:F)->Self{ SdlJoypadProvider{ mapper, - pump_counter:PUMP_THRESHOLD + pump_counter:PUMP_THRESHOLD, + keyborad_state: unsafe{SDL_GetKeyboardState(std::ptr::null_mut())} } } } @@ -30,16 +32,14 @@ implSDL_Scancode> JoypadProvider for SdlJoypadProvider{ SDL_PumpEvents(); } - let keyborad_state:*const u8 = SDL_GetKeyboardState(std::ptr::null_mut()); - - joypad.buttons[Button::A as usize] = *keyborad_state.offset(mapper(Button::A) as isize) != 0; - joypad.buttons[Button::B as usize] = *keyborad_state.offset(mapper(Button::B) as isize) != 0; - joypad.buttons[Button::Start as usize] = *keyborad_state.offset(mapper(Button::Start) as isize) != 0; - joypad.buttons[Button::Select as usize] = *keyborad_state.offset(mapper(Button::Select) as isize) != 0; - joypad.buttons[Button::Up as usize] = *keyborad_state.offset(mapper(Button::Up) as isize) != 0; - joypad.buttons[Button::Down as usize] = *keyborad_state.offset(mapper(Button::Down) as isize) != 0; - joypad.buttons[Button::Right as usize] = *keyborad_state.offset(mapper(Button::Right) as isize) != 0; - joypad.buttons[Button::Left as usize] = *keyborad_state.offset(mapper(Button::Left) as isize) != 0; + joypad.buttons[Button::A as usize] = *self.keyborad_state.offset(mapper(Button::A) as isize) != 0; + joypad.buttons[Button::B as usize] = *self.keyborad_state.offset(mapper(Button::B) as isize) != 0; + joypad.buttons[Button::Start as usize] = *self.keyborad_state.offset(mapper(Button::Start) as isize) != 0; + joypad.buttons[Button::Select as usize] = *self.keyborad_state.offset(mapper(Button::Select) as isize) != 0; + joypad.buttons[Button::Up as usize] = *self.keyborad_state.offset(mapper(Button::Up) as isize) != 0; + joypad.buttons[Button::Down as usize] = *self.keyborad_state.offset(mapper(Button::Down) as isize) != 0; + joypad.buttons[Button::Right as usize] = *self.keyborad_state.offset(mapper(Button::Right) as isize) != 0; + joypad.buttons[Button::Left as usize] = *self.keyborad_state.offset(mapper(Button::Left) as isize) != 0; } } } \ No newline at end of file From d3274332287375177dff4a732e5f88ef585eddeb Mon Sep 17 00:00:00 2001 From: alloncm Date: Mon, 3 May 2021 00:27:08 +0300 Subject: [PATCH 004/136] Add resources and fix typo --- README.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 219893b1..3d6286a4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # MagenBoy -A GameBoy emulator developped by me. +A GameBoy emulator developed by me. The main goal of this project is to be able to play Pokemon on my own emulator. @@ -30,4 +30,12 @@ The main goal of this project is to be able to play Pokemon on my own emulator. ## GameBoy Color -Curerently there is no Support (support is planned in the future) \ No newline at end of file +Curerently there is no Support (support is planned in the future) + +## Resources +- [The Pandocs](https://gbdev.io/pandocs/) +- [gbops](https://izik1.github.io/gbops/index.html) +- [The GameBoy Programming Manual](http://index-of.es/Varios-2/Game%20Boy%20Programming%20Manual.pdf) +- [gbdev gameboy sound hardware](https://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware) +- [Hactix's awsome blog post](https://hacktix.github.io/GBEDG/) +- [Nightshade's awsome blog post](https://nightshade256.github.io/2021/03/27/gb-sound-emulation.html) \ No newline at end of file From 9803f163635902177495de28752e31e2d6d4df2f Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 7 May 2021 13:29:03 +0300 Subject: [PATCH 005/136] Create LICENSE Choose to use GPL3 --- LICENSE | 674 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 674 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..f288702d --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. From 61fd4ea069d64fa2f3cf41638d8dc31af1733772 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 8 May 2021 14:50:27 +0300 Subject: [PATCH 006/136] Fix sync audio (#45) Audio sync is now mostly non blocking. There is still a short blocking in the audio device in orer to sync to the last byte with the audio without this the audio will go out of sync with time --- gb/src/main.rs | 17 +++++++++++++++-- gb/src/sdl_audio_device.rs | 4 ++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/gb/src/main.rs b/gb/src/main.rs index 10f0dd15..309774a7 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -14,8 +14,11 @@ use std::{ use log::info; use sdl2::sys::*; +const FPS:f64 = GB_FREQUENCY as f64 / 70224.0; +const FRAME_TIME_MS:f64 = (1.0 / FPS) * 1000.0; -fn extend_vec(vec:Vec, scale:usize, w:usize, h:usize)->Vec{ + +fn extend_vec(vec:&[u32], scale:usize, w:usize, h:usize)->Vec{ let mut new_vec = vec![0;vec.len()*scale*scale]; for y in 0..h{ let sy = y*scale; @@ -140,7 +143,9 @@ fn main() { unsafe{ let mut event: std::mem::MaybeUninit = std::mem::MaybeUninit::uninit(); + let mut start:u64 = SDL_GetPerformanceCounter(); loop{ + if SDL_PollEvent(event.as_mut_ptr()) != 0{ let event: SDL_Event = event.assume_init(); if event.type_ == SDL_EventType::SDL_QUIT as u32{ @@ -148,7 +153,7 @@ fn main() { } } - let frame_buffer:Vec = gameboy.cycle_frame().to_vec(); + let frame_buffer = gameboy.cycle_frame(); let scaled_buffer = extend_vec(frame_buffer, screen_scale as usize, SCREEN_WIDTH, SCREEN_HEIGHT); let mut pixels: *mut c_void = std::ptr::null_mut(); @@ -160,6 +165,14 @@ fn main() { SDL_RenderClear(renderer); SDL_RenderCopy(renderer, texture, std::ptr::null(), std::ptr::null()); SDL_RenderPresent(renderer); + + let end = SDL_GetPerformanceCounter(); + let elapsed_ms:f64 = (end - start) as f64 / SDL_GetPerformanceFrequency() as f64 * 1000.0; + if elapsed_ms < FRAME_TIME_MS{ + SDL_Delay((FRAME_TIME_MS - elapsed_ms).floor() as u32); + } + + start = SDL_GetPerformanceCounter(); } SDL_Quit(); diff --git a/gb/src/sdl_audio_device.rs b/gb/src/sdl_audio_device.rs index f2a13657..36a645d6 100644 --- a/gb/src/sdl_audio_device.rs +++ b/gb/src/sdl_audio_device.rs @@ -5,7 +5,7 @@ use crate::audio_resampler::AudioResampler; //After twicking those numbers Iv reached this, this will affect fps which will affect sound tearing const BUFFER_SIZE:usize = 1024 * 2; -const SAMPLES_TO_WAIT:u32 = BUFFER_SIZE as u32 * 4; +const BYTES_TO_WAIT:u32 = BUFFER_SIZE as u32 * 8; pub struct SdlAudioDevie{ device_id: SDL_AudioDeviceID, @@ -73,7 +73,7 @@ impl SdlAudioDevie{ let data_byte_len = (audio.len() * std::mem::size_of::()) as u32; unsafe{ - while SDL_GetQueuedAudioSize(self.device_id) > SAMPLES_TO_WAIT{} + while SDL_GetQueuedAudioSize(self.device_id) > BYTES_TO_WAIT{} SDL_ClearError(); if SDL_QueueAudio(self.device_id, audio_ptr, data_byte_len) != 0{ From 665327ec5d2e4c8ceddeda80c15eebba19b44ba5 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 8 May 2021 17:27:36 +0300 Subject: [PATCH 007/136] Move the apu to use the io_comps this a new method for syncing the io devices with the io ports --- lib_gb/src/apu/apu_registers_updater.rs | 436 ++++++++++++++++-------- lib_gb/src/apu/gb_apu.rs | 23 +- lib_gb/src/apu/mod.rs | 2 +- lib_gb/src/machine/gameboy.rs | 24 +- lib_gb/src/mmu/gb_mmu.rs | 36 +- lib_gb/src/mmu/io_comps.rs | 58 ++++ lib_gb/src/mmu/io_ports.rs | 2 + lib_gb/src/mmu/mmu_register_updater.rs | 14 +- lib_gb/src/mmu/mod.rs | 3 +- lib_gb/src/ppu/ppu_register_updater.rs | 20 +- 10 files changed, 408 insertions(+), 210 deletions(-) create mode 100644 lib_gb/src/mmu/io_comps.rs diff --git a/lib_gb/src/apu/apu_registers_updater.rs b/lib_gb/src/apu/apu_registers_updater.rs index 40d33124..92940cb9 100644 --- a/lib_gb/src/apu/apu_registers_updater.rs +++ b/lib_gb/src/apu/apu_registers_updater.rs @@ -14,82 +14,146 @@ use super::{ sound_utils::NUMBER_OF_CHANNELS }; -pub fn update_apu_registers(memory:&mut GbMmu, apu:&mut GbApu){ - prepare_control_registers(apu, memory); - - if apu.enabled{ - prepare_wave_channel(&mut apu.wave_channel, memory, &apu.frame_sequencer); - prepare_tone_sweep_channel(&mut apu.sweep_tone_channel, memory, &apu.frame_sequencer); - prepare_noise_channel(&mut apu.noise_channel, memory, &apu.frame_sequencer); - prepare_tone_channel(&mut apu.tone_channel, memory, &apu.frame_sequencer); +// pub fn update_apu_registers(memory:&mut GbMmu, apu:&mut GbApu){ +// prepare_control_registers(apu, memory); + +// if apu.enabled{ +// prepare_wave_channel(&mut apu.wave_channel, memory, &apu.frame_sequencer); +// prepare_tone_sweep_channel(&mut apu.sweep_tone_channel, memory, &apu.frame_sequencer); +// prepare_noise_channel(&mut apu.noise_channel, memory, &apu.frame_sequencer); +// prepare_tone_channel(&mut apu.tone_channel, memory, &apu.frame_sequencer); +// } +// } + +// fn prepare_tone_channel(channel:&mut Channel, memory:&mut GbMmu,fs:&FrameSequencer){ + +// if memory.io_ports.get_ports_cycle_trigger()[NR21_REGISTER_INDEX as usize]{ +// channel.sound_length = 64 - (memory.read_unprotected(NR21_REGISTER_ADDRESS) & 0b11_1111) as u16; +// } +// if memory.io_ports.get_ports_cycle_trigger()[NR22_REGISTER_INDEX as usize]{ +// update_volume_envelope(memory.read_unprotected(NR22_REGISTER_ADDRESS), &mut channel.sample_producer.envelop); + +// if !is_dac_enabled(channel.sample_producer.envelop.volume, channel.sample_producer.envelop.increase_envelope){ +// channel.enabled = false; +// } +// } +// if memory.io_ports.get_ports_cycle_trigger()[NR23_REGISTER_INDEX as usize]{ +// //discard lower bit +// channel.frequency &= 0xFF00; +// channel.frequency |= memory.read_unprotected(NR23_REGISTER_ADDRESS) as u16; +// } +// if memory.io_ports.get_ports_cycle_trigger()[NR24_REGISTER_INDEX as usize]{ +// let nr24 = memory.read_unprotected(NR24_REGISTER_ADDRESS); +// //discrad upper bit +// channel.frequency <<= 8; +// channel.frequency >>= 8; +// channel.frequency |= (nr24 as u16 & 0b111) << 8; +// let dac_enabled = is_dac_enabled(channel.sample_producer.envelop.volume, channel.sample_producer.envelop.increase_envelope); +// update_channel_conrol_register(channel, dac_enabled, nr24, 64, fs); +// if nr24 & BIT_7_MASK != 0{ +// //volume +// channel.sample_producer.envelop.envelop_duration_counter = channel.sample_producer.envelop.number_of_envelope_sweep; +// channel.sample_producer.envelop.current_volume = channel.sample_producer.envelop.volume; +// } +// } +// } + + +pub fn set_nr41(channel:&mut Channel, value:u8){ + let length_data = value & 0b11_1111; + channel.sound_length = 64 - length_data as u16 +} + +pub fn set_nr42(channel:&mut Channel, value:u8){ + update_volume_envelope( value, &mut channel.sample_producer.envelop); + if !is_dac_enabled(channel.sample_producer.envelop.volume, channel.sample_producer.envelop.increase_envelope){ + channel.enabled = false; } } -fn prepare_tone_channel(channel:&mut Channel, memory:&mut GbMmu,fs:&FrameSequencer){ +pub fn set_nr43(channel:&mut Channel, nr43:u8){ + channel.sample_producer.bits_to_shift_divisor = (nr43 & 0b1111_0000) >> 4; + channel.sample_producer.width_mode = (nr43 & BIT_3_MASK) != 0; + channel.sample_producer.divisor_code = nr43 & 0b111; +} - if memory.io_ports.get_ports_cycle_trigger()[NR21_REGISTER_INDEX as usize]{ - channel.sound_length = 64 - (memory.read_unprotected(NR21_REGISTER_ADDRESS) & 0b11_1111) as u16; - } - if memory.io_ports.get_ports_cycle_trigger()[NR22_REGISTER_INDEX as usize]{ - update_volume_envelope(memory.read_unprotected(NR22_REGISTER_ADDRESS), &mut channel.sample_producer.envelop); - - if !is_dac_enabled(channel.sample_producer.envelop.volume, channel.sample_producer.envelop.increase_envelope){ - channel.enabled = false; - } - } - if memory.io_ports.get_ports_cycle_trigger()[NR23_REGISTER_INDEX as usize]{ - //discard lower bit - channel.frequency &= 0xFF00; - channel.frequency |= memory.read_unprotected(NR23_REGISTER_ADDRESS) as u16; - } - if memory.io_ports.get_ports_cycle_trigger()[NR24_REGISTER_INDEX as usize]{ - let nr24 = memory.read_unprotected(NR24_REGISTER_ADDRESS); - //discrad upper bit - channel.frequency <<= 8; - channel.frequency >>= 8; - channel.frequency |= (nr24 as u16 & 0b111) << 8; - let dac_enabled = is_dac_enabled(channel.sample_producer.envelop.volume, channel.sample_producer.envelop.increase_envelope); - update_channel_conrol_register(channel, dac_enabled, nr24, 64, fs); - if nr24 & BIT_7_MASK != 0{ - //volume - channel.sample_producer.envelop.envelop_duration_counter = channel.sample_producer.envelop.number_of_envelope_sweep; - channel.sample_producer.envelop.current_volume = channel.sample_producer.envelop.volume; - } +pub fn set_nr44(channel:&mut Channel, fs:&FrameSequencer, nr44:u8){ + let dac_enabled = is_dac_enabled(channel.sample_producer.envelop.volume, channel.sample_producer.envelop.increase_envelope); + update_channel_conrol_register(channel, dac_enabled, nr44, 64, fs); + if (nr44 & BIT_7_MASK) != 0{ + //On trigger all the LFSR bits are set (lfsr is 15 bit register) + channel.sample_producer.lfsr = 0x7FFF; + + channel.sample_producer.envelop.current_volume = channel.sample_producer.envelop.volume; } } -fn prepare_noise_channel(channel:&mut Channel, memory:&mut GbMmu,fs:&FrameSequencer){ +// fn prepare_noise_channel(channel:&mut Channel, memory:&mut GbMmu,fs:&FrameSequencer){ + +// if memory.io_ports.get_ports_cycle_trigger()[NR41_REGISTER_INDEX as usize]{ +// let length_data = memory.read_unprotected(NR41_REGISTER_ADDRESS) & 0b11_1111; +// channel.sound_length = 64 - length_data as u16 +// } +// if memory.io_ports.get_ports_cycle_trigger()[NR42_REGISTER_INDEX as usize]{ +// update_volume_envelope( memory.read_unprotected(NR42_REGISTER_ADDRESS), &mut channel.sample_producer.envelop); +// if !is_dac_enabled(channel.sample_producer.envelop.volume, channel.sample_producer.envelop.increase_envelope){ +// channel.enabled = false; +// } +// } +// if memory.io_ports.get_ports_cycle_trigger()[NR43_REGISTER_INDEX as usize]{ +// let nr43 = memory.read_unprotected(NR43_REGISTER_ADDRESS); +// channel.sample_producer.bits_to_shift_divisor = (nr43 & 0b1111_0000) >> 4; +// channel.sample_producer.width_mode = (nr43 & BIT_3_MASK) != 0; +// channel.sample_producer.divisor_code = nr43 & 0b111; +// } +// if memory.io_ports.get_ports_cycle_trigger()[NR44_REGISTER_INDEX as usize]{ +// let nr44 = memory.read_unprotected(NR44_REGISTER_ADDRESS); +// let dac_enabled = is_dac_enabled(channel.sample_producer.envelop.volume, channel.sample_producer.envelop.increase_envelope); +// update_channel_conrol_register(channel, dac_enabled, nr44, 64, fs); +// if (nr44 & BIT_7_MASK) != 0{ +// //On trigger all the LFSR bits are set (lfsr is 15 bit register) +// channel.sample_producer.lfsr = 0x7FFF; - if memory.io_ports.get_ports_cycle_trigger()[NR41_REGISTER_INDEX as usize]{ - let length_data = memory.read_unprotected(NR41_REGISTER_ADDRESS) & 0b11_1111; - channel.sound_length = 64 - length_data as u16 - } - if memory.io_ports.get_ports_cycle_trigger()[NR42_REGISTER_INDEX as usize]{ - update_volume_envelope( memory.read_unprotected(NR42_REGISTER_ADDRESS), &mut channel.sample_producer.envelop); - if !is_dac_enabled(channel.sample_producer.envelop.volume, channel.sample_producer.envelop.increase_envelope){ - channel.enabled = false; - } + +// channel.sample_producer.envelop.current_volume = channel.sample_producer.envelop.volume; +// } +// } +// } + + +pub fn set_nr50(apu:&mut GbApu, nr50:u8){ + apu.right_terminal.enabled = nr50 & BIT_3_MASK != 0; + apu.left_terminal.enabled = nr50 & BIT_7_MASK != 0; + + apu.right_terminal.volume = nr50 & 0b111; + apu.left_terminal.volume = (nr50 & 0b111_0000) >> 4; +} + + +pub fn set_nr51(apu:&mut GbApu, nr51:u8){ + for i in 0..NUMBER_OF_CHANNELS{ + apu.right_terminal.channels[i as usize] = nr51 & (1 << i) != 0; } - if memory.io_ports.get_ports_cycle_trigger()[NR43_REGISTER_INDEX as usize]{ - let nr43 = memory.read_unprotected(NR43_REGISTER_ADDRESS); - channel.sample_producer.bits_to_shift_divisor = (nr43 & 0b1111_0000) >> 4; - channel.sample_producer.width_mode = (nr43 & BIT_3_MASK) != 0; - channel.sample_producer.divisor_code = nr43 & 0b111; + for i in 0..NUMBER_OF_CHANNELS{ + apu.left_terminal.channels[i as usize] = nr51 & (0b1_0000 << i) != 0; } - if memory.io_ports.get_ports_cycle_trigger()[NR44_REGISTER_INDEX as usize]{ - let nr44 = memory.read_unprotected(NR44_REGISTER_ADDRESS); - let dac_enabled = is_dac_enabled(channel.sample_producer.envelop.volume, channel.sample_producer.envelop.increase_envelope); - update_channel_conrol_register(channel, dac_enabled, nr44, 64, fs); - if (nr44 & BIT_7_MASK) != 0{ - //On trigger all the LFSR bits are set (lfsr is 15 bit register) - channel.sample_producer.lfsr = 0x7FFF; +} - - channel.sample_producer.envelop.current_volume = channel.sample_producer.envelop.volume; - } +pub fn set_nr52(apu:&mut GbApu, ports:&mut IoPorts, nr52:u8){ + apu.enabled = nr52 & BIT_7_MASK != 0; + + for i in NR10_REGISTER_INDEX..NR52_REGISTER_INDEX{ + ports.write_unprotected(i, 0); } } +pub fn get_nr52(apu:&GbApu, nr52:&mut u8){ + set_bit_u8(nr52, 3, apu.noise_channel.enabled && apu.noise_channel.length_enable && apu.noise_channel.sound_length != 0); + set_bit_u8(nr52, 2, apu.wave_channel.enabled && apu.wave_channel.length_enable && apu.wave_channel.sound_length != 0); + set_bit_u8(nr52, 1, apu.tone_channel.enabled && apu.tone_channel.length_enable && apu.tone_channel.sound_length != 0); + set_bit_u8(nr52, 0, apu.sweep_tone_channel.enabled && apu.sweep_tone_channel.length_enable && apu.sweep_tone_channel.sound_length != 0); +} + fn prepare_control_registers(apu:&mut GbApu, memory:&impl UnprotectedMemory){ let channel_control = memory.read_unprotected(NR50_REGISTER_ADDRESS); apu.right_terminal.enabled = channel_control & BIT_3_MASK != 0; @@ -111,100 +175,196 @@ fn prepare_control_registers(apu:&mut GbApu, memory:&impl Un apu.enabled = master_sound & BIT_7_MASK != 0; } -fn prepare_wave_channel(channel:&mut Channel, memory:&mut GbMmu,fs:&FrameSequencer){ - - if memory.io_ports.get_ports_cycle_trigger()[NR30_REGISTER_INDEX as usize]{ - if (memory.read_unprotected(NR30_REGISTER_ADDRESS) & BIT_7_MASK) == 0{ - channel.enabled = false; - } - } - if memory.io_ports.get_ports_cycle_trigger()[NR31_REGISTER_INDEX as usize]{ - channel.sound_length = 256 - (memory.read_unprotected(NR31_REGISTER_ADDRESS) as u16); +pub fn set_nr30(channel:&mut Channel, value:u8){ + if (value & BIT_7_MASK) == 0{ + channel.enabled = false; } - if memory.io_ports.get_ports_cycle_trigger()[NR32_REGISTER_INDEX as usize]{ - //I want bits 5-6 - let nr32 = memory.read_unprotected(NR32_REGISTER_ADDRESS); - channel.sample_producer.volume = (nr32 & 0b110_0000) >> 5; - } - if memory.io_ports.get_ports_cycle_trigger()[NR33_REGISTER_INDEX as usize]{ - //discard lower 8 bits - channel.frequency &= 0xFF00; - channel.frequency |= memory.read_unprotected(NR33_REGISTER_ADDRESS) as u16; - } - if memory.io_ports.get_ports_cycle_trigger()[NR34_REGISTER_INDEX as usize]{ - let nr34 = memory.read_unprotected(NR34_REGISTER_ADDRESS); +} - //clear the upper 8 bits - channel.frequency &= 0xFF; - channel.frequency |= ((nr34 & 0b111) as u16) << 8; +pub fn set_nr31(channel:&mut Channel, value:u8){ + channel.sound_length = 256 - (value as u16); +} - let dac_enabled = (memory.read_unprotected(NR30_REGISTER_ADDRESS) & BIT_7_MASK) != 0; - update_channel_conrol_register(channel, dac_enabled, nr34, 256, fs); +pub fn set_nr32(channel:&mut Channel, nr32:u8){ + //I want bits 5-6 + channel.sample_producer.volume = (nr32 & 0b110_0000) >> 5; +} - if nr34 & BIT_7_MASK != 0{ - channel.sample_producer.reset_counter(); - } - } +pub fn set_nr33(channel:&mut Channel, nr33:u8){ + //discard lower 8 bits + channel.frequency &= 0xFF00; + channel.frequency |= nr33 as u16; +} - for i in 0..=0xF{ - channel.sample_producer.wave_samples[i] = memory.read_unprotected(0xFF30 + i as u16); +pub fn set_nr34(channel:&mut Channel, fs:&FrameSequencer, nr30:u8, nr34:u8){ + //clear the upper 8 bits + channel.frequency &= 0xFF; + channel.frequency |= ((nr34 & 0b111) as u16) << 8; + + let dac_enabled = (nr30 & BIT_7_MASK) != 0; + update_channel_conrol_register(channel, dac_enabled, nr34, 256, fs); + + if nr34 & BIT_7_MASK != 0{ + channel.sample_producer.reset_counter(); } } -fn prepare_tone_sweep_channel(channel:&mut Channel, memory:&mut GbMmu, fs:&FrameSequencer){ - let nr10 = memory.read_unprotected(NR10_REGISTER_ADDRESS); - let nr11 = memory.read_unprotected(NR11_REGISTER_ADDRESS); - let nr12 = memory.read_unprotected(NR12_REGISTER_ADDRESS); - let nr13 = memory.read_unprotected(NR13_REGISTER_ADDRESS); - let nr14 = memory.read_unprotected(NR14_REGISTER_ADDRESS); +// fn prepare_wave_channel(channel:&mut Channel, memory:&mut GbMmu,fs:&FrameSequencer){ + +// if memory.io_ports.get_ports_cycle_trigger()[NR30_REGISTER_INDEX as usize]{ +// if (memory.read_unprotected(NR30_REGISTER_ADDRESS) & BIT_7_MASK) == 0{ +// channel.enabled = false; +// } +// } +// if memory.io_ports.get_ports_cycle_trigger()[NR31_REGISTER_INDEX as usize]{ +// channel.sound_length = 256 - (memory.read_unprotected(NR31_REGISTER_ADDRESS) as u16); +// } +// if memory.io_ports.get_ports_cycle_trigger()[NR32_REGISTER_INDEX as usize]{ +// //I want bits 5-6 +// let nr32 = memory.read_unprotected(NR32_REGISTER_ADDRESS); +// channel.sample_producer.volume = (nr32 & 0b110_0000) >> 5; +// } +// if memory.io_ports.get_ports_cycle_trigger()[NR33_REGISTER_INDEX as usize]{ +// //discard lower 8 bits +// channel.frequency &= 0xFF00; +// channel.frequency |= memory.read_unprotected(NR33_REGISTER_ADDRESS) as u16; +// } +// if memory.io_ports.get_ports_cycle_trigger()[NR34_REGISTER_INDEX as usize]{ +// let nr34 = memory.read_unprotected(NR34_REGISTER_ADDRESS); + +// //clear the upper 8 bits +// channel.frequency &= 0xFF; +// channel.frequency |= ((nr34 & 0b111) as u16) << 8; + +// let dac_enabled = (memory.read_unprotected(NR30_REGISTER_ADDRESS) & BIT_7_MASK) != 0; +// update_channel_conrol_register(channel, dac_enabled, nr34, 256, fs); + +// if nr34 & BIT_7_MASK != 0{ +// channel.sample_producer.reset_counter(); +// } +// } + +// for i in 0..=0xF{ +// channel.sample_producer.wave_samples[i] = memory.read_unprotected(0xFF30 + i as u16); +// } +// } + +pub fn set_nr10(channel:&mut Channel, value:u8){ + let sweep = channel.sample_producer.sweep.as_mut().unwrap(); + sweep.sweep_decrease = (value & 0b1000) != 0; + sweep.sweep_shift = value & 0b111; + sweep.sweep_period = (value & 0b111_0000) >> 4; +} - if memory.io_ports.get_ports_cycle_trigger()[NR10_REGISTER_INDEX as usize]{ - //sweep - let sweep = channel.sample_producer.sweep.as_mut().unwrap(); - sweep.sweep_decrease = (nr10 & 0b1000) != 0; - sweep.sweep_shift = nr10 & 0b111; - sweep.sweep_period = (nr10 & 0b111_0000) >> 4; - } - if memory.io_ports.get_ports_cycle_trigger()[NR11_REGISTER_INDEX as usize]{ - channel.sample_producer.wave_duty = (nr11 & 0b1100_0000) >> 6; - channel.sound_length = 64 - (nr11 & 0b11_1111) as u16 +pub fn set_nr11(channel:&mut Channel, value:u8){ + channel.sample_producer.wave_duty = (value & 0b1100_0000) >> 6; + channel.sound_length = 64 - (value & 0b11_1111) as u16 +} + pub fn set_nr12(channel:&mut Channel, value:u8){ + update_volume_envelope(value, &mut channel.sample_producer.envelop); + + if !is_dac_enabled(channel.sample_producer.envelop.volume, channel.sample_producer.envelop.increase_envelope){ + channel.enabled = false; } - if memory.io_ports.get_ports_cycle_trigger()[NR12_REGISTER_INDEX as usize]{ - update_volume_envelope(nr12, &mut channel.sample_producer.envelop); + } + + pub fn set_nr13(channel:&mut Channel, value:u8){ + //discard lower bits + channel.frequency &= 0xFF00; + channel.frequency |= value as u16; + } +pub fn set_nr14(channel:&mut Channel, fs:&FrameSequencer, nr14:u8){ + //discard upper bits + channel.frequency &= 0xFF; + channel.frequency |= ((nr14 & 0b111) as u16) << 8; + + let dac_enabled = is_dac_enabled(channel.sample_producer.envelop.volume, channel.sample_producer.envelop.increase_envelope); + update_channel_conrol_register(channel, dac_enabled, nr14, 64, fs); + + if nr14 & BIT_7_MASK != 0{ + //volume + channel.sample_producer.envelop.envelop_duration_counter = channel.sample_producer.envelop.number_of_envelope_sweep; + channel.sample_producer.envelop.current_volume = channel.sample_producer.envelop.volume; - if !is_dac_enabled(channel.sample_producer.envelop.volume, channel.sample_producer.envelop.increase_envelope){ - channel.enabled = false; + //sweep + let sweep = channel.sample_producer.sweep.as_mut().unwrap(); + sweep.channel_trigger(channel.frequency); + if sweep.sweep_shift > 0{ + let freq = sweep.calculate_new_frequency(); + channel.enabled = !FreqSweep::check_overflow(freq); } + } - if memory.io_ports.get_ports_cycle_trigger()[NR13_REGISTER_INDEX as usize]{ - //discard lower bits - channel.frequency &= 0xFF00; - channel.frequency |= nr13 as u16; +} + +pub fn set_nr24(channel:&mut Channel, fs:&FrameSequencer, nr14:u8){ + //discard upper bits + channel.frequency &= 0xFF; + channel.frequency |= ((nr14 & 0b111) as u16) << 8; + + let dac_enabled = is_dac_enabled(channel.sample_producer.envelop.volume, channel.sample_producer.envelop.increase_envelope); + update_channel_conrol_register(channel, dac_enabled, nr14, 64, fs); + + if nr14 & BIT_7_MASK != 0{ + //volume + channel.sample_producer.envelop.envelop_duration_counter = channel.sample_producer.envelop.number_of_envelope_sweep; + channel.sample_producer.envelop.current_volume = channel.sample_producer.envelop.volume; } - if memory.io_ports.get_ports_cycle_trigger()[NR14_REGISTER_INDEX as usize]{ - //discard upper bits - channel.frequency &= 0xFF; - channel.frequency |= ((nr14 & 0b111) as u16) << 8; +} +// fn prepare_tone_sweep_channel(channel:&mut Channel, memory:&mut GbMmu, fs:&FrameSequencer){ +// let nr10 = memory.read_unprotected(NR10_REGISTER_ADDRESS); +// let nr11 = memory.read_unprotected(NR11_REGISTER_ADDRESS); +// let nr12 = memory.read_unprotected(NR12_REGISTER_ADDRESS); +// let nr13 = memory.read_unprotected(NR13_REGISTER_ADDRESS); +// let nr14 = memory.read_unprotected(NR14_REGISTER_ADDRESS); + +// if memory.io_ports.get_ports_cycle_trigger()[NR10_REGISTER_INDEX as usize]{ +// //sweep +// let sweep = channel.sample_producer.sweep.as_mut().unwrap(); +// sweep.sweep_decrease = (nr10 & 0b1000) != 0; +// sweep.sweep_shift = nr10 & 0b111; +// sweep.sweep_period = (nr10 & 0b111_0000) >> 4; +// } +// if memory.io_ports.get_ports_cycle_trigger()[NR11_REGISTER_INDEX as usize]{ +// channel.sample_producer.wave_duty = (nr11 & 0b1100_0000) >> 6; +// channel.sound_length = 64 - (nr11 & 0b11_1111) as u16 +// } +// if memory.io_ports.get_ports_cycle_trigger()[NR12_REGISTER_INDEX as usize]{ +// update_volume_envelope(nr12, &mut channel.sample_producer.envelop); - let dac_enabled = is_dac_enabled(channel.sample_producer.envelop.volume, channel.sample_producer.envelop.increase_envelope); - update_channel_conrol_register(channel, dac_enabled, nr14, 64, fs); +// if !is_dac_enabled(channel.sample_producer.envelop.volume, channel.sample_producer.envelop.increase_envelope){ +// channel.enabled = false; +// } +// } +// if memory.io_ports.get_ports_cycle_trigger()[NR13_REGISTER_INDEX as usize]{ +// //discard lower bits +// channel.frequency &= 0xFF00; +// channel.frequency |= nr13 as u16; +// } +// if memory.io_ports.get_ports_cycle_trigger()[NR14_REGISTER_INDEX as usize]{ +// //discard upper bits +// channel.frequency &= 0xFF; +// channel.frequency |= ((nr14 & 0b111) as u16) << 8; + +// let dac_enabled = is_dac_enabled(channel.sample_producer.envelop.volume, channel.sample_producer.envelop.increase_envelope); +// update_channel_conrol_register(channel, dac_enabled, nr14, 64, fs); - if nr14 & BIT_7_MASK != 0{ - //volume - channel.sample_producer.envelop.envelop_duration_counter = channel.sample_producer.envelop.number_of_envelope_sweep; - channel.sample_producer.envelop.current_volume = channel.sample_producer.envelop.volume; +// if nr14 & BIT_7_MASK != 0{ +// //volume +// channel.sample_producer.envelop.envelop_duration_counter = channel.sample_producer.envelop.number_of_envelope_sweep; +// channel.sample_producer.envelop.current_volume = channel.sample_producer.envelop.volume; - //sweep - let sweep = channel.sample_producer.sweep.as_mut().unwrap(); - sweep.channel_trigger(channel.frequency); - if sweep.sweep_shift > 0{ - let freq = sweep.calculate_new_frequency(); - channel.enabled = !FreqSweep::check_overflow(freq); - } +// //sweep +// let sweep = channel.sample_producer.sweep.as_mut().unwrap(); +// sweep.channel_trigger(channel.frequency); +// if sweep.sweep_shift > 0{ +// let freq = sweep.calculate_new_frequency(); +// channel.enabled = !FreqSweep::check_overflow(freq); +// } - } - } -} +// } +// } +// } fn update_channel_conrol_register(channel:&mut Channel, dac_enabled:bool, control_register:u8, max_sound_length:u16, fs:&FrameSequencer){ diff --git a/lib_gb/src/apu/gb_apu.rs b/lib_gb/src/apu/gb_apu.rs index 12cb1f7a..1cc35c25 100644 --- a/lib_gb/src/apu/gb_apu.rs +++ b/lib_gb/src/apu/gb_apu.rs @@ -9,10 +9,6 @@ use super::{ wave_sample_producer::WaveSampleProducer, sound_utils::NUMBER_OF_CHANNELS }; -use crate::{ - mmu::memory::UnprotectedMemory, - utils::{bit_masks::set_bit_u8, memory_registers::{NR10_REGISTER_ADDRESS, NR52_REGISTER_ADDRESS}} -}; pub const AUDIO_BUFFER_SIZE:usize = 0x400; @@ -50,7 +46,7 @@ impl GbApu{ } } - pub fn cycle(&mut self, memory:&mut impl UnprotectedMemory, m_cycles_passed:u8){ + pub fn cycle(&mut self, m_cycles_passed:u8){ //converting m_cycles to t_cycles let t_cycles = m_cycles_passed * 4; @@ -76,8 +72,6 @@ impl GbApu{ self.push_buffer_if_full(); } - - self.update_registers(memory); } else{ for _ in 0..t_cycles{ @@ -87,11 +81,6 @@ impl GbApu{ self.push_buffer_if_full(); } - //Reseting the apu state - for i in NR10_REGISTER_ADDRESS..NR52_REGISTER_ADDRESS{ - memory.write_unprotected(i, 0); - } - self.tone_channel.reset(); self.sweep_tone_channel.reset(); self.wave_channel.reset(); @@ -142,16 +131,6 @@ impl GbApu{ } } - fn update_registers(&mut self, memory:&mut impl UnprotectedMemory){ - - let mut control_register = memory.read_unprotected(0xFF26); - set_bit_u8(&mut control_register, 3, self.noise_channel.enabled && self.noise_channel.length_enable && self.noise_channel.sound_length != 0); - set_bit_u8(&mut control_register, 2, self.wave_channel.enabled && self.wave_channel.length_enable && self.wave_channel.sound_length != 0); - set_bit_u8(&mut control_register, 1, self.tone_channel.enabled && self.tone_channel.length_enable && self.tone_channel.sound_length != 0); - set_bit_u8(&mut control_register, 0, self.sweep_tone_channel.enabled && self.sweep_tone_channel.length_enable && self.sweep_tone_channel.sound_length != 0); - - memory.write_unprotected(NR52_REGISTER_ADDRESS, control_register); - } pub fn update_sweep_frequency(channel:&mut Channel){ let sweep:&mut FreqSweep = &mut channel.sample_producer.sweep.as_mut().unwrap(); diff --git a/lib_gb/src/apu/mod.rs b/lib_gb/src/apu/mod.rs index 8c418a4a..cf32280d 100644 --- a/lib_gb/src/apu/mod.rs +++ b/lib_gb/src/apu/mod.rs @@ -14,5 +14,5 @@ pub mod noise_sample_producer; mod sound_utils; mod apu_registers_updater; -pub use apu_registers_updater::update_apu_registers; +pub use apu_registers_updater::*; diff --git a/lib_gb/src/machine/gameboy.rs b/lib_gb/src/machine/gameboy.rs index eff555fb..60202e9f 100644 --- a/lib_gb/src/machine/gameboy.rs +++ b/lib_gb/src/machine/gameboy.rs @@ -12,10 +12,9 @@ use log::debug; pub struct GameBoy<'a, JP: JoypadProvider, AD:AudioDevice> { cpu: GbCpu, - mmu: GbMmu::<'a>, - opcode_resolver:OpcodeResolver::>, + mmu: GbMmu::<'a, AD>, + opcode_resolver:OpcodeResolver::>, ppu:GbPpu, - apu:GbApu, interrupts_handler:InterruptsHandler, cycles_counter:u32, joypad_provider: JP, @@ -28,10 +27,9 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ pub fn new_with_bootrom(mbc:&'a mut Box,joypad_provider:JP, audio_device:AD, boot_rom:[u8;BOOT_ROM_SIZE])->GameBoy{ GameBoy{ cpu:GbCpu::default(), - mmu:GbMmu::new_with_bootrom(mbc, boot_rom), + mmu:GbMmu::new_with_bootrom(mbc, boot_rom, GbApu::new(audio_device)), opcode_resolver:OpcodeResolver::default(), ppu:GbPpu::default(), - apu:GbApu::new(audio_device), interrupts_handler: InterruptsHandler::default(), cycles_counter:0, joypad_provider: joypad_provider, @@ -52,10 +50,9 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ GameBoy{ cpu:cpu, - mmu:GbMmu::new(mbc), + mmu:GbMmu::new(mbc, GbApu::new(audio_device)), opcode_resolver:OpcodeResolver::default(), ppu:GbPpu::default(), - apu:GbApu::new(audio_device), interrupts_handler: InterruptsHandler::default(), cycles_counter:0, joypad_provider: joypad_provider, @@ -82,7 +79,7 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ //For the DMA controller mmu_register_updater::update_mmu_registers(&mut self.mmu, &mut self.dma); - timer_register_updater::update_timer_registers(&mut self.timer, &mut self.mmu.io_ports); + timer_register_updater::update_timer_registers(&mut self.timer, &mut self.mmu.io_comps.ports); self.timer.cycle(&mut self.mmu, cpu_cycles_passed); self.dma.cycle(&mut self.mmu, cpu_cycles_passed as u8); @@ -97,7 +94,7 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ let interrupt_cycles = self.interrupts_handler.handle_interrupts(&mut self.cpu, &mut self.ppu, &mut self.mmu); if interrupt_cycles != 0{ self.dma.cycle(&mut self.mmu, interrupt_cycles as u8); - timer_register_updater::update_timer_registers(&mut self.timer, &mut self.mmu.io_ports); + timer_register_updater::update_timer_registers(&mut self.timer, &mut self.mmu.io_comps.ports); self.timer.cycle(&mut self.mmu, interrupt_cycles as u8); mmu_register_updater::update_mmu_registers(&mut self.mmu, &mut self.dma); @@ -112,11 +109,12 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ //APU - apu::update_apu_registers(&mut self.mmu, &mut self.apu); - self.apu.cycle(&mut self.mmu, iter_total_cycles as u8); + // apu::update_apu_registers(&mut self.mmu, &mut self.apu); + // self.apu.cycle(iter_total_cycles as u8); + self.mmu.io_comps.cycle(iter_total_cycles); //clears io ports - self.mmu.io_ports.clear_io_ports_triggers(); + self.mmu.io_comps.ports.clear_io_ports_triggers(); //In case the ppu just turned I want to keep it sync with the actual screen and thats why Im reseting the loop to finish //the frame when the ppu finishes the frame @@ -161,7 +159,7 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ - let opcode_func:OpcodeFuncType = self.opcode_resolver.get_opcode(opcode, &self.mmu, &mut self.cpu.program_counter); + let opcode_func:OpcodeFuncType> = self.opcode_resolver.get_opcode(opcode, &self.mmu, &mut self.cpu.program_counter); match opcode_func{ OpcodeFuncType::OpcodeFunc(func)=>func(&mut self.cpu), OpcodeFuncType::MemoryOpcodeFunc(func)=>func(&mut self.cpu, &mut self.mmu), diff --git a/lib_gb/src/mmu/gb_mmu.rs b/lib_gb/src/mmu/gb_mmu.rs index 1993a67f..ec9b848c 100644 --- a/lib_gb/src/mmu/gb_mmu.rs +++ b/lib_gb/src/mmu/gb_mmu.rs @@ -1,9 +1,9 @@ -use super::memory::*; +use super::{io_comps::IoComps, memory::*}; use super::ram::Ram; use super::vram::VRam; use super::io_ports::IoPorts; use super::access_bus::AccessBus; -use crate::utils::memory_registers::BOOT_REGISTER_ADDRESS; +use crate::{apu::{audio_device::AudioDevice, gb_apu::GbApu}, utils::memory_registers::BOOT_REGISTER_ADDRESS}; use super::carts::mbc::Mbc; use crate::ppu::ppu_state::PpuState; use std::boxed::Box; @@ -14,11 +14,11 @@ const SPRITE_ATTRIBUTE_TABLE_SIZE:usize = 0xA0; const BAD_READ_VALUE:u8 = 0xFF; -pub struct GbMmu<'a>{ +pub struct GbMmu<'a, D:AudioDevice>{ pub ram: Ram, pub vram: VRam, pub finished_boot:bool, - pub io_ports: IoPorts, + pub io_comps: IoComps, boot_rom:[u8;BOOT_ROM_SIZE], mbc: &'a mut Box, sprite_attribute_table:[u8;SPRITE_ATTRIBUTE_TABLE_SIZE], @@ -30,11 +30,11 @@ pub struct GbMmu<'a>{ //DMA only locks the used bus. there 2 possible used buses: extrnal (wram, rom, sram) and video (vram) -impl<'a> Memory for GbMmu<'a>{ +impl<'a, D:AudioDevice> Memory for GbMmu<'a, D>{ fn read(&self, address:u16)->u8{ if let Some (bus) = &self.dma_state{ return match address{ - 0xFF00..=0xFF7F => self.io_ports.read(address - 0xFF00), + 0xFF00..=0xFF7F => self.io_comps.read(address - 0xFF00), 0xFEA0..=0xFEFF | 0xFF80..=0xFFFE | 0xFFFF=>self.read_unprotected(address), 0x8000..=0x9FFF => if let AccessBus::External = bus {self.read_unprotected(address)} else{Self::bad_dma_read(address)}, 0..=0x7FFF | 0xA000..=0xFDFF => if let AccessBus::Video = bus {self.read_unprotected(address)} else{Self::bad_dma_read(address)}, @@ -60,7 +60,7 @@ impl<'a> Memory for GbMmu<'a>{ return BAD_READ_VALUE; } }, - 0xFF00..=0xFF7F => self.io_ports.read(address - 0xFF00), + 0xFF00..=0xFF7F => self.io_comps.read(address - 0xFF00), _=>self.read_unprotected(address) }; } @@ -68,7 +68,7 @@ impl<'a> Memory for GbMmu<'a>{ fn write(&mut self, address:u16, value:u8){ if let Some(bus) = &self.dma_state{ match address{ - 0xFF00..=0xFF7F => self.io_ports.write(address- 0xFF00, value), + 0xFF00..=0xFF7F => self.io_comps.write(address- 0xFF00, value), 0xFF80..=0xFFFE | 0xFFFF=>self.write_unprotected(address, value), 0x8000..=0x9FFF => if let AccessBus::External = bus {self.write_unprotected(address, value)} else{Self::bad_dma_write(address)}, 0..=0x7FFF | 0xA000..=0xFDFF => if let AccessBus::Video = bus {self.write_unprotected(address, value)} else{Self::bad_dma_write(address)}, @@ -93,14 +93,14 @@ impl<'a> Memory for GbMmu<'a>{ log::warn!("bad oam write") } }, - 0xFF00..=0xFF7F=>self.io_ports.write(address - 0xFF00, value), + 0xFF00..=0xFF7F=>self.io_comps.write(address - 0xFF00, value), _=>self.write_unprotected(address, value) } } } } -impl<'a> UnprotectedMemory for GbMmu<'a>{ +impl<'a, D:AudioDevice> UnprotectedMemory for GbMmu<'a, D>{ fn read_unprotected(&self, address:u16) ->u8 { return match address{ 0x0..=0xFF=>{ @@ -119,7 +119,7 @@ impl<'a> UnprotectedMemory for GbMmu<'a>{ 0xE000..=0xFDFF=>self.ram.read_bank0(address - 0xE000), 0xFE00..=0xFE9F=>self.sprite_attribute_table[(address-0xFE00) as usize], 0xFEA0..=0xFEFF=>0x0, - 0xFF00..=0xFF7F=>self.io_ports.read_unprotected(address - 0xFF00), + 0xFF00..=0xFF7F=>self.io_comps.ports.read_unprotected(address - 0xFF00), 0xFF80..=0xFFFE=>self.hram[(address-0xFF80) as usize], 0xFFFF=>self.interupt_enable_register }; @@ -135,18 +135,18 @@ impl<'a> UnprotectedMemory for GbMmu<'a>{ 0xD000..=0xDFFF=>self.ram.write_current_bank(address-0xD000,value), 0xFE00..=0xFE9F=>self.sprite_attribute_table[(address-0xFE00) as usize] = value, 0xFEA0..=0xFEFF=>{}, - 0xFF00..=0xFF7F=>self.io_ports.write_unprotected(address - 0xFF00, value), + 0xFF00..=0xFF7F=>self.io_comps.ports.write_unprotected(address - 0xFF00, value), 0xFF80..=0xFFFE=>self.hram[(address-0xFF80) as usize] = value, 0xFFFF=>self.interupt_enable_register = value } } } -impl<'a> GbMmu<'a>{ - pub fn new_with_bootrom(mbc:&'a mut Box, boot_rom:[u8;BOOT_ROM_SIZE])->Self{ +impl<'a, D:AudioDevice> GbMmu<'a, D>{ + pub fn new_with_bootrom(mbc:&'a mut Box, boot_rom:[u8;BOOT_ROM_SIZE], apu:GbApu)->Self{ GbMmu{ ram:Ram::default(), - io_ports:IoPorts::default(), + io_comps:IoComps{apu, ports:IoPorts::default()}, mbc:mbc, vram:VRam::default(), sprite_attribute_table:[0;SPRITE_ATTRIBUTE_TABLE_SIZE], @@ -159,10 +159,10 @@ impl<'a> GbMmu<'a>{ } } - pub fn new(mbc:&'a mut Box)->Self{ + pub fn new(mbc:&'a mut Box, apu:GbApu)->Self{ let mut mmu = GbMmu{ ram:Ram::default(), - io_ports:IoPorts::default(), + io_comps:IoComps{apu, ports:IoPorts::default()}, mbc:mbc, vram:VRam::default(), sprite_attribute_table:[0;SPRITE_ATTRIBUTE_TABLE_SIZE], @@ -175,7 +175,7 @@ impl<'a> GbMmu<'a>{ }; //Setting the bootrom register to be set (the boot sequence has over) - mmu.io_ports.write_unprotected(BOOT_REGISTER_ADDRESS - 0xFF00, 1); + mmu.io_comps.ports.write_unprotected(BOOT_REGISTER_ADDRESS - 0xFF00, 1); mmu } diff --git a/lib_gb/src/mmu/io_comps.rs b/lib_gb/src/mmu/io_comps.rs new file mode 100644 index 00000000..f28a4e69 --- /dev/null +++ b/lib_gb/src/mmu/io_comps.rs @@ -0,0 +1,58 @@ +use crate::{apu::{audio_device::AudioDevice, gb_apu::GbApu, set_nr11, set_nr12, set_nr13, volume_envelop::VolumeEnvlope}, utils::{bit_masks::BIT_3_MASK, memory_registers::*}}; +use crate::ppu::gb_ppu::GbPpu; +use crate::apu::*; +use crate::timer::gb_timer::GbTimer; +use super::{io_ports::IoPorts, memory::{Memory, UnprotectedMemory}, oam_dma_transferer::OamDmaTransferer}; +use super::io_ports::*; + +pub struct IoComps{ + pub apu: GbApu, + pub ports:IoPorts, +} + +impl Memory for IoComps{ + fn read(&self, address:u16)->u8 { + let mut value = self.ports.read(address); + match address { + NR52_REGISTER_INDEX=>get_nr52(&self.apu, &mut value), + _=>{} + } + + value + } + + fn write(&mut self, address:u16, value:u8) { + match address{ + NR10_REGISTER_INDEX=> set_nr10(&mut self.apu.sweep_tone_channel, value), + NR11_REGISTER_INDEX=> set_nr11(&mut self.apu.sweep_tone_channel, value), + NR12_REGISTER_INDEX=> set_nr12(&mut self.apu.sweep_tone_channel, value), + NR13_REGISTER_INDEX=> set_nr13(&mut self.apu.sweep_tone_channel, value), + NR14_REGISTER_INDEX=> set_nr14(&mut self.apu.sweep_tone_channel, &self.apu.frame_sequencer, value), + NR21_REGISTER_INDEX=> set_nr11(&mut self.apu.tone_channel, value), + NR22_REGISTER_INDEX=> set_nr12(&mut self.apu.tone_channel, value), + NR23_REGISTER_INDEX=> set_nr13(&mut self.apu.tone_channel, value), + NR24_REGISTER_INDEX=> set_nr24(&mut self.apu.tone_channel, &self.apu.frame_sequencer, value), + NR30_REGISTER_INDEX=> set_nr30(&mut self.apu.wave_channel, value), + NR31_REGISTER_INDEX=> set_nr31(&mut self.apu.wave_channel, value), + NR32_REGISTER_INDEX=> set_nr32(&mut self.apu.wave_channel, value), + NR33_REGISTER_INDEX=> set_nr33(&mut self.apu.wave_channel, value), + NR34_REGISTER_INDEX=> set_nr34(&mut self.apu.wave_channel, &self.apu.frame_sequencer, self.ports.read_unprotected(NR30_REGISTER_INDEX),value), + NR41_REGISTER_INDEX=> set_nr41(&mut self.apu.noise_channel, value), + NR42_REGISTER_INDEX=> set_nr42(&mut self.apu.noise_channel, value), + NR43_REGISTER_INDEX=> set_nr43(&mut self.apu.noise_channel, value), + NR44_REGISTER_INDEX=> set_nr44(&mut self.apu.noise_channel, &self.apu.frame_sequencer, value), + NR50_REGISTER_INDEX=> set_nr50(&mut self.apu, value), + NR51_REGISTER_INDEX=> set_nr51(&mut self.apu, value), + NR52_REGISTER_INDEX=> set_nr52(&mut self.apu, &mut self.ports,value), + _=>{} + } + + self.ports.write(address, value); + } +} + +impl IoComps{ + pub fn cycle(&mut self, cycles:u32){ + self.apu.cycle(cycles as u8); + } +} \ No newline at end of file diff --git a/lib_gb/src/mmu/io_ports.rs b/lib_gb/src/mmu/io_ports.rs index 10c799e1..430fe8e0 100644 --- a/lib_gb/src/mmu/io_ports.rs +++ b/lib_gb/src/mmu/io_ports.rs @@ -40,6 +40,8 @@ pub_io_port_index!(NR41_REGISTER_INDEX, NR41_REGISTER_ADDRESS); pub_io_port_index!(NR42_REGISTER_INDEX, NR42_REGISTER_ADDRESS); pub_io_port_index!(NR43_REGISTER_INDEX, NR43_REGISTER_ADDRESS); pub_io_port_index!(NR44_REGISTER_INDEX, NR44_REGISTER_ADDRESS); +pub_io_port_index!(NR50_REGISTER_INDEX, NR50_REGISTER_ADDRESS); +pub_io_port_index!(NR51_REGISTER_INDEX, NR51_REGISTER_ADDRESS); pub_io_port_index!(NR52_REGISTER_INDEX, NR52_REGISTER_ADDRESS); pub struct IoPorts{ diff --git a/lib_gb/src/mmu/mmu_register_updater.rs b/lib_gb/src/mmu/mmu_register_updater.rs index 69cbe119..1acb1b66 100644 --- a/lib_gb/src/mmu/mmu_register_updater.rs +++ b/lib_gb/src/mmu/mmu_register_updater.rs @@ -1,14 +1,14 @@ -use crate::{ppu::ppu_state::PpuState, utils::memory_registers::*}; +use crate::{apu::audio_device::AudioDevice, ppu::ppu_state::PpuState, utils::memory_registers::*}; use super::{access_bus::AccessBus, gb_mmu::GbMmu, io_ports::IO_PORTS_MEMORY_OFFSET, memory::UnprotectedMemory, oam_dma_transferer::OamDmaTransferer}; const DMA_REGISTER_INDEX:usize = (DMA_REGISTER_ADDRESS - IO_PORTS_MEMORY_OFFSET) as usize; -pub fn update_mmu_registers(memory: &mut GbMmu,dma:&mut OamDmaTransferer){ +pub fn update_mmu_registers(memory: &mut GbMmu,dma:&mut OamDmaTransferer){ handle_ppu_state(memory, memory.read_unprotected(STAT_REGISTER_ADDRESS)); handle_wram_register(memory, memory.read_unprotected(SVBK_REGISTER_ADDRESS)); handle_bootrom_register(memory, memory.read_unprotected(BOOT_REGISTER_ADDRESS)); - let ports = memory.io_ports.get_ports_cycle_trigger(); + let ports = memory.io_comps.ports.get_ports_cycle_trigger(); if ports[DMA_REGISTER_INDEX]{ ports[DMA_REGISTER_INDEX] = false; handle_dma_transfer_register(memory.read_unprotected(DMA_REGISTER_ADDRESS), dma, memory); @@ -18,20 +18,20 @@ pub fn update_mmu_registers(memory: &mut GbMmu,dma:&mut OamDmaTransferer){ } } -fn handle_ppu_state(memory:&mut GbMmu, stat:u8){ +fn handle_ppu_state(memory:&mut GbMmu, stat:u8){ memory.ppu_state = PpuState::from_u8(stat & 0b0000_0011); } -fn handle_wram_register(memory: &mut GbMmu, register:u8){ +fn handle_wram_register(memory: &mut GbMmu, register:u8){ let bank:u8 = register & 0b00000111; memory.ram.set_bank(bank); } -fn handle_bootrom_register(memory: &mut GbMmu, register:u8){ +fn handle_bootrom_register(memory: &mut GbMmu, register:u8){ memory.finished_boot = register == 1; } -fn handle_dma_transfer_register(register:u8, dma: &mut OamDmaTransferer, mmu:&mut GbMmu){ +fn handle_dma_transfer_register(register:u8, dma: &mut OamDmaTransferer, mmu:&mut GbMmu){ dma.soure_address = (register as u16) << 8; dma.enable = match register{ 0..=0x7F=>Some(AccessBus::External), diff --git a/lib_gb/src/mmu/mod.rs b/lib_gb/src/mmu/mod.rs index 890e7c99..77a98784 100644 --- a/lib_gb/src/mmu/mod.rs +++ b/lib_gb/src/mmu/mod.rs @@ -7,4 +7,5 @@ pub mod io_ports; pub mod carts; pub mod access_bus; pub mod mmu_register_updater; -pub mod oam_dma_transferer; \ No newline at end of file +pub mod oam_dma_transferer; +pub mod io_comps; \ No newline at end of file diff --git a/lib_gb/src/ppu/ppu_register_updater.rs b/lib_gb/src/ppu/ppu_register_updater.rs index 6bfda866..18735f8e 100644 --- a/lib_gb/src/ppu/ppu_register_updater.rs +++ b/lib_gb/src/ppu/ppu_register_updater.rs @@ -1,4 +1,4 @@ -use crate::mmu::{gb_mmu::GbMmu, io_ports::IO_PORTS_MEMORY_OFFSET, memory::UnprotectedMemory}; +use crate::{apu::audio_device::AudioDevice, mmu::{gb_mmu::GbMmu, io_ports::IO_PORTS_MEMORY_OFFSET, memory::UnprotectedMemory}}; use crate::utils::{memory_registers::*, bit_masks::*}; use super::{ gb_ppu::GbPpu, color::*, colors::*}; @@ -14,29 +14,29 @@ const OBP1_REGISTER_INDEX:usize = (OBP1_REGISTER_ADDRESS - IO_PORTS_MEMORY_OFFSE const WX_REGISTER_INDEX:usize = (WX_REGISTER_ADDRESS - IO_PORTS_MEMORY_OFFSET) as usize; const WY_REGISTER_INDEX:usize = (WY_REGISTER_ADDRESS - IO_PORTS_MEMORY_OFFSET) as usize; -pub fn update_ppu_regsiters(memory:&mut GbMmu, ppu: &mut GbPpu){ - if memory.io_ports.get_ports_cycle_trigger()[LCDC_REGISTER_INDEX] { +pub fn update_ppu_regsiters(memory:&mut GbMmu, ppu: &mut GbPpu){ + if memory.io_comps.ports.get_ports_cycle_trigger()[LCDC_REGISTER_INDEX] { handle_lcdcontrol_register(memory.read_unprotected(LCDC_REGISTER_ADDRESS), ppu); } - if memory.io_ports.get_ports_cycle_trigger()[STAT_REGISTER_INDEX]{ + if memory.io_comps.ports.get_ports_cycle_trigger()[STAT_REGISTER_INDEX]{ update_stat_register(memory.read_unprotected(STAT_REGISTER_ADDRESS), ppu); } - if memory.io_ports.get_ports_cycle_trigger()[SCX_REGISTER_INDEX] || memory.io_ports.get_ports_cycle_trigger()[SCY_REGISTER_INDEX]{ + if memory.io_comps.ports.get_ports_cycle_trigger()[SCX_REGISTER_INDEX] || memory.io_comps.ports.get_ports_cycle_trigger()[SCY_REGISTER_INDEX]{ handle_scroll_registers(memory.read_unprotected(SCX_REGISTER_ADDRESS), memory.read_unprotected(SCY_REGISTER_ADDRESS), ppu); } - if memory.io_ports.get_ports_cycle_trigger()[BGP_REGISTER_INDEX]{ + if memory.io_comps.ports.get_ports_cycle_trigger()[BGP_REGISTER_INDEX]{ handle_bg_pallet_register(memory.read_unprotected(BGP_REGISTER_ADDRESS), &mut ppu.bg_color_mapping); } - if memory.io_ports.get_ports_cycle_trigger()[OBP0_REGISTER_INDEX]{ + if memory.io_comps.ports.get_ports_cycle_trigger()[OBP0_REGISTER_INDEX]{ handle_obp_pallet_register(memory.read_unprotected(OBP0_REGISTER_ADDRESS), &mut ppu.obj_color_mapping0); } - if memory.io_ports.get_ports_cycle_trigger()[OBP1_REGISTER_INDEX]{ + if memory.io_comps.ports.get_ports_cycle_trigger()[OBP1_REGISTER_INDEX]{ handle_obp_pallet_register(memory.read_unprotected(OBP1_REGISTER_ADDRESS), &mut ppu.obj_color_mapping1); } - if memory.io_ports.get_ports_cycle_trigger()[WY_REGISTER_INDEX]{ + if memory.io_comps.ports.get_ports_cycle_trigger()[WY_REGISTER_INDEX]{ handle_wy_register(memory.read_unprotected(WY_REGISTER_ADDRESS), ppu); } - if memory.io_ports.get_ports_cycle_trigger()[WX_REGISTER_INDEX]{ + if memory.io_comps.ports.get_ports_cycle_trigger()[WX_REGISTER_INDEX]{ handle_wx_register(memory.read_unprotected(WX_REGISTER_ADDRESS), ppu); } } From b8febdeffcf11c50b6627fff5153e4d1258d732e Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 8 May 2021 22:21:26 +0300 Subject: [PATCH 008/136] Add the timer to the io comps --- lib_gb/src/machine/gameboy.rs | 20 +++--------- lib_gb/src/mmu/gb_mmu.rs | 6 ++-- lib_gb/src/mmu/io_comps.rs | 16 ++++++++-- lib_gb/src/mmu/io_ports.rs | 4 ++- lib_gb/src/timer/gb_timer.rs | 37 ++++++++++++---------- lib_gb/src/timer/timer_register_updater.rs | 22 +++++++++++++ 6 files changed, 67 insertions(+), 38 deletions(-) diff --git a/lib_gb/src/machine/gameboy.rs b/lib_gb/src/machine/gameboy.rs index 60202e9f..e30c3e0b 100644 --- a/lib_gb/src/machine/gameboy.rs +++ b/lib_gb/src/machine/gameboy.rs @@ -1,10 +1,4 @@ -use crate::{ - apu::{self, audio_device::AudioDevice, gb_apu::GbApu}, - cpu::{gb_cpu::GbCpu, opcodes::opcode_resolver::*}, - keypad::{joypad::Joypad, joypad_provider::JoypadProvider, joypad_register_updater}, - mmu::{carts::mbc::Mbc, gb_mmu::{GbMmu, BOOT_ROM_SIZE}, memory::Memory, mmu_register_updater, oam_dma_transferer::OamDmaTransferer}, - ppu::{gb_ppu::{CYCLES_PER_FRAME, GbPpu, SCREEN_HEIGHT, SCREEN_WIDTH}, ppu_register_updater}, timer::{gb_timer::GbTimer, timer_register_updater} -}; +use crate::{apu::{self, audio_device::AudioDevice, gb_apu::GbApu}, cpu::{gb_cpu::GbCpu, opcodes::opcode_resolver::*}, keypad::{joypad::Joypad, joypad_provider::JoypadProvider, joypad_register_updater}, mmu::{carts::mbc::Mbc, gb_mmu::{GbMmu, BOOT_ROM_SIZE}, memory::{Memory, UnprotectedMemory}, mmu_register_updater, oam_dma_transferer::OamDmaTransferer}, ppu::{gb_ppu::{CYCLES_PER_FRAME, GbPpu, SCREEN_HEIGHT, SCREEN_WIDTH}, ppu_register_updater}, timer::{gb_timer::GbTimer, timer_register_updater}, utils::memory_registers::IF_REGISTER_ADDRESS}; use super::interrupts_handler::InterruptsHandler; use std::boxed::Box; use log::debug; @@ -18,7 +12,6 @@ pub struct GameBoy<'a, JP: JoypadProvider, AD:AudioDevice> { interrupts_handler:InterruptsHandler, cycles_counter:u32, joypad_provider: JP, - timer: GbTimer, dma:OamDmaTransferer } @@ -33,7 +26,6 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ interrupts_handler: InterruptsHandler::default(), cycles_counter:0, joypad_provider: joypad_provider, - timer:GbTimer::default(), dma: OamDmaTransferer::default() } } @@ -56,7 +48,6 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ interrupts_handler: InterruptsHandler::default(), cycles_counter:0, joypad_provider: joypad_provider, - timer: GbTimer::default(), dma: OamDmaTransferer::default() } } @@ -79,10 +70,11 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ //For the DMA controller mmu_register_updater::update_mmu_registers(&mut self.mmu, &mut self.dma); - timer_register_updater::update_timer_registers(&mut self.timer, &mut self.mmu.io_comps.ports); - self.timer.cycle(&mut self.mmu, cpu_cycles_passed); self.dma.cycle(&mut self.mmu, cpu_cycles_passed as u8); + + self.mmu.io_comps.cycle(cpu_cycles_passed as u32); + //For the PPU mmu_register_updater::update_mmu_registers(&mut self.mmu, &mut self.dma); @@ -94,8 +86,6 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ let interrupt_cycles = self.interrupts_handler.handle_interrupts(&mut self.cpu, &mut self.ppu, &mut self.mmu); if interrupt_cycles != 0{ self.dma.cycle(&mut self.mmu, interrupt_cycles as u8); - timer_register_updater::update_timer_registers(&mut self.timer, &mut self.mmu.io_comps.ports); - self.timer.cycle(&mut self.mmu, interrupt_cycles as u8); mmu_register_updater::update_mmu_registers(&mut self.mmu, &mut self.dma); //PPU @@ -104,6 +94,7 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ mmu_register_updater::update_mmu_registers(&mut self.mmu, &mut self.dma); } + self.mmu.io_comps.cycle(interrupt_cycles as u32); let iter_total_cycles= cpu_cycles_passed as u32 + interrupt_cycles as u32; @@ -111,7 +102,6 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ //APU // apu::update_apu_registers(&mut self.mmu, &mut self.apu); // self.apu.cycle(iter_total_cycles as u8); - self.mmu.io_comps.cycle(iter_total_cycles); //clears io ports self.mmu.io_comps.ports.clear_io_ports_triggers(); diff --git a/lib_gb/src/mmu/gb_mmu.rs b/lib_gb/src/mmu/gb_mmu.rs index ec9b848c..a923b640 100644 --- a/lib_gb/src/mmu/gb_mmu.rs +++ b/lib_gb/src/mmu/gb_mmu.rs @@ -3,7 +3,7 @@ use super::ram::Ram; use super::vram::VRam; use super::io_ports::IoPorts; use super::access_bus::AccessBus; -use crate::{apu::{audio_device::AudioDevice, gb_apu::GbApu}, utils::memory_registers::BOOT_REGISTER_ADDRESS}; +use crate::{apu::{audio_device::AudioDevice, gb_apu::GbApu}, timer::gb_timer::GbTimer, utils::memory_registers::BOOT_REGISTER_ADDRESS}; use super::carts::mbc::Mbc; use crate::ppu::ppu_state::PpuState; use std::boxed::Box; @@ -146,7 +146,7 @@ impl<'a, D:AudioDevice> GbMmu<'a, D>{ pub fn new_with_bootrom(mbc:&'a mut Box, boot_rom:[u8;BOOT_ROM_SIZE], apu:GbApu)->Self{ GbMmu{ ram:Ram::default(), - io_comps:IoComps{apu, ports:IoPorts::default()}, + io_comps:IoComps{apu, ports:IoPorts::default(), timer:GbTimer::default()}, mbc:mbc, vram:VRam::default(), sprite_attribute_table:[0;SPRITE_ATTRIBUTE_TABLE_SIZE], @@ -162,7 +162,7 @@ impl<'a, D:AudioDevice> GbMmu<'a, D>{ pub fn new(mbc:&'a mut Box, apu:GbApu)->Self{ let mut mmu = GbMmu{ ram:Ram::default(), - io_comps:IoComps{apu, ports:IoPorts::default()}, + io_comps:IoComps{apu, ports:IoPorts::default(), timer:GbTimer::default()}, mbc:mbc, vram:VRam::default(), sprite_attribute_table:[0;SPRITE_ATTRIBUTE_TABLE_SIZE], diff --git a/lib_gb/src/mmu/io_comps.rs b/lib_gb/src/mmu/io_comps.rs index f28a4e69..515c2837 100644 --- a/lib_gb/src/mmu/io_comps.rs +++ b/lib_gb/src/mmu/io_comps.rs @@ -1,4 +1,4 @@ -use crate::{apu::{audio_device::AudioDevice, gb_apu::GbApu, set_nr11, set_nr12, set_nr13, volume_envelop::VolumeEnvlope}, utils::{bit_masks::BIT_3_MASK, memory_registers::*}}; +use crate::{apu::{audio_device::AudioDevice, gb_apu::GbApu, set_nr11, set_nr12, set_nr13, volume_envelop::VolumeEnvlope}, timer::timer_register_updater::*, utils::{bit_masks::BIT_3_MASK, memory_registers::*}}; use crate::ppu::gb_ppu::GbPpu; use crate::apu::*; use crate::timer::gb_timer::GbTimer; @@ -7,6 +7,7 @@ use super::io_ports::*; pub struct IoComps{ pub apu: GbApu, + pub timer: GbTimer, pub ports:IoPorts, } @@ -14,7 +15,9 @@ impl Memory for IoComps{ fn read(&self, address:u16)->u8 { let mut value = self.ports.read(address); match address { - NR52_REGISTER_INDEX=>get_nr52(&self.apu, &mut value), + DIV_REGISTER_INDEX=> value = get_div(&self.timer), + TIMA_REGISTER_INDEX=> value = self.timer.tima_register, + NR52_REGISTER_INDEX=> get_nr52(&self.apu, &mut value), _=>{} } @@ -23,6 +26,12 @@ impl Memory for IoComps{ fn write(&mut self, address:u16, value:u8) { match address{ + //timer + DIV_REGISTER_INDEX=> reset_div(&mut self.timer), + TIMA_REGISTER_INDEX=> set_tima(&mut self.timer, value), + TMA_REGISTER_INDEX=> set_tma(&mut self.timer, value), + TAC_REGISTER_INDEX=> set_tac(&mut self.timer, value), + //APU NR10_REGISTER_INDEX=> set_nr10(&mut self.apu.sweep_tone_channel, value), NR11_REGISTER_INDEX=> set_nr11(&mut self.apu.sweep_tone_channel, value), NR12_REGISTER_INDEX=> set_nr12(&mut self.apu.sweep_tone_channel, value), @@ -53,6 +62,9 @@ impl Memory for IoComps{ impl IoComps{ pub fn cycle(&mut self, cycles:u32){ + let mut if_register = self.ports.read_unprotected(IF_REGISTER_ADDRESS - 0xFF00); + self.timer.cycle(&mut if_register, cycles as u8); self.apu.cycle(cycles as u8); + self.ports.write_unprotected(IF_REGISTER_ADDRESS - 0xFF00, if_register); } } \ No newline at end of file diff --git a/lib_gb/src/mmu/io_ports.rs b/lib_gb/src/mmu/io_ports.rs index 430fe8e0..85827751 100644 --- a/lib_gb/src/mmu/io_ports.rs +++ b/lib_gb/src/mmu/io_ports.rs @@ -18,8 +18,10 @@ macro_rules! pub_io_port_index{ } pub_io_port_index!(DIV_REGISTER_INDEX, DIV_REGISTER_ADDRESS); +pub_io_port_index!(TAC_REGISTER_INDEX, TAC_REGISTER_ADDRESS); +pub_io_port_index!(TIMA_REGISTER_INDEX, TIMA_REGISTER_ADDRESS); +pub_io_port_index!(TMA_REGISTER_INDEX, TMA_REGISTER_ADDRESS); -io_port_index!(TAC_REGISTER_INDEX, TAC_REGISTER_ADDRESS); io_port_index!(STAT_REGISTER_INDEX, STAT_REGISTER_ADDRESS); io_port_index!(JOYP_REGISTER_INDEX, JOYP_REGISTER_ADDRESS); pub_io_port_index!(NR10_REGISTER_INDEX, NR10_REGISTER_ADDRESS); diff --git a/lib_gb/src/timer/gb_timer.rs b/lib_gb/src/timer/gb_timer.rs index 9761cc7e..02560d61 100644 --- a/lib_gb/src/timer/gb_timer.rs +++ b/lib_gb/src/timer/gb_timer.rs @@ -1,9 +1,13 @@ -use crate::{mmu::memory::UnprotectedMemory, utils::{bit_masks::*, memory_registers::*}}; +use crate::utils::{bit_masks::*, memory_registers::*}; pub struct GbTimer{ pub system_counter:u16, pub tima_overflow:bool, + pub tima_register:u8, + pub tma_register:u8, + pub tac_tegister:u8, + last_and_result:bool, reload_cooldown_counter:u8 } @@ -12,6 +16,9 @@ impl Default for GbTimer{ fn default() -> Self { GbTimer{ system_counter:0, + tima_register:0, + tma_register:0, + tac_tegister:0, last_and_result: false, reload_cooldown_counter: 0, tima_overflow:false @@ -20,9 +27,9 @@ impl Default for GbTimer{ } impl GbTimer{ - pub fn cycle(&mut self, memory:&mut impl UnprotectedMemory, m_cycles:u8){ - let mut tima_register = memory.read_unprotected(TIMA_REGISTER_ADDRESS); - let (timer_interval, timer_enable) = Self::get_timer_controller_data(memory); + pub fn cycle(&mut self, if_register:&mut u8, m_cycles:u8){ + // let mut tima_register = memory.read_unprotected(TIMA_REGISTER_ADDRESS); + let (timer_interval, timer_enable) = self.get_timer_controller_data(); for _ in 0..m_cycles * 4{ if timer_enable && self.tima_overflow{ @@ -30,10 +37,8 @@ impl GbTimer{ if self.reload_cooldown_counter >= 4{ self.reload_cooldown_counter = 0; - let mut if_register = memory.read_unprotected(IF_REGISTER_ADDRESS); - if_register |= BIT_2_MASK; - memory.write_unprotected(IF_REGISTER_ADDRESS, if_register); - tima_register = memory.read_unprotected(TMA_REGISTER_ADDRESS); + *if_register |= BIT_2_MASK; + self.tima_register = self.tma_register; self.tima_overflow = false; } } @@ -53,23 +58,21 @@ impl GbTimer{ } let current_and_result = bit_value && timer_enable; if !current_and_result && self.last_and_result{ - let(value, overflow) = tima_register.overflowing_add(1); - tima_register = value; + let(value, overflow) = self.tima_register.overflowing_add(1); + self.tima_register = value; self.tima_overflow = overflow; self.reload_cooldown_counter = 0; } self.last_and_result = current_and_result; } - memory.write_unprotected(DIV_REGISTER_ADDRESS, (self.system_counter >> 8) as u8); - memory.write_unprotected(TIMA_REGISTER_ADDRESS, tima_register); - + // memory.write_unprotected(DIV_REGISTER_ADDRESS, (self.system_counter >> 8) as u8); + // memory.write_unprotected(TIMA_REGISTER_ADDRESS, tima_register); } - fn get_timer_controller_data(memory: &mut impl UnprotectedMemory)->(u8, bool){ - let timer_controller = memory.read_unprotected(TAC_REGISTER_ADDRESS); - let timer_enable:bool = timer_controller & BIT_2_MASK != 0; + fn get_timer_controller_data(&self)->(u8, bool){ + let timer_enable:bool = self.tac_tegister & BIT_2_MASK != 0; - return (timer_controller & 0b11, timer_enable); + return (self.tac_tegister & 0b11, timer_enable); } } \ No newline at end of file diff --git a/lib_gb/src/timer/timer_register_updater.rs b/lib_gb/src/timer/timer_register_updater.rs index bf101a0a..38c1ade8 100644 --- a/lib_gb/src/timer/timer_register_updater.rs +++ b/lib_gb/src/timer/timer_register_updater.rs @@ -13,4 +13,26 @@ pub fn update_timer_registers(timer:&mut GbTimer, memory:&mut IoPorts){ timer.tima_overflow = false; ports[TIMA_REGISTER_INDEX] = false; } +} + +pub fn get_div(timer: &GbTimer)->u8{ + (timer.system_counter >> 8) as u8 +} + +pub fn set_tima(timer: &mut GbTimer, value:u8){ + timer.tima_register = value; + timer.tima_overflow = false; +} + +pub fn set_tma(timer: &mut GbTimer, value:u8){ + timer.tma_register = value; +} + +pub fn set_tac(timer: &mut GbTimer, value:u8){ + timer.tac_tegister = value & 0b111; +} + +//Reset on write +pub fn reset_div(timer: &mut GbTimer){ + timer.system_counter = 0; } \ No newline at end of file From 81ae144d061ae028d49a6ae683b6fc9e40e51fbf Mon Sep 17 00:00:00 2001 From: alloncm Date: Sun, 9 May 2021 02:32:45 +0300 Subject: [PATCH 009/136] WIP In the middle of the ppu Does not compile! --- lib_gb/src/machine/gameboy.rs | 5 +- lib_gb/src/mmu/gb_mmu.rs | 17 ++--- lib_gb/src/mmu/io_comps.rs | 37 +++++++++- lib_gb/src/ppu/gb_ppu.rs | 99 ++++++++++++++------------ lib_gb/src/ppu/ppu_register_updater.rs | 34 +++++++-- 5 files changed, 125 insertions(+), 67 deletions(-) diff --git a/lib_gb/src/machine/gameboy.rs b/lib_gb/src/machine/gameboy.rs index e30c3e0b..9d4f3214 100644 --- a/lib_gb/src/machine/gameboy.rs +++ b/lib_gb/src/machine/gameboy.rs @@ -92,9 +92,10 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ ppu_register_updater::update_ppu_regsiters(&mut self.mmu, &mut self.ppu); self.ppu.update_gb_screen(&mut self.mmu, interrupt_cycles as u32); mmu_register_updater::update_mmu_registers(&mut self.mmu, &mut self.dma); - } - self.mmu.io_comps.cycle(interrupt_cycles as u32); + + self.mmu.io_comps.cycle(interrupt_cycles as u32); + } let iter_total_cycles= cpu_cycles_passed as u32 + interrupt_cycles as u32; diff --git a/lib_gb/src/mmu/gb_mmu.rs b/lib_gb/src/mmu/gb_mmu.rs index a923b640..30a07ea4 100644 --- a/lib_gb/src/mmu/gb_mmu.rs +++ b/lib_gb/src/mmu/gb_mmu.rs @@ -1,4 +1,4 @@ -use super::{io_comps::IoComps, memory::*}; +use super::{io_comps::IoComps, memory::*, oam_dma_transferer::OamDmaTransferer}; use super::ram::Ram; use super::vram::VRam; use super::io_ports::IoPorts; @@ -16,7 +16,6 @@ const BAD_READ_VALUE:u8 = 0xFF; pub struct GbMmu<'a, D:AudioDevice>{ pub ram: Ram, - pub vram: VRam, pub finished_boot:bool, pub io_comps: IoComps, boot_rom:[u8;BOOT_ROM_SIZE], @@ -44,7 +43,7 @@ impl<'a, D:AudioDevice> Memory for GbMmu<'a, D>{ return match address{ 0x8000..=0x9FFF=>{ if self.is_vram_ready_for_io(){ - return self.vram.read_current_bank(address-0x8000); + return self.io_comps.ppu.vram.read_current_bank(address-0x8000); } else{ log::warn!("bad vram read"); @@ -79,7 +78,7 @@ impl<'a, D:AudioDevice> Memory for GbMmu<'a, D>{ match address{ 0x8000..=0x9FFF=>{ if self.is_vram_ready_for_io(){ - self.vram.write_current_bank(address-0x8000, value); + self.io_comps.ppu.vram.write_current_bank(address-0x8000, value); } else{ log::warn!("bad vram write") @@ -112,7 +111,7 @@ impl<'a, D:AudioDevice> UnprotectedMemory for GbMmu<'a, D>{ }, 0x100..=0x3FFF=>self.mbc.read_bank0(address), 0x4000..=0x7FFF=>self.mbc.read_current_bank(address-0x4000), - 0x8000..=0x9FFF=>self.vram.read_current_bank(address-0x8000), + 0x8000..=0x9FFF=>self.io_comps.ppu.vram.read_current_bank(address-0x8000), 0xA000..=0xBFFF=>self.mbc.read_external_ram(address-0xA000), 0xC000..=0xCFFF =>self.ram.read_bank0(address - 0xC000), 0xD000..=0xDFFF=>self.ram.read_current_bank(address-0xD000), @@ -128,7 +127,7 @@ impl<'a, D:AudioDevice> UnprotectedMemory for GbMmu<'a, D>{ fn write_unprotected(&mut self, address:u16, value:u8) { match address{ 0x0..=0x7FFF=>self.mbc.write_rom(address, value), - 0x8000..=0x9FFF=>self.vram.write_current_bank(address-0x8000, value), + 0x8000..=0x9FFF=>self.io_comps.ppu.vram.write_current_bank(address-0x8000, value), 0xA000..=0xBFFF=>self.mbc.write_external_ram(address-0xA000,value), 0xC000..=0xCFFF =>self.ram.write_bank0(address - 0xC000,value), 0xE000..=0xFDFF=>self.ram.write_bank0(address - 0xE000,value), @@ -146,9 +145,8 @@ impl<'a, D:AudioDevice> GbMmu<'a, D>{ pub fn new_with_bootrom(mbc:&'a mut Box, boot_rom:[u8;BOOT_ROM_SIZE], apu:GbApu)->Self{ GbMmu{ ram:Ram::default(), - io_comps:IoComps{apu, ports:IoPorts::default(), timer:GbTimer::default()}, + io_comps:IoComps::new(apu), mbc:mbc, - vram:VRam::default(), sprite_attribute_table:[0;SPRITE_ATTRIBUTE_TABLE_SIZE], hram:[0;HRAM_SIZE], interupt_enable_register:0, @@ -162,9 +160,8 @@ impl<'a, D:AudioDevice> GbMmu<'a, D>{ pub fn new(mbc:&'a mut Box, apu:GbApu)->Self{ let mut mmu = GbMmu{ ram:Ram::default(), - io_comps:IoComps{apu, ports:IoPorts::default(), timer:GbTimer::default()}, + io_comps:IoComps::new(apu), mbc:mbc, - vram:VRam::default(), sprite_attribute_table:[0;SPRITE_ATTRIBUTE_TABLE_SIZE], hram:[0;HRAM_SIZE], interupt_enable_register:0, diff --git a/lib_gb/src/mmu/io_comps.rs b/lib_gb/src/mmu/io_comps.rs index 515c2837..6d35252c 100644 --- a/lib_gb/src/mmu/io_comps.rs +++ b/lib_gb/src/mmu/io_comps.rs @@ -1,23 +1,41 @@ -use crate::{apu::{audio_device::AudioDevice, gb_apu::GbApu, set_nr11, set_nr12, set_nr13, volume_envelop::VolumeEnvlope}, timer::timer_register_updater::*, utils::{bit_masks::BIT_3_MASK, memory_registers::*}}; +use crate::{apu::{audio_device::AudioDevice, gb_apu::GbApu, set_nr11, set_nr12, set_nr13, volume_envelop::VolumeEnvlope}, ppu::ppu_register_updater::*, timer::timer_register_updater::*, utils::{bit_masks::BIT_3_MASK, memory_registers::*}}; use crate::ppu::gb_ppu::GbPpu; use crate::apu::*; use crate::timer::gb_timer::GbTimer; -use super::{io_ports::IoPorts, memory::{Memory, UnprotectedMemory}, oam_dma_transferer::OamDmaTransferer}; +use super::{io_ports::IoPorts, memory::{Memory, UnprotectedMemory}}; use super::io_ports::*; pub struct IoComps{ pub apu: GbApu, pub timer: GbTimer, + pub ppu:GbPpu, pub ports:IoPorts, } +io_port_index!(LCDC_REGISTER_INDEX, LCDC_REGISTER_ADDRESS); +io_port_index!(STAT_REGISTER_INDEX, STAT_REGISTER_ADDRESS); +io_port_index!(SCY_REGISTER_INDEX, SCY_REGISTER_ADDRESS); +io_port_index!(SCX_REGISTER_INDEX, SCX_REGISTER_ADDRESS); +io_port_index!(LYC_REGISTER_INDEX, LYC_REGISTER_ADDRESS); +io_port_index!(WY_REGISTER_INDEX, WY_REGISTER_ADDRESS); +io_port_index!(WX_REGISTER_INDEX, WX_REGISTER_ADDRESS); +io_port_index!(BGP_REGISTER_INDEX, BGP_REGISTER_ADDRESS); +io_port_index!(OBP0_REGISTER_INDEX, OBP0_REGISTER_ADDRESS); +io_port_index!(OBP1_REGISTER_INDEX, OBP1_REGISTER_ADDRESS); + + impl Memory for IoComps{ fn read(&self, address:u16)->u8 { let mut value = self.ports.read(address); match address { + //Timer DIV_REGISTER_INDEX=> value = get_div(&self.timer), TIMA_REGISTER_INDEX=> value = self.timer.tima_register, + //APU NR52_REGISTER_INDEX=> get_nr52(&self.apu, &mut value), + //PPU + STAT_REGISTER_INDEX=> value = get_stat(&self.ppu), + LY_REGISTER_ADDRESS=> value = get_ly(&self.ppu), _=>{} } @@ -53,6 +71,17 @@ impl Memory for IoComps{ NR50_REGISTER_INDEX=> set_nr50(&mut self.apu, value), NR51_REGISTER_INDEX=> set_nr51(&mut self.apu, value), NR52_REGISTER_INDEX=> set_nr52(&mut self.apu, &mut self.ports,value), + //PPU + LCDC_REGISTER_INDEX=> handle_lcdcontrol_register(value, &mut self.ppu), + STAT_REGISTER_INDEX=> update_stat_register(value, &mut self.ppu), + SCY_REGISTER_INDEX=> set_scy(&mut self.ppu, value), + SCX_REGISTER_INDEX=> set_scx(&mut self.ppu, value), + LYC_REGISTER_INDEX=> set_lyc(&mut self.ppu, value), + BGP_REGISTER_INDEX=> handle_bg_pallet_register(value,&mut self.ppu.bg_color_mapping), + OBP0_REGISTER_INDEX=> handle_obp_pallet_register(value,&mut self.ppu.obj_color_mapping0), + OBP1_REGISTER_INDEX=> handle_obp_pallet_register(value,&mut self.ppu.obj_color_mapping1), + WY_REGISTER_INDEX=> handle_wy_register(value, &mut self.ppu), + WX_REGISTER_INDEX=> handle_wx_register(value, &mut self.ppu), _=>{} } @@ -61,6 +90,10 @@ impl Memory for IoComps{ } impl IoComps{ + pub fn new(apu:GbApu)->Self{ + Self{apu, ports:IoPorts::default(), timer:GbTimer::default(), ppu:GbPpu::default()} + } + pub fn cycle(&mut self, cycles:u32){ let mut if_register = self.ports.read_unprotected(IF_REGISTER_ADDRESS - 0xFF00); self.timer.cycle(&mut if_register, cycles as u8); diff --git a/lib_gb/src/ppu/gb_ppu.rs b/lib_gb/src/ppu/gb_ppu.rs index 5fa54ee7..afb2742b 100644 --- a/lib_gb/src/ppu/gb_ppu.rs +++ b/lib_gb/src/ppu/gb_ppu.rs @@ -1,4 +1,4 @@ -use crate::mmu::memory::UnprotectedMemory; +use crate::mmu::{memory::UnprotectedMemory, vram::VRam}; use super::ppu_state::PpuState; use super::color::Color; use super::colors::*; @@ -37,6 +37,8 @@ const SPRITE_SIZE_IN_MEMORY:u16 = 16; const BLANK_SCREEN_BUFFER:[u32; SCREEN_HEIGHT * SCREEN_WIDTH] = [GbPpu::color_as_uint(&WHITE);SCREEN_HEIGHT * SCREEN_WIDTH]; pub struct GbPpu { + pub vram: VRam, + pub screen_buffer: [u32; SCREEN_HEIGHT*SCREEN_WIDTH], pub screen_enable: bool, pub window_enable: bool, @@ -55,6 +57,9 @@ pub struct GbPpu { pub current_line_drawn: u8, pub state:PpuState, + pub stat_register:u8, + pub lyc_register:u8, + //interrupts pub v_blank_interrupt_request:bool, pub h_blank_interrupt_request:bool, @@ -73,6 +78,9 @@ pub struct GbPpu { impl Default for GbPpu { fn default() -> Self { GbPpu { + vram:VRam::default(), + stat_register:0, + lyc_register:0, background_enabled: false, background_scroll: Vec2:: { x: 0, y: 0 }, window_scroll: Vec2:: { x: 0, y: 0 }, @@ -134,11 +142,9 @@ impl GbPpu { } } - fn update_ly_register(&mut self, memory:&mut impl UnprotectedMemory){ + fn update_ly_register(&mut self, if_register:&mut u8){ if self.current_line_drawn >= SCREEN_HEIGHT as u8 && !self.v_blank_triggered{ - let mut if_register = memory.read_unprotected(IF_REGISTER_ADDRESS); - if_register |= BIT_0_MASK; - memory.write_unprotected(IF_REGISTER_ADDRESS, if_register); + *if_register |= BIT_0_MASK; self.v_blank_triggered = true; } @@ -146,26 +152,25 @@ impl GbPpu { self.v_blank_triggered = false; } - memory.write_unprotected(LY_REGISTER_ADDRESS, self.current_line_drawn); + // memory.write_unprotected(LY_REGISTER_ADDRESS, self.current_line_drawn); } - fn update_stat_register(&mut self, memory: &mut impl UnprotectedMemory){ - let mut register = memory.read_unprotected(STAT_REGISTER_ADDRESS); + fn update_stat_register(&mut self, if_register:&mut u8){ let mut lcd_stat_interrupt:bool = false; - if self.current_line_drawn == memory.read_unprotected(LYC_REGISTER_ADDRESS){ - register |= BIT_2_MASK; + if self.current_line_drawn == self.lyc_register{ + self.stat_register |= BIT_2_MASK; if self.coincidence_interrupt_request { lcd_stat_interrupt = true; } } else{ - register &= !BIT_2_MASK; + self.stat_register &= !BIT_2_MASK; } //clears the 2 lower bits - register = (register >> 2)<<2; - register |= self.state as u8; + self.stat_register = (self.stat_register >> 2)<<2; + self.stat_register |= self.state as u8; match self.state{ PpuState::OamSearch=>{ @@ -188,9 +193,7 @@ impl GbPpu { if lcd_stat_interrupt{ if !self.stat_triggered{ - let mut if_register = memory.read_unprotected(IF_REGISTER_ADDRESS); - if_register |= BIT_1_MASK; - memory.write_unprotected(IF_REGISTER_ADDRESS, if_register); + *if_register |= BIT_1_MASK; self.stat_triggered = true; } @@ -199,7 +202,7 @@ impl GbPpu { self.stat_triggered = false; } - memory.write_unprotected(STAT_REGISTER_ADDRESS, register); + // memory.write_unprotected(STAT_REGISTER_ADDRESS, register); } fn get_ppu_state(cycle_counter:u32, last_ly:u8)->PpuState{ @@ -224,7 +227,7 @@ impl GbPpu { }; } - pub fn update_gb_screen(&mut self, memory: &mut impl UnprotectedMemory, cycles_passed:u32){ + pub fn update_gb_screen(&mut self,if_register:&mut u8, cycles_passed:u32){ if !self.screen_enable && self.last_screen_state { self.current_line_drawn = 0; self.current_cycle = 0; @@ -244,16 +247,16 @@ impl GbPpu { self.update_ly(); self.state = Self::get_ppu_state(self.current_cycle, self.current_line_drawn); - self.update_ly_register(memory); - self.update_stat_register(memory); + self.update_ly_register(if_register); + self.update_stat_register(if_register); if self.state as u8 == PpuState::PixelTransfer as u8{ if !self.line_rendered { self.line_rendered = true; - let mut frame_buffer_line = self.get_bg_frame_buffer(memory); - self.draw_window_frame_buffer(memory, &mut frame_buffer_line); - self.draw_objects_frame_buffer(memory, &mut frame_buffer_line); + let mut frame_buffer_line = self.get_bg_frame_buffer(); + self.draw_window_frame_buffer(&mut frame_buffer_line); + self.draw_objects_frame_buffer(&mut frame_buffer_line); let line_index = self.current_line_drawn as usize * SCREEN_WIDTH; @@ -264,7 +267,11 @@ impl GbPpu { } } - fn get_bg_frame_buffer(&self, memory: &impl UnprotectedMemory)-> [Color;SCREEN_WIDTH] { + fn read_vram(&self, address:u16)->u8{ + self.vram.read_current_bank(address - 0x8000) + } + + fn get_bg_frame_buffer(&self)-> [Color;SCREEN_WIDTH] { if !self.background_enabled{ //color in BGP 0 let color = self.get_bg_color(0); @@ -282,16 +289,16 @@ impl GbPpu { let index = ((current_line.wrapping_add(self.background_scroll.y)) / NORMAL_SPRITE_HIEGHT) as u16; if self.window_tile_background_map_data_address { for i in 0..BG_SPRITES_PER_LINE { - let chr: u8 = memory.read_unprotected(address + (index*BG_SPRITES_PER_LINE) + i); - let sprite = Self::get_normal_sprite(chr, memory, 0x8000); + let chr: u8 = self.read_vram(address + (index*BG_SPRITES_PER_LINE) + i); + let sprite = self.get_normal_sprite(chr, 0x8000); line_sprites.push(sprite); } } else { for i in 0..BG_SPRITES_PER_LINE { - let mut chr: u8 = memory.read_unprotected(address + (index*BG_SPRITES_PER_LINE) + i); + let mut chr: u8 = self.read_vram(address + (index*BG_SPRITES_PER_LINE) + i); chr = chr.wrapping_add(0x80); - let sprite = Self::get_normal_sprite(chr, memory, 0x8800); + let sprite = self.get_normal_sprite(chr, 0x8800); line_sprites.push(sprite); } } @@ -315,14 +322,14 @@ impl GbPpu { return screen_line; } - fn get_normal_sprite(index:u8, memory:&impl UnprotectedMemory, data_address:u16)->NormalSprite{ + fn get_normal_sprite(&self, index:u8, data_address:u16)->NormalSprite{ let mut sprite = NormalSprite::new(); let mut line_number = 0; let start:u16 = index as u16 * SPRITE_SIZE_IN_MEMORY; let end:u16 = start + SPRITE_SIZE_IN_MEMORY; for j in (start .. end).step_by(2) { - Self::get_line(memory, &mut sprite, data_address + j, line_number); + self.get_line(&mut sprite, data_address + j, line_number); line_number += 1; } @@ -330,7 +337,7 @@ impl GbPpu { } - fn draw_window_frame_buffer(&mut self, memory: &impl UnprotectedMemory, line:&mut [Color;SCREEN_WIDTH]) { + fn draw_window_frame_buffer(&mut self, line:&mut [Color;SCREEN_WIDTH]) { if !self.window_enable || !self.background_enabled || self.current_line_drawn < self.window_scroll.y{ return; } @@ -356,16 +363,16 @@ impl GbPpu { let index = ((self.window_line_counter) / 8) as u16; if self.window_tile_background_map_data_address { for i in 0..BG_SPRITES_PER_LINE { - let chr: u8 = memory.read_unprotected(address + (index*BG_SPRITES_PER_LINE) + i); - let sprite = Self::get_normal_sprite(chr, memory, 0x8000); + let chr: u8 = self.read_vram(address + (index*BG_SPRITES_PER_LINE) + i); + let sprite = self.get_normal_sprite(chr, 0x8000); line_sprites.push(sprite); } } else { for i in 0..BG_SPRITES_PER_LINE { - let mut chr: u8 = memory.read_unprotected(address + (index*BG_SPRITES_PER_LINE) + i); + let mut chr: u8 = self.read_vram(address + (index*BG_SPRITES_PER_LINE) + i); chr = chr.wrapping_add(0x80); - let sprite = Self::get_normal_sprite(chr, memory, 0x8800); + let sprite = self.get_normal_sprite(chr, 0x8800); line_sprites.push(sprite); } } @@ -387,7 +394,7 @@ impl GbPpu { self.window_line_counter += 1; } - fn draw_objects_frame_buffer(&self, memory:&impl UnprotectedMemory, line:&mut [Color;SCREEN_WIDTH]){ + fn draw_objects_frame_buffer(&self, line:&mut [Color;SCREEN_WIDTH]){ if !self.sprite_enable{ return; } @@ -401,8 +408,8 @@ impl GbPpu { break; } - let end_y = memory.read_unprotected(OAM_ADDRESS + i); - let end_x = memory.read_unprotected(OAM_ADDRESS + i + 1); + let end_y = self.read_vram(OAM_ADDRESS + i); + let end_x = self.read_vram(OAM_ADDRESS + i + 1); let start_y = cmp::max(0, (end_y as i16) - SPRITE_MAX_HEIGHT as i16) as u8; //cheks if this sprite apears in this line @@ -417,8 +424,8 @@ impl GbPpu { continue; } - let tile_number = memory.read_unprotected(OAM_ADDRESS + i + 2); - let attributes = memory.read_unprotected(OAM_ADDRESS + i + 3); + let tile_number = self.read_vram(OAM_ADDRESS + i + 2); + let attributes = self.read_vram(OAM_ADDRESS + i + 3); obj_attributes.push(SpriteAttribute::new(end_y, end_x, tile_number, attributes)); } @@ -430,7 +437,7 @@ impl GbPpu { obj_attributes.sort_by(|a, b| b.x.cmp(&a.x)); for obj_attribute in &obj_attributes{ - let mut sprite = Self::get_sprite(obj_attribute.tile_number, memory, 0x8000, self.sprite_extended); + let mut sprite = self.get_sprite(obj_attribute.tile_number, 0x8000, self.sprite_extended); if obj_attribute.flip_y { sprite.flip_y(); @@ -471,7 +478,7 @@ impl GbPpu { }; } - fn get_sprite(mut index:u8, memory:&impl UnprotectedMemory, data_address:u16, extended:bool)->Box{ + fn get_sprite(&self, mut index:u8, data_address:u16, extended:bool)->Box{ let mut sprite:Box; if extended{ //ignore bit 0 @@ -487,7 +494,7 @@ impl GbPpu { let end:u16 = start + ((sprite.size() as u16) *2); let raw = Box::into_raw(sprite); for j in (start .. end).step_by(2) { - Self::get_line(memory, raw, data_address + j, line_number); + self.get_line( raw, data_address + j, line_number); line_number += 1; } unsafe{sprite = Box::from_raw(raw);} @@ -495,9 +502,9 @@ impl GbPpu { return sprite; } - fn get_line(memory:&impl UnprotectedMemory, sprite:*mut dyn Sprite, address:u16, line_number:u8){ - let byte = memory.read_unprotected(address); - let next = memory.read_unprotected(address + 1); + fn get_line(&self, sprite:*mut dyn Sprite, address:u16, line_number:u8){ + let byte = self.read_vram(address); + let next = self.read_vram(address + 1); for k in (0..SPRITE_WIDTH).rev() { let mask = 1 << k; let mut value = (byte & mask) >> k; diff --git a/lib_gb/src/ppu/ppu_register_updater.rs b/lib_gb/src/ppu/ppu_register_updater.rs index 18735f8e..bf21d54c 100644 --- a/lib_gb/src/ppu/ppu_register_updater.rs +++ b/lib_gb/src/ppu/ppu_register_updater.rs @@ -42,7 +42,7 @@ pub fn update_ppu_regsiters(memory:&mut GbMmu, ppu: &mut GbP } -fn handle_lcdcontrol_register( register:u8, ppu:&mut GbPpu){ +pub fn handle_lcdcontrol_register( register:u8, ppu:&mut GbPpu){ ppu.screen_enable = (register & BIT_7_MASK) != 0; ppu.window_tile_map_address = (register & BIT_6_MASK) != 0; ppu.window_enable = (register & BIT_5_MASK) != 0; @@ -53,26 +53,34 @@ fn handle_lcdcontrol_register( register:u8, ppu:&mut GbPpu){ ppu.background_enabled = (register & BIT_0_MASK) != 0; } -fn update_stat_register(register:u8, ppu: &mut GbPpu){ +pub fn update_stat_register(register:u8, ppu: &mut GbPpu){ ppu.h_blank_interrupt_request = register & BIT_3_MASK != 0; ppu.v_blank_interrupt_request = register & BIT_4_MASK != 0; ppu.oam_search_interrupt_request = register & BIT_5_MASK != 0; ppu.coincidence_interrupt_request = register & BIT_6_MASK != 0; } -fn handle_scroll_registers(scroll_x:u8, scroll_y:u8, ppu: &mut GbPpu){ +pub fn handle_scroll_registers(scroll_x:u8, scroll_y:u8, ppu: &mut GbPpu){ ppu.background_scroll.x = scroll_x; ppu.background_scroll.y = scroll_y; } -fn handle_bg_pallet_register(register:u8, pallet:&mut [Color;4] ){ +pub fn set_scx(ppu: &mut GbPpu, value:u8){ + ppu.window_scroll.x = value; +} + +pub fn set_scy(ppu:&mut GbPpu, value:u8){ + ppu.window_scroll.y = value; +} + +pub fn handle_bg_pallet_register(register:u8, pallet:&mut [Color;4] ){ pallet[0] = get_matching_color(register&0b00000011); pallet[1] = get_matching_color((register&0b00001100)>>2); pallet[2] = get_matching_color((register&0b00110000)>>4); pallet[3] = get_matching_color((register&0b11000000)>>6); } -fn handle_obp_pallet_register(register:u8, pallet:&mut [Option;4] ){ +pub fn handle_obp_pallet_register(register:u8, pallet:&mut [Option;4] ){ pallet[0] = None; pallet[1] = Some(get_matching_color((register&0b00001100)>>2)); pallet[2] = Some(get_matching_color((register&0b00110000)>>4)); @@ -89,11 +97,11 @@ fn get_matching_color(number:u8)->Color{ }; } -fn handle_wy_register(register:u8, ppu:&mut GbPpu){ +pub fn handle_wy_register(register:u8, ppu:&mut GbPpu){ ppu.window_scroll.y = register; } -fn handle_wx_register(register:u8, ppu:&mut GbPpu){ +pub fn handle_wx_register(register:u8, ppu:&mut GbPpu){ if register < WX_OFFSET{ ppu.window_scroll.x = 0; } @@ -101,3 +109,15 @@ fn handle_wx_register(register:u8, ppu:&mut GbPpu){ ppu.window_scroll.x = register - WX_OFFSET; } } + +pub fn get_ly(ppu:&GbPpu)->u8{ + ppu.current_line_drawn +} + +pub fn get_stat(ppu:&GbPpu)->u8{ + ppu.stat_register +} + +pub fn set_lyc(ppu:&mut GbPpu, value:u8){ + ppu.lyc_register = value; +} From 46cdced3c31b752b2128cedb19eeaedaa0b1752c Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 28 May 2021 14:55:24 +0300 Subject: [PATCH 010/136] move the PPU to the IO comps --- lib_gb/src/machine/gameboy.rs | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/lib_gb/src/machine/gameboy.rs b/lib_gb/src/machine/gameboy.rs index 9d4f3214..3b8fe08b 100644 --- a/lib_gb/src/machine/gameboy.rs +++ b/lib_gb/src/machine/gameboy.rs @@ -1,4 +1,4 @@ -use crate::{apu::{self, audio_device::AudioDevice, gb_apu::GbApu}, cpu::{gb_cpu::GbCpu, opcodes::opcode_resolver::*}, keypad::{joypad::Joypad, joypad_provider::JoypadProvider, joypad_register_updater}, mmu::{carts::mbc::Mbc, gb_mmu::{GbMmu, BOOT_ROM_SIZE}, memory::{Memory, UnprotectedMemory}, mmu_register_updater, oam_dma_transferer::OamDmaTransferer}, ppu::{gb_ppu::{CYCLES_PER_FRAME, GbPpu, SCREEN_HEIGHT, SCREEN_WIDTH}, ppu_register_updater}, timer::{gb_timer::GbTimer, timer_register_updater}, utils::memory_registers::IF_REGISTER_ADDRESS}; +use crate::{apu::{audio_device::AudioDevice, gb_apu::GbApu}, cpu::{gb_cpu::GbCpu, opcodes::opcode_resolver::*}, keypad::{joypad::Joypad, joypad_provider::JoypadProvider, joypad_register_updater}, mmu::{carts::mbc::Mbc, gb_mmu::{GbMmu, BOOT_ROM_SIZE}, memory::Memory, mmu_register_updater, oam_dma_transferer::OamDmaTransferer}, ppu::{gb_ppu::{CYCLES_PER_FRAME, SCREEN_HEIGHT, SCREEN_WIDTH} }}; use super::interrupts_handler::InterruptsHandler; use std::boxed::Box; use log::debug; @@ -8,7 +8,6 @@ pub struct GameBoy<'a, JP: JoypadProvider, AD:AudioDevice> { cpu: GbCpu, mmu: GbMmu::<'a, AD>, opcode_resolver:OpcodeResolver::>, - ppu:GbPpu, interrupts_handler:InterruptsHandler, cycles_counter:u32, joypad_provider: JP, @@ -22,7 +21,6 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ cpu:GbCpu::default(), mmu:GbMmu::new_with_bootrom(mbc, boot_rom, GbApu::new(audio_device)), opcode_resolver:OpcodeResolver::default(), - ppu:GbPpu::default(), interrupts_handler: InterruptsHandler::default(), cycles_counter:0, joypad_provider: joypad_provider, @@ -44,7 +42,6 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ cpu:cpu, mmu:GbMmu::new(mbc, GbApu::new(audio_device)), opcode_resolver:OpcodeResolver::default(), - ppu:GbPpu::default(), interrupts_handler: InterruptsHandler::default(), cycles_counter:0, joypad_provider: joypad_provider, @@ -55,7 +52,7 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ pub fn cycle_frame(&mut self)->&[u32;SCREEN_HEIGHT*SCREEN_WIDTH]{ let mut joypad = Joypad::default(); - let mut last_ppu_power_state:bool = self.ppu.screen_enable; + let mut last_ppu_power_state:bool = self.mmu.io_comps.ppu.screen_enable; while self.cycles_counter < CYCLES_PER_FRAME{ self.joypad_provider.provide(&mut joypad); @@ -73,24 +70,20 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ self.dma.cycle(&mut self.mmu, cpu_cycles_passed as u8); + mmu_register_updater::update_mmu_registers(&mut self.mmu, &mut self.dma); self.mmu.io_comps.cycle(cpu_cycles_passed as u32); //For the PPU - mmu_register_updater::update_mmu_registers(&mut self.mmu, &mut self.dma); - ppu_register_updater::update_ppu_regsiters(&mut self.mmu, &mut self.ppu); - self.ppu.update_gb_screen(&mut self.mmu, cpu_cycles_passed as u32); mmu_register_updater::update_mmu_registers(&mut self.mmu, &mut self.dma); //interrupts - let interrupt_cycles = self.interrupts_handler.handle_interrupts(&mut self.cpu, &mut self.ppu, &mut self.mmu); + let interrupt_cycles = self.interrupts_handler.handle_interrupts(&mut self.cpu, &mut self.mmu); if interrupt_cycles != 0{ self.dma.cycle(&mut self.mmu, interrupt_cycles as u8); mmu_register_updater::update_mmu_registers(&mut self.mmu, &mut self.dma); //PPU - ppu_register_updater::update_ppu_regsiters(&mut self.mmu, &mut self.ppu); - self.ppu.update_gb_screen(&mut self.mmu, interrupt_cycles as u32); mmu_register_updater::update_mmu_registers(&mut self.mmu, &mut self.dma); @@ -100,28 +93,24 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ let iter_total_cycles= cpu_cycles_passed as u32 + interrupt_cycles as u32; - //APU - // apu::update_apu_registers(&mut self.mmu, &mut self.apu); - // self.apu.cycle(iter_total_cycles as u8); - //clears io ports self.mmu.io_comps.ports.clear_io_ports_triggers(); //In case the ppu just turned I want to keep it sync with the actual screen and thats why Im reseting the loop to finish //the frame when the ppu finishes the frame - if !last_ppu_power_state && self.ppu.screen_enable{ + if !last_ppu_power_state && self.mmu.io_comps.ppu.screen_enable{ self.cycles_counter = 0; } self.cycles_counter += iter_total_cycles; - last_ppu_power_state = self.ppu.screen_enable; + last_ppu_power_state = self.mmu.io_comps.ppu.screen_enable; } if self.cycles_counter >= CYCLES_PER_FRAME{ self.cycles_counter -= CYCLES_PER_FRAME; } - return self.ppu.get_frame_buffer(); + return self.mmu.io_comps.ppu.get_frame_buffer(); } fn fetch_next_byte(&mut self)->u8{ From de40608706a34a5ea066d3a3a5741b88c35f9d8a Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 28 May 2021 14:56:00 +0300 Subject: [PATCH 011/136] Move the ppu to the io comps --- lib_gb/src/machine/interrupts_handler.rs | 17 ++++++++++------- lib_gb/src/mmu/gb_mmu.rs | 18 ++++++------------ lib_gb/src/mmu/io_comps.rs | 6 ++++-- lib_gb/src/ppu/gb_ppu.rs | 19 ++++++++----------- lib_gb/src/ppu/ppu_register_updater.rs | 6 ++++-- lib_gb/src/timer/gb_timer.rs | 2 +- 6 files changed, 33 insertions(+), 35 deletions(-) diff --git a/lib_gb/src/machine/interrupts_handler.rs b/lib_gb/src/machine/interrupts_handler.rs index 72c7db65..f15927d6 100644 --- a/lib_gb/src/machine/interrupts_handler.rs +++ b/lib_gb/src/machine/interrupts_handler.rs @@ -1,8 +1,11 @@ -use crate::{cpu::gb_cpu::GbCpu, ppu::gb_ppu::GbPpu, utils::memory_registers::IE_REGISTER_ADDRESS}; -use crate::utils::{ +use crate::{cpu::gb_cpu::GbCpu, utils::{ bit_masks::*, - memory_registers::IF_REGISTER_ADDRESS -}; + memory_registers::{ + IE_REGISTER_ADDRESS, + IF_REGISTER_ADDRESS, + STAT_REGISTER_ADDRESS + } +}}; use crate::cpu::opcodes::opcodes_utils::push; use crate::mmu::memory::Memory; @@ -26,18 +29,18 @@ impl Default for InterruptsHandler{ impl InterruptsHandler{ - pub fn handle_interrupts(&mut self, cpu:&mut GbCpu,ppu:&mut GbPpu, memory:&mut impl Memory)->u8{ + pub fn handle_interrupts(&mut self, cpu:&mut GbCpu, memory:&mut impl Memory)->u8{ //this is delayed by one instruction cause there is this delay since EI opcode is called untill the interrupt could happen let mut interupt_flag = memory.read(IF_REGISTER_ADDRESS); let interupt_enable = memory.read(IE_REGISTER_ADDRESS); + let stat_register = memory.read(STAT_REGISTER_ADDRESS); if cpu.mie && self.ei_triggered{ if interupt_flag & BIT_0_MASK != 0 && interupt_enable & BIT_0_MASK != 0{ return Self::prepare_for_interut(cpu, BIT_0_MASK, V_BLANK_INTERRUPT_ADDERESS, memory, &mut interupt_flag); } - if interupt_flag & BIT_1_MASK != 0 && interupt_enable & BIT_1_MASK != 0 && - (ppu.v_blank_interrupt_request || ppu.oam_search_interrupt_request || ppu.h_blank_interrupt_request || ppu.coincidence_interrupt_request){ + if interupt_flag & BIT_1_MASK != 0 && interupt_enable & BIT_1_MASK != 0 && (stat_register & 0b111_1000) != 0{ return Self::prepare_for_interut(cpu, BIT_1_MASK, LCD_STAT_INTERRUPT_ADDERESS, memory, &mut interupt_flag); } if interupt_flag & BIT_2_MASK != 0 && interupt_enable & BIT_2_MASK != 0{ diff --git a/lib_gb/src/mmu/gb_mmu.rs b/lib_gb/src/mmu/gb_mmu.rs index 30a07ea4..5e653d9a 100644 --- a/lib_gb/src/mmu/gb_mmu.rs +++ b/lib_gb/src/mmu/gb_mmu.rs @@ -1,16 +1,13 @@ -use super::{io_comps::IoComps, memory::*, oam_dma_transferer::OamDmaTransferer}; +use super::{io_comps::IoComps, memory::*}; use super::ram::Ram; -use super::vram::VRam; -use super::io_ports::IoPorts; use super::access_bus::AccessBus; -use crate::{apu::{audio_device::AudioDevice, gb_apu::GbApu}, timer::gb_timer::GbTimer, utils::memory_registers::BOOT_REGISTER_ADDRESS}; +use crate::{apu::{audio_device::AudioDevice, gb_apu::GbApu}, utils::memory_registers::BOOT_REGISTER_ADDRESS}; use super::carts::mbc::Mbc; use crate::ppu::ppu_state::PpuState; use std::boxed::Box; pub const BOOT_ROM_SIZE:usize = 0x100; const HRAM_SIZE:usize = 0x7F; -const SPRITE_ATTRIBUTE_TABLE_SIZE:usize = 0xA0; const BAD_READ_VALUE:u8 = 0xFF; @@ -20,7 +17,6 @@ pub struct GbMmu<'a, D:AudioDevice>{ pub io_comps: IoComps, boot_rom:[u8;BOOT_ROM_SIZE], mbc: &'a mut Box, - sprite_attribute_table:[u8;SPRITE_ATTRIBUTE_TABLE_SIZE], hram: [u8;HRAM_SIZE], interupt_enable_register:u8, pub dma_state:Option, @@ -52,7 +48,7 @@ impl<'a, D:AudioDevice> Memory for GbMmu<'a, D>{ }, 0xFE00..=0xFE9F=>{ if self.is_oam_ready_for_io(){ - return self.sprite_attribute_table[(address-0xFE00) as usize]; + return self.io_comps.ppu.sprite_attribute_table[(address-0xFE00) as usize]; } else{ log::warn!("bad oam read"); @@ -86,7 +82,7 @@ impl<'a, D:AudioDevice> Memory for GbMmu<'a, D>{ }, 0xFE00..=0xFE9F=>{ if self.is_oam_ready_for_io(){ - self.sprite_attribute_table[(address-0xFE00) as usize] = value; + self.io_comps.ppu.sprite_attribute_table[(address-0xFE00) as usize] = value; } else{ log::warn!("bad oam write") @@ -116,7 +112,7 @@ impl<'a, D:AudioDevice> UnprotectedMemory for GbMmu<'a, D>{ 0xC000..=0xCFFF =>self.ram.read_bank0(address - 0xC000), 0xD000..=0xDFFF=>self.ram.read_current_bank(address-0xD000), 0xE000..=0xFDFF=>self.ram.read_bank0(address - 0xE000), - 0xFE00..=0xFE9F=>self.sprite_attribute_table[(address-0xFE00) as usize], + 0xFE00..=0xFE9F=>self.io_comps.ppu.sprite_attribute_table[(address-0xFE00) as usize], 0xFEA0..=0xFEFF=>0x0, 0xFF00..=0xFF7F=>self.io_comps.ports.read_unprotected(address - 0xFF00), 0xFF80..=0xFFFE=>self.hram[(address-0xFF80) as usize], @@ -132,7 +128,7 @@ impl<'a, D:AudioDevice> UnprotectedMemory for GbMmu<'a, D>{ 0xC000..=0xCFFF =>self.ram.write_bank0(address - 0xC000,value), 0xE000..=0xFDFF=>self.ram.write_bank0(address - 0xE000,value), 0xD000..=0xDFFF=>self.ram.write_current_bank(address-0xD000,value), - 0xFE00..=0xFE9F=>self.sprite_attribute_table[(address-0xFE00) as usize] = value, + 0xFE00..=0xFE9F=>self.io_comps.ppu.sprite_attribute_table[(address-0xFE00) as usize] = value, 0xFEA0..=0xFEFF=>{}, 0xFF00..=0xFF7F=>self.io_comps.ports.write_unprotected(address - 0xFF00, value), 0xFF80..=0xFFFE=>self.hram[(address-0xFF80) as usize] = value, @@ -147,7 +143,6 @@ impl<'a, D:AudioDevice> GbMmu<'a, D>{ ram:Ram::default(), io_comps:IoComps::new(apu), mbc:mbc, - sprite_attribute_table:[0;SPRITE_ATTRIBUTE_TABLE_SIZE], hram:[0;HRAM_SIZE], interupt_enable_register:0, boot_rom:boot_rom, @@ -162,7 +157,6 @@ impl<'a, D:AudioDevice> GbMmu<'a, D>{ ram:Ram::default(), io_comps:IoComps::new(apu), mbc:mbc, - sprite_attribute_table:[0;SPRITE_ATTRIBUTE_TABLE_SIZE], hram:[0;HRAM_SIZE], interupt_enable_register:0, boot_rom:[0;BOOT_ROM_SIZE], diff --git a/lib_gb/src/mmu/io_comps.rs b/lib_gb/src/mmu/io_comps.rs index 6d35252c..6a205061 100644 --- a/lib_gb/src/mmu/io_comps.rs +++ b/lib_gb/src/mmu/io_comps.rs @@ -1,4 +1,4 @@ -use crate::{apu::{audio_device::AudioDevice, gb_apu::GbApu, set_nr11, set_nr12, set_nr13, volume_envelop::VolumeEnvlope}, ppu::ppu_register_updater::*, timer::timer_register_updater::*, utils::{bit_masks::BIT_3_MASK, memory_registers::*}}; +use crate::{apu::{audio_device::AudioDevice, gb_apu::GbApu, set_nr11, set_nr12, set_nr13}, ppu::ppu_register_updater::*, timer::timer_register_updater::*, utils::memory_registers::*}; use crate::ppu::gb_ppu::GbPpu; use crate::apu::*; use crate::timer::gb_timer::GbTimer; @@ -16,6 +16,7 @@ io_port_index!(LCDC_REGISTER_INDEX, LCDC_REGISTER_ADDRESS); io_port_index!(STAT_REGISTER_INDEX, STAT_REGISTER_ADDRESS); io_port_index!(SCY_REGISTER_INDEX, SCY_REGISTER_ADDRESS); io_port_index!(SCX_REGISTER_INDEX, SCX_REGISTER_ADDRESS); +io_port_index!(LY_REGISTER_INDEX, LY_REGISTER_ADDRESS); io_port_index!(LYC_REGISTER_INDEX, LYC_REGISTER_ADDRESS); io_port_index!(WY_REGISTER_INDEX, WY_REGISTER_ADDRESS); io_port_index!(WX_REGISTER_INDEX, WX_REGISTER_ADDRESS); @@ -35,7 +36,7 @@ impl Memory for IoComps{ NR52_REGISTER_INDEX=> get_nr52(&self.apu, &mut value), //PPU STAT_REGISTER_INDEX=> value = get_stat(&self.ppu), - LY_REGISTER_ADDRESS=> value = get_ly(&self.ppu), + LY_REGISTER_INDEX=> value = get_ly(&self.ppu), _=>{} } @@ -98,6 +99,7 @@ impl IoComps{ let mut if_register = self.ports.read_unprotected(IF_REGISTER_ADDRESS - 0xFF00); self.timer.cycle(&mut if_register, cycles as u8); self.apu.cycle(cycles as u8); + self.ppu.update_gb_screen(&mut if_register, cycles); self.ports.write_unprotected(IF_REGISTER_ADDRESS - 0xFF00, if_register); } } \ No newline at end of file diff --git a/lib_gb/src/ppu/gb_ppu.rs b/lib_gb/src/ppu/gb_ppu.rs index afb2742b..f108a644 100644 --- a/lib_gb/src/ppu/gb_ppu.rs +++ b/lib_gb/src/ppu/gb_ppu.rs @@ -1,4 +1,4 @@ -use crate::mmu::{memory::UnprotectedMemory, vram::VRam}; +use crate::mmu::vram::VRam; use super::ppu_state::PpuState; use super::color::Color; use super::colors::*; @@ -9,7 +9,6 @@ use super::extended_sprite::ExtendedSprite; use super::sprite::Sprite; use super::sprite_attribute::SpriteAttribute; use crate::utils::{ - memory_registers::*, bit_masks::* }; use std::cmp; @@ -25,7 +24,7 @@ const PIXEL_TRANSFER_CLOCKS:u8 = 43; const H_BLANK_CLOCKS:u8 = 51; const DRAWING_CYCLE_CLOCKS: u8 = OAM_CLOCKS + H_BLANK_CLOCKS + PIXEL_TRANSFER_CLOCKS; const LY_MAX_VALUE:u8 = 153; -const OAM_ADDRESS:u16 = 0xFE00; +const SPRITE_ATTRIBUTE_TABLE_SIZE:usize = 0xA0; const OAM_SIZE:u16 = 0xA0; const OBJ_PER_LINE:usize = 10; const SPRITE_WIDTH:u8 = 8; @@ -38,6 +37,7 @@ const BLANK_SCREEN_BUFFER:[u32; SCREEN_HEIGHT * SCREEN_WIDTH] = [GbPpu::color_as pub struct GbPpu { pub vram: VRam, + pub sprite_attribute_table:[u8;SPRITE_ATTRIBUTE_TABLE_SIZE], pub screen_buffer: [u32; SCREEN_HEIGHT*SCREEN_WIDTH], pub screen_enable: bool, @@ -79,6 +79,7 @@ impl Default for GbPpu { fn default() -> Self { GbPpu { vram:VRam::default(), + sprite_attribute_table: [0;SPRITE_ATTRIBUTE_TABLE_SIZE], stat_register:0, lyc_register:0, background_enabled: false, @@ -151,8 +152,6 @@ impl GbPpu { else if self.current_line_drawn < SCREEN_HEIGHT as u8{ self.v_blank_triggered = false; } - - // memory.write_unprotected(LY_REGISTER_ADDRESS, self.current_line_drawn); } fn update_stat_register(&mut self, if_register:&mut u8){ @@ -201,8 +200,6 @@ impl GbPpu { else{ self.stat_triggered = false; } - - // memory.write_unprotected(STAT_REGISTER_ADDRESS, register); } fn get_ppu_state(cycle_counter:u32, last_ly:u8)->PpuState{ @@ -408,8 +405,8 @@ impl GbPpu { break; } - let end_y = self.read_vram(OAM_ADDRESS + i); - let end_x = self.read_vram(OAM_ADDRESS + i + 1); + let end_y = self.sprite_attribute_table[i as usize]; + let end_x = self.sprite_attribute_table[(i + 1) as usize]; let start_y = cmp::max(0, (end_y as i16) - SPRITE_MAX_HEIGHT as i16) as u8; //cheks if this sprite apears in this line @@ -424,8 +421,8 @@ impl GbPpu { continue; } - let tile_number = self.read_vram(OAM_ADDRESS + i + 2); - let attributes = self.read_vram(OAM_ADDRESS + i + 3); + let tile_number = self.sprite_attribute_table[(i + 2) as usize]; + let attributes = self.sprite_attribute_table[(i + 3) as usize]; obj_attributes.push(SpriteAttribute::new(end_y, end_x, tile_number, attributes)); } diff --git a/lib_gb/src/ppu/ppu_register_updater.rs b/lib_gb/src/ppu/ppu_register_updater.rs index bf21d54c..b0ebabd2 100644 --- a/lib_gb/src/ppu/ppu_register_updater.rs +++ b/lib_gb/src/ppu/ppu_register_updater.rs @@ -58,6 +58,8 @@ pub fn update_stat_register(register:u8, ppu: &mut GbPpu){ ppu.v_blank_interrupt_request = register & BIT_4_MASK != 0; ppu.oam_search_interrupt_request = register & BIT_5_MASK != 0; ppu.coincidence_interrupt_request = register & BIT_6_MASK != 0; + + ppu.stat_register = register & 0b111_1000; } pub fn handle_scroll_registers(scroll_x:u8, scroll_y:u8, ppu: &mut GbPpu){ @@ -66,11 +68,11 @@ pub fn handle_scroll_registers(scroll_x:u8, scroll_y:u8, ppu: &mut GbPpu){ } pub fn set_scx(ppu: &mut GbPpu, value:u8){ - ppu.window_scroll.x = value; + ppu.background_scroll.x = value; } pub fn set_scy(ppu:&mut GbPpu, value:u8){ - ppu.window_scroll.y = value; + ppu.background_scroll.y = value; } pub fn handle_bg_pallet_register(register:u8, pallet:&mut [Color;4] ){ diff --git a/lib_gb/src/timer/gb_timer.rs b/lib_gb/src/timer/gb_timer.rs index 02560d61..8590fa49 100644 --- a/lib_gb/src/timer/gb_timer.rs +++ b/lib_gb/src/timer/gb_timer.rs @@ -1,4 +1,4 @@ -use crate::utils::{bit_masks::*, memory_registers::*}; +use crate::utils::bit_masks::*; pub struct GbTimer{ pub system_counter:u16, From df8dbd587fb7e2acadb51c310bf769b71ca02467 Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 28 May 2021 18:00:04 +0300 Subject: [PATCH 012/136] Move the DMA and the MMU to the new io comps --- lib_gb/src/machine/gameboy.rs | 38 +++-------------- lib_gb/src/mmu/gb_mmu.rs | 59 +++++++++++++++----------- lib_gb/src/mmu/io_comps.rs | 20 ++++++++- lib_gb/src/mmu/mmu_register_updater.rs | 10 ++--- lib_gb/src/mmu/oam_dma_transferer.rs | 25 +---------- lib_gb/src/mmu/vram.rs | 3 +- 6 files changed, 67 insertions(+), 88 deletions(-) diff --git a/lib_gb/src/machine/gameboy.rs b/lib_gb/src/machine/gameboy.rs index 3b8fe08b..945631e2 100644 --- a/lib_gb/src/machine/gameboy.rs +++ b/lib_gb/src/machine/gameboy.rs @@ -1,4 +1,4 @@ -use crate::{apu::{audio_device::AudioDevice, gb_apu::GbApu}, cpu::{gb_cpu::GbCpu, opcodes::opcode_resolver::*}, keypad::{joypad::Joypad, joypad_provider::JoypadProvider, joypad_register_updater}, mmu::{carts::mbc::Mbc, gb_mmu::{GbMmu, BOOT_ROM_SIZE}, memory::Memory, mmu_register_updater, oam_dma_transferer::OamDmaTransferer}, ppu::{gb_ppu::{CYCLES_PER_FRAME, SCREEN_HEIGHT, SCREEN_WIDTH} }}; +use crate::{apu::{audio_device::AudioDevice, gb_apu::GbApu}, cpu::{gb_cpu::GbCpu, opcodes::opcode_resolver::*}, keypad::{joypad::Joypad, joypad_provider::JoypadProvider, joypad_register_updater}, mmu::{carts::mbc::Mbc, gb_mmu::{GbMmu, BOOT_ROM_SIZE}, memory::Memory}, ppu::{gb_ppu::{CYCLES_PER_FRAME, SCREEN_HEIGHT, SCREEN_WIDTH} }}; use super::interrupts_handler::InterruptsHandler; use std::boxed::Box; use log::debug; @@ -10,8 +10,7 @@ pub struct GameBoy<'a, JP: JoypadProvider, AD:AudioDevice> { opcode_resolver:OpcodeResolver::>, interrupts_handler:InterruptsHandler, cycles_counter:u32, - joypad_provider: JP, - dma:OamDmaTransferer + joypad_provider: JP } impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ @@ -23,8 +22,7 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ opcode_resolver:OpcodeResolver::default(), interrupts_handler: InterruptsHandler::default(), cycles_counter:0, - joypad_provider: joypad_provider, - dma: OamDmaTransferer::default() + joypad_provider: joypad_provider } } @@ -45,7 +43,6 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ interrupts_handler: InterruptsHandler::default(), cycles_counter:0, joypad_provider: joypad_provider, - dma: OamDmaTransferer::default() } } @@ -63,39 +60,18 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ if !self.cpu.halt{ cpu_cycles_passed = self.execute_opcode(); } - - //For the DMA controller - mmu_register_updater::update_mmu_registers(&mut self.mmu, &mut self.dma); - - self.dma.cycle(&mut self.mmu, cpu_cycles_passed as u8); - - mmu_register_updater::update_mmu_registers(&mut self.mmu, &mut self.dma); - self.mmu.io_comps.cycle(cpu_cycles_passed as u32); - - //For the PPU - - mmu_register_updater::update_mmu_registers(&mut self.mmu, &mut self.dma); + self.mmu.cycle(cpu_cycles_passed); //interrupts let interrupt_cycles = self.interrupts_handler.handle_interrupts(&mut self.cpu, &mut self.mmu); - if interrupt_cycles != 0{ - self.dma.cycle(&mut self.mmu, interrupt_cycles as u8); - mmu_register_updater::update_mmu_registers(&mut self.mmu, &mut self.dma); - - //PPU - mmu_register_updater::update_mmu_registers(&mut self.mmu, &mut self.dma); - - - self.mmu.io_comps.cycle(interrupt_cycles as u32); + if interrupt_cycles != 0{ + self.mmu.cycle(interrupt_cycles); } let iter_total_cycles= cpu_cycles_passed as u32 + interrupt_cycles as u32; - //clears io ports - self.mmu.io_comps.ports.clear_io_ports_triggers(); - //In case the ppu just turned I want to keep it sync with the actual screen and thats why Im reseting the loop to finish //the frame when the ppu finishes the frame if !last_ppu_power_state && self.mmu.io_comps.ppu.screen_enable{ @@ -124,7 +100,7 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ let opcode:u8 = self.fetch_next_byte(); //debug - if self.mmu.finished_boot{ + if self.mmu.io_comps.finished_boot{ let a = *self.cpu.af.high(); let b = *self.cpu.bc.high(); let c = *self.cpu.bc.low(); diff --git a/lib_gb/src/mmu/gb_mmu.rs b/lib_gb/src/mmu/gb_mmu.rs index 5e653d9a..5cbd3d05 100644 --- a/lib_gb/src/mmu/gb_mmu.rs +++ b/lib_gb/src/mmu/gb_mmu.rs @@ -1,5 +1,4 @@ use super::{io_comps::IoComps, memory::*}; -use super::ram::Ram; use super::access_bus::AccessBus; use crate::{apu::{audio_device::AudioDevice, gb_apu::GbApu}, utils::memory_registers::BOOT_REGISTER_ADDRESS}; use super::carts::mbc::Mbc; @@ -8,26 +7,24 @@ use std::boxed::Box; pub const BOOT_ROM_SIZE:usize = 0x100; const HRAM_SIZE:usize = 0x7F; +const DMA_SIZE:u16 = 0xA0; +const DMA_DEST:u16 = 0xFE00; const BAD_READ_VALUE:u8 = 0xFF; pub struct GbMmu<'a, D:AudioDevice>{ - pub ram: Ram, - pub finished_boot:bool, pub io_comps: IoComps, boot_rom:[u8;BOOT_ROM_SIZE], mbc: &'a mut Box, hram: [u8;HRAM_SIZE], - interupt_enable_register:u8, - pub dma_state:Option, - pub ppu_state:PpuState + interupt_enable_register:u8 } //DMA only locks the used bus. there 2 possible used buses: extrnal (wram, rom, sram) and video (vram) impl<'a, D:AudioDevice> Memory for GbMmu<'a, D>{ fn read(&self, address:u16)->u8{ - if let Some (bus) = &self.dma_state{ + if let Some (bus) = &self.io_comps.dma.enable{ return match address{ 0xFF00..=0xFF7F => self.io_comps.read(address - 0xFF00), 0xFEA0..=0xFEFF | 0xFF80..=0xFFFE | 0xFFFF=>self.read_unprotected(address), @@ -61,7 +58,7 @@ impl<'a, D:AudioDevice> Memory for GbMmu<'a, D>{ } fn write(&mut self, address:u16, value:u8){ - if let Some(bus) = &self.dma_state{ + if let Some(bus) = &self.io_comps.dma.enable{ match address{ 0xFF00..=0xFF7F => self.io_comps.write(address- 0xFF00, value), 0xFF80..=0xFFFE | 0xFFFF=>self.write_unprotected(address, value), @@ -99,7 +96,7 @@ impl<'a, D:AudioDevice> UnprotectedMemory for GbMmu<'a, D>{ fn read_unprotected(&self, address:u16) ->u8 { return match address{ 0x0..=0xFF=>{ - if self.finished_boot{ + if self.io_comps.finished_boot{ return self.mbc.read_bank0(address); } @@ -109,9 +106,9 @@ impl<'a, D:AudioDevice> UnprotectedMemory for GbMmu<'a, D>{ 0x4000..=0x7FFF=>self.mbc.read_current_bank(address-0x4000), 0x8000..=0x9FFF=>self.io_comps.ppu.vram.read_current_bank(address-0x8000), 0xA000..=0xBFFF=>self.mbc.read_external_ram(address-0xA000), - 0xC000..=0xCFFF =>self.ram.read_bank0(address - 0xC000), - 0xD000..=0xDFFF=>self.ram.read_current_bank(address-0xD000), - 0xE000..=0xFDFF=>self.ram.read_bank0(address - 0xE000), + 0xC000..=0xCFFF =>self.io_comps.ram.read_bank0(address - 0xC000), + 0xD000..=0xDFFF=>self.io_comps.ram.read_current_bank(address-0xD000), + 0xE000..=0xFDFF=>self.io_comps.ram.read_bank0(address - 0xE000), 0xFE00..=0xFE9F=>self.io_comps.ppu.sprite_attribute_table[(address-0xFE00) as usize], 0xFEA0..=0xFEFF=>0x0, 0xFF00..=0xFF7F=>self.io_comps.ports.read_unprotected(address - 0xFF00), @@ -125,9 +122,9 @@ impl<'a, D:AudioDevice> UnprotectedMemory for GbMmu<'a, D>{ 0x0..=0x7FFF=>self.mbc.write_rom(address, value), 0x8000..=0x9FFF=>self.io_comps.ppu.vram.write_current_bank(address-0x8000, value), 0xA000..=0xBFFF=>self.mbc.write_external_ram(address-0xA000,value), - 0xC000..=0xCFFF =>self.ram.write_bank0(address - 0xC000,value), - 0xE000..=0xFDFF=>self.ram.write_bank0(address - 0xE000,value), - 0xD000..=0xDFFF=>self.ram.write_current_bank(address-0xD000,value), + 0xC000..=0xCFFF =>self.io_comps.ram.write_bank0(address - 0xC000,value), + 0xE000..=0xFDFF=>self.io_comps.ram.write_bank0(address - 0xE000,value), + 0xD000..=0xDFFF=>self.io_comps.ram.write_current_bank(address-0xD000,value), 0xFE00..=0xFE9F=>self.io_comps.ppu.sprite_attribute_table[(address-0xFE00) as usize] = value, 0xFEA0..=0xFEFF=>{}, 0xFF00..=0xFF7F=>self.io_comps.ports.write_unprotected(address - 0xFF00, value), @@ -140,29 +137,21 @@ impl<'a, D:AudioDevice> UnprotectedMemory for GbMmu<'a, D>{ impl<'a, D:AudioDevice> GbMmu<'a, D>{ pub fn new_with_bootrom(mbc:&'a mut Box, boot_rom:[u8;BOOT_ROM_SIZE], apu:GbApu)->Self{ GbMmu{ - ram:Ram::default(), io_comps:IoComps::new(apu), mbc:mbc, hram:[0;HRAM_SIZE], interupt_enable_register:0, boot_rom:boot_rom, - finished_boot:false, - ppu_state:PpuState::OamSearch, - dma_state: None } } pub fn new(mbc:&'a mut Box, apu:GbApu)->Self{ let mut mmu = GbMmu{ - ram:Ram::default(), io_comps:IoComps::new(apu), mbc:mbc, hram:[0;HRAM_SIZE], interupt_enable_register:0, boot_rom:[0;BOOT_ROM_SIZE], - finished_boot:true, - ppu_state:PpuState::OamSearch, - dma_state:None }; //Setting the bootrom register to be set (the boot sequence has over) @@ -171,13 +160,33 @@ impl<'a, D:AudioDevice> GbMmu<'a, D>{ mmu } + pub fn cycle(&mut self, cycles:u8){ + self.handle_dma_trasnfer(cycles); + self.io_comps.cycle(cycles as u32); + } + + fn handle_dma_trasnfer(&mut self, cycles: u8) { + if self.io_comps.dma.enable.is_some(){ + let cycles_to_run = std::cmp::min(self.io_comps.dma.dma_cycle_counter + cycles as u16, DMA_SIZE); + for i in self.io_comps.dma.dma_cycle_counter..cycles_to_run as u16{ + self.write_unprotected(DMA_DEST + i, self.read_unprotected(self.io_comps.dma.soure_address + i)); + } + + self.io_comps.dma.dma_cycle_counter += cycles as u16; + if self.io_comps.dma.dma_cycle_counter >= DMA_SIZE{ + self.io_comps.dma.dma_cycle_counter = 0; + self.io_comps.dma.enable = Option::None; + } + } + } + fn is_oam_ready_for_io(&self)->bool{ - let ppu_state = self.ppu_state as u8; + let ppu_state = self.io_comps.ppu.state as u8; return ppu_state != PpuState::OamSearch as u8 && ppu_state != PpuState::PixelTransfer as u8 } fn is_vram_ready_for_io(&self)->bool{ - return self.ppu_state as u8 != PpuState::PixelTransfer as u8; + return self.io_comps.ppu.state as u8 != PpuState::PixelTransfer as u8; } fn bad_dma_read(address:u16)->u8{ diff --git a/lib_gb/src/mmu/io_comps.rs b/lib_gb/src/mmu/io_comps.rs index 6a205061..d9dd933e 100644 --- a/lib_gb/src/mmu/io_comps.rs +++ b/lib_gb/src/mmu/io_comps.rs @@ -2,14 +2,17 @@ use crate::{apu::{audio_device::AudioDevice, gb_apu::GbApu, set_nr11, set_nr12, use crate::ppu::gb_ppu::GbPpu; use crate::apu::*; use crate::timer::gb_timer::GbTimer; -use super::{io_ports::IoPorts, memory::{Memory, UnprotectedMemory}}; +use super::{access_bus::AccessBus, io_ports::IoPorts, memory::{Memory, UnprotectedMemory}, oam_dma_transferer::OamDmaTransferer, ram::Ram}; use super::io_ports::*; pub struct IoComps{ + pub ram: Ram, pub apu: GbApu, pub timer: GbTimer, pub ppu:GbPpu, pub ports:IoPorts, + pub dma:OamDmaTransferer, + pub finished_boot:bool, } io_port_index!(LCDC_REGISTER_INDEX, LCDC_REGISTER_ADDRESS); @@ -18,8 +21,10 @@ io_port_index!(SCY_REGISTER_INDEX, SCY_REGISTER_ADDRESS); io_port_index!(SCX_REGISTER_INDEX, SCX_REGISTER_ADDRESS); io_port_index!(LY_REGISTER_INDEX, LY_REGISTER_ADDRESS); io_port_index!(LYC_REGISTER_INDEX, LYC_REGISTER_ADDRESS); +io_port_index!(DMA_REGISTER_INDEX, DMA_REGISTER_ADDRESS); io_port_index!(WY_REGISTER_INDEX, WY_REGISTER_ADDRESS); io_port_index!(WX_REGISTER_INDEX, WX_REGISTER_ADDRESS); +io_port_index!(BOOT_REGISTER_INDEX, BOOT_REGISTER_ADDRESS); io_port_index!(BGP_REGISTER_INDEX, BGP_REGISTER_ADDRESS); io_port_index!(OBP0_REGISTER_INDEX, OBP0_REGISTER_ADDRESS); io_port_index!(OBP1_REGISTER_INDEX, OBP1_REGISTER_ADDRESS); @@ -78,11 +83,22 @@ impl Memory for IoComps{ SCY_REGISTER_INDEX=> set_scy(&mut self.ppu, value), SCX_REGISTER_INDEX=> set_scx(&mut self.ppu, value), LYC_REGISTER_INDEX=> set_lyc(&mut self.ppu, value), + DMA_REGISTER_INDEX=>{ + let address = (value as u16) << 8; + self.dma.soure_address = address; + self.dma.enable = match value{ + 0..=0x7F=>Some(AccessBus::External), + 0x80..=0x9F=>Some(AccessBus::Video), + 0xA0..=0xFF=>Some(AccessBus::External) + } + } BGP_REGISTER_INDEX=> handle_bg_pallet_register(value,&mut self.ppu.bg_color_mapping), OBP0_REGISTER_INDEX=> handle_obp_pallet_register(value,&mut self.ppu.obj_color_mapping0), OBP1_REGISTER_INDEX=> handle_obp_pallet_register(value,&mut self.ppu.obj_color_mapping1), WY_REGISTER_INDEX=> handle_wy_register(value, &mut self.ppu), WX_REGISTER_INDEX=> handle_wx_register(value, &mut self.ppu), + BOOT_REGISTER_INDEX=> self.finished_boot = value != 0, + // TODO: handle gbc registers (expecailly ram and vram) _=>{} } @@ -92,7 +108,7 @@ impl Memory for IoComps{ impl IoComps{ pub fn new(apu:GbApu)->Self{ - Self{apu, ports:IoPorts::default(), timer:GbTimer::default(), ppu:GbPpu::default()} + Self{apu, ports:IoPorts::default(), timer:GbTimer::default(), ppu:GbPpu::default(), dma:OamDmaTransferer::default(),finished_boot:false, ram:Ram::default()} } pub fn cycle(&mut self, cycles:u32){ diff --git a/lib_gb/src/mmu/mmu_register_updater.rs b/lib_gb/src/mmu/mmu_register_updater.rs index 1acb1b66..c82d3cd7 100644 --- a/lib_gb/src/mmu/mmu_register_updater.rs +++ b/lib_gb/src/mmu/mmu_register_updater.rs @@ -14,21 +14,21 @@ pub fn update_mmu_registers(memory: &mut GbMmu,dma:&mut OamDma handle_dma_transfer_register(memory.read_unprotected(DMA_REGISTER_ADDRESS), dma, memory); } else{ - memory.dma_state = dma.enable; + // memory.dma_state = dma.enable; } } fn handle_ppu_state(memory:&mut GbMmu, stat:u8){ - memory.ppu_state = PpuState::from_u8(stat & 0b0000_0011); + // memory.ppu_state = PpuState::from_u8(stat & 0b0000_0011); } fn handle_wram_register(memory: &mut GbMmu, register:u8){ let bank:u8 = register & 0b00000111; - memory.ram.set_bank(bank); + // memory.ram.set_bank(bank); } fn handle_bootrom_register(memory: &mut GbMmu, register:u8){ - memory.finished_boot = register == 1; + // memory.finished_boot = register == 1; } fn handle_dma_transfer_register(register:u8, dma: &mut OamDmaTransferer, mmu:&mut GbMmu){ @@ -39,5 +39,5 @@ fn handle_dma_transfer_register(register:u8, dma: &mut OamDmaTran 0xA0..=0xFF=>Some(AccessBus::External) }; - mmu.dma_state = dma.enable; + // mmu.dma_state = dma.enable; } \ No newline at end of file diff --git a/lib_gb/src/mmu/oam_dma_transferer.rs b/lib_gb/src/mmu/oam_dma_transferer.rs index a24034e6..6768dc70 100644 --- a/lib_gb/src/mmu/oam_dma_transferer.rs +++ b/lib_gb/src/mmu/oam_dma_transferer.rs @@ -1,34 +1,13 @@ -use super::{access_bus::AccessBus, memory::UnprotectedMemory}; - -const DMA_SIZE:u16 = 0xA0; -const DMA_DEST:u16 = 0xFE00; +use super::access_bus::AccessBus; pub struct OamDmaTransferer{ pub soure_address:u16, pub enable:Option, - dma_cycle_counter:u16 + pub dma_cycle_counter:u16 } impl Default for OamDmaTransferer{ fn default() -> Self { OamDmaTransferer{dma_cycle_counter:0, enable:None, soure_address:0} } -} - -impl OamDmaTransferer{ - pub fn cycle(&mut self,memory:&mut impl UnprotectedMemory, m_cycles:u8){ - if self.enable.is_some(){ - let cycles_to_run = std::cmp::min(self.dma_cycle_counter + m_cycles as u16, DMA_SIZE); - for i in self.dma_cycle_counter..cycles_to_run as u16{ - memory.write_unprotected(DMA_DEST + i, memory.read_unprotected(self.soure_address + i)); - } - - self.dma_cycle_counter += m_cycles as u16; - - if self.dma_cycle_counter >= DMA_SIZE{ - self.enable = None; - self.dma_cycle_counter = 0; - } - } - } } \ No newline at end of file diff --git a/lib_gb/src/mmu/vram.rs b/lib_gb/src/mmu/vram.rs index 5395140a..d1ba629d 100644 --- a/lib_gb/src/mmu/vram.rs +++ b/lib_gb/src/mmu/vram.rs @@ -6,8 +6,7 @@ pub struct VRam{ } impl VRam{ - pub fn set_bank(&mut self, bank:u8) - { + pub fn set_bank(&mut self, bank:u8){ self.current_bank_register = bank; } From 2a967717243bd04113b172d8c17356622568493a Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 28 May 2021 19:17:03 +0300 Subject: [PATCH 013/136] Remove redundant code after the change --- lib_gb/src/apu/apu_registers_updater.rs | 194 +-------------------- lib_gb/src/mmu/mmu_register_updater.rs | 43 ----- lib_gb/src/mmu/mod.rs | 3 +- lib_gb/src/ppu/ppu_register_updater.rs | 41 +---- lib_gb/src/timer/timer_register_updater.rs | 15 -- lib_gb/src/utils/memory_registers.rs | 1 - 6 files changed, 3 insertions(+), 294 deletions(-) delete mode 100644 lib_gb/src/mmu/mmu_register_updater.rs diff --git a/lib_gb/src/apu/apu_registers_updater.rs b/lib_gb/src/apu/apu_registers_updater.rs index 92940cb9..45a4a5d9 100644 --- a/lib_gb/src/apu/apu_registers_updater.rs +++ b/lib_gb/src/apu/apu_registers_updater.rs @@ -1,4 +1,4 @@ -use crate::{mmu::{gb_mmu::GbMmu, memory::UnprotectedMemory, io_ports::*}, utils::{bit_masks::*, memory_registers::*}}; +use crate::{mmu::{memory::UnprotectedMemory, io_ports::*}, utils::bit_masks::*}; use super::{ audio_device::AudioDevice, @@ -14,50 +14,6 @@ use super::{ sound_utils::NUMBER_OF_CHANNELS }; -// pub fn update_apu_registers(memory:&mut GbMmu, apu:&mut GbApu){ -// prepare_control_registers(apu, memory); - -// if apu.enabled{ -// prepare_wave_channel(&mut apu.wave_channel, memory, &apu.frame_sequencer); -// prepare_tone_sweep_channel(&mut apu.sweep_tone_channel, memory, &apu.frame_sequencer); -// prepare_noise_channel(&mut apu.noise_channel, memory, &apu.frame_sequencer); -// prepare_tone_channel(&mut apu.tone_channel, memory, &apu.frame_sequencer); -// } -// } - -// fn prepare_tone_channel(channel:&mut Channel, memory:&mut GbMmu,fs:&FrameSequencer){ - -// if memory.io_ports.get_ports_cycle_trigger()[NR21_REGISTER_INDEX as usize]{ -// channel.sound_length = 64 - (memory.read_unprotected(NR21_REGISTER_ADDRESS) & 0b11_1111) as u16; -// } -// if memory.io_ports.get_ports_cycle_trigger()[NR22_REGISTER_INDEX as usize]{ -// update_volume_envelope(memory.read_unprotected(NR22_REGISTER_ADDRESS), &mut channel.sample_producer.envelop); - -// if !is_dac_enabled(channel.sample_producer.envelop.volume, channel.sample_producer.envelop.increase_envelope){ -// channel.enabled = false; -// } -// } -// if memory.io_ports.get_ports_cycle_trigger()[NR23_REGISTER_INDEX as usize]{ -// //discard lower bit -// channel.frequency &= 0xFF00; -// channel.frequency |= memory.read_unprotected(NR23_REGISTER_ADDRESS) as u16; -// } -// if memory.io_ports.get_ports_cycle_trigger()[NR24_REGISTER_INDEX as usize]{ -// let nr24 = memory.read_unprotected(NR24_REGISTER_ADDRESS); -// //discrad upper bit -// channel.frequency <<= 8; -// channel.frequency >>= 8; -// channel.frequency |= (nr24 as u16 & 0b111) << 8; -// let dac_enabled = is_dac_enabled(channel.sample_producer.envelop.volume, channel.sample_producer.envelop.increase_envelope); -// update_channel_conrol_register(channel, dac_enabled, nr24, 64, fs); -// if nr24 & BIT_7_MASK != 0{ -// //volume -// channel.sample_producer.envelop.envelop_duration_counter = channel.sample_producer.envelop.number_of_envelope_sweep; -// channel.sample_producer.envelop.current_volume = channel.sample_producer.envelop.volume; -// } -// } -// } - pub fn set_nr41(channel:&mut Channel, value:u8){ let length_data = value & 0b11_1111; @@ -88,39 +44,6 @@ pub fn set_nr44(channel:&mut Channel, fs:&FrameSequencer, n } } -// fn prepare_noise_channel(channel:&mut Channel, memory:&mut GbMmu,fs:&FrameSequencer){ - -// if memory.io_ports.get_ports_cycle_trigger()[NR41_REGISTER_INDEX as usize]{ -// let length_data = memory.read_unprotected(NR41_REGISTER_ADDRESS) & 0b11_1111; -// channel.sound_length = 64 - length_data as u16 -// } -// if memory.io_ports.get_ports_cycle_trigger()[NR42_REGISTER_INDEX as usize]{ -// update_volume_envelope( memory.read_unprotected(NR42_REGISTER_ADDRESS), &mut channel.sample_producer.envelop); -// if !is_dac_enabled(channel.sample_producer.envelop.volume, channel.sample_producer.envelop.increase_envelope){ -// channel.enabled = false; -// } -// } -// if memory.io_ports.get_ports_cycle_trigger()[NR43_REGISTER_INDEX as usize]{ -// let nr43 = memory.read_unprotected(NR43_REGISTER_ADDRESS); -// channel.sample_producer.bits_to_shift_divisor = (nr43 & 0b1111_0000) >> 4; -// channel.sample_producer.width_mode = (nr43 & BIT_3_MASK) != 0; -// channel.sample_producer.divisor_code = nr43 & 0b111; -// } -// if memory.io_ports.get_ports_cycle_trigger()[NR44_REGISTER_INDEX as usize]{ -// let nr44 = memory.read_unprotected(NR44_REGISTER_ADDRESS); -// let dac_enabled = is_dac_enabled(channel.sample_producer.envelop.volume, channel.sample_producer.envelop.increase_envelope); -// update_channel_conrol_register(channel, dac_enabled, nr44, 64, fs); -// if (nr44 & BIT_7_MASK) != 0{ -// //On trigger all the LFSR bits are set (lfsr is 15 bit register) -// channel.sample_producer.lfsr = 0x7FFF; - - -// channel.sample_producer.envelop.current_volume = channel.sample_producer.envelop.volume; -// } -// } -// } - - pub fn set_nr50(apu:&mut GbApu, nr50:u8){ apu.right_terminal.enabled = nr50 & BIT_3_MASK != 0; apu.left_terminal.enabled = nr50 & BIT_7_MASK != 0; @@ -154,27 +77,6 @@ pub fn get_nr52(apu:&GbApu, nr52:&mut u8){ set_bit_u8(nr52, 0, apu.sweep_tone_channel.enabled && apu.sweep_tone_channel.length_enable && apu.sweep_tone_channel.sound_length != 0); } -fn prepare_control_registers(apu:&mut GbApu, memory:&impl UnprotectedMemory){ - let channel_control = memory.read_unprotected(NR50_REGISTER_ADDRESS); - apu.right_terminal.enabled = channel_control & BIT_3_MASK != 0; - apu.left_terminal.enabled = channel_control & BIT_7_MASK != 0; - - apu.right_terminal.volume = channel_control & 0b111; - apu.left_terminal.volume = (channel_control & 0b111_0000) >> 4; - - let channels_output_terminals = memory.read_unprotected(NR51_REGISTER_ADDRESS); - - for i in 0..NUMBER_OF_CHANNELS{ - apu.right_terminal.channels[i as usize] = channels_output_terminals & (1 << i) != 0; - } - for i in 0..NUMBER_OF_CHANNELS{ - apu.left_terminal.channels[i as usize] = channels_output_terminals & (0b1_0000 << i) != 0; - } - - let master_sound = memory.read_unprotected(NR52_REGISTER_ADDRESS); - apu.enabled = master_sound & BIT_7_MASK != 0; -} - pub fn set_nr30(channel:&mut Channel, value:u8){ if (value & BIT_7_MASK) == 0{ channel.enabled = false; @@ -209,46 +111,6 @@ pub fn set_nr34(channel:&mut Channel, fs:&FrameSequencer, nr } } -// fn prepare_wave_channel(channel:&mut Channel, memory:&mut GbMmu,fs:&FrameSequencer){ - -// if memory.io_ports.get_ports_cycle_trigger()[NR30_REGISTER_INDEX as usize]{ -// if (memory.read_unprotected(NR30_REGISTER_ADDRESS) & BIT_7_MASK) == 0{ -// channel.enabled = false; -// } -// } -// if memory.io_ports.get_ports_cycle_trigger()[NR31_REGISTER_INDEX as usize]{ -// channel.sound_length = 256 - (memory.read_unprotected(NR31_REGISTER_ADDRESS) as u16); -// } -// if memory.io_ports.get_ports_cycle_trigger()[NR32_REGISTER_INDEX as usize]{ -// //I want bits 5-6 -// let nr32 = memory.read_unprotected(NR32_REGISTER_ADDRESS); -// channel.sample_producer.volume = (nr32 & 0b110_0000) >> 5; -// } -// if memory.io_ports.get_ports_cycle_trigger()[NR33_REGISTER_INDEX as usize]{ -// //discard lower 8 bits -// channel.frequency &= 0xFF00; -// channel.frequency |= memory.read_unprotected(NR33_REGISTER_ADDRESS) as u16; -// } -// if memory.io_ports.get_ports_cycle_trigger()[NR34_REGISTER_INDEX as usize]{ -// let nr34 = memory.read_unprotected(NR34_REGISTER_ADDRESS); - -// //clear the upper 8 bits -// channel.frequency &= 0xFF; -// channel.frequency |= ((nr34 & 0b111) as u16) << 8; - -// let dac_enabled = (memory.read_unprotected(NR30_REGISTER_ADDRESS) & BIT_7_MASK) != 0; -// update_channel_conrol_register(channel, dac_enabled, nr34, 256, fs); - -// if nr34 & BIT_7_MASK != 0{ -// channel.sample_producer.reset_counter(); -// } -// } - -// for i in 0..=0xF{ -// channel.sample_producer.wave_samples[i] = memory.read_unprotected(0xFF30 + i as u16); -// } -// } - pub fn set_nr10(channel:&mut Channel, value:u8){ let sweep = channel.sample_producer.sweep.as_mut().unwrap(); sweep.sweep_decrease = (value & 0b1000) != 0; @@ -311,60 +173,6 @@ pub fn set_nr24(channel:&mut Channel, fs:&FrameSequencer, channel.sample_producer.envelop.current_volume = channel.sample_producer.envelop.volume; } } -// fn prepare_tone_sweep_channel(channel:&mut Channel, memory:&mut GbMmu, fs:&FrameSequencer){ -// let nr10 = memory.read_unprotected(NR10_REGISTER_ADDRESS); -// let nr11 = memory.read_unprotected(NR11_REGISTER_ADDRESS); -// let nr12 = memory.read_unprotected(NR12_REGISTER_ADDRESS); -// let nr13 = memory.read_unprotected(NR13_REGISTER_ADDRESS); -// let nr14 = memory.read_unprotected(NR14_REGISTER_ADDRESS); - -// if memory.io_ports.get_ports_cycle_trigger()[NR10_REGISTER_INDEX as usize]{ -// //sweep -// let sweep = channel.sample_producer.sweep.as_mut().unwrap(); -// sweep.sweep_decrease = (nr10 & 0b1000) != 0; -// sweep.sweep_shift = nr10 & 0b111; -// sweep.sweep_period = (nr10 & 0b111_0000) >> 4; -// } -// if memory.io_ports.get_ports_cycle_trigger()[NR11_REGISTER_INDEX as usize]{ -// channel.sample_producer.wave_duty = (nr11 & 0b1100_0000) >> 6; -// channel.sound_length = 64 - (nr11 & 0b11_1111) as u16 -// } -// if memory.io_ports.get_ports_cycle_trigger()[NR12_REGISTER_INDEX as usize]{ -// update_volume_envelope(nr12, &mut channel.sample_producer.envelop); - -// if !is_dac_enabled(channel.sample_producer.envelop.volume, channel.sample_producer.envelop.increase_envelope){ -// channel.enabled = false; -// } -// } -// if memory.io_ports.get_ports_cycle_trigger()[NR13_REGISTER_INDEX as usize]{ -// //discard lower bits -// channel.frequency &= 0xFF00; -// channel.frequency |= nr13 as u16; -// } -// if memory.io_ports.get_ports_cycle_trigger()[NR14_REGISTER_INDEX as usize]{ -// //discard upper bits -// channel.frequency &= 0xFF; -// channel.frequency |= ((nr14 & 0b111) as u16) << 8; - -// let dac_enabled = is_dac_enabled(channel.sample_producer.envelop.volume, channel.sample_producer.envelop.increase_envelope); -// update_channel_conrol_register(channel, dac_enabled, nr14, 64, fs); - -// if nr14 & BIT_7_MASK != 0{ -// //volume -// channel.sample_producer.envelop.envelop_duration_counter = channel.sample_producer.envelop.number_of_envelope_sweep; -// channel.sample_producer.envelop.current_volume = channel.sample_producer.envelop.volume; - -// //sweep -// let sweep = channel.sample_producer.sweep.as_mut().unwrap(); -// sweep.channel_trigger(channel.frequency); -// if sweep.sweep_shift > 0{ -// let freq = sweep.calculate_new_frequency(); -// channel.enabled = !FreqSweep::check_overflow(freq); -// } - -// } -// } -// } fn update_channel_conrol_register(channel:&mut Channel, dac_enabled:bool, control_register:u8, max_sound_length:u16, fs:&FrameSequencer){ diff --git a/lib_gb/src/mmu/mmu_register_updater.rs b/lib_gb/src/mmu/mmu_register_updater.rs deleted file mode 100644 index c82d3cd7..00000000 --- a/lib_gb/src/mmu/mmu_register_updater.rs +++ /dev/null @@ -1,43 +0,0 @@ -use crate::{apu::audio_device::AudioDevice, ppu::ppu_state::PpuState, utils::memory_registers::*}; -use super::{access_bus::AccessBus, gb_mmu::GbMmu, io_ports::IO_PORTS_MEMORY_OFFSET, memory::UnprotectedMemory, oam_dma_transferer::OamDmaTransferer}; - -const DMA_REGISTER_INDEX:usize = (DMA_REGISTER_ADDRESS - IO_PORTS_MEMORY_OFFSET) as usize; - -pub fn update_mmu_registers(memory: &mut GbMmu,dma:&mut OamDmaTransferer){ - - handle_ppu_state(memory, memory.read_unprotected(STAT_REGISTER_ADDRESS)); - handle_wram_register(memory, memory.read_unprotected(SVBK_REGISTER_ADDRESS)); - handle_bootrom_register(memory, memory.read_unprotected(BOOT_REGISTER_ADDRESS)); - let ports = memory.io_comps.ports.get_ports_cycle_trigger(); - if ports[DMA_REGISTER_INDEX]{ - ports[DMA_REGISTER_INDEX] = false; - handle_dma_transfer_register(memory.read_unprotected(DMA_REGISTER_ADDRESS), dma, memory); - } - else{ - // memory.dma_state = dma.enable; - } -} - -fn handle_ppu_state(memory:&mut GbMmu, stat:u8){ - // memory.ppu_state = PpuState::from_u8(stat & 0b0000_0011); -} - -fn handle_wram_register(memory: &mut GbMmu, register:u8){ - let bank:u8 = register & 0b00000111; - // memory.ram.set_bank(bank); -} - -fn handle_bootrom_register(memory: &mut GbMmu, register:u8){ - // memory.finished_boot = register == 1; -} - -fn handle_dma_transfer_register(register:u8, dma: &mut OamDmaTransferer, mmu:&mut GbMmu){ - dma.soure_address = (register as u16) << 8; - dma.enable = match register{ - 0..=0x7F=>Some(AccessBus::External), - 0x80..=0x9F=>Some(AccessBus::Video), - 0xA0..=0xFF=>Some(AccessBus::External) - }; - - // mmu.dma_state = dma.enable; -} \ No newline at end of file diff --git a/lib_gb/src/mmu/mod.rs b/lib_gb/src/mmu/mod.rs index 77a98784..088648b6 100644 --- a/lib_gb/src/mmu/mod.rs +++ b/lib_gb/src/mmu/mod.rs @@ -5,7 +5,6 @@ pub mod vram; #[macro_use] pub mod io_ports; pub mod carts; -pub mod access_bus; -pub mod mmu_register_updater; +pub mod access_bus; pub mod oam_dma_transferer; pub mod io_comps; \ No newline at end of file diff --git a/lib_gb/src/ppu/ppu_register_updater.rs b/lib_gb/src/ppu/ppu_register_updater.rs index b0ebabd2..44d28a72 100644 --- a/lib_gb/src/ppu/ppu_register_updater.rs +++ b/lib_gb/src/ppu/ppu_register_updater.rs @@ -1,47 +1,8 @@ -use crate::{apu::audio_device::AudioDevice, mmu::{gb_mmu::GbMmu, io_ports::IO_PORTS_MEMORY_OFFSET, memory::UnprotectedMemory}}; -use crate::utils::{memory_registers::*, bit_masks::*}; +use crate::utils::bit_masks::*; use super::{ gb_ppu::GbPpu, color::*, colors::*}; const WX_OFFSET:u8 = 7; -const LCDC_REGISTER_INDEX:usize = (LCDC_REGISTER_ADDRESS - IO_PORTS_MEMORY_OFFSET) as usize; -const STAT_REGISTER_INDEX:usize = (STAT_REGISTER_ADDRESS - IO_PORTS_MEMORY_OFFSET) as usize; -const SCX_REGISTER_INDEX:usize = (SCX_REGISTER_ADDRESS - IO_PORTS_MEMORY_OFFSET) as usize; -const SCY_REGISTER_INDEX:usize = (SCY_REGISTER_ADDRESS - IO_PORTS_MEMORY_OFFSET) as usize; -const BGP_REGISTER_INDEX:usize = (BGP_REGISTER_ADDRESS - IO_PORTS_MEMORY_OFFSET) as usize; -const OBP0_REGISTER_INDEX:usize = (OBP0_REGISTER_ADDRESS - IO_PORTS_MEMORY_OFFSET) as usize; -const OBP1_REGISTER_INDEX:usize = (OBP1_REGISTER_ADDRESS - IO_PORTS_MEMORY_OFFSET) as usize; -const WX_REGISTER_INDEX:usize = (WX_REGISTER_ADDRESS - IO_PORTS_MEMORY_OFFSET) as usize; -const WY_REGISTER_INDEX:usize = (WY_REGISTER_ADDRESS - IO_PORTS_MEMORY_OFFSET) as usize; - -pub fn update_ppu_regsiters(memory:&mut GbMmu, ppu: &mut GbPpu){ - if memory.io_comps.ports.get_ports_cycle_trigger()[LCDC_REGISTER_INDEX] { - handle_lcdcontrol_register(memory.read_unprotected(LCDC_REGISTER_ADDRESS), ppu); - } - if memory.io_comps.ports.get_ports_cycle_trigger()[STAT_REGISTER_INDEX]{ - update_stat_register(memory.read_unprotected(STAT_REGISTER_ADDRESS), ppu); - } - if memory.io_comps.ports.get_ports_cycle_trigger()[SCX_REGISTER_INDEX] || memory.io_comps.ports.get_ports_cycle_trigger()[SCY_REGISTER_INDEX]{ - handle_scroll_registers(memory.read_unprotected(SCX_REGISTER_ADDRESS), memory.read_unprotected(SCY_REGISTER_ADDRESS), ppu); - } - if memory.io_comps.ports.get_ports_cycle_trigger()[BGP_REGISTER_INDEX]{ - handle_bg_pallet_register(memory.read_unprotected(BGP_REGISTER_ADDRESS), &mut ppu.bg_color_mapping); - } - if memory.io_comps.ports.get_ports_cycle_trigger()[OBP0_REGISTER_INDEX]{ - handle_obp_pallet_register(memory.read_unprotected(OBP0_REGISTER_ADDRESS), &mut ppu.obj_color_mapping0); - } - if memory.io_comps.ports.get_ports_cycle_trigger()[OBP1_REGISTER_INDEX]{ - handle_obp_pallet_register(memory.read_unprotected(OBP1_REGISTER_ADDRESS), &mut ppu.obj_color_mapping1); - } - if memory.io_comps.ports.get_ports_cycle_trigger()[WY_REGISTER_INDEX]{ - handle_wy_register(memory.read_unprotected(WY_REGISTER_ADDRESS), ppu); - } - if memory.io_comps.ports.get_ports_cycle_trigger()[WX_REGISTER_INDEX]{ - handle_wx_register(memory.read_unprotected(WX_REGISTER_ADDRESS), ppu); - } -} - - pub fn handle_lcdcontrol_register( register:u8, ppu:&mut GbPpu){ ppu.screen_enable = (register & BIT_7_MASK) != 0; ppu.window_tile_map_address = (register & BIT_6_MASK) != 0; diff --git a/lib_gb/src/timer/timer_register_updater.rs b/lib_gb/src/timer/timer_register_updater.rs index 38c1ade8..b925f3e6 100644 --- a/lib_gb/src/timer/timer_register_updater.rs +++ b/lib_gb/src/timer/timer_register_updater.rs @@ -1,20 +1,5 @@ -use crate::{mmu::io_ports::{IoPorts, DIV_REGISTER_INDEX, IO_PORTS_MEMORY_OFFSET}, utils::memory_registers::TIMA_REGISTER_ADDRESS}; use super::gb_timer::GbTimer; -const TIMA_REGISTER_INDEX: usize = (TIMA_REGISTER_ADDRESS - IO_PORTS_MEMORY_OFFSET) as usize; - -pub fn update_timer_registers(timer:&mut GbTimer, memory:&mut IoPorts){ - let ports = memory.get_ports_cycle_trigger(); - if ports[DIV_REGISTER_INDEX as usize]{ - timer.system_counter = 0; - ports[DIV_REGISTER_INDEX as usize] = false; - } - if ports[TIMA_REGISTER_INDEX]{ - timer.tima_overflow = false; - ports[TIMA_REGISTER_INDEX] = false; - } -} - pub fn get_div(timer: &GbTimer)->u8{ (timer.system_counter >> 8) as u8 } diff --git a/lib_gb/src/utils/memory_registers.rs b/lib_gb/src/utils/memory_registers.rs index df69b742..3386a5ff 100644 --- a/lib_gb/src/utils/memory_registers.rs +++ b/lib_gb/src/utils/memory_registers.rs @@ -38,5 +38,4 @@ pub const OBP1_REGISTER_ADDRESS:u16 = 0xFF49; pub const WY_REGISTER_ADDRESS:u16 = 0xFF4A; pub const WX_REGISTER_ADDRESS:u16 = 0xFF4B; pub const BOOT_REGISTER_ADDRESS:u16 = 0xFF50; -pub const SVBK_REGISTER_ADDRESS:u16 = 0xFF70; pub const IE_REGISTER_ADDRESS:u16 = 0xFFFF; \ No newline at end of file From 688a0510bfe7436ecbe6ca1bf6ff1ad1678680db Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 28 May 2021 19:50:22 +0300 Subject: [PATCH 014/136] Remove more redundant io ports code --- lib_gb/src/mmu/io_ports.rs | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/lib_gb/src/mmu/io_ports.rs b/lib_gb/src/mmu/io_ports.rs index 85827751..7e17a200 100644 --- a/lib_gb/src/mmu/io_ports.rs +++ b/lib_gb/src/mmu/io_ports.rs @@ -47,8 +47,7 @@ pub_io_port_index!(NR51_REGISTER_INDEX, NR51_REGISTER_ADDRESS); pub_io_port_index!(NR52_REGISTER_INDEX, NR52_REGISTER_ADDRESS); pub struct IoPorts{ - ports:[u8;IO_PORTS_SIZE], - ports_cycle_trigger:[bool; IO_PORTS_SIZE] + ports:[u8;IO_PORTS_SIZE] } impl Memory for IoPorts{ @@ -93,8 +92,6 @@ impl Memory for IoPorts{ } _=>{} } - - self.ports_cycle_trigger[address as usize] = true; self.ports[address as usize] = value; } @@ -110,21 +107,10 @@ impl UnprotectedMemory for IoPorts{ } } -impl IoPorts{ - pub fn get_ports_cycle_trigger(&mut self)->&mut [bool; IO_PORTS_SIZE]{ - return &mut self.ports_cycle_trigger; - } - - pub fn clear_io_ports_triggers(&mut self){ - unsafe{std::ptr::write_bytes::(self.ports_cycle_trigger.as_mut_ptr(), 0, IO_PORTS_SIZE);} - } -} - impl Default for IoPorts{ fn default()->Self{ let mut io_ports = IoPorts{ - ports:[0;IO_PORTS_SIZE], - ports_cycle_trigger:[false;IO_PORTS_SIZE] + ports:[0;IO_PORTS_SIZE] }; //joypad register initiall value From 6913c288cb10d2ca6c4b80e9569b092ecdfa6ca5 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 29 May 2021 00:00:12 +0300 Subject: [PATCH 015/136] CR fixes * Indentation * Comments --- lib_gb/src/apu/apu_registers_updater.rs | 5 +++-- lib_gb/src/machine/gameboy.rs | 8 +++++++- lib_gb/src/machine/interrupts_handler.rs | 1 + 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/lib_gb/src/apu/apu_registers_updater.rs b/lib_gb/src/apu/apu_registers_updater.rs index 45a4a5d9..646c6c10 100644 --- a/lib_gb/src/apu/apu_registers_updater.rs +++ b/lib_gb/src/apu/apu_registers_updater.rs @@ -128,13 +128,14 @@ pub fn set_nr11(channel:&mut Channel, value:u8){ if !is_dac_enabled(channel.sample_producer.envelop.volume, channel.sample_producer.envelop.increase_envelope){ channel.enabled = false; } - } +} pub fn set_nr13(channel:&mut Channel, value:u8){ //discard lower bits channel.frequency &= 0xFF00; channel.frequency |= value as u16; - } +} + pub fn set_nr14(channel:&mut Channel, fs:&FrameSequencer, nr14:u8){ //discard upper bits channel.frequency &= 0xFF; diff --git a/lib_gb/src/machine/gameboy.rs b/lib_gb/src/machine/gameboy.rs index 945631e2..b63f9269 100644 --- a/lib_gb/src/machine/gameboy.rs +++ b/lib_gb/src/machine/gameboy.rs @@ -1,4 +1,10 @@ -use crate::{apu::{audio_device::AudioDevice, gb_apu::GbApu}, cpu::{gb_cpu::GbCpu, opcodes::opcode_resolver::*}, keypad::{joypad::Joypad, joypad_provider::JoypadProvider, joypad_register_updater}, mmu::{carts::mbc::Mbc, gb_mmu::{GbMmu, BOOT_ROM_SIZE}, memory::Memory}, ppu::{gb_ppu::{CYCLES_PER_FRAME, SCREEN_HEIGHT, SCREEN_WIDTH} }}; +use crate::{ + apu::{audio_device::AudioDevice, gb_apu::GbApu}, + cpu::{gb_cpu::GbCpu, opcodes::opcode_resolver::*}, + keypad::{joypad::Joypad, joypad_provider::JoypadProvider, joypad_register_updater}, + mmu::{carts::mbc::Mbc, gb_mmu::{GbMmu, BOOT_ROM_SIZE}, memory::Memory}, + ppu::{gb_ppu::{CYCLES_PER_FRAME, SCREEN_HEIGHT, SCREEN_WIDTH}} +}; use super::interrupts_handler::InterruptsHandler; use std::boxed::Box; use log::debug; diff --git a/lib_gb/src/machine/interrupts_handler.rs b/lib_gb/src/machine/interrupts_handler.rs index f15927d6..89a53174 100644 --- a/lib_gb/src/machine/interrupts_handler.rs +++ b/lib_gb/src/machine/interrupts_handler.rs @@ -40,6 +40,7 @@ impl InterruptsHandler{ if interupt_flag & BIT_0_MASK != 0 && interupt_enable & BIT_0_MASK != 0{ return Self::prepare_for_interut(cpu, BIT_0_MASK, V_BLANK_INTERRUPT_ADDERESS, memory, &mut interupt_flag); } + // Checking those STAT register bits for the STAT interrupts requests if interupt_flag & BIT_1_MASK != 0 && interupt_enable & BIT_1_MASK != 0 && (stat_register & 0b111_1000) != 0{ return Self::prepare_for_interut(cpu, BIT_1_MASK, LCD_STAT_INTERRUPT_ADDERESS, memory, &mut interupt_flag); } From 545a30d4924b36cebdf930c6bc8533344418f6ed Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 29 May 2021 10:59:09 +0300 Subject: [PATCH 016/136] Rename the file from io_comps to io_components --- lib_gb/src/machine/gameboy.rs | 10 +-- lib_gb/src/mmu/gb_mmu.rs | 78 +++++++++---------- .../src/mmu/{io_comps.rs => io_components.rs} | 6 +- lib_gb/src/mmu/mod.rs | 2 +- 4 files changed, 48 insertions(+), 48 deletions(-) rename lib_gb/src/mmu/{io_comps.rs => io_components.rs} (97%) diff --git a/lib_gb/src/machine/gameboy.rs b/lib_gb/src/machine/gameboy.rs index b63f9269..7340701d 100644 --- a/lib_gb/src/machine/gameboy.rs +++ b/lib_gb/src/machine/gameboy.rs @@ -55,7 +55,7 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ pub fn cycle_frame(&mut self)->&[u32;SCREEN_HEIGHT*SCREEN_WIDTH]{ let mut joypad = Joypad::default(); - let mut last_ppu_power_state:bool = self.mmu.io_comps.ppu.screen_enable; + let mut last_ppu_power_state:bool = self.mmu.io_components.ppu.screen_enable; while self.cycles_counter < CYCLES_PER_FRAME{ self.joypad_provider.provide(&mut joypad); @@ -80,19 +80,19 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ //In case the ppu just turned I want to keep it sync with the actual screen and thats why Im reseting the loop to finish //the frame when the ppu finishes the frame - if !last_ppu_power_state && self.mmu.io_comps.ppu.screen_enable{ + if !last_ppu_power_state && self.mmu.io_components.ppu.screen_enable{ self.cycles_counter = 0; } self.cycles_counter += iter_total_cycles; - last_ppu_power_state = self.mmu.io_comps.ppu.screen_enable; + last_ppu_power_state = self.mmu.io_components.ppu.screen_enable; } if self.cycles_counter >= CYCLES_PER_FRAME{ self.cycles_counter -= CYCLES_PER_FRAME; } - return self.mmu.io_comps.ppu.get_frame_buffer(); + return self.mmu.io_components.ppu.get_frame_buffer(); } fn fetch_next_byte(&mut self)->u8{ @@ -106,7 +106,7 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ let opcode:u8 = self.fetch_next_byte(); //debug - if self.mmu.io_comps.finished_boot{ + if self.mmu.io_components.finished_boot{ let a = *self.cpu.af.high(); let b = *self.cpu.bc.high(); let c = *self.cpu.bc.low(); diff --git a/lib_gb/src/mmu/gb_mmu.rs b/lib_gb/src/mmu/gb_mmu.rs index 5cbd3d05..9d3b8af4 100644 --- a/lib_gb/src/mmu/gb_mmu.rs +++ b/lib_gb/src/mmu/gb_mmu.rs @@ -1,4 +1,4 @@ -use super::{io_comps::IoComps, memory::*}; +use super::{io_components::IoComponents, memory::*}; use super::access_bus::AccessBus; use crate::{apu::{audio_device::AudioDevice, gb_apu::GbApu}, utils::memory_registers::BOOT_REGISTER_ADDRESS}; use super::carts::mbc::Mbc; @@ -13,7 +13,7 @@ const DMA_DEST:u16 = 0xFE00; const BAD_READ_VALUE:u8 = 0xFF; pub struct GbMmu<'a, D:AudioDevice>{ - pub io_comps: IoComps, + pub io_components: IoComponents, boot_rom:[u8;BOOT_ROM_SIZE], mbc: &'a mut Box, hram: [u8;HRAM_SIZE], @@ -24,9 +24,9 @@ pub struct GbMmu<'a, D:AudioDevice>{ //DMA only locks the used bus. there 2 possible used buses: extrnal (wram, rom, sram) and video (vram) impl<'a, D:AudioDevice> Memory for GbMmu<'a, D>{ fn read(&self, address:u16)->u8{ - if let Some (bus) = &self.io_comps.dma.enable{ + if let Some (bus) = &self.io_components.dma.enable{ return match address{ - 0xFF00..=0xFF7F => self.io_comps.read(address - 0xFF00), + 0xFF00..=0xFF7F => self.io_components.read(address - 0xFF00), 0xFEA0..=0xFEFF | 0xFF80..=0xFFFE | 0xFFFF=>self.read_unprotected(address), 0x8000..=0x9FFF => if let AccessBus::External = bus {self.read_unprotected(address)} else{Self::bad_dma_read(address)}, 0..=0x7FFF | 0xA000..=0xFDFF => if let AccessBus::Video = bus {self.read_unprotected(address)} else{Self::bad_dma_read(address)}, @@ -36,7 +36,7 @@ impl<'a, D:AudioDevice> Memory for GbMmu<'a, D>{ return match address{ 0x8000..=0x9FFF=>{ if self.is_vram_ready_for_io(){ - return self.io_comps.ppu.vram.read_current_bank(address-0x8000); + return self.io_components.ppu.vram.read_current_bank(address-0x8000); } else{ log::warn!("bad vram read"); @@ -45,22 +45,22 @@ impl<'a, D:AudioDevice> Memory for GbMmu<'a, D>{ }, 0xFE00..=0xFE9F=>{ if self.is_oam_ready_for_io(){ - return self.io_comps.ppu.sprite_attribute_table[(address-0xFE00) as usize]; + return self.io_components.ppu.sprite_attribute_table[(address-0xFE00) as usize]; } else{ log::warn!("bad oam read"); return BAD_READ_VALUE; } }, - 0xFF00..=0xFF7F => self.io_comps.read(address - 0xFF00), + 0xFF00..=0xFF7F => self.io_components.read(address - 0xFF00), _=>self.read_unprotected(address) }; } fn write(&mut self, address:u16, value:u8){ - if let Some(bus) = &self.io_comps.dma.enable{ + if let Some(bus) = &self.io_components.dma.enable{ match address{ - 0xFF00..=0xFF7F => self.io_comps.write(address- 0xFF00, value), + 0xFF00..=0xFF7F => self.io_components.write(address- 0xFF00, value), 0xFF80..=0xFFFE | 0xFFFF=>self.write_unprotected(address, value), 0x8000..=0x9FFF => if let AccessBus::External = bus {self.write_unprotected(address, value)} else{Self::bad_dma_write(address)}, 0..=0x7FFF | 0xA000..=0xFDFF => if let AccessBus::Video = bus {self.write_unprotected(address, value)} else{Self::bad_dma_write(address)}, @@ -71,7 +71,7 @@ impl<'a, D:AudioDevice> Memory for GbMmu<'a, D>{ match address{ 0x8000..=0x9FFF=>{ if self.is_vram_ready_for_io(){ - self.io_comps.ppu.vram.write_current_bank(address-0x8000, value); + self.io_components.ppu.vram.write_current_bank(address-0x8000, value); } else{ log::warn!("bad vram write") @@ -79,13 +79,13 @@ impl<'a, D:AudioDevice> Memory for GbMmu<'a, D>{ }, 0xFE00..=0xFE9F=>{ if self.is_oam_ready_for_io(){ - self.io_comps.ppu.sprite_attribute_table[(address-0xFE00) as usize] = value; + self.io_components.ppu.sprite_attribute_table[(address-0xFE00) as usize] = value; } else{ log::warn!("bad oam write") } }, - 0xFF00..=0xFF7F=>self.io_comps.write(address - 0xFF00, value), + 0xFF00..=0xFF7F=>self.io_components.write(address - 0xFF00, value), _=>self.write_unprotected(address, value) } } @@ -96,7 +96,7 @@ impl<'a, D:AudioDevice> UnprotectedMemory for GbMmu<'a, D>{ fn read_unprotected(&self, address:u16) ->u8 { return match address{ 0x0..=0xFF=>{ - if self.io_comps.finished_boot{ + if self.io_components.finished_boot{ return self.mbc.read_bank0(address); } @@ -104,14 +104,14 @@ impl<'a, D:AudioDevice> UnprotectedMemory for GbMmu<'a, D>{ }, 0x100..=0x3FFF=>self.mbc.read_bank0(address), 0x4000..=0x7FFF=>self.mbc.read_current_bank(address-0x4000), - 0x8000..=0x9FFF=>self.io_comps.ppu.vram.read_current_bank(address-0x8000), + 0x8000..=0x9FFF=>self.io_components.ppu.vram.read_current_bank(address-0x8000), 0xA000..=0xBFFF=>self.mbc.read_external_ram(address-0xA000), - 0xC000..=0xCFFF =>self.io_comps.ram.read_bank0(address - 0xC000), - 0xD000..=0xDFFF=>self.io_comps.ram.read_current_bank(address-0xD000), - 0xE000..=0xFDFF=>self.io_comps.ram.read_bank0(address - 0xE000), - 0xFE00..=0xFE9F=>self.io_comps.ppu.sprite_attribute_table[(address-0xFE00) as usize], + 0xC000..=0xCFFF =>self.io_components.ram.read_bank0(address - 0xC000), + 0xD000..=0xDFFF=>self.io_components.ram.read_current_bank(address-0xD000), + 0xE000..=0xFDFF=>self.io_components.ram.read_bank0(address - 0xE000), + 0xFE00..=0xFE9F=>self.io_components.ppu.sprite_attribute_table[(address-0xFE00) as usize], 0xFEA0..=0xFEFF=>0x0, - 0xFF00..=0xFF7F=>self.io_comps.ports.read_unprotected(address - 0xFF00), + 0xFF00..=0xFF7F=>self.io_components.ports.read_unprotected(address - 0xFF00), 0xFF80..=0xFFFE=>self.hram[(address-0xFF80) as usize], 0xFFFF=>self.interupt_enable_register }; @@ -120,14 +120,14 @@ impl<'a, D:AudioDevice> UnprotectedMemory for GbMmu<'a, D>{ fn write_unprotected(&mut self, address:u16, value:u8) { match address{ 0x0..=0x7FFF=>self.mbc.write_rom(address, value), - 0x8000..=0x9FFF=>self.io_comps.ppu.vram.write_current_bank(address-0x8000, value), + 0x8000..=0x9FFF=>self.io_components.ppu.vram.write_current_bank(address-0x8000, value), 0xA000..=0xBFFF=>self.mbc.write_external_ram(address-0xA000,value), - 0xC000..=0xCFFF =>self.io_comps.ram.write_bank0(address - 0xC000,value), - 0xE000..=0xFDFF=>self.io_comps.ram.write_bank0(address - 0xE000,value), - 0xD000..=0xDFFF=>self.io_comps.ram.write_current_bank(address-0xD000,value), - 0xFE00..=0xFE9F=>self.io_comps.ppu.sprite_attribute_table[(address-0xFE00) as usize] = value, + 0xC000..=0xCFFF =>self.io_components.ram.write_bank0(address - 0xC000,value), + 0xE000..=0xFDFF=>self.io_components.ram.write_bank0(address - 0xE000,value), + 0xD000..=0xDFFF=>self.io_components.ram.write_current_bank(address-0xD000,value), + 0xFE00..=0xFE9F=>self.io_components.ppu.sprite_attribute_table[(address-0xFE00) as usize] = value, 0xFEA0..=0xFEFF=>{}, - 0xFF00..=0xFF7F=>self.io_comps.ports.write_unprotected(address - 0xFF00, value), + 0xFF00..=0xFF7F=>self.io_components.ports.write_unprotected(address - 0xFF00, value), 0xFF80..=0xFFFE=>self.hram[(address-0xFF80) as usize] = value, 0xFFFF=>self.interupt_enable_register = value } @@ -137,7 +137,7 @@ impl<'a, D:AudioDevice> UnprotectedMemory for GbMmu<'a, D>{ impl<'a, D:AudioDevice> GbMmu<'a, D>{ pub fn new_with_bootrom(mbc:&'a mut Box, boot_rom:[u8;BOOT_ROM_SIZE], apu:GbApu)->Self{ GbMmu{ - io_comps:IoComps::new(apu), + io_components:IoComponents::new(apu), mbc:mbc, hram:[0;HRAM_SIZE], interupt_enable_register:0, @@ -147,7 +147,7 @@ impl<'a, D:AudioDevice> GbMmu<'a, D>{ pub fn new(mbc:&'a mut Box, apu:GbApu)->Self{ let mut mmu = GbMmu{ - io_comps:IoComps::new(apu), + io_components:IoComponents::new(apu), mbc:mbc, hram:[0;HRAM_SIZE], interupt_enable_register:0, @@ -155,38 +155,38 @@ impl<'a, D:AudioDevice> GbMmu<'a, D>{ }; //Setting the bootrom register to be set (the boot sequence has over) - mmu.io_comps.ports.write_unprotected(BOOT_REGISTER_ADDRESS - 0xFF00, 1); + mmu.io_components.ports.write_unprotected(BOOT_REGISTER_ADDRESS - 0xFF00, 1); mmu } pub fn cycle(&mut self, cycles:u8){ self.handle_dma_trasnfer(cycles); - self.io_comps.cycle(cycles as u32); + self.io_components.cycle(cycles as u32); } fn handle_dma_trasnfer(&mut self, cycles: u8) { - if self.io_comps.dma.enable.is_some(){ - let cycles_to_run = std::cmp::min(self.io_comps.dma.dma_cycle_counter + cycles as u16, DMA_SIZE); - for i in self.io_comps.dma.dma_cycle_counter..cycles_to_run as u16{ - self.write_unprotected(DMA_DEST + i, self.read_unprotected(self.io_comps.dma.soure_address + i)); + if self.io_components.dma.enable.is_some(){ + let cycles_to_run = std::cmp::min(self.io_components.dma.dma_cycle_counter + cycles as u16, DMA_SIZE); + for i in self.io_components.dma.dma_cycle_counter..cycles_to_run as u16{ + self.write_unprotected(DMA_DEST + i, self.read_unprotected(self.io_components.dma.soure_address + i)); } - self.io_comps.dma.dma_cycle_counter += cycles as u16; - if self.io_comps.dma.dma_cycle_counter >= DMA_SIZE{ - self.io_comps.dma.dma_cycle_counter = 0; - self.io_comps.dma.enable = Option::None; + self.io_components.dma.dma_cycle_counter += cycles as u16; + if self.io_components.dma.dma_cycle_counter >= DMA_SIZE{ + self.io_components.dma.dma_cycle_counter = 0; + self.io_components.dma.enable = Option::None; } } } fn is_oam_ready_for_io(&self)->bool{ - let ppu_state = self.io_comps.ppu.state as u8; + let ppu_state = self.io_components.ppu.state as u8; return ppu_state != PpuState::OamSearch as u8 && ppu_state != PpuState::PixelTransfer as u8 } fn is_vram_ready_for_io(&self)->bool{ - return self.io_comps.ppu.state as u8 != PpuState::PixelTransfer as u8; + return self.io_components.ppu.state as u8 != PpuState::PixelTransfer as u8; } fn bad_dma_read(address:u16)->u8{ diff --git a/lib_gb/src/mmu/io_comps.rs b/lib_gb/src/mmu/io_components.rs similarity index 97% rename from lib_gb/src/mmu/io_comps.rs rename to lib_gb/src/mmu/io_components.rs index d9dd933e..207e77d4 100644 --- a/lib_gb/src/mmu/io_comps.rs +++ b/lib_gb/src/mmu/io_components.rs @@ -5,7 +5,7 @@ use crate::timer::gb_timer::GbTimer; use super::{access_bus::AccessBus, io_ports::IoPorts, memory::{Memory, UnprotectedMemory}, oam_dma_transferer::OamDmaTransferer, ram::Ram}; use super::io_ports::*; -pub struct IoComps{ +pub struct IoComponents{ pub ram: Ram, pub apu: GbApu, pub timer: GbTimer, @@ -30,7 +30,7 @@ io_port_index!(OBP0_REGISTER_INDEX, OBP0_REGISTER_ADDRESS); io_port_index!(OBP1_REGISTER_INDEX, OBP1_REGISTER_ADDRESS); -impl Memory for IoComps{ +impl Memory for IoComponents{ fn read(&self, address:u16)->u8 { let mut value = self.ports.read(address); match address { @@ -106,7 +106,7 @@ impl Memory for IoComps{ } } -impl IoComps{ +impl IoComponents{ pub fn new(apu:GbApu)->Self{ Self{apu, ports:IoPorts::default(), timer:GbTimer::default(), ppu:GbPpu::default(), dma:OamDmaTransferer::default(),finished_boot:false, ram:Ram::default()} } diff --git a/lib_gb/src/mmu/mod.rs b/lib_gb/src/mmu/mod.rs index 088648b6..fcf1219a 100644 --- a/lib_gb/src/mmu/mod.rs +++ b/lib_gb/src/mmu/mod.rs @@ -7,4 +7,4 @@ pub mod io_ports; pub mod carts; pub mod access_bus; pub mod oam_dma_transferer; -pub mod io_comps; \ No newline at end of file +pub mod io_components; \ No newline at end of file From 26cb50b0db5401b3eda334a7ef5a7dc9c4ed8331 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 29 May 2021 11:16:58 +0300 Subject: [PATCH 017/136] Rename OamDmaTransferer to OamDmaTranfer --- lib_gb/src/mmu/io_components.rs | 6 +++--- lib_gb/src/mmu/mod.rs | 2 +- .../src/mmu/{oam_dma_transferer.rs => oam_dma_transfer.rs} | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) rename lib_gb/src/mmu/{oam_dma_transferer.rs => oam_dma_transfer.rs} (53%) diff --git a/lib_gb/src/mmu/io_components.rs b/lib_gb/src/mmu/io_components.rs index 207e77d4..a0cf8183 100644 --- a/lib_gb/src/mmu/io_components.rs +++ b/lib_gb/src/mmu/io_components.rs @@ -2,7 +2,7 @@ use crate::{apu::{audio_device::AudioDevice, gb_apu::GbApu, set_nr11, set_nr12, use crate::ppu::gb_ppu::GbPpu; use crate::apu::*; use crate::timer::gb_timer::GbTimer; -use super::{access_bus::AccessBus, io_ports::IoPorts, memory::{Memory, UnprotectedMemory}, oam_dma_transferer::OamDmaTransferer, ram::Ram}; +use super::{access_bus::AccessBus, io_ports::IoPorts, memory::{Memory, UnprotectedMemory}, oam_dma_transfer::OamDmaTransfer, ram::Ram}; use super::io_ports::*; pub struct IoComponents{ @@ -11,7 +11,7 @@ pub struct IoComponents{ pub timer: GbTimer, pub ppu:GbPpu, pub ports:IoPorts, - pub dma:OamDmaTransferer, + pub dma:OamDmaTransfer, pub finished_boot:bool, } @@ -108,7 +108,7 @@ impl Memory for IoComponents{ impl IoComponents{ pub fn new(apu:GbApu)->Self{ - Self{apu, ports:IoPorts::default(), timer:GbTimer::default(), ppu:GbPpu::default(), dma:OamDmaTransferer::default(),finished_boot:false, ram:Ram::default()} + Self{apu, ports:IoPorts::default(), timer:GbTimer::default(), ppu:GbPpu::default(), dma:OamDmaTransfer::default(),finished_boot:false, ram:Ram::default()} } pub fn cycle(&mut self, cycles:u32){ diff --git a/lib_gb/src/mmu/mod.rs b/lib_gb/src/mmu/mod.rs index fcf1219a..886978cb 100644 --- a/lib_gb/src/mmu/mod.rs +++ b/lib_gb/src/mmu/mod.rs @@ -6,5 +6,5 @@ pub mod vram; pub mod io_ports; pub mod carts; pub mod access_bus; -pub mod oam_dma_transferer; +pub mod oam_dma_transfer; pub mod io_components; \ No newline at end of file diff --git a/lib_gb/src/mmu/oam_dma_transferer.rs b/lib_gb/src/mmu/oam_dma_transfer.rs similarity index 53% rename from lib_gb/src/mmu/oam_dma_transferer.rs rename to lib_gb/src/mmu/oam_dma_transfer.rs index 6768dc70..703d74d1 100644 --- a/lib_gb/src/mmu/oam_dma_transferer.rs +++ b/lib_gb/src/mmu/oam_dma_transfer.rs @@ -1,13 +1,13 @@ use super::access_bus::AccessBus; -pub struct OamDmaTransferer{ +pub struct OamDmaTransfer{ pub soure_address:u16, pub enable:Option, pub dma_cycle_counter:u16 } -impl Default for OamDmaTransferer{ +impl Default for OamDmaTransfer{ fn default() -> Self { - OamDmaTransferer{dma_cycle_counter:0, enable:None, soure_address:0} + OamDmaTransfer{dma_cycle_counter:0, enable:None, soure_address:0} } } \ No newline at end of file From 00c577785e721121b955e86a23fa4546e044826e Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 29 May 2021 13:37:04 +0300 Subject: [PATCH 018/136] Remove the IoPorts struct --- lib_gb/src/apu/apu_registers_updater.rs | 6 +- lib_gb/src/mmu/gb_mmu.rs | 6 +- lib_gb/src/mmu/io_components.rs | 95 +++++++++++++++++++------ lib_gb/src/mmu/io_ports.rs | 80 +-------------------- 4 files changed, 81 insertions(+), 106 deletions(-) diff --git a/lib_gb/src/apu/apu_registers_updater.rs b/lib_gb/src/apu/apu_registers_updater.rs index 646c6c10..c7f4a20d 100644 --- a/lib_gb/src/apu/apu_registers_updater.rs +++ b/lib_gb/src/apu/apu_registers_updater.rs @@ -1,4 +1,4 @@ -use crate::{mmu::{memory::UnprotectedMemory, io_ports::*}, utils::bit_masks::*}; +use crate::{mmu::io_ports::*, utils::bit_masks::*}; use super::{ audio_device::AudioDevice, @@ -62,11 +62,11 @@ pub fn set_nr51(apu:&mut GbApu, nr51:u8){ } } -pub fn set_nr52(apu:&mut GbApu, ports:&mut IoPorts, nr52:u8){ +pub fn set_nr52(apu:&mut GbApu, ports:&mut [u8;IO_PORTS_SIZE], nr52:u8){ apu.enabled = nr52 & BIT_7_MASK != 0; for i in NR10_REGISTER_INDEX..NR52_REGISTER_INDEX{ - ports.write_unprotected(i, 0); + ports[i as usize] = 0; } } diff --git a/lib_gb/src/mmu/gb_mmu.rs b/lib_gb/src/mmu/gb_mmu.rs index 9d3b8af4..98d44eba 100644 --- a/lib_gb/src/mmu/gb_mmu.rs +++ b/lib_gb/src/mmu/gb_mmu.rs @@ -111,7 +111,7 @@ impl<'a, D:AudioDevice> UnprotectedMemory for GbMmu<'a, D>{ 0xE000..=0xFDFF=>self.io_components.ram.read_bank0(address - 0xE000), 0xFE00..=0xFE9F=>self.io_components.ppu.sprite_attribute_table[(address-0xFE00) as usize], 0xFEA0..=0xFEFF=>0x0, - 0xFF00..=0xFF7F=>self.io_components.ports.read_unprotected(address - 0xFF00), + 0xFF00..=0xFF7F=>self.io_components.read_unprotected(address - 0xFF00), 0xFF80..=0xFFFE=>self.hram[(address-0xFF80) as usize], 0xFFFF=>self.interupt_enable_register }; @@ -127,7 +127,7 @@ impl<'a, D:AudioDevice> UnprotectedMemory for GbMmu<'a, D>{ 0xD000..=0xDFFF=>self.io_components.ram.write_current_bank(address-0xD000,value), 0xFE00..=0xFE9F=>self.io_components.ppu.sprite_attribute_table[(address-0xFE00) as usize] = value, 0xFEA0..=0xFEFF=>{}, - 0xFF00..=0xFF7F=>self.io_components.ports.write_unprotected(address - 0xFF00, value), + 0xFF00..=0xFF7F=>self.io_components.write_unprotected(address - 0xFF00, value), 0xFF80..=0xFFFE=>self.hram[(address-0xFF80) as usize] = value, 0xFFFF=>self.interupt_enable_register = value } @@ -155,7 +155,7 @@ impl<'a, D:AudioDevice> GbMmu<'a, D>{ }; //Setting the bootrom register to be set (the boot sequence has over) - mmu.io_components.ports.write_unprotected(BOOT_REGISTER_ADDRESS - 0xFF00, 1); + mmu.io_components.write_unprotected(BOOT_REGISTER_ADDRESS - 0xFF00, 1); mmu } diff --git a/lib_gb/src/mmu/io_components.rs b/lib_gb/src/mmu/io_components.rs index a0cf8183..01cbfdd6 100644 --- a/lib_gb/src/mmu/io_components.rs +++ b/lib_gb/src/mmu/io_components.rs @@ -2,15 +2,19 @@ use crate::{apu::{audio_device::AudioDevice, gb_apu::GbApu, set_nr11, set_nr12, use crate::ppu::gb_ppu::GbPpu; use crate::apu::*; use crate::timer::gb_timer::GbTimer; -use super::{access_bus::AccessBus, io_ports::IoPorts, memory::{Memory, UnprotectedMemory}, oam_dma_transfer::OamDmaTransfer, ram::Ram}; +use super::{access_bus::AccessBus, memory::*, oam_dma_transfer::OamDmaTransfer, ram::Ram}; use super::io_ports::*; + +pub const IO_PORTS_SIZE:usize = 0x80; + + pub struct IoComponents{ pub ram: Ram, pub apu: GbApu, pub timer: GbTimer, pub ppu:GbPpu, - pub ports:IoPorts, + ports:[u8;IO_PORTS_SIZE], pub dma:OamDmaTransfer, pub finished_boot:bool, } @@ -32,29 +36,59 @@ io_port_index!(OBP1_REGISTER_INDEX, OBP1_REGISTER_ADDRESS); impl Memory for IoComponents{ fn read(&self, address:u16)->u8 { - let mut value = self.ports.read(address); - match address { + let mut value = self.ports[address as usize]; + return match address { //Timer - DIV_REGISTER_INDEX=> value = get_div(&self.timer), - TIMA_REGISTER_INDEX=> value = self.timer.tima_register, + TAC_REGISTER_INDEX=> value & 0b111, + DIV_REGISTER_INDEX=> get_div(&self.timer), + TIMA_REGISTER_INDEX=> self.timer.tima_register, //APU - NR52_REGISTER_INDEX=> get_nr52(&self.apu, &mut value), + NR10_REGISTER_INDEX=>value | 0b1000_0000, + NR11_REGISTER_INDEX=> value | 0b0011_1111, + NR13_REGISTER_INDEX=> 0xFF, + NR14_REGISTER_INDEX=> value | 0b1011_1111, + 0x15 => 0xFF, //Not used + NR21_REGISTER_INDEX=> value | 0b0011_1111, + NR23_REGISTER_INDEX=> 0xFF, + NR24_REGISTER_INDEX=> value | 0b1011_1111, + NR30_REGISTER_INDEX=> value | 0b0111_1111, + NR31_REGISTER_INDEX=> value | 0xFF, + NR32_REGISTER_INDEX=> value | 0b1001_1111, + NR33_REGISTER_INDEX=> value | 0xFF, + NR34_REGISTER_INDEX=> value | 0b1011_1111, + 0x1F => 0xFF, //Not used + NR41_REGISTER_INDEX=> 0xFF, + NR44_REGISTER_INDEX=> value | 0b1011_1111, + NR52_REGISTER_INDEX=> { + get_nr52(&self.apu, &mut value); + value + } + 0x27..=0x2F => 0xFF, //Not used //PPU - STAT_REGISTER_INDEX=> value = get_stat(&self.ppu), - LY_REGISTER_INDEX=> value = get_ly(&self.ppu), - _=>{} - } - - value + STAT_REGISTER_INDEX=> get_stat(&self.ppu), + LY_REGISTER_INDEX=> get_ly(&self.ppu), + //Joypad + JOYP_REGISTER_INDEX => { + let joypad_value = self.ports[JOYP_REGISTER_INDEX as usize]; + (joypad_value & 0xF) | (value & 0xF0) + } + _=>value + }; } - fn write(&mut self, address:u16, value:u8) { + fn write(&mut self, address:u16, mut value:u8) { match address{ //timer - DIV_REGISTER_INDEX=> reset_div(&mut self.timer), + DIV_REGISTER_INDEX=> { + reset_div(&mut self.timer); + value = 0; + } TIMA_REGISTER_INDEX=> set_tima(&mut self.timer, value), TMA_REGISTER_INDEX=> set_tma(&mut self.timer, value), - TAC_REGISTER_INDEX=> set_tac(&mut self.timer, value), + TAC_REGISTER_INDEX=> { + set_tac(&mut self.timer, value); + value &= 0b111; + } //APU NR10_REGISTER_INDEX=> set_nr10(&mut self.apu.sweep_tone_channel, value), NR11_REGISTER_INDEX=> set_nr11(&mut self.apu.sweep_tone_channel, value), @@ -69,7 +103,7 @@ impl Memory for IoComponents{ NR31_REGISTER_INDEX=> set_nr31(&mut self.apu.wave_channel, value), NR32_REGISTER_INDEX=> set_nr32(&mut self.apu.wave_channel, value), NR33_REGISTER_INDEX=> set_nr33(&mut self.apu.wave_channel, value), - NR34_REGISTER_INDEX=> set_nr34(&mut self.apu.wave_channel, &self.apu.frame_sequencer, self.ports.read_unprotected(NR30_REGISTER_INDEX),value), + NR34_REGISTER_INDEX=> set_nr34(&mut self.apu.wave_channel, &self.apu.frame_sequencer, self.ports[NR30_REGISTER_INDEX as usize],value), NR41_REGISTER_INDEX=> set_nr41(&mut self.apu.noise_channel, value), NR42_REGISTER_INDEX=> set_nr42(&mut self.apu.noise_channel, value), NR43_REGISTER_INDEX=> set_nr43(&mut self.apu.noise_channel, value), @@ -79,7 +113,10 @@ impl Memory for IoComponents{ NR52_REGISTER_INDEX=> set_nr52(&mut self.apu, &mut self.ports,value), //PPU LCDC_REGISTER_INDEX=> handle_lcdcontrol_register(value, &mut self.ppu), - STAT_REGISTER_INDEX=> update_stat_register(value, &mut self.ppu), + STAT_REGISTER_INDEX=> { + update_stat_register(value, &mut self.ppu); + value = (value >> 2) << 2; + }, SCY_REGISTER_INDEX=> set_scy(&mut self.ppu, value), SCX_REGISTER_INDEX=> set_scx(&mut self.ppu, value), LYC_REGISTER_INDEX=> set_lyc(&mut self.ppu, value), @@ -98,24 +135,38 @@ impl Memory for IoComponents{ WY_REGISTER_INDEX=> handle_wy_register(value, &mut self.ppu), WX_REGISTER_INDEX=> handle_wx_register(value, &mut self.ppu), BOOT_REGISTER_INDEX=> self.finished_boot = value != 0, + JOYP_REGISTER_INDEX => { + let joypad_value = self.ports[JOYP_REGISTER_INDEX as usize]; + value = (joypad_value & 0xF) | (value & 0xF0); + } // TODO: handle gbc registers (expecailly ram and vram) _=>{} } - self.ports.write(address, value); + self.ports[address as usize] = value; + } +} + +impl UnprotectedMemory for IoComponents{ + fn read_unprotected(&self, address:u16)->u8 { + self.ports[address as usize] + } + + fn write_unprotected(&mut self, address:u16, value:u8) { + self.ports[address as usize] = value; } } impl IoComponents{ pub fn new(apu:GbApu)->Self{ - Self{apu, ports:IoPorts::default(), timer:GbTimer::default(), ppu:GbPpu::default(), dma:OamDmaTransfer::default(),finished_boot:false, ram:Ram::default()} + Self{apu, ports:[0;IO_PORTS_SIZE], timer:GbTimer::default(), ppu:GbPpu::default(), dma:OamDmaTransfer::default(),finished_boot:false, ram:Ram::default()} } pub fn cycle(&mut self, cycles:u32){ - let mut if_register = self.ports.read_unprotected(IF_REGISTER_ADDRESS - 0xFF00); + let mut if_register = self.ports[IF_REGISTER_ADDRESS as usize - 0xFF00]; self.timer.cycle(&mut if_register, cycles as u8); self.apu.cycle(cycles as u8); self.ppu.update_gb_screen(&mut if_register, cycles); - self.ports.write_unprotected(IF_REGISTER_ADDRESS - 0xFF00, if_register); + self.ports[IF_REGISTER_ADDRESS as usize - 0xFF00] = if_register; } } \ No newline at end of file diff --git a/lib_gb/src/mmu/io_ports.rs b/lib_gb/src/mmu/io_ports.rs index 7e17a200..120e358f 100644 --- a/lib_gb/src/mmu/io_ports.rs +++ b/lib_gb/src/mmu/io_ports.rs @@ -1,5 +1,4 @@ use crate::utils::memory_registers::*; -use super::memory::{UnprotectedMemory, Memory}; pub const IO_PORTS_SIZE:usize = 0x80; @@ -22,8 +21,7 @@ pub_io_port_index!(TAC_REGISTER_INDEX, TAC_REGISTER_ADDRESS); pub_io_port_index!(TIMA_REGISTER_INDEX, TIMA_REGISTER_ADDRESS); pub_io_port_index!(TMA_REGISTER_INDEX, TMA_REGISTER_ADDRESS); -io_port_index!(STAT_REGISTER_INDEX, STAT_REGISTER_ADDRESS); -io_port_index!(JOYP_REGISTER_INDEX, JOYP_REGISTER_ADDRESS); +pub_io_port_index!(JOYP_REGISTER_INDEX, JOYP_REGISTER_ADDRESS); pub_io_port_index!(NR10_REGISTER_INDEX, NR10_REGISTER_ADDRESS); pub_io_port_index!(NR11_REGISTER_INDEX, NR11_REGISTER_ADDRESS); pub_io_port_index!(NR12_REGISTER_INDEX, NR12_REGISTER_ADDRESS); @@ -44,78 +42,4 @@ pub_io_port_index!(NR43_REGISTER_INDEX, NR43_REGISTER_ADDRESS); pub_io_port_index!(NR44_REGISTER_INDEX, NR44_REGISTER_ADDRESS); pub_io_port_index!(NR50_REGISTER_INDEX, NR50_REGISTER_ADDRESS); pub_io_port_index!(NR51_REGISTER_INDEX, NR51_REGISTER_ADDRESS); -pub_io_port_index!(NR52_REGISTER_INDEX, NR52_REGISTER_ADDRESS); - -pub struct IoPorts{ - ports:[u8;IO_PORTS_SIZE] -} - -impl Memory for IoPorts{ - fn read(&self, address:u16)->u8{ - let value = self.ports[address as usize]; - match address{ - NR10_REGISTER_INDEX=> value | 0b1000_0000, - NR11_REGISTER_INDEX=> value | 0b0011_1111, - NR13_REGISTER_INDEX=> 0xFF, - NR14_REGISTER_INDEX=> value | 0b1011_1111, - 0x15 => 0xFF, //Not used - NR21_REGISTER_INDEX=> value | 0b0011_1111, - NR23_REGISTER_INDEX=> 0xFF, - NR24_REGISTER_INDEX=> value | 0b1011_1111, - NR30_REGISTER_INDEX=> value | 0b0111_1111, - NR31_REGISTER_INDEX=> value | 0xFF, - NR32_REGISTER_INDEX=> value | 0b1001_1111, - NR33_REGISTER_INDEX=> value | 0xFF, - NR34_REGISTER_INDEX=> value | 0b1011_1111, - 0x1F => 0xFF, //Not used - NR41_REGISTER_INDEX=> 0xFF, - NR44_REGISTER_INDEX=> value | 0b1011_1111, - NR52_REGISTER_INDEX=> value | 0b0111_0000, - 0x27..=0x2F => 0xFF, //Not used - TAC_REGISTER_INDEX=> value & 0b111, - JOYP_REGISTER_INDEX => { - let joypad_value = self.ports[JOYP_REGISTER_INDEX as usize]; - (joypad_value & 0xF) | (value & 0xF0) - } - _=>value - } - } - - fn write(&mut self, address:u16, mut value:u8){ - match address{ - DIV_REGISTER_INDEX=> value = 0, - TAC_REGISTER_INDEX=> value &= 0b111, - STAT_REGISTER_INDEX => value = (value >> 2) << 2, - JOYP_REGISTER_INDEX => { - let joypad_value = self.ports[JOYP_REGISTER_INDEX as usize]; - value = (joypad_value & 0xF) | (value & 0xF0); - } - _=>{} - } - - self.ports[address as usize] = value; - } -} - -impl UnprotectedMemory for IoPorts{ - fn write_unprotected(&mut self, address:u16, value:u8){ - self.ports[address as usize] = value; - } - - fn read_unprotected(&self, address:u16) ->u8 { - self.ports[address as usize] - } -} - -impl Default for IoPorts{ - fn default()->Self{ - let mut io_ports = IoPorts{ - ports:[0;IO_PORTS_SIZE] - }; - - //joypad register initiall value - io_ports.ports[JOYP_REGISTER_INDEX as usize] = 0xFF; - - io_ports - } -} \ No newline at end of file +pub_io_port_index!(NR52_REGISTER_INDEX, NR52_REGISTER_ADDRESS); \ No newline at end of file From 02e7c6f95209ad324330922a4b3f9ecf62a47cac Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 29 May 2021 13:52:11 +0300 Subject: [PATCH 019/136] CR fixes * Make a const * Remove comments --- lib_gb/src/mmu/io_components.rs | 5 +++-- lib_gb/src/timer/gb_timer.rs | 4 ---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/lib_gb/src/mmu/io_components.rs b/lib_gb/src/mmu/io_components.rs index 01cbfdd6..7a3449d9 100644 --- a/lib_gb/src/mmu/io_components.rs +++ b/lib_gb/src/mmu/io_components.rs @@ -32,6 +32,7 @@ io_port_index!(BOOT_REGISTER_INDEX, BOOT_REGISTER_ADDRESS); io_port_index!(BGP_REGISTER_INDEX, BGP_REGISTER_ADDRESS); io_port_index!(OBP0_REGISTER_INDEX, OBP0_REGISTER_ADDRESS); io_port_index!(OBP1_REGISTER_INDEX, OBP1_REGISTER_ADDRESS); +io_port_index!(IF_REGISTER_INDEX, IF_REGISTER_ADDRESS); impl Memory for IoComponents{ @@ -163,10 +164,10 @@ impl IoComponents{ } pub fn cycle(&mut self, cycles:u32){ - let mut if_register = self.ports[IF_REGISTER_ADDRESS as usize - 0xFF00]; + let mut if_register = self.ports[IF_REGISTER_INDEX as usize]; self.timer.cycle(&mut if_register, cycles as u8); self.apu.cycle(cycles as u8); self.ppu.update_gb_screen(&mut if_register, cycles); - self.ports[IF_REGISTER_ADDRESS as usize - 0xFF00] = if_register; + self.ports[IF_REGISTER_INDEX as usize] = if_register; } } \ No newline at end of file diff --git a/lib_gb/src/timer/gb_timer.rs b/lib_gb/src/timer/gb_timer.rs index 8590fa49..0979f20b 100644 --- a/lib_gb/src/timer/gb_timer.rs +++ b/lib_gb/src/timer/gb_timer.rs @@ -28,7 +28,6 @@ impl Default for GbTimer{ impl GbTimer{ pub fn cycle(&mut self, if_register:&mut u8, m_cycles:u8){ - // let mut tima_register = memory.read_unprotected(TIMA_REGISTER_ADDRESS); let (timer_interval, timer_enable) = self.get_timer_controller_data(); for _ in 0..m_cycles * 4{ @@ -65,9 +64,6 @@ impl GbTimer{ } self.last_and_result = current_and_result; } - - // memory.write_unprotected(DIV_REGISTER_ADDRESS, (self.system_counter >> 8) as u8); - // memory.write_unprotected(TIMA_REGISTER_ADDRESS, tima_register); } fn get_timer_controller_data(&self)->(u8, bool){ From 3ba9bfa8126e0828270e6e9842ecf5b7b93f8855 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 29 May 2021 13:59:58 +0300 Subject: [PATCH 020/136] Update the readme --- README.md | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 3d6286a4..4b74cce9 100644 --- a/README.md +++ b/README.md @@ -15,14 +15,17 @@ The main goal of this project is to be able to play Pokemon on my own emulator. ### Development Status -- [Blargg's cpu_instrs](https://github.com/retrio/gb-test-roms/tree/master/cpu_instrs) - :thumbsup: -- [dmg-acid2](https://github.com/mattcurrie/dmg-acid2) - :thumbsup: -- [TurtleTests](https://github.com/Powerlated/TurtleTests) - :thumbsup: -- Accurate emulation - +- CPU - Cycle accurate CPU +- PPU - Scan line accurate PPU +- Timer - Mostly accurate timer +- APU - Mostly accurate APU +- Tests + - [Blargg's cpu_instrs](https://github.com/retrio/gb-test-roms/tree/master/cpu_instrs) - :thumbsup: + - [dmg-acid2](https://github.com/mattcurrie/dmg-acid2) - :thumbsup: + - [TurtleTests](https://github.com/Powerlated/TurtleTests) - :thumbsup: - [CPU cycle accurate](https://github.com/retrio/gb-test-roms/tree/master/instr_timing) - :thumbsup: - - PPU currently opcoce accurate - :thumbsup: - - APU currently cycle accurate, passes some of [blargs dmg_sound tests](https://github.com/retrio/gb-test-roms/tree/master/dmg_sound)- :thumbsup: - - Timer cycle acurate, passes most of [mooneye-gb tests](https://github.com/wilbertpol/mooneye-gb/tree/master/tests/acceptance/timer) - :thumbsup: + - APU passes some of [blargs dmg_sound tests](https://github.com/retrio/gb-test-roms/tree/master/dmg_sound)- :thumbsup: + - Timer passes most of [mooneye-gb tests](https://github.com/Gekkio/mooneye-gb/tree/master/tests/acceptance/timer) - :thumbsup: ### Games Tested - Pokemon Red - :thumbsup: From d5a6ca13dd77353263d3988329e3e444f63c867f Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 29 May 2021 14:50:34 +0300 Subject: [PATCH 021/136] Fix a bug in the bootrom register Fix a bug where no bootrom supplied the bootrom register would have been set but not the correspond variable. In order to fix this the im using the write and not the write_unprotected. --- lib_gb/src/mmu/gb_mmu.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_gb/src/mmu/gb_mmu.rs b/lib_gb/src/mmu/gb_mmu.rs index 98d44eba..85356538 100644 --- a/lib_gb/src/mmu/gb_mmu.rs +++ b/lib_gb/src/mmu/gb_mmu.rs @@ -155,7 +155,7 @@ impl<'a, D:AudioDevice> GbMmu<'a, D>{ }; //Setting the bootrom register to be set (the boot sequence has over) - mmu.io_components.write_unprotected(BOOT_REGISTER_ADDRESS - 0xFF00, 1); + mmu.write(BOOT_REGISTER_ADDRESS, 1); mmu } From 3dbb30c8026bfa073634e1c8937fa44973e71654 Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 4 Jun 2021 13:31:30 +0300 Subject: [PATCH 022/136] Remove the complex opcode resolving Move to a classic switch case resolving and running for the opcodes --- lib_gb/src/cpu/mod.rs | 3 +- lib_gb/src/cpu/opcode_runner.rs | 211 +++++++++++++++++++ lib_gb/src/cpu/opcodes/mod.rs | 4 +- lib_gb/src/cpu/opcodes/opcode_resolver.rs | 103 ---------- lib_gb/src/cpu/opcodes/opcodes_resolvers.rs | 216 -------------------- lib_gb/src/machine/gameboy.rs | 42 +--- 6 files changed, 216 insertions(+), 363 deletions(-) create mode 100644 lib_gb/src/cpu/opcode_runner.rs delete mode 100644 lib_gb/src/cpu/opcodes/opcode_resolver.rs delete mode 100644 lib_gb/src/cpu/opcodes/opcodes_resolvers.rs diff --git a/lib_gb/src/cpu/mod.rs b/lib_gb/src/cpu/mod.rs index 3f39be16..296b0e63 100644 --- a/lib_gb/src/cpu/mod.rs +++ b/lib_gb/src/cpu/mod.rs @@ -1,4 +1,5 @@ pub mod gb_cpu; pub mod register; pub mod opcodes; -pub mod flag; \ No newline at end of file +pub mod flag; +pub mod opcode_runner; \ No newline at end of file diff --git a/lib_gb/src/cpu/opcode_runner.rs b/lib_gb/src/cpu/opcode_runner.rs new file mode 100644 index 00000000..5ff6fede --- /dev/null +++ b/lib_gb/src/cpu/opcode_runner.rs @@ -0,0 +1,211 @@ +use crate::mmu::memory::Memory; +use super::{ + gb_cpu::GbCpu, + opcodes::{ + arithmetic_8bit_instructions::*, + rotate_shift_instructions::*, + cpu_control_instructions::*, + jump_instructions::*, + load_16bit_instructions::*, + arithmetic_16bit_instructions::*, + load_8bit_instructions::*, + single_bit_sintructions::*, + } +}; + + +type U16MemoryOpcodeFunc = fn(&mut GbCpu,&mut T,u16)->u8; +type U32MemoryOpcodeFunc = fn(&mut GbCpu,&mut T,u32)->u8; + +impl GbCpu{ + pub fn run_opcode(&mut self, memory:&mut impl Memory)->u8{ + let opcode = self.fetch_next_byte(memory); + + match opcode{ + //Stop + 0x10=>{ + let next_byte = self.fetch_next_byte(memory); + if next_byte == 0{ + stop(self, memory) + } + else{ + std::panic!("Invalid stop opcode, second byte: {:#X}", next_byte); + } + } + + //just cpu + 0x00=>1, + 0x07=>rlca(self), + 0x0F=>rrca(self), + 0x17=>rla(self), + 0x1F=>rra(self), + 0x2F=>cpl(self), + 0x27=>daa(self), + 0x37=>scf(self), + 0x3F=>ccf(self), + 0x76=>halt(self), + 0xE9=>jump_hl(self), + 0xF3=>di(self), + 0xF9=>load_sp_hl(self), + 0xFB=>ei(self), + + //cpu and opcode + 0x03|0x13|0x23|0x33=>inc_rr(self, opcode), + 0x04|0x14|0x24|0x0C|0x1C|0x2C|0x3C=>inc_r(self, opcode), + 0x05|0x15|0x25|0x0D|0x1D|0x2D|0x3D=>dec_r(self, opcode), + 0x09|0x19|0x29|0x39=>add_hl_rr(self, opcode), + 0x0B|0x1B|0x2B|0x3B=>dec_rr(self, opcode), + 0x40..=0x45 | 0x47..=0x4D | 0x4F..=0x55 | 0x57..=0x5D | + 0x5F..=0x65 | 0x67..=0x6D | 0x6F | 0x78..=0x7D | 0x7F=>ld_r_r(self, opcode), + 0x80..=0x85 | 0x87=>add_a_r(self, opcode), + 0x88..=0x8D | 0x8F=>adc_a_r(self, opcode), + 0x90..=0x95 | 0x97=>sub_a_r(self, opcode), + 0x98..=0x9D | 0x9F=>sbc_a_r(self, opcode), + 0xA0..=0xA5 | 0xA7=>and_a_r(self, opcode), + 0xA8..=0xAD | 0xAF=>xor_a_r(self, opcode), + 0xB0..=0xB5 | 0xB7=>or_a_r(self, opcode), + 0xB8..=0xBD | 0xBF=>cp_a_r(self, opcode), + + //u16 opcode + 0x06|0x0E|0x16|0x1E|0x26|0x2E|0x3E=>run_u16_opcode(self, memory, opcode, ld_r_n), + 0x18=>run_u16_opcode(self, memory, opcode, jump_r), + 0x20|0x28|0x30|0x38=>run_u16_opcode(self, memory, opcode, jump_r_cc), + 0xC6=>run_u16_opcode(self, memory, opcode, add_a_nn), + 0xCE=>run_u16_opcode(self, memory, opcode, adc_a_nn), + 0xD6=>run_u16_opcode(self, memory, opcode, sub_a_nn), + 0xDE=>run_u16_opcode(self, memory, opcode, sbc_a_nn), + 0xE6=>run_u16_opcode(self, memory, opcode, and_a_nn), + 0xE8=>run_u16_opcode(self, memory, opcode, add_sp_dd), + 0xEE=>run_u16_opcode(self, memory, opcode, xor_a_nn), + 0xF6=>run_u16_opcode(self, memory, opcode, or_a_nn), + 0xF8=>run_u16_opcode(self, memory, opcode, ld_hl_spdd), + 0xFE=>run_u16_opcode(self, memory, opcode, cp_a_nn), + + //u32 opcodes + 0x01 | 0x11 | 0x21 | 0x31=>run_u32_opcode(self, memory, opcode, load_rr_nn), + 0xC2 | 0xD2 | 0xCA | 0xDA=>run_u32_opcode(self, memory, opcode,jump_cc), + 0xC3=>run_u32_opcode(self, memory, opcode,jump), + + //Memory opcodes + 0x02=>ld_bc_a(self, memory), + 0x0A=>ld_a_bc(self, memory), + 0x12=>ld_de_a(self, memory), + 0x1A=>ld_a_de(self, memory), + 0x22=>ldi_hl_a(self, memory), + 0x2A=>ldi_a_hl(self, memory), + 0x32=>ldd_hl_a(self, memory), + 0x34=>inc_hl(self, memory), + 0x35=>dec_hl(self, memory), + 0x3A=>ldd_a_hl(self, memory), + 0x86=>add_a_hl(self, memory), + 0x8E=>adc_a_hl(self, memory), + 0x96=>sub_a_hl(self, memory), + 0x9E=>sbc_a_hl(self, memory), + 0xA6=>and_a_hl(self, memory), + 0xAE=>xor_a_hl(self, memory), + 0xB6=>or_a_hl(self, memory), + 0xBE=>cp_a_hl(self, memory), + 0xC9=>ret(self, memory), + 0xD9=>reti(self, memory), + 0xE2=>ld_ioport_c_a(self, memory), + 0xF2=>ld_a_ioport_c(self, memory), + + //Memory u8 opcodes + 0x46|0x4E|0x56|0x5E|0x66|0x6E|0x7E=>ld_r_hl(self, memory, opcode), + 0x70..=0x75 | 0x77=>ld_hl_r(self, memory, opcode), + 0xC0|0xC8|0xD0|0xD8=>ret_cc(self, memory, opcode), + 0xC1|0xD1|0xE1|0xF1=>pop(self, memory, opcode), + 0xC5|0xD5|0xE5|0xF5=>push(self, memory, opcode), + 0xC7|0xCF|0xD7|0xDF|0xE7|0xEF|0xF7|0xFF=>rst(self, memory, opcode), + + //Memory u16 opcodes + 0x36=>run_u16_memory_opcode(self, memory, opcode, ld_hl_n), + 0xE0=>run_u16_memory_opcode(self, memory, opcode, ld_ioport_n_a), + 0xF0=>run_u16_memory_opcode(self, memory, opcode, ld_a_ioport_n), + + //Memory u32 opcodes + + 0x08=>run_u32_memory_opcode(self, memory, opcode, ld_nn_sp), + 0xC4|0xCC|0xD4|0xDC=>run_u32_memory_opcode(self, memory, opcode, call_cc), + 0xCD=>run_u32_memory_opcode(self, memory, opcode, call), + 0xEA=>run_u32_memory_opcode(self, memory, opcode, ld_nn_a), + 0xFA=>run_u32_memory_opcode(self, memory, opcode, ld_a_nn), + + //0xCB opcodes + 0xCB=>{ + let next = self.fetch_next_byte(memory); + let u16_opcode = (opcode as u16) << 8 | next as u16; + match next{ + 0x00..=0x05 | 0x07=>rlc_r(self, u16_opcode), + 0x08..=0x0D | 0x0F=>rrc_r(self, u16_opcode), + 0x10..=0x15 | 0x17=>rl_r(self, u16_opcode), + 0x18..=0x1D | 0x1F=>rr_r(self, u16_opcode), + 0x20..=0x25 | 0x27=>sla_r(self, u16_opcode), + 0x28..=0x2D | 0x2F=>sra_r(self, u16_opcode), + 0x30..=0x35 | 0x37=>swap_r(self, u16_opcode), + 0x38..=0x3D | 0x3F=>srl_r(self, u16_opcode), + 0x40..=0x45 | 0x47..=0x4D | 0x4F..=0x55 | 0x57..=0x5D | + 0x5F..=0x65 | 0x67..=0x6D | 0x6F..=0x75 | 0x77..=0x7D | 0x7F =>bit_r(self, u16_opcode), + 0x80..=0x85 | 0x87..=0x8D | 0x8F..=0x95 | 0x97..=0x9D | + 0x9F..=0xA5 | 0xA7..=0xAD | 0xAF..=0xB5 | 0xB7..=0xBD | 0xBF =>res_r(self, u16_opcode), + 0xC0..=0xC5 | 0xC7..=0xCD | 0xCF..=0xD5 | 0xD7..=0xDD | + 0xDF..=0xE5 | 0xE7..=0xED | 0xEF..=0xF5 | 0xF7..=0xFD | 0xFF =>set_r(self, u16_opcode), + + 0x06=>rlc_hl(self, memory), + 0x0E=>rrc_hl(self, memory), + 0x16=>rl_hl(self, memory), + 0x1E=>rr_hl(self, memory), + 0x26=>sla_hl(self, memory), + 0x2E=>sra_hl(self, memory), + 0x36=>swap_hl(self, memory), + 0x3E=>srl_hl(self, memory), + + 0x46|0x4E|0x56|0x5E|0x66|0x6E|0x76|0x7E=>bit_hl(self, memory, u16_opcode), + 0x86|0x8E|0x96|0x9E|0xA6|0xAE|0xB6|0xBE=>res_hl(self, memory, u16_opcode), + 0xC6|0xCE|0xD6|0xDE|0xE6|0xEE|0xF6|0xFE=>set_hl(self, memory, u16_opcode), + } + }, + + _=>std::panic!("Unsupported opcode:{:#X}", opcode) + } + } + + + fn fetch_next_byte(&mut self, memory: &impl Memory)->u8{ + let byte:u8 = memory.read(self.program_counter); + self.program_counter+=1; + return byte; + } +} + + + +fn run_u16_opcode(cpu: &mut GbCpu, memory: &impl Memory, opcode:u8, opcode_func:fn(&mut GbCpu, u16)->u8)->u8{ + let u16_opcode = get_u16_opcode(cpu, memory, opcode); + opcode_func(cpu, u16_opcode) +} + +fn run_u16_memory_opcode(cpu: &mut GbCpu, memory: &mut T, opcode:u8, opcode_func:U16MemoryOpcodeFunc)->u8{ + let u16_opcode = get_u16_opcode(cpu, memory, opcode); + opcode_func(cpu, memory, u16_opcode) +} + +fn run_u32_opcode(cpu: &mut GbCpu, memory: &impl Memory, opcode:u8, opcode_func:fn(&mut GbCpu, u32)->u8)->u8{ + let mut u32_opcode:u32 = ((opcode as u32)<<8) | (cpu.fetch_next_byte(memory) as u32); + u32_opcode <<= 8; + u32_opcode |= cpu.fetch_next_byte(memory) as u32; + + opcode_func(cpu, u32_opcode) +} + +fn run_u32_memory_opcode(cpu: &mut GbCpu, memory: &mut T, opcode:u8, opcode_func:U32MemoryOpcodeFunc)->u8{ + let mut u32_opcode:u32 = ((opcode as u32)<<8) | (cpu.fetch_next_byte(memory) as u32); + u32_opcode <<= 8; + u32_opcode |= cpu.fetch_next_byte(memory) as u32; + + opcode_func(cpu, memory, u32_opcode) +} + +fn get_u16_opcode(cpu:&mut GbCpu, memory:&impl Memory, opcode:u8)->u16{ + (opcode as u16) << 8 | cpu.fetch_next_byte(memory) as u16 +} \ No newline at end of file diff --git a/lib_gb/src/cpu/opcodes/mod.rs b/lib_gb/src/cpu/opcodes/mod.rs index 5fafc025..1a879b56 100644 --- a/lib_gb/src/cpu/opcodes/mod.rs +++ b/lib_gb/src/cpu/opcodes/mod.rs @@ -6,6 +6,4 @@ pub mod arithmetic_16bit_instructions; pub mod jump_instructions; pub mod cpu_control_instructions; pub mod rotate_shift_instructions; -pub mod single_bit_sintructions; -pub mod opcodes_resolvers; -pub mod opcode_resolver; \ No newline at end of file +pub mod single_bit_sintructions; \ No newline at end of file diff --git a/lib_gb/src/cpu/opcodes/opcode_resolver.rs b/lib_gb/src/cpu/opcodes/opcode_resolver.rs deleted file mode 100644 index 1cd23b56..00000000 --- a/lib_gb/src/cpu/opcodes/opcode_resolver.rs +++ /dev/null @@ -1,103 +0,0 @@ -use super::opcodes_resolvers::*; -use crate::mmu::memory::Memory; - - -pub enum OpcodeFuncType{ - OpcodeFunc(OpcodeFunc), - U8OpcodeFunc(U8OpcodeFunc), - U16OpcodeFunc(U16OpcodeFunc), - U32OpcodeFunc(U32OpcodeFunc), - MemoryOpcodeFunc(MemoryOpcodeFunc), - U8MemoryOpcodeFunc(U8MemoryOpcodeFunc), - U16MemoryOpcodeFunc(U16MemoryOpcodeFunc), - U32MemoryOpcodeFunc(U32MemoryOpcodeFunc) -} - -pub struct OpcodeResolver{ - opcode_func_resolver:fn(u8)->Option, - u8_opcode_func_resolver:fn(u8)->Option, - u16_opcode_func_resolver:fn(u8,u8)->Option, - u32_opcode_func_resolver:fn(u8)->Option, - memory_opcode_func_resolver:fn(u8)->Option>, - memory_opcode_func_2bytes_resolver:fn(u8,u8)->Option>, - u8_memory_opcode_func_resolver:fn(u8)->Option>, - u16_memory_opcode_func_resolver:fn(u8,u8)->Option>, - u32_memory_opcode_func_resolver:fn(u8)->Option> -} - - -impl OpcodeResolver{ - pub fn get_opcode(&mut self, opcode:u8, memory:&impl Memory, program_counter:&mut u16)->OpcodeFuncType{ - let opcode_func = (self.opcode_func_resolver)(opcode); - match opcode_func{ - Some(func)=> return OpcodeFuncType::OpcodeFunc(func), - None=>{} - } - let memory_opcode_func = (self.memory_opcode_func_resolver)(opcode); - match memory_opcode_func{ - Some(func)=> return OpcodeFuncType::MemoryOpcodeFunc(func), - None=>{} - } - let u8_opcode_func=(self.u8_opcode_func_resolver)(opcode); - match u8_opcode_func{ - Some(func)=> return OpcodeFuncType::U8OpcodeFunc(func), - None=>{} - } - let u8_memory_func=(self.u8_memory_opcode_func_resolver)(opcode); - match u8_memory_func{ - Some(func)=> return OpcodeFuncType::U8MemoryOpcodeFunc(func), - None=>{} - } - let postfix:u8 = memory.read(*program_counter); - let u16_opcode_func=(self.u16_opcode_func_resolver)(opcode, postfix); - match u16_opcode_func{ - Some(func)=>return OpcodeFuncType::U16OpcodeFunc(func), - None=>{} - } - let u32_opcode_func = (self.u32_opcode_func_resolver)(opcode); - match u32_opcode_func{ - Some(func)=> return OpcodeFuncType::U32OpcodeFunc(func), - None=>{} - } - let u32_memory_opcode_func=(self.u32_memory_opcode_func_resolver)(opcode); - match u32_memory_opcode_func{ - Some(func)=> return OpcodeFuncType::U32MemoryOpcodeFunc(func), - None=>{} - } - let u16_memory_opcode_func = (self.u16_memory_opcode_func_resolver)(opcode, postfix); - match u16_memory_opcode_func{ - Some(func)=>return OpcodeFuncType::U16MemoryOpcodeFunc(func), - None=>{} - } - let memory_opcode_func = (self.memory_opcode_func_2bytes_resolver)(opcode, postfix); - match memory_opcode_func{ - Some(func)=>{ - //this is the only opcodes type that does not uses the postfix byte and therfore does not increment the program counter - //so im incrementing is manually - *program_counter+=1; - return OpcodeFuncType::MemoryOpcodeFunc(func); - }, - None=>{} - } - - std::panic!("no opcode matching: {:#X?}, nextb{:#X?}, c_pc{:#X?}",opcode, postfix, program_counter); - } -} - -impl Default for OpcodeResolver{ - fn default()->OpcodeResolver{ - OpcodeResolver{ - opcode_func_resolver:get_opcode_func_resolver(), - memory_opcode_func_resolver:get_memory_opcode_func_resolver(), - memory_opcode_func_2bytes_resolver:get_memory_opcode_func_2bytes_resolver(), - u8_opcode_func_resolver:get_u8_opcode_func_resolver(), - u8_memory_opcode_func_resolver:get_u8_memory_opcode_func_resolver(), - u16_memory_opcode_func_resolver:get_u16_memory_opcode_func_resolver(), - u16_opcode_func_resolver:get_u16_opcode_func_resolver(), - u32_opcode_func_resolver:get_u32_opcode_func_resolver(), - u32_memory_opcode_func_resolver:get_u32_memory_opcode_func_resolver() - } - } -} - - diff --git a/lib_gb/src/cpu/opcodes/opcodes_resolvers.rs b/lib_gb/src/cpu/opcodes/opcodes_resolvers.rs deleted file mode 100644 index 55428582..00000000 --- a/lib_gb/src/cpu/opcodes/opcodes_resolvers.rs +++ /dev/null @@ -1,216 +0,0 @@ -use crate::cpu::gb_cpu::GbCpu; -use crate::mmu::memory::Memory; -use std::option::Option; -use super::{ - arithmetic_16bit_instructions::*, - arithmetic_8bit_instructions::*, - cpu_control_instructions::*, - jump_instructions::*, - load_16bit_instructions::*, - load_8bit_instructions::*, - rotate_shift_instructions::*, - single_bit_sintructions::* -}; - - -pub type OpcodeFunc = fn(&mut GbCpu)->u8; -pub type U8OpcodeFunc = fn(&mut GbCpu,u8)->u8; -pub type U16OpcodeFunc = fn(&mut GbCpu,u16)->u8; -pub type U32OpcodeFunc = fn(&mut GbCpu,u32)->u8; -pub type MemoryOpcodeFunc = fn(&mut GbCpu,&mut T)->u8; -pub type U8MemoryOpcodeFunc = fn(&mut GbCpu,&mut T,u8)->u8; -pub type U16MemoryOpcodeFunc = fn(&mut GbCpu,&mut T,u16)->u8; -pub type U32MemoryOpcodeFunc = fn(&mut GbCpu,&mut T,u32)->u8; - - -pub fn get_opcode_func_resolver()->fn(u8)->Option{ - |opcode:u8|->Option{ - match opcode{ - 0x00=>Some(|_|1), - 0x07=>Some(rlca), - 0x0F=>Some(rrca), - 0x17=>Some(rla), - 0x1F=>Some(rra), - 0x2F=>Some(cpl), - 0x27=>Some(daa), - 0x37=>Some(scf), - 0x3F=>Some(ccf), - 0x76=>Some(halt), - 0xE9=>Some(jump_hl), - 0xF3=>Some(di), - 0xF9=>Some(load_sp_hl), - 0xFB=>Some(ei), - _=>None - } - } -} - -pub fn get_u8_opcode_func_resolver()->fn(u8)->Option{ - |opcode:u8|->Option{ - match opcode{ - 0x03|0x13|0x23|0x33=>Some(inc_rr), - 0x04|0x14|0x24|0x0C|0x1C|0x2C|0x3C=>Some(inc_r), - 0x05|0x15|0x25|0x0D|0x1D|0x2D|0x3D=>Some(dec_r), - 0x09|0x19|0x29|0x39=>Some(add_hl_rr), - 0x0B|0x1B|0x2B|0x3B=>Some(dec_rr), - 0x40..=0x45|0x47..=0x4D|0x4F..=0x55|0x57..=0x5D| - 0x5F..=0x65|0x67..=0x6D|0x6F|0x78..=0x7D|0x7F=>Some(ld_r_r), - 0x80..=0x85 | 0x87=>Some(add_a_r), - 0x88..=0x8D | 0x8F=>Some(adc_a_r), - 0x90..=0x95 | 0x97=>Some(sub_a_r), - 0x98..=0x9D | 0x9F=>Some(sbc_a_r), - 0xA0..=0xA5 | 0xA7=>Some(and_a_r), - 0xA8..=0xAD | 0xAF=>Some(xor_a_r), - 0xB0..=0xB5 | 0xB7=>Some(or_a_r), - 0xB8..=0xBD | 0xBF=>Some(cp_a_r), - _=>None - } - } -} - -pub fn get_u16_opcode_func_resolver()->fn(u8,u8)->Option{ - |opcode:u8, next:u8|->Option{ - match opcode{ - 0x06|0x0E|0x16|0x1E|0x26|0x2E|0x3E=>Some(ld_r_n), - 0x18=>Some(jump_r), - 0x20|0x28|0x30|0x38=>Some(jump_r_cc), - 0xC6=>Some(add_a_nn), - 0xCB=>match next{ - 0x00..=0x05 | 0x07=> Some(rlc_r), - 0x08..=0x0D | 0x0F=>Some(rrc_r), - 0x10..=0x15 | 0x17=>Some(rl_r), - 0x18..=0x1D | 0x1F=>Some(rr_r), - 0x20..=0x25 | 0x27=>Some(sla_r), - 0x28..=0x2D | 0x2F=>Some(sra_r), - 0x30..=0x35 | 0x37=>Some(swap_r), - 0x38..=0x3D | 0x3F=>Some(srl_r), - 0x40..=0x45 | 0x47..=0x4D | 0x4F..=0x55 | 0x57..=0x5D | - 0x5F..=0x65 | 0x67..=0x6D | 0x6F..=0x75 | 0x77..=0x7D | 0x7F =>Some(bit_r), - 0x80..=0x85 | 0x87..=0x8D | 0x8F..=0x95 | 0x97..=0x9D | - 0x9F..=0xA5 | 0xA7..=0xAD | 0xAF..=0xB5 | 0xB7..=0xBD | 0xBF =>Some(res_r), - 0xC0..=0xC5 | 0xC7..=0xCD | 0xCF..=0xD5 | 0xD7..=0xDD | - 0xDF..=0xE5 | 0xE7..=0xED | 0xEF..=0xF5 | 0xF7..=0xFD | 0xFF =>Some(set_r), - _=>None - }, - 0xCE=>Some(adc_a_nn), - 0xD6=>Some(sub_a_nn), - 0xDE=>Some(sbc_a_nn), - 0xE6=>Some(and_a_nn), - 0xE8=>Some(add_sp_dd), - 0xEE=>Some(xor_a_nn), - 0xF6=>Some(or_a_nn), - 0xF8=>Some(ld_hl_spdd), - 0xFE=>Some(cp_a_nn), - _=>None - } - } -} - -pub fn get_u32_opcode_func_resolver()->fn(u8)->Option{ - |opcode:u8|->Option{ - match opcode{ - 0x01 | 0x11 | 0x21 | 0x31=>Some(load_rr_nn), - 0xC2 | 0xD2 | 0xCA | 0xDA=>Some(jump_cc), - 0xC3=>Some(jump), - _=>None - } - } -} - -pub fn get_memory_opcode_func_resolver()->fn(u8)->Option>{ - |opcode:u8|->Option>{ - match opcode{ - 0x02=>Some(ld_bc_a), - 0x0A=>Some(ld_a_bc), - 0x12=>Some(ld_de_a), - 0x1A=>Some(ld_a_de), - 0x22=>Some(ldi_hl_a), - 0x2A=>Some(ldi_a_hl), - 0x32=>Some(ldd_hl_a), - 0x34=>Some(inc_hl), - 0x35=>Some(dec_hl), - 0x3A=>Some(ldd_a_hl), - 0x86=>Some(add_a_hl), - 0x8E=>Some(adc_a_hl), - 0x96=>Some(sub_a_hl), - 0x9E=>Some(sbc_a_hl), - 0xA6=>Some(and_a_hl), - 0xAE=>Some(xor_a_hl), - 0xB6=>Some(or_a_hl), - 0xBE=>Some(cp_a_hl), - 0xC9=>Some(ret), - 0xD9=>Some(reti), - 0xE2=>Some(ld_ioport_c_a), - 0xF2=>Some(ld_a_ioport_c), - _=>None - } - } -} - -pub fn get_memory_opcode_func_2bytes_resolver()->fn(u8,u8)->Option>{ - |opcode:u8,next_byte:u8|->Option>{ - if opcode == 0x10 && next_byte == 0{ - return Some(stop); - } - - if opcode == 0xCB{ - return match next_byte{ - 0x06=>Some(rlc_hl), - 0x0E=>Some(rrc_hl), - 0x16=>Some(rl_hl), - 0x1E=>Some(rr_hl), - 0x26=>Some(sla_hl), - 0x2E=>Some(sra_hl), - 0x36=>Some(swap_hl), - 0x3E=>Some(srl_hl), - _=>None - }; - } - - return None; - } -} - -pub fn get_u8_memory_opcode_func_resolver()->fn(u8)->Option>{ - |opcode:u8|->Option>{ - match opcode{ - 0x46|0x4E|0x56|0x5E|0x66|0x6E|0x7E=>Some(ld_r_hl), - 0x70..=0x75 | 0x77=>Some(ld_hl_r), - 0xC0|0xC8|0xD0|0xD8=>Some(ret_cc), - 0xC1|0xD1|0xE1|0xF1=>Some(pop), - 0xC5|0xD5|0xE5|0xF5=>Some(push), - 0xC7|0xCF|0xD7|0xDF|0xE7|0xEF|0xF7|0xFF=>Some(rst), - _=>None - } - } -} - -pub fn get_u16_memory_opcode_func_resolver()->fn(u8,u8)->Option>{ - |opcode:u8, next_byte:u8|->Option>{ - match opcode{ - 0x36=>Some(ld_hl_n), - 0xE0=>Some(ld_ioport_n_a), - 0xF0=>Some(ld_a_ioport_n), - 0xCB=>match next_byte{ - 0x46|0x4E|0x56|0x5E|0x66|0x6E|0x76|0x7E=>Some(bit_hl), - 0x86|0x8E|0x96|0x9E|0xA6|0xAE|0xB6|0xBE=>Some(res_hl), - 0xC6|0xCE|0xD6|0xDE|0xE6|0xEE|0xF6|0xFE=>Some(set_hl), - _=>None - }, - _=>None - } - } -} - -pub fn get_u32_memory_opcode_func_resolver()->fn(u8)->Option>{ - |opcode:u8|->Option>{ - match opcode{ - 0x08=>Some(ld_nn_sp), - 0xC4|0xCC|0xD4|0xDC=>Some(call_cc), - 0xCD=>Some(call), - 0xEA=>Some(ld_nn_a), - 0xFA=>Some(ld_a_nn), - _=>None - } - } -} \ No newline at end of file diff --git a/lib_gb/src/machine/gameboy.rs b/lib_gb/src/machine/gameboy.rs index 7340701d..d730976c 100644 --- a/lib_gb/src/machine/gameboy.rs +++ b/lib_gb/src/machine/gameboy.rs @@ -1,6 +1,6 @@ use crate::{ apu::{audio_device::AudioDevice, gb_apu::GbApu}, - cpu::{gb_cpu::GbCpu, opcodes::opcode_resolver::*}, + cpu::gb_cpu::GbCpu, keypad::{joypad::Joypad, joypad_provider::JoypadProvider, joypad_register_updater}, mmu::{carts::mbc::Mbc, gb_mmu::{GbMmu, BOOT_ROM_SIZE}, memory::Memory}, ppu::{gb_ppu::{CYCLES_PER_FRAME, SCREEN_HEIGHT, SCREEN_WIDTH}} @@ -13,7 +13,6 @@ use log::debug; pub struct GameBoy<'a, JP: JoypadProvider, AD:AudioDevice> { cpu: GbCpu, mmu: GbMmu::<'a, AD>, - opcode_resolver:OpcodeResolver::>, interrupts_handler:InterruptsHandler, cycles_counter:u32, joypad_provider: JP @@ -25,7 +24,6 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ GameBoy{ cpu:GbCpu::default(), mmu:GbMmu::new_with_bootrom(mbc, boot_rom, GbApu::new(audio_device)), - opcode_resolver:OpcodeResolver::default(), interrupts_handler: InterruptsHandler::default(), cycles_counter:0, joypad_provider: joypad_provider @@ -45,7 +43,6 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ GameBoy{ cpu:cpu, mmu:GbMmu::new(mbc, GbApu::new(audio_device)), - opcode_resolver:OpcodeResolver::default(), interrupts_handler: InterruptsHandler::default(), cycles_counter:0, joypad_provider: joypad_provider, @@ -95,15 +92,8 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ return self.mmu.io_components.ppu.get_frame_buffer(); } - fn fetch_next_byte(&mut self)->u8{ - let byte:u8 = self.mmu.read(self.cpu.program_counter); - self.cpu.program_counter+=1; - return byte; - } - fn execute_opcode(&mut self)->u8{ let pc = self.cpu.program_counter; - let opcode:u8 = self.fetch_next_byte(); //debug if self.mmu.io_components.finished_boot{ @@ -119,35 +109,7 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ a,f,b,c,d,e,h,l, self.cpu.stack_pointer, pc, self.mmu.read(pc), self.mmu.read(pc+1), self.mmu.read(pc+2), self.mmu.read(pc+3)); } - - - let opcode_func:OpcodeFuncType> = self.opcode_resolver.get_opcode(opcode, &self.mmu, &mut self.cpu.program_counter); - match opcode_func{ - OpcodeFuncType::OpcodeFunc(func)=>func(&mut self.cpu), - OpcodeFuncType::MemoryOpcodeFunc(func)=>func(&mut self.cpu, &mut self.mmu), - OpcodeFuncType::U8OpcodeFunc(func)=>func(&mut self.cpu, opcode), - OpcodeFuncType::U8MemoryOpcodeFunc(func)=>func(&mut self.cpu, &mut self.mmu, opcode), - OpcodeFuncType::U16OpcodeFunc(func)=>{ - let u16_opcode:u16 = ((opcode as u16)<<8) | (self.fetch_next_byte() as u16); - func(&mut self.cpu, u16_opcode) - }, - OpcodeFuncType::U16MemoryOpcodeFunc(func)=>{ - let u16_opcode:u16 = ((opcode as u16)<<8) | (self.fetch_next_byte() as u16); - func(&mut self.cpu, &mut self.mmu, u16_opcode) - }, - OpcodeFuncType::U32OpcodeFunc(func)=>{ - let mut u32_opcode:u32 = ((opcode as u32)<<8) | (self.fetch_next_byte() as u32); - u32_opcode <<= 8; - u32_opcode |= self.fetch_next_byte() as u32; - func(&mut self.cpu, u32_opcode) - }, - OpcodeFuncType::U32MemoryOpcodeFunc(func)=>{ - let mut u32_opcode:u32 = ((opcode as u32)<<8) | (self.fetch_next_byte() as u32); - u32_opcode <<= 8; - u32_opcode |= self.fetch_next_byte() as u32; - func(&mut self.cpu, &mut self.mmu, u32_opcode) - } - } + self.cpu.run_opcode(&mut self.mmu) } } From cdbee8e59b708f0bcccda61dd71bff3f9c4acaab Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 5 Jun 2021 23:59:13 +0300 Subject: [PATCH 023/136] Starting to implement a fifo ppu --- lib_gb/src/ppu/fifo_ppu.rs | 77 ++++++++++++++++++++++++++++++++++++++ lib_gb/src/ppu/mod.rs | 1 + 2 files changed, 78 insertions(+) create mode 100644 lib_gb/src/ppu/fifo_ppu.rs diff --git a/lib_gb/src/ppu/fifo_ppu.rs b/lib_gb/src/ppu/fifo_ppu.rs new file mode 100644 index 00000000..1598ece8 --- /dev/null +++ b/lib_gb/src/ppu/fifo_ppu.rs @@ -0,0 +1,77 @@ +use super::{ppu_state::PpuState, sprite_attribute::SpriteAttribute}; + +pub struct FifoPpu{ + oam_entries:[SpriteAttribute; 10], + current_oam_entry:u8, + t_cycles_passed:u16, + state:PpuState, + ly_register:u8, +} + +impl FifoPpu{ + pub fn cycle(&mut self, m_cycles:u8, oam:&[u8;0xA0], extended_sprite:bool)->(){ + let sprite_height = if extended_sprite {16} else {8}; + + for _ in 0..m_cycles{ + match self.state{ + PpuState::OamSearch=>{ + for _ in 0..2{ + self.t_cycles_passed += 2; //half a m_cycle + let oam_index = self.t_cycles_passed / 2; + let oam_entry_address = (oam_index * 4) as usize; + let end_y = oam[oam_entry_address]; + let end_x = oam[oam_entry_address + 1]; + + if end_x > 0 && self.ly_register + 16 >= end_y && self.ly_register + 16 < end_y + sprite_height && self.current_oam_entry < 10{ + let tile_number = oam[oam_entry_address + 2]; + let attributes = oam[oam_entry_address + 3]; + self.oam_entries[self.current_oam_entry as usize] = SpriteAttribute::new(end_y, end_x, tile_number, attributes); + self.current_oam_entry += 1; + } + } + + if self.t_cycles_passed == 80{ + self.state = PpuState::PixelTransfer; + } + } + PpuState::Hblank=>{ + self.t_cycles_passed += 4; + if self.t_cycles_passed == 456{ + if self.ly_register == 143{ + self.state = PpuState::Vblank; + } + else{ + self.state = PpuState::OamSearch; + } + self.t_cycles_passed = 0; + self.ly_register += 1; + } + } + PpuState::Vblank=>{ + self.t_cycles_passed += 4; + if self.t_cycles_passed == 4560{ + self.state = PpuState::OamSearch; + self.t_cycles_passed = 0; + self.ly_register = 0; + } + else{ + self.ly_register = 144 + (self.t_cycles_passed % 456) as u8; + } + } + PpuState::PixelTransfer=>{ + + } + } + } + } +} + +struct PixelFetcher{ + +} + +impl PixelFetcher{ + pub fn cycle(&mut self){ + + } +} \ No newline at end of file diff --git a/lib_gb/src/ppu/mod.rs b/lib_gb/src/ppu/mod.rs index e732249f..b7d6b829 100644 --- a/lib_gb/src/ppu/mod.rs +++ b/lib_gb/src/ppu/mod.rs @@ -3,6 +3,7 @@ pub mod ppu_state; pub mod color; pub mod colors; pub mod ppu_register_updater; +mod fifo_ppu; mod normal_sprite; mod sprite_attribute; mod extended_sprite; From 3c2c2bb1a14a00ec937846c5d33aea65936c2be7 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sun, 13 Jun 2021 00:18:56 +0300 Subject: [PATCH 024/136] WIP in the middle of the fifo ppy --- lib_gb/src/ppu/fifo_ppu.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/lib_gb/src/ppu/fifo_ppu.rs b/lib_gb/src/ppu/fifo_ppu.rs index 1598ece8..d55eec28 100644 --- a/lib_gb/src/ppu/fifo_ppu.rs +++ b/lib_gb/src/ppu/fifo_ppu.rs @@ -1,11 +1,27 @@ +use crate::utils::{vec2::Vec2, bit_masks::*}; +use crate::mmu::vram::VRam; use super::{ppu_state::PpuState, sprite_attribute::SpriteAttribute}; +enum FethcingState{ + TileNumber, + LowTileData(u8), + HighTileData(u8), + Push(u8) +} + pub struct FifoPpu{ oam_entries:[SpriteAttribute; 10], + vram: VRam, current_oam_entry:u8, t_cycles_passed:u16, state:PpuState, + lcd_control:u8, ly_register:u8, + window_pos:Vec2, + bg_pos:Vec2, + pixel_fething_state: FethcingState, + + x_pos_counter: u8, } impl FifoPpu{ @@ -59,7 +75,23 @@ impl FifoPpu{ } } PpuState::PixelTransfer=>{ + match self.pixel_fething_state{ + FethcingState::TileNumber=>{ + let rendering_wnd = self.window_pos.x >= self.bg_pos.x && self.window_pos.y >= self.bg_pos.y && self.lcd_control & BIT_5_MASK != 0; + let tile_num = if rendering_wnd{ + let tile_map_address:u16 = if self.lcd_control & BIT_6_MASK == 0 {0x1800} else {0x1C00}; + self.vram.read_current_bank(tile_map_address + self.x_pos_counter as u16) + } + else{ + let tile_map_address = if self.lcd_control & BIT_3_MASK == 0 {0x1800} else {0x1C00}; + let scx_offset = (self.bg_pos.x as u16 / 8) & 0x1F; //Anding with 31 in order to + self.vram.read_current_bank(tile_map_address + self.x_pos_counter as u16 + scx_offset) + }; + + } + _=>{} + } } } } From 427df0e99a848b00e47bc7177c5f180b1522acc0 Mon Sep 17 00:00:00 2001 From: alloncm Date: Wed, 21 Jul 2021 18:06:47 +0300 Subject: [PATCH 025/136] The fifo supposed to support the bg layer --- lib_gb/src/ppu/fifo_ppu.rs | 68 ++++++++++++++++++++++++++++++++------ 1 file changed, 57 insertions(+), 11 deletions(-) diff --git a/lib_gb/src/ppu/fifo_ppu.rs b/lib_gb/src/ppu/fifo_ppu.rs index d55eec28..1ec6a260 100644 --- a/lib_gb/src/ppu/fifo_ppu.rs +++ b/lib_gb/src/ppu/fifo_ppu.rs @@ -5,8 +5,8 @@ use super::{ppu_state::PpuState, sprite_attribute::SpriteAttribute}; enum FethcingState{ TileNumber, LowTileData(u8), - HighTileData(u8), - Push(u8) + HighTileData(u8,u8), + Push(u8,u8) } pub struct FifoPpu{ @@ -21,13 +21,16 @@ pub struct FifoPpu{ bg_pos:Vec2, pixel_fething_state: FethcingState, - x_pos_counter: u8, + pos_counter: Vec2, + bg_fifo: Vec } impl FifoPpu{ - pub fn cycle(&mut self, m_cycles:u8, oam:&[u8;0xA0], extended_sprite:bool)->(){ + pub fn cycle(&mut self, m_cycles:u8, oam:&[u8;0xA0], extended_sprite:bool)->Vec{ let sprite_height = if extended_sprite {16} else {8}; + let mut pixels_to_push_to_lcd = Vec::::new(); + for _ in 0..m_cycles{ match self.state{ PpuState::OamSearch=>{ @@ -77,24 +80,67 @@ impl FifoPpu{ PpuState::PixelTransfer=>{ match self.pixel_fething_state{ FethcingState::TileNumber=>{ - let rendering_wnd = self.window_pos.x >= self.bg_pos.x && self.window_pos.y >= self.bg_pos.y && self.lcd_control & BIT_5_MASK != 0; - let tile_num = if rendering_wnd{ + let tile_num = if self.is_redering_wnd(){ let tile_map_address:u16 = if self.lcd_control & BIT_6_MASK == 0 {0x1800} else {0x1C00}; - self.vram.read_current_bank(tile_map_address + self.x_pos_counter as u16) + self.vram.read_current_bank(tile_map_address + ((32 * (self.pos_counter. y / 8)) + (self.pos_counter.x / 8) )as u16) } else{ let tile_map_address = if self.lcd_control & BIT_3_MASK == 0 {0x1800} else {0x1C00}; - let scx_offset = (self.bg_pos.x as u16 / 8) & 0x1F; //Anding with 31 in order to - self.vram.read_current_bank(tile_map_address + self.x_pos_counter as u16 + scx_offset) + let scx_offset = self.bg_pos.x as u16 / 8; + let scy_offset = (((self.bg_pos.y + self.pos_counter.y) & 0xFF) / 8) as u16; + self.vram.read_current_bank(tile_map_address + (32 * scy_offset + (self.pos_counter.x as u16 + scx_offset) & 31)) + }; + + self.pixel_fething_state = FethcingState::LowTileData(tile_num); + self.t_cycles_passed += 2; + } + FethcingState::LowTileData(tile_num)=>{ + let current_tile_base_data_address = if self.lcd_control & BIT_4_MASK == 0 && tile_num & BIT_7_MASK == 0 {0x1000} else {0}; + let current_tile_data_address = current_tile_base_data_address + (tile_num as u16 * 16); + let low_data = if self.is_redering_wnd(){ + self.vram.read_current_bank(current_tile_data_address + (2 * (self.pos_counter.y % 8)) as u16) + } else{ + self.vram.read_current_bank(current_tile_data_address + (2 * ((self.bg_pos.y + self.ly_register) % 8)) as u16) }; + self.pixel_fething_state = FethcingState::HighTileData(tile_num, low_data); + self.t_cycles_passed += 2; + } + FethcingState::HighTileData(tile_num, low_data)=>{ + let current_tile_base_data_address = if self.lcd_control & BIT_4_MASK == 0 && tile_num & BIT_7_MASK == 0 {0x1000} else {0}; + let current_tile_data_address = current_tile_base_data_address + (tile_num as u16 * 16); + let high_data = if self.is_redering_wnd(){ + self.vram.read_current_bank(current_tile_data_address + (2 * (self.pos_counter.y % 8)) as u16 + 1) + } else{ + self.vram.read_current_bank(current_tile_data_address + (2 * ((self.bg_pos.y + self.ly_register) % 8)) as u16 + 1) + }; + self.pixel_fething_state = FethcingState::Push(low_data, high_data); + self.t_cycles_passed += 2; + } + FethcingState::Push(low_data, high_data)=>{ + for i in (0..8).rev(){ + let mask = 1 << i; + let mut pixel = (low_data & mask) >> i; + pixel |= (high_data & mask) >> (i - 1); + self.bg_fifo.push(pixel); + } + + self.pixel_fething_state = FethcingState::TileNumber; + self.t_cycles_passed += 2; } - _=>{} } } } - } + + pixels_to_push_to_lcd.append(&mut self.bg_fifo); + } + + return pixels_to_push_to_lcd; + } + + fn is_redering_wnd(&self)->bool{ + self.window_pos.x >= self.bg_pos.x && self.window_pos.y >= self.bg_pos.y && self.lcd_control & BIT_5_MASK != 0 } } From 35b9946195177565c50149a54aed365846ad448f Mon Sep 17 00:00:00 2001 From: alloncm Date: Wed, 21 Jul 2021 18:28:04 +0300 Subject: [PATCH 026/136] Wrap the fifo code in order to start and test it --- lib_gb/src/ppu/fifo_ppu.rs | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/lib_gb/src/ppu/fifo_ppu.rs b/lib_gb/src/ppu/fifo_ppu.rs index 1ec6a260..245bdf3e 100644 --- a/lib_gb/src/ppu/fifo_ppu.rs +++ b/lib_gb/src/ppu/fifo_ppu.rs @@ -12,6 +12,7 @@ enum FethcingState{ pub struct FifoPpu{ oam_entries:[SpriteAttribute; 10], vram: VRam, + oam:[u8;0xA0], current_oam_entry:u8, t_cycles_passed:u16, state:PpuState, @@ -26,7 +27,12 @@ pub struct FifoPpu{ } impl FifoPpu{ - pub fn cycle(&mut self, m_cycles:u8, oam:&[u8;0xA0], extended_sprite:bool)->Vec{ + + pub fn cycle(&mut self, m_cycles:u8){ + + } + + pub fn cycle_fetcher(&mut self, m_cycles:u8, extended_sprite:bool)->Vec{ let sprite_height = if extended_sprite {16} else {8}; let mut pixels_to_push_to_lcd = Vec::::new(); @@ -38,12 +44,12 @@ impl FifoPpu{ self.t_cycles_passed += 2; //half a m_cycle let oam_index = self.t_cycles_passed / 2; let oam_entry_address = (oam_index * 4) as usize; - let end_y = oam[oam_entry_address]; - let end_x = oam[oam_entry_address + 1]; + let end_y = self.oam[oam_entry_address]; + let end_x = self.oam[oam_entry_address + 1]; if end_x > 0 && self.ly_register + 16 >= end_y && self.ly_register + 16 < end_y + sprite_height && self.current_oam_entry < 10{ - let tile_number = oam[oam_entry_address + 2]; - let attributes = oam[oam_entry_address + 3]; + let tile_number = self.oam[oam_entry_address + 2]; + let attributes = self.oam[oam_entry_address + 3]; self.oam_entries[self.current_oam_entry as usize] = SpriteAttribute::new(end_y, end_x, tile_number, attributes); self.current_oam_entry += 1; } @@ -130,6 +136,11 @@ impl FifoPpu{ self.t_cycles_passed += 2; } } + + if self.pos_counter.x == 160{ + self.state = PpuState::Hblank; + self.pos_counter.x = 0; + } } } From 9587841ced8ac8615e3b599b0959b6128d3289c2 Mon Sep 17 00:00:00 2001 From: alloncm Date: Thu, 22 Jul 2021 20:44:30 +0300 Subject: [PATCH 027/136] WIP adding the registers to the new ppu --- lib_gb/src/ppu/fifo_ppu.rs | 72 ++++++++++++++++++++-- lib_gb/src/ppu/fifo_register_updater.rs | 79 +++++++++++++++++++++++++ lib_gb/src/ppu/mod.rs | 1 + 3 files changed, 146 insertions(+), 6 deletions(-) create mode 100644 lib_gb/src/ppu/fifo_register_updater.rs diff --git a/lib_gb/src/ppu/fifo_ppu.rs b/lib_gb/src/ppu/fifo_ppu.rs index 245bdf3e..8ee0b635 100644 --- a/lib_gb/src/ppu/fifo_ppu.rs +++ b/lib_gb/src/ppu/fifo_ppu.rs @@ -1,5 +1,6 @@ use crate::utils::{vec2::Vec2, bit_masks::*}; use crate::mmu::vram::VRam; +use super::color::Color; use super::{ppu_state::PpuState, sprite_attribute::SpriteAttribute}; enum FethcingState{ @@ -16,11 +17,24 @@ pub struct FifoPpu{ current_oam_entry:u8, t_cycles_passed:u16, state:PpuState, - lcd_control:u8, + pub lcd_control:u8, + pub stat_register:u8, + pub lyc_register:u8, ly_register:u8, window_pos:Vec2, - bg_pos:Vec2, + pub bg_pos:Vec2, pixel_fething_state: FethcingState, + extended_sprite: bool, + bg_color_mapping: [Color; 4], + + screen_buffer: [u32; 160*144], + screen_buffer_index:usize, + + //interrupts + pub v_blank_interrupt_request:bool, + pub h_blank_interrupt_request:bool, + pub oam_search_interrupt_request:bool, + pub coincidence_interrupt_request:bool, pos_counter: Vec2, bg_fifo: Vec @@ -28,12 +42,32 @@ pub struct FifoPpu{ impl FifoPpu{ - pub fn cycle(&mut self, m_cycles:u8){ - + pub fn get_frame_buffer(&self)->&[u32; 160 * 144]{ + &self.screen_buffer } - pub fn cycle_fetcher(&mut self, m_cycles:u8, extended_sprite:bool)->Vec{ - let sprite_height = if extended_sprite {16} else {8}; + pub fn cycle(&mut self, m_cycles:u8, if_register:&mut u8){ + let pixels = self.cycle_fetcher(m_cycles, if_register); + + //update stat register + self.stat_register &= 0b1111_1100; //clear first 2 bits + self.stat_register |= self.state as u8; + + for pixel in pixels.as_slice(){ + let p = self.bg_color_mapping[*pixel as usize].clone(); + self.screen_buffer[self.screen_buffer_index] = Self::color_as_uint(&p); + self.screen_buffer_index += 1; + if self.screen_buffer_index == self.screen_buffer.len(){ + self.screen_buffer_index = 0; + unsafe{ + std::ptr::write_bytes(self.screen_buffer.as_mut_ptr(), 0, self.screen_buffer.len()); + } + } + } + } + + fn cycle_fetcher(&mut self, m_cycles:u8, if_register:&mut u8)->Vec{ + let sprite_height = if self.extended_sprite {16} else {8}; let mut pixels_to_push_to_lcd = Vec::::new(); @@ -64,9 +98,15 @@ impl FifoPpu{ if self.t_cycles_passed == 456{ if self.ly_register == 143{ self.state = PpuState::Vblank; + if self.v_blank_interrupt_request{ + *if_register |= BIT_1_MASK; + } } else{ self.state = PpuState::OamSearch; + if self.oam_search_interrupt_request{ + *if_register |= BIT_1_MASK; + } } self.t_cycles_passed = 0; self.ly_register += 1; @@ -76,6 +116,9 @@ impl FifoPpu{ self.t_cycles_passed += 4; if self.t_cycles_passed == 4560{ self.state = PpuState::OamSearch; + if self.oam_search_interrupt_request{ + *if_register |= BIT_1_MASK; + } self.t_cycles_passed = 0; self.ly_register = 0; } @@ -139,10 +182,23 @@ impl FifoPpu{ if self.pos_counter.x == 160{ self.state = PpuState::Hblank; + if self.h_blank_interrupt_request{ + *if_register |= BIT_1_MASK; + } self.pos_counter.x = 0; } } } + + if self.ly_register == self.lyc_register{ + self.stat_register |= BIT_2_MASK; + if self.coincidence_interrupt_request{ + *if_register |= BIT_1_MASK; + } + } + else{ + self.stat_register &= !BIT_2_MASK; + } pixels_to_push_to_lcd.append(&mut self.bg_fifo); } @@ -153,6 +209,10 @@ impl FifoPpu{ fn is_redering_wnd(&self)->bool{ self.window_pos.x >= self.bg_pos.x && self.window_pos.y >= self.bg_pos.y && self.lcd_control & BIT_5_MASK != 0 } + + const fn color_as_uint(color: &Color) -> u32 { + ((color.r as u32) << 16) | ((color.g as u32) << 8) | (color.b as u32) + } } struct PixelFetcher{ diff --git a/lib_gb/src/ppu/fifo_register_updater.rs b/lib_gb/src/ppu/fifo_register_updater.rs new file mode 100644 index 00000000..f3ccb52b --- /dev/null +++ b/lib_gb/src/ppu/fifo_register_updater.rs @@ -0,0 +1,79 @@ +use crate::utils::bit_masks::*; +use super::{ fifo_ppu::FifoPpu, color::*, colors::*}; + +const WX_OFFSET:u8 = 7; + +pub fn handle_lcdcontrol_register( register:u8, ppu:&mut FifoPpu){ + ppu.lcd_control = register; +} + +pub fn update_stat_register(register:u8, ppu: &mut FifoPpu){ + ppu.h_blank_interrupt_request = register & BIT_3_MASK != 0; + ppu.v_blank_interrupt_request = register & BIT_4_MASK != 0; + ppu.oam_search_interrupt_request = register & BIT_5_MASK != 0; + ppu.coincidence_interrupt_request = register & BIT_6_MASK != 0; + + ppu.stat_register = register & 0b111_1000; +} + +pub fn handle_scroll_registers(scroll_x:u8, scroll_y:u8, ppu: &mut FifoPpu){ + ppu.background_scroll.x = scroll_x; + ppu.background_scroll.y = scroll_y; +} + +pub fn set_scx(ppu: &mut GbPpu, value:u8){ + ppu.background_scroll.x = value; +} + +pub fn set_scy(ppu:&mut GbPpu, value:u8){ + ppu.background_scroll.y = value; +} + +pub fn handle_bg_pallet_register(register:u8, pallet:&mut [Color;4] ){ + pallet[0] = get_matching_color(register&0b00000011); + pallet[1] = get_matching_color((register&0b00001100)>>2); + pallet[2] = get_matching_color((register&0b00110000)>>4); + pallet[3] = get_matching_color((register&0b11000000)>>6); +} + +pub fn handle_obp_pallet_register(register:u8, pallet:&mut [Option;4] ){ + pallet[0] = None; + pallet[1] = Some(get_matching_color((register&0b00001100)>>2)); + pallet[2] = Some(get_matching_color((register&0b00110000)>>4)); + pallet[3] = Some(get_matching_color((register&0b11000000)>>6)); +} + +fn get_matching_color(number:u8)->Color{ + return match number{ + 0b00=>WHITE, + 0b01=>LIGHT_GRAY, + 0b10=>DARK_GRAY, + 0b11=>BLACK, + _=>std::panic!("no macthing color for color number: {}", number) + }; +} + +pub fn handle_wy_register(register:u8, ppu:&mut GbPpu){ + ppu.window_scroll.y = register; +} + +pub fn handle_wx_register(register:u8, ppu:&mut GbPpu){ + if register < WX_OFFSET{ + ppu.window_scroll.x = 0; + } + else{ + ppu.window_scroll.x = register - WX_OFFSET; + } +} + +pub fn get_ly(ppu:&GbPpu)->u8{ + ppu.current_line_drawn +} + +pub fn get_stat(ppu:&FifoPpu)->u8{ + ppu.stat_register +} + +pub fn set_lyc(ppu:&mut GbPpu, value:u8){ + ppu.lyc_register = value; +} diff --git a/lib_gb/src/ppu/mod.rs b/lib_gb/src/ppu/mod.rs index b7d6b829..db8169df 100644 --- a/lib_gb/src/ppu/mod.rs +++ b/lib_gb/src/ppu/mod.rs @@ -3,6 +3,7 @@ pub mod ppu_state; pub mod color; pub mod colors; pub mod ppu_register_updater; +pub mod fifo_register_updater; mod fifo_ppu; mod normal_sprite; mod sprite_attribute; From 683e2dfefbac76fc01ead89e01073f41d16deabf Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 23 Jul 2021 12:48:11 +0300 Subject: [PATCH 028/136] Remove uncessary function --- lib_gb/src/ppu/ppu_register_updater.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/lib_gb/src/ppu/ppu_register_updater.rs b/lib_gb/src/ppu/ppu_register_updater.rs index 44d28a72..2dc2f751 100644 --- a/lib_gb/src/ppu/ppu_register_updater.rs +++ b/lib_gb/src/ppu/ppu_register_updater.rs @@ -23,11 +23,6 @@ pub fn update_stat_register(register:u8, ppu: &mut GbPpu){ ppu.stat_register = register & 0b111_1000; } -pub fn handle_scroll_registers(scroll_x:u8, scroll_y:u8, ppu: &mut GbPpu){ - ppu.background_scroll.x = scroll_x; - ppu.background_scroll.y = scroll_y; -} - pub fn set_scx(ppu: &mut GbPpu, value:u8){ ppu.background_scroll.x = value; } From 67f49ba5f648df199e655bea6ef7c0970496d820 Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 23 Jul 2021 12:48:26 +0300 Subject: [PATCH 029/136] Add support for the registers --- lib_gb/src/ppu/fifo_ppu.rs | 64 +++++++++++++++++++------ lib_gb/src/ppu/fifo_register_updater.rs | 29 +++++------ 2 files changed, 61 insertions(+), 32 deletions(-) diff --git a/lib_gb/src/ppu/fifo_ppu.rs b/lib_gb/src/ppu/fifo_ppu.rs index 8ee0b635..41d9d49a 100644 --- a/lib_gb/src/ppu/fifo_ppu.rs +++ b/lib_gb/src/ppu/fifo_ppu.rs @@ -1,6 +1,9 @@ +use std::mem::{self, MaybeUninit}; + use crate::utils::{vec2::Vec2, bit_masks::*}; use crate::mmu::vram::VRam; use super::color::Color; +use super::colors::*; use super::{ppu_state::PpuState, sprite_attribute::SpriteAttribute}; enum FethcingState{ @@ -20,11 +23,10 @@ pub struct FifoPpu{ pub lcd_control:u8, pub stat_register:u8, pub lyc_register:u8, - ly_register:u8, - window_pos:Vec2, + pub ly_register:u8, + pub window_pos:Vec2, pub bg_pos:Vec2, pixel_fething_state: FethcingState, - extended_sprite: bool, bg_color_mapping: [Color; 4], screen_buffer: [u32; 160*144], @@ -40,6 +42,48 @@ pub struct FifoPpu{ bg_fifo: Vec } +impl Default for FifoPpu{ + fn default() -> Self { + let oam_entries = { + let mut data: [MaybeUninit; 10] = unsafe{ + MaybeUninit::uninit().assume_init() + }; + + for elem in &mut data[..]{ + *elem = MaybeUninit::new(SpriteAttribute::new(0, 0, 0, 0)); + } + + unsafe{mem::transmute::<_, [SpriteAttribute;10]>(data)} + }; + + Self{ + vram: VRam::default(), + oam: [0;0xA0], + stat_register: 0, + lyc_register: 0, + lcd_control: 0, + bg_pos: Vec2::{x:0, y:0}, + window_pos: Vec2::{x:0,y:0}, + screen_buffer:[0;160*144], + bg_color_mapping:[WHITE, LIGHT_GRAY, DARK_GRAY, BLACK], + ly_register:0, + state: PpuState::OamSearch, + pos_counter: Vec2::{x:0,y:0}, + //interrupts + v_blank_interrupt_request:false, + h_blank_interrupt_request:false, + oam_search_interrupt_request:false, + coincidence_interrupt_request:false, + oam_entries:oam_entries, + current_oam_entry:0, + pixel_fething_state:FethcingState::TileNumber, + screen_buffer_index:0, + t_cycles_passed:0, + bg_fifo:Vec::::with_capacity(16) + } + } +} + impl FifoPpu{ pub fn get_frame_buffer(&self)->&[u32; 160 * 144]{ @@ -67,7 +111,7 @@ impl FifoPpu{ } fn cycle_fetcher(&mut self, m_cycles:u8, if_register:&mut u8)->Vec{ - let sprite_height = if self.extended_sprite {16} else {8}; + let sprite_height = if self.lcd_control & BIT_2_MASK != 0 {16} else {8}; let mut pixels_to_push_to_lcd = Vec::::new(); @@ -213,14 +257,4 @@ impl FifoPpu{ const fn color_as_uint(color: &Color) -> u32 { ((color.r as u32) << 16) | ((color.g as u32) << 8) | (color.b as u32) } -} - -struct PixelFetcher{ - -} - -impl PixelFetcher{ - pub fn cycle(&mut self){ - - } -} \ No newline at end of file +} diff --git a/lib_gb/src/ppu/fifo_register_updater.rs b/lib_gb/src/ppu/fifo_register_updater.rs index f3ccb52b..27750da6 100644 --- a/lib_gb/src/ppu/fifo_register_updater.rs +++ b/lib_gb/src/ppu/fifo_register_updater.rs @@ -16,17 +16,12 @@ pub fn update_stat_register(register:u8, ppu: &mut FifoPpu){ ppu.stat_register = register & 0b111_1000; } -pub fn handle_scroll_registers(scroll_x:u8, scroll_y:u8, ppu: &mut FifoPpu){ - ppu.background_scroll.x = scroll_x; - ppu.background_scroll.y = scroll_y; +pub fn set_scx(ppu: &mut FifoPpu, value:u8){ + ppu.bg_pos.x = value; } -pub fn set_scx(ppu: &mut GbPpu, value:u8){ - ppu.background_scroll.x = value; -} - -pub fn set_scy(ppu:&mut GbPpu, value:u8){ - ppu.background_scroll.y = value; +pub fn set_scy(ppu:&mut FifoPpu, value:u8){ + ppu.bg_pos.y = value; } pub fn handle_bg_pallet_register(register:u8, pallet:&mut [Color;4] ){ @@ -53,27 +48,27 @@ fn get_matching_color(number:u8)->Color{ }; } -pub fn handle_wy_register(register:u8, ppu:&mut GbPpu){ - ppu.window_scroll.y = register; +pub fn handle_wy_register(register:u8, ppu:&mut FifoPpu){ + ppu.window_pos.y = register; } -pub fn handle_wx_register(register:u8, ppu:&mut GbPpu){ +pub fn handle_wx_register(register:u8, ppu:&mut FifoPpu){ if register < WX_OFFSET{ - ppu.window_scroll.x = 0; + ppu.window_pos.x = 0; } else{ - ppu.window_scroll.x = register - WX_OFFSET; + ppu.window_pos.x = register - WX_OFFSET; } } -pub fn get_ly(ppu:&GbPpu)->u8{ - ppu.current_line_drawn +pub fn get_ly(ppu:&FifoPpu)->u8{ + ppu.ly_register } pub fn get_stat(ppu:&FifoPpu)->u8{ ppu.stat_register } -pub fn set_lyc(ppu:&mut GbPpu, value:u8){ +pub fn set_lyc(ppu:&mut FifoPpu, value:u8){ ppu.lyc_register = value; } From 601b75e8ad14e8b5f1bcd9a23056f7dd6500770c Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 24 Jul 2021 00:46:33 +0300 Subject: [PATCH 030/136] Add gfx device and sdl gfx device and progressed with the fifo implementation --- gb/src/main.rs | 63 +------- gb/src/sdl_audio_device.rs | 1 + gb/src/sdl_gfx_device.rs | 78 ++++++++++ lib_gb/src/machine/gameboy.rs | 37 ++--- lib_gb/src/mmu/gb_mmu.rs | 27 ++-- lib_gb/src/mmu/io_components.rs | 23 ++- lib_gb/src/ppu/fifo_ppu.rs | 199 +++++++++++++----------- lib_gb/src/ppu/fifo_register_updater.rs | 27 ++-- lib_gb/src/ppu/gfx_device.rs | 3 + lib_gb/src/ppu/mod.rs | 3 +- 10 files changed, 259 insertions(+), 202 deletions(-) create mode 100644 gb/src/sdl_gfx_device.rs create mode 100644 lib_gb/src/ppu/gfx_device.rs diff --git a/gb/src/main.rs b/gb/src/main.rs index 309774a7..47656f4e 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -4,36 +4,17 @@ mod sdl_audio_device; mod audio_resampler; mod wav_file_audio_device; mod multi_device_audio; +mod sdl_gfx_device; use crate::{mbc_handler::*, sdl_joypad_provider::*, multi_device_audio::*}; use lib_gb::{keypad::button::Button, machine::gameboy::GameBoy, mmu::gb_mmu::BOOT_ROM_SIZE, ppu::gb_ppu::{SCREEN_HEIGHT, SCREEN_WIDTH}, GB_FREQUENCY, apu::audio_device::*}; -use std::{ - ffi::{c_void, CString}, - fs, env, result::Result, vec::Vec -}; +use std::{fs, env, result::Result, vec::Vec}; use log::info; use sdl2::sys::*; const FPS:f64 = GB_FREQUENCY as f64 / 70224.0; const FRAME_TIME_MS:f64 = (1.0 / FPS) * 1000.0; - -fn extend_vec(vec:&[u32], scale:usize, w:usize, h:usize)->Vec{ - let mut new_vec = vec![0;vec.len()*scale*scale]; - for y in 0..h{ - let sy = y*scale; - for x in 0..w{ - let sx = x*scale; - for i in 0..scale{ - for j in 0..scale{ - new_vec[(sy+i)*(w*scale)+sx+j] = vec[y*w+x]; - } - } - } - } - return new_vec; -} - fn init_logger(debug:bool)->Result<(), fern::InitError>{ let level = if debug {log::LevelFilter::Debug} else {log::LevelFilter::Info}; let mut fern_logger = fern::Dispatch::new() @@ -77,8 +58,6 @@ fn check_for_terminal_feature_flag(args:&Vec::, flag:&str)->bool{ } fn main() { - let screen_scale:u32 = 4; - let args: Vec = env::args().collect(); let debug_level = check_for_terminal_feature_flag(&args, "--log"); @@ -88,25 +67,6 @@ fn main() { Result::Err(error)=>std::panic!("error initing logger: {}", error) } - let buffer_width = SCREEN_WIDTH as u32 * screen_scale; - let buffer_height = SCREEN_HEIGHT as u32* screen_scale; - let program_name = CString::new("MagenBoy").unwrap(); - let (_window, renderer, texture): (*mut SDL_Window, *mut SDL_Renderer, *mut SDL_Texture) = unsafe{ - SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO); - let wind:*mut SDL_Window = SDL_CreateWindow( - program_name.as_ptr(), - SDL_WINDOWPOS_UNDEFINED_MASK as i32, SDL_WINDOWPOS_UNDEFINED_MASK as i32, - buffer_width as i32, buffer_height as i32, 0); - - let rend: *mut SDL_Renderer = SDL_CreateRenderer(wind, -1, 0); - - let tex: *mut SDL_Texture = SDL_CreateTexture(rend, - SDL_PixelFormatEnum::SDL_PIXELFORMAT_ARGB8888 as u32, SDL_TextureAccess::SDL_TEXTUREACCESS_STREAMING as i32, - buffer_width as i32, buffer_height as i32); - - (wind, rend, tex) - }; - let audio_device = sdl_audio_device::SdlAudioDevie::new(44100); let mut devices: Vec::> = Vec::new(); devices.push(Box::new(audio_device)); @@ -117,6 +77,8 @@ fn main() { let audio_devices = MultiAudioDevice::new(devices); + let sdl_gfx_device = sdl_gfx_device::SdlGfxDevice::new(SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32, "MagenBoy"); + let program_name = &args[1]; let mut mbc = initialize_mbc(program_name); let joypad_provider = SdlJoypadProvider::new(buttons_mapper); @@ -130,12 +92,12 @@ fn main() { bootrom[i] = file[i]; } - GameBoy::new_with_bootrom(&mut mbc, joypad_provider,audio_devices, bootrom) + GameBoy::new_with_bootrom(&mut mbc, joypad_provider,audio_devices, sdl_gfx_device, bootrom) } Result::Err(_)=>{ info!("could not find bootrom... booting directly to rom"); - GameBoy::new(&mut mbc, joypad_provider, audio_devices) + GameBoy::new(&mut mbc, joypad_provider, audio_devices, sdl_gfx_device) } }; @@ -153,18 +115,7 @@ fn main() { } } - let frame_buffer = gameboy.cycle_frame(); - let scaled_buffer = extend_vec(frame_buffer, screen_scale as usize, SCREEN_WIDTH, SCREEN_HEIGHT); - - let mut pixels: *mut c_void = std::ptr::null_mut(); - let mut length: std::os::raw::c_int = 0; - SDL_LockTexture(texture, std::ptr::null(), &mut pixels, &mut length); - std::ptr::copy_nonoverlapping(scaled_buffer.as_ptr(),pixels as *mut u32, scaled_buffer.len()); - SDL_UnlockTexture(texture); - - SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, texture, std::ptr::null(), std::ptr::null()); - SDL_RenderPresent(renderer); + gameboy.cycle_frame(); let end = SDL_GetPerformanceCounter(); let elapsed_ms:f64 = (end - start) as f64 / SDL_GetPerformanceFrequency() as f64 * 1000.0; diff --git a/gb/src/sdl_audio_device.rs b/gb/src/sdl_audio_device.rs index 36a645d6..2346a9f4 100644 --- a/gb/src/sdl_audio_device.rs +++ b/gb/src/sdl_audio_device.rs @@ -33,6 +33,7 @@ impl SdlAudioDevie{ let mut uninit_audio_spec:MaybeUninit = MaybeUninit::uninit(); let device_id = unsafe{ + SDL_Init(SDL_INIT_AUDIO); SDL_ClearError(); let id = SDL_OpenAudioDevice(std::ptr::null(), 0, &desired_audio_spec, uninit_audio_spec.as_mut_ptr() , 0); diff --git a/gb/src/sdl_gfx_device.rs b/gb/src/sdl_gfx_device.rs new file mode 100644 index 00000000..42fab877 --- /dev/null +++ b/gb/src/sdl_gfx_device.rs @@ -0,0 +1,78 @@ +use std::ffi::{CString, c_void}; + +use lib_gb::ppu::gfx_device::GfxDevice; +use sdl2::sys::*; + +pub struct SdlGfxDevice{ + _window_name: CString, + renderer: *mut SDL_Renderer, + texture: *mut SDL_Texture, + width:u32, + height:u32, + sacle:u8, +} + +impl SdlGfxDevice{ + pub fn new(buffer_width:u32, buffer_height:u32, window_name:&str)->Self{ + let cs_wnd_name = CString::new(window_name).unwrap(); + + let (_window, renderer, texture): (*mut SDL_Window, *mut SDL_Renderer, *mut SDL_Texture) = unsafe{ + SDL_Init(SDL_INIT_VIDEO); + let wind:*mut SDL_Window = SDL_CreateWindow( + cs_wnd_name.as_ptr(), + SDL_WINDOWPOS_UNDEFINED_MASK as i32, SDL_WINDOWPOS_UNDEFINED_MASK as i32, + buffer_width as i32 * 4, buffer_height as i32 * 4, 0); + + let rend: *mut SDL_Renderer = SDL_CreateRenderer(wind, -1, 0); + + let tex: *mut SDL_Texture = SDL_CreateTexture(rend, + SDL_PixelFormatEnum::SDL_PIXELFORMAT_ARGB8888 as u32, SDL_TextureAccess::SDL_TEXTUREACCESS_STREAMING as i32, + buffer_width as i32 * 4, buffer_height as i32 * 4); + + (wind, rend, tex) + }; + + Self{ + _window_name: cs_wnd_name, + renderer, + texture, + height:buffer_height, + width:buffer_width, + sacle:4 + } + } + + fn extend_vec(vec:&[u32], scale:usize, w:usize, h:usize)->Vec{ + let mut new_vec = vec![0;vec.len()*scale*scale]; + for y in 0..h{ + let sy = y*scale; + for x in 0..w{ + let sx = x*scale; + for i in 0..scale{ + for j in 0..scale{ + new_vec[(sy+i)*(w*scale)+sx+j] = vec[y*w+x]; + } + } + } + } + return new_vec; + } +} + +impl GfxDevice for SdlGfxDevice{ + fn swap_buffer(&self, buffer:&[u32]) { + unsafe{ + let extended_buffer = Self::extend_vec(buffer, self.sacle as usize, self.width as usize, self.height as usize); + + let mut pixels: *mut c_void = std::ptr::null_mut(); + let mut length: std::os::raw::c_int = 0; + SDL_LockTexture(self.texture, std::ptr::null(), &mut pixels, &mut length); + std::ptr::copy_nonoverlapping(extended_buffer.as_ptr(),pixels as *mut u32, extended_buffer.len()); + SDL_UnlockTexture(self.texture); + + SDL_RenderClear(self.renderer); + SDL_RenderCopy(self.renderer, self.texture, std::ptr::null(), std::ptr::null()); + SDL_RenderPresent(self.renderer); + } + } +} \ No newline at end of file diff --git a/lib_gb/src/machine/gameboy.rs b/lib_gb/src/machine/gameboy.rs index 7340701d..4a1f9111 100644 --- a/lib_gb/src/machine/gameboy.rs +++ b/lib_gb/src/machine/gameboy.rs @@ -1,30 +1,25 @@ -use crate::{ - apu::{audio_device::AudioDevice, gb_apu::GbApu}, - cpu::{gb_cpu::GbCpu, opcodes::opcode_resolver::*}, - keypad::{joypad::Joypad, joypad_provider::JoypadProvider, joypad_register_updater}, - mmu::{carts::mbc::Mbc, gb_mmu::{GbMmu, BOOT_ROM_SIZE}, memory::Memory}, - ppu::{gb_ppu::{CYCLES_PER_FRAME, SCREEN_HEIGHT, SCREEN_WIDTH}} -}; +use crate::{apu::{audio_device::AudioDevice, gb_apu::GbApu}, cpu::{gb_cpu::GbCpu, opcodes::opcode_resolver::*}, keypad::{joypad::Joypad, joypad_provider::JoypadProvider, joypad_register_updater}, mmu::{carts::mbc::Mbc, gb_mmu::{GbMmu, BOOT_ROM_SIZE}, memory::Memory}, ppu::{gb_ppu::{CYCLES_PER_FRAME, SCREEN_HEIGHT, SCREEN_WIDTH}, gfx_device::GfxDevice}, utils::bit_masks::BIT_7_MASK}; + use super::interrupts_handler::InterruptsHandler; use std::boxed::Box; use log::debug; -pub struct GameBoy<'a, JP: JoypadProvider, AD:AudioDevice> { +pub struct GameBoy<'a, JP: JoypadProvider, AD:AudioDevice, GFX:GfxDevice> { cpu: GbCpu, - mmu: GbMmu::<'a, AD>, - opcode_resolver:OpcodeResolver::>, + mmu: GbMmu::<'a, AD, GFX>, + opcode_resolver:OpcodeResolver::>, interrupts_handler:InterruptsHandler, cycles_counter:u32, joypad_provider: JP } -impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ +impl<'a, JP:JoypadProvider, AD:AudioDevice, GFX:GfxDevice> GameBoy<'a, JP, AD, GFX>{ - pub fn new_with_bootrom(mbc:&'a mut Box,joypad_provider:JP, audio_device:AD, boot_rom:[u8;BOOT_ROM_SIZE])->GameBoy{ + pub fn new_with_bootrom(mbc:&'a mut Box,joypad_provider:JP, audio_device:AD, gfx_device:GFX, boot_rom:[u8;BOOT_ROM_SIZE])->GameBoy{ GameBoy{ cpu:GbCpu::default(), - mmu:GbMmu::new_with_bootrom(mbc, boot_rom, GbApu::new(audio_device)), + mmu:GbMmu::new_with_bootrom(mbc, boot_rom, GbApu::new(audio_device), gfx_device), opcode_resolver:OpcodeResolver::default(), interrupts_handler: InterruptsHandler::default(), cycles_counter:0, @@ -32,7 +27,7 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ } } - pub fn new(mbc:&'a mut Box,joypad_provider:JP, audio_device:AD)->GameBoy{ + pub fn new(mbc:&'a mut Box,joypad_provider:JP, audio_device:AD, gfx_device:GFX)->GameBoy{ let mut cpu = GbCpu::default(); //Values after the bootrom *cpu.af.value() = 0x190; @@ -44,7 +39,7 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ GameBoy{ cpu:cpu, - mmu:GbMmu::new(mbc, GbApu::new(audio_device)), + mmu:GbMmu::new(mbc, GbApu::new(audio_device), gfx_device), opcode_resolver:OpcodeResolver::default(), interrupts_handler: InterruptsHandler::default(), cycles_counter:0, @@ -52,10 +47,10 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ } } - pub fn cycle_frame(&mut self)->&[u32;SCREEN_HEIGHT*SCREEN_WIDTH]{ + pub fn cycle_frame(&mut self){ let mut joypad = Joypad::default(); - let mut last_ppu_power_state:bool = self.mmu.io_components.ppu.screen_enable; + let mut last_ppu_power_state:bool = (self.mmu.io_components.ppu.lcd_control & BIT_7_MASK) != 0; while self.cycles_counter < CYCLES_PER_FRAME{ self.joypad_provider.provide(&mut joypad); @@ -80,19 +75,17 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ //In case the ppu just turned I want to keep it sync with the actual screen and thats why Im reseting the loop to finish //the frame when the ppu finishes the frame - if !last_ppu_power_state && self.mmu.io_components.ppu.screen_enable{ + if !last_ppu_power_state && (self.mmu.io_components.ppu.lcd_control & BIT_7_MASK) != 0{ self.cycles_counter = 0; } self.cycles_counter += iter_total_cycles; - last_ppu_power_state = self.mmu.io_components.ppu.screen_enable; + last_ppu_power_state = (self.mmu.io_components.ppu.lcd_control & BIT_7_MASK) != 0; } if self.cycles_counter >= CYCLES_PER_FRAME{ self.cycles_counter -= CYCLES_PER_FRAME; } - - return self.mmu.io_components.ppu.get_frame_buffer(); } fn fetch_next_byte(&mut self)->u8{ @@ -121,7 +114,7 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice> GameBoy<'a, JP, AD>{ - let opcode_func:OpcodeFuncType> = self.opcode_resolver.get_opcode(opcode, &self.mmu, &mut self.cpu.program_counter); + let opcode_func:OpcodeFuncType> = self.opcode_resolver.get_opcode(opcode, &self.mmu, &mut self.cpu.program_counter); match opcode_func{ OpcodeFuncType::OpcodeFunc(func)=>func(&mut self.cpu), OpcodeFuncType::MemoryOpcodeFunc(func)=>func(&mut self.cpu, &mut self.mmu), diff --git a/lib_gb/src/mmu/gb_mmu.rs b/lib_gb/src/mmu/gb_mmu.rs index 85356538..8c434add 100644 --- a/lib_gb/src/mmu/gb_mmu.rs +++ b/lib_gb/src/mmu/gb_mmu.rs @@ -1,5 +1,6 @@ use super::{io_components::IoComponents, memory::*}; use super::access_bus::AccessBus; +use crate::ppu::gfx_device::GfxDevice; use crate::{apu::{audio_device::AudioDevice, gb_apu::GbApu}, utils::memory_registers::BOOT_REGISTER_ADDRESS}; use super::carts::mbc::Mbc; use crate::ppu::ppu_state::PpuState; @@ -12,8 +13,8 @@ const DMA_DEST:u16 = 0xFE00; const BAD_READ_VALUE:u8 = 0xFF; -pub struct GbMmu<'a, D:AudioDevice>{ - pub io_components: IoComponents, +pub struct GbMmu<'a, D:AudioDevice, G:GfxDevice>{ + pub io_components: IoComponents, boot_rom:[u8;BOOT_ROM_SIZE], mbc: &'a mut Box, hram: [u8;HRAM_SIZE], @@ -22,7 +23,7 @@ pub struct GbMmu<'a, D:AudioDevice>{ //DMA only locks the used bus. there 2 possible used buses: extrnal (wram, rom, sram) and video (vram) -impl<'a, D:AudioDevice> Memory for GbMmu<'a, D>{ +impl<'a, D:AudioDevice, G:GfxDevice> Memory for GbMmu<'a, D, G>{ fn read(&self, address:u16)->u8{ if let Some (bus) = &self.io_components.dma.enable{ return match address{ @@ -45,7 +46,7 @@ impl<'a, D:AudioDevice> Memory for GbMmu<'a, D>{ }, 0xFE00..=0xFE9F=>{ if self.is_oam_ready_for_io(){ - return self.io_components.ppu.sprite_attribute_table[(address-0xFE00) as usize]; + return self.io_components.ppu.oam[(address-0xFE00) as usize]; } else{ log::warn!("bad oam read"); @@ -79,7 +80,7 @@ impl<'a, D:AudioDevice> Memory for GbMmu<'a, D>{ }, 0xFE00..=0xFE9F=>{ if self.is_oam_ready_for_io(){ - self.io_components.ppu.sprite_attribute_table[(address-0xFE00) as usize] = value; + self.io_components.ppu.oam[(address-0xFE00) as usize] = value; } else{ log::warn!("bad oam write") @@ -92,7 +93,7 @@ impl<'a, D:AudioDevice> Memory for GbMmu<'a, D>{ } } -impl<'a, D:AudioDevice> UnprotectedMemory for GbMmu<'a, D>{ +impl<'a, D:AudioDevice, G:GfxDevice> UnprotectedMemory for GbMmu<'a, D, G>{ fn read_unprotected(&self, address:u16) ->u8 { return match address{ 0x0..=0xFF=>{ @@ -109,7 +110,7 @@ impl<'a, D:AudioDevice> UnprotectedMemory for GbMmu<'a, D>{ 0xC000..=0xCFFF =>self.io_components.ram.read_bank0(address - 0xC000), 0xD000..=0xDFFF=>self.io_components.ram.read_current_bank(address-0xD000), 0xE000..=0xFDFF=>self.io_components.ram.read_bank0(address - 0xE000), - 0xFE00..=0xFE9F=>self.io_components.ppu.sprite_attribute_table[(address-0xFE00) as usize], + 0xFE00..=0xFE9F=>self.io_components.ppu.oam[(address-0xFE00) as usize], 0xFEA0..=0xFEFF=>0x0, 0xFF00..=0xFF7F=>self.io_components.read_unprotected(address - 0xFF00), 0xFF80..=0xFFFE=>self.hram[(address-0xFF80) as usize], @@ -125,7 +126,7 @@ impl<'a, D:AudioDevice> UnprotectedMemory for GbMmu<'a, D>{ 0xC000..=0xCFFF =>self.io_components.ram.write_bank0(address - 0xC000,value), 0xE000..=0xFDFF=>self.io_components.ram.write_bank0(address - 0xE000,value), 0xD000..=0xDFFF=>self.io_components.ram.write_current_bank(address-0xD000,value), - 0xFE00..=0xFE9F=>self.io_components.ppu.sprite_attribute_table[(address-0xFE00) as usize] = value, + 0xFE00..=0xFE9F=>self.io_components.ppu.oam[(address-0xFE00) as usize] = value, 0xFEA0..=0xFEFF=>{}, 0xFF00..=0xFF7F=>self.io_components.write_unprotected(address - 0xFF00, value), 0xFF80..=0xFFFE=>self.hram[(address-0xFF80) as usize] = value, @@ -134,10 +135,10 @@ impl<'a, D:AudioDevice> UnprotectedMemory for GbMmu<'a, D>{ } } -impl<'a, D:AudioDevice> GbMmu<'a, D>{ - pub fn new_with_bootrom(mbc:&'a mut Box, boot_rom:[u8;BOOT_ROM_SIZE], apu:GbApu)->Self{ +impl<'a, D:AudioDevice, G:GfxDevice> GbMmu<'a, D, G>{ + pub fn new_with_bootrom(mbc:&'a mut Box, boot_rom:[u8;BOOT_ROM_SIZE], apu:GbApu, gfx_device:G)->Self{ GbMmu{ - io_components:IoComponents::new(apu), + io_components:IoComponents::new(apu, gfx_device), mbc:mbc, hram:[0;HRAM_SIZE], interupt_enable_register:0, @@ -145,9 +146,9 @@ impl<'a, D:AudioDevice> GbMmu<'a, D>{ } } - pub fn new(mbc:&'a mut Box, apu:GbApu)->Self{ + pub fn new(mbc:&'a mut Box, apu:GbApu, gfx_device: G)->Self{ let mut mmu = GbMmu{ - io_components:IoComponents::new(apu), + io_components:IoComponents::new(apu, gfx_device), mbc:mbc, hram:[0;HRAM_SIZE], interupt_enable_register:0, diff --git a/lib_gb/src/mmu/io_components.rs b/lib_gb/src/mmu/io_components.rs index 7a3449d9..dbb41f92 100644 --- a/lib_gb/src/mmu/io_components.rs +++ b/lib_gb/src/mmu/io_components.rs @@ -1,5 +1,4 @@ -use crate::{apu::{audio_device::AudioDevice, gb_apu::GbApu, set_nr11, set_nr12, set_nr13}, ppu::ppu_register_updater::*, timer::timer_register_updater::*, utils::memory_registers::*}; -use crate::ppu::gb_ppu::GbPpu; +use crate::{apu::{audio_device::AudioDevice, gb_apu::GbApu, set_nr11, set_nr12, set_nr13}, ppu::{fifo_ppu::FifoPpu, fifo_register_updater::*, gfx_device::GfxDevice}, timer::timer_register_updater::*, utils::memory_registers::*}; use crate::apu::*; use crate::timer::gb_timer::GbTimer; use super::{access_bus::AccessBus, memory::*, oam_dma_transfer::OamDmaTransfer, ram::Ram}; @@ -9,11 +8,11 @@ use super::io_ports::*; pub const IO_PORTS_SIZE:usize = 0x80; -pub struct IoComponents{ +pub struct IoComponents{ pub ram: Ram, pub apu: GbApu, pub timer: GbTimer, - pub ppu:GbPpu, + pub ppu:FifoPpu, ports:[u8;IO_PORTS_SIZE], pub dma:OamDmaTransfer, pub finished_boot:bool, @@ -35,7 +34,7 @@ io_port_index!(OBP1_REGISTER_INDEX, OBP1_REGISTER_ADDRESS); io_port_index!(IF_REGISTER_INDEX, IF_REGISTER_ADDRESS); -impl Memory for IoComponents{ +impl Memory for IoComponents{ fn read(&self, address:u16)->u8 { let mut value = self.ports[address as usize]; return match address { @@ -131,8 +130,8 @@ impl Memory for IoComponents{ } } BGP_REGISTER_INDEX=> handle_bg_pallet_register(value,&mut self.ppu.bg_color_mapping), - OBP0_REGISTER_INDEX=> handle_obp_pallet_register(value,&mut self.ppu.obj_color_mapping0), - OBP1_REGISTER_INDEX=> handle_obp_pallet_register(value,&mut self.ppu.obj_color_mapping1), + //OBP0_REGISTER_INDEX=> handle_obp_pallet_register(value,&mut self.ppu.obj_color_mapping0), + //OBP1_REGISTER_INDEX=> handle_obp_pallet_register(value,&mut self.ppu.obj_color_mapping1), WY_REGISTER_INDEX=> handle_wy_register(value, &mut self.ppu), WX_REGISTER_INDEX=> handle_wx_register(value, &mut self.ppu), BOOT_REGISTER_INDEX=> self.finished_boot = value != 0, @@ -148,7 +147,7 @@ impl Memory for IoComponents{ } } -impl UnprotectedMemory for IoComponents{ +impl UnprotectedMemory for IoComponents{ fn read_unprotected(&self, address:u16)->u8 { self.ports[address as usize] } @@ -158,16 +157,16 @@ impl UnprotectedMemory for IoComponents{ } } -impl IoComponents{ - pub fn new(apu:GbApu)->Self{ - Self{apu, ports:[0;IO_PORTS_SIZE], timer:GbTimer::default(), ppu:GbPpu::default(), dma:OamDmaTransfer::default(),finished_boot:false, ram:Ram::default()} +impl IoComponents{ + pub fn new(apu:GbApu, gfx_device:GFX)->Self{ + Self{apu, ports:[0;IO_PORTS_SIZE], timer:GbTimer::default(), ppu:FifoPpu::new(gfx_device), dma:OamDmaTransfer::default(),finished_boot:false, ram:Ram::default()} } pub fn cycle(&mut self, cycles:u32){ let mut if_register = self.ports[IF_REGISTER_INDEX as usize]; self.timer.cycle(&mut if_register, cycles as u8); self.apu.cycle(cycles as u8); - self.ppu.update_gb_screen(&mut if_register, cycles); + self.ppu.cycle( cycles as u8, &mut if_register); self.ports[IF_REGISTER_INDEX as usize] = if_register; } } \ No newline at end of file diff --git a/lib_gb/src/ppu/fifo_ppu.rs b/lib_gb/src/ppu/fifo_ppu.rs index 41d9d49a..b2a92967 100644 --- a/lib_gb/src/ppu/fifo_ppu.rs +++ b/lib_gb/src/ppu/fifo_ppu.rs @@ -4,6 +4,7 @@ use crate::utils::{vec2::Vec2, bit_masks::*}; use crate::mmu::vram::VRam; use super::color::Color; use super::colors::*; +use super::gfx_device::GfxDevice; use super::{ppu_state::PpuState, sprite_attribute::SpriteAttribute}; enum FethcingState{ @@ -13,13 +14,15 @@ enum FethcingState{ Push(u8,u8) } -pub struct FifoPpu{ +pub struct FifoPpu{ + gfx_device: GFX, + oam_entries:[SpriteAttribute; 10], - vram: VRam, - oam:[u8;0xA0], + pub vram: VRam, + pub oam:[u8;0xA0], current_oam_entry:u8, t_cycles_passed:u16, - state:PpuState, + pub state:PpuState, pub lcd_control:u8, pub stat_register:u8, pub lyc_register:u8, @@ -27,7 +30,7 @@ pub struct FifoPpu{ pub window_pos:Vec2, pub bg_pos:Vec2, pixel_fething_state: FethcingState, - bg_color_mapping: [Color; 4], + pub bg_color_mapping: [Color; 4], screen_buffer: [u32; 160*144], screen_buffer_index:usize, @@ -39,11 +42,12 @@ pub struct FifoPpu{ pub coincidence_interrupt_request:bool, pos_counter: Vec2, - bg_fifo: Vec + bg_fifo: Vec, } -impl Default for FifoPpu{ - fn default() -> Self { +impl FifoPpu{ + + pub fn new(device:GFX) -> Self { let oam_entries = { let mut data: [MaybeUninit; 10] = unsafe{ MaybeUninit::uninit().assume_init() @@ -57,6 +61,7 @@ impl Default for FifoPpu{ }; Self{ + gfx_device: device, vram: VRam::default(), oam: [0;0xA0], stat_register: 0, @@ -79,18 +84,32 @@ impl Default for FifoPpu{ pixel_fething_state:FethcingState::TileNumber, screen_buffer_index:0, t_cycles_passed:0, - bg_fifo:Vec::::with_capacity(16) + bg_fifo:Vec::::with_capacity(16), } } -} -impl FifoPpu{ + pub fn turn_off(&mut self){ + self.screen_buffer_index = 0; + self.t_cycles_passed = 0; + unsafe{ + std::ptr::write_bytes(self.screen_buffer.as_mut_ptr(), 0xFF, self.screen_buffer.len()); + } + self.gfx_device.swap_buffer(&self.screen_buffer); + self.state = PpuState::Hblank; + self.bg_fifo.clear(); + self.ly_register = 0; + + } - pub fn get_frame_buffer(&self)->&[u32; 160 * 144]{ - &self.screen_buffer + pub fn turn_on(&mut self){ + self.state = PpuState::OamSearch; } pub fn cycle(&mut self, m_cycles:u8, if_register:&mut u8){ + if self.lcd_control & BIT_7_MASK == 0{ + return; + } + let pixels = self.cycle_fetcher(m_cycles, if_register); //update stat register @@ -102,43 +121,40 @@ impl FifoPpu{ self.screen_buffer[self.screen_buffer_index] = Self::color_as_uint(&p); self.screen_buffer_index += 1; if self.screen_buffer_index == self.screen_buffer.len(){ + self.gfx_device.swap_buffer(&self.screen_buffer); self.screen_buffer_index = 0; - unsafe{ - std::ptr::write_bytes(self.screen_buffer.as_mut_ptr(), 0, self.screen_buffer.len()); - } } } } fn cycle_fetcher(&mut self, m_cycles:u8, if_register:&mut u8)->Vec{ - let sprite_height = if self.lcd_control & BIT_2_MASK != 0 {16} else {8}; + let sprite_height = if (self.lcd_control & BIT_2_MASK) != 0 {16} else {8}; let mut pixels_to_push_to_lcd = Vec::::new(); for _ in 0..m_cycles{ match self.state{ - PpuState::OamSearch=>{ - for _ in 0..2{ - self.t_cycles_passed += 2; //half a m_cycle - let oam_index = self.t_cycles_passed / 2; - let oam_entry_address = (oam_index * 4) as usize; - let end_y = self.oam[oam_entry_address]; - let end_x = self.oam[oam_entry_address + 1]; - - if end_x > 0 && self.ly_register + 16 >= end_y && self.ly_register + 16 < end_y + sprite_height && self.current_oam_entry < 10{ - let tile_number = self.oam[oam_entry_address + 2]; - let attributes = self.oam[oam_entry_address + 3]; - self.oam_entries[self.current_oam_entry as usize] = SpriteAttribute::new(end_y, end_x, tile_number, attributes); - self.current_oam_entry += 1; - } + PpuState::OamSearch=>{ + let oam_index = self.t_cycles_passed / 2; + let oam_entry_address = (oam_index * 4) as usize; + let end_y = self.oam[oam_entry_address]; + let end_x = self.oam[oam_entry_address + 1]; + + if end_x > 0 && self.ly_register + 16 >= end_y && self.ly_register + 16 < end_y + sprite_height && self.current_oam_entry < 10{ + let tile_number = self.oam[oam_entry_address + 2]; + let attributes = self.oam[oam_entry_address + 3]; + self.oam_entries[self.current_oam_entry as usize] = SpriteAttribute::new(end_y, end_x, tile_number, attributes); + self.current_oam_entry += 1; } - + + self.t_cycles_passed += 2; //half a m_cycle + if self.t_cycles_passed == 80{ self.state = PpuState::PixelTransfer; + self.pixel_fething_state = FethcingState::TileNumber; } } PpuState::Hblank=>{ - self.t_cycles_passed += 4; if self.t_cycles_passed == 456{ if self.ly_register == 143{ self.state = PpuState::Vblank; @@ -155,9 +171,10 @@ impl FifoPpu{ self.t_cycles_passed = 0; self.ly_register += 1; } + + self.t_cycles_passed += 2; } PpuState::Vblank=>{ - self.t_cycles_passed += 4; if self.t_cycles_passed == 4560{ self.state = PpuState::OamSearch; if self.oam_search_interrupt_request{ @@ -167,69 +184,75 @@ impl FifoPpu{ self.ly_register = 0; } else{ - self.ly_register = 144 + (self.t_cycles_passed % 456) as u8; + self.ly_register = 144 + (self.t_cycles_passed / 456) as u8; } + + self.t_cycles_passed += 2; } PpuState::PixelTransfer=>{ - match self.pixel_fething_state{ - FethcingState::TileNumber=>{ - let tile_num = if self.is_redering_wnd(){ - let tile_map_address:u16 = if self.lcd_control & BIT_6_MASK == 0 {0x1800} else {0x1C00}; - self.vram.read_current_bank(tile_map_address + ((32 * (self.pos_counter. y / 8)) + (self.pos_counter.x / 8) )as u16) - } - else{ - let tile_map_address = if self.lcd_control & BIT_3_MASK == 0 {0x1800} else {0x1C00}; - let scx_offset = self.bg_pos.x as u16 / 8; - let scy_offset = (((self.bg_pos.y + self.pos_counter.y) & 0xFF) / 8) as u16; - self.vram.read_current_bank(tile_map_address + (32 * scy_offset + (self.pos_counter.x as u16 + scx_offset) & 31)) - }; + for _ in 0..2{ + match self.pixel_fething_state{ + FethcingState::TileNumber=>{ + let tile_num = if self.is_redering_wnd(){ + let tile_map_address:u16 = if (self.lcd_control & BIT_6_MASK) == 0 {0x1800} else {0x1C00}; + self.vram.read_current_bank(tile_map_address + ((32 * (self.pos_counter. y / 8)) + (self.pos_counter.x / 8) )as u16) + } + else{ + let tile_map_address = if (self.lcd_control & BIT_3_MASK) == 0 {0x1800} else {0x1C00}; + let scx_offset = self.bg_pos.x as u16 / 8; + let scy_offset = (((self.bg_pos.y + self.pos_counter.y) & 0xFF) / 8) as u16; + self.vram.read_current_bank(tile_map_address + (32 * scy_offset + (self.pos_counter.x as u16 + scx_offset) & 31)) + }; - self.pixel_fething_state = FethcingState::LowTileData(tile_num); - self.t_cycles_passed += 2; - } - FethcingState::LowTileData(tile_num)=>{ - let current_tile_base_data_address = if self.lcd_control & BIT_4_MASK == 0 && tile_num & BIT_7_MASK == 0 {0x1000} else {0}; - let current_tile_data_address = current_tile_base_data_address + (tile_num as u16 * 16); - let low_data = if self.is_redering_wnd(){ - self.vram.read_current_bank(current_tile_data_address + (2 * (self.pos_counter.y % 8)) as u16) - } else{ - self.vram.read_current_bank(current_tile_data_address + (2 * ((self.bg_pos.y + self.ly_register) % 8)) as u16) - }; + self.pixel_fething_state = FethcingState::LowTileData(tile_num); + self.t_cycles_passed += 2; + } + FethcingState::LowTileData(tile_num)=>{ + let current_tile_base_data_address = if (self.lcd_control & BIT_4_MASK) == 0 && (tile_num & BIT_7_MASK) == 0 {0x1000} else {0}; + let current_tile_data_address = current_tile_base_data_address + (tile_num as u16 * 16); + let low_data = if self.is_redering_wnd(){ + self.vram.read_current_bank(current_tile_data_address + (2 * (self.pos_counter.y % 8)) as u16) + } else{ + self.vram.read_current_bank(current_tile_data_address + (2 * ((self.bg_pos.y + self.ly_register) % 8)) as u16) + }; - self.pixel_fething_state = FethcingState::HighTileData(tile_num, low_data); - self.t_cycles_passed += 2; - } - FethcingState::HighTileData(tile_num, low_data)=>{ - let current_tile_base_data_address = if self.lcd_control & BIT_4_MASK == 0 && tile_num & BIT_7_MASK == 0 {0x1000} else {0}; - let current_tile_data_address = current_tile_base_data_address + (tile_num as u16 * 16); - let high_data = if self.is_redering_wnd(){ - self.vram.read_current_bank(current_tile_data_address + (2 * (self.pos_counter.y % 8)) as u16 + 1) - } else{ - self.vram.read_current_bank(current_tile_data_address + (2 * ((self.bg_pos.y + self.ly_register) % 8)) as u16 + 1) - }; + self.pixel_fething_state = FethcingState::HighTileData(tile_num, low_data); + self.t_cycles_passed += 2; + } + FethcingState::HighTileData(tile_num, low_data)=>{ + let current_tile_base_data_address = if (self.lcd_control & BIT_4_MASK) == 0 && (tile_num & BIT_7_MASK) == 0 {0x1000} else {0}; + let current_tile_data_address = current_tile_base_data_address + (tile_num as u16 * 16); + let high_data = if self.is_redering_wnd(){ + self.vram.read_current_bank(current_tile_data_address + (2 * (self.pos_counter.y % 8)) as u16 + 1) + } else{ + self.vram.read_current_bank(current_tile_data_address + (2 * ((self.bg_pos.y + self.ly_register) % 8)) as u16 + 1) + }; - self.pixel_fething_state = FethcingState::Push(low_data, high_data); - self.t_cycles_passed += 2; - } - FethcingState::Push(low_data, high_data)=>{ - for i in (0..8).rev(){ - let mask = 1 << i; - let mut pixel = (low_data & mask) >> i; - pixel |= (high_data & mask) >> (i - 1); - self.bg_fifo.push(pixel); + self.pixel_fething_state = FethcingState::Push(low_data, high_data); + self.t_cycles_passed += 2; } + FethcingState::Push(low_data, high_data)=>{ + for i in (0..8).rev(){ + let mask = 1 << i; + let mut pixel = (low_data & mask) >> i; + pixel |= ((high_data & mask) >> i) << 1; + self.bg_fifo.push(pixel); + } - self.pixel_fething_state = FethcingState::TileNumber; - self.t_cycles_passed += 2; + self.pixel_fething_state = FethcingState::TileNumber; + self.t_cycles_passed += 2; + + self.pos_counter.x += 8; + } } - } - if self.pos_counter.x == 160{ - self.state = PpuState::Hblank; - if self.h_blank_interrupt_request{ - *if_register |= BIT_1_MASK; + if self.pos_counter.x == 160{ + self.state = PpuState::Hblank; + if self.h_blank_interrupt_request{ + *if_register |= BIT_1_MASK; + } + self.pos_counter.x = 0; } - self.pos_counter.x = 0; } } } @@ -251,10 +274,10 @@ impl FifoPpu{ } fn is_redering_wnd(&self)->bool{ - self.window_pos.x >= self.bg_pos.x && self.window_pos.y >= self.bg_pos.y && self.lcd_control & BIT_5_MASK != 0 + self.window_pos.x >= self.bg_pos.x && self.window_pos.y >= self.bg_pos.y && (self.lcd_control & BIT_5_MASK) != 0 } - const fn color_as_uint(color: &Color) -> u32 { + fn color_as_uint(color: &Color) -> u32 { ((color.r as u32) << 16) | ((color.g as u32) << 8) | (color.b as u32) } } diff --git a/lib_gb/src/ppu/fifo_register_updater.rs b/lib_gb/src/ppu/fifo_register_updater.rs index 27750da6..ddcc1877 100644 --- a/lib_gb/src/ppu/fifo_register_updater.rs +++ b/lib_gb/src/ppu/fifo_register_updater.rs @@ -1,13 +1,20 @@ use crate::utils::bit_masks::*; -use super::{ fifo_ppu::FifoPpu, color::*, colors::*}; +use super::{color::*, colors::*, fifo_ppu::FifoPpu, gfx_device::GfxDevice}; const WX_OFFSET:u8 = 7; -pub fn handle_lcdcontrol_register( register:u8, ppu:&mut FifoPpu){ +pub fn handle_lcdcontrol_register( register:u8, ppu:&mut FifoPpu){ + if ppu.lcd_control & BIT_7_MASK != 0 && register & BIT_7_MASK == 0{ + ppu.turn_off(); + } + else if ppu.lcd_control & BIT_7_MASK == 0 && register & BIT_7_MASK != 0{ + ppu.turn_on(); + } + ppu.lcd_control = register; } -pub fn update_stat_register(register:u8, ppu: &mut FifoPpu){ +pub fn update_stat_register(register:u8, ppu: &mut FifoPpu){ ppu.h_blank_interrupt_request = register & BIT_3_MASK != 0; ppu.v_blank_interrupt_request = register & BIT_4_MASK != 0; ppu.oam_search_interrupt_request = register & BIT_5_MASK != 0; @@ -16,11 +23,11 @@ pub fn update_stat_register(register:u8, ppu: &mut FifoPpu){ ppu.stat_register = register & 0b111_1000; } -pub fn set_scx(ppu: &mut FifoPpu, value:u8){ +pub fn set_scx(ppu: &mut FifoPpu, value:u8){ ppu.bg_pos.x = value; } -pub fn set_scy(ppu:&mut FifoPpu, value:u8){ +pub fn set_scy(ppu:&mut FifoPpu, value:u8){ ppu.bg_pos.y = value; } @@ -48,11 +55,11 @@ fn get_matching_color(number:u8)->Color{ }; } -pub fn handle_wy_register(register:u8, ppu:&mut FifoPpu){ +pub fn handle_wy_register(register:u8, ppu:&mut FifoPpu){ ppu.window_pos.y = register; } -pub fn handle_wx_register(register:u8, ppu:&mut FifoPpu){ +pub fn handle_wx_register(register:u8, ppu:&mut FifoPpu){ if register < WX_OFFSET{ ppu.window_pos.x = 0; } @@ -61,14 +68,14 @@ pub fn handle_wx_register(register:u8, ppu:&mut FifoPpu){ } } -pub fn get_ly(ppu:&FifoPpu)->u8{ +pub fn get_ly(ppu:&FifoPpu)->u8{ ppu.ly_register } -pub fn get_stat(ppu:&FifoPpu)->u8{ +pub fn get_stat(ppu:&FifoPpu)->u8{ ppu.stat_register } -pub fn set_lyc(ppu:&mut FifoPpu, value:u8){ +pub fn set_lyc(ppu:&mut FifoPpu, value:u8){ ppu.lyc_register = value; } diff --git a/lib_gb/src/ppu/gfx_device.rs b/lib_gb/src/ppu/gfx_device.rs new file mode 100644 index 00000000..400f51a2 --- /dev/null +++ b/lib_gb/src/ppu/gfx_device.rs @@ -0,0 +1,3 @@ +pub trait GfxDevice{ + fn swap_buffer(&self, buffer:&[u32]); +} \ No newline at end of file diff --git a/lib_gb/src/ppu/mod.rs b/lib_gb/src/ppu/mod.rs index db8169df..6c200387 100644 --- a/lib_gb/src/ppu/mod.rs +++ b/lib_gb/src/ppu/mod.rs @@ -4,7 +4,8 @@ pub mod color; pub mod colors; pub mod ppu_register_updater; pub mod fifo_register_updater; -mod fifo_ppu; +pub mod fifo_ppu; +pub mod gfx_device; mod normal_sprite; mod sprite_attribute; mod extended_sprite; From 3ce9e29672949f43b91e7a082aa5b9285b4938b7 Mon Sep 17 00:00:00 2001 From: alloncm Date: Mon, 26 Jul 2021 14:45:30 +0300 Subject: [PATCH 031/136] Fix the fetiching tile number stage in the new ppu The bootrom npw displays the logo! --- lib_gb/src/ppu/fifo_ppu.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib_gb/src/ppu/fifo_ppu.rs b/lib_gb/src/ppu/fifo_ppu.rs index b2a92967..788f5692 100644 --- a/lib_gb/src/ppu/fifo_ppu.rs +++ b/lib_gb/src/ppu/fifo_ppu.rs @@ -199,9 +199,10 @@ impl FifoPpu{ } else{ let tile_map_address = if (self.lcd_control & BIT_3_MASK) == 0 {0x1800} else {0x1C00}; - let scx_offset = self.bg_pos.x as u16 / 8; - let scy_offset = (((self.bg_pos.y + self.pos_counter.y) & 0xFF) / 8) as u16; - self.vram.read_current_bank(tile_map_address + (32 * scy_offset + (self.pos_counter.x as u16 + scx_offset) & 31)) + let scx_offset = ((self.bg_pos.x as u16 + self.pos_counter.x as u16) / 8 ) & 31; + let scy_offset = ((self.bg_pos.y as u16 + self.ly_register as u16) & 0xFF) / 8; + + self.vram.read_current_bank(tile_map_address + ((32 * scy_offset) + scx_offset)) }; self.pixel_fething_state = FethcingState::LowTileData(tile_num); From 3a299f77dc31af7219144dbfcb9e79d076a65e2b Mon Sep 17 00:00:00 2001 From: alloncm Date: Mon, 26 Jul 2021 15:37:13 +0300 Subject: [PATCH 032/136] Fix the timing issue with the fifo ppu --- gb/src/sdl_gfx_device.rs | 2 +- lib_gb/src/ppu/fifo_ppu.rs | 114 ++++++++++++++++++------------------- 2 files changed, 57 insertions(+), 59 deletions(-) diff --git a/gb/src/sdl_gfx_device.rs b/gb/src/sdl_gfx_device.rs index 42fab877..06d1c3d2 100644 --- a/gb/src/sdl_gfx_device.rs +++ b/gb/src/sdl_gfx_device.rs @@ -70,7 +70,7 @@ impl GfxDevice for SdlGfxDevice{ std::ptr::copy_nonoverlapping(extended_buffer.as_ptr(),pixels as *mut u32, extended_buffer.len()); SDL_UnlockTexture(self.texture); - SDL_RenderClear(self.renderer); + //There is no need to call SDL_RenderClear since im replacing the whole buffer SDL_RenderCopy(self.renderer, self.texture, std::ptr::null(), std::ptr::null()); SDL_RenderPresent(self.renderer); } diff --git a/lib_gb/src/ppu/fifo_ppu.rs b/lib_gb/src/ppu/fifo_ppu.rs index 788f5692..82b55a46 100644 --- a/lib_gb/src/ppu/fifo_ppu.rs +++ b/lib_gb/src/ppu/fifo_ppu.rs @@ -132,7 +132,7 @@ impl FifoPpu{ let mut pixels_to_push_to_lcd = Vec::::new(); - for _ in 0..m_cycles{ + for _ in 0..m_cycles * 2{ match self.state{ PpuState::OamSearch=>{ let oam_index = self.t_cycles_passed / 2; @@ -155,6 +155,8 @@ impl FifoPpu{ } } PpuState::Hblank=>{ + self.t_cycles_passed += 2; + if self.t_cycles_passed == 456{ if self.ly_register == 143{ self.state = PpuState::Vblank; @@ -171,8 +173,6 @@ impl FifoPpu{ self.t_cycles_passed = 0; self.ly_register += 1; } - - self.t_cycles_passed += 2; } PpuState::Vblank=>{ if self.t_cycles_passed == 4560{ @@ -190,70 +190,68 @@ impl FifoPpu{ self.t_cycles_passed += 2; } PpuState::PixelTransfer=>{ - for _ in 0..2{ - match self.pixel_fething_state{ - FethcingState::TileNumber=>{ - let tile_num = if self.is_redering_wnd(){ - let tile_map_address:u16 = if (self.lcd_control & BIT_6_MASK) == 0 {0x1800} else {0x1C00}; - self.vram.read_current_bank(tile_map_address + ((32 * (self.pos_counter. y / 8)) + (self.pos_counter.x / 8) )as u16) - } - else{ - let tile_map_address = if (self.lcd_control & BIT_3_MASK) == 0 {0x1800} else {0x1C00}; - let scx_offset = ((self.bg_pos.x as u16 + self.pos_counter.x as u16) / 8 ) & 31; - let scy_offset = ((self.bg_pos.y as u16 + self.ly_register as u16) & 0xFF) / 8; + match self.pixel_fething_state{ + FethcingState::TileNumber=>{ + let tile_num = if self.is_redering_wnd(){ + let tile_map_address:u16 = if (self.lcd_control & BIT_6_MASK) == 0 {0x1800} else {0x1C00}; + self.vram.read_current_bank(tile_map_address + ((32 * (self.pos_counter. y / 8)) + (self.pos_counter.x / 8) )as u16) + } + else{ + let tile_map_address = if (self.lcd_control & BIT_3_MASK) == 0 {0x1800} else {0x1C00}; + let scx_offset = ((self.bg_pos.x as u16 + self.pos_counter.x as u16) / 8 ) & 31; + let scy_offset = ((self.bg_pos.y as u16 + self.ly_register as u16) & 0xFF) / 8; - self.vram.read_current_bank(tile_map_address + ((32 * scy_offset) + scx_offset)) - }; + self.vram.read_current_bank(tile_map_address + ((32 * scy_offset) + scx_offset)) + }; - self.pixel_fething_state = FethcingState::LowTileData(tile_num); - self.t_cycles_passed += 2; - } - FethcingState::LowTileData(tile_num)=>{ - let current_tile_base_data_address = if (self.lcd_control & BIT_4_MASK) == 0 && (tile_num & BIT_7_MASK) == 0 {0x1000} else {0}; - let current_tile_data_address = current_tile_base_data_address + (tile_num as u16 * 16); - let low_data = if self.is_redering_wnd(){ - self.vram.read_current_bank(current_tile_data_address + (2 * (self.pos_counter.y % 8)) as u16) - } else{ - self.vram.read_current_bank(current_tile_data_address + (2 * ((self.bg_pos.y + self.ly_register) % 8)) as u16) - }; + self.pixel_fething_state = FethcingState::LowTileData(tile_num); + self.t_cycles_passed += 2; + } + FethcingState::LowTileData(tile_num)=>{ + let current_tile_base_data_address = if (self.lcd_control & BIT_4_MASK) == 0 && (tile_num & BIT_7_MASK) == 0 {0x1000} else {0}; + let current_tile_data_address = current_tile_base_data_address + (tile_num as u16 * 16); + let low_data = if self.is_redering_wnd(){ + self.vram.read_current_bank(current_tile_data_address + (2 * (self.pos_counter.y % 8)) as u16) + } else{ + self.vram.read_current_bank(current_tile_data_address + (2 * ((self.bg_pos.y + self.ly_register) % 8)) as u16) + }; - self.pixel_fething_state = FethcingState::HighTileData(tile_num, low_data); - self.t_cycles_passed += 2; - } - FethcingState::HighTileData(tile_num, low_data)=>{ - let current_tile_base_data_address = if (self.lcd_control & BIT_4_MASK) == 0 && (tile_num & BIT_7_MASK) == 0 {0x1000} else {0}; - let current_tile_data_address = current_tile_base_data_address + (tile_num as u16 * 16); - let high_data = if self.is_redering_wnd(){ - self.vram.read_current_bank(current_tile_data_address + (2 * (self.pos_counter.y % 8)) as u16 + 1) - } else{ - self.vram.read_current_bank(current_tile_data_address + (2 * ((self.bg_pos.y + self.ly_register) % 8)) as u16 + 1) - }; + self.pixel_fething_state = FethcingState::HighTileData(tile_num, low_data); + self.t_cycles_passed += 2; + } + FethcingState::HighTileData(tile_num, low_data)=>{ + let current_tile_base_data_address = if (self.lcd_control & BIT_4_MASK) == 0 && (tile_num & BIT_7_MASK) == 0 {0x1000} else {0}; + let current_tile_data_address = current_tile_base_data_address + (tile_num as u16 * 16); + let high_data = if self.is_redering_wnd(){ + self.vram.read_current_bank(current_tile_data_address + (2 * (self.pos_counter.y % 8)) as u16 + 1) + } else{ + self.vram.read_current_bank(current_tile_data_address + (2 * ((self.bg_pos.y + self.ly_register) % 8)) as u16 + 1) + }; - self.pixel_fething_state = FethcingState::Push(low_data, high_data); - self.t_cycles_passed += 2; + self.pixel_fething_state = FethcingState::Push(low_data, high_data); + self.t_cycles_passed += 2; + } + FethcingState::Push(low_data, high_data)=>{ + for i in (0..8).rev(){ + let mask = 1 << i; + let mut pixel = (low_data & mask) >> i; + pixel |= ((high_data & mask) >> i) << 1; + self.bg_fifo.push(pixel); } - FethcingState::Push(low_data, high_data)=>{ - for i in (0..8).rev(){ - let mask = 1 << i; - let mut pixel = (low_data & mask) >> i; - pixel |= ((high_data & mask) >> i) << 1; - self.bg_fifo.push(pixel); - } - self.pixel_fething_state = FethcingState::TileNumber; - self.t_cycles_passed += 2; - - self.pos_counter.x += 8; - } + self.pixel_fething_state = FethcingState::TileNumber; + self.t_cycles_passed += 2; + + self.pos_counter.x += 8; } + } - if self.pos_counter.x == 160{ - self.state = PpuState::Hblank; - if self.h_blank_interrupt_request{ - *if_register |= BIT_1_MASK; - } - self.pos_counter.x = 0; + if self.pos_counter.x == 160{ + self.state = PpuState::Hblank; + if self.h_blank_interrupt_request{ + *if_register |= BIT_1_MASK; } + self.pos_counter.x = 0; } } } From 58dc36f7a65db2506e61e94fc30e26d43cbdb878 Mon Sep 17 00:00:00 2001 From: alloncm Date: Mon, 26 Jul 2021 16:01:52 +0300 Subject: [PATCH 033/136] FIx a vblank interrupt not fired --- lib_gb/src/ppu/fifo_ppu.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/lib_gb/src/ppu/fifo_ppu.rs b/lib_gb/src/ppu/fifo_ppu.rs index 82b55a46..728f4a1d 100644 --- a/lib_gb/src/ppu/fifo_ppu.rs +++ b/lib_gb/src/ppu/fifo_ppu.rs @@ -160,6 +160,7 @@ impl FifoPpu{ if self.t_cycles_passed == 456{ if self.ly_register == 143{ self.state = PpuState::Vblank; + *if_register |= BIT_0_MASK; if self.v_blank_interrupt_request{ *if_register |= BIT_1_MASK; } From a43c0957efe2c3dc18ff8ec85c379e4ca398bdfd Mon Sep 17 00:00:00 2001 From: alloncm Date: Mon, 26 Jul 2021 18:31:08 +0300 Subject: [PATCH 034/136] Hope I fixed the stat interrupt trigger --- lib_gb/src/ppu/fifo_ppu.rs | 47 +++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/lib_gb/src/ppu/fifo_ppu.rs b/lib_gb/src/ppu/fifo_ppu.rs index 728f4a1d..6ad8d454 100644 --- a/lib_gb/src/ppu/fifo_ppu.rs +++ b/lib_gb/src/ppu/fifo_ppu.rs @@ -43,6 +43,8 @@ pub struct FifoPpu{ pos_counter: Vec2, bg_fifo: Vec, + stat_triggered:bool, + trigger_stat_interrupt:bool, } impl FifoPpu{ @@ -84,7 +86,9 @@ impl FifoPpu{ pixel_fething_state:FethcingState::TileNumber, screen_buffer_index:0, t_cycles_passed:0, - bg_fifo:Vec::::with_capacity(16), + bg_fifo:Vec::::with_capacity(8), + stat_triggered:false, + trigger_stat_interrupt:false, } } @@ -98,7 +102,8 @@ impl FifoPpu{ self.state = PpuState::Hblank; self.bg_fifo.clear(); self.ly_register = 0; - + self.stat_triggered = false; + self.trigger_stat_interrupt = false; } pub fn turn_on(&mut self){ @@ -116,6 +121,26 @@ impl FifoPpu{ self.stat_register &= 0b1111_1100; //clear first 2 bits self.stat_register |= self.state as u8; + if self.ly_register == self.lyc_register{ + if self.coincidence_interrupt_request { + self.stat_triggered = true; + } + self.stat_register |= BIT_2_MASK; + } + else{ + self.stat_register &= !BIT_2_MASK; + } + + if self.trigger_stat_interrupt{ + if !self.stat_triggered{ + *if_register |= BIT_1_MASK; + self.stat_triggered = true; + } + } + else{ + self.stat_triggered = false; + } + for pixel in pixels.as_slice(){ let p = self.bg_color_mapping[*pixel as usize].clone(); self.screen_buffer[self.screen_buffer_index] = Self::color_as_uint(&p); @@ -162,13 +187,13 @@ impl FifoPpu{ self.state = PpuState::Vblank; *if_register |= BIT_0_MASK; if self.v_blank_interrupt_request{ - *if_register |= BIT_1_MASK; + self.stat_triggered = true; } } else{ self.state = PpuState::OamSearch; if self.oam_search_interrupt_request{ - *if_register |= BIT_1_MASK; + self.stat_triggered = true; } } self.t_cycles_passed = 0; @@ -179,7 +204,7 @@ impl FifoPpu{ if self.t_cycles_passed == 4560{ self.state = PpuState::OamSearch; if self.oam_search_interrupt_request{ - *if_register |= BIT_1_MASK; + self.stat_triggered = true; } self.t_cycles_passed = 0; self.ly_register = 0; @@ -250,22 +275,12 @@ impl FifoPpu{ if self.pos_counter.x == 160{ self.state = PpuState::Hblank; if self.h_blank_interrupt_request{ - *if_register |= BIT_1_MASK; + self.stat_triggered = true; } self.pos_counter.x = 0; } } } - - if self.ly_register == self.lyc_register{ - self.stat_register |= BIT_2_MASK; - if self.coincidence_interrupt_request{ - *if_register |= BIT_1_MASK; - } - } - else{ - self.stat_register &= !BIT_2_MASK; - } pixels_to_push_to_lcd.append(&mut self.bg_fifo); } From e4ba52e35d4fce82e15fde7fbcf169c98c4d1206 Mon Sep 17 00:00:00 2001 From: alloncm Date: Mon, 26 Jul 2021 18:41:39 +0300 Subject: [PATCH 035/136] More fixes for the interrupt --- lib_gb/src/ppu/fifo_ppu.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib_gb/src/ppu/fifo_ppu.rs b/lib_gb/src/ppu/fifo_ppu.rs index 6ad8d454..2a7e9abe 100644 --- a/lib_gb/src/ppu/fifo_ppu.rs +++ b/lib_gb/src/ppu/fifo_ppu.rs @@ -123,7 +123,7 @@ impl FifoPpu{ if self.ly_register == self.lyc_register{ if self.coincidence_interrupt_request { - self.stat_triggered = true; + self.trigger_stat_interrupt = true; } self.stat_register |= BIT_2_MASK; } @@ -187,13 +187,13 @@ impl FifoPpu{ self.state = PpuState::Vblank; *if_register |= BIT_0_MASK; if self.v_blank_interrupt_request{ - self.stat_triggered = true; + self.trigger_stat_interrupt = true; } } else{ self.state = PpuState::OamSearch; if self.oam_search_interrupt_request{ - self.stat_triggered = true; + self.trigger_stat_interrupt = true; } } self.t_cycles_passed = 0; @@ -204,7 +204,7 @@ impl FifoPpu{ if self.t_cycles_passed == 4560{ self.state = PpuState::OamSearch; if self.oam_search_interrupt_request{ - self.stat_triggered = true; + self.trigger_stat_interrupt = true; } self.t_cycles_passed = 0; self.ly_register = 0; @@ -275,7 +275,7 @@ impl FifoPpu{ if self.pos_counter.x == 160{ self.state = PpuState::Hblank; if self.h_blank_interrupt_request{ - self.stat_triggered = true; + self.trigger_stat_interrupt = true; } self.pos_counter.x = 0; } From f350e621d5af13b2a0e4579f4bed4e3d4f4d8371 Mon Sep 17 00:00:00 2001 From: alloncm Date: Tue, 27 Jul 2021 23:57:12 +0300 Subject: [PATCH 036/136] Move the fifo stuff to its own folder --- lib_gb/src/mmu/io_components.rs | 2 +- lib_gb/src/ppu/fifo/bg_fetcher.rs | 84 +++++++++++++++++++ lib_gb/src/ppu/fifo/fetching_state.rs | 6 ++ lib_gb/src/ppu/{ => fifo}/fifo_ppu.rs | 18 ++-- .../ppu/{ => fifo}/fifo_register_updater.rs | 2 +- lib_gb/src/ppu/fifo/mod.rs | 4 + lib_gb/src/ppu/mod.rs | 3 +- 7 files changed, 104 insertions(+), 15 deletions(-) create mode 100644 lib_gb/src/ppu/fifo/bg_fetcher.rs create mode 100644 lib_gb/src/ppu/fifo/fetching_state.rs rename lib_gb/src/ppu/{ => fifo}/fifo_ppu.rs (97%) rename lib_gb/src/ppu/{ => fifo}/fifo_register_updater.rs (96%) create mode 100644 lib_gb/src/ppu/fifo/mod.rs diff --git a/lib_gb/src/mmu/io_components.rs b/lib_gb/src/mmu/io_components.rs index dbb41f92..6495f5eb 100644 --- a/lib_gb/src/mmu/io_components.rs +++ b/lib_gb/src/mmu/io_components.rs @@ -1,4 +1,4 @@ -use crate::{apu::{audio_device::AudioDevice, gb_apu::GbApu, set_nr11, set_nr12, set_nr13}, ppu::{fifo_ppu::FifoPpu, fifo_register_updater::*, gfx_device::GfxDevice}, timer::timer_register_updater::*, utils::memory_registers::*}; +use crate::{apu::{audio_device::AudioDevice, gb_apu::GbApu, set_nr11, set_nr12, set_nr13}, ppu::{fifo::fifo_ppu::FifoPpu, fifo::fifo_register_updater::*, gfx_device::GfxDevice}, timer::timer_register_updater::*, utils::memory_registers::*}; use crate::apu::*; use crate::timer::gb_timer::GbTimer; use super::{access_bus::AccessBus, memory::*, oam_dma_transfer::OamDmaTransfer, ram::Ram}; diff --git a/lib_gb/src/ppu/fifo/bg_fetcher.rs b/lib_gb/src/ppu/fifo/bg_fetcher.rs new file mode 100644 index 00000000..26dc337c --- /dev/null +++ b/lib_gb/src/ppu/fifo/bg_fetcher.rs @@ -0,0 +1,84 @@ +use crate::{mmu::vram::VRam, utils::{bit_masks::*, vec2::Vec2}}; + +use super::fetching_state::FethcingState; + +pub struct BGFetcher{ + pub current_x_pos:u8, + pub fifo:Vec, + + current_fetching_state:FethcingState, +} + +impl BGFetcher{ + pub fn new()->Self{ + BGFetcher{ + current_fetching_state:FethcingState::TileNumber, + current_x_pos:0, + fifo:Vec::::with_capacity(8) + } + } + + pub fn reset(&mut self){ + self.fifo.clear(); + self.current_x_pos = 0; + self.current_fetching_state = FethcingState::TileNumber; + } + + pub fn fetch_pixels(&mut self, vram:&VRam, lcd_control:u8, ly_register:u8, window_pos:&Vec2, bg_pos:&Vec2){ + match self.current_fetching_state{ + FethcingState::TileNumber=>{ + let tile_num = if Self::is_redering_wnd(lcd_control, window_pos, bg_pos){ + let tile_map_address:u16 = if (lcd_control & BIT_6_MASK) == 0 {0x1800} else {0x1C00}; + vram.read_current_bank(tile_map_address + ((32 * (ly_register / 8)) + (self.current_x_pos / 8) )as u16) + } + else{ + let tile_map_address = if (lcd_control & BIT_3_MASK) == 0 {0x1800} else {0x1C00}; + let scx_offset = ((bg_pos.x as u16 + self.current_x_pos as u16) / 8 ) & 31; + let scy_offset = ((bg_pos.y as u16 + ly_register as u16) & 0xFF) / 8; + + vram.read_current_bank(tile_map_address + ((32 * scy_offset) + scx_offset)) + }; + + self.current_fetching_state = FethcingState::LowTileData(tile_num); + } + FethcingState::LowTileData(tile_num)=>{ + let current_tile_base_data_address = if (lcd_control & BIT_4_MASK) == 0 && (tile_num & BIT_7_MASK) == 0 {0x1000} else {0}; + let current_tile_data_address = current_tile_base_data_address + (tile_num as u16 * 16); + let low_data = if Self::is_redering_wnd(lcd_control, window_pos, bg_pos){ + vram.read_current_bank(current_tile_data_address + (2 * (ly_register % 8)) as u16) + } else{ + vram.read_current_bank(current_tile_data_address + (2 * ((bg_pos.y + ly_register) % 8)) as u16) + }; + + self.current_fetching_state = FethcingState::HighTileData(tile_num, low_data); + } + FethcingState::HighTileData(tile_num, low_data)=>{ + let current_tile_base_data_address = if (lcd_control & BIT_4_MASK) == 0 && (tile_num & BIT_7_MASK) == 0 {0x1000} else {0}; + let current_tile_data_address = current_tile_base_data_address + (tile_num as u16 * 16); + let high_data = if Self::is_redering_wnd(lcd_control, window_pos, bg_pos){ + vram.read_current_bank(current_tile_data_address + (2 * (ly_register % 8)) as u16 + 1) + } else{ + vram.read_current_bank(current_tile_data_address + (2 * ((bg_pos.y + ly_register) % 8)) as u16 + 1) + }; + + self.current_fetching_state = FethcingState::Push(low_data, high_data); + } + FethcingState::Push(low_data, high_data)=>{ + for i in (0..8).rev(){ + let mask = 1 << i; + let mut pixel = (low_data & mask) >> i; + pixel |= ((high_data & mask) >> i) << 1; + self.fifo.push(pixel); + } + + self.current_fetching_state = FethcingState::TileNumber; + + self.current_x_pos += 8; + } + } + } + + fn is_redering_wnd(lcd_control:u8, window_pos:&Vec2, bg_pos:&Vec2)->bool{ + window_pos.x >= bg_pos.x && window_pos.y >= bg_pos.y && (lcd_control & BIT_5_MASK) != 0 + } +} \ No newline at end of file diff --git a/lib_gb/src/ppu/fifo/fetching_state.rs b/lib_gb/src/ppu/fifo/fetching_state.rs new file mode 100644 index 00000000..086456a9 --- /dev/null +++ b/lib_gb/src/ppu/fifo/fetching_state.rs @@ -0,0 +1,6 @@ +pub enum FethcingState{ + TileNumber, + LowTileData(u8), + HighTileData(u8,u8), + Push(u8,u8) +} \ No newline at end of file diff --git a/lib_gb/src/ppu/fifo_ppu.rs b/lib_gb/src/ppu/fifo/fifo_ppu.rs similarity index 97% rename from lib_gb/src/ppu/fifo_ppu.rs rename to lib_gb/src/ppu/fifo/fifo_ppu.rs index 2a7e9abe..cc7feb68 100644 --- a/lib_gb/src/ppu/fifo_ppu.rs +++ b/lib_gb/src/ppu/fifo/fifo_ppu.rs @@ -2,17 +2,13 @@ use std::mem::{self, MaybeUninit}; use crate::utils::{vec2::Vec2, bit_masks::*}; use crate::mmu::vram::VRam; -use super::color::Color; -use super::colors::*; -use super::gfx_device::GfxDevice; -use super::{ppu_state::PpuState, sprite_attribute::SpriteAttribute}; - -enum FethcingState{ - TileNumber, - LowTileData(u8), - HighTileData(u8,u8), - Push(u8,u8) -} +use crate::ppu::color::Color; +use crate::ppu::colors::*; +use crate::ppu::gfx_device::GfxDevice; +use crate::ppu::{ppu_state::PpuState, sprite_attribute::SpriteAttribute}; + +use super::fetching_state::FethcingState; + pub struct FifoPpu{ gfx_device: GFX, diff --git a/lib_gb/src/ppu/fifo_register_updater.rs b/lib_gb/src/ppu/fifo/fifo_register_updater.rs similarity index 96% rename from lib_gb/src/ppu/fifo_register_updater.rs rename to lib_gb/src/ppu/fifo/fifo_register_updater.rs index ddcc1877..c9c7a218 100644 --- a/lib_gb/src/ppu/fifo_register_updater.rs +++ b/lib_gb/src/ppu/fifo/fifo_register_updater.rs @@ -1,5 +1,5 @@ use crate::utils::bit_masks::*; -use super::{color::*, colors::*, fifo_ppu::FifoPpu, gfx_device::GfxDevice}; +use crate::ppu::{color::*, colors::*, fifo::fifo_ppu::FifoPpu, gfx_device::GfxDevice}; const WX_OFFSET:u8 = 7; diff --git a/lib_gb/src/ppu/fifo/mod.rs b/lib_gb/src/ppu/fifo/mod.rs new file mode 100644 index 00000000..0758e929 --- /dev/null +++ b/lib_gb/src/ppu/fifo/mod.rs @@ -0,0 +1,4 @@ +pub mod fifo_ppu; +pub mod fifo_register_updater; +mod bg_fetcher; +mod fetching_state; \ No newline at end of file diff --git a/lib_gb/src/ppu/mod.rs b/lib_gb/src/ppu/mod.rs index 6c200387..2cbc0069 100644 --- a/lib_gb/src/ppu/mod.rs +++ b/lib_gb/src/ppu/mod.rs @@ -3,8 +3,7 @@ pub mod ppu_state; pub mod color; pub mod colors; pub mod ppu_register_updater; -pub mod fifo_register_updater; -pub mod fifo_ppu; +pub mod fifo; pub mod gfx_device; mod normal_sprite; mod sprite_attribute; From 9bfe5533cb6097e1da0f91dcbaca9c6d6caa09a5 Mon Sep 17 00:00:00 2001 From: alloncm Date: Wed, 28 Jul 2021 00:07:44 +0300 Subject: [PATCH 037/136] Integrate the bg fetcher --- lib_gb/src/ppu/fifo/fifo_ppu.rs | 83 +++++---------------------------- 1 file changed, 11 insertions(+), 72 deletions(-) diff --git a/lib_gb/src/ppu/fifo/fifo_ppu.rs b/lib_gb/src/ppu/fifo/fifo_ppu.rs index cc7feb68..1c3d28cd 100644 --- a/lib_gb/src/ppu/fifo/fifo_ppu.rs +++ b/lib_gb/src/ppu/fifo/fifo_ppu.rs @@ -7,7 +7,7 @@ use crate::ppu::colors::*; use crate::ppu::gfx_device::GfxDevice; use crate::ppu::{ppu_state::PpuState, sprite_attribute::SpriteAttribute}; -use super::fetching_state::FethcingState; +use super::bg_fetcher::BGFetcher; pub struct FifoPpu{ @@ -25,7 +25,6 @@ pub struct FifoPpu{ pub ly_register:u8, pub window_pos:Vec2, pub bg_pos:Vec2, - pixel_fething_state: FethcingState, pub bg_color_mapping: [Color; 4], screen_buffer: [u32; 160*144], @@ -37,8 +36,7 @@ pub struct FifoPpu{ pub oam_search_interrupt_request:bool, pub coincidence_interrupt_request:bool, - pos_counter: Vec2, - bg_fifo: Vec, + bg_fetcher:BGFetcher, stat_triggered:bool, trigger_stat_interrupt:bool, } @@ -71,7 +69,6 @@ impl FifoPpu{ bg_color_mapping:[WHITE, LIGHT_GRAY, DARK_GRAY, BLACK], ly_register:0, state: PpuState::OamSearch, - pos_counter: Vec2::{x:0,y:0}, //interrupts v_blank_interrupt_request:false, h_blank_interrupt_request:false, @@ -79,12 +76,11 @@ impl FifoPpu{ coincidence_interrupt_request:false, oam_entries:oam_entries, current_oam_entry:0, - pixel_fething_state:FethcingState::TileNumber, screen_buffer_index:0, t_cycles_passed:0, - bg_fifo:Vec::::with_capacity(8), stat_triggered:false, trigger_stat_interrupt:false, + bg_fetcher:BGFetcher::new(), } } @@ -96,7 +92,7 @@ impl FifoPpu{ } self.gfx_device.swap_buffer(&self.screen_buffer); self.state = PpuState::Hblank; - self.bg_fifo.clear(); + self.bg_fetcher.fifo.clear(); self.ly_register = 0; self.stat_triggered = false; self.trigger_stat_interrupt = false; @@ -172,7 +168,7 @@ impl FifoPpu{ if self.t_cycles_passed == 80{ self.state = PpuState::PixelTransfer; - self.pixel_fething_state = FethcingState::TileNumber; + self.bg_fetcher.reset(); } } PpuState::Hblank=>{ @@ -212,83 +208,26 @@ impl FifoPpu{ self.t_cycles_passed += 2; } PpuState::PixelTransfer=>{ - match self.pixel_fething_state{ - FethcingState::TileNumber=>{ - let tile_num = if self.is_redering_wnd(){ - let tile_map_address:u16 = if (self.lcd_control & BIT_6_MASK) == 0 {0x1800} else {0x1C00}; - self.vram.read_current_bank(tile_map_address + ((32 * (self.pos_counter. y / 8)) + (self.pos_counter.x / 8) )as u16) - } - else{ - let tile_map_address = if (self.lcd_control & BIT_3_MASK) == 0 {0x1800} else {0x1C00}; - let scx_offset = ((self.bg_pos.x as u16 + self.pos_counter.x as u16) / 8 ) & 31; - let scy_offset = ((self.bg_pos.y as u16 + self.ly_register as u16) & 0xFF) / 8; - - self.vram.read_current_bank(tile_map_address + ((32 * scy_offset) + scx_offset)) - }; - - self.pixel_fething_state = FethcingState::LowTileData(tile_num); - self.t_cycles_passed += 2; - } - FethcingState::LowTileData(tile_num)=>{ - let current_tile_base_data_address = if (self.lcd_control & BIT_4_MASK) == 0 && (tile_num & BIT_7_MASK) == 0 {0x1000} else {0}; - let current_tile_data_address = current_tile_base_data_address + (tile_num as u16 * 16); - let low_data = if self.is_redering_wnd(){ - self.vram.read_current_bank(current_tile_data_address + (2 * (self.pos_counter.y % 8)) as u16) - } else{ - self.vram.read_current_bank(current_tile_data_address + (2 * ((self.bg_pos.y + self.ly_register) % 8)) as u16) - }; - - self.pixel_fething_state = FethcingState::HighTileData(tile_num, low_data); - self.t_cycles_passed += 2; - } - FethcingState::HighTileData(tile_num, low_data)=>{ - let current_tile_base_data_address = if (self.lcd_control & BIT_4_MASK) == 0 && (tile_num & BIT_7_MASK) == 0 {0x1000} else {0}; - let current_tile_data_address = current_tile_base_data_address + (tile_num as u16 * 16); - let high_data = if self.is_redering_wnd(){ - self.vram.read_current_bank(current_tile_data_address + (2 * (self.pos_counter.y % 8)) as u16 + 1) - } else{ - self.vram.read_current_bank(current_tile_data_address + (2 * ((self.bg_pos.y + self.ly_register) % 8)) as u16 + 1) - }; - - self.pixel_fething_state = FethcingState::Push(low_data, high_data); - self.t_cycles_passed += 2; - } - FethcingState::Push(low_data, high_data)=>{ - for i in (0..8).rev(){ - let mask = 1 << i; - let mut pixel = (low_data & mask) >> i; - pixel |= ((high_data & mask) >> i) << 1; - self.bg_fifo.push(pixel); - } + self.bg_fetcher.fetch_pixels(&self.vram, self.lcd_control, self.ly_register, &self.window_pos, &self.bg_pos); - self.pixel_fething_state = FethcingState::TileNumber; - self.t_cycles_passed += 2; - - self.pos_counter.x += 8; - } - } + pixels_to_push_to_lcd.append(&mut self.bg_fetcher.fifo); - if self.pos_counter.x == 160{ + if self.bg_fetcher.current_x_pos == 160{ self.state = PpuState::Hblank; if self.h_blank_interrupt_request{ self.trigger_stat_interrupt = true; } - self.pos_counter.x = 0; + self.bg_fetcher.reset(); } + self.t_cycles_passed += 2; } } - - pixels_to_push_to_lcd.append(&mut self.bg_fifo); } return pixels_to_push_to_lcd; } - fn is_redering_wnd(&self)->bool{ - self.window_pos.x >= self.bg_pos.x && self.window_pos.y >= self.bg_pos.y && (self.lcd_control & BIT_5_MASK) != 0 - } - fn color_as_uint(color: &Color) -> u32 { ((color.r as u32) << 16) | ((color.g as u32) << 8) | (color.b as u32) } -} +} \ No newline at end of file From 391b362eae16fb7a358a99d43ffa0218e5e24be1 Mon Sep 17 00:00:00 2001 From: alloncm Date: Wed, 28 Jul 2021 01:35:50 +0300 Subject: [PATCH 038/136] Add the sprite fetcher --- lib_gb/src/ppu/fifo/mod.rs | 3 +- lib_gb/src/ppu/fifo/sprite_fetcher.rs | 76 +++++++++++++++++++++++++++ 2 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 lib_gb/src/ppu/fifo/sprite_fetcher.rs diff --git a/lib_gb/src/ppu/fifo/mod.rs b/lib_gb/src/ppu/fifo/mod.rs index 0758e929..948958b3 100644 --- a/lib_gb/src/ppu/fifo/mod.rs +++ b/lib_gb/src/ppu/fifo/mod.rs @@ -1,4 +1,5 @@ pub mod fifo_ppu; pub mod fifo_register_updater; mod bg_fetcher; -mod fetching_state; \ No newline at end of file +mod fetching_state; +mod sprite_fetcher; \ No newline at end of file diff --git a/lib_gb/src/ppu/fifo/sprite_fetcher.rs b/lib_gb/src/ppu/fifo/sprite_fetcher.rs new file mode 100644 index 00000000..e3b78ba3 --- /dev/null +++ b/lib_gb/src/ppu/fifo/sprite_fetcher.rs @@ -0,0 +1,76 @@ +use std::mem::{self, MaybeUninit}; + +use crate::{mmu::vram::VRam, ppu::sprite_attribute::SpriteAttribute}; + +use super::fetching_state::FethcingState; + +pub struct SpriteFetcher{ + pub fifo:Vec<(u8, u8)>, + pub oam_entries:[SpriteAttribute; 10], + + current_fetching_state:FethcingState, + current_oam_entry:u8, +} + +impl SpriteFetcher{ + pub fn new()->Self{ + let oam_entries = { + let mut data: [MaybeUninit; 10] = unsafe{ + MaybeUninit::uninit().assume_init() + }; + + for elem in &mut data[..]{ + *elem = MaybeUninit::new(SpriteAttribute::new(0, 0, 0, 0)); + } + + unsafe{mem::transmute::<_, [SpriteAttribute;10]>(data)} + }; + + SpriteFetcher{ + current_fetching_state:FethcingState::TileNumber, + current_oam_entry:0, + oam_entries, + fifo:Vec::<(u8,u8)>::with_capacity(8) + } + } + + pub fn reset(&mut self){ + self.current_oam_entry = 0; + self.current_fetching_state = FethcingState::TileNumber; + self.fifo.clear(); + } + + pub fn fetch_pixels(&mut self, vram:&VRam, ly_register:u8, lcd_control:u8){ + match self.current_fetching_state{ + FethcingState::TileNumber=>{ + if self.oam_entries.len() > self.current_oam_entry as usize{ + self.current_fetching_state = FethcingState::LowTileData(self.oam_entries[self.current_oam_entry as usize].tile_number); + } + } + FethcingState::LowTileData(tile_num)=>{ + let current_tile_data_address = 0x8000 + (tile_num as u16 * 16); + let low_data = vram.read_current_bank(current_tile_data_address + (2 * (ly_register % 8)) as u16); + self.current_fetching_state = FethcingState::HighTileData(tile_num, low_data); + } + FethcingState::HighTileData(tile_num, low_data)=>{ + let current_tile_data_address = 0x8000 + (tile_num as u16 * 16); + let high_data = vram.read_current_bank(current_tile_data_address + (2 * (ly_register % 8)) as u16 + 1); + self.current_fetching_state = FethcingState::Push(low_data, high_data); + } + FethcingState::Push(low_data, high_data)=>{ + let oam_attribute = &self.oam_entries[self.current_oam_entry as usize]; + let range = if oam_attribute.flip_x {0..=7} else {7..=0}; + for i in range{ + let mask = 1 << i; + let mut pixel = (low_data & mask) >> i; + pixel |= ((high_data & mask) >> i) << 1; + self.fifo.push((pixel, self.current_oam_entry)); + } + + self.current_fetching_state = FethcingState::TileNumber; + + self.current_oam_entry += 1; + } + } + } +} \ No newline at end of file From 58e64e21a7e2de23c20a801027cf9242617de6b0 Mon Sep 17 00:00:00 2001 From: alloncm Date: Wed, 28 Jul 2021 02:45:19 +0300 Subject: [PATCH 039/136] Integrate the sprite fetcher Not working as intended --- lib_gb/src/mmu/io_components.rs | 4 +- lib_gb/src/ppu/fifo/fifo_ppu.rs | 65 ++++++++++++++++++--------- lib_gb/src/ppu/fifo/sprite_fetcher.rs | 6 +-- 3 files changed, 49 insertions(+), 26 deletions(-) diff --git a/lib_gb/src/mmu/io_components.rs b/lib_gb/src/mmu/io_components.rs index 6495f5eb..45f6b24a 100644 --- a/lib_gb/src/mmu/io_components.rs +++ b/lib_gb/src/mmu/io_components.rs @@ -130,8 +130,8 @@ impl Memory for IoComponents{ } } BGP_REGISTER_INDEX=> handle_bg_pallet_register(value,&mut self.ppu.bg_color_mapping), - //OBP0_REGISTER_INDEX=> handle_obp_pallet_register(value,&mut self.ppu.obj_color_mapping0), - //OBP1_REGISTER_INDEX=> handle_obp_pallet_register(value,&mut self.ppu.obj_color_mapping1), + OBP0_REGISTER_INDEX=> handle_obp_pallet_register(value,&mut self.ppu.obj_color_mapping0), + OBP1_REGISTER_INDEX=> handle_obp_pallet_register(value,&mut self.ppu.obj_color_mapping1), WY_REGISTER_INDEX=> handle_wy_register(value, &mut self.ppu), WX_REGISTER_INDEX=> handle_wx_register(value, &mut self.ppu), BOOT_REGISTER_INDEX=> self.finished_boot = value != 0, diff --git a/lib_gb/src/ppu/fifo/fifo_ppu.rs b/lib_gb/src/ppu/fifo/fifo_ppu.rs index 1c3d28cd..e5c00cb7 100644 --- a/lib_gb/src/ppu/fifo/fifo_ppu.rs +++ b/lib_gb/src/ppu/fifo/fifo_ppu.rs @@ -1,5 +1,3 @@ -use std::mem::{self, MaybeUninit}; - use crate::utils::{vec2::Vec2, bit_masks::*}; use crate::mmu::vram::VRam; use crate::ppu::color::Color; @@ -8,12 +6,12 @@ use crate::ppu::gfx_device::GfxDevice; use crate::ppu::{ppu_state::PpuState, sprite_attribute::SpriteAttribute}; use super::bg_fetcher::BGFetcher; +use super::sprite_fetcher::SpriteFetcher; pub struct FifoPpu{ gfx_device: GFX, - oam_entries:[SpriteAttribute; 10], pub vram: VRam, pub oam:[u8;0xA0], current_oam_entry:u8, @@ -26,6 +24,8 @@ pub struct FifoPpu{ pub window_pos:Vec2, pub bg_pos:Vec2, pub bg_color_mapping: [Color; 4], + pub obj_color_mapping0: [Option;4], + pub obj_color_mapping1: [Option;4], screen_buffer: [u32; 160*144], screen_buffer_index:usize, @@ -37,6 +37,7 @@ pub struct FifoPpu{ pub coincidence_interrupt_request:bool, bg_fetcher:BGFetcher, + sprite_fetcher:SpriteFetcher, stat_triggered:bool, trigger_stat_interrupt:bool, } @@ -44,17 +45,7 @@ pub struct FifoPpu{ impl FifoPpu{ pub fn new(device:GFX) -> Self { - let oam_entries = { - let mut data: [MaybeUninit; 10] = unsafe{ - MaybeUninit::uninit().assume_init() - }; - - for elem in &mut data[..]{ - *elem = MaybeUninit::new(SpriteAttribute::new(0, 0, 0, 0)); - } - - unsafe{mem::transmute::<_, [SpriteAttribute;10]>(data)} - }; + Self{ gfx_device: device, @@ -67,6 +58,8 @@ impl FifoPpu{ window_pos: Vec2::{x:0,y:0}, screen_buffer:[0;160*144], bg_color_mapping:[WHITE, LIGHT_GRAY, DARK_GRAY, BLACK], + obj_color_mapping0: [None, Some(LIGHT_GRAY), Some(DARK_GRAY), Some(BLACK)], + obj_color_mapping1: [None, Some(LIGHT_GRAY), Some(DARK_GRAY), Some(BLACK)], ly_register:0, state: PpuState::OamSearch, //interrupts @@ -74,13 +67,13 @@ impl FifoPpu{ h_blank_interrupt_request:false, oam_search_interrupt_request:false, coincidence_interrupt_request:false, - oam_entries:oam_entries, current_oam_entry:0, screen_buffer_index:0, t_cycles_passed:0, stat_triggered:false, trigger_stat_interrupt:false, bg_fetcher:BGFetcher::new(), + sprite_fetcher:SpriteFetcher::new(), } } @@ -134,8 +127,7 @@ impl FifoPpu{ } for pixel in pixels.as_slice(){ - let p = self.bg_color_mapping[*pixel as usize].clone(); - self.screen_buffer[self.screen_buffer_index] = Self::color_as_uint(&p); + self.screen_buffer[self.screen_buffer_index] = Self::color_as_uint(&pixel); self.screen_buffer_index += 1; if self.screen_buffer_index == self.screen_buffer.len(){ self.gfx_device.swap_buffer(&self.screen_buffer); @@ -144,10 +136,10 @@ impl FifoPpu{ } } - fn cycle_fetcher(&mut self, m_cycles:u8, if_register:&mut u8)->Vec{ + fn cycle_fetcher(&mut self, m_cycles:u8, if_register:&mut u8)->Vec{ let sprite_height = if (self.lcd_control & BIT_2_MASK) != 0 {16} else {8}; - let mut pixels_to_push_to_lcd = Vec::::new(); + let mut pixels_to_push_to_lcd = Vec::::new(); for _ in 0..m_cycles * 2{ match self.state{ @@ -160,7 +152,7 @@ impl FifoPpu{ if end_x > 0 && self.ly_register + 16 >= end_y && self.ly_register + 16 < end_y + sprite_height && self.current_oam_entry < 10{ let tile_number = self.oam[oam_entry_address + 2]; let attributes = self.oam[oam_entry_address + 3]; - self.oam_entries[self.current_oam_entry as usize] = SpriteAttribute::new(end_y, end_x, tile_number, attributes); + self.sprite_fetcher.oam_entries[self.current_oam_entry as usize] = SpriteAttribute::new(end_y, end_x, tile_number, attributes); self.current_oam_entry += 1; } @@ -169,6 +161,7 @@ impl FifoPpu{ if self.t_cycles_passed == 80{ self.state = PpuState::PixelTransfer; self.bg_fetcher.reset(); + self.sprite_fetcher.reset(); } } PpuState::Hblank=>{ @@ -209,8 +202,37 @@ impl FifoPpu{ } PpuState::PixelTransfer=>{ self.bg_fetcher.fetch_pixels(&self.vram, self.lcd_control, self.ly_register, &self.window_pos, &self.bg_pos); + if self.lcd_control & BIT_1_MASK != 0{ + self.sprite_fetcher.fetch_pixels(&self.vram, self.ly_register); + } + + while !self.sprite_fetcher.fifo.is_empty(){ + let bg_pixel = self.bg_fetcher.fifo.remove(0); + let sprite_pixel = self.sprite_fetcher.fifo.remove(0); + let pixel_oam_attribute = &self.sprite_fetcher.oam_entries[sprite_pixel.1 as usize]; + + if sprite_pixel.0 == 0 || pixel_oam_attribute.is_bg_priority{ + pixels_to_push_to_lcd.push(self.bg_color_mapping[bg_pixel as usize]); + } + else{ + let pixel = if pixel_oam_attribute.palette_number{ + self.obj_color_mapping0[sprite_pixel.0 as usize] + } + else{ + self.obj_color_mapping1[sprite_pixel.0 as usize] + }; + + if let Some(color) = pixel{ + pixels_to_push_to_lcd.push(color); + } + else{ + std::panic!("Corruption in the object color pallete"); + } + } + } - pixels_to_push_to_lcd.append(&mut self.bg_fetcher.fifo); + pixels_to_push_to_lcd.extend(self.bg_fetcher.fifo.iter().map(|c| self.bg_color_mapping[*c as usize])); + self.bg_fetcher.fifo.clear(); if self.bg_fetcher.current_x_pos == 160{ self.state = PpuState::Hblank; @@ -218,6 +240,7 @@ impl FifoPpu{ self.trigger_stat_interrupt = true; } self.bg_fetcher.reset(); + self.sprite_fetcher.reset(); } self.t_cycles_passed += 2; } diff --git a/lib_gb/src/ppu/fifo/sprite_fetcher.rs b/lib_gb/src/ppu/fifo/sprite_fetcher.rs index e3b78ba3..c6cab321 100644 --- a/lib_gb/src/ppu/fifo/sprite_fetcher.rs +++ b/lib_gb/src/ppu/fifo/sprite_fetcher.rs @@ -40,7 +40,7 @@ impl SpriteFetcher{ self.fifo.clear(); } - pub fn fetch_pixels(&mut self, vram:&VRam, ly_register:u8, lcd_control:u8){ + pub fn fetch_pixels(&mut self, vram:&VRam, ly_register:u8){ match self.current_fetching_state{ FethcingState::TileNumber=>{ if self.oam_entries.len() > self.current_oam_entry as usize{ @@ -48,12 +48,12 @@ impl SpriteFetcher{ } } FethcingState::LowTileData(tile_num)=>{ - let current_tile_data_address = 0x8000 + (tile_num as u16 * 16); + let current_tile_data_address = tile_num as u16 * 16; let low_data = vram.read_current_bank(current_tile_data_address + (2 * (ly_register % 8)) as u16); self.current_fetching_state = FethcingState::HighTileData(tile_num, low_data); } FethcingState::HighTileData(tile_num, low_data)=>{ - let current_tile_data_address = 0x8000 + (tile_num as u16 * 16); + let current_tile_data_address = tile_num as u16 * 16; let high_data = vram.read_current_bank(current_tile_data_address + (2 * (ly_register % 8)) as u16 + 1); self.current_fetching_state = FethcingState::Push(low_data, high_data); } From dd28221e39d81ee37648ec4417c81a9c6412203b Mon Sep 17 00:00:00 2001 From: alloncm Date: Wed, 28 Jul 2021 03:42:38 +0300 Subject: [PATCH 040/136] Still implementing the sprites --- lib_gb/src/ppu/fifo/bg_fetcher.rs | 17 +++--- lib_gb/src/ppu/fifo/fifo_ppu.rs | 81 +++++++++++++++------------ lib_gb/src/ppu/fifo/sprite_fetcher.rs | 7 ++- 3 files changed, 58 insertions(+), 47 deletions(-) diff --git a/lib_gb/src/ppu/fifo/bg_fetcher.rs b/lib_gb/src/ppu/fifo/bg_fetcher.rs index 26dc337c..7cc17c52 100644 --- a/lib_gb/src/ppu/fifo/bg_fetcher.rs +++ b/lib_gb/src/ppu/fifo/bg_fetcher.rs @@ -64,16 +64,15 @@ impl BGFetcher{ self.current_fetching_state = FethcingState::Push(low_data, high_data); } FethcingState::Push(low_data, high_data)=>{ - for i in (0..8).rev(){ - let mask = 1 << i; - let mut pixel = (low_data & mask) >> i; - pixel |= ((high_data & mask) >> i) << 1; - self.fifo.push(pixel); + if self.fifo.is_empty(){ + for i in (0..8).rev(){ + let mask = 1 << i; + let mut pixel = (low_data & mask) >> i; + pixel |= ((high_data & mask) >> i) << 1; + self.fifo.push(pixel); + } + self.current_fetching_state = FethcingState::TileNumber; } - - self.current_fetching_state = FethcingState::TileNumber; - - self.current_x_pos += 8; } } } diff --git a/lib_gb/src/ppu/fifo/fifo_ppu.rs b/lib_gb/src/ppu/fifo/fifo_ppu.rs index e5c00cb7..47c40195 100644 --- a/lib_gb/src/ppu/fifo/fifo_ppu.rs +++ b/lib_gb/src/ppu/fifo/fifo_ppu.rs @@ -28,6 +28,7 @@ pub struct FifoPpu{ pub obj_color_mapping1: [Option;4], screen_buffer: [u32; 160*144], + push_lcd_buffer:Vec, screen_buffer_index:usize, //interrupts @@ -74,6 +75,7 @@ impl FifoPpu{ trigger_stat_interrupt:false, bg_fetcher:BGFetcher::new(), sprite_fetcher:SpriteFetcher::new(), + push_lcd_buffer:Vec::::new(), } } @@ -100,7 +102,7 @@ impl FifoPpu{ return; } - let pixels = self.cycle_fetcher(m_cycles, if_register); + self.cycle_fetcher(m_cycles, if_register); //update stat register self.stat_register &= 0b1111_1100; //clear first 2 bits @@ -126,7 +128,7 @@ impl FifoPpu{ self.stat_triggered = false; } - for pixel in pixels.as_slice(){ + for pixel in self.push_lcd_buffer.iter(){ self.screen_buffer[self.screen_buffer_index] = Self::color_as_uint(&pixel); self.screen_buffer_index += 1; if self.screen_buffer_index == self.screen_buffer.len(){ @@ -134,13 +136,13 @@ impl FifoPpu{ self.screen_buffer_index = 0; } } + + self.push_lcd_buffer.clear(); } - fn cycle_fetcher(&mut self, m_cycles:u8, if_register:&mut u8)->Vec{ + fn cycle_fetcher(&mut self, m_cycles:u8, if_register:&mut u8){ let sprite_height = if (self.lcd_control & BIT_2_MASK) != 0 {16} else {8}; - let mut pixels_to_push_to_lcd = Vec::::new(); - for _ in 0..m_cycles * 2{ match self.state{ PpuState::OamSearch=>{ @@ -203,37 +205,13 @@ impl FifoPpu{ PpuState::PixelTransfer=>{ self.bg_fetcher.fetch_pixels(&self.vram, self.lcd_control, self.ly_register, &self.window_pos, &self.bg_pos); if self.lcd_control & BIT_1_MASK != 0{ - self.sprite_fetcher.fetch_pixels(&self.vram, self.ly_register); + self.sprite_fetcher.fetch_pixels(&self.vram, self.ly_register, self.bg_fetcher.current_x_pos); } - - while !self.sprite_fetcher.fifo.is_empty(){ - let bg_pixel = self.bg_fetcher.fifo.remove(0); - let sprite_pixel = self.sprite_fetcher.fifo.remove(0); - let pixel_oam_attribute = &self.sprite_fetcher.oam_entries[sprite_pixel.1 as usize]; - - if sprite_pixel.0 == 0 || pixel_oam_attribute.is_bg_priority{ - pixels_to_push_to_lcd.push(self.bg_color_mapping[bg_pixel as usize]); - } - else{ - let pixel = if pixel_oam_attribute.palette_number{ - self.obj_color_mapping0[sprite_pixel.0 as usize] - } - else{ - self.obj_color_mapping1[sprite_pixel.0 as usize] - }; - - if let Some(color) = pixel{ - pixels_to_push_to_lcd.push(color); - } - else{ - std::panic!("Corruption in the object color pallete"); - } - } + + for _ in 0..2{ + self.try_push_to_lcd(); } - pixels_to_push_to_lcd.extend(self.bg_fetcher.fifo.iter().map(|c| self.bg_color_mapping[*c as usize])); - self.bg_fetcher.fifo.clear(); - if self.bg_fetcher.current_x_pos == 160{ self.state = PpuState::Hblank; if self.h_blank_interrupt_request{ @@ -245,9 +223,40 @@ impl FifoPpu{ self.t_cycles_passed += 2; } } - } - - return pixels_to_push_to_lcd; + } + } + + fn try_push_to_lcd(&mut self){ + if !self.bg_fetcher.fifo.is_empty(){ + let bg_pixel = self.bg_color_mapping[self.bg_fetcher.fifo.remove(0) as usize]; + let pixel = if !self.sprite_fetcher.fifo.is_empty(){ + let sprite_color_num = self.sprite_fetcher.fifo.remove(0); + let pixel_oam_attribute = &self.sprite_fetcher.oam_entries[sprite_color_num.1 as usize]; + + if sprite_color_num.0 == 0 || pixel_oam_attribute.is_bg_priority{ + + } + let sprite_pixel = if pixel_oam_attribute.palette_number{ + self.obj_color_mapping0[sprite_color_num.0 as usize] + } + else{ + self.obj_color_mapping1[sprite_color_num.0 as usize] + }; + + if let Some(color) = sprite_pixel{ + color + } + else{ + std::panic!("Corruption in the object color pallete"); + } + } + else{ + bg_pixel + }; + + self.push_lcd_buffer.push(pixel); + self.bg_fetcher.current_x_pos += 1; + } } fn color_as_uint(color: &Color) -> u32 { diff --git a/lib_gb/src/ppu/fifo/sprite_fetcher.rs b/lib_gb/src/ppu/fifo/sprite_fetcher.rs index c6cab321..b1c87f69 100644 --- a/lib_gb/src/ppu/fifo/sprite_fetcher.rs +++ b/lib_gb/src/ppu/fifo/sprite_fetcher.rs @@ -40,11 +40,14 @@ impl SpriteFetcher{ self.fifo.clear(); } - pub fn fetch_pixels(&mut self, vram:&VRam, ly_register:u8){ + pub fn fetch_pixels(&mut self, vram:&VRam, ly_register:u8, current_x_pos:u8){ match self.current_fetching_state{ FethcingState::TileNumber=>{ if self.oam_entries.len() > self.current_oam_entry as usize{ - self.current_fetching_state = FethcingState::LowTileData(self.oam_entries[self.current_oam_entry as usize].tile_number); + let oam_entry = &self.oam_entries[self.current_oam_entry as usize]; + if oam_entry.x <= current_x_pos + 8{ + self.current_fetching_state = FethcingState::LowTileData(oam_entry.tile_number); + } } } FethcingState::LowTileData(tile_num)=>{ From 260414d0afa77802b98e7081f444fec4cf595873 Mon Sep 17 00:00:00 2001 From: alloncm Date: Wed, 28 Jul 2021 14:41:51 +0300 Subject: [PATCH 041/136] Fix bugs u8 overflow and rendering Rendered the first tile twice in the bg --- lib_gb/src/ppu/fifo/bg_fetcher.rs | 5 +-- lib_gb/src/ppu/fifo/fifo_ppu.rs | 48 +++++++++++++-------------- lib_gb/src/ppu/fifo/sprite_fetcher.rs | 5 ++- 3 files changed, 31 insertions(+), 27 deletions(-) diff --git a/lib_gb/src/ppu/fifo/bg_fetcher.rs b/lib_gb/src/ppu/fifo/bg_fetcher.rs index 7cc17c52..d088162e 100644 --- a/lib_gb/src/ppu/fifo/bg_fetcher.rs +++ b/lib_gb/src/ppu/fifo/bg_fetcher.rs @@ -47,7 +47,7 @@ impl BGFetcher{ let low_data = if Self::is_redering_wnd(lcd_control, window_pos, bg_pos){ vram.read_current_bank(current_tile_data_address + (2 * (ly_register % 8)) as u16) } else{ - vram.read_current_bank(current_tile_data_address + (2 * ((bg_pos.y + ly_register) % 8)) as u16) + vram.read_current_bank(current_tile_data_address + (2 * ((bg_pos.y as u16 + ly_register as u16) % 8)) ) }; self.current_fetching_state = FethcingState::HighTileData(tile_num, low_data); @@ -58,7 +58,7 @@ impl BGFetcher{ let high_data = if Self::is_redering_wnd(lcd_control, window_pos, bg_pos){ vram.read_current_bank(current_tile_data_address + (2 * (ly_register % 8)) as u16 + 1) } else{ - vram.read_current_bank(current_tile_data_address + (2 * ((bg_pos.y + ly_register) % 8)) as u16 + 1) + vram.read_current_bank(current_tile_data_address + (2 * ((bg_pos.y as u16 + ly_register as u16) % 8)) + 1) }; self.current_fetching_state = FethcingState::Push(low_data, high_data); @@ -70,6 +70,7 @@ impl BGFetcher{ let mut pixel = (low_data & mask) >> i; pixel |= ((high_data & mask) >> i) << 1; self.fifo.push(pixel); + self.current_x_pos += 1; } self.current_fetching_state = FethcingState::TileNumber; } diff --git a/lib_gb/src/ppu/fifo/fifo_ppu.rs b/lib_gb/src/ppu/fifo/fifo_ppu.rs index 47c40195..96200ad8 100644 --- a/lib_gb/src/ppu/fifo/fifo_ppu.rs +++ b/lib_gb/src/ppu/fifo/fifo_ppu.rs @@ -14,7 +14,6 @@ pub struct FifoPpu{ pub vram: VRam, pub oam:[u8;0xA0], - current_oam_entry:u8, t_cycles_passed:u16, pub state:PpuState, pub lcd_control:u8, @@ -68,7 +67,6 @@ impl FifoPpu{ h_blank_interrupt_request:false, oam_search_interrupt_request:false, coincidence_interrupt_request:false, - current_oam_entry:0, screen_buffer_index:0, t_cycles_passed:0, stat_triggered:false, @@ -87,10 +85,11 @@ impl FifoPpu{ } self.gfx_device.swap_buffer(&self.screen_buffer); self.state = PpuState::Hblank; - self.bg_fetcher.fifo.clear(); self.ly_register = 0; self.stat_triggered = false; self.trigger_stat_interrupt = false; + self.bg_fetcher.reset(); + self.sprite_fetcher.reset(); } pub fn turn_on(&mut self){ @@ -151,19 +150,17 @@ impl FifoPpu{ let end_y = self.oam[oam_entry_address]; let end_x = self.oam[oam_entry_address + 1]; - if end_x > 0 && self.ly_register + 16 >= end_y && self.ly_register + 16 < end_y + sprite_height && self.current_oam_entry < 10{ + if end_x > 0 && self.ly_register + 16 >= end_y && self.ly_register + 16 < end_y + sprite_height && self.sprite_fetcher.oam_entries_len < 10{ let tile_number = self.oam[oam_entry_address + 2]; let attributes = self.oam[oam_entry_address + 3]; - self.sprite_fetcher.oam_entries[self.current_oam_entry as usize] = SpriteAttribute::new(end_y, end_x, tile_number, attributes); - self.current_oam_entry += 1; + self.sprite_fetcher.oam_entries[self.sprite_fetcher.oam_entries_len as usize] = SpriteAttribute::new(end_y, end_x, tile_number, attributes); + self.sprite_fetcher.oam_entries_len += 1; } self.t_cycles_passed += 2; //half a m_cycle if self.t_cycles_passed == 80{ self.state = PpuState::PixelTransfer; - self.bg_fetcher.reset(); - self.sprite_fetcher.reset(); } } PpuState::Hblank=>{ @@ -203,16 +200,18 @@ impl FifoPpu{ self.t_cycles_passed += 2; } PpuState::PixelTransfer=>{ - self.bg_fetcher.fetch_pixels(&self.vram, self.lcd_control, self.ly_register, &self.window_pos, &self.bg_pos); - if self.lcd_control & BIT_1_MASK != 0{ - self.sprite_fetcher.fetch_pixels(&self.vram, self.ly_register, self.bg_fetcher.current_x_pos); + if self.bg_fetcher.current_x_pos < 160{ + self.bg_fetcher.fetch_pixels(&self.vram, self.lcd_control, self.ly_register, &self.window_pos, &self.bg_pos); + if self.lcd_control & BIT_1_MASK != 0{ + self.sprite_fetcher.fetch_pixels(&self.vram, self.ly_register, self.bg_fetcher.current_x_pos); + } } for _ in 0..2{ self.try_push_to_lcd(); } - if self.bg_fetcher.current_x_pos == 160{ + if self.bg_fetcher.current_x_pos == 160 && self.bg_fetcher.fifo.is_empty(){ self.state = PpuState::Hblank; if self.h_blank_interrupt_request{ self.trigger_stat_interrupt = true; @@ -234,20 +233,22 @@ impl FifoPpu{ let pixel_oam_attribute = &self.sprite_fetcher.oam_entries[sprite_color_num.1 as usize]; if sprite_color_num.0 == 0 || pixel_oam_attribute.is_bg_priority{ - - } - let sprite_pixel = if pixel_oam_attribute.palette_number{ - self.obj_color_mapping0[sprite_color_num.0 as usize] + bg_pixel } else{ - self.obj_color_mapping1[sprite_color_num.0 as usize] - }; + let sprite_pixel = if pixel_oam_attribute.palette_number{ + self.obj_color_mapping0[sprite_color_num.0 as usize] + } + else{ + self.obj_color_mapping1[sprite_color_num.0 as usize] + }; - if let Some(color) = sprite_pixel{ - color - } - else{ - std::panic!("Corruption in the object color pallete"); + if let Some(color) = sprite_pixel{ + color + } + else{ + std::panic!("Corruption in the object color pallete"); + } } } else{ @@ -255,7 +256,6 @@ impl FifoPpu{ }; self.push_lcd_buffer.push(pixel); - self.bg_fetcher.current_x_pos += 1; } } diff --git a/lib_gb/src/ppu/fifo/sprite_fetcher.rs b/lib_gb/src/ppu/fifo/sprite_fetcher.rs index b1c87f69..f4d31a44 100644 --- a/lib_gb/src/ppu/fifo/sprite_fetcher.rs +++ b/lib_gb/src/ppu/fifo/sprite_fetcher.rs @@ -7,6 +7,7 @@ use super::fetching_state::FethcingState; pub struct SpriteFetcher{ pub fifo:Vec<(u8, u8)>, pub oam_entries:[SpriteAttribute; 10], + pub oam_entries_len:u8, current_fetching_state:FethcingState, current_oam_entry:u8, @@ -29,6 +30,7 @@ impl SpriteFetcher{ SpriteFetcher{ current_fetching_state:FethcingState::TileNumber, current_oam_entry:0, + oam_entries_len:0, oam_entries, fifo:Vec::<(u8,u8)>::with_capacity(8) } @@ -36,6 +38,7 @@ impl SpriteFetcher{ pub fn reset(&mut self){ self.current_oam_entry = 0; + self.oam_entries_len = 0; self.current_fetching_state = FethcingState::TileNumber; self.fifo.clear(); } @@ -43,7 +46,7 @@ impl SpriteFetcher{ pub fn fetch_pixels(&mut self, vram:&VRam, ly_register:u8, current_x_pos:u8){ match self.current_fetching_state{ FethcingState::TileNumber=>{ - if self.oam_entries.len() > self.current_oam_entry as usize{ + if self.oam_entries_len > self.current_oam_entry{ let oam_entry = &self.oam_entries[self.current_oam_entry as usize]; if oam_entry.x <= current_x_pos + 8{ self.current_fetching_state = FethcingState::LowTileData(oam_entry.tile_number); From 9aff501473a0f1938b67c699dd0bbb305013441e Mon Sep 17 00:00:00 2001 From: alloncm Date: Wed, 28 Jul 2021 15:46:17 +0300 Subject: [PATCH 042/136] First sprites on the screen! --- lib_gb/src/ppu/fifo/fifo_ppu.rs | 4 ++-- lib_gb/src/ppu/fifo/sprite_fetcher.rs | 22 ++++++++++++++++------ 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/lib_gb/src/ppu/fifo/fifo_ppu.rs b/lib_gb/src/ppu/fifo/fifo_ppu.rs index 96200ad8..d4ec90ae 100644 --- a/lib_gb/src/ppu/fifo/fifo_ppu.rs +++ b/lib_gb/src/ppu/fifo/fifo_ppu.rs @@ -237,10 +237,10 @@ impl FifoPpu{ } else{ let sprite_pixel = if pixel_oam_attribute.palette_number{ - self.obj_color_mapping0[sprite_color_num.0 as usize] + self.obj_color_mapping1[sprite_color_num.0 as usize] } else{ - self.obj_color_mapping1[sprite_color_num.0 as usize] + self.obj_color_mapping0[sprite_color_num.0 as usize] }; if let Some(color) = sprite_pixel{ diff --git a/lib_gb/src/ppu/fifo/sprite_fetcher.rs b/lib_gb/src/ppu/fifo/sprite_fetcher.rs index f4d31a44..5f5662b0 100644 --- a/lib_gb/src/ppu/fifo/sprite_fetcher.rs +++ b/lib_gb/src/ppu/fifo/sprite_fetcher.rs @@ -65,12 +65,22 @@ impl SpriteFetcher{ } FethcingState::Push(low_data, high_data)=>{ let oam_attribute = &self.oam_entries[self.current_oam_entry as usize]; - let range = if oam_attribute.flip_x {0..=7} else {7..=0}; - for i in range{ - let mask = 1 << i; - let mut pixel = (low_data & mask) >> i; - pixel |= ((high_data & mask) >> i) << 1; - self.fifo.push((pixel, self.current_oam_entry)); + + if !oam_attribute.flip_x{ + for i in (0..8).rev(){ + let mask = 1 << i; + let mut pixel = (low_data & mask) >> i; + pixel |= ((high_data & mask) >> i) << 1; + self.fifo.push((pixel, self.current_oam_entry)); + } + } + else{ + for i in 0..8{ + let mask = 1 << i; + let mut pixel = (low_data & mask) >> i; + pixel |= ((high_data & mask) >> i) << 1; + self.fifo.push((pixel, self.current_oam_entry)); + } } self.current_fetching_state = FethcingState::TileNumber; From db57edf8ac90218dc252bb289971d302ffecc809 Mon Sep 17 00:00:00 2001 From: alloncm Date: Wed, 28 Jul 2021 19:33:11 +0300 Subject: [PATCH 043/136] Fix the stat interrupt and support for hiding bg --- lib_gb/src/ppu/fifo/bg_fetcher.rs | 21 ++++++++++++++------ lib_gb/src/ppu/fifo/fifo_ppu.rs | 2 ++ lib_gb/src/ppu/fifo/fifo_register_updater.rs | 2 +- 3 files changed, 18 insertions(+), 7 deletions(-) diff --git a/lib_gb/src/ppu/fifo/bg_fetcher.rs b/lib_gb/src/ppu/fifo/bg_fetcher.rs index d088162e..304cf2f3 100644 --- a/lib_gb/src/ppu/fifo/bg_fetcher.rs +++ b/lib_gb/src/ppu/fifo/bg_fetcher.rs @@ -65,13 +65,22 @@ impl BGFetcher{ } FethcingState::Push(low_data, high_data)=>{ if self.fifo.is_empty(){ - for i in (0..8).rev(){ - let mask = 1 << i; - let mut pixel = (low_data & mask) >> i; - pixel |= ((high_data & mask) >> i) << 1; - self.fifo.push(pixel); - self.current_x_pos += 1; + if lcd_control & BIT_0_MASK == 0{ + for _ in 0..8{ + self.fifo.push(0); + self.current_x_pos += 1; + } } + else{ + for i in (0..8).rev(){ + let mask = 1 << i; + let mut pixel = (low_data & mask) >> i; + pixel |= ((high_data & mask) >> i) << 1; + self.fifo.push(pixel); + self.current_x_pos += 1; + } + } + self.current_fetching_state = FethcingState::TileNumber; } } diff --git a/lib_gb/src/ppu/fifo/fifo_ppu.rs b/lib_gb/src/ppu/fifo/fifo_ppu.rs index d4ec90ae..5e35f65e 100644 --- a/lib_gb/src/ppu/fifo/fifo_ppu.rs +++ b/lib_gb/src/ppu/fifo/fifo_ppu.rs @@ -126,6 +126,8 @@ impl FifoPpu{ else{ self.stat_triggered = false; } + + self.trigger_stat_interrupt = false; for pixel in self.push_lcd_buffer.iter(){ self.screen_buffer[self.screen_buffer_index] = Self::color_as_uint(&pixel); diff --git a/lib_gb/src/ppu/fifo/fifo_register_updater.rs b/lib_gb/src/ppu/fifo/fifo_register_updater.rs index c9c7a218..8ed87b58 100644 --- a/lib_gb/src/ppu/fifo/fifo_register_updater.rs +++ b/lib_gb/src/ppu/fifo/fifo_register_updater.rs @@ -78,4 +78,4 @@ pub fn get_stat(ppu:&FifoPpu)->u8{ pub fn set_lyc(ppu:&mut FifoPpu, value:u8){ ppu.lyc_register = value; -} +} \ No newline at end of file From e361544a4f1eb67d2f835969f6b4ae217bae4ec2 Mon Sep 17 00:00:00 2001 From: alloncm Date: Thu, 29 Jul 2021 12:21:41 +0300 Subject: [PATCH 044/136] Adding propper support for the window --- lib_gb/src/ppu/fifo/bg_fetcher.rs | 44 ++++++++++++++++++++++++------- lib_gb/src/ppu/fifo/fifo_ppu.rs | 3 +++ 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/lib_gb/src/ppu/fifo/bg_fetcher.rs b/lib_gb/src/ppu/fifo/bg_fetcher.rs index 304cf2f3..233d6e46 100644 --- a/lib_gb/src/ppu/fifo/bg_fetcher.rs +++ b/lib_gb/src/ppu/fifo/bg_fetcher.rs @@ -5,7 +5,10 @@ use super::fetching_state::FethcingState; pub struct BGFetcher{ pub current_x_pos:u8, pub fifo:Vec, + pub window_line_counter:u8, + rendered_window:bool, + rendering_window:bool, current_fetching_state:FethcingState, } @@ -14,7 +17,10 @@ impl BGFetcher{ BGFetcher{ current_fetching_state:FethcingState::TileNumber, current_x_pos:0, - fifo:Vec::::with_capacity(8) + fifo:Vec::::with_capacity(8), + window_line_counter:0, + rendered_window:false, + rendering_window:false } } @@ -22,14 +28,34 @@ impl BGFetcher{ self.fifo.clear(); self.current_x_pos = 0; self.current_fetching_state = FethcingState::TileNumber; + self.rendered_window = false; + self.rendering_window = false; + } + + pub fn try_increment_window_counter(&mut self){ + if self.rendered_window{ + self.window_line_counter += 1; + } } pub fn fetch_pixels(&mut self, vram:&VRam, lcd_control:u8, ly_register:u8, window_pos:&Vec2, bg_pos:&Vec2){ + let last_rendering_status = self.rendering_window; + self.rendering_window = self.is_redering_wnd(lcd_control, window_pos, ly_register); + if self.rendering_window{ + self.rendered_window = true; + + // In case I was rendering a background pixel need to reset the state of the fectcher + // (and maybe clear the fifo but right now Im not doing it since im not sure what about the current_x_pos var) + if !last_rendering_status{ + self.current_fetching_state = FethcingState::TileNumber; + } + } + match self.current_fetching_state{ FethcingState::TileNumber=>{ - let tile_num = if Self::is_redering_wnd(lcd_control, window_pos, bg_pos){ + let tile_num = if self.rendering_window{ let tile_map_address:u16 = if (lcd_control & BIT_6_MASK) == 0 {0x1800} else {0x1C00}; - vram.read_current_bank(tile_map_address + ((32 * (ly_register / 8)) + (self.current_x_pos / 8) )as u16) + vram.read_current_bank(tile_map_address + (32 * (self.window_line_counter as u16 / 8)) + ((self.current_x_pos - window_pos.x) as u16 / 8)) } else{ let tile_map_address = if (lcd_control & BIT_3_MASK) == 0 {0x1800} else {0x1C00}; @@ -44,8 +70,8 @@ impl BGFetcher{ FethcingState::LowTileData(tile_num)=>{ let current_tile_base_data_address = if (lcd_control & BIT_4_MASK) == 0 && (tile_num & BIT_7_MASK) == 0 {0x1000} else {0}; let current_tile_data_address = current_tile_base_data_address + (tile_num as u16 * 16); - let low_data = if Self::is_redering_wnd(lcd_control, window_pos, bg_pos){ - vram.read_current_bank(current_tile_data_address + (2 * (ly_register % 8)) as u16) + let low_data = if self.rendering_window{ + vram.read_current_bank(current_tile_data_address + (2 * (self.window_line_counter % 8)) as u16) } else{ vram.read_current_bank(current_tile_data_address + (2 * ((bg_pos.y as u16 + ly_register as u16) % 8)) ) }; @@ -55,8 +81,8 @@ impl BGFetcher{ FethcingState::HighTileData(tile_num, low_data)=>{ let current_tile_base_data_address = if (lcd_control & BIT_4_MASK) == 0 && (tile_num & BIT_7_MASK) == 0 {0x1000} else {0}; let current_tile_data_address = current_tile_base_data_address + (tile_num as u16 * 16); - let high_data = if Self::is_redering_wnd(lcd_control, window_pos, bg_pos){ - vram.read_current_bank(current_tile_data_address + (2 * (ly_register % 8)) as u16 + 1) + let high_data = if self.rendering_window{ + vram.read_current_bank(current_tile_data_address + (2 * (self.window_line_counter % 8)) as u16 + 1) } else{ vram.read_current_bank(current_tile_data_address + (2 * ((bg_pos.y as u16 + ly_register as u16) % 8)) + 1) }; @@ -87,7 +113,7 @@ impl BGFetcher{ } } - fn is_redering_wnd(lcd_control:u8, window_pos:&Vec2, bg_pos:&Vec2)->bool{ - window_pos.x >= bg_pos.x && window_pos.y >= bg_pos.y && (lcd_control & BIT_5_MASK) != 0 + fn is_redering_wnd(&self, lcd_control:u8, window_pos:&Vec2, ly_register:u8)->bool{ + window_pos.x <= self.current_x_pos && window_pos.y <= ly_register && (lcd_control & BIT_5_MASK) != 0 } } \ No newline at end of file diff --git a/lib_gb/src/ppu/fifo/fifo_ppu.rs b/lib_gb/src/ppu/fifo/fifo_ppu.rs index 5e35f65e..fefb48a4 100644 --- a/lib_gb/src/ppu/fifo/fifo_ppu.rs +++ b/lib_gb/src/ppu/fifo/fifo_ppu.rs @@ -171,6 +171,8 @@ impl FifoPpu{ if self.t_cycles_passed == 456{ if self.ly_register == 143{ self.state = PpuState::Vblank; + //reseting the window counter on vblank + self.bg_fetcher.window_line_counter = 0; *if_register |= BIT_0_MASK; if self.v_blank_interrupt_request{ self.trigger_stat_interrupt = true; @@ -184,6 +186,7 @@ impl FifoPpu{ } self.t_cycles_passed = 0; self.ly_register += 1; + self.bg_fetcher.try_increment_window_counter(); } } PpuState::Vblank=>{ From 16daae8d4e3311aff0a5eb49fbcc9fc85e338ff8 Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Fri, 30 Jul 2021 13:46:57 +0300 Subject: [PATCH 045/136] Adapt to linux and amde error message clearer --- gb/src/main.rs | 2 +- gb/src/mbc_handler.rs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/gb/src/main.rs b/gb/src/main.rs index 47656f4e..eaccf968 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -83,7 +83,7 @@ fn main() { let mut mbc = initialize_mbc(program_name); let joypad_provider = SdlJoypadProvider::new(buttons_mapper); - let mut gameboy = match fs::read("Dependencies\\Init\\dmg_boot.bin"){ + let mut gameboy = match fs::read("Dependencies/Init/dmg_boot.bin"){ Result::Ok(file)=>{ info!("found bootrom!"); diff --git a/gb/src/mbc_handler.rs b/gb/src/mbc_handler.rs index 879dbfa7..c7edc901 100644 --- a/gb/src/mbc_handler.rs +++ b/gb/src/mbc_handler.rs @@ -10,7 +10,8 @@ pub const SAVE_SUFFIX:&str = ".sav"; pub fn initialize_mbc(program_name:&String)->Box{ let program_path = format!("{}{}",program_name,PROGRAM_SUFFIX); - let program = fs::read(program_path).expect("No program found, notice that function must have a `.gb` suffix"); + let error_message = format!("No program found, notice that the file must have a `.gb` suffix - {}\n", program_name); + let program = fs::read(program_path).expect(error_message.as_str()); let mbc_type = program[CARTRIDGE_TYPE_ADDRESS]; From 00e74570d8f5450d280503a3a61f12795e69bfed Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Fri, 30 Jul 2021 13:47:06 +0300 Subject: [PATCH 046/136] Fix the window rendering --- lib_gb/src/ppu/fifo/fifo_ppu.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_gb/src/ppu/fifo/fifo_ppu.rs b/lib_gb/src/ppu/fifo/fifo_ppu.rs index fefb48a4..d019e980 100644 --- a/lib_gb/src/ppu/fifo/fifo_ppu.rs +++ b/lib_gb/src/ppu/fifo/fifo_ppu.rs @@ -186,7 +186,6 @@ impl FifoPpu{ } self.t_cycles_passed = 0; self.ly_register += 1; - self.bg_fetcher.try_increment_window_counter(); } } PpuState::Vblank=>{ @@ -221,6 +220,7 @@ impl FifoPpu{ if self.h_blank_interrupt_request{ self.trigger_stat_interrupt = true; } + self.bg_fetcher.try_increment_window_counter(); self.bg_fetcher.reset(); self.sprite_fetcher.reset(); } From ecd624df16000039a32a9c460196e2797af949c6 Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Fri, 30 Jul 2021 13:55:32 +0300 Subject: [PATCH 047/136] Update the readme to include the ultimate gb talk as one of the resources --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 4b74cce9..7dd2c8dd 100644 --- a/README.md +++ b/README.md @@ -41,4 +41,5 @@ Curerently there is no Support (support is planned in the future) - [The GameBoy Programming Manual](http://index-of.es/Varios-2/Game%20Boy%20Programming%20Manual.pdf) - [gbdev gameboy sound hardware](https://gbdev.gg8.se/wiki/articles/Gameboy_sound_hardware) - [Hactix's awsome blog post](https://hacktix.github.io/GBEDG/) -- [Nightshade's awsome blog post](https://nightshade256.github.io/2021/03/27/gb-sound-emulation.html) \ No newline at end of file +- [Nightshade's awsome blog post](https://nightshade256.github.io/2021/03/27/gb-sound-emulation.html) +- [The Ultimate GameBoy Talk](https://www.youtube.com/watch?v=HyzD8pNlpwI) \ No newline at end of file From 894339dd30aebfe09997124d6c8d57a1c9c3f287 Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Fri, 30 Jul 2021 17:18:49 +0300 Subject: [PATCH 048/136] Fix sprite fetcher Ordering the sprites after fetching them from oam ram --- lib_gb/src/ppu/fifo/fifo_ppu.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib_gb/src/ppu/fifo/fifo_ppu.rs b/lib_gb/src/ppu/fifo/fifo_ppu.rs index d019e980..4f69f97a 100644 --- a/lib_gb/src/ppu/fifo/fifo_ppu.rs +++ b/lib_gb/src/ppu/fifo/fifo_ppu.rs @@ -162,6 +162,8 @@ impl FifoPpu{ self.t_cycles_passed += 2; //half a m_cycle if self.t_cycles_passed == 80{ + let slice = self.sprite_fetcher.oam_entries[0..self.sprite_fetcher.oam_entries_len as usize].as_mut(); + slice.sort_by(|s1:&SpriteAttribute, s2:&SpriteAttribute| s1.x.cmp(&s2.x)); self.state = PpuState::PixelTransfer; } } From 5f845a31a2d720694dc7a53740a89324e4feb877 Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Sat, 31 Jul 2021 17:59:35 +0300 Subject: [PATCH 049/136] Seperate the pixel x pos from the bg x pos --- lib_gb/src/ppu/fifo/bg_fetcher.rs | 2 +- lib_gb/src/ppu/fifo/fifo_ppu.rs | 12 +++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/lib_gb/src/ppu/fifo/bg_fetcher.rs b/lib_gb/src/ppu/fifo/bg_fetcher.rs index 233d6e46..ad14c1be 100644 --- a/lib_gb/src/ppu/fifo/bg_fetcher.rs +++ b/lib_gb/src/ppu/fifo/bg_fetcher.rs @@ -3,10 +3,10 @@ use crate::{mmu::vram::VRam, utils::{bit_masks::*, vec2::Vec2}}; use super::fetching_state::FethcingState; pub struct BGFetcher{ - pub current_x_pos:u8, pub fifo:Vec, pub window_line_counter:u8, + current_x_pos:u8, rendered_window:bool, rendering_window:bool, current_fetching_state:FethcingState, diff --git a/lib_gb/src/ppu/fifo/fifo_ppu.rs b/lib_gb/src/ppu/fifo/fifo_ppu.rs index 4f69f97a..79ef1bf2 100644 --- a/lib_gb/src/ppu/fifo/fifo_ppu.rs +++ b/lib_gb/src/ppu/fifo/fifo_ppu.rs @@ -29,6 +29,7 @@ pub struct FifoPpu{ screen_buffer: [u32; 160*144], push_lcd_buffer:Vec, screen_buffer_index:usize, + pixel_x_pos:u8, //interrupts pub v_blank_interrupt_request:bool, @@ -74,6 +75,7 @@ impl FifoPpu{ bg_fetcher:BGFetcher::new(), sprite_fetcher:SpriteFetcher::new(), push_lcd_buffer:Vec::::new(), + pixel_x_pos:0, } } @@ -90,6 +92,7 @@ impl FifoPpu{ self.trigger_stat_interrupt = false; self.bg_fetcher.reset(); self.sprite_fetcher.reset(); + self.pixel_x_pos = 0; } pub fn turn_on(&mut self){ @@ -186,6 +189,7 @@ impl FifoPpu{ self.trigger_stat_interrupt = true; } } + self.pixel_x_pos = 0; self.t_cycles_passed = 0; self.ly_register += 1; } @@ -196,6 +200,7 @@ impl FifoPpu{ if self.oam_search_interrupt_request{ self.trigger_stat_interrupt = true; } + self.pixel_x_pos = 0; self.t_cycles_passed = 0; self.ly_register = 0; } @@ -206,10 +211,10 @@ impl FifoPpu{ self.t_cycles_passed += 2; } PpuState::PixelTransfer=>{ - if self.bg_fetcher.current_x_pos < 160{ + if self.pixel_x_pos < 160{ self.bg_fetcher.fetch_pixels(&self.vram, self.lcd_control, self.ly_register, &self.window_pos, &self.bg_pos); if self.lcd_control & BIT_1_MASK != 0{ - self.sprite_fetcher.fetch_pixels(&self.vram, self.ly_register, self.bg_fetcher.current_x_pos); + self.sprite_fetcher.fetch_pixels(&self.vram, self.ly_register, self.pixel_x_pos); } } @@ -217,7 +222,7 @@ impl FifoPpu{ self.try_push_to_lcd(); } - if self.bg_fetcher.current_x_pos == 160 && self.bg_fetcher.fifo.is_empty(){ + if self.pixel_x_pos == 160 { self.state = PpuState::Hblank; if self.h_blank_interrupt_request{ self.trigger_stat_interrupt = true; @@ -263,6 +268,7 @@ impl FifoPpu{ }; self.push_lcd_buffer.push(pixel); + self.pixel_x_pos += 1; } } From c3d0a4fe7e119f4f1ef44c641f11ad67ccbdb707 Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Sat, 31 Jul 2021 20:34:28 +0300 Subject: [PATCH 050/136] The bg stops render when a sprite has been found Both fifos cant operate at the same time. the sprite fifo stops the bg fifo and the pixels to the lcd pusher. --- lib_gb/src/ppu/fifo/bg_fetcher.rs | 4 ++++ lib_gb/src/ppu/fifo/fifo_ppu.rs | 15 ++++++++++----- lib_gb/src/ppu/fifo/sprite_fetcher.rs | 9 ++++++++- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/lib_gb/src/ppu/fifo/bg_fetcher.rs b/lib_gb/src/ppu/fifo/bg_fetcher.rs index ad14c1be..3bfcf0ff 100644 --- a/lib_gb/src/ppu/fifo/bg_fetcher.rs +++ b/lib_gb/src/ppu/fifo/bg_fetcher.rs @@ -32,6 +32,10 @@ impl BGFetcher{ self.rendering_window = false; } + pub fn pause(&mut self){ + self.current_fetching_state = FethcingState::TileNumber; + } + pub fn try_increment_window_counter(&mut self){ if self.rendered_window{ self.window_line_counter += 1; diff --git a/lib_gb/src/ppu/fifo/fifo_ppu.rs b/lib_gb/src/ppu/fifo/fifo_ppu.rs index 79ef1bf2..fb417ec2 100644 --- a/lib_gb/src/ppu/fifo/fifo_ppu.rs +++ b/lib_gb/src/ppu/fifo/fifo_ppu.rs @@ -212,15 +212,20 @@ impl FifoPpu{ } PpuState::PixelTransfer=>{ if self.pixel_x_pos < 160{ - self.bg_fetcher.fetch_pixels(&self.vram, self.lcd_control, self.ly_register, &self.window_pos, &self.bg_pos); if self.lcd_control & BIT_1_MASK != 0{ self.sprite_fetcher.fetch_pixels(&self.vram, self.ly_register, self.pixel_x_pos); } + if self.sprite_fetcher.rendering{ + self.bg_fetcher.pause(); + } + else{ + self.bg_fetcher.fetch_pixels(&self.vram, self.lcd_control, self.ly_register, &self.window_pos, &self.bg_pos); + for _ in 0..2{ + self.try_push_to_lcd(); + } + } } - - for _ in 0..2{ - self.try_push_to_lcd(); - } + if self.pixel_x_pos == 160 { self.state = PpuState::Hblank; diff --git a/lib_gb/src/ppu/fifo/sprite_fetcher.rs b/lib_gb/src/ppu/fifo/sprite_fetcher.rs index 5f5662b0..886d19e8 100644 --- a/lib_gb/src/ppu/fifo/sprite_fetcher.rs +++ b/lib_gb/src/ppu/fifo/sprite_fetcher.rs @@ -8,6 +8,7 @@ pub struct SpriteFetcher{ pub fifo:Vec<(u8, u8)>, pub oam_entries:[SpriteAttribute; 10], pub oam_entries_len:u8, + pub rendering:bool, current_fetching_state:FethcingState, current_oam_entry:u8, @@ -32,7 +33,8 @@ impl SpriteFetcher{ current_oam_entry:0, oam_entries_len:0, oam_entries, - fifo:Vec::<(u8,u8)>::with_capacity(8) + fifo:Vec::<(u8,u8)>::with_capacity(8), + rendering:false } } @@ -41,6 +43,7 @@ impl SpriteFetcher{ self.oam_entries_len = 0; self.current_fetching_state = FethcingState::TileNumber; self.fifo.clear(); + self.rendering = false; } pub fn fetch_pixels(&mut self, vram:&VRam, ly_register:u8, current_x_pos:u8){ @@ -50,8 +53,12 @@ impl SpriteFetcher{ let oam_entry = &self.oam_entries[self.current_oam_entry as usize]; if oam_entry.x <= current_x_pos + 8{ self.current_fetching_state = FethcingState::LowTileData(oam_entry.tile_number); + self.rendering = true; + return; } } + // Reach here if not rendering this time a sprite + self.rendering = false; } FethcingState::LowTileData(tile_num)=>{ let current_tile_data_address = tile_num as u16 * 16; From fb470cbf0c2f62fe26876dbbb187c74eb06bca26 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sun, 1 Aug 2021 13:39:31 +0300 Subject: [PATCH 051/136] Fix sprite fetcher The fifo is 8 pxiels length. If it contains pixels from previous sprite the new sprite first number of pixels in the fifo are discarded and not pushed to the fifo. This way sprite whos ovelapping wont override each other and the sprite with the lowest x value (higher priority) will be drawn. --- lib_gb/src/ppu/fifo/sprite_fetcher.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib_gb/src/ppu/fifo/sprite_fetcher.rs b/lib_gb/src/ppu/fifo/sprite_fetcher.rs index 886d19e8..214bfbe3 100644 --- a/lib_gb/src/ppu/fifo/sprite_fetcher.rs +++ b/lib_gb/src/ppu/fifo/sprite_fetcher.rs @@ -51,7 +51,7 @@ impl SpriteFetcher{ FethcingState::TileNumber=>{ if self.oam_entries_len > self.current_oam_entry{ let oam_entry = &self.oam_entries[self.current_oam_entry as usize]; - if oam_entry.x <= current_x_pos + 8{ + if oam_entry.x <= current_x_pos + 8 && current_x_pos < oam_entry.x{ self.current_fetching_state = FethcingState::LowTileData(oam_entry.tile_number); self.rendering = true; return; @@ -72,9 +72,10 @@ impl SpriteFetcher{ } FethcingState::Push(low_data, high_data)=>{ let oam_attribute = &self.oam_entries[self.current_oam_entry as usize]; + let start_x = self.fifo.len(); - if !oam_attribute.flip_x{ - for i in (0..8).rev(){ + if oam_attribute.flip_x{ + for i in start_x..8{ let mask = 1 << i; let mut pixel = (low_data & mask) >> i; pixel |= ((high_data & mask) >> i) << 1; @@ -82,7 +83,7 @@ impl SpriteFetcher{ } } else{ - for i in 0..8{ + for i in (start_x..8).rev(){ let mask = 1 << i; let mut pixel = (low_data & mask) >> i; pixel |= ((high_data & mask) >> i) << 1; From ee583811c8c29edca8d89867fef2a7d5a0044bff Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 13 Aug 2021 12:09:45 +0300 Subject: [PATCH 052/136] Add support for sprite flipping on Y axis --- lib_gb/src/ppu/fifo/sprite_fetcher.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/lib_gb/src/ppu/fifo/sprite_fetcher.rs b/lib_gb/src/ppu/fifo/sprite_fetcher.rs index 214bfbe3..73941e16 100644 --- a/lib_gb/src/ppu/fifo/sprite_fetcher.rs +++ b/lib_gb/src/ppu/fifo/sprite_fetcher.rs @@ -61,13 +61,23 @@ impl SpriteFetcher{ self.rendering = false; } FethcingState::LowTileData(tile_num)=>{ - let current_tile_data_address = tile_num as u16 * 16; - let low_data = vram.read_current_bank(current_tile_data_address + (2 * (ly_register % 8)) as u16); + let oam_attribute = &self.oam_entries[self.current_oam_entry as usize]; + let current_tile_data_address = if !oam_attribute.flip_y { + tile_num as u16 * 16 + (2 * (ly_register % 8)) as u16 + }else{ + tile_num as u16 * 16 + (2 * (7 - (ly_register % 8))) as u16 + }; + let low_data = vram.read_current_bank(current_tile_data_address); self.current_fetching_state = FethcingState::HighTileData(tile_num, low_data); } FethcingState::HighTileData(tile_num, low_data)=>{ - let current_tile_data_address = tile_num as u16 * 16; - let high_data = vram.read_current_bank(current_tile_data_address + (2 * (ly_register % 8)) as u16 + 1); + let oam_attribute = &self.oam_entries[self.current_oam_entry as usize]; + let current_tile_data_address = if !oam_attribute.flip_y { + tile_num as u16 * 16 + (2 * (ly_register % 8)) as u16 + }else{ + tile_num as u16 * 16 + (2 * (7 - (ly_register % 8))) as u16 + }; + let high_data = vram.read_current_bank(current_tile_data_address + 1); self.current_fetching_state = FethcingState::Push(low_data, high_data); } FethcingState::Push(low_data, high_data)=>{ From 33a18e59cc927290531fa5f1b6054552997382b9 Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 13 Aug 2021 14:00:51 +0300 Subject: [PATCH 053/136] Update the fifo for the new sprite fetcher --- lib_gb/src/ppu/fifo/fifo_ppu.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_gb/src/ppu/fifo/fifo_ppu.rs b/lib_gb/src/ppu/fifo/fifo_ppu.rs index fb417ec2..f17ffa71 100644 --- a/lib_gb/src/ppu/fifo/fifo_ppu.rs +++ b/lib_gb/src/ppu/fifo/fifo_ppu.rs @@ -213,7 +213,7 @@ impl FifoPpu{ PpuState::PixelTransfer=>{ if self.pixel_x_pos < 160{ if self.lcd_control & BIT_1_MASK != 0{ - self.sprite_fetcher.fetch_pixels(&self.vram, self.ly_register, self.pixel_x_pos); + self.sprite_fetcher.fetch_pixels(&self.vram, self.lcd_control, self.ly_register, self.pixel_x_pos); } if self.sprite_fetcher.rendering{ self.bg_fetcher.pause(); From 205f1fa482584cad2de4bdaa18924001ddce259b Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 13 Aug 2021 14:01:17 +0300 Subject: [PATCH 054/136] Trying to add support for extended sprites --- lib_gb/src/ppu/fifo/sprite_fetcher.rs | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/lib_gb/src/ppu/fifo/sprite_fetcher.rs b/lib_gb/src/ppu/fifo/sprite_fetcher.rs index 73941e16..b435978f 100644 --- a/lib_gb/src/ppu/fifo/sprite_fetcher.rs +++ b/lib_gb/src/ppu/fifo/sprite_fetcher.rs @@ -1,6 +1,6 @@ use std::mem::{self, MaybeUninit}; -use crate::{mmu::vram::VRam, ppu::sprite_attribute::SpriteAttribute}; +use crate::{mmu::vram::VRam, ppu::sprite_attribute::SpriteAttribute, utils::bit_masks::{BIT_0_MASK, BIT_2_MASK}}; use super::fetching_state::FethcingState; @@ -46,13 +46,19 @@ impl SpriteFetcher{ self.rendering = false; } - pub fn fetch_pixels(&mut self, vram:&VRam, ly_register:u8, current_x_pos:u8){ + pub fn fetch_pixels(&mut self, vram:&VRam, lcd_control:u8, ly_register:u8, current_x_pos:u8){ + let sprite_size = if lcd_control & & BIT_2_MASK == 0 {8} else{16}; + match self.current_fetching_state{ FethcingState::TileNumber=>{ if self.oam_entries_len > self.current_oam_entry{ let oam_entry = &self.oam_entries[self.current_oam_entry as usize]; if oam_entry.x <= current_x_pos + 8 && current_x_pos < oam_entry.x{ - self.current_fetching_state = FethcingState::LowTileData(oam_entry.tile_number); + let mut tile_number = oam_entry.tile_number; + if lcd_control & BIT_2_MASK != 0{ + tile_number &= !BIT_0_MASK + } + self.current_fetching_state = FethcingState::LowTileData(tile_number); self.rendering = true; return; } @@ -63,9 +69,9 @@ impl SpriteFetcher{ FethcingState::LowTileData(tile_num)=>{ let oam_attribute = &self.oam_entries[self.current_oam_entry as usize]; let current_tile_data_address = if !oam_attribute.flip_y { - tile_num as u16 * 16 + (2 * (ly_register % 8)) as u16 + tile_num as u16 * 16 + (2 * (ly_register % sprite_size)) as u16 }else{ - tile_num as u16 * 16 + (2 * (7 - (ly_register % 8))) as u16 + tile_num as u16 * 16 + (2 * ((sprite_size - 1) - (ly_register % sprite_size))) as u16 }; let low_data = vram.read_current_bank(current_tile_data_address); self.current_fetching_state = FethcingState::HighTileData(tile_num, low_data); @@ -73,9 +79,9 @@ impl SpriteFetcher{ FethcingState::HighTileData(tile_num, low_data)=>{ let oam_attribute = &self.oam_entries[self.current_oam_entry as usize]; let current_tile_data_address = if !oam_attribute.flip_y { - tile_num as u16 * 16 + (2 * (ly_register % 8)) as u16 + tile_num as u16 * 16 + (2 * (ly_register % sprite_size)) as u16 }else{ - tile_num as u16 * 16 + (2 * (7 - (ly_register % 8))) as u16 + tile_num as u16 * 16 + (2 * ((sprite_size - 1) - (ly_register % sprite_size))) as u16 }; let high_data = vram.read_current_bank(current_tile_data_address + 1); self.current_fetching_state = FethcingState::Push(low_data, high_data); @@ -102,7 +108,6 @@ impl SpriteFetcher{ } self.current_fetching_state = FethcingState::TileNumber; - self.current_oam_entry += 1; } } From f2c4496acee4cf4d1e689417cf4fc14e5b2db178 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sun, 15 Aug 2021 00:10:41 +0300 Subject: [PATCH 055/136] Implement the extended sprites correctly. Found a bug in the way I rendered the sprites. --- lib_gb/src/ppu/fifo/sprite_fetcher.rs | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/lib_gb/src/ppu/fifo/sprite_fetcher.rs b/lib_gb/src/ppu/fifo/sprite_fetcher.rs index b435978f..6b7a5208 100644 --- a/lib_gb/src/ppu/fifo/sprite_fetcher.rs +++ b/lib_gb/src/ppu/fifo/sprite_fetcher.rs @@ -47,7 +47,7 @@ impl SpriteFetcher{ } pub fn fetch_pixels(&mut self, vram:&VRam, lcd_control:u8, ly_register:u8, current_x_pos:u8){ - let sprite_size = if lcd_control & & BIT_2_MASK == 0 {8} else{16}; + let sprite_size = if lcd_control & BIT_2_MASK == 0 {8} else{16}; match self.current_fetching_state{ FethcingState::TileNumber=>{ @@ -68,21 +68,13 @@ impl SpriteFetcher{ } FethcingState::LowTileData(tile_num)=>{ let oam_attribute = &self.oam_entries[self.current_oam_entry as usize]; - let current_tile_data_address = if !oam_attribute.flip_y { - tile_num as u16 * 16 + (2 * (ly_register % sprite_size)) as u16 - }else{ - tile_num as u16 * 16 + (2 * ((sprite_size - 1) - (ly_register % sprite_size))) as u16 - }; + let current_tile_data_address = Self::get_current_tile_data_address(ly_register, oam_attribute, sprite_size, tile_num); let low_data = vram.read_current_bank(current_tile_data_address); self.current_fetching_state = FethcingState::HighTileData(tile_num, low_data); } FethcingState::HighTileData(tile_num, low_data)=>{ let oam_attribute = &self.oam_entries[self.current_oam_entry as usize]; - let current_tile_data_address = if !oam_attribute.flip_y { - tile_num as u16 * 16 + (2 * (ly_register % sprite_size)) as u16 - }else{ - tile_num as u16 * 16 + (2 * ((sprite_size - 1) - (ly_register % sprite_size))) as u16 - }; + let current_tile_data_address = Self::get_current_tile_data_address(ly_register, oam_attribute, sprite_size, tile_num); let high_data = vram.read_current_bank(current_tile_data_address + 1); self.current_fetching_state = FethcingState::Push(low_data, high_data); } @@ -112,4 +104,17 @@ impl SpriteFetcher{ } } } + + // Receiving the tile_num since in case of extended sprite this could change (the first bit is reset) + fn get_current_tile_data_address(ly_register:u8, sprite_attrib:&SpriteAttribute, sprite_size:u8, tile_num:u8)->u16{ + return if sprite_attrib.flip_y{ + // Since Im flipping but dont know for what rect (8X8 or 8X16) I need sub this from the size (minus 1 casue im starting to count from 0 in the screen lines). + tile_num as u16 * 16 + (2 * (sprite_size - 1 - (16 - (sprite_attrib.y - ly_register)))) as u16 + } + else{ + // Since the sprite attribute y pos is the right most dot of the rect + // Im subtracting this from 16 (since the rects are 8X16) + tile_num as u16 * 16 + (2 * (16 - (sprite_attrib.y - ly_register))) as u16 + }; + } } \ No newline at end of file From e6a375515b6f5aedc81ac0f38f17e2f5e11865e3 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sun, 15 Aug 2021 02:35:31 +0300 Subject: [PATCH 056/136] Fix the proprity in the fifo --- lib_gb/src/ppu/fifo/fifo_ppu.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib_gb/src/ppu/fifo/fifo_ppu.rs b/lib_gb/src/ppu/fifo/fifo_ppu.rs index f17ffa71..85239df7 100644 --- a/lib_gb/src/ppu/fifo/fifo_ppu.rs +++ b/lib_gb/src/ppu/fifo/fifo_ppu.rs @@ -244,12 +244,13 @@ impl FifoPpu{ fn try_push_to_lcd(&mut self){ if !self.bg_fetcher.fifo.is_empty(){ - let bg_pixel = self.bg_color_mapping[self.bg_fetcher.fifo.remove(0) as usize]; + let bg_pixel_color_num = self.bg_fetcher.fifo.remove(0); + let bg_pixel = self.bg_color_mapping[bg_pixel_color_num as usize]; let pixel = if !self.sprite_fetcher.fifo.is_empty(){ let sprite_color_num = self.sprite_fetcher.fifo.remove(0); let pixel_oam_attribute = &self.sprite_fetcher.oam_entries[sprite_color_num.1 as usize]; - if sprite_color_num.0 == 0 || pixel_oam_attribute.is_bg_priority{ + if sprite_color_num.0 == 0 || (pixel_oam_attribute.is_bg_priority && bg_pixel_color_num != 0){ bg_pixel } else{ From 21ed932d33f04adc7e3f659039278b1a5835e695 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sun, 15 Aug 2021 02:36:06 +0300 Subject: [PATCH 057/136] WIP try to impement the internal sprites priority --- lib_gb/src/ppu/fifo/sprite_fetcher.rs | 28 +++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/lib_gb/src/ppu/fifo/sprite_fetcher.rs b/lib_gb/src/ppu/fifo/sprite_fetcher.rs index 6b7a5208..bd016f3b 100644 --- a/lib_gb/src/ppu/fifo/sprite_fetcher.rs +++ b/lib_gb/src/ppu/fifo/sprite_fetcher.rs @@ -83,19 +83,39 @@ impl SpriteFetcher{ let start_x = self.fifo.len(); if oam_attribute.flip_x{ - for i in start_x..8{ + for i in 0..8{ let mask = 1 << i; let mut pixel = (low_data & mask) >> i; pixel |= ((high_data & mask) >> i) << 1; - self.fifo.push((pixel, self.current_oam_entry)); + if i < start_x { + if self.fifo[i].0 != 0{ + continue; + } + else{ + self.fifo[i].0 = pixel; + } + } + else{ + self.fifo.push((pixel, self.current_oam_entry)); + } } } else{ - for i in (start_x..8).rev(){ + for i in (0..8).rev(){ let mask = 1 << i; let mut pixel = (low_data & mask) >> i; pixel |= ((high_data & mask) >> i) << 1; - self.fifo.push((pixel, self.current_oam_entry)); + if i > 7 - start_x { + if self.fifo[7 - i].0 != 0{ + continue; + } + else{ + self.fifo[7 - i].0 = pixel; + } + } + else{ + self.fifo.push((pixel, self.current_oam_entry)); + } } } From 46eca23dfacd4814ce87ec1b34d9809e42f67290 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sun, 15 Aug 2021 12:37:20 +0300 Subject: [PATCH 058/136] WIP Try to impl the sprite priority Need to better understant the sprite rendering priority --- lib_gb/src/ppu/fifo/fifo_ppu.rs | 7 +------ lib_gb/src/ppu/fifo/sprite_fetcher.rs | 16 +++++----------- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/lib_gb/src/ppu/fifo/fifo_ppu.rs b/lib_gb/src/ppu/fifo/fifo_ppu.rs index 85239df7..03994c21 100644 --- a/lib_gb/src/ppu/fifo/fifo_ppu.rs +++ b/lib_gb/src/ppu/fifo/fifo_ppu.rs @@ -261,12 +261,7 @@ impl FifoPpu{ self.obj_color_mapping0[sprite_color_num.0 as usize] }; - if let Some(color) = sprite_pixel{ - color - } - else{ - std::panic!("Corruption in the object color pallete"); - } + sprite_pixel.expect("Corruption in the object color pallete") } } else{ diff --git a/lib_gb/src/ppu/fifo/sprite_fetcher.rs b/lib_gb/src/ppu/fifo/sprite_fetcher.rs index bd016f3b..e0afcfb2 100644 --- a/lib_gb/src/ppu/fifo/sprite_fetcher.rs +++ b/lib_gb/src/ppu/fifo/sprite_fetcher.rs @@ -88,11 +88,8 @@ impl SpriteFetcher{ let mut pixel = (low_data & mask) >> i; pixel |= ((high_data & mask) >> i) << 1; if i < start_x { - if self.fifo[i].0 != 0{ - continue; - } - else{ - self.fifo[i].0 = pixel; + if self.fifo[i].0 == 0{ + // self.fifo[i].0 = pixel; } } else{ @@ -105,12 +102,9 @@ impl SpriteFetcher{ let mask = 1 << i; let mut pixel = (low_data & mask) >> i; pixel |= ((high_data & mask) >> i) << 1; - if i > 7 - start_x { - if self.fifo[7 - i].0 != 0{ - continue; - } - else{ - self.fifo[7 - i].0 = pixel; + if 7 - i < start_x { + if self.fifo[7 - i].0 == 0{ + // self.fifo[7 - i].0 = pixel; } } else{ From d959651f98b1f0425142b4de6a0f78d005189f74 Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 20 Aug 2021 18:28:39 +0300 Subject: [PATCH 059/136] Fix the fifo obj rendering The bug was a case when the x position is more advanced than the start of the obj x pos. the pixels would still be pushed and we will end up with more than 8 pixels in the fifo. Solve it by checking the x pos too. --- lib_gb/src/ppu/fifo/sprite_fetcher.rs | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/lib_gb/src/ppu/fifo/sprite_fetcher.rs b/lib_gb/src/ppu/fifo/sprite_fetcher.rs index e0afcfb2..e311ce66 100644 --- a/lib_gb/src/ppu/fifo/sprite_fetcher.rs +++ b/lib_gb/src/ppu/fifo/sprite_fetcher.rs @@ -82,34 +82,32 @@ impl SpriteFetcher{ let oam_attribute = &self.oam_entries[self.current_oam_entry as usize]; let start_x = self.fifo.len(); + let skip_x = 8 - (oam_attribute.x - current_x_pos) as usize; + if oam_attribute.flip_x{ - for i in 0..8{ + for i in (0 + skip_x)..8{ let mask = 1 << i; let mut pixel = (low_data & mask) >> i; pixel |= ((high_data & mask) >> i) << 1; - if i < start_x { - if self.fifo[i].0 == 0{ - // self.fifo[i].0 = pixel; - } - } - else{ + if i + skip_x >= start_x { self.fifo.push((pixel, self.current_oam_entry)); } + else if self.fifo[i + skip_x].0 == 0{ + self.fifo[i+ skip_x] = (pixel, self.current_oam_entry); + } } } else{ - for i in (0..8).rev(){ + for i in (0..(8 - skip_x)).rev(){ let mask = 1 << i; let mut pixel = (low_data & mask) >> i; pixel |= ((high_data & mask) >> i) << 1; - if 7 - i < start_x { - if self.fifo[7 - i].0 == 0{ - // self.fifo[7 - i].0 = pixel; - } - } - else{ + if 7 - skip_x - i >= start_x { self.fifo.push((pixel, self.current_oam_entry)); } + else if self.fifo[7 - skip_x - i].0 == 0{ + self.fifo[7 - skip_x - i] = (pixel, self.current_oam_entry); + } } } From 69a3e94bc1aa2e4fa2bec3926dd08d00b3443ed4 Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 20 Aug 2021 19:39:49 +0300 Subject: [PATCH 060/136] Arrange the bg fetcher code. I think I know how to solve the problem with the x axis scroll animation It has something to do with SCX mod 8 pixels wich are discarded at the beginning of the stage 3 (push). --- lib_gb/src/ppu/fifo/bg_fetcher.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/lib_gb/src/ppu/fifo/bg_fetcher.rs b/lib_gb/src/ppu/fifo/bg_fetcher.rs index 3bfcf0ff..05fac806 100644 --- a/lib_gb/src/ppu/fifo/bg_fetcher.rs +++ b/lib_gb/src/ppu/fifo/bg_fetcher.rs @@ -72,24 +72,14 @@ impl BGFetcher{ self.current_fetching_state = FethcingState::LowTileData(tile_num); } FethcingState::LowTileData(tile_num)=>{ - let current_tile_base_data_address = if (lcd_control & BIT_4_MASK) == 0 && (tile_num & BIT_7_MASK) == 0 {0x1000} else {0}; - let current_tile_data_address = current_tile_base_data_address + (tile_num as u16 * 16); - let low_data = if self.rendering_window{ - vram.read_current_bank(current_tile_data_address + (2 * (self.window_line_counter % 8)) as u16) - } else{ - vram.read_current_bank(current_tile_data_address + (2 * ((bg_pos.y as u16 + ly_register as u16) % 8)) ) - }; + let address = self.get_tila_data_address(lcd_control, bg_pos, ly_register, tile_num); + let low_data = vram.read_current_bank(address); self.current_fetching_state = FethcingState::HighTileData(tile_num, low_data); } FethcingState::HighTileData(tile_num, low_data)=>{ - let current_tile_base_data_address = if (lcd_control & BIT_4_MASK) == 0 && (tile_num & BIT_7_MASK) == 0 {0x1000} else {0}; - let current_tile_data_address = current_tile_base_data_address + (tile_num as u16 * 16); - let high_data = if self.rendering_window{ - vram.read_current_bank(current_tile_data_address + (2 * (self.window_line_counter % 8)) as u16 + 1) - } else{ - vram.read_current_bank(current_tile_data_address + (2 * ((bg_pos.y as u16 + ly_register as u16) % 8)) + 1) - }; + let address = self.get_tila_data_address(lcd_control, bg_pos, ly_register, tile_num); + let high_data = vram.read_current_bank(address + 1); self.current_fetching_state = FethcingState::Push(low_data, high_data); } @@ -117,6 +107,16 @@ impl BGFetcher{ } } + fn get_tila_data_address(&self, lcd_control:u8, bg_pos:&Vec2, ly_register:u8, tile_num:u8)->u16{ + let current_tile_base_data_address = if (lcd_control & BIT_4_MASK) == 0 && (tile_num & BIT_7_MASK) == 0 {0x1000} else {0}; + let current_tile_data_address = current_tile_base_data_address + (tile_num as u16 * 16); + return if self.rendering_window{ + current_tile_data_address + (2 * (self.window_line_counter % 8)) as u16 + } else{ + current_tile_data_address + (2 * ((bg_pos.y as u16 + ly_register as u16) % 8)) + }; + } + fn is_redering_wnd(&self, lcd_control:u8, window_pos:&Vec2, ly_register:u8)->bool{ window_pos.x <= self.current_x_pos && window_pos.y <= ly_register && (lcd_control & BIT_5_MASK) != 0 } From 1926087cc704f49ca96c407980a79909be0506c6 Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 20 Aug 2021 20:20:27 +0300 Subject: [PATCH 061/136] Fix the bg rendering Allow the smooth moving animation on X axis --- lib_gb/src/ppu/fifo/fifo_ppu.rs | 40 +++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/lib_gb/src/ppu/fifo/fifo_ppu.rs b/lib_gb/src/ppu/fifo/fifo_ppu.rs index 03994c21..7b2308fe 100644 --- a/lib_gb/src/ppu/fifo/fifo_ppu.rs +++ b/lib_gb/src/ppu/fifo/fifo_ppu.rs @@ -30,6 +30,7 @@ pub struct FifoPpu{ push_lcd_buffer:Vec, screen_buffer_index:usize, pixel_x_pos:u8, + scanline_started:bool, //interrupts pub v_blank_interrupt_request:bool, @@ -76,6 +77,7 @@ impl FifoPpu{ sprite_fetcher:SpriteFetcher::new(), push_lcd_buffer:Vec::::new(), pixel_x_pos:0, + scanline_started:false } } @@ -168,6 +170,7 @@ impl FifoPpu{ let slice = self.sprite_fetcher.oam_entries[0..self.sprite_fetcher.oam_entries_len as usize].as_mut(); slice.sort_by(|s1:&SpriteAttribute, s2:&SpriteAttribute| s1.x.cmp(&s2.x)); self.state = PpuState::PixelTransfer; + self.scanline_started = false; } } PpuState::Hblank=>{ @@ -222,28 +225,47 @@ impl FifoPpu{ self.bg_fetcher.fetch_pixels(&self.vram, self.lcd_control, self.ly_register, &self.window_pos, &self.bg_pos); for _ in 0..2{ self.try_push_to_lcd(); + if self.pixel_x_pos == 160 { + self.state = PpuState::Hblank; + if self.h_blank_interrupt_request{ + self.trigger_stat_interrupt = true; + } + self.bg_fetcher.try_increment_window_counter(); + self.bg_fetcher.reset(); + self.sprite_fetcher.reset(); + + // If im on the first iteration and finished the 160 pixels break; + // In this case the number of t_cycles should be eneven but it will break + // my code way too much for now so Im leaving this as it is... (maybe in the future) + break; + } } } } - if self.pixel_x_pos == 160 { - self.state = PpuState::Hblank; - if self.h_blank_interrupt_request{ - self.trigger_stat_interrupt = true; - } - self.bg_fetcher.try_increment_window_counter(); - self.bg_fetcher.reset(); - self.sprite_fetcher.reset(); - } + self.t_cycles_passed += 2; } } + } } fn try_push_to_lcd(&mut self){ if !self.bg_fetcher.fifo.is_empty(){ + if !self.scanline_started{ + // discard the next pixel in the bg fifo + // the bg fifo should start with 8 pixels and not push more untill its empty again + if 8 - self.bg_fetcher.fifo.len() >= self.bg_pos.x as usize % 8{ + self.scanline_started = true; + } + else{ + self.bg_fetcher.fifo.remove(0); + return; + } + } + let bg_pixel_color_num = self.bg_fetcher.fifo.remove(0); let bg_pixel = self.bg_color_mapping[bg_pixel_color_num as usize]; let pixel = if !self.sprite_fetcher.fifo.is_empty(){ From dff63cb725efafebad9a3ddc666e9ac09633694f Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 3 Sep 2021 20:19:25 +0300 Subject: [PATCH 062/136] FIx the bug with the missing vertical object lines The bug would ocour only for objects drawn with odd scx register. Since the state machine of the sprite fetcher worked in steps of 2 t_cycles it would miss some lines. Moved the state machine to work for every t_cycle and not for the even ones. --- lib_gb/src/ppu/fifo/bg_fetcher.rs | 13 +++- lib_gb/src/ppu/fifo/fifo_ppu.rs | 48 +++++++-------- lib_gb/src/ppu/fifo/sprite_fetcher.rs | 88 +++++++++++++++------------ 3 files changed, 86 insertions(+), 63 deletions(-) diff --git a/lib_gb/src/ppu/fifo/bg_fetcher.rs b/lib_gb/src/ppu/fifo/bg_fetcher.rs index 05fac806..6161b044 100644 --- a/lib_gb/src/ppu/fifo/bg_fetcher.rs +++ b/lib_gb/src/ppu/fifo/bg_fetcher.rs @@ -10,6 +10,7 @@ pub struct BGFetcher{ rendered_window:bool, rendering_window:bool, current_fetching_state:FethcingState, + t_cycles_counter:u8, } impl BGFetcher{ @@ -20,7 +21,8 @@ impl BGFetcher{ fifo:Vec::::with_capacity(8), window_line_counter:0, rendered_window:false, - rendering_window:false + rendering_window:false, + t_cycles_counter:0 } } @@ -30,10 +32,12 @@ impl BGFetcher{ self.current_fetching_state = FethcingState::TileNumber; self.rendered_window = false; self.rendering_window = false; + self.t_cycles_counter = 0; } pub fn pause(&mut self){ self.current_fetching_state = FethcingState::TileNumber; + self.t_cycles_counter = 0; } pub fn try_increment_window_counter(&mut self){ @@ -43,6 +47,11 @@ impl BGFetcher{ } pub fn fetch_pixels(&mut self, vram:&VRam, lcd_control:u8, ly_register:u8, window_pos:&Vec2, bg_pos:&Vec2){ + if self.t_cycles_counter % 2 == 0{ + self.t_cycles_counter = (self.t_cycles_counter + 1) % 8; + return; + } + let last_rendering_status = self.rendering_window; self.rendering_window = self.is_redering_wnd(lcd_control, window_pos, ly_register); if self.rendering_window{ @@ -105,6 +114,8 @@ impl BGFetcher{ } } } + + self.t_cycles_counter = (self.t_cycles_counter + 1) % 8; } fn get_tila_data_address(&self, lcd_control:u8, bg_pos:&Vec2, ly_register:u8, tile_num:u8)->u16{ diff --git a/lib_gb/src/ppu/fifo/fifo_ppu.rs b/lib_gb/src/ppu/fifo/fifo_ppu.rs index 7b2308fe..148a0dbe 100644 --- a/lib_gb/src/ppu/fifo/fifo_ppu.rs +++ b/lib_gb/src/ppu/fifo/fifo_ppu.rs @@ -214,31 +214,31 @@ impl FifoPpu{ self.t_cycles_passed += 2; } PpuState::PixelTransfer=>{ - if self.pixel_x_pos < 160{ - if self.lcd_control & BIT_1_MASK != 0{ - self.sprite_fetcher.fetch_pixels(&self.vram, self.lcd_control, self.ly_register, self.pixel_x_pos); - } - if self.sprite_fetcher.rendering{ - self.bg_fetcher.pause(); - } - else{ - self.bg_fetcher.fetch_pixels(&self.vram, self.lcd_control, self.ly_register, &self.window_pos, &self.bg_pos); - for _ in 0..2{ - self.try_push_to_lcd(); - if self.pixel_x_pos == 160 { - self.state = PpuState::Hblank; - if self.h_blank_interrupt_request{ - self.trigger_stat_interrupt = true; - } - self.bg_fetcher.try_increment_window_counter(); - self.bg_fetcher.reset(); - self.sprite_fetcher.reset(); + for _ in 0..2{ + if self.pixel_x_pos < 160{ + if self.lcd_control & BIT_1_MASK != 0{ + self.sprite_fetcher.fetch_pixels(&self.vram, self.lcd_control, self.ly_register, self.pixel_x_pos); + } + if self.sprite_fetcher.rendering{ + self.bg_fetcher.pause(); + } + else{ + self.bg_fetcher.fetch_pixels(&self.vram, self.lcd_control, self.ly_register, &self.window_pos, &self.bg_pos); + self.try_push_to_lcd(); + if self.pixel_x_pos == 160 { + self.state = PpuState::Hblank; + if self.h_blank_interrupt_request{ + self.trigger_stat_interrupt = true; + } + self.bg_fetcher.try_increment_window_counter(); + self.bg_fetcher.reset(); + self.sprite_fetcher.reset(); - // If im on the first iteration and finished the 160 pixels break; - // In this case the number of t_cycles should be eneven but it will break - // my code way too much for now so Im leaving this as it is... (maybe in the future) - break; - } + // If im on the first iteration and finished the 160 pixels break; + // In this case the number of t_cycles should be eneven but it will break + // my code way too much for now so Im leaving this as it is... (maybe in the future) + break; + } } } } diff --git a/lib_gb/src/ppu/fifo/sprite_fetcher.rs b/lib_gb/src/ppu/fifo/sprite_fetcher.rs index e311ce66..517a37f9 100644 --- a/lib_gb/src/ppu/fifo/sprite_fetcher.rs +++ b/lib_gb/src/ppu/fifo/sprite_fetcher.rs @@ -12,6 +12,7 @@ pub struct SpriteFetcher{ current_fetching_state:FethcingState, current_oam_entry:u8, + t_cycle_inner_state_counter:u8, } impl SpriteFetcher{ @@ -34,7 +35,8 @@ impl SpriteFetcher{ oam_entries_len:0, oam_entries, fifo:Vec::<(u8,u8)>::with_capacity(8), - rendering:false + rendering:false, + t_cycle_inner_state_counter:0, } } @@ -42,6 +44,7 @@ impl SpriteFetcher{ self.current_oam_entry = 0; self.oam_entries_len = 0; self.current_fetching_state = FethcingState::TileNumber; + self.t_cycle_inner_state_counter = 0; self.fifo.clear(); self.rendering = false; } @@ -67,54 +70,63 @@ impl SpriteFetcher{ self.rendering = false; } FethcingState::LowTileData(tile_num)=>{ - let oam_attribute = &self.oam_entries[self.current_oam_entry as usize]; - let current_tile_data_address = Self::get_current_tile_data_address(ly_register, oam_attribute, sprite_size, tile_num); - let low_data = vram.read_current_bank(current_tile_data_address); - self.current_fetching_state = FethcingState::HighTileData(tile_num, low_data); + if self.t_cycle_inner_state_counter % 2 != 0{ + let oam_attribute = &self.oam_entries[self.current_oam_entry as usize]; + let current_tile_data_address = Self::get_current_tile_data_address(ly_register, oam_attribute, sprite_size, tile_num); + let low_data = vram.read_current_bank(current_tile_data_address); + self.current_fetching_state = FethcingState::HighTileData(tile_num, low_data); + } } FethcingState::HighTileData(tile_num, low_data)=>{ - let oam_attribute = &self.oam_entries[self.current_oam_entry as usize]; - let current_tile_data_address = Self::get_current_tile_data_address(ly_register, oam_attribute, sprite_size, tile_num); - let high_data = vram.read_current_bank(current_tile_data_address + 1); - self.current_fetching_state = FethcingState::Push(low_data, high_data); + if self.t_cycle_inner_state_counter % 2 != 0{ + let oam_attribute = &self.oam_entries[self.current_oam_entry as usize]; + let current_tile_data_address = Self::get_current_tile_data_address(ly_register, oam_attribute, sprite_size, tile_num); + let high_data = vram.read_current_bank(current_tile_data_address + 1); + self.current_fetching_state = FethcingState::Push(low_data, high_data); + } } FethcingState::Push(low_data, high_data)=>{ - let oam_attribute = &self.oam_entries[self.current_oam_entry as usize]; - let start_x = self.fifo.len(); - - let skip_x = 8 - (oam_attribute.x - current_x_pos) as usize; - - if oam_attribute.flip_x{ - for i in (0 + skip_x)..8{ - let mask = 1 << i; - let mut pixel = (low_data & mask) >> i; - pixel |= ((high_data & mask) >> i) << 1; - if i + skip_x >= start_x { - self.fifo.push((pixel, self.current_oam_entry)); - } - else if self.fifo[i + skip_x].0 == 0{ - self.fifo[i+ skip_x] = (pixel, self.current_oam_entry); + if self.t_cycle_inner_state_counter % 2 != 0{ + + let oam_attribute = &self.oam_entries[self.current_oam_entry as usize]; + let start_x = self.fifo.len(); + + let skip_x = 8 - (oam_attribute.x - current_x_pos) as usize; + + if oam_attribute.flip_x{ + for i in (0 + skip_x)..8{ + let mask = 1 << i; + let mut pixel = (low_data & mask) >> i; + pixel |= ((high_data & mask) >> i) << 1; + if i + skip_x >= start_x { + self.fifo.push((pixel, self.current_oam_entry)); + } + else if self.fifo[i + skip_x].0 == 0{ + self.fifo[i+ skip_x] = (pixel, self.current_oam_entry); + } } } - } - else{ - for i in (0..(8 - skip_x)).rev(){ - let mask = 1 << i; - let mut pixel = (low_data & mask) >> i; - pixel |= ((high_data & mask) >> i) << 1; - if 7 - skip_x - i >= start_x { - self.fifo.push((pixel, self.current_oam_entry)); - } - else if self.fifo[7 - skip_x - i].0 == 0{ - self.fifo[7 - skip_x - i] = (pixel, self.current_oam_entry); + else{ + for i in (0..(8 - skip_x)).rev(){ + let mask = 1 << i; + let mut pixel = (low_data & mask) >> i; + pixel |= ((high_data & mask) >> i) << 1; + if 7 - skip_x - i >= start_x { + self.fifo.push((pixel, self.current_oam_entry)); + } + else if self.fifo[7 - skip_x - i].0 == 0{ + self.fifo[7 - skip_x - i] = (pixel, self.current_oam_entry); + } } } - } - self.current_fetching_state = FethcingState::TileNumber; - self.current_oam_entry += 1; + self.current_fetching_state = FethcingState::TileNumber; + self.current_oam_entry += 1; + } } } + + self.t_cycle_inner_state_counter = (self.t_cycle_inner_state_counter + 1) % 8; } // Receiving the tile_num since in case of extended sprite this could change (the first bit is reset) From f52c16c45902d851b237039773c6ff711e72d9ce Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 3 Sep 2021 20:24:25 +0300 Subject: [PATCH 063/136] Arrange the gameboy main code --- lib_gb/src/machine/gameboy.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib_gb/src/machine/gameboy.rs b/lib_gb/src/machine/gameboy.rs index 4a1f9111..362f7f9a 100644 --- a/lib_gb/src/machine/gameboy.rs +++ b/lib_gb/src/machine/gameboy.rs @@ -1,4 +1,10 @@ -use crate::{apu::{audio_device::AudioDevice, gb_apu::GbApu}, cpu::{gb_cpu::GbCpu, opcodes::opcode_resolver::*}, keypad::{joypad::Joypad, joypad_provider::JoypadProvider, joypad_register_updater}, mmu::{carts::mbc::Mbc, gb_mmu::{GbMmu, BOOT_ROM_SIZE}, memory::Memory}, ppu::{gb_ppu::{CYCLES_PER_FRAME, SCREEN_HEIGHT, SCREEN_WIDTH}, gfx_device::GfxDevice}, utils::bit_masks::BIT_7_MASK}; +use crate::{ + apu::{audio_device::AudioDevice, gb_apu::GbApu}, + cpu::{gb_cpu::GbCpu, opcodes::opcode_resolver::*}, + keypad::{joypad::Joypad, joypad_provider::JoypadProvider, joypad_register_updater}, + mmu::{carts::mbc::Mbc, gb_mmu::{GbMmu, BOOT_ROM_SIZE}, memory::Memory}, + ppu::{gb_ppu::CYCLES_PER_FRAME, gfx_device::GfxDevice}, utils::bit_masks::BIT_7_MASK +}; use super::interrupts_handler::InterruptsHandler; use std::boxed::Box; From b71436e1c9f3d57722525e9e600b918ef70b4658 Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 3 Sep 2021 23:39:28 +0300 Subject: [PATCH 064/136] Code indentation and redundant code removal This commit is a result of a SCR (self code review). --- lib_gb/src/machine/gameboy.rs | 27 ++++----------------------- lib_gb/src/mmu/io_components.rs | 7 +++++-- lib_gb/src/ppu/fifo/bg_fetcher.rs | 1 - lib_gb/src/ppu/fifo/fifo_ppu.rs | 5 ++--- lib_gb/src/ppu/fifo/sprite_fetcher.rs | 4 +--- 5 files changed, 12 insertions(+), 32 deletions(-) diff --git a/lib_gb/src/machine/gameboy.rs b/lib_gb/src/machine/gameboy.rs index f1f0489e..e35e97cd 100644 --- a/lib_gb/src/machine/gameboy.rs +++ b/lib_gb/src/machine/gameboy.rs @@ -3,9 +3,8 @@ use crate::{ cpu::gb_cpu::GbCpu, keypad::{joypad::Joypad, joypad_provider::JoypadProvider, joypad_register_updater}, mmu::{carts::mbc::Mbc, gb_mmu::{GbMmu, BOOT_ROM_SIZE}, memory::Memory}, - ppu::{gb_ppu::CYCLES_PER_FRAME, gfx_device::GfxDevice}, utils::bit_masks::BIT_7_MASK + ppu::{gb_ppu::CYCLES_PER_FRAME, gfx_device::GfxDevice} }; - use super::interrupts_handler::InterruptsHandler; use std::boxed::Box; use log::debug; @@ -14,9 +13,7 @@ use log::debug; pub struct GameBoy<'a, JP: JoypadProvider, AD:AudioDevice, GFX:GfxDevice> { cpu: GbCpu, mmu: GbMmu::<'a, AD, GFX>, - opcode_resolver:OpcodeResolver::>, interrupts_handler:InterruptsHandler, - cycles_counter:u32, joypad_provider: JP } @@ -27,7 +24,6 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice, GFX:GfxDevice> GameBoy<'a, JP, AD, G cpu:GbCpu::default(), mmu:GbMmu::new_with_bootrom(mbc, boot_rom, GbApu::new(audio_device), gfx_device), interrupts_handler: InterruptsHandler::default(), - cycles_counter:0, joypad_provider: joypad_provider } } @@ -46,7 +42,6 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice, GFX:GfxDevice> GameBoy<'a, JP, AD, G cpu:cpu, mmu:GbMmu::new(mbc, GbApu::new(audio_device), gfx_device), interrupts_handler: InterruptsHandler::default(), - cycles_counter:0, joypad_provider: joypad_provider, } } @@ -54,9 +49,9 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice, GFX:GfxDevice> GameBoy<'a, JP, AD, G pub fn cycle_frame(&mut self){ let mut joypad = Joypad::default(); - let mut last_ppu_power_state:bool = (self.mmu.io_components.ppu.lcd_control & BIT_7_MASK) != 0; + let mut cycles_counter = 0; - while self.cycles_counter < CYCLES_PER_FRAME{ + while cycles_counter < CYCLES_PER_FRAME{ self.joypad_provider.provide(&mut joypad); joypad_register_updater::update_joypad_registers(&joypad, &mut self.mmu); @@ -73,22 +68,8 @@ impl<'a, JP:JoypadProvider, AD:AudioDevice, GFX:GfxDevice> GameBoy<'a, JP, AD, G if interrupt_cycles != 0{ self.mmu.cycle(interrupt_cycles); } - - let iter_total_cycles= cpu_cycles_passed as u32 + interrupt_cycles as u32; - - - //In case the ppu just turned I want to keep it sync with the actual screen and thats why Im reseting the loop to finish - //the frame when the ppu finishes the frame - if !last_ppu_power_state && (self.mmu.io_components.ppu.lcd_control & BIT_7_MASK) != 0{ - self.cycles_counter = 0; - } - - self.cycles_counter += iter_total_cycles; - last_ppu_power_state = (self.mmu.io_components.ppu.lcd_control & BIT_7_MASK) != 0; - } - if self.cycles_counter >= CYCLES_PER_FRAME{ - self.cycles_counter -= CYCLES_PER_FRAME; + cycles_counter += cpu_cycles_passed as u32 + interrupt_cycles as u32; } } diff --git a/lib_gb/src/mmu/io_components.rs b/lib_gb/src/mmu/io_components.rs index 45f6b24a..f7cb010d 100644 --- a/lib_gb/src/mmu/io_components.rs +++ b/lib_gb/src/mmu/io_components.rs @@ -1,5 +1,8 @@ -use crate::{apu::{audio_device::AudioDevice, gb_apu::GbApu, set_nr11, set_nr12, set_nr13}, ppu::{fifo::fifo_ppu::FifoPpu, fifo::fifo_register_updater::*, gfx_device::GfxDevice}, timer::timer_register_updater::*, utils::memory_registers::*}; -use crate::apu::*; +use crate::{apu::{*,audio_device::AudioDevice, gb_apu::GbApu}, + ppu::{fifo::fifo_ppu::FifoPpu, fifo::fifo_register_updater::*, gfx_device::GfxDevice}, + timer::timer_register_updater::*, + utils::memory_registers::* +}; use crate::timer::gb_timer::GbTimer; use super::{access_bus::AccessBus, memory::*, oam_dma_transfer::OamDmaTransfer, ram::Ram}; use super::io_ports::*; diff --git a/lib_gb/src/ppu/fifo/bg_fetcher.rs b/lib_gb/src/ppu/fifo/bg_fetcher.rs index 6161b044..01113385 100644 --- a/lib_gb/src/ppu/fifo/bg_fetcher.rs +++ b/lib_gb/src/ppu/fifo/bg_fetcher.rs @@ -1,5 +1,4 @@ use crate::{mmu::vram::VRam, utils::{bit_masks::*, vec2::Vec2}}; - use super::fetching_state::FethcingState; pub struct BGFetcher{ diff --git a/lib_gb/src/ppu/fifo/fifo_ppu.rs b/lib_gb/src/ppu/fifo/fifo_ppu.rs index 148a0dbe..759bf826 100644 --- a/lib_gb/src/ppu/fifo/fifo_ppu.rs +++ b/lib_gb/src/ppu/fifo/fifo_ppu.rs @@ -84,9 +84,8 @@ impl FifoPpu{ pub fn turn_off(&mut self){ self.screen_buffer_index = 0; self.t_cycles_passed = 0; - unsafe{ - std::ptr::write_bytes(self.screen_buffer.as_mut_ptr(), 0xFF, self.screen_buffer.len()); - } + //This is an expensive operation! + unsafe{std::ptr::write_bytes(self.screen_buffer.as_mut_ptr(), 0xFF, self.screen_buffer.len())}; self.gfx_device.swap_buffer(&self.screen_buffer); self.state = PpuState::Hblank; self.ly_register = 0; diff --git a/lib_gb/src/ppu/fifo/sprite_fetcher.rs b/lib_gb/src/ppu/fifo/sprite_fetcher.rs index 517a37f9..9b720c5a 100644 --- a/lib_gb/src/ppu/fifo/sprite_fetcher.rs +++ b/lib_gb/src/ppu/fifo/sprite_fetcher.rs @@ -18,9 +18,7 @@ pub struct SpriteFetcher{ impl SpriteFetcher{ pub fn new()->Self{ let oam_entries = { - let mut data: [MaybeUninit; 10] = unsafe{ - MaybeUninit::uninit().assume_init() - }; + let mut data: [MaybeUninit; 10] = unsafe{MaybeUninit::uninit().assume_init()}; for elem in &mut data[..]{ *elem = MaybeUninit::new(SpriteAttribute::new(0, 0, 0, 0)); From 15b1b6cad31ef13a9ca0f6dd69a15103b643614d Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 4 Sep 2021 13:35:49 +0300 Subject: [PATCH 065/136] Refactor the fetchers Now using a better state machine mechanism. --- Cargo.lock | 338 +++++++++++++++++++ Cargo.toml | 5 + lib_gb/src/ppu/fifo/bg_fetcher.rs | 48 ++- lib_gb/src/ppu/fifo/fetcher_state_machine.rs | 30 ++ lib_gb/src/ppu/fifo/fetching_state.rs | 17 +- lib_gb/src/ppu/fifo/fifo_ppu.rs | 31 +- lib_gb/src/ppu/fifo/mod.rs | 1 + lib_gb/src/ppu/fifo/sprite_fetcher.rs | 145 ++++---- 8 files changed, 493 insertions(+), 122 deletions(-) create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 lib_gb/src/ppu/fifo/fetcher_state_machine.rs diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 00000000..288d6d7b --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,338 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cc" +version = "1.0.70" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26a6ce4b6a484fa3edb70f7efa6fc430fd2b87285fe8b84304fd0936faa0dc0" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "time", + "winapi", +] + +[[package]] +name = "cmake" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb6210b637171dfba4cda12e579ac6dc73f5165ad56133e5d72ef3131f320855" +dependencies = [ + "cc", +] + +[[package]] +name = "crc32fast" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81156fece84ab6a9f2afdb109ce3ae577e42b1228441eded99bd77f627953b1a" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "encoding_rs" +version = "0.8.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80df024fbc5ac80f87dfef0d9f5209a252f2a497f7f42944cff24d8253cac065" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "fern" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9a4820f0ccc8a7afd67c39a0f1a0f4b07ca1725164271a64939d7aeb9af065" +dependencies = [ + "log", +] + +[[package]] +name = "filetime" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "975ccf83d8d9d0d84682850a38c8169027be83368805971cc4f238c2b245bc98" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall", + "winapi", +] + +[[package]] +name = "flate2" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80edafed416a46fb378521624fab1cfa2eb514784fd8921adbe8a8d8321da811" +dependencies = [ + "cfg-if 1.0.0", + "crc32fast", + "libc", + "miniz_oxide", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lib_gb" +version = "1.0.0" +dependencies = [ + "log", +] + +[[package]] +name = "libc" +version = "0.2.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cb00336871be5ed2c8ed44b60ae9959dc5b9f08539422ed43f09e34ecaeba21" + +[[package]] +name = "log" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "magenboy" +version = "1.0.0" +dependencies = [ + "chrono", + "fern", + "lib_gb", + "log", + "sdl2", + "wav", +] + +[[package]] +name = "memchr" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" + +[[package]] +name = "miniz_oxide" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b" +dependencies = [ + "adler", + "autocfg", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "redox_syscall" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8383f39639269cde97d255a32bdb68c047337295414940c68bdd30c2e13203ff" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.6.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" + +[[package]] +name = "riff" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9b1a3d5f46d53f4a3478e2be4a5a5ce5108ea58b100dcd139830eae7f79a3a1" + +[[package]] +name = "sdl2" +version = "0.34.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deecbc3fa9460acff5a1e563e05cb5f31bba0aa0c214bb49a43db8159176d54b" +dependencies = [ + "bitflags", + "lazy_static", + "libc", + "sdl2-sys", +] + +[[package]] +name = "sdl2-sys" +version = "0.34.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a29aa21f175b5a41a6e26da572d5e5d1ee5660d35f9f9d0913e8a802098f74" +dependencies = [ + "cfg-if 0.1.10", + "cmake", + "flate2", + "libc", + "tar", + "unidiff", + "version-compare", +] + +[[package]] +name = "tar" +version = "0.4.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f5515d3add52e0bbdcad7b83c388bb36ba7b754dda3b5f5bc2d38640cdba5c" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "time" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db9e6914ab8b1ae1c260a4ae7a49b6c5611b40328a735b21862567685e73255" +dependencies = [ + "libc", + "wasi", + "winapi", +] + +[[package]] +name = "unidiff" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a62719acf1933bfdbeb73a657ecd9ecece70b405125267dd549e2e2edc232c" +dependencies = [ + "encoding_rs", + "lazy_static", + "regex", +] + +[[package]] +name = "version-compare" +version = "0.0.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d63556a25bae6ea31b52e640d7c41d1ab27faba4ccb600013837a3d0b3994ca1" + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wav" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "549df97e073e1f901a489f159eb451bbe9c790e2f217394f4ce01acda0380b0c" +dependencies = [ + "riff", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "xattr" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" +dependencies = [ + "libc", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 00000000..5c90c705 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = [ + "gb", + "lib_gb" +] \ No newline at end of file diff --git a/lib_gb/src/ppu/fifo/bg_fetcher.rs b/lib_gb/src/ppu/fifo/bg_fetcher.rs index 01113385..4b2e4a87 100644 --- a/lib_gb/src/ppu/fifo/bg_fetcher.rs +++ b/lib_gb/src/ppu/fifo/bg_fetcher.rs @@ -1,5 +1,5 @@ use crate::{mmu::vram::VRam, utils::{bit_masks::*, vec2::Vec2}}; -use super::fetching_state::FethcingState; +use super::{fetcher_state_machine::FetcherStateMachine, fetching_state::*}; pub struct BGFetcher{ pub fifo:Vec, @@ -8,35 +8,32 @@ pub struct BGFetcher{ current_x_pos:u8, rendered_window:bool, rendering_window:bool, - current_fetching_state:FethcingState, - t_cycles_counter:u8, + fetcher_state_machine:FetcherStateMachine, } impl BGFetcher{ pub fn new()->Self{ + let state_machine = [FetchingState::Sleep, FetchingState::FetchTileNumber, FetchingState::Sleep, FetchingState::FetchLowTile, FetchingState::Sleep, FetchingState::FetchHighTile, FetchingState::Sleep, FetchingState::Push]; BGFetcher{ - current_fetching_state:FethcingState::TileNumber, + fetcher_state_machine:FetcherStateMachine::new(state_machine), current_x_pos:0, fifo:Vec::::with_capacity(8), window_line_counter:0, rendered_window:false, rendering_window:false, - t_cycles_counter:0 } } pub fn reset(&mut self){ self.fifo.clear(); self.current_x_pos = 0; - self.current_fetching_state = FethcingState::TileNumber; + self.fetcher_state_machine.reset(); self.rendered_window = false; self.rendering_window = false; - self.t_cycles_counter = 0; } pub fn pause(&mut self){ - self.current_fetching_state = FethcingState::TileNumber; - self.t_cycles_counter = 0; + self.fetcher_state_machine.reset(); } pub fn try_increment_window_counter(&mut self){ @@ -46,11 +43,6 @@ impl BGFetcher{ } pub fn fetch_pixels(&mut self, vram:&VRam, lcd_control:u8, ly_register:u8, window_pos:&Vec2, bg_pos:&Vec2){ - if self.t_cycles_counter % 2 == 0{ - self.t_cycles_counter = (self.t_cycles_counter + 1) % 8; - return; - } - let last_rendering_status = self.rendering_window; self.rendering_window = self.is_redering_wnd(lcd_control, window_pos, ly_register); if self.rendering_window{ @@ -59,12 +51,12 @@ impl BGFetcher{ // In case I was rendering a background pixel need to reset the state of the fectcher // (and maybe clear the fifo but right now Im not doing it since im not sure what about the current_x_pos var) if !last_rendering_status{ - self.current_fetching_state = FethcingState::TileNumber; + self.fetcher_state_machine.reset(); } } - match self.current_fetching_state{ - FethcingState::TileNumber=>{ + match self.fetcher_state_machine.current_state(){ + FetchingState::FetchTileNumber=>{ let tile_num = if self.rendering_window{ let tile_map_address:u16 = if (lcd_control & BIT_6_MASK) == 0 {0x1800} else {0x1C00}; vram.read_current_bank(tile_map_address + (32 * (self.window_line_counter as u16 / 8)) + ((self.current_x_pos - window_pos.x) as u16 / 8)) @@ -77,21 +69,24 @@ impl BGFetcher{ vram.read_current_bank(tile_map_address + ((32 * scy_offset) + scx_offset)) }; - self.current_fetching_state = FethcingState::LowTileData(tile_num); + self.fetcher_state_machine.data = FetchingStateData{low_tile_data:tile_num}; } - FethcingState::LowTileData(tile_num)=>{ + FetchingState::FetchLowTile=>{ + let tile_num = unsafe{self.fetcher_state_machine.data.low_tile_data}; let address = self.get_tila_data_address(lcd_control, bg_pos, ly_register, tile_num); let low_data = vram.read_current_bank(address); - self.current_fetching_state = FethcingState::HighTileData(tile_num, low_data); + self.fetcher_state_machine.data = FetchingStateData{high_tile_data:(tile_num, low_data)}; } - FethcingState::HighTileData(tile_num, low_data)=>{ + FetchingState::FetchHighTile=>{ + let (tile_num, low_data) = unsafe{self.fetcher_state_machine.data.high_tile_data}; let address = self.get_tila_data_address(lcd_control, bg_pos, ly_register, tile_num); let high_data = vram.read_current_bank(address + 1); - self.current_fetching_state = FethcingState::Push(low_data, high_data); + self.fetcher_state_machine.data = FetchingStateData{push_data:(low_data, high_data)}; } - FethcingState::Push(low_data, high_data)=>{ + FetchingState::Push=>{ + let (low_data, high_data) = unsafe{self.fetcher_state_machine.data.push_data}; if self.fifo.is_empty(){ if lcd_control & BIT_0_MASK == 0{ for _ in 0..8{ @@ -108,13 +103,12 @@ impl BGFetcher{ self.current_x_pos += 1; } } - - self.current_fetching_state = FethcingState::TileNumber; } } + FetchingState::Sleep=>{} } - - self.t_cycles_counter = (self.t_cycles_counter + 1) % 8; + + self.fetcher_state_machine.advance(); } fn get_tila_data_address(&self, lcd_control:u8, bg_pos:&Vec2, ly_register:u8, tile_num:u8)->u16{ diff --git a/lib_gb/src/ppu/fifo/fetcher_state_machine.rs b/lib_gb/src/ppu/fifo/fetcher_state_machine.rs new file mode 100644 index 00000000..e09f02d4 --- /dev/null +++ b/lib_gb/src/ppu/fifo/fetcher_state_machine.rs @@ -0,0 +1,30 @@ +use super::fetching_state::*; + +pub struct FetcherStateMachine{ + pub state:usize, + pub data:FetchingStateData, + pub state_machine:[FetchingState;8] +} + +impl FetcherStateMachine{ + pub fn advance(&mut self){ + self.state = (self.state + 1) % 8; + } + + pub fn new(state_machine:[FetchingState;8])->Self{ + Self{ + data:FetchingStateData{low_tile_data:0}, + state:0, + state_machine + } + } + + pub fn reset(&mut self){ + self.state = 0; + self.data.low_tile_data = 0; + } + + pub fn current_state(&self)->&FetchingState{ + &self.state_machine[self.state] + } +} \ No newline at end of file diff --git a/lib_gb/src/ppu/fifo/fetching_state.rs b/lib_gb/src/ppu/fifo/fetching_state.rs index 086456a9..bd2cf339 100644 --- a/lib_gb/src/ppu/fifo/fetching_state.rs +++ b/lib_gb/src/ppu/fifo/fetching_state.rs @@ -1,6 +1,13 @@ -pub enum FethcingState{ - TileNumber, - LowTileData(u8), - HighTileData(u8,u8), - Push(u8,u8) +pub enum FetchingState{ + FetchTileNumber, + FetchLowTile, + FetchHighTile, + Push, + Sleep +} + +pub union FetchingStateData{ + pub low_tile_data:u8, + pub high_tile_data:(u8,u8), + pub push_data:(u8,u8) } \ No newline at end of file diff --git a/lib_gb/src/ppu/fifo/fifo_ppu.rs b/lib_gb/src/ppu/fifo/fifo_ppu.rs index 759bf826..c1bc9452 100644 --- a/lib_gb/src/ppu/fifo/fifo_ppu.rs +++ b/lib_gb/src/ppu/fifo/fifo_ppu.rs @@ -223,27 +223,24 @@ impl FifoPpu{ } else{ self.bg_fetcher.fetch_pixels(&self.vram, self.lcd_control, self.ly_register, &self.window_pos, &self.bg_pos); - self.try_push_to_lcd(); - if self.pixel_x_pos == 160 { - self.state = PpuState::Hblank; - if self.h_blank_interrupt_request{ - self.trigger_stat_interrupt = true; - } - self.bg_fetcher.try_increment_window_counter(); - self.bg_fetcher.reset(); - self.sprite_fetcher.reset(); - - // If im on the first iteration and finished the 160 pixels break; - // In this case the number of t_cycles should be eneven but it will break - // my code way too much for now so Im leaving this as it is... (maybe in the future) - break; + self.try_push_to_lcd(); + if self.pixel_x_pos == 160 { + self.state = PpuState::Hblank; + if self.h_blank_interrupt_request{ + self.trigger_stat_interrupt = true; } + self.bg_fetcher.try_increment_window_counter(); + self.bg_fetcher.reset(); + self.sprite_fetcher.reset(); + + // If im on the first iteration and finished the 160 pixels break; + // In this case the number of t_cycles should be eneven but it will break + // my code way too much for now so Im leaving this as it is... (maybe in the future) + break; + } } } } - - - self.t_cycles_passed += 2; } } diff --git a/lib_gb/src/ppu/fifo/mod.rs b/lib_gb/src/ppu/fifo/mod.rs index 948958b3..e8e06684 100644 --- a/lib_gb/src/ppu/fifo/mod.rs +++ b/lib_gb/src/ppu/fifo/mod.rs @@ -2,4 +2,5 @@ pub mod fifo_ppu; pub mod fifo_register_updater; mod bg_fetcher; mod fetching_state; +mod fetcher_state_machine; mod sprite_fetcher; \ No newline at end of file diff --git a/lib_gb/src/ppu/fifo/sprite_fetcher.rs b/lib_gb/src/ppu/fifo/sprite_fetcher.rs index 9b720c5a..9bb90cd3 100644 --- a/lib_gb/src/ppu/fifo/sprite_fetcher.rs +++ b/lib_gb/src/ppu/fifo/sprite_fetcher.rs @@ -1,8 +1,6 @@ use std::mem::{self, MaybeUninit}; - use crate::{mmu::vram::VRam, ppu::sprite_attribute::SpriteAttribute, utils::bit_masks::{BIT_0_MASK, BIT_2_MASK}}; - -use super::fetching_state::FethcingState; +use super::{fetcher_state_machine::FetcherStateMachine, fetching_state::*}; pub struct SpriteFetcher{ pub fifo:Vec<(u8, u8)>, @@ -10,9 +8,8 @@ pub struct SpriteFetcher{ pub oam_entries_len:u8, pub rendering:bool, - current_fetching_state:FethcingState, + fetcher_state_machine:FetcherStateMachine, current_oam_entry:u8, - t_cycle_inner_state_counter:u8, } impl SpriteFetcher{ @@ -27,22 +24,22 @@ impl SpriteFetcher{ unsafe{mem::transmute::<_, [SpriteAttribute;10]>(data)} }; + let state_machine:[FetchingState;8] = [FetchingState::FetchTileNumber, FetchingState::FetchTileNumber, FetchingState::Sleep, FetchingState::FetchLowTile, FetchingState::Sleep, FetchingState::FetchHighTile, FetchingState::Sleep, FetchingState::Push]; + SpriteFetcher{ - current_fetching_state:FethcingState::TileNumber, + fetcher_state_machine:FetcherStateMachine::new(state_machine), current_oam_entry:0, oam_entries_len:0, oam_entries, fifo:Vec::<(u8,u8)>::with_capacity(8), rendering:false, - t_cycle_inner_state_counter:0, } } pub fn reset(&mut self){ self.current_oam_entry = 0; self.oam_entries_len = 0; - self.current_fetching_state = FethcingState::TileNumber; - self.t_cycle_inner_state_counter = 0; + self.fetcher_state_machine.reset(); self.fifo.clear(); self.rendering = false; } @@ -50,81 +47,83 @@ impl SpriteFetcher{ pub fn fetch_pixels(&mut self, vram:&VRam, lcd_control:u8, ly_register:u8, current_x_pos:u8){ let sprite_size = if lcd_control & BIT_2_MASK == 0 {8} else{16}; - match self.current_fetching_state{ - FethcingState::TileNumber=>{ - if self.oam_entries_len > self.current_oam_entry{ - let oam_entry = &self.oam_entries[self.current_oam_entry as usize]; - if oam_entry.x <= current_x_pos + 8 && current_x_pos < oam_entry.x{ - let mut tile_number = oam_entry.tile_number; - if lcd_control & BIT_2_MASK != 0{ - tile_number &= !BIT_0_MASK - } - self.current_fetching_state = FethcingState::LowTileData(tile_number); - self.rendering = true; - return; - } - } - // Reach here if not rendering this time a sprite - self.rendering = false; + match self.fetcher_state_machine.current_state(){ + FetchingState::FetchTileNumber=>{ + self.try_fetch_tile_number(current_x_pos, lcd_control); } - FethcingState::LowTileData(tile_num)=>{ - if self.t_cycle_inner_state_counter % 2 != 0{ - let oam_attribute = &self.oam_entries[self.current_oam_entry as usize]; - let current_tile_data_address = Self::get_current_tile_data_address(ly_register, oam_attribute, sprite_size, tile_num); - let low_data = vram.read_current_bank(current_tile_data_address); - self.current_fetching_state = FethcingState::HighTileData(tile_num, low_data); - } + FetchingState::FetchLowTile=>{ + let tile_num = unsafe{self.fetcher_state_machine.data.low_tile_data}; + let oam_attribute = &self.oam_entries[self.current_oam_entry as usize]; + let current_tile_data_address = Self::get_current_tile_data_address(ly_register, oam_attribute, sprite_size, tile_num); + let low_data = vram.read_current_bank(current_tile_data_address); + self.fetcher_state_machine.data = FetchingStateData{high_tile_data:(tile_num, low_data)}; + self.fetcher_state_machine.advance(); } - FethcingState::HighTileData(tile_num, low_data)=>{ - if self.t_cycle_inner_state_counter % 2 != 0{ - let oam_attribute = &self.oam_entries[self.current_oam_entry as usize]; - let current_tile_data_address = Self::get_current_tile_data_address(ly_register, oam_attribute, sprite_size, tile_num); - let high_data = vram.read_current_bank(current_tile_data_address + 1); - self.current_fetching_state = FethcingState::Push(low_data, high_data); - } + FetchingState::FetchHighTile=>{ + let (tile_num, low_data) = unsafe{self.fetcher_state_machine.data.high_tile_data}; + let oam_attribute = &self.oam_entries[self.current_oam_entry as usize]; + let current_tile_data_address = Self::get_current_tile_data_address(ly_register, oam_attribute, sprite_size, tile_num); + let high_data = vram.read_current_bank(current_tile_data_address + 1); + self.fetcher_state_machine.data = FetchingStateData{push_data:(low_data, high_data)}; + self.fetcher_state_machine.advance(); } - FethcingState::Push(low_data, high_data)=>{ - if self.t_cycle_inner_state_counter % 2 != 0{ - - let oam_attribute = &self.oam_entries[self.current_oam_entry as usize]; - let start_x = self.fifo.len(); - - let skip_x = 8 - (oam_attribute.x - current_x_pos) as usize; - - if oam_attribute.flip_x{ - for i in (0 + skip_x)..8{ - let mask = 1 << i; - let mut pixel = (low_data & mask) >> i; - pixel |= ((high_data & mask) >> i) << 1; - if i + skip_x >= start_x { - self.fifo.push((pixel, self.current_oam_entry)); - } - else if self.fifo[i + skip_x].0 == 0{ - self.fifo[i+ skip_x] = (pixel, self.current_oam_entry); - } + FetchingState::Push=>{ + let (low_data, high_data) = unsafe{self.fetcher_state_machine.data.push_data}; + let oam_attribute = &self.oam_entries[self.current_oam_entry as usize]; + let start_x = self.fifo.len(); + + let skip_x = 8 - (oam_attribute.x - current_x_pos) as usize; + + if oam_attribute.flip_x{ + for i in (0 + skip_x)..8{ + let mask = 1 << i; + let mut pixel = (low_data & mask) >> i; + pixel |= ((high_data & mask) >> i) << 1; + if i + skip_x >= start_x { + self.fifo.push((pixel, self.current_oam_entry)); + } + else if self.fifo[i + skip_x].0 == 0{ + self.fifo[i+ skip_x] = (pixel, self.current_oam_entry); } } - else{ - for i in (0..(8 - skip_x)).rev(){ - let mask = 1 << i; - let mut pixel = (low_data & mask) >> i; - pixel |= ((high_data & mask) >> i) << 1; - if 7 - skip_x - i >= start_x { - self.fifo.push((pixel, self.current_oam_entry)); - } - else if self.fifo[7 - skip_x - i].0 == 0{ - self.fifo[7 - skip_x - i] = (pixel, self.current_oam_entry); - } + } + else{ + for i in (0..(8 - skip_x)).rev(){ + let mask = 1 << i; + let mut pixel = (low_data & mask) >> i; + pixel |= ((high_data & mask) >> i) << 1; + if 7 - skip_x - i >= start_x { + self.fifo.push((pixel, self.current_oam_entry)); + } + else if self.fifo[7 - skip_x - i].0 == 0{ + self.fifo[7 - skip_x - i] = (pixel, self.current_oam_entry); } } - - self.current_fetching_state = FethcingState::TileNumber; - self.current_oam_entry += 1; } + + self.current_oam_entry += 1; + self.fetcher_state_machine.advance(); } + FetchingState::Sleep=>self.fetcher_state_machine.advance() } + } - self.t_cycle_inner_state_counter = (self.t_cycle_inner_state_counter + 1) % 8; + //This is a function on order to abort if rendering + fn try_fetch_tile_number(&mut self, current_x_pos: u8, lcd_control: u8) { + if self.oam_entries_len > self.current_oam_entry{ + let oam_entry = &self.oam_entries[self.current_oam_entry as usize]; + if oam_entry.x <= current_x_pos + 8 && current_x_pos < oam_entry.x{ + let mut tile_number = oam_entry.tile_number; + if lcd_control & BIT_2_MASK != 0{ + tile_number &= !BIT_0_MASK + } + self.rendering = true; + self.fetcher_state_machine.data = FetchingStateData{low_tile_data:tile_number}; + self.fetcher_state_machine.advance(); + return; + } + } + self.rendering = false; } // Receiving the tile_num since in case of extended sprite this could change (the first bit is reset) From 0b3b51d9f2612d38724a79753a324ef06fc83ec8 Mon Sep 17 00:00:00 2001 From: alloncm Date: Mon, 6 Sep 2021 13:56:19 +0300 Subject: [PATCH 066/136] Arrange the fifo code SCR - Arrange code and extract some to functions, fix typos and made some fields private --- lib_gb/src/ppu/fifo/bg_fetcher.rs | 4 +- lib_gb/src/ppu/fifo/fetcher_state_machine.rs | 4 +- lib_gb/src/ppu/fifo/fifo_ppu.rs | 50 +++++++++----------- 3 files changed, 27 insertions(+), 31 deletions(-) diff --git a/lib_gb/src/ppu/fifo/bg_fetcher.rs b/lib_gb/src/ppu/fifo/bg_fetcher.rs index 4b2e4a87..08ac75b0 100644 --- a/lib_gb/src/ppu/fifo/bg_fetcher.rs +++ b/lib_gb/src/ppu/fifo/bg_fetcher.rs @@ -44,7 +44,7 @@ impl BGFetcher{ pub fn fetch_pixels(&mut self, vram:&VRam, lcd_control:u8, ly_register:u8, window_pos:&Vec2, bg_pos:&Vec2){ let last_rendering_status = self.rendering_window; - self.rendering_window = self.is_redering_wnd(lcd_control, window_pos, ly_register); + self.rendering_window = self.is_rendering_wnd(lcd_control, window_pos, ly_register); if self.rendering_window{ self.rendered_window = true; @@ -121,7 +121,7 @@ impl BGFetcher{ }; } - fn is_redering_wnd(&self, lcd_control:u8, window_pos:&Vec2, ly_register:u8)->bool{ + fn is_rendering_wnd(&self, lcd_control:u8, window_pos:&Vec2, ly_register:u8)->bool{ window_pos.x <= self.current_x_pos && window_pos.y <= ly_register && (lcd_control & BIT_5_MASK) != 0 } } \ No newline at end of file diff --git a/lib_gb/src/ppu/fifo/fetcher_state_machine.rs b/lib_gb/src/ppu/fifo/fetcher_state_machine.rs index e09f02d4..eda063ca 100644 --- a/lib_gb/src/ppu/fifo/fetcher_state_machine.rs +++ b/lib_gb/src/ppu/fifo/fetcher_state_machine.rs @@ -1,9 +1,9 @@ use super::fetching_state::*; pub struct FetcherStateMachine{ - pub state:usize, pub data:FetchingStateData, - pub state_machine:[FetchingState;8] + state:usize, + state_machine:[FetchingState;8] } impl FetcherStateMachine{ diff --git a/lib_gb/src/ppu/fifo/fifo_ppu.rs b/lib_gb/src/ppu/fifo/fifo_ppu.rs index c1bc9452..9f4edfea 100644 --- a/lib_gb/src/ppu/fifo/fifo_ppu.rs +++ b/lib_gb/src/ppu/fifo/fifo_ppu.rs @@ -10,11 +10,9 @@ use super::sprite_fetcher::SpriteFetcher; pub struct FifoPpu{ - gfx_device: GFX, pub vram: VRam, pub oam:[u8;0xA0], - t_cycles_passed:u16, pub state:PpuState, pub lcd_control:u8, pub stat_register:u8, @@ -26,18 +24,19 @@ pub struct FifoPpu{ pub obj_color_mapping0: [Option;4], pub obj_color_mapping1: [Option;4], - screen_buffer: [u32; 160*144], - push_lcd_buffer:Vec, - screen_buffer_index:usize, - pixel_x_pos:u8, - scanline_started:bool, - //interrupts pub v_blank_interrupt_request:bool, pub h_blank_interrupt_request:bool, pub oam_search_interrupt_request:bool, pub coincidence_interrupt_request:bool, + gfx_device: GFX, + t_cycles_passed:u16, + screen_buffer: [u32; 160*144], + push_lcd_buffer:Vec, + screen_buffer_index:usize, + pixel_x_pos:u8, + scanline_started:bool, bg_fetcher:BGFetcher, sprite_fetcher:SpriteFetcher, stat_triggered:bool, @@ -45,10 +44,7 @@ pub struct FifoPpu{ } impl FifoPpu{ - pub fn new(device:GFX) -> Self { - - Self{ gfx_device: device, vram: VRam::default(), @@ -107,10 +103,23 @@ impl FifoPpu{ self.cycle_fetcher(m_cycles, if_register); - //update stat register - self.stat_register &= 0b1111_1100; //clear first 2 bits - self.stat_register |= self.state as u8; + self.update_stat_register(if_register); + for pixel in self.push_lcd_buffer.iter(){ + self.screen_buffer[self.screen_buffer_index] = Self::color_as_uint(&pixel); + self.screen_buffer_index += 1; + if self.screen_buffer_index == self.screen_buffer.len(){ + self.gfx_device.swap_buffer(&self.screen_buffer); + self.screen_buffer_index = 0; + } + } + + self.push_lcd_buffer.clear(); + } + + fn update_stat_register(&mut self, if_register: &mut u8) { + self.stat_register &= 0b1111_1100; + self.stat_register |= self.state as u8; if self.ly_register == self.lyc_register{ if self.coincidence_interrupt_request { self.trigger_stat_interrupt = true; @@ -120,7 +129,6 @@ impl FifoPpu{ else{ self.stat_register &= !BIT_2_MASK; } - if self.trigger_stat_interrupt{ if !self.stat_triggered{ *if_register |= BIT_1_MASK; @@ -130,19 +138,7 @@ impl FifoPpu{ else{ self.stat_triggered = false; } - self.trigger_stat_interrupt = false; - - for pixel in self.push_lcd_buffer.iter(){ - self.screen_buffer[self.screen_buffer_index] = Self::color_as_uint(&pixel); - self.screen_buffer_index += 1; - if self.screen_buffer_index == self.screen_buffer.len(){ - self.gfx_device.swap_buffer(&self.screen_buffer); - self.screen_buffer_index = 0; - } - } - - self.push_lcd_buffer.clear(); } fn cycle_fetcher(&mut self, m_cycles:u8, if_register:&mut u8){ From fdc26242500039a8c3ad1c9775b30cabbb3399ea Mon Sep 17 00:00:00 2001 From: alloncm Date: Mon, 6 Sep 2021 15:25:12 +0300 Subject: [PATCH 067/136] Arrange and rename Change BGFetcher to BackgroundFetcher Move the to color code into the color mod --- lib_gb/src/ppu/color.rs | 6 ++++++ .../fifo/{bg_fetcher.rs => background_fetcher.rs} | 6 +++--- lib_gb/src/ppu/fifo/fifo_ppu.rs | 14 +++++--------- lib_gb/src/ppu/fifo/mod.rs | 2 +- 4 files changed, 15 insertions(+), 13 deletions(-) rename lib_gb/src/ppu/fifo/{bg_fetcher.rs => background_fetcher.rs} (98%) diff --git a/lib_gb/src/ppu/color.rs b/lib_gb/src/ppu/color.rs index a0806109..26c8fb8c 100644 --- a/lib_gb/src/ppu/color.rs +++ b/lib_gb/src/ppu/color.rs @@ -33,3 +33,9 @@ impl PartialEq for Color{ self.r == color.r } } + +impl From for u32{ + fn from(color: Color) -> Self { + ((color.r as u32) << 16) | ((color.g as u32) << 8) | (color.b as u32) + } +} \ No newline at end of file diff --git a/lib_gb/src/ppu/fifo/bg_fetcher.rs b/lib_gb/src/ppu/fifo/background_fetcher.rs similarity index 98% rename from lib_gb/src/ppu/fifo/bg_fetcher.rs rename to lib_gb/src/ppu/fifo/background_fetcher.rs index 08ac75b0..62bdb2e9 100644 --- a/lib_gb/src/ppu/fifo/bg_fetcher.rs +++ b/lib_gb/src/ppu/fifo/background_fetcher.rs @@ -1,7 +1,7 @@ use crate::{mmu::vram::VRam, utils::{bit_masks::*, vec2::Vec2}}; use super::{fetcher_state_machine::FetcherStateMachine, fetching_state::*}; -pub struct BGFetcher{ +pub struct BackgroundFetcher{ pub fifo:Vec, pub window_line_counter:u8, @@ -11,10 +11,10 @@ pub struct BGFetcher{ fetcher_state_machine:FetcherStateMachine, } -impl BGFetcher{ +impl BackgroundFetcher{ pub fn new()->Self{ let state_machine = [FetchingState::Sleep, FetchingState::FetchTileNumber, FetchingState::Sleep, FetchingState::FetchLowTile, FetchingState::Sleep, FetchingState::FetchHighTile, FetchingState::Sleep, FetchingState::Push]; - BGFetcher{ + BackgroundFetcher{ fetcher_state_machine:FetcherStateMachine::new(state_machine), current_x_pos:0, fifo:Vec::::with_capacity(8), diff --git a/lib_gb/src/ppu/fifo/fifo_ppu.rs b/lib_gb/src/ppu/fifo/fifo_ppu.rs index 9f4edfea..eeae2ddb 100644 --- a/lib_gb/src/ppu/fifo/fifo_ppu.rs +++ b/lib_gb/src/ppu/fifo/fifo_ppu.rs @@ -1,11 +1,11 @@ use crate::utils::{vec2::Vec2, bit_masks::*}; use crate::mmu::vram::VRam; -use crate::ppu::color::Color; +use crate::ppu::color::*; use crate::ppu::colors::*; use crate::ppu::gfx_device::GfxDevice; use crate::ppu::{ppu_state::PpuState, sprite_attribute::SpriteAttribute}; -use super::bg_fetcher::BGFetcher; +use super::background_fetcher::BackgroundFetcher; use super::sprite_fetcher::SpriteFetcher; @@ -37,7 +37,7 @@ pub struct FifoPpu{ screen_buffer_index:usize, pixel_x_pos:u8, scanline_started:bool, - bg_fetcher:BGFetcher, + bg_fetcher:BackgroundFetcher, sprite_fetcher:SpriteFetcher, stat_triggered:bool, trigger_stat_interrupt:bool, @@ -69,7 +69,7 @@ impl FifoPpu{ t_cycles_passed:0, stat_triggered:false, trigger_stat_interrupt:false, - bg_fetcher:BGFetcher::new(), + bg_fetcher:BackgroundFetcher::new(), sprite_fetcher:SpriteFetcher::new(), push_lcd_buffer:Vec::::new(), pixel_x_pos:0, @@ -106,7 +106,7 @@ impl FifoPpu{ self.update_stat_register(if_register); for pixel in self.push_lcd_buffer.iter(){ - self.screen_buffer[self.screen_buffer_index] = Self::color_as_uint(&pixel); + self.screen_buffer[self.screen_buffer_index] = u32::from(*pixel); self.screen_buffer_index += 1; if self.screen_buffer_index == self.screen_buffer.len(){ self.gfx_device.swap_buffer(&self.screen_buffer); @@ -286,8 +286,4 @@ impl FifoPpu{ self.pixel_x_pos += 1; } } - - fn color_as_uint(color: &Color) -> u32 { - ((color.r as u32) << 16) | ((color.g as u32) << 8) | (color.b as u32) - } } \ No newline at end of file diff --git a/lib_gb/src/ppu/fifo/mod.rs b/lib_gb/src/ppu/fifo/mod.rs index e8e06684..3ed89231 100644 --- a/lib_gb/src/ppu/fifo/mod.rs +++ b/lib_gb/src/ppu/fifo/mod.rs @@ -1,6 +1,6 @@ pub mod fifo_ppu; pub mod fifo_register_updater; -mod bg_fetcher; +mod background_fetcher; mod fetching_state; mod fetcher_state_machine; mod sprite_fetcher; \ No newline at end of file From a3513c158b132bff9d97aa432124cb9d28e591ff Mon Sep 17 00:00:00 2001 From: alloncm Date: Mon, 6 Sep 2021 15:38:47 +0300 Subject: [PATCH 068/136] Change FetchingStateData to a struct from union Made this more safe and less error prone --- lib_gb/src/ppu/fifo/background_fetcher.rs | 14 ++++++++------ lib_gb/src/ppu/fifo/fetcher_state_machine.rs | 4 ++-- lib_gb/src/ppu/fifo/fetching_state.rs | 16 ++++++++++++---- lib_gb/src/ppu/fifo/sprite_fetcher.rs | 14 ++++++++------ 4 files changed, 30 insertions(+), 18 deletions(-) diff --git a/lib_gb/src/ppu/fifo/background_fetcher.rs b/lib_gb/src/ppu/fifo/background_fetcher.rs index 62bdb2e9..d9975777 100644 --- a/lib_gb/src/ppu/fifo/background_fetcher.rs +++ b/lib_gb/src/ppu/fifo/background_fetcher.rs @@ -69,24 +69,26 @@ impl BackgroundFetcher{ vram.read_current_bank(tile_map_address + ((32 * scy_offset) + scx_offset)) }; - self.fetcher_state_machine.data = FetchingStateData{low_tile_data:tile_num}; + self.fetcher_state_machine.data.reset(); + self.fetcher_state_machine.data.tile_data = Some(tile_num); } FetchingState::FetchLowTile=>{ - let tile_num = unsafe{self.fetcher_state_machine.data.low_tile_data}; + let tile_num = self.fetcher_state_machine.data.tile_data.expect("State machine is corrupted, No Tile data on FetchLowTIle"); let address = self.get_tila_data_address(lcd_control, bg_pos, ly_register, tile_num); let low_data = vram.read_current_bank(address); - self.fetcher_state_machine.data = FetchingStateData{high_tile_data:(tile_num, low_data)}; + self.fetcher_state_machine.data.low_tile_data = Some(low_data); } FetchingState::FetchHighTile=>{ - let (tile_num, low_data) = unsafe{self.fetcher_state_machine.data.high_tile_data}; + let tile_num= self.fetcher_state_machine.data.tile_data.expect("State machine is corrupted, No Tile data on FetchHighTIle"); let address = self.get_tila_data_address(lcd_control, bg_pos, ly_register, tile_num); let high_data = vram.read_current_bank(address + 1); - self.fetcher_state_machine.data = FetchingStateData{push_data:(low_data, high_data)}; + self.fetcher_state_machine.data.high_tile_data = Some(high_data); } FetchingState::Push=>{ - let (low_data, high_data) = unsafe{self.fetcher_state_machine.data.push_data}; + let low_data = self.fetcher_state_machine.data.low_tile_data.expect("State machine is corrupted, No Low data on Push"); + let high_data = self.fetcher_state_machine.data.high_tile_data.expect("State machine is corrupted, No High data on Push"); if self.fifo.is_empty(){ if lcd_control & BIT_0_MASK == 0{ for _ in 0..8{ diff --git a/lib_gb/src/ppu/fifo/fetcher_state_machine.rs b/lib_gb/src/ppu/fifo/fetcher_state_machine.rs index eda063ca..e051c30b 100644 --- a/lib_gb/src/ppu/fifo/fetcher_state_machine.rs +++ b/lib_gb/src/ppu/fifo/fetcher_state_machine.rs @@ -13,7 +13,7 @@ impl FetcherStateMachine{ pub fn new(state_machine:[FetchingState;8])->Self{ Self{ - data:FetchingStateData{low_tile_data:0}, + data:FetchingStateData{high_tile_data:None, low_tile_data:None, tile_data:None}, state:0, state_machine } @@ -21,7 +21,7 @@ impl FetcherStateMachine{ pub fn reset(&mut self){ self.state = 0; - self.data.low_tile_data = 0; + self.data.reset(); } pub fn current_state(&self)->&FetchingState{ diff --git a/lib_gb/src/ppu/fifo/fetching_state.rs b/lib_gb/src/ppu/fifo/fetching_state.rs index bd2cf339..68b6c777 100644 --- a/lib_gb/src/ppu/fifo/fetching_state.rs +++ b/lib_gb/src/ppu/fifo/fetching_state.rs @@ -6,8 +6,16 @@ pub enum FetchingState{ Sleep } -pub union FetchingStateData{ - pub low_tile_data:u8, - pub high_tile_data:(u8,u8), - pub push_data:(u8,u8) +pub struct FetchingStateData{ + pub tile_data:Option, + pub low_tile_data:Option, + pub high_tile_data:Option, +} + +impl FetchingStateData{ + pub fn reset(&mut self){ + self.high_tile_data = None; + self.low_tile_data = None; + self.tile_data = None; + } } \ No newline at end of file diff --git a/lib_gb/src/ppu/fifo/sprite_fetcher.rs b/lib_gb/src/ppu/fifo/sprite_fetcher.rs index 9bb90cd3..fa5989da 100644 --- a/lib_gb/src/ppu/fifo/sprite_fetcher.rs +++ b/lib_gb/src/ppu/fifo/sprite_fetcher.rs @@ -52,23 +52,24 @@ impl SpriteFetcher{ self.try_fetch_tile_number(current_x_pos, lcd_control); } FetchingState::FetchLowTile=>{ - let tile_num = unsafe{self.fetcher_state_machine.data.low_tile_data}; + let tile_num = self.fetcher_state_machine.data.tile_data.expect("State machine is corrupted, No Tile data on FetchLowTIle"); let oam_attribute = &self.oam_entries[self.current_oam_entry as usize]; let current_tile_data_address = Self::get_current_tile_data_address(ly_register, oam_attribute, sprite_size, tile_num); let low_data = vram.read_current_bank(current_tile_data_address); - self.fetcher_state_machine.data = FetchingStateData{high_tile_data:(tile_num, low_data)}; + self.fetcher_state_machine.data.low_tile_data = Some(low_data); self.fetcher_state_machine.advance(); } FetchingState::FetchHighTile=>{ - let (tile_num, low_data) = unsafe{self.fetcher_state_machine.data.high_tile_data}; + let tile_num= self.fetcher_state_machine.data.tile_data.expect("State machine is corrupted, No Tile data on FetchHighTIle"); let oam_attribute = &self.oam_entries[self.current_oam_entry as usize]; let current_tile_data_address = Self::get_current_tile_data_address(ly_register, oam_attribute, sprite_size, tile_num); let high_data = vram.read_current_bank(current_tile_data_address + 1); - self.fetcher_state_machine.data = FetchingStateData{push_data:(low_data, high_data)}; + self.fetcher_state_machine.data.high_tile_data = Some(high_data); self.fetcher_state_machine.advance(); } FetchingState::Push=>{ - let (low_data, high_data) = unsafe{self.fetcher_state_machine.data.push_data}; + let low_data = self.fetcher_state_machine.data.low_tile_data.expect("State machine is corrupted, No Low data on Push"); + let high_data = self.fetcher_state_machine.data.high_tile_data.expect("State machine is corrupted, No High data on Push"); let oam_attribute = &self.oam_entries[self.current_oam_entry as usize]; let start_x = self.fifo.len(); @@ -118,7 +119,8 @@ impl SpriteFetcher{ tile_number &= !BIT_0_MASK } self.rendering = true; - self.fetcher_state_machine.data = FetchingStateData{low_tile_data:tile_number}; + self.fetcher_state_machine.data.reset(); + self.fetcher_state_machine.data.tile_data = Some(tile_number); self.fetcher_state_machine.advance(); return; } From 6380c55dc14880342268382ce67f03265be2fc4a Mon Sep 17 00:00:00 2001 From: alloncm Date: Mon, 6 Sep 2021 16:11:23 +0300 Subject: [PATCH 069/136] Remove unused files and modules Replaced the scanline based ppu with the fifo ppu and adpted the rest of the project to it --- lib_gb/src/machine/gameboy.rs | 4 +- lib_gb/src/mmu/io_components.rs | 6 +- lib_gb/src/ppu/extended_sprite.rs | 61 -- lib_gb/src/ppu/fifo/fifo_ppu.rs | 289 -------- lib_gb/src/ppu/fifo/fifo_register_updater.rs | 81 --- lib_gb/src/ppu/fifo/mod.rs | 8 +- lib_gb/src/ppu/gb_ppu.rs | 673 +++++++------------ lib_gb/src/ppu/mod.rs | 5 +- lib_gb/src/ppu/normal_sprite.rs | 67 -- lib_gb/src/ppu/ppu_register_updater.rs | 50 +- lib_gb/src/ppu/sprite.rs | 13 - 11 files changed, 260 insertions(+), 997 deletions(-) delete mode 100644 lib_gb/src/ppu/extended_sprite.rs delete mode 100644 lib_gb/src/ppu/fifo/fifo_ppu.rs delete mode 100644 lib_gb/src/ppu/fifo/fifo_register_updater.rs delete mode 100644 lib_gb/src/ppu/normal_sprite.rs delete mode 100644 lib_gb/src/ppu/sprite.rs diff --git a/lib_gb/src/machine/gameboy.rs b/lib_gb/src/machine/gameboy.rs index e35e97cd..5ed41351 100644 --- a/lib_gb/src/machine/gameboy.rs +++ b/lib_gb/src/machine/gameboy.rs @@ -3,12 +3,14 @@ use crate::{ cpu::gb_cpu::GbCpu, keypad::{joypad::Joypad, joypad_provider::JoypadProvider, joypad_register_updater}, mmu::{carts::mbc::Mbc, gb_mmu::{GbMmu, BOOT_ROM_SIZE}, memory::Memory}, - ppu::{gb_ppu::CYCLES_PER_FRAME, gfx_device::GfxDevice} + ppu::gfx_device::GfxDevice }; use super::interrupts_handler::InterruptsHandler; use std::boxed::Box; use log::debug; +//CPU frequrncy: 4,194,304 / 59.727~ / 4 == 70224 / 4 +pub const CYCLES_PER_FRAME:u32 = 17556; pub struct GameBoy<'a, JP: JoypadProvider, AD:AudioDevice, GFX:GfxDevice> { cpu: GbCpu, diff --git a/lib_gb/src/mmu/io_components.rs b/lib_gb/src/mmu/io_components.rs index f7cb010d..d990aed3 100644 --- a/lib_gb/src/mmu/io_components.rs +++ b/lib_gb/src/mmu/io_components.rs @@ -1,5 +1,5 @@ use crate::{apu::{*,audio_device::AudioDevice, gb_apu::GbApu}, - ppu::{fifo::fifo_ppu::FifoPpu, fifo::fifo_register_updater::*, gfx_device::GfxDevice}, + ppu::{gb_ppu::GbPpu, ppu_register_updater::*, gfx_device::GfxDevice}, timer::timer_register_updater::*, utils::memory_registers::* }; @@ -15,7 +15,7 @@ pub struct IoComponents{ pub ram: Ram, pub apu: GbApu, pub timer: GbTimer, - pub ppu:FifoPpu, + pub ppu:GbPpu, ports:[u8;IO_PORTS_SIZE], pub dma:OamDmaTransfer, pub finished_boot:bool, @@ -162,7 +162,7 @@ impl UnprotectedMemory for IoComponents{ impl IoComponents{ pub fn new(apu:GbApu, gfx_device:GFX)->Self{ - Self{apu, ports:[0;IO_PORTS_SIZE], timer:GbTimer::default(), ppu:FifoPpu::new(gfx_device), dma:OamDmaTransfer::default(),finished_boot:false, ram:Ram::default()} + Self{apu, ports:[0;IO_PORTS_SIZE], timer:GbTimer::default(), ppu:GbPpu::new(gfx_device), dma:OamDmaTransfer::default(),finished_boot:false, ram:Ram::default()} } pub fn cycle(&mut self, cycles:u32){ diff --git a/lib_gb/src/ppu/extended_sprite.rs b/lib_gb/src/ppu/extended_sprite.rs deleted file mode 100644 index 09a92d96..00000000 --- a/lib_gb/src/ppu/extended_sprite.rs +++ /dev/null @@ -1,61 +0,0 @@ -use super::sprite::*; - -pub struct ExtendedSprite{ - pub pixels:[u8;128] -} - -impl ExtendedSprite { - const SIZE:u8 = 16; - - pub fn new() -> ExtendedSprite { - ExtendedSprite { pixels: [0; 128] } - } -} - -impl Clone for ExtendedSprite{ - fn clone(&self)->Self{ - ExtendedSprite{ - pixels:self.pixels - } - } -} - -impl Sprite for ExtendedSprite{ - fn size(&self)->u8 {Self::SIZE} - - fn get_pixel(&self, pos: u8)->u8{ - self.pixels[pos as usize] - } - - fn set_pixel(&mut self, pos: u8, pixel: u8){ - self.pixels[pos as usize] = pixel; - } - - fn flip_x(&mut self){ - let mut fliiped = ExtendedSprite::new(); - - for y in 0..16{ - let line = &self.pixels[y*8 .. (y+1)*8]; - for x in 0..4{ - fliiped.pixels[y*8 + x] = line[7-x]; - fliiped.pixels[y*8 + (7-x)] = line[x]; - } - } - - *self = fliiped; - } - - fn flip_y(&mut self){ - let mut flipped = ExtendedSprite::new(); - for y in 0..8{ - let upper_line = &self.pixels[y*8..(y+1)*8]; - let opposite_index = 15-y; - let lower_line = &self.pixels[opposite_index*8..(opposite_index+1)*8]; - - copy_pixels(&mut flipped,y as u8, lower_line); - copy_pixels(&mut flipped,opposite_index as u8, upper_line); - } - - *self = flipped; - } -} \ No newline at end of file diff --git a/lib_gb/src/ppu/fifo/fifo_ppu.rs b/lib_gb/src/ppu/fifo/fifo_ppu.rs deleted file mode 100644 index eeae2ddb..00000000 --- a/lib_gb/src/ppu/fifo/fifo_ppu.rs +++ /dev/null @@ -1,289 +0,0 @@ -use crate::utils::{vec2::Vec2, bit_masks::*}; -use crate::mmu::vram::VRam; -use crate::ppu::color::*; -use crate::ppu::colors::*; -use crate::ppu::gfx_device::GfxDevice; -use crate::ppu::{ppu_state::PpuState, sprite_attribute::SpriteAttribute}; - -use super::background_fetcher::BackgroundFetcher; -use super::sprite_fetcher::SpriteFetcher; - - -pub struct FifoPpu{ - - pub vram: VRam, - pub oam:[u8;0xA0], - pub state:PpuState, - pub lcd_control:u8, - pub stat_register:u8, - pub lyc_register:u8, - pub ly_register:u8, - pub window_pos:Vec2, - pub bg_pos:Vec2, - pub bg_color_mapping: [Color; 4], - pub obj_color_mapping0: [Option;4], - pub obj_color_mapping1: [Option;4], - - //interrupts - pub v_blank_interrupt_request:bool, - pub h_blank_interrupt_request:bool, - pub oam_search_interrupt_request:bool, - pub coincidence_interrupt_request:bool, - - gfx_device: GFX, - t_cycles_passed:u16, - screen_buffer: [u32; 160*144], - push_lcd_buffer:Vec, - screen_buffer_index:usize, - pixel_x_pos:u8, - scanline_started:bool, - bg_fetcher:BackgroundFetcher, - sprite_fetcher:SpriteFetcher, - stat_triggered:bool, - trigger_stat_interrupt:bool, -} - -impl FifoPpu{ - pub fn new(device:GFX) -> Self { - Self{ - gfx_device: device, - vram: VRam::default(), - oam: [0;0xA0], - stat_register: 0, - lyc_register: 0, - lcd_control: 0, - bg_pos: Vec2::{x:0, y:0}, - window_pos: Vec2::{x:0,y:0}, - screen_buffer:[0;160*144], - bg_color_mapping:[WHITE, LIGHT_GRAY, DARK_GRAY, BLACK], - obj_color_mapping0: [None, Some(LIGHT_GRAY), Some(DARK_GRAY), Some(BLACK)], - obj_color_mapping1: [None, Some(LIGHT_GRAY), Some(DARK_GRAY), Some(BLACK)], - ly_register:0, - state: PpuState::OamSearch, - //interrupts - v_blank_interrupt_request:false, - h_blank_interrupt_request:false, - oam_search_interrupt_request:false, - coincidence_interrupt_request:false, - screen_buffer_index:0, - t_cycles_passed:0, - stat_triggered:false, - trigger_stat_interrupt:false, - bg_fetcher:BackgroundFetcher::new(), - sprite_fetcher:SpriteFetcher::new(), - push_lcd_buffer:Vec::::new(), - pixel_x_pos:0, - scanline_started:false - } - } - - pub fn turn_off(&mut self){ - self.screen_buffer_index = 0; - self.t_cycles_passed = 0; - //This is an expensive operation! - unsafe{std::ptr::write_bytes(self.screen_buffer.as_mut_ptr(), 0xFF, self.screen_buffer.len())}; - self.gfx_device.swap_buffer(&self.screen_buffer); - self.state = PpuState::Hblank; - self.ly_register = 0; - self.stat_triggered = false; - self.trigger_stat_interrupt = false; - self.bg_fetcher.reset(); - self.sprite_fetcher.reset(); - self.pixel_x_pos = 0; - } - - pub fn turn_on(&mut self){ - self.state = PpuState::OamSearch; - } - - pub fn cycle(&mut self, m_cycles:u8, if_register:&mut u8){ - if self.lcd_control & BIT_7_MASK == 0{ - return; - } - - self.cycle_fetcher(m_cycles, if_register); - - self.update_stat_register(if_register); - - for pixel in self.push_lcd_buffer.iter(){ - self.screen_buffer[self.screen_buffer_index] = u32::from(*pixel); - self.screen_buffer_index += 1; - if self.screen_buffer_index == self.screen_buffer.len(){ - self.gfx_device.swap_buffer(&self.screen_buffer); - self.screen_buffer_index = 0; - } - } - - self.push_lcd_buffer.clear(); - } - - fn update_stat_register(&mut self, if_register: &mut u8) { - self.stat_register &= 0b1111_1100; - self.stat_register |= self.state as u8; - if self.ly_register == self.lyc_register{ - if self.coincidence_interrupt_request { - self.trigger_stat_interrupt = true; - } - self.stat_register |= BIT_2_MASK; - } - else{ - self.stat_register &= !BIT_2_MASK; - } - if self.trigger_stat_interrupt{ - if !self.stat_triggered{ - *if_register |= BIT_1_MASK; - self.stat_triggered = true; - } - } - else{ - self.stat_triggered = false; - } - self.trigger_stat_interrupt = false; - } - - fn cycle_fetcher(&mut self, m_cycles:u8, if_register:&mut u8){ - let sprite_height = if (self.lcd_control & BIT_2_MASK) != 0 {16} else {8}; - - for _ in 0..m_cycles * 2{ - match self.state{ - PpuState::OamSearch=>{ - let oam_index = self.t_cycles_passed / 2; - let oam_entry_address = (oam_index * 4) as usize; - let end_y = self.oam[oam_entry_address]; - let end_x = self.oam[oam_entry_address + 1]; - - if end_x > 0 && self.ly_register + 16 >= end_y && self.ly_register + 16 < end_y + sprite_height && self.sprite_fetcher.oam_entries_len < 10{ - let tile_number = self.oam[oam_entry_address + 2]; - let attributes = self.oam[oam_entry_address + 3]; - self.sprite_fetcher.oam_entries[self.sprite_fetcher.oam_entries_len as usize] = SpriteAttribute::new(end_y, end_x, tile_number, attributes); - self.sprite_fetcher.oam_entries_len += 1; - } - - self.t_cycles_passed += 2; //half a m_cycle - - if self.t_cycles_passed == 80{ - let slice = self.sprite_fetcher.oam_entries[0..self.sprite_fetcher.oam_entries_len as usize].as_mut(); - slice.sort_by(|s1:&SpriteAttribute, s2:&SpriteAttribute| s1.x.cmp(&s2.x)); - self.state = PpuState::PixelTransfer; - self.scanline_started = false; - } - } - PpuState::Hblank=>{ - self.t_cycles_passed += 2; - - if self.t_cycles_passed == 456{ - if self.ly_register == 143{ - self.state = PpuState::Vblank; - //reseting the window counter on vblank - self.bg_fetcher.window_line_counter = 0; - *if_register |= BIT_0_MASK; - if self.v_blank_interrupt_request{ - self.trigger_stat_interrupt = true; - } - } - else{ - self.state = PpuState::OamSearch; - if self.oam_search_interrupt_request{ - self.trigger_stat_interrupt = true; - } - } - self.pixel_x_pos = 0; - self.t_cycles_passed = 0; - self.ly_register += 1; - } - } - PpuState::Vblank=>{ - if self.t_cycles_passed == 4560{ - self.state = PpuState::OamSearch; - if self.oam_search_interrupt_request{ - self.trigger_stat_interrupt = true; - } - self.pixel_x_pos = 0; - self.t_cycles_passed = 0; - self.ly_register = 0; - } - else{ - self.ly_register = 144 + (self.t_cycles_passed / 456) as u8; - } - - self.t_cycles_passed += 2; - } - PpuState::PixelTransfer=>{ - for _ in 0..2{ - if self.pixel_x_pos < 160{ - if self.lcd_control & BIT_1_MASK != 0{ - self.sprite_fetcher.fetch_pixels(&self.vram, self.lcd_control, self.ly_register, self.pixel_x_pos); - } - if self.sprite_fetcher.rendering{ - self.bg_fetcher.pause(); - } - else{ - self.bg_fetcher.fetch_pixels(&self.vram, self.lcd_control, self.ly_register, &self.window_pos, &self.bg_pos); - self.try_push_to_lcd(); - if self.pixel_x_pos == 160 { - self.state = PpuState::Hblank; - if self.h_blank_interrupt_request{ - self.trigger_stat_interrupt = true; - } - self.bg_fetcher.try_increment_window_counter(); - self.bg_fetcher.reset(); - self.sprite_fetcher.reset(); - - // If im on the first iteration and finished the 160 pixels break; - // In this case the number of t_cycles should be eneven but it will break - // my code way too much for now so Im leaving this as it is... (maybe in the future) - break; - } - } - } - } - self.t_cycles_passed += 2; - } - } - - } - } - - fn try_push_to_lcd(&mut self){ - if !self.bg_fetcher.fifo.is_empty(){ - if !self.scanline_started{ - // discard the next pixel in the bg fifo - // the bg fifo should start with 8 pixels and not push more untill its empty again - if 8 - self.bg_fetcher.fifo.len() >= self.bg_pos.x as usize % 8{ - self.scanline_started = true; - } - else{ - self.bg_fetcher.fifo.remove(0); - return; - } - } - - let bg_pixel_color_num = self.bg_fetcher.fifo.remove(0); - let bg_pixel = self.bg_color_mapping[bg_pixel_color_num as usize]; - let pixel = if !self.sprite_fetcher.fifo.is_empty(){ - let sprite_color_num = self.sprite_fetcher.fifo.remove(0); - let pixel_oam_attribute = &self.sprite_fetcher.oam_entries[sprite_color_num.1 as usize]; - - if sprite_color_num.0 == 0 || (pixel_oam_attribute.is_bg_priority && bg_pixel_color_num != 0){ - bg_pixel - } - else{ - let sprite_pixel = if pixel_oam_attribute.palette_number{ - self.obj_color_mapping1[sprite_color_num.0 as usize] - } - else{ - self.obj_color_mapping0[sprite_color_num.0 as usize] - }; - - sprite_pixel.expect("Corruption in the object color pallete") - } - } - else{ - bg_pixel - }; - - self.push_lcd_buffer.push(pixel); - self.pixel_x_pos += 1; - } - } -} \ No newline at end of file diff --git a/lib_gb/src/ppu/fifo/fifo_register_updater.rs b/lib_gb/src/ppu/fifo/fifo_register_updater.rs deleted file mode 100644 index 8ed87b58..00000000 --- a/lib_gb/src/ppu/fifo/fifo_register_updater.rs +++ /dev/null @@ -1,81 +0,0 @@ -use crate::utils::bit_masks::*; -use crate::ppu::{color::*, colors::*, fifo::fifo_ppu::FifoPpu, gfx_device::GfxDevice}; - -const WX_OFFSET:u8 = 7; - -pub fn handle_lcdcontrol_register( register:u8, ppu:&mut FifoPpu){ - if ppu.lcd_control & BIT_7_MASK != 0 && register & BIT_7_MASK == 0{ - ppu.turn_off(); - } - else if ppu.lcd_control & BIT_7_MASK == 0 && register & BIT_7_MASK != 0{ - ppu.turn_on(); - } - - ppu.lcd_control = register; -} - -pub fn update_stat_register(register:u8, ppu: &mut FifoPpu){ - ppu.h_blank_interrupt_request = register & BIT_3_MASK != 0; - ppu.v_blank_interrupt_request = register & BIT_4_MASK != 0; - ppu.oam_search_interrupt_request = register & BIT_5_MASK != 0; - ppu.coincidence_interrupt_request = register & BIT_6_MASK != 0; - - ppu.stat_register = register & 0b111_1000; -} - -pub fn set_scx(ppu: &mut FifoPpu, value:u8){ - ppu.bg_pos.x = value; -} - -pub fn set_scy(ppu:&mut FifoPpu, value:u8){ - ppu.bg_pos.y = value; -} - -pub fn handle_bg_pallet_register(register:u8, pallet:&mut [Color;4] ){ - pallet[0] = get_matching_color(register&0b00000011); - pallet[1] = get_matching_color((register&0b00001100)>>2); - pallet[2] = get_matching_color((register&0b00110000)>>4); - pallet[3] = get_matching_color((register&0b11000000)>>6); -} - -pub fn handle_obp_pallet_register(register:u8, pallet:&mut [Option;4] ){ - pallet[0] = None; - pallet[1] = Some(get_matching_color((register&0b00001100)>>2)); - pallet[2] = Some(get_matching_color((register&0b00110000)>>4)); - pallet[3] = Some(get_matching_color((register&0b11000000)>>6)); -} - -fn get_matching_color(number:u8)->Color{ - return match number{ - 0b00=>WHITE, - 0b01=>LIGHT_GRAY, - 0b10=>DARK_GRAY, - 0b11=>BLACK, - _=>std::panic!("no macthing color for color number: {}", number) - }; -} - -pub fn handle_wy_register(register:u8, ppu:&mut FifoPpu){ - ppu.window_pos.y = register; -} - -pub fn handle_wx_register(register:u8, ppu:&mut FifoPpu){ - if register < WX_OFFSET{ - ppu.window_pos.x = 0; - } - else{ - ppu.window_pos.x = register - WX_OFFSET; - } -} - -pub fn get_ly(ppu:&FifoPpu)->u8{ - ppu.ly_register -} - -pub fn get_stat(ppu:&FifoPpu)->u8{ - ppu.stat_register -} - -pub fn set_lyc(ppu:&mut FifoPpu, value:u8){ - ppu.lyc_register = value; -} \ No newline at end of file diff --git a/lib_gb/src/ppu/fifo/mod.rs b/lib_gb/src/ppu/fifo/mod.rs index 3ed89231..361dc6f5 100644 --- a/lib_gb/src/ppu/fifo/mod.rs +++ b/lib_gb/src/ppu/fifo/mod.rs @@ -1,6 +1,4 @@ -pub mod fifo_ppu; -pub mod fifo_register_updater; -mod background_fetcher; +pub mod background_fetcher; +pub mod sprite_fetcher; mod fetching_state; -mod fetcher_state_machine; -mod sprite_fetcher; \ No newline at end of file +mod fetcher_state_machine; \ No newline at end of file diff --git a/lib_gb/src/ppu/gb_ppu.rs b/lib_gb/src/ppu/gb_ppu.rs index f108a644..2aa77d8c 100644 --- a/lib_gb/src/ppu/gb_ppu.rs +++ b/lib_gb/src/ppu/gb_ppu.rs @@ -1,64 +1,29 @@ +use crate::utils::{vec2::Vec2, bit_masks::*}; use crate::mmu::vram::VRam; -use super::ppu_state::PpuState; -use super::color::Color; -use super::colors::*; -use crate::utils::vec2::Vec2; -use super::colors::WHITE; -use super::normal_sprite::NormalSprite; -use super::extended_sprite::ExtendedSprite; -use super::sprite::Sprite; -use super::sprite_attribute::SpriteAttribute; -use crate::utils::{ - bit_masks::* -}; -use std::cmp; +use crate::ppu::color::*; +use crate::ppu::colors::*; +use crate::ppu::gfx_device::GfxDevice; +use crate::ppu::{ppu_state::PpuState, sprite_attribute::SpriteAttribute}; + +use super::fifo::background_fetcher::BackgroundFetcher; +use super::fifo::sprite_fetcher::SpriteFetcher; pub const SCREEN_HEIGHT: usize = 144; pub const SCREEN_WIDTH: usize = 160; +pub struct GbPpu{ -//CPU frequrncy: 4,194,304 / 59.727~ / 4 == 70224 / 4 -pub const CYCLES_PER_FRAME:u32 = 17556; - -const OAM_CLOCKS:u8 = 20; -const PIXEL_TRANSFER_CLOCKS:u8 = 43; -const H_BLANK_CLOCKS:u8 = 51; -const DRAWING_CYCLE_CLOCKS: u8 = OAM_CLOCKS + H_BLANK_CLOCKS + PIXEL_TRANSFER_CLOCKS; -const LY_MAX_VALUE:u8 = 153; -const SPRITE_ATTRIBUTE_TABLE_SIZE:usize = 0xA0; -const OAM_SIZE:u16 = 0xA0; -const OBJ_PER_LINE:usize = 10; -const SPRITE_WIDTH:u8 = 8; -const NORMAL_SPRITE_HIEGHT:u8 = 8; -const SPRITE_MAX_HEIGHT:u8 = 16; -const BG_SPRITES_PER_LINE:u16 = 32; -const SPRITE_SIZE_IN_MEMORY:u16 = 16; - -const BLANK_SCREEN_BUFFER:[u32; SCREEN_HEIGHT * SCREEN_WIDTH] = [GbPpu::color_as_uint(&WHITE);SCREEN_HEIGHT * SCREEN_WIDTH]; - -pub struct GbPpu { pub vram: VRam, - pub sprite_attribute_table:[u8;SPRITE_ATTRIBUTE_TABLE_SIZE], - - pub screen_buffer: [u32; SCREEN_HEIGHT*SCREEN_WIDTH], - pub screen_enable: bool, - pub window_enable: bool, - pub sprite_extended: bool, - pub background_enabled: bool, - pub gbc_mode: bool, - pub sprite_enable: bool, - pub window_tile_map_address: bool, - pub window_tile_background_map_data_address: bool, - pub background_tile_map_address: bool, - pub background_scroll: Vec2, - pub window_scroll: Vec2, - pub bg_color_mapping: [Color; 4], - pub obj_color_mapping0: [Option;4], - pub obj_color_mapping1: [Option;4], - pub current_line_drawn: u8, + pub oam:[u8;0xA0], pub state:PpuState, - + pub lcd_control:u8, pub stat_register:u8, pub lyc_register:u8, + pub ly_register:u8, + pub window_pos:Vec2, + pub bg_pos:Vec2, + pub bg_color_mapping: [Color; 4], + pub obj_color_mapping0: [Option;4], + pub obj_color_mapping1: [Option;4], //interrupts pub v_blank_interrupt_request:bool, @@ -66,448 +31,260 @@ pub struct GbPpu { pub oam_search_interrupt_request:bool, pub coincidence_interrupt_request:bool, - window_active:bool, - window_line_counter:u8, - line_rendered:bool, - current_cycle:u32, - last_screen_state:bool, - v_blank_triggered:bool, - stat_triggered:bool + gfx_device: GFX, + t_cycles_passed:u16, + screen_buffer: [u32; SCREEN_HEIGHT * SCREEN_WIDTH], + push_lcd_buffer:Vec, + screen_buffer_index:usize, + pixel_x_pos:u8, + scanline_started:bool, + bg_fetcher:BackgroundFetcher, + sprite_fetcher:SpriteFetcher, + stat_triggered:bool, + trigger_stat_interrupt:bool, } -impl Default for GbPpu { - fn default() -> Self { - GbPpu { - vram:VRam::default(), - sprite_attribute_table: [0;SPRITE_ATTRIBUTE_TABLE_SIZE], - stat_register:0, - lyc_register:0, - background_enabled: false, - background_scroll: Vec2:: { x: 0, y: 0 }, - window_scroll: Vec2:: { x: 0, y: 0 }, - background_tile_map_address: false, - gbc_mode: false, - screen_buffer: [0; SCREEN_HEIGHT*SCREEN_WIDTH], - screen_enable: false, - sprite_enable: false, - sprite_extended: false, - window_enable: false, - window_tile_background_map_data_address: false, - window_tile_map_address: false, - bg_color_mapping: [WHITE, LIGHT_GRAY, DARK_GRAY, BLACK], +impl GbPpu{ + pub fn new(device:GFX) -> Self { + Self{ + gfx_device: device, + vram: VRam::default(), + oam: [0;0xA0], + stat_register: 0, + lyc_register: 0, + lcd_control: 0, + bg_pos: Vec2::{x:0, y:0}, + window_pos: Vec2::{x:0,y:0}, + screen_buffer:[0;160*144], + bg_color_mapping:[WHITE, LIGHT_GRAY, DARK_GRAY, BLACK], obj_color_mapping0: [None, Some(LIGHT_GRAY), Some(DARK_GRAY), Some(BLACK)], obj_color_mapping1: [None, Some(LIGHT_GRAY), Some(DARK_GRAY), Some(BLACK)], - current_line_drawn:0, - state:PpuState::OamSearch, - line_rendered:false, - window_line_counter:0, - window_active:false, - current_cycle:0, - last_screen_state:true, - v_blank_triggered:false, - stat_triggered:false, + ly_register:0, + state: PpuState::OamSearch, //interrupts - v_blank_interrupt_request:false, + v_blank_interrupt_request:false, h_blank_interrupt_request:false, - oam_search_interrupt_request:false, - coincidence_interrupt_request:false + oam_search_interrupt_request:false, + coincidence_interrupt_request:false, + screen_buffer_index:0, + t_cycles_passed:0, + stat_triggered:false, + trigger_stat_interrupt:false, + bg_fetcher:BackgroundFetcher::new(), + sprite_fetcher:SpriteFetcher::new(), + push_lcd_buffer:Vec::::new(), + pixel_x_pos:0, + scanline_started:false } } -} -impl GbPpu { - const fn color_as_uint(color: &Color) -> u32 { - ((color.r as u32) << 16) | ((color.g as u32) << 8) | (color.b as u32) + pub fn turn_off(&mut self){ + self.screen_buffer_index = 0; + self.t_cycles_passed = 0; + //This is an expensive operation! + unsafe{std::ptr::write_bytes(self.screen_buffer.as_mut_ptr(), 0xFF, self.screen_buffer.len())}; + self.gfx_device.swap_buffer(&self.screen_buffer); + self.state = PpuState::Hblank; + self.ly_register = 0; + self.stat_triggered = false; + self.trigger_stat_interrupt = false; + self.bg_fetcher.reset(); + self.sprite_fetcher.reset(); + self.pixel_x_pos = 0; } - pub fn get_frame_buffer(&self)->&[u32;SCREEN_HEIGHT*SCREEN_WIDTH]{ - return &self.screen_buffer; + pub fn turn_on(&mut self){ + self.state = PpuState::OamSearch; } - fn update_ly(&mut self){ - - let line = self.current_cycle/DRAWING_CYCLE_CLOCKS as u32; - if self.current_cycle >= CYCLES_PER_FRAME { - self.current_line_drawn = 0; - self.line_rendered = false; - self.current_cycle -= CYCLES_PER_FRAME; - self.window_line_counter = 0; - self.window_active = false; - } - else if self.current_line_drawn != line as u8{ - self.current_line_drawn = line as u8; - self.line_rendered = false; - } - else if self.current_line_drawn > LY_MAX_VALUE{ - std::panic!("invalid LY register value: {}", self.current_line_drawn); + pub fn cycle(&mut self, m_cycles:u8, if_register:&mut u8){ + if self.lcd_control & BIT_7_MASK == 0{ + return; } - } - fn update_ly_register(&mut self, if_register:&mut u8){ - if self.current_line_drawn >= SCREEN_HEIGHT as u8 && !self.v_blank_triggered{ - *if_register |= BIT_0_MASK; - - self.v_blank_triggered = true; - } - else if self.current_line_drawn < SCREEN_HEIGHT as u8{ - self.v_blank_triggered = false; + self.cycle_fetcher(m_cycles, if_register); + + self.update_stat_register(if_register); + + for pixel in self.push_lcd_buffer.iter(){ + self.screen_buffer[self.screen_buffer_index] = u32::from(*pixel); + self.screen_buffer_index += 1; + if self.screen_buffer_index == self.screen_buffer.len(){ + self.gfx_device.swap_buffer(&self.screen_buffer); + self.screen_buffer_index = 0; + } } - } - fn update_stat_register(&mut self, if_register:&mut u8){ - let mut lcd_stat_interrupt:bool = false; + self.push_lcd_buffer.clear(); + } - if self.current_line_drawn == self.lyc_register{ - self.stat_register |= BIT_2_MASK; + fn update_stat_register(&mut self, if_register: &mut u8) { + self.stat_register &= 0b1111_1100; + self.stat_register |= self.state as u8; + if self.ly_register == self.lyc_register{ if self.coincidence_interrupt_request { - lcd_stat_interrupt = true; + self.trigger_stat_interrupt = true; } + self.stat_register |= BIT_2_MASK; } else{ self.stat_register &= !BIT_2_MASK; } - - //clears the 2 lower bits - self.stat_register = (self.stat_register >> 2)<<2; - self.stat_register |= self.state as u8; - - match self.state{ - PpuState::OamSearch=>{ - if self.oam_search_interrupt_request{ - lcd_stat_interrupt = true; - } - }, - PpuState::Hblank=>{ - if self.h_blank_interrupt_request{ - lcd_stat_interrupt = true; - } - }, - PpuState::Vblank=>{ - if self.v_blank_interrupt_request{ - lcd_stat_interrupt = true; - } - }, - _=>{} - } - - if lcd_stat_interrupt{ + if self.trigger_stat_interrupt{ if !self.stat_triggered{ *if_register |= BIT_1_MASK; - self.stat_triggered = true; } } else{ self.stat_triggered = false; } + self.trigger_stat_interrupt = false; } - fn get_ppu_state(cycle_counter:u32, last_ly:u8)->PpuState{ - if last_ly >= SCREEN_HEIGHT as u8{ - return PpuState::Vblank; - } - - //getting the reminder of the clocks - let current_line_clocks = cycle_counter % DRAWING_CYCLE_CLOCKS as u32; - - const OAM_SERACH_END:u8 = OAM_CLOCKS - 1; - const PIXEL_TRANSFER_START:u8 = OAM_CLOCKS; - const PIXEL_TRANSFER_END:u8 = OAM_CLOCKS + PIXEL_TRANSFER_CLOCKS - 1; - const H_BLANK_START:u8 = OAM_CLOCKS + PIXEL_TRANSFER_CLOCKS; - const H_BLANK_END:u8 = H_BLANK_START + H_BLANK_CLOCKS - 1; - - return match current_line_clocks as u8{ - 0 ..= OAM_SERACH_END => PpuState::OamSearch, // 0-19 (20) - PIXEL_TRANSFER_START ..= PIXEL_TRANSFER_END => PpuState::PixelTransfer, //20-62 (43) - H_BLANK_START ..= H_BLANK_END => PpuState::Hblank,//63-113(51) - _=>std::panic!("Error calculating ppu state") - }; - } - - pub fn update_gb_screen(&mut self,if_register:&mut u8, cycles_passed:u32){ - if !self.screen_enable && self.last_screen_state { - self.current_line_drawn = 0; - self.current_cycle = 0; - self.screen_buffer = BLANK_SCREEN_BUFFER; - self.state = PpuState::Hblank; - self.window_active = false; - self.last_screen_state = self.screen_enable; - return; - } - else if !self.screen_enable{ - return; - } - - self.last_screen_state = self.screen_enable; - - self.current_cycle += cycles_passed as u32; - self.update_ly(); - self.state = Self::get_ppu_state(self.current_cycle, self.current_line_drawn); - - self.update_ly_register(if_register); - self.update_stat_register(if_register); - - if self.state as u8 == PpuState::PixelTransfer as u8{ - if !self.line_rendered { - self.line_rendered = true; + fn cycle_fetcher(&mut self, m_cycles:u8, if_register:&mut u8){ + let sprite_height = if (self.lcd_control & BIT_2_MASK) != 0 {16} else {8}; - let mut frame_buffer_line = self.get_bg_frame_buffer(); - self.draw_window_frame_buffer(&mut frame_buffer_line); - self.draw_objects_frame_buffer(&mut frame_buffer_line); - - let line_index = self.current_line_drawn as usize * SCREEN_WIDTH; - - for i in line_index..line_index+SCREEN_WIDTH{ - self.screen_buffer[i] = Self::color_as_uint(&frame_buffer_line[(i - line_index)]); + for _ in 0..m_cycles * 2{ + match self.state{ + PpuState::OamSearch=>{ + let oam_index = self.t_cycles_passed / 2; + let oam_entry_address = (oam_index * 4) as usize; + let end_y = self.oam[oam_entry_address]; + let end_x = self.oam[oam_entry_address + 1]; + + if end_x > 0 && self.ly_register + 16 >= end_y && self.ly_register + 16 < end_y + sprite_height && self.sprite_fetcher.oam_entries_len < 10{ + let tile_number = self.oam[oam_entry_address + 2]; + let attributes = self.oam[oam_entry_address + 3]; + self.sprite_fetcher.oam_entries[self.sprite_fetcher.oam_entries_len as usize] = SpriteAttribute::new(end_y, end_x, tile_number, attributes); + self.sprite_fetcher.oam_entries_len += 1; + } + + self.t_cycles_passed += 2; //half a m_cycle + + if self.t_cycles_passed == 80{ + let slice = self.sprite_fetcher.oam_entries[0..self.sprite_fetcher.oam_entries_len as usize].as_mut(); + slice.sort_by(|s1:&SpriteAttribute, s2:&SpriteAttribute| s1.x.cmp(&s2.x)); + self.state = PpuState::PixelTransfer; + self.scanline_started = false; + } + } + PpuState::Hblank=>{ + self.t_cycles_passed += 2; + + if self.t_cycles_passed == 456{ + if self.ly_register == 143{ + self.state = PpuState::Vblank; + //reseting the window counter on vblank + self.bg_fetcher.window_line_counter = 0; + *if_register |= BIT_0_MASK; + if self.v_blank_interrupt_request{ + self.trigger_stat_interrupt = true; + } + } + else{ + self.state = PpuState::OamSearch; + if self.oam_search_interrupt_request{ + self.trigger_stat_interrupt = true; + } + } + self.pixel_x_pos = 0; + self.t_cycles_passed = 0; + self.ly_register += 1; + } + } + PpuState::Vblank=>{ + if self.t_cycles_passed == 4560{ + self.state = PpuState::OamSearch; + if self.oam_search_interrupt_request{ + self.trigger_stat_interrupt = true; + } + self.pixel_x_pos = 0; + self.t_cycles_passed = 0; + self.ly_register = 0; + } + else{ + self.ly_register = 144 + (self.t_cycles_passed / 456) as u8; + } + + self.t_cycles_passed += 2; + } + PpuState::PixelTransfer=>{ + for _ in 0..2{ + if self.pixel_x_pos < 160{ + if self.lcd_control & BIT_1_MASK != 0{ + self.sprite_fetcher.fetch_pixels(&self.vram, self.lcd_control, self.ly_register, self.pixel_x_pos); + } + if self.sprite_fetcher.rendering{ + self.bg_fetcher.pause(); + } + else{ + self.bg_fetcher.fetch_pixels(&self.vram, self.lcd_control, self.ly_register, &self.window_pos, &self.bg_pos); + self.try_push_to_lcd(); + if self.pixel_x_pos == 160 { + self.state = PpuState::Hblank; + if self.h_blank_interrupt_request{ + self.trigger_stat_interrupt = true; + } + self.bg_fetcher.try_increment_window_counter(); + self.bg_fetcher.reset(); + self.sprite_fetcher.reset(); + + // If im on the first iteration and finished the 160 pixels break; + // In this case the number of t_cycles should be eneven but it will break + // my code way too much for now so Im leaving this as it is... (maybe in the future) + break; + } + } + } + } + self.t_cycles_passed += 2; } - } - } - } - - fn read_vram(&self, address:u16)->u8{ - self.vram.read_current_bank(address - 0x8000) - } - - fn get_bg_frame_buffer(&self)-> [Color;SCREEN_WIDTH] { - if !self.background_enabled{ - //color in BGP 0 - let color = self.get_bg_color(0); - return [color;SCREEN_WIDTH] - } - - let current_line = self.current_line_drawn; - - let address = if self.background_tile_map_address { - 0x9C00 - } else { - 0x9800 - }; - let mut line_sprites:Vec = Vec::with_capacity(BG_SPRITES_PER_LINE as usize); - let index = ((current_line.wrapping_add(self.background_scroll.y)) / NORMAL_SPRITE_HIEGHT) as u16; - if self.window_tile_background_map_data_address { - for i in 0..BG_SPRITES_PER_LINE { - let chr: u8 = self.read_vram(address + (index*BG_SPRITES_PER_LINE) + i); - let sprite = self.get_normal_sprite(chr, 0x8000); - line_sprites.push(sprite); - } - } - else { - for i in 0..BG_SPRITES_PER_LINE { - let mut chr: u8 = self.read_vram(address + (index*BG_SPRITES_PER_LINE) + i); - chr = chr.wrapping_add(0x80); - let sprite = self.get_normal_sprite(chr, 0x8800); - line_sprites.push(sprite); - } - } - - let mut drawn_line:[Color; 256] = [Color::default();256]; - - let sprite_line = (current_line as u16 + self.background_scroll.y as u16) % 8; - for i in 0..line_sprites.len(){ - for j in 0..SPRITE_WIDTH{ - let pixel = line_sprites[i].pixels[((sprite_line as u8 * SPRITE_WIDTH) + j) as usize]; - drawn_line[(i * SPRITE_WIDTH as usize) + j as usize] = self.get_bg_color(pixel); - } - } - - let mut screen_line:[Color;SCREEN_WIDTH] = [Color::default();SCREEN_WIDTH]; - for i in 0..SCREEN_WIDTH{ - let index:usize = (i as u8).wrapping_add(self.background_scroll.x) as usize; - screen_line[i] = drawn_line[index] - } - - return screen_line; - } - - fn get_normal_sprite(&self, index:u8, data_address:u16)->NormalSprite{ - let mut sprite = NormalSprite::new(); - - let mut line_number = 0; - let start:u16 = index as u16 * SPRITE_SIZE_IN_MEMORY; - let end:u16 = start + SPRITE_SIZE_IN_MEMORY; - for j in (start .. end).step_by(2) { - self.get_line(&mut sprite, data_address + j, line_number); - line_number += 1; - } - - return sprite; - } - - - fn draw_window_frame_buffer(&mut self, line:&mut [Color;SCREEN_WIDTH]) { - if !self.window_enable || !self.background_enabled || self.current_line_drawn < self.window_scroll.y{ - return; - } - - if self.current_line_drawn == self.window_scroll.y{ - self.window_active = true; - } - - if !self.window_active { - return; - } - - if self.window_scroll.x as usize > SCREEN_WIDTH { - return; - } - - let address = if self.window_tile_map_address { - 0x9C00 - } else { - 0x9800 - }; - let mut line_sprites:Vec = Vec::with_capacity(BG_SPRITES_PER_LINE as usize); - let index = ((self.window_line_counter) / 8) as u16; - if self.window_tile_background_map_data_address { - for i in 0..BG_SPRITES_PER_LINE { - let chr: u8 = self.read_vram(address + (index*BG_SPRITES_PER_LINE) + i); - let sprite = self.get_normal_sprite(chr, 0x8000); - line_sprites.push(sprite); - } - } - else { - for i in 0..BG_SPRITES_PER_LINE { - let mut chr: u8 = self.read_vram(address + (index*BG_SPRITES_PER_LINE) + i); - chr = chr.wrapping_add(0x80); - let sprite = self.get_normal_sprite(chr, 0x8800); - line_sprites.push(sprite); - } - } - - let mut drawn_line:[Color; 256] = [Color::default();256]; - - let sprite_line = ( self.window_line_counter) % NORMAL_SPRITE_HIEGHT; - for i in 0..line_sprites.len(){ - for j in 0..SPRITE_WIDTH{ - let pixel = line_sprites[i].pixels[((sprite_line * SPRITE_WIDTH) + j) as usize]; - drawn_line[(i * SPRITE_WIDTH as usize) + j as usize] = self.get_bg_color(pixel); - } - } - - for i in self.window_scroll.x as usize..SCREEN_WIDTH{ - line[(i as usize)] = drawn_line[i - self.window_scroll.x as usize]; - } - - self.window_line_counter += 1; - } - - fn draw_objects_frame_buffer(&self, line:&mut [Color;SCREEN_WIDTH]){ - if !self.sprite_enable{ - return; - } - - let currrent_line = self.current_line_drawn; - - let mut obj_attributes = Vec::with_capacity(OBJ_PER_LINE as usize); - - for i in (0..OAM_SIZE).step_by(4){ - if obj_attributes.len() >= OBJ_PER_LINE{ - break; - } - - let end_y = self.sprite_attribute_table[i as usize]; - let end_x = self.sprite_attribute_table[(i + 1) as usize]; - let start_y = cmp::max(0, (end_y as i16) - SPRITE_MAX_HEIGHT as i16) as u8; - - //cheks if this sprite apears in this line - if currrent_line >= end_y || currrent_line < start_y || end_x == 0 || end_x >=168{ - continue; - } - - //end_y is is the upper y value of the sprite + 16 lines and normal sprite is 8 lines. - //so checking if this sprite shouldnt be drawn - //on extended sprite end_y should be within all the values of current line - if !self.sprite_extended && end_y - currrent_line <= 8{ - continue; } - let tile_number = self.sprite_attribute_table[(i + 2) as usize]; - let attributes = self.sprite_attribute_table[(i + 3) as usize]; - - obj_attributes.push(SpriteAttribute::new(end_y, end_x, tile_number, attributes)); } + } - //sprites that occurs first in the oam memory draws first so im reversing it so the first ones will be last and will - //draw onto the last ones. - obj_attributes.reverse(); - //ordering this from the less priority to the higher where the smaller x the priority higher. - obj_attributes.sort_by(|a, b| b.x.cmp(&a.x)); - - for obj_attribute in &obj_attributes{ - let mut sprite = self.get_sprite(obj_attribute.tile_number, 0x8000, self.sprite_extended); - - if obj_attribute.flip_y { - sprite.flip_y(); + fn try_push_to_lcd(&mut self){ + if !self.bg_fetcher.fifo.is_empty(){ + if !self.scanline_started{ + // discard the next pixel in the bg fifo + // the bg fifo should start with 8 pixels and not push more untill its empty again + if 8 - self.bg_fetcher.fifo.len() >= self.bg_pos.x as usize % 8{ + self.scanline_started = true; + } + else{ + self.bg_fetcher.fifo.remove(0); + return; + } } - if obj_attribute.flip_x{ - sprite.flip_x(); - } - - let end_x = cmp::min(obj_attribute.x, SCREEN_WIDTH as u8); - let start_x = cmp::max(0, (end_x as i16) - SPRITE_WIDTH as i16) as u8; - let start_y = cmp::max(0, (obj_attribute.y as i16) - SPRITE_MAX_HEIGHT as i16) as u8; - let sprite_line = currrent_line - start_y; + let bg_pixel_color_num = self.bg_fetcher.fifo.remove(0); + let bg_pixel = self.bg_color_mapping[bg_pixel_color_num as usize]; + let pixel = if !self.sprite_fetcher.fifo.is_empty(){ + let sprite_color_num = self.sprite_fetcher.fifo.remove(0); + let pixel_oam_attribute = &self.sprite_fetcher.oam_entries[sprite_color_num.1 as usize]; - for x in start_x..end_x{ - let pixel = sprite.get_pixel(sprite_line * SPRITE_WIDTH + (x - start_x)); - let color = self.get_obj_color(pixel, obj_attribute.palette_number); - - if let Some(c) = color{ - if !(obj_attribute.is_bg_priority && self.get_bg_color(0) != line[x as usize]){ - line[x as usize] = c + if sprite_color_num.0 == 0 || (pixel_oam_attribute.is_bg_priority && bg_pixel_color_num != 0){ + bg_pixel + } + else{ + let sprite_pixel = if pixel_oam_attribute.palette_number{ + self.obj_color_mapping1[sprite_color_num.0 as usize] } + else{ + self.obj_color_mapping0[sprite_color_num.0 as usize] + }; + + sprite_pixel.expect("Corruption in the object color pallete") } } - } - } - - fn get_bg_color(&self, color: u8) -> Color { - return self.bg_color_mapping[color as usize].clone(); - } + else{ + bg_pixel + }; - fn get_obj_color(&self, color:u8, pallet_bit_set:bool)->Option{ - return if pallet_bit_set{ - self.obj_color_mapping1[color as usize].clone() + self.push_lcd_buffer.push(pixel); + self.pixel_x_pos += 1; } - else{ - self.obj_color_mapping0[color as usize].clone() - }; } - - fn get_sprite(&self, mut index:u8, data_address:u16, extended:bool)->Box{ - let mut sprite:Box; - if extended{ - //ignore bit 0 - index = (index >> 1) << 1; - sprite = Box::new(ExtendedSprite::new()); - } - else{ - sprite = Box::new(NormalSprite::new()); - } - - let mut line_number = 0; - let start:u16 = index as u16 * SPRITE_SIZE_IN_MEMORY; - let end:u16 = start + ((sprite.size() as u16) *2); - let raw = Box::into_raw(sprite); - for j in (start .. end).step_by(2) { - self.get_line( raw, data_address + j, line_number); - line_number += 1; - } - unsafe{sprite = Box::from_raw(raw);} - - return sprite; - } - - fn get_line(&self, sprite:*mut dyn Sprite, address:u16, line_number:u8){ - let byte = self.read_vram(address); - let next = self.read_vram(address + 1); - for k in (0..SPRITE_WIDTH).rev() { - let mask = 1 << k; - let mut value = (byte & mask) >> k; - value |= ((next & mask) >> k) << 1; - let swaped = SPRITE_WIDTH - 1 - k; - unsafe{(*sprite).set_pixel(line_number * SPRITE_WIDTH + swaped, value);} - } - } -} +} \ No newline at end of file diff --git a/lib_gb/src/ppu/mod.rs b/lib_gb/src/ppu/mod.rs index 2cbc0069..ca293728 100644 --- a/lib_gb/src/ppu/mod.rs +++ b/lib_gb/src/ppu/mod.rs @@ -5,7 +5,4 @@ pub mod colors; pub mod ppu_register_updater; pub mod fifo; pub mod gfx_device; -mod normal_sprite; -mod sprite_attribute; -mod extended_sprite; -mod sprite; \ No newline at end of file +mod sprite_attribute; \ No newline at end of file diff --git a/lib_gb/src/ppu/normal_sprite.rs b/lib_gb/src/ppu/normal_sprite.rs deleted file mode 100644 index 5da6832b..00000000 --- a/lib_gb/src/ppu/normal_sprite.rs +++ /dev/null @@ -1,67 +0,0 @@ -use super::sprite::Sprite; - -pub struct NormalSprite { - pub pixels: [u8; 64] -} - -impl NormalSprite { - const SIZE:u8 = 8; - - pub fn new() -> NormalSprite { - NormalSprite { pixels: [0; 64] } - } - - fn copy_pixels(sprite:&mut NormalSprite, index:usize, pixels:&[u8]){ - for i in 0..pixels.len(){ - sprite.pixels[index * 8 + i] = pixels[i]; - } - } -} - -impl Clone for NormalSprite{ - fn clone(&self)->Self{ - NormalSprite{ - pixels:self.pixels - } - } -} - -impl Sprite for NormalSprite{ - fn size(&self)->u8 {Self::SIZE} - - fn get_pixel(&self, pos: u8)->u8{ - self.pixels[pos as usize] - } - - fn set_pixel(&mut self, pos: u8, pixel: u8){ - self.pixels[pos as usize] = pixel; - } - - fn flip_x(&mut self){ - let mut fliiped = NormalSprite::new(); - - for y in 0..8{ - let line = &self.pixels[y*8 .. (y+1)*8]; - for x in 0..4{ - fliiped.pixels[y*8 + x] = line[7-x]; - fliiped.pixels[y*8 + (7-x)] = line[x]; - } - } - - *self = fliiped; - } - - fn flip_y(&mut self){ - let mut flipped = NormalSprite::new(); - for y in 0..4{ - let upper_line = &self.pixels[y*8..(y+1)*8]; - let opposite_index = 7-y; - let lower_line = &self.pixels[opposite_index*8..(opposite_index+1)*8]; - - Self::copy_pixels(&mut flipped,y, lower_line); - Self::copy_pixels(&mut flipped,opposite_index, upper_line); - } - - *self = flipped; - } -} \ No newline at end of file diff --git a/lib_gb/src/ppu/ppu_register_updater.rs b/lib_gb/src/ppu/ppu_register_updater.rs index 2dc2f751..2157ac41 100644 --- a/lib_gb/src/ppu/ppu_register_updater.rs +++ b/lib_gb/src/ppu/ppu_register_updater.rs @@ -1,20 +1,20 @@ use crate::utils::bit_masks::*; -use super::{ gb_ppu::GbPpu, color::*, colors::*}; +use super::{color::*, colors::*, gb_ppu::GbPpu, gfx_device::GfxDevice}; const WX_OFFSET:u8 = 7; -pub fn handle_lcdcontrol_register( register:u8, ppu:&mut GbPpu){ - ppu.screen_enable = (register & BIT_7_MASK) != 0; - ppu.window_tile_map_address = (register & BIT_6_MASK) != 0; - ppu.window_enable = (register & BIT_5_MASK) != 0; - ppu.window_tile_background_map_data_address = (register & BIT_4_MASK) != 0; - ppu.background_tile_map_address = (register & BIT_3_MASK) != 0; - ppu.sprite_extended = (register & BIT_2_MASK) != 0; - ppu.sprite_enable = (register & BIT_1_MASK) != 0; - ppu.background_enabled = (register & BIT_0_MASK) != 0; +pub fn handle_lcdcontrol_register( register:u8, ppu:&mut GbPpu){ + if ppu.lcd_control & BIT_7_MASK != 0 && register & BIT_7_MASK == 0{ + ppu.turn_off(); + } + else if ppu.lcd_control & BIT_7_MASK == 0 && register & BIT_7_MASK != 0{ + ppu.turn_on(); + } + + ppu.lcd_control = register; } -pub fn update_stat_register(register:u8, ppu: &mut GbPpu){ +pub fn update_stat_register(register:u8, ppu: &mut GbPpu){ ppu.h_blank_interrupt_request = register & BIT_3_MASK != 0; ppu.v_blank_interrupt_request = register & BIT_4_MASK != 0; ppu.oam_search_interrupt_request = register & BIT_5_MASK != 0; @@ -23,12 +23,12 @@ pub fn update_stat_register(register:u8, ppu: &mut GbPpu){ ppu.stat_register = register & 0b111_1000; } -pub fn set_scx(ppu: &mut GbPpu, value:u8){ - ppu.background_scroll.x = value; +pub fn set_scx(ppu: &mut GbPpu, value:u8){ + ppu.bg_pos.x = value; } -pub fn set_scy(ppu:&mut GbPpu, value:u8){ - ppu.background_scroll.y = value; +pub fn set_scy(ppu:&mut GbPpu, value:u8){ + ppu.bg_pos.y = value; } pub fn handle_bg_pallet_register(register:u8, pallet:&mut [Color;4] ){ @@ -55,27 +55,27 @@ fn get_matching_color(number:u8)->Color{ }; } -pub fn handle_wy_register(register:u8, ppu:&mut GbPpu){ - ppu.window_scroll.y = register; +pub fn handle_wy_register(register:u8, ppu:&mut GbPpu){ + ppu.window_pos.y = register; } -pub fn handle_wx_register(register:u8, ppu:&mut GbPpu){ +pub fn handle_wx_register(register:u8, ppu:&mut GbPpu){ if register < WX_OFFSET{ - ppu.window_scroll.x = 0; + ppu.window_pos.x = 0; } else{ - ppu.window_scroll.x = register - WX_OFFSET; + ppu.window_pos.x = register - WX_OFFSET; } } -pub fn get_ly(ppu:&GbPpu)->u8{ - ppu.current_line_drawn +pub fn get_ly(ppu:&GbPpu)->u8{ + ppu.ly_register } -pub fn get_stat(ppu:&GbPpu)->u8{ +pub fn get_stat(ppu:&GbPpu)->u8{ ppu.stat_register } -pub fn set_lyc(ppu:&mut GbPpu, value:u8){ +pub fn set_lyc(ppu:&mut GbPpu, value:u8){ ppu.lyc_register = value; -} +} \ No newline at end of file diff --git a/lib_gb/src/ppu/sprite.rs b/lib_gb/src/ppu/sprite.rs deleted file mode 100644 index 2eeeab2e..00000000 --- a/lib_gb/src/ppu/sprite.rs +++ /dev/null @@ -1,13 +0,0 @@ -pub trait Sprite{ - fn size(&self)->u8; - fn flip_x(&mut self); - fn flip_y(&mut self); - fn get_pixel(&self, pos:u8)->u8; - fn set_pixel(&mut self, pos:u8, pixel:u8); -} - -pub fn copy_pixels(sprite:&mut dyn Sprite, index:u8, pixels:&[u8]){ - for i in 0..pixels.len(){ - sprite.set_pixel(index * 8 + i as u8, pixels[i]) ; - } -} \ No newline at end of file From 73dbb1907958b36bd51956b43bc103099bdf8dd8 Mon Sep 17 00:00:00 2001 From: alloncm Date: Mon, 6 Sep 2021 17:27:38 +0300 Subject: [PATCH 070/136] Change magic numbers into consts and fix casting Added a lot of constants for the code to make it clearer --- gb/src/main.rs | 3 +- gb/src/sdl_gfx_device.rs | 4 +-- lib_gb/src/mmu/io_components.rs | 2 +- lib_gb/src/ppu/fifo/background_fetcher.rs | 20 ++++++----- lib_gb/src/ppu/fifo/sprite_fetcher.rs | 27 ++++++++------ lib_gb/src/ppu/gb_ppu.rs | 44 +++++++++++++---------- 6 files changed, 59 insertions(+), 41 deletions(-) diff --git a/gb/src/main.rs b/gb/src/main.rs index eaccf968..2f738005 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -14,6 +14,7 @@ use sdl2::sys::*; const FPS:f64 = GB_FREQUENCY as f64 / 70224.0; const FRAME_TIME_MS:f64 = (1.0 / FPS) * 1000.0; +const SCREEN_SCALE:u8 = 4; fn init_logger(debug:bool)->Result<(), fern::InitError>{ let level = if debug {log::LevelFilter::Debug} else {log::LevelFilter::Info}; @@ -77,7 +78,7 @@ fn main() { let audio_devices = MultiAudioDevice::new(devices); - let sdl_gfx_device = sdl_gfx_device::SdlGfxDevice::new(SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32, "MagenBoy"); + let sdl_gfx_device = sdl_gfx_device::SdlGfxDevice::new(SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32, "MagenBoy", SCREEN_SCALE); let program_name = &args[1]; let mut mbc = initialize_mbc(program_name); diff --git a/gb/src/sdl_gfx_device.rs b/gb/src/sdl_gfx_device.rs index 06d1c3d2..d74ffa46 100644 --- a/gb/src/sdl_gfx_device.rs +++ b/gb/src/sdl_gfx_device.rs @@ -13,7 +13,7 @@ pub struct SdlGfxDevice{ } impl SdlGfxDevice{ - pub fn new(buffer_width:u32, buffer_height:u32, window_name:&str)->Self{ + pub fn new(buffer_width:u32, buffer_height:u32, window_name:&str, screen_scale: u8)->Self{ let cs_wnd_name = CString::new(window_name).unwrap(); let (_window, renderer, texture): (*mut SDL_Window, *mut SDL_Renderer, *mut SDL_Texture) = unsafe{ @@ -38,7 +38,7 @@ impl SdlGfxDevice{ texture, height:buffer_height, width:buffer_width, - sacle:4 + sacle:screen_scale } } diff --git a/lib_gb/src/mmu/io_components.rs b/lib_gb/src/mmu/io_components.rs index d990aed3..25a3068e 100644 --- a/lib_gb/src/mmu/io_components.rs +++ b/lib_gb/src/mmu/io_components.rs @@ -169,7 +169,7 @@ impl IoComponents{ let mut if_register = self.ports[IF_REGISTER_INDEX as usize]; self.timer.cycle(&mut if_register, cycles as u8); self.apu.cycle(cycles as u8); - self.ppu.cycle( cycles as u8, &mut if_register); + self.ppu.cycle( cycles, &mut if_register); self.ports[IF_REGISTER_INDEX as usize] = if_register; } } \ No newline at end of file diff --git a/lib_gb/src/ppu/fifo/background_fetcher.rs b/lib_gb/src/ppu/fifo/background_fetcher.rs index d9975777..fda21729 100644 --- a/lib_gb/src/ppu/fifo/background_fetcher.rs +++ b/lib_gb/src/ppu/fifo/background_fetcher.rs @@ -1,6 +1,9 @@ use crate::{mmu::vram::VRam, utils::{bit_masks::*, vec2::Vec2}}; use super::{fetcher_state_machine::FetcherStateMachine, fetching_state::*}; +const FIFO_SIZE:u8 = 8; +const SPRITE_WIDTH:u8 = 8; + pub struct BackgroundFetcher{ pub fifo:Vec, pub window_line_counter:u8, @@ -17,7 +20,7 @@ impl BackgroundFetcher{ BackgroundFetcher{ fetcher_state_machine:FetcherStateMachine::new(state_machine), current_x_pos:0, - fifo:Vec::::with_capacity(8), + fifo:Vec::::with_capacity(FIFO_SIZE as usize), window_line_counter:0, rendered_window:false, rendering_window:false, @@ -59,12 +62,12 @@ impl BackgroundFetcher{ FetchingState::FetchTileNumber=>{ let tile_num = if self.rendering_window{ let tile_map_address:u16 = if (lcd_control & BIT_6_MASK) == 0 {0x1800} else {0x1C00}; - vram.read_current_bank(tile_map_address + (32 * (self.window_line_counter as u16 / 8)) + ((self.current_x_pos - window_pos.x) as u16 / 8)) + vram.read_current_bank(tile_map_address + (32 * (self.window_line_counter as u16 / SPRITE_WIDTH as u16)) + ((self.current_x_pos - window_pos.x) as u16 / SPRITE_WIDTH as u16)) } else{ let tile_map_address = if (lcd_control & BIT_3_MASK) == 0 {0x1800} else {0x1C00}; - let scx_offset = ((bg_pos.x as u16 + self.current_x_pos as u16) / 8 ) & 31; - let scy_offset = ((bg_pos.y as u16 + ly_register as u16) & 0xFF) / 8; + let scx_offset = ((bg_pos.x as u16 + self.current_x_pos as u16) / SPRITE_WIDTH as u16 ) & 31; + let scy_offset = ((bg_pos.y as u16 + ly_register as u16) & 0xFF) / SPRITE_WIDTH as u16; vram.read_current_bank(tile_map_address + ((32 * scy_offset) + scx_offset)) }; @@ -91,13 +94,14 @@ impl BackgroundFetcher{ let high_data = self.fetcher_state_machine.data.high_tile_data.expect("State machine is corrupted, No High data on Push"); if self.fifo.is_empty(){ if lcd_control & BIT_0_MASK == 0{ - for _ in 0..8{ + for _ in 0..SPRITE_WIDTH{ + //When the baclkground is off pushes 0 self.fifo.push(0); self.current_x_pos += 1; } } else{ - for i in (0..8).rev(){ + for i in (0..SPRITE_WIDTH).rev(){ let mask = 1 << i; let mut pixel = (low_data & mask) >> i; pixel |= ((high_data & mask) >> i) << 1; @@ -117,9 +121,9 @@ impl BackgroundFetcher{ let current_tile_base_data_address = if (lcd_control & BIT_4_MASK) == 0 && (tile_num & BIT_7_MASK) == 0 {0x1000} else {0}; let current_tile_data_address = current_tile_base_data_address + (tile_num as u16 * 16); return if self.rendering_window{ - current_tile_data_address + (2 * (self.window_line_counter % 8)) as u16 + current_tile_data_address + (2 * (self.window_line_counter % SPRITE_WIDTH)) as u16 } else{ - current_tile_data_address + (2 * ((bg_pos.y as u16 + ly_register as u16) % 8)) + current_tile_data_address + (2 * ((bg_pos.y as u16 + ly_register as u16) % SPRITE_WIDTH as u16)) }; } diff --git a/lib_gb/src/ppu/fifo/sprite_fetcher.rs b/lib_gb/src/ppu/fifo/sprite_fetcher.rs index fa5989da..0ab42c23 100644 --- a/lib_gb/src/ppu/fifo/sprite_fetcher.rs +++ b/lib_gb/src/ppu/fifo/sprite_fetcher.rs @@ -2,6 +2,12 @@ use std::mem::{self, MaybeUninit}; use crate::{mmu::vram::VRam, ppu::sprite_attribute::SpriteAttribute, utils::bit_masks::{BIT_0_MASK, BIT_2_MASK}}; use super::{fetcher_state_machine::FetcherStateMachine, fetching_state::*}; +pub const NORMAL_SPRITE_HIGHT:u8 = 8; +pub const EXTENDED_SPRITE_HIGHT:u8 = 16; +pub const FIFO_SIZE:u8 = 8; +const SPRITE_WIDTH:u8 = 8; +pub const MAX_SPRITES_PER_LINE:usize = 10; + pub struct SpriteFetcher{ pub fifo:Vec<(u8, u8)>, pub oam_entries:[SpriteAttribute; 10], @@ -15,13 +21,13 @@ pub struct SpriteFetcher{ impl SpriteFetcher{ pub fn new()->Self{ let oam_entries = { - let mut data: [MaybeUninit; 10] = unsafe{MaybeUninit::uninit().assume_init()}; + let mut data: [MaybeUninit; MAX_SPRITES_PER_LINE] = unsafe{MaybeUninit::uninit().assume_init()}; for elem in &mut data[..]{ *elem = MaybeUninit::new(SpriteAttribute::new(0, 0, 0, 0)); } - unsafe{mem::transmute::<_, [SpriteAttribute;10]>(data)} + unsafe{mem::transmute::<_, [SpriteAttribute; MAX_SPRITES_PER_LINE]>(data)} }; let state_machine:[FetchingState;8] = [FetchingState::FetchTileNumber, FetchingState::FetchTileNumber, FetchingState::Sleep, FetchingState::FetchLowTile, FetchingState::Sleep, FetchingState::FetchHighTile, FetchingState::Sleep, FetchingState::Push]; @@ -31,7 +37,7 @@ impl SpriteFetcher{ current_oam_entry:0, oam_entries_len:0, oam_entries, - fifo:Vec::<(u8,u8)>::with_capacity(8), + fifo:Vec::<(u8,u8)>::with_capacity(FIFO_SIZE as usize), rendering:false, } } @@ -45,7 +51,7 @@ impl SpriteFetcher{ } pub fn fetch_pixels(&mut self, vram:&VRam, lcd_control:u8, ly_register:u8, current_x_pos:u8){ - let sprite_size = if lcd_control & BIT_2_MASK == 0 {8} else{16}; + let sprite_size = if lcd_control & BIT_2_MASK == 0 {NORMAL_SPRITE_HIGHT} else{EXTENDED_SPRITE_HIGHT}; match self.fetcher_state_machine.current_state(){ FetchingState::FetchTileNumber=>{ @@ -76,7 +82,7 @@ impl SpriteFetcher{ let skip_x = 8 - (oam_attribute.x - current_x_pos) as usize; if oam_attribute.flip_x{ - for i in (0 + skip_x)..8{ + for i in (0 + skip_x)..SPRITE_WIDTH as usize{ let mask = 1 << i; let mut pixel = (low_data & mask) >> i; pixel |= ((high_data & mask) >> i) << 1; @@ -89,15 +95,16 @@ impl SpriteFetcher{ } } else{ - for i in (0..(8 - skip_x)).rev(){ + let fifo_max_index = FIFO_SIZE as usize - 1; + for i in (0..(SPRITE_WIDTH as usize - skip_x)).rev(){ let mask = 1 << i; let mut pixel = (low_data & mask) >> i; pixel |= ((high_data & mask) >> i) << 1; - if 7 - skip_x - i >= start_x { + if fifo_max_index - skip_x - i >= start_x { self.fifo.push((pixel, self.current_oam_entry)); } - else if self.fifo[7 - skip_x - i].0 == 0{ - self.fifo[7 - skip_x - i] = (pixel, self.current_oam_entry); + else if self.fifo[fifo_max_index - skip_x - i].0 == 0{ + self.fifo[fifo_max_index - skip_x - i] = (pixel, self.current_oam_entry); } } } @@ -113,7 +120,7 @@ impl SpriteFetcher{ fn try_fetch_tile_number(&mut self, current_x_pos: u8, lcd_control: u8) { if self.oam_entries_len > self.current_oam_entry{ let oam_entry = &self.oam_entries[self.current_oam_entry as usize]; - if oam_entry.x <= current_x_pos + 8 && current_x_pos < oam_entry.x{ + if oam_entry.x <= current_x_pos + SPRITE_WIDTH && current_x_pos < oam_entry.x{ let mut tile_number = oam_entry.tile_number; if lcd_control & BIT_2_MASK != 0{ tile_number &= !BIT_0_MASK diff --git a/lib_gb/src/ppu/gb_ppu.rs b/lib_gb/src/ppu/gb_ppu.rs index 2aa77d8c..d2bd0253 100644 --- a/lib_gb/src/ppu/gb_ppu.rs +++ b/lib_gb/src/ppu/gb_ppu.rs @@ -6,12 +6,17 @@ use crate::ppu::gfx_device::GfxDevice; use crate::ppu::{ppu_state::PpuState, sprite_attribute::SpriteAttribute}; use super::fifo::background_fetcher::BackgroundFetcher; -use super::fifo::sprite_fetcher::SpriteFetcher; +use super::fifo::sprite_fetcher::*; pub const SCREEN_HEIGHT: usize = 144; pub const SCREEN_WIDTH: usize = 160; -pub struct GbPpu{ +const OAM_ENTRY_SIZE:u16 = 4; + +const OAM_SEARCH_T_CYCLES_LENGTH: u16 = 80; +const HBLANK_T_CYCLES_LENGTH: u16 = 456; +const VBLANK_T_CYCLES_LENGTH: u16 = 4560; +pub struct GbPpu{ pub vram: VRam, pub oam:[u8;0xA0], pub state:PpuState, @@ -55,7 +60,7 @@ impl GbPpu{ lcd_control: 0, bg_pos: Vec2::{x:0, y:0}, window_pos: Vec2::{x:0,y:0}, - screen_buffer:[0;160*144], + screen_buffer:[0;SCREEN_HEIGHT * SCREEN_WIDTH], bg_color_mapping:[WHITE, LIGHT_GRAY, DARK_GRAY, BLACK], obj_color_mapping0: [None, Some(LIGHT_GRAY), Some(DARK_GRAY), Some(BLACK)], obj_color_mapping1: [None, Some(LIGHT_GRAY), Some(DARK_GRAY), Some(BLACK)], @@ -97,7 +102,7 @@ impl GbPpu{ self.state = PpuState::OamSearch; } - pub fn cycle(&mut self, m_cycles:u8, if_register:&mut u8){ + pub fn cycle(&mut self, m_cycles:u32, if_register:&mut u8){ if self.lcd_control & BIT_7_MASK == 0{ return; } @@ -142,18 +147,18 @@ impl GbPpu{ self.trigger_stat_interrupt = false; } - fn cycle_fetcher(&mut self, m_cycles:u8, if_register:&mut u8){ - let sprite_height = if (self.lcd_control & BIT_2_MASK) != 0 {16} else {8}; + fn cycle_fetcher(&mut self, m_cycles:u32, if_register:&mut u8){ + let sprite_height = if (self.lcd_control & BIT_2_MASK) != 0 {EXTENDED_SPRITE_HIGHT} else {NORMAL_SPRITE_HIGHT}; for _ in 0..m_cycles * 2{ match self.state{ PpuState::OamSearch=>{ let oam_index = self.t_cycles_passed / 2; - let oam_entry_address = (oam_index * 4) as usize; + let oam_entry_address = (oam_index * OAM_ENTRY_SIZE) as usize; let end_y = self.oam[oam_entry_address]; let end_x = self.oam[oam_entry_address + 1]; - if end_x > 0 && self.ly_register + 16 >= end_y && self.ly_register + 16 < end_y + sprite_height && self.sprite_fetcher.oam_entries_len < 10{ + if end_x > 0 && self.ly_register + 16 >= end_y && self.ly_register + 16 < end_y + sprite_height && self.sprite_fetcher.oam_entries_len < MAX_SPRITES_PER_LINE as u8{ let tile_number = self.oam[oam_entry_address + 2]; let attributes = self.oam[oam_entry_address + 3]; self.sprite_fetcher.oam_entries[self.sprite_fetcher.oam_entries_len as usize] = SpriteAttribute::new(end_y, end_x, tile_number, attributes); @@ -162,7 +167,7 @@ impl GbPpu{ self.t_cycles_passed += 2; //half a m_cycle - if self.t_cycles_passed == 80{ + if self.t_cycles_passed == OAM_SEARCH_T_CYCLES_LENGTH{ let slice = self.sprite_fetcher.oam_entries[0..self.sprite_fetcher.oam_entries_len as usize].as_mut(); slice.sort_by(|s1:&SpriteAttribute, s2:&SpriteAttribute| s1.x.cmp(&s2.x)); self.state = PpuState::PixelTransfer; @@ -172,8 +177,11 @@ impl GbPpu{ PpuState::Hblank=>{ self.t_cycles_passed += 2; - if self.t_cycles_passed == 456{ - if self.ly_register == 143{ + if self.t_cycles_passed == HBLANK_T_CYCLES_LENGTH{ + self.pixel_x_pos = 0; + self.t_cycles_passed = 0; + self.ly_register += 1; + if self.ly_register == SCREEN_HEIGHT as u8{ self.state = PpuState::Vblank; //reseting the window counter on vblank self.bg_fetcher.window_line_counter = 0; @@ -188,13 +196,10 @@ impl GbPpu{ self.trigger_stat_interrupt = true; } } - self.pixel_x_pos = 0; - self.t_cycles_passed = 0; - self.ly_register += 1; } } PpuState::Vblank=>{ - if self.t_cycles_passed == 4560{ + if self.t_cycles_passed == VBLANK_T_CYCLES_LENGTH{ self.state = PpuState::OamSearch; if self.oam_search_interrupt_request{ self.trigger_stat_interrupt = true; @@ -204,14 +209,15 @@ impl GbPpu{ self.ly_register = 0; } else{ - self.ly_register = 144 + (self.t_cycles_passed / 456) as u8; + //VBlank is technically 10 HBlank combined + self.ly_register = SCREEN_HEIGHT as u8 + (self.t_cycles_passed / HBLANK_T_CYCLES_LENGTH) as u8; } self.t_cycles_passed += 2; } PpuState::PixelTransfer=>{ for _ in 0..2{ - if self.pixel_x_pos < 160{ + if self.pixel_x_pos < SCREEN_WIDTH as u8{ if self.lcd_control & BIT_1_MASK != 0{ self.sprite_fetcher.fetch_pixels(&self.vram, self.lcd_control, self.ly_register, self.pixel_x_pos); } @@ -221,7 +227,7 @@ impl GbPpu{ else{ self.bg_fetcher.fetch_pixels(&self.vram, self.lcd_control, self.ly_register, &self.window_pos, &self.bg_pos); self.try_push_to_lcd(); - if self.pixel_x_pos == 160 { + if self.pixel_x_pos == SCREEN_WIDTH as u8{ self.state = PpuState::Hblank; if self.h_blank_interrupt_request{ self.trigger_stat_interrupt = true; @@ -250,7 +256,7 @@ impl GbPpu{ if !self.scanline_started{ // discard the next pixel in the bg fifo // the bg fifo should start with 8 pixels and not push more untill its empty again - if 8 - self.bg_fetcher.fifo.len() >= self.bg_pos.x as usize % 8{ + if FIFO_SIZE as usize - self.bg_fetcher.fifo.len() >= self.bg_pos.x as usize % FIFO_SIZE as usize{ self.scanline_started = true; } else{ From d2fe771393ecb45c7c70b134775286fda7383c02 Mon Sep 17 00:00:00 2001 From: alloncm Date: Mon, 6 Sep 2021 19:11:33 +0300 Subject: [PATCH 071/136] Implemented the fifo DS + some tests Add some tests (not fully coverage) the code still dont works --- lib_gb/src/lib.rs | 2 +- lib_gb/src/ppu/fifo/background_fetcher.rs | 13 ++--- lib_gb/src/ppu/fifo/mod.rs | 5 +- lib_gb/src/ppu/fifo/sprite_fetcher.rs | 22 ++------ lib_gb/src/ppu/gb_ppu.rs | 12 ++-- lib_gb/src/utils/fixed_size_queue.rs | 68 +++++++++++++++++++++++ lib_gb/src/utils/mod.rs | 32 ++++++++++- lib_gb/tests/fixed_size_queue_tests.rs | 53 ++++++++++++++++++ 8 files changed, 174 insertions(+), 33 deletions(-) create mode 100644 lib_gb/src/utils/fixed_size_queue.rs create mode 100644 lib_gb/tests/fixed_size_queue_tests.rs diff --git a/lib_gb/src/lib.rs b/lib_gb/src/lib.rs index f32f9f39..40a90b86 100644 --- a/lib_gb/src/lib.rs +++ b/lib_gb/src/lib.rs @@ -5,6 +5,6 @@ pub mod mmu; pub mod keypad; pub mod apu; pub mod timer; +pub mod utils; -mod utils; pub use utils::GB_FREQUENCY; \ No newline at end of file diff --git a/lib_gb/src/ppu/fifo/background_fetcher.rs b/lib_gb/src/ppu/fifo/background_fetcher.rs index fda21729..9b9255f0 100644 --- a/lib_gb/src/ppu/fifo/background_fetcher.rs +++ b/lib_gb/src/ppu/fifo/background_fetcher.rs @@ -1,11 +1,8 @@ -use crate::{mmu::vram::VRam, utils::{bit_masks::*, vec2::Vec2}}; -use super::{fetcher_state_machine::FetcherStateMachine, fetching_state::*}; - -const FIFO_SIZE:u8 = 8; -const SPRITE_WIDTH:u8 = 8; +use crate::{mmu::vram::VRam, utils::{bit_masks::*, fixed_size_queue::FixedSizeQueue, vec2::Vec2}}; +use super::{FIFO_SIZE, SPRITE_WIDTH, fetcher_state_machine::FetcherStateMachine, fetching_state::*}; pub struct BackgroundFetcher{ - pub fifo:Vec, + pub fifo:FixedSizeQueue, pub window_line_counter:u8, current_x_pos:u8, @@ -20,7 +17,7 @@ impl BackgroundFetcher{ BackgroundFetcher{ fetcher_state_machine:FetcherStateMachine::new(state_machine), current_x_pos:0, - fifo:Vec::::with_capacity(FIFO_SIZE as usize), + fifo:FixedSizeQueue::::new(), window_line_counter:0, rendered_window:false, rendering_window:false, @@ -92,7 +89,7 @@ impl BackgroundFetcher{ FetchingState::Push=>{ let low_data = self.fetcher_state_machine.data.low_tile_data.expect("State machine is corrupted, No Low data on Push"); let high_data = self.fetcher_state_machine.data.high_tile_data.expect("State machine is corrupted, No High data on Push"); - if self.fifo.is_empty(){ + if self.fifo.len() == 0{ if lcd_control & BIT_0_MASK == 0{ for _ in 0..SPRITE_WIDTH{ //When the baclkground is off pushes 0 diff --git a/lib_gb/src/ppu/fifo/mod.rs b/lib_gb/src/ppu/fifo/mod.rs index 361dc6f5..cd122215 100644 --- a/lib_gb/src/ppu/fifo/mod.rs +++ b/lib_gb/src/ppu/fifo/mod.rs @@ -1,4 +1,7 @@ pub mod background_fetcher; pub mod sprite_fetcher; mod fetching_state; -mod fetcher_state_machine; \ No newline at end of file +mod fetcher_state_machine; + +pub const FIFO_SIZE:usize = 8; +pub const SPRITE_WIDTH:u8 = 8; \ No newline at end of file diff --git a/lib_gb/src/ppu/fifo/sprite_fetcher.rs b/lib_gb/src/ppu/fifo/sprite_fetcher.rs index 0ab42c23..4c39965c 100644 --- a/lib_gb/src/ppu/fifo/sprite_fetcher.rs +++ b/lib_gb/src/ppu/fifo/sprite_fetcher.rs @@ -1,15 +1,13 @@ -use std::mem::{self, MaybeUninit}; -use crate::{mmu::vram::VRam, ppu::sprite_attribute::SpriteAttribute, utils::bit_masks::{BIT_0_MASK, BIT_2_MASK}}; -use super::{fetcher_state_machine::FetcherStateMachine, fetching_state::*}; +use crate::{mmu::vram::VRam, ppu::sprite_attribute::SpriteAttribute, utils::{self, bit_masks::{BIT_0_MASK, BIT_2_MASK}, fixed_size_queue::FixedSizeQueue}}; +use super::{FIFO_SIZE, SPRITE_WIDTH, fetcher_state_machine::FetcherStateMachine, fetching_state::*}; pub const NORMAL_SPRITE_HIGHT:u8 = 8; pub const EXTENDED_SPRITE_HIGHT:u8 = 16; -pub const FIFO_SIZE:u8 = 8; -const SPRITE_WIDTH:u8 = 8; + pub const MAX_SPRITES_PER_LINE:usize = 10; pub struct SpriteFetcher{ - pub fifo:Vec<(u8, u8)>, + pub fifo:FixedSizeQueue<(u8, u8), FIFO_SIZE>, pub oam_entries:[SpriteAttribute; 10], pub oam_entries_len:u8, pub rendering:bool, @@ -20,15 +18,7 @@ pub struct SpriteFetcher{ impl SpriteFetcher{ pub fn new()->Self{ - let oam_entries = { - let mut data: [MaybeUninit; MAX_SPRITES_PER_LINE] = unsafe{MaybeUninit::uninit().assume_init()}; - - for elem in &mut data[..]{ - *elem = MaybeUninit::new(SpriteAttribute::new(0, 0, 0, 0)); - } - - unsafe{mem::transmute::<_, [SpriteAttribute; MAX_SPRITES_PER_LINE]>(data)} - }; + let oam_entries:[SpriteAttribute; MAX_SPRITES_PER_LINE] = utils::create_array(|| SpriteAttribute::new(0,0,0,0)); let state_machine:[FetchingState;8] = [FetchingState::FetchTileNumber, FetchingState::FetchTileNumber, FetchingState::Sleep, FetchingState::FetchLowTile, FetchingState::Sleep, FetchingState::FetchHighTile, FetchingState::Sleep, FetchingState::Push]; @@ -37,7 +27,7 @@ impl SpriteFetcher{ current_oam_entry:0, oam_entries_len:0, oam_entries, - fifo:Vec::<(u8,u8)>::with_capacity(FIFO_SIZE as usize), + fifo:FixedSizeQueue::<(u8,u8), 8>::new(), rendering:false, } } diff --git a/lib_gb/src/ppu/gb_ppu.rs b/lib_gb/src/ppu/gb_ppu.rs index d2bd0253..8f6e3af9 100644 --- a/lib_gb/src/ppu/gb_ppu.rs +++ b/lib_gb/src/ppu/gb_ppu.rs @@ -6,7 +6,7 @@ use crate::ppu::gfx_device::GfxDevice; use crate::ppu::{ppu_state::PpuState, sprite_attribute::SpriteAttribute}; use super::fifo::background_fetcher::BackgroundFetcher; -use super::fifo::sprite_fetcher::*; +use super::fifo::{FIFO_SIZE, sprite_fetcher::*}; pub const SCREEN_HEIGHT: usize = 144; pub const SCREEN_WIDTH: usize = 160; @@ -252,7 +252,7 @@ impl GbPpu{ } fn try_push_to_lcd(&mut self){ - if !self.bg_fetcher.fifo.is_empty(){ + if !self.bg_fetcher.fifo.len() == 0{ if !self.scanline_started{ // discard the next pixel in the bg fifo // the bg fifo should start with 8 pixels and not push more untill its empty again @@ -260,15 +260,15 @@ impl GbPpu{ self.scanline_started = true; } else{ - self.bg_fetcher.fifo.remove(0); + self.bg_fetcher.fifo.remove(); return; } } - let bg_pixel_color_num = self.bg_fetcher.fifo.remove(0); + let bg_pixel_color_num = self.bg_fetcher.fifo.remove(); let bg_pixel = self.bg_color_mapping[bg_pixel_color_num as usize]; - let pixel = if !self.sprite_fetcher.fifo.is_empty(){ - let sprite_color_num = self.sprite_fetcher.fifo.remove(0); + let pixel = if !(self.sprite_fetcher.fifo.len() == 0){ + let sprite_color_num = self.sprite_fetcher.fifo.remove(); let pixel_oam_attribute = &self.sprite_fetcher.oam_entries[sprite_color_num.1 as usize]; if sprite_color_num.0 == 0 || (pixel_oam_attribute.is_bg_priority && bg_pixel_color_num != 0){ diff --git a/lib_gb/src/utils/fixed_size_queue.rs b/lib_gb/src/utils/fixed_size_queue.rs new file mode 100644 index 00000000..ef8a0760 --- /dev/null +++ b/lib_gb/src/utils/fixed_size_queue.rs @@ -0,0 +1,68 @@ +use super::create_default_array; + +pub struct FixedSizeQueue{ + data: [T;SIZE], + length: usize, +} + +impl FixedSizeQueue{ + pub fn new()->Self{ + Self{ + data:create_default_array(), + length:0, + } + } + + pub fn push(&mut self, t:T){ + if self.length < SIZE{ + self.data[self.length] = t; + self.length += 1; + } + else{ + std::panic!("queue is already full, size: {}", SIZE); + } + } + + pub fn remove(&mut self)->T{ + if self.length > 0{ + let t = self.data[0]; + for i in 1..self.length{ + self.data[i - 1] = self.data[i]; + } + self.length -= 1; + return t; + } + + std::panic!("The fifo is empty"); + } + + pub fn clear(&mut self){ + self.length = 0; + } + + pub fn len(&self)->usize{ + self.length + } +} + +impl std::ops::Index for FixedSizeQueue{ + type Output = T; + + fn index(&self, index: usize) -> &Self::Output { + if index < self.length{ + return &self.data[index]; + } + + std::panic!("Index is out of range"); + } +} + +impl std::ops::IndexMut for FixedSizeQueue{ + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + if index < self.length{ + return &mut self.data[index]; + } + + std::panic!("Index is out of range"); + } +} \ No newline at end of file diff --git a/lib_gb/src/utils/mod.rs b/lib_gb/src/utils/mod.rs index ef58f315..f6a7bc27 100644 --- a/lib_gb/src/utils/mod.rs +++ b/lib_gb/src/utils/mod.rs @@ -1,5 +1,35 @@ +use std::mem::MaybeUninit; + pub mod vec2; pub mod memory_registers; pub mod bit_masks; +pub mod fixed_size_queue; + +pub const GB_FREQUENCY:u32 = 4_194_304; + +pub fn create_default_array()->[T;SIZE]{ + let mut data: [MaybeUninit; SIZE] = unsafe{MaybeUninit::uninit().assume_init()}; + + for elem in &mut data[..]{ + *elem = MaybeUninit::new(T::default()); + } + unsafe{ + let casted_data = std::ptr::read(&data as *const [MaybeUninit;SIZE] as *const [T;SIZE]); + std::mem::forget(data); + // std::mem::forget(data); + return casted_data; + } +} +pub fn create_arrayT,const SIZE:usize>(func:F)->[T;SIZE]{ + let mut data: [MaybeUninit; SIZE] = unsafe{MaybeUninit::uninit().assume_init()}; -pub const GB_FREQUENCY:u32 = 4_194_304; \ No newline at end of file + for elem in &mut data[..]{ + *elem = MaybeUninit::new(func()); + } + unsafe{ + let casted_data = std::ptr::read(&data as *const [MaybeUninit;SIZE] as *const [T;SIZE]); + std::mem::forget(data); + // std::mem::forget(data); + return casted_data; + } +} \ No newline at end of file diff --git a/lib_gb/tests/fixed_size_queue_tests.rs b/lib_gb/tests/fixed_size_queue_tests.rs new file mode 100644 index 00000000..3e2e7391 --- /dev/null +++ b/lib_gb/tests/fixed_size_queue_tests.rs @@ -0,0 +1,53 @@ +use lib_gb::utils::fixed_size_queue::FixedSizeQueue; + +#[test] +fn test_fifo(){ + let mut fifo = FixedSizeQueue::::new(); + fifo.push(10); + fifo.push(22); + + assert_eq!(fifo.len(), 2); + assert_eq!(fifo[0], 10); + assert_eq!(fifo[1], 22); + + fifo.remove(); + assert_eq!(fifo.len(), 1); + assert_eq!(fifo[0], 22); + + fifo[0] = 21; + assert_eq!(fifo[0], 21); +} + +#[test] +#[should_panic] +fn panic_on_fifo_full(){ + let mut fifo = FixedSizeQueue::::new(); + fifo.push(1); + fifo.push(2); + fifo.push(3); + + //should panic + fifo.push(1); +} + +#[test] +#[should_panic] +fn panic_on_get_fifo_index_out_of_range(){ + let mut fifo = FixedSizeQueue::::new(); + fifo.push(1); + fifo.push(2); + + //should panic + let _ = fifo[2]; +} + +#[test] +#[should_panic] +fn panic_on_fifo_set_index_out_of_range(){ + let mut fifo = FixedSizeQueue::::new(); + fifo.push(1); + fifo.push(2); + + //should panic + fifo[2] = 4; +} \ No newline at end of file From cc472ce5a24533e46225bbc464accfdad17e9b56 Mon Sep 17 00:00:00 2001 From: alloncm Date: Wed, 8 Sep 2021 16:38:36 +0300 Subject: [PATCH 072/136] Clean the create array util --- lib_gb/src/utils/mod.rs | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/lib_gb/src/utils/mod.rs b/lib_gb/src/utils/mod.rs index f6a7bc27..0dc5325e 100644 --- a/lib_gb/src/utils/mod.rs +++ b/lib_gb/src/utils/mod.rs @@ -8,18 +8,9 @@ pub mod fixed_size_queue; pub const GB_FREQUENCY:u32 = 4_194_304; pub fn create_default_array()->[T;SIZE]{ - let mut data: [MaybeUninit; SIZE] = unsafe{MaybeUninit::uninit().assume_init()}; - - for elem in &mut data[..]{ - *elem = MaybeUninit::new(T::default()); - } - unsafe{ - let casted_data = std::ptr::read(&data as *const [MaybeUninit;SIZE] as *const [T;SIZE]); - std::mem::forget(data); - // std::mem::forget(data); - return casted_data; - } + create_array(||T::default()) } + pub fn create_arrayT,const SIZE:usize>(func:F)->[T;SIZE]{ let mut data: [MaybeUninit; SIZE] = unsafe{MaybeUninit::uninit().assume_init()}; @@ -29,7 +20,6 @@ pub fn create_arrayT,const SIZE:usize>(func:F)->[T;SIZE]{ unsafe{ let casted_data = std::ptr::read(&data as *const [MaybeUninit;SIZE] as *const [T;SIZE]); std::mem::forget(data); - // std::mem::forget(data); return casted_data; } } \ No newline at end of file From 4fec856f430fbb244625d375a447beea9f5258a7 Mon Sep 17 00:00:00 2001 From: alloncm Date: Thu, 9 Sep 2021 15:00:06 +0300 Subject: [PATCH 073/136] Fix this weird bug where the image is courupted In rust ! operator will bitwise not integers, I forgot to add a proper pranthesis and this broke the ppu --- lib_gb/src/ppu/gb_ppu.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_gb/src/ppu/gb_ppu.rs b/lib_gb/src/ppu/gb_ppu.rs index 8f6e3af9..15b89ecb 100644 --- a/lib_gb/src/ppu/gb_ppu.rs +++ b/lib_gb/src/ppu/gb_ppu.rs @@ -252,7 +252,7 @@ impl GbPpu{ } fn try_push_to_lcd(&mut self){ - if !self.bg_fetcher.fifo.len() == 0{ + if !(self.bg_fetcher.fifo.len() == 0){ if !self.scanline_started{ // discard the next pixel in the bg fifo // the bg fifo should start with 8 pixels and not push more untill its empty again From 4142854e24f387837531c24db948eb419dc9ff52 Mon Sep 17 00:00:00 2001 From: alloncm Date: Thu, 9 Sep 2021 18:06:35 +0300 Subject: [PATCH 074/136] Fix the window rendering, passing the turtle tests Implement the window rendering correctly: - handle the LY==WY case - increment the internal window counter correctly (only when LY is greater than WY) - Some more fixes (reseting vars on vblank etc) --- lib_gb/src/ppu/fifo/background_fetcher.rs | 13 ++++++++----- lib_gb/src/ppu/gb_ppu.rs | 5 ++++- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/lib_gb/src/ppu/fifo/background_fetcher.rs b/lib_gb/src/ppu/fifo/background_fetcher.rs index 9b9255f0..21a6e0ba 100644 --- a/lib_gb/src/ppu/fifo/background_fetcher.rs +++ b/lib_gb/src/ppu/fifo/background_fetcher.rs @@ -4,6 +4,7 @@ use super::{FIFO_SIZE, SPRITE_WIDTH, fetcher_state_machine::FetcherStateMachine, pub struct BackgroundFetcher{ pub fifo:FixedSizeQueue, pub window_line_counter:u8, + pub has_wy_reached_ly:bool, current_x_pos:u8, rendered_window:bool, @@ -21,6 +22,7 @@ impl BackgroundFetcher{ window_line_counter:0, rendered_window:false, rendering_window:false, + has_wy_reached_ly:false, } } @@ -36,15 +38,16 @@ impl BackgroundFetcher{ self.fetcher_state_machine.reset(); } - pub fn try_increment_window_counter(&mut self){ - if self.rendered_window{ + pub fn try_increment_window_counter(&mut self, ly_register:u8, wy_register:u8){ + if self.rendered_window && ly_register >= wy_register{ self.window_line_counter += 1; } } pub fn fetch_pixels(&mut self, vram:&VRam, lcd_control:u8, ly_register:u8, window_pos:&Vec2, bg_pos:&Vec2){ + self.has_wy_reached_ly = self.has_wy_reached_ly || ly_register == window_pos.y; let last_rendering_status = self.rendering_window; - self.rendering_window = self.is_rendering_wnd(lcd_control, window_pos, ly_register); + self.rendering_window = self.is_rendering_wnd(lcd_control, window_pos); if self.rendering_window{ self.rendered_window = true; @@ -124,7 +127,7 @@ impl BackgroundFetcher{ }; } - fn is_rendering_wnd(&self, lcd_control:u8, window_pos:&Vec2, ly_register:u8)->bool{ - window_pos.x <= self.current_x_pos && window_pos.y <= ly_register && (lcd_control & BIT_5_MASK) != 0 + fn is_rendering_wnd(&self, lcd_control:u8, window_pos:&Vec2)->bool{ + window_pos.x <= self.current_x_pos && self.has_wy_reached_ly && (lcd_control & BIT_5_MASK) != 0 } } \ No newline at end of file diff --git a/lib_gb/src/ppu/gb_ppu.rs b/lib_gb/src/ppu/gb_ppu.rs index 15b89ecb..cc6607fe 100644 --- a/lib_gb/src/ppu/gb_ppu.rs +++ b/lib_gb/src/ppu/gb_ppu.rs @@ -93,6 +93,8 @@ impl GbPpu{ self.ly_register = 0; self.stat_triggered = false; self.trigger_stat_interrupt = false; + self.bg_fetcher.has_wy_reached_ly = false; + self.bg_fetcher.window_line_counter = 0; self.bg_fetcher.reset(); self.sprite_fetcher.reset(); self.pixel_x_pos = 0; @@ -185,6 +187,7 @@ impl GbPpu{ self.state = PpuState::Vblank; //reseting the window counter on vblank self.bg_fetcher.window_line_counter = 0; + self.bg_fetcher.has_wy_reached_ly = false; *if_register |= BIT_0_MASK; if self.v_blank_interrupt_request{ self.trigger_stat_interrupt = true; @@ -232,7 +235,7 @@ impl GbPpu{ if self.h_blank_interrupt_request{ self.trigger_stat_interrupt = true; } - self.bg_fetcher.try_increment_window_counter(); + self.bg_fetcher.try_increment_window_counter(self.ly_register, self.window_pos.y); self.bg_fetcher.reset(); self.sprite_fetcher.reset(); From b4347cd697f8ce9e8fd797af91d31f5481ef13d7 Mon Sep 17 00:00:00 2001 From: alloncm Date: Thu, 9 Sep 2021 23:47:18 +0300 Subject: [PATCH 075/136] SCR - New const - Remove redundant field - arrange code --- lib_gb/src/ppu/fifo/background_fetcher.rs | 18 ++++++------------ lib_gb/src/ppu/fifo/sprite_fetcher.rs | 18 +++++++++--------- lib_gb/src/ppu/gb_ppu.rs | 5 +++-- 3 files changed, 18 insertions(+), 23 deletions(-) diff --git a/lib_gb/src/ppu/fifo/background_fetcher.rs b/lib_gb/src/ppu/fifo/background_fetcher.rs index 21a6e0ba..42e633c6 100644 --- a/lib_gb/src/ppu/fifo/background_fetcher.rs +++ b/lib_gb/src/ppu/fifo/background_fetcher.rs @@ -7,7 +7,6 @@ pub struct BackgroundFetcher{ pub has_wy_reached_ly:bool, current_x_pos:u8, - rendered_window:bool, rendering_window:bool, fetcher_state_machine:FetcherStateMachine, } @@ -20,7 +19,6 @@ impl BackgroundFetcher{ current_x_pos:0, fifo:FixedSizeQueue::::new(), window_line_counter:0, - rendered_window:false, rendering_window:false, has_wy_reached_ly:false, } @@ -30,7 +28,6 @@ impl BackgroundFetcher{ self.fifo.clear(); self.current_x_pos = 0; self.fetcher_state_machine.reset(); - self.rendered_window = false; self.rendering_window = false; } @@ -39,7 +36,7 @@ impl BackgroundFetcher{ } pub fn try_increment_window_counter(&mut self, ly_register:u8, wy_register:u8){ - if self.rendered_window && ly_register >= wy_register{ + if self.rendering_window && ly_register >= wy_register{ self.window_line_counter += 1; } } @@ -48,14 +45,11 @@ impl BackgroundFetcher{ self.has_wy_reached_ly = self.has_wy_reached_ly || ly_register == window_pos.y; let last_rendering_status = self.rendering_window; self.rendering_window = self.is_rendering_wnd(lcd_control, window_pos); - if self.rendering_window{ - self.rendered_window = true; - - // In case I was rendering a background pixel need to reset the state of the fectcher - // (and maybe clear the fifo but right now Im not doing it since im not sure what about the current_x_pos var) - if !last_rendering_status{ - self.fetcher_state_machine.reset(); - } + + // In case I was rendering a background pixel need to reset the state of the fetcher + // (and maybe clear the fifo but right now Im not doing it since im not sure what about the current_x_pos var) + if self.rendering_window && !last_rendering_status{ + self.fetcher_state_machine.reset(); } match self.fetcher_state_machine.current_state(){ diff --git a/lib_gb/src/ppu/fifo/sprite_fetcher.rs b/lib_gb/src/ppu/fifo/sprite_fetcher.rs index 4c39965c..fe6a3625 100644 --- a/lib_gb/src/ppu/fifo/sprite_fetcher.rs +++ b/lib_gb/src/ppu/fifo/sprite_fetcher.rs @@ -3,7 +3,6 @@ use super::{FIFO_SIZE, SPRITE_WIDTH, fetcher_state_machine::FetcherStateMachine, pub const NORMAL_SPRITE_HIGHT:u8 = 8; pub const EXTENDED_SPRITE_HIGHT:u8 = 16; - pub const MAX_SPRITES_PER_LINE:usize = 10; pub struct SpriteFetcher{ @@ -19,7 +18,6 @@ pub struct SpriteFetcher{ impl SpriteFetcher{ pub fn new()->Self{ let oam_entries:[SpriteAttribute; MAX_SPRITES_PER_LINE] = utils::create_array(|| SpriteAttribute::new(0,0,0,0)); - let state_machine:[FetchingState;8] = [FetchingState::FetchTileNumber, FetchingState::FetchTileNumber, FetchingState::Sleep, FetchingState::FetchLowTile, FetchingState::Sleep, FetchingState::FetchHighTile, FetchingState::Sleep, FetchingState::Push]; SpriteFetcher{ @@ -68,14 +66,11 @@ impl SpriteFetcher{ let high_data = self.fetcher_state_machine.data.high_tile_data.expect("State machine is corrupted, No High data on Push"); let oam_attribute = &self.oam_entries[self.current_oam_entry as usize]; let start_x = self.fifo.len(); - let skip_x = 8 - (oam_attribute.x - current_x_pos) as usize; if oam_attribute.flip_x{ for i in (0 + skip_x)..SPRITE_WIDTH as usize{ - let mask = 1 << i; - let mut pixel = (low_data & mask) >> i; - pixel |= ((high_data & mask) >> i) << 1; + let pixel = Self::get_decoded_pixel(i, low_data, high_data); if i + skip_x >= start_x { self.fifo.push((pixel, self.current_oam_entry)); } @@ -87,9 +82,7 @@ impl SpriteFetcher{ else{ let fifo_max_index = FIFO_SIZE as usize - 1; for i in (0..(SPRITE_WIDTH as usize - skip_x)).rev(){ - let mask = 1 << i; - let mut pixel = (low_data & mask) >> i; - pixel |= ((high_data & mask) >> i) << 1; + let pixel = Self::get_decoded_pixel(i, low_data, high_data); if fifo_max_index - skip_x - i >= start_x { self.fifo.push((pixel, self.current_oam_entry)); } @@ -137,4 +130,11 @@ impl SpriteFetcher{ tile_num as u16 * 16 + (2 * (16 - (sprite_attrib.y - ly_register))) as u16 }; } + + fn get_decoded_pixel(index: usize, low_data: u8, high_data: u8) -> u8 { + let mask = 1 << index; + let mut pixel = (low_data & mask) >> index; + pixel |= ((high_data & mask) >> index) << 1; + pixel + } } \ No newline at end of file diff --git a/lib_gb/src/ppu/gb_ppu.rs b/lib_gb/src/ppu/gb_ppu.rs index cc6607fe..06758648 100644 --- a/lib_gb/src/ppu/gb_ppu.rs +++ b/lib_gb/src/ppu/gb_ppu.rs @@ -11,6 +11,7 @@ use super::fifo::{FIFO_SIZE, sprite_fetcher::*}; pub const SCREEN_HEIGHT: usize = 144; pub const SCREEN_WIDTH: usize = 160; const OAM_ENTRY_SIZE:u16 = 4; +const OAM_MEMORY_SIZE:usize = 0xA0; const OAM_SEARCH_T_CYCLES_LENGTH: u16 = 80; const HBLANK_T_CYCLES_LENGTH: u16 = 456; @@ -18,7 +19,7 @@ const VBLANK_T_CYCLES_LENGTH: u16 = 4560; pub struct GbPpu{ pub vram: VRam, - pub oam:[u8;0xA0], + pub oam:[u8;OAM_MEMORY_SIZE], pub state:PpuState, pub lcd_control:u8, pub stat_register:u8, @@ -54,7 +55,7 @@ impl GbPpu{ Self{ gfx_device: device, vram: VRam::default(), - oam: [0;0xA0], + oam: [0;OAM_MEMORY_SIZE], stat_register: 0, lyc_register: 0, lcd_control: 0, From fd229f81570c3148582b4717d573c42d97429b42 Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 10 Sep 2021 00:15:46 +0300 Subject: [PATCH 076/136] starting to implement integration test This will run as part of the ci cd --- gb/src/mbc_handler.rs | 8 +----- lib_gb/src/machine/mbc_initializer.rs | 7 ++++- lib_gb/tests/calculate_hashes.rs | 41 +++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 8 deletions(-) create mode 100644 lib_gb/tests/calculate_hashes.rs diff --git a/gb/src/mbc_handler.rs b/gb/src/mbc_handler.rs index c7edc901..31619eb9 100644 --- a/gb/src/mbc_handler.rs +++ b/gb/src/mbc_handler.rs @@ -3,7 +3,6 @@ use std::boxed::Box; use std::fs; use log::info; -const CARTRIDGE_TYPE_ADDRESS:usize = 0x147; const PROGRAM_SUFFIX:&str = ".gb"; pub const SAVE_SUFFIX:&str = ".sav"; @@ -12,14 +11,9 @@ pub fn initialize_mbc(program_name:&String)->Box{ let program_path = format!("{}{}",program_name,PROGRAM_SUFFIX); let error_message = format!("No program found, notice that the file must have a `.gb` suffix - {}\n", program_name); let program = fs::read(program_path).expect(error_message.as_str()); - - let mbc_type = program[CARTRIDGE_TYPE_ADDRESS]; - - info!("initializing cartridge of type: {:#X}", mbc_type); - let save_data = try_get_save_data(program_name); - return lib_gb::machine::mbc_initializer::initialize_mbc(mbc_type, program, save_data); + return lib_gb::machine::mbc_initializer::initialize_mbc(program, save_data); } fn try_get_save_data(name:&String)->Option>{ diff --git a/lib_gb/src/machine/mbc_initializer.rs b/lib_gb/src/machine/mbc_initializer.rs index d2dd9a51..22c9fce8 100644 --- a/lib_gb/src/machine/mbc_initializer.rs +++ b/lib_gb/src/machine/mbc_initializer.rs @@ -1,6 +1,11 @@ use crate::mmu::carts::*; -pub fn initialize_mbc(mbc_type:u8, program:Vec, save_data:Option>)->Box{ +const CARTRIDGE_TYPE_ADDRESS:usize = 0x147; + +pub fn initialize_mbc(program:Vec, save_data:Option>)->Box{ + let mbc_type = program[CARTRIDGE_TYPE_ADDRESS]; + log::info!("initializing cartridge of type: {:#X}", mbc_type); + match mbc_type{ 0x0|0x8=>Box::new(Rom::new(program,false, None)), 0x9=>Box::new(Rom::new(program, true, save_data)), diff --git a/lib_gb/tests/calculate_hashes.rs b/lib_gb/tests/calculate_hashes.rs new file mode 100644 index 00000000..cb46bab9 --- /dev/null +++ b/lib_gb/tests/calculate_hashes.rs @@ -0,0 +1,41 @@ +use std::collections::hash_map::DefaultHasher; +use std::hash::{Hash, Hasher}; +use lib_gb::machine::gameboy::GameBoy; +use lib_gb::machine::mbc_initializer::initialize_mbc; +use lib_gb::ppu::gfx_device::GfxDevice; +use lib_gb::apu::audio_device::AudioDevice; +use lib_gb::keypad::joypad_provider::JoypadProvider; + +struct StubGfxDevice; +impl GfxDevice for StubGfxDevice{ + fn swap_buffer(&self, buffer:&[u32]) { + let mut s = DefaultHasher::new(); + buffer.hash(&mut s); + println!("{}",s.finish()); + std::thread::sleep(std::time::Duration::from_secs(2)); + // std::process::exit(0); + } +} + +struct StubAudioDevice; +impl AudioDevice for StubAudioDevice{ + fn push_buffer(&mut self, _buffer:&[lib_gb::apu::audio_device::Sample]) {} +} + +struct StubJoypadProvider; +impl JoypadProvider for StubJoypadProvider{ + fn provide(&mut self, _joypad:&mut lib_gb::keypad::joypad::Joypad) {} +} + + +#[test] +fn calc_hash(){ + let program = std::fs::read("C:/Users/Alon/Downloads/GameBoy/window_y_trigger.gb") + .expect("Error could not find file"); + let mut mbc = initialize_mbc(program, None); + + let mut gameboy = GameBoy::new(&mut mbc, StubJoypadProvider{}, StubAudioDevice{}, StubGfxDevice{}); + + loop {gameboy.cycle_frame();} + +} \ No newline at end of file From 75a38f3e20093c2eb56001d198a3cd4b721feb67 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 11 Sep 2021 12:47:11 +0300 Subject: [PATCH 077/136] Create a ci-cd file for github actions --- .github/workflows/rust.yml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 .github/workflows/rust.yml diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml new file mode 100644 index 00000000..f92cf6cf --- /dev/null +++ b/.github/workflows/rust.yml @@ -0,0 +1,22 @@ +name: Rust + +on: + push: + branches: [ dev, master ] + pull_request: + branches: [ master, dev, feature/* ] + +env: + CARGO_TERM_COLOR: always + +jobs: + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + - name: Build + run: cargo build --verbose + - name: Run tests + run: cargo test --verbose From 2f7332ee9cb8b28e2a75df4df28ad93c4d0b501d Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 11 Sep 2021 15:16:00 +0300 Subject: [PATCH 078/136] Add integration tests Add for - cpu_instrs - turtle test -- window ly trigger -- window ly trigger offscreen - instr_timing dmg_acid2 is not here cause it must have a bootrom inorder to run properly. Currently the emulator does not emualte the bootrom properly and a dump of he original one is required. --- Cargo.lock | 910 ++++++++++++++++++++++++++++++ lib_gb/Cargo.toml | 4 + lib_gb/tests/calculate_hashes.rs | 41 -- lib_gb/tests/integration_tests.rs | 150 +++++ 4 files changed, 1064 insertions(+), 41 deletions(-) delete mode 100644 lib_gb/tests/calculate_hashes.rs create mode 100644 lib_gb/tests/integration_tests.rs diff --git a/Cargo.lock b/Cargo.lock index 288d6d7b..2ab8194e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "adler" version = "1.0.2" @@ -21,12 +23,57 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +[[package]] +name = "base64" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" + [[package]] name = "bitflags" version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bumpalo" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "bytes" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" + +[[package]] +name = "bzip2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6afcd980b5f3a45017c57e57a2fcccbb351cc43a356ce117ef760ef8052b89b0" +dependencies = [ + "bzip2-sys", + "libc", +] + +[[package]] +name = "bzip2-sys" +version = "0.1.11+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" +dependencies = [ + "cc", + "libc", + "pkg-config", +] + [[package]] name = "cc" version = "1.0.70" @@ -67,6 +114,22 @@ dependencies = [ "cc", ] +[[package]] +name = "core-foundation" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a89e2ae426ea83155dccf10c0fa6b1463ef6d5fcb44cee0b224a408fa640a62" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea221b5284a47e40033bf9b66f35f984ec0ea2931eb03505246cd27a963f981b" + [[package]] name = "crc32fast" version = "1.2.1" @@ -118,6 +181,244 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d" + +[[package]] +name = "futures-io" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "522de2a0fe3e380f1bc577ba0474108faf3f6b18321dbf60b3b9c39a75073377" + +[[package]] +name = "futures-sink" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36ea153c13024fe480590b3e3d4cad89a0cfacecc24577b68f86c6ced9c2bc11" + +[[package]] +name = "futures-task" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d3d00f4eddb73e498a54394f228cd55853bdf059259e8e7bc6e69d408892e99" + +[[package]] +name = "futures-util" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481" +dependencies = [ + "autocfg", + "futures-core", + "futures-io", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "wasi", +] + +[[package]] +name = "h2" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7f3675cfef6a30c8031cf9e6493ebdc3bb3272a3fea3923c4210d1830e6a472" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "http" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527e8c9ac747e28542699a951517aa9a6945af506cd1f2e1b53a576c17b6cc11" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "399c583b2979440c60be0821a6199eca73bc3c8dcd9d070d75ac726e2c6186e5" +dependencies = [ + "bytes", + "http", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" + +[[package]] +name = "httpdate" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6456b8a6c8f33fee7d958fcd1b60d55b11940a79e63ae87013e6d22e26034440" + +[[package]] +name = "hyper" +version = "0.14.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13f67199e765030fa08fe0bd581af683f0d5bc04ea09c2b1102012c5fb90e7fd" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper-tls" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +dependencies = [ + "bytes", + "hyper", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "indexmap" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc633605454125dec4b66843673f01c7df2b89479b32e0ed634e43a91cff62a5" +dependencies = [ + "autocfg", + "hashbrown", +] + +[[package]] +name = "ipnet" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f2d64f2edebec4ce84ad108148e67e1064789bee435edc5b60ad398714a3a9" + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "js-sys" +version = "0.3.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1866b355d9c878e5e607473cbe3f63282c0b7aad2db1dbebf55076c686918254" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -129,6 +430,8 @@ name = "lib_gb" version = "1.0.0" dependencies = [ "log", + "reqwest", + "zip", ] [[package]] @@ -158,12 +461,24 @@ dependencies = [ "wav", ] +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + [[package]] name = "memchr" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +[[package]] +name = "mime" +version = "0.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a60c7ce501c71e03a9c9c0d35b861413ae925bd979cc7a4e30d060069aaac8d" + [[package]] name = "miniz_oxide" version = "0.4.4" @@ -174,6 +489,55 @@ dependencies = [ "autocfg", ] +[[package]] +name = "mio" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16" +dependencies = [ + "libc", + "log", + "miow", + "ntapi", + "winapi", +] + +[[package]] +name = "miow" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" +dependencies = [ + "winapi", +] + +[[package]] +name = "native-tls" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48ba9f7719b5a0f42f338907614285fb5fd70e53858141f69898a1fb7203b24d" +dependencies = [ + "lazy_static", + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "ntapi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" +dependencies = [ + "winapi", +] + [[package]] name = "num-integer" version = "0.1.44" @@ -193,6 +557,143 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "692fcb63b64b1758029e0a96ee63e049ce8c5948587f2f7208df04625e5f6b56" + +[[package]] +name = "openssl" +version = "0.10.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d9facdb76fec0b73c406f125d44d86fdad818d66fef0531eec9233ca425ff4a" +dependencies = [ + "bitflags", + "cfg-if 1.0.0", + "foreign-types", + "libc", + "once_cell", + "openssl-sys", +] + +[[package]] +name = "openssl-probe" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a" + +[[package]] +name = "openssl-sys" +version = "0.9.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1996d2d305e561b70d1ee0c53f1542833f4e1ac6ce9a6708b6ff2738ca67dc82" +dependencies = [ + "autocfg", + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project-lite" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d31d11c69a6b52a174b42bdc0c30e5e11670f90788b2c471c31c1d17d449443" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "proc-macro2" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" +dependencies = [ + "rand_core", +] + [[package]] name = "redox_syscall" version = "0.2.10" @@ -219,12 +720,71 @@ version = "0.6.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" +[[package]] +name = "remove_dir_all" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +dependencies = [ + "winapi", +] + +[[package]] +name = "reqwest" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "246e9f61b9bb77df069a947682be06e31ac43ea37862e244a69f177694ea6d22" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "hyper-tls", + "ipnet", + "js-sys", + "lazy_static", + "log", + "mime", + "native-tls", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_urlencoded", + "tokio", + "tokio-native-tls", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "winreg", +] + [[package]] name = "riff" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9b1a3d5f46d53f4a3478e2be4a5a5ce5108ea58b100dcd139830eae7f79a3a1" +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "schannel" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f05ba609c234e60bee0d547fe94a4c7e9da733d1c962cf6e59efa4cd9c8bc75" +dependencies = [ + "lazy_static", + "winapi", +] + [[package]] name = "sdl2" version = "0.34.5" @@ -252,6 +812,85 @@ dependencies = [ "version-compare", ] +[[package]] +name = "security-framework" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525bc1abfda2e1998d152c45cf13e696f76d0a4972310b22fac1658b05df7c87" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" + +[[package]] +name = "serde_json" +version = "1.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7f9e390c27c3c0ce8bc5d725f6e4d30a29d26659494aa4b17535f7522c5c950" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "slab" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c307a32c1c5c437f38c7fd45d753050587732ba8628319fbdf12a7e289ccc590" + +[[package]] +name = "socket2" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "765f090f0e423d2b55843402a07915add955e7d60657db13707a159727326cad" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "syn" +version = "1.0.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f107db402c2c2055242dbf4d2af0e69197202e9faacbef9571bbe47f5a1b84" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "tar" version = "0.4.37" @@ -263,6 +902,40 @@ dependencies = [ "xattr", ] +[[package]] +name = "tempfile" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "rand", + "redox_syscall", + "remove_dir_all", + "winapi", +] + +[[package]] +name = "thiserror" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "602eca064b2d83369e2b2f34b09c70b605402801927c65c11071ac911d299b88" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad553cc2c78e8de258400763a647e80e6d1b31ee237275d756f6836d204494c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "time" version = "0.1.44" @@ -274,6 +947,114 @@ dependencies = [ "winapi", ] +[[package]] +name = "tinyvec" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "848a1e1181b9f6753b5e96a092749e29b11d19ede67dfbbd6c7dc7e0f49b5338" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "tokio" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4efe6fc2395938c8155973d7be49fe8d03a843726e285e100a8a383cc0154ce" +dependencies = [ + "autocfg", + "bytes", + "libc", + "memchr", + "mio", + "num_cpus", + "pin-project-lite", + "winapi", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7d995660bd2b7f8c1568414c1126076c13fbb725c40112dc0120b78eb9b717b" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d3725d3efa29485e87311c5b699de63cde14b00ed4d256b8318aa30ca452cd" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "log", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower-service" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" + +[[package]] +name = "tracing" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09adeb8c97449311ccd28a427f96fb563e7fd31aabf994189879d9da2394b89d" +dependencies = [ + "cfg-if 1.0.0", + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ca517f43f0fb96e0c3072ed5c275fe5eece87e8cb52f4a77b69226d3b1c9df8" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "try-lock" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" + +[[package]] +name = "unicode-bidi" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "246f4c42e67e7a4e3c6106ff716a5d067d4132a642840b242e357e468a2a0085" + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + [[package]] name = "unidiff" version = "0.3.3" @@ -285,18 +1066,114 @@ dependencies = [ "regex", ] +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "version-compare" version = "0.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d63556a25bae6ea31b52e640d7c41d1ab27faba4ccb600013837a3d0b3994ca1" +[[package]] +name = "want" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +dependencies = [ + "log", + "try-lock", +] + [[package]] name = "wasi" version = "0.10.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" +[[package]] +name = "wasm-bindgen" +version = "0.2.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e68338db6becec24d3c7977b5bf8a48be992c934b5d07177e3931f5dc9b076c" +dependencies = [ + "cfg-if 1.0.0", + "serde", + "serde_json", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f34c405b4f0658583dba0c1c7c9b694f3cac32655db463b56c254a1c75269523" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a87d738d4abc4cf22f6eb142f5b9a81301331ee3c767f2fef2fda4e325492060" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d5a6580be83b19dc570a8f9c324251687ab2184e57086f71625feb57ec77c8" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3775a030dc6f5a0afd8a84981a21cc92a781eb429acef9ecce476d0c9113e92" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c279e376c7a8e8752a8f1eaa35b7b0bee6bb9fb0cdacfa97cc3f1f289c87e2b4" + [[package]] name = "wav" version = "0.6.0" @@ -306,6 +1183,16 @@ dependencies = [ "riff", ] +[[package]] +name = "web-sys" +version = "0.3.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a84d70d1ec7d2da2d26a5bd78f4bca1b8c3254805363ce743b7a05bc30d195a" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "winapi" version = "0.3.9" @@ -328,6 +1215,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +[[package]] +name = "winreg" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0120db82e8a1e0b9fb3345a539c478767c0048d842860994d96113d5b667bd69" +dependencies = [ + "winapi", +] + [[package]] name = "xattr" version = "0.2.2" @@ -336,3 +1232,17 @@ checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" dependencies = [ "libc", ] + +[[package]] +name = "zip" +version = "0.5.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93ab48844d61251bb3835145c521d88aa4031d7139e8485990f60ca911fa0815" +dependencies = [ + "byteorder", + "bzip2", + "crc32fast", + "flate2", + "thiserror", + "time", +] diff --git a/lib_gb/Cargo.toml b/lib_gb/Cargo.toml index ca607529..23973ca9 100644 --- a/lib_gb/Cargo.toml +++ b/lib_gb/Cargo.toml @@ -5,3 +5,7 @@ authors = ["alloncm "] edition = "2018" [dependencies] log = "0.4" + +[dev-dependencies] +reqwest = { version = "0.11", features = ["blocking"] } +zip = "0.5" diff --git a/lib_gb/tests/calculate_hashes.rs b/lib_gb/tests/calculate_hashes.rs deleted file mode 100644 index cb46bab9..00000000 --- a/lib_gb/tests/calculate_hashes.rs +++ /dev/null @@ -1,41 +0,0 @@ -use std::collections::hash_map::DefaultHasher; -use std::hash::{Hash, Hasher}; -use lib_gb::machine::gameboy::GameBoy; -use lib_gb::machine::mbc_initializer::initialize_mbc; -use lib_gb::ppu::gfx_device::GfxDevice; -use lib_gb::apu::audio_device::AudioDevice; -use lib_gb::keypad::joypad_provider::JoypadProvider; - -struct StubGfxDevice; -impl GfxDevice for StubGfxDevice{ - fn swap_buffer(&self, buffer:&[u32]) { - let mut s = DefaultHasher::new(); - buffer.hash(&mut s); - println!("{}",s.finish()); - std::thread::sleep(std::time::Duration::from_secs(2)); - // std::process::exit(0); - } -} - -struct StubAudioDevice; -impl AudioDevice for StubAudioDevice{ - fn push_buffer(&mut self, _buffer:&[lib_gb::apu::audio_device::Sample]) {} -} - -struct StubJoypadProvider; -impl JoypadProvider for StubJoypadProvider{ - fn provide(&mut self, _joypad:&mut lib_gb::keypad::joypad::Joypad) {} -} - - -#[test] -fn calc_hash(){ - let program = std::fs::read("C:/Users/Alon/Downloads/GameBoy/window_y_trigger.gb") - .expect("Error could not find file"); - let mut mbc = initialize_mbc(program, None); - - let mut gameboy = GameBoy::new(&mut mbc, StubJoypadProvider{}, StubAudioDevice{}, StubGfxDevice{}); - - loop {gameboy.cycle_frame();} - -} \ No newline at end of file diff --git a/lib_gb/tests/integration_tests.rs b/lib_gb/tests/integration_tests.rs new file mode 100644 index 00000000..a3447371 --- /dev/null +++ b/lib_gb/tests/integration_tests.rs @@ -0,0 +1,150 @@ +use std::collections::hash_map::DefaultHasher; +use std::hash::{Hash, Hasher}; +use std::io::Read; +use lib_gb::{ + apu::audio_device::AudioDevice, keypad::joypad_provider::JoypadProvider, + machine::{gameboy::GameBoy, mbc_initializer::initialize_mbc}, ppu::gfx_device::GfxDevice +}; + +static mut LAST_HASH:u64 = 0; +static mut FOUND_HASH:bool = false; +struct CheckHashGfxDevice{ + hash:u64 +} +impl GfxDevice for CheckHashGfxDevice{ + fn swap_buffer(&self, buffer:&[u32]) { + let mut s = DefaultHasher::new(); + buffer.hash(&mut s); + let hash = s.finish(); + unsafe{ + if LAST_HASH == hash && hash == self.hash{ + println!("{}", hash); + FOUND_HASH = true; + } + LAST_HASH = hash; + } + } +} + +struct StubAudioDevice; +impl AudioDevice for StubAudioDevice{ + fn push_buffer(&mut self, _buffer:&[lib_gb::apu::audio_device::Sample]) {} +} + +struct StubJoypadProvider; +impl JoypadProvider for StubJoypadProvider{ + fn provide(&mut self, _joypad:&mut lib_gb::keypad::joypad::Joypad) {} +} + +#[test] +fn test_cpu_instrs(){ + let file_url = "https://raw.githubusercontent.com/retrio/gb-test-roms/master/cpu_instrs/cpu_instrs.gb"; + run_integration_test_from_url(file_url, 800, 3798827046966939676); +} + +#[test] +fn test_cpu_instrs_timing(){ + let file_url = "https://raw.githubusercontent.com/retrio/gb-test-roms/master/instr_timing/instr_timing.gb"; + run_integration_test_from_url(file_url, 100, 469033992149587554); +} + +#[test] +fn test_turtle_window_y_trigger(){ + run_turtle_integration_test("window_y_trigger.gb", 15511617103807079362); +} + +#[test] +fn run_turtle_window_y_trigger_wx_offscreen(){ + run_turtle_integration_test("window_y_trigger_wx_offscreen.gb", 15592061677463553443); +} + +fn run_turtle_integration_test(program_name:&str, hash:u64){ + let zip_url = "https://github.com/Powerlated/TurtleTests/releases/download/v1.0/release.zip"; + + let file = reqwest::blocking::get(zip_url).unwrap() + .bytes().unwrap(); + + let cursor = std::io::Cursor::new(file.as_ref()); + + let mut programs = zip::ZipArchive::new(cursor).unwrap(); + let zip_file = programs.by_name(program_name).unwrap(); + let program = zip_file.bytes().map(|x|x.unwrap()).collect::>(); + + run_integration_test(program, 100, hash, format!("The program: {} has failed", program_name)); +} + +fn run_integration_test_from_url(program_url:&str, frames_to_execute:u32, expected_hash:u64){ + let file = reqwest::blocking::get(program_url).unwrap() + .bytes().unwrap(); + + let program = Vec::from(file.as_ref()); + let fail_message = format!("The program {} has failed", program_url); + run_integration_test(program, frames_to_execute, expected_hash, fail_message); +} + +fn run_integration_test(program:Vec, frames_to_execute:u32, expected_hash:u64, fail_message:String){ + let mut mbc = initialize_mbc(program, None); + + let mut gameboy = GameBoy::new( + &mut mbc, + StubJoypadProvider{}, + StubAudioDevice{}, + CheckHashGfxDevice{hash:expected_hash} + ); + + unsafe{ + FOUND_HASH = false; + LAST_HASH = 0; + } + + for _ in 0..frames_to_execute { + gameboy.cycle_frame(); + unsafe{ + if FOUND_HASH{ + return; + } + } + } + assert!(false, "{}", fail_message); +} + + +/* +// This test is for clcualting the hash of a new test rom +#[test] +fn calc_hash(){ + static mut FRAMES_COUNTER:u32 = 0; + struct GetHashGfxDevice; + impl GfxDevice for GetHashGfxDevice{ + fn swap_buffer(&self, buffer:&[u32]) { + unsafe{ + if FRAMES_COUNTER < 700{ + FRAMES_COUNTER += 1; + return; + } + } + let mut s = DefaultHasher::new(); + buffer.hash(&mut s); + let hash = s.finish(); + unsafe{ + if LAST_HASH == hash{ + println!("{}", hash); + std::process::exit(0); + } + LAST_HASH = hash; + } + } + } + + let program = std::fs::read("C:/Users/Alon/Downloads/GameBoy/window_y_trigger_wx_offscreen.gb") + .expect("Error could not find file"); + + let program = Vec::from(program); + + let mut mbc = initialize_mbc(program, None); + + let mut gameboy = GameBoy::new(&mut mbc, StubJoypadProvider{}, StubAudioDevice{}, GetHashGfxDevice{}); + + loop {gameboy.cycle_frame();} +} +*/ From 9d1c1d3f035453974ea71b8dc7c46656761c7d0b Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 11 Sep 2021 15:16:24 +0300 Subject: [PATCH 079/136] Some repo cleanning --- lib_gb/src/mmu/io_ports.rs | 1 - lib_gb/src/timer/gb_timer.rs | 3 --- 2 files changed, 4 deletions(-) diff --git a/lib_gb/src/mmu/io_ports.rs b/lib_gb/src/mmu/io_ports.rs index 120e358f..58dc253f 100644 --- a/lib_gb/src/mmu/io_ports.rs +++ b/lib_gb/src/mmu/io_ports.rs @@ -4,7 +4,6 @@ pub const IO_PORTS_SIZE:usize = 0x80; pub const IO_PORTS_MEMORY_OFFSET:u16 = 0xFF00; -#[macro_use] macro_rules! io_port_index{ ($name:ident, $reg_address:expr) => { const $name:u16 = $reg_address - IO_PORTS_MEMORY_OFFSET; diff --git a/lib_gb/src/timer/gb_timer.rs b/lib_gb/src/timer/gb_timer.rs index 0979f20b..444a2f1e 100644 --- a/lib_gb/src/timer/gb_timer.rs +++ b/lib_gb/src/timer/gb_timer.rs @@ -52,9 +52,6 @@ impl GbTimer{ _=> std::panic!("bad timer interval vlaue: {}", timer_interval) }; - if self.last_and_result && !timer_enable{ - println!("edge case"); - } let current_and_result = bit_value && timer_enable; if !current_and_result && self.last_and_result{ let(value, overflow) = self.tima_register.overflowing_add(1); From a0404c3bcb136196f797c9e7e68feb67df5873a3 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 11 Sep 2021 16:09:54 +0300 Subject: [PATCH 080/136] Made the code thread safe Remove the static unsafe state and now the code a bit safer :) --- lib_gb/tests/integration_tests.rs | 28 +++++++++++----------------- 1 file changed, 11 insertions(+), 17 deletions(-) diff --git a/lib_gb/tests/integration_tests.rs b/lib_gb/tests/integration_tests.rs index a3447371..b6a12b92 100644 --- a/lib_gb/tests/integration_tests.rs +++ b/lib_gb/tests/integration_tests.rs @@ -6,10 +6,10 @@ use lib_gb::{ machine::{gameboy::GameBoy, mbc_initializer::initialize_mbc}, ppu::gfx_device::GfxDevice }; -static mut LAST_HASH:u64 = 0; -static mut FOUND_HASH:bool = false; struct CheckHashGfxDevice{ - hash:u64 + hash:u64, + last_hash_p:*mut u64, + found_p:*mut bool, } impl GfxDevice for CheckHashGfxDevice{ fn swap_buffer(&self, buffer:&[u32]) { @@ -17,11 +17,11 @@ impl GfxDevice for CheckHashGfxDevice{ buffer.hash(&mut s); let hash = s.finish(); unsafe{ - if LAST_HASH == hash && hash == self.hash{ + if *self.last_hash_p == hash && hash == self.hash{ println!("{}", hash); - FOUND_HASH = true; + *self.found_p = true; } - LAST_HASH = hash; + *self.last_hash_p = hash; } } } @@ -84,25 +84,19 @@ fn run_integration_test_from_url(program_url:&str, frames_to_execute:u32, expect fn run_integration_test(program:Vec, frames_to_execute:u32, expected_hash:u64, fail_message:String){ let mut mbc = initialize_mbc(program, None); - + let mut last_hash:u64 = 0; + let mut found = false; let mut gameboy = GameBoy::new( &mut mbc, StubJoypadProvider{}, StubAudioDevice{}, - CheckHashGfxDevice{hash:expected_hash} + CheckHashGfxDevice{hash:expected_hash,last_hash_p:&mut last_hash, found_p:&mut found} ); - - unsafe{ - FOUND_HASH = false; - LAST_HASH = 0; - } for _ in 0..frames_to_execute { gameboy.cycle_frame(); - unsafe{ - if FOUND_HASH{ - return; - } + if found{ + return; } } assert!(false, "{}", fail_message); From 6cd1225aa5ecdbdc4fec5a184164a53f256f886e Mon Sep 17 00:00:00 2001 From: alloncm Date: Sun, 12 Sep 2021 18:43:11 +0300 Subject: [PATCH 081/136] The emulation code now runs in another thread Since SDL is really ambiguous about rendering from another thread the emulation is running on another thread. Using crossbeam parker for sync between threads (Some kind of AutoResetEvent from C#) And using rtrb for the ring buffer queue for moving data between the threads --- Cargo.lock | 102 +++++++++++++++++++++++++++++++++++ gb/Cargo.toml | 4 +- gb/src/main.rs | 100 ++++++++++++++++++++++------------ gb/src/sdl_gfx_device.rs | 4 +- lib_gb/src/ppu/gfx_device.rs | 4 +- 5 files changed, 175 insertions(+), 39 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 288d6d7b..8907c423 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,5 +1,7 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. +version = 3 + [[package]] name = "adler" version = "1.0.2" @@ -27,6 +29,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "cache-padded" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba" + [[package]] name = "cc" version = "1.0.70" @@ -76,6 +84,74 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "crossbeam" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae5588f6b3c3cb05239e90bd110f257254aecd01e4635400391aeae07497845" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-epoch", + "crossbeam-queue", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b10ddc024425c88c2ad148c1b0fd53f4c6d38db9697c9f1588381212fa657c9" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" +dependencies = [ + "cfg-if 1.0.0", + "lazy_static", +] + [[package]] name = "encoding_rs" version = "0.8.28" @@ -151,9 +227,11 @@ name = "magenboy" version = "1.0.0" dependencies = [ "chrono", + "crossbeam", "fern", "lib_gb", "log", + "rtrb", "sdl2", "wav", ] @@ -164,6 +242,15 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" +[[package]] +name = "memoffset" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9" +dependencies = [ + "autocfg", +] + [[package]] name = "miniz_oxide" version = "0.4.4" @@ -225,6 +312,21 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9b1a3d5f46d53f4a3478e2be4a5a5ce5108ea58b100dcd139830eae7f79a3a1" +[[package]] +name = "rtrb" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "318256ac02f7e11a48a10339ba5dca8bd7eb17496abf384e8ea909bb2ae5275f" +dependencies = [ + "cache-padded", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + [[package]] name = "sdl2" version = "0.34.5" diff --git a/gb/Cargo.toml b/gb/Cargo.toml index f9339ee7..ed0e0a4c 100644 --- a/gb/Cargo.toml +++ b/gb/Cargo.toml @@ -11,4 +11,6 @@ log = "0.4" fern = "0.6.0" chrono = "0.4" sdl2 = {version = "0.34", features = ["bundled","static-link"]} -wav = "0.6.0" \ No newline at end of file +wav = "0.6.0" +crossbeam = "0.8" +rtrb = "0.1" \ No newline at end of file diff --git a/gb/src/main.rs b/gb/src/main.rs index 2f738005..23e6d045 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -7,7 +7,7 @@ mod multi_device_audio; mod sdl_gfx_device; use crate::{mbc_handler::*, sdl_joypad_provider::*, multi_device_audio::*}; -use lib_gb::{keypad::button::Button, machine::gameboy::GameBoy, mmu::gb_mmu::BOOT_ROM_SIZE, ppu::gb_ppu::{SCREEN_HEIGHT, SCREEN_WIDTH}, GB_FREQUENCY, apu::audio_device::*}; +use lib_gb::{GB_FREQUENCY, apu::audio_device::*, keypad::button::Button, machine::gameboy::GameBoy, mmu::gb_mmu::BOOT_ROM_SIZE, ppu::{gb_ppu::{SCREEN_HEIGHT, SCREEN_WIDTH}, gfx_device::GfxDevice}}; use std::{fs, env, result::Result, vec::Vec}; use log::info; use sdl2::sys::*; @@ -58,6 +58,18 @@ fn check_for_terminal_feature_flag(args:&Vec::, flag:&str)->bool{ args.len() >= 3 && args.contains(&String::from(flag)) } +struct SpscGfxDevice{ + producer: rtrb::Producer<[u32;SCREEN_HEIGHT * SCREEN_WIDTH ]>, + parker: crossbeam::sync::Parker, +} + +impl GfxDevice for SpscGfxDevice{ + fn swap_buffer(&mut self, buffer:&[u32; SCREEN_WIDTH * SCREEN_HEIGHT]) { + self.producer.push(buffer.clone()).unwrap(); + self.parker.park(); + } +} + fn main() { let args: Vec = env::args().collect(); @@ -68,41 +80,57 @@ fn main() { Result::Err(error)=>std::panic!("error initing logger: {}", error) } - let audio_device = sdl_audio_device::SdlAudioDevie::new(44100); - let mut devices: Vec::> = Vec::new(); - devices.push(Box::new(audio_device)); - if check_for_terminal_feature_flag(&args, "--file-audio"){ - let wav_ad = wav_file_audio_device::WavfileAudioDevice::new(44100, GB_FREQUENCY, "output.wav"); - devices.push(Box::new(wav_ad)); - } - - let audio_devices = MultiAudioDevice::new(devices); + let mut sdl_gfx_device = sdl_gfx_device::SdlGfxDevice::new(SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32, "MagenBoy", SCREEN_SCALE); - let sdl_gfx_device = sdl_gfx_device::SdlGfxDevice::new(SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32, "MagenBoy", SCREEN_SCALE); - - let program_name = &args[1]; - let mut mbc = initialize_mbc(program_name); - let joypad_provider = SdlJoypadProvider::new(buttons_mapper); - - let mut gameboy = match fs::read("Dependencies/Init/dmg_boot.bin"){ - Result::Ok(file)=>{ - info!("found bootrom!"); + let (producer, mut c) = rtrb::RingBuffer::<[u32; SCREEN_HEIGHT * SCREEN_WIDTH]>::new(1).split(); + let parker = crossbeam::sync::Parker::new(); + let unparker = parker.unparker().clone(); + let spsc_gfx_device = SpscGfxDevice{producer, parker: parker}; + - let mut bootrom:[u8;BOOT_ROM_SIZE] = [0;BOOT_ROM_SIZE]; - for i in 0..BOOT_ROM_SIZE{ - bootrom[i] = file[i]; - } - - GameBoy::new_with_bootrom(&mut mbc, joypad_provider,audio_devices, sdl_gfx_device, bootrom) + let program_name = args[1].clone(); + + std::thread::Builder::new().name("Emualtion Thread".to_string()).spawn(move ||{ + + let audio_device = sdl_audio_device::SdlAudioDevie::new(44100); + let mut devices: Vec::> = Vec::new(); + devices.push(Box::new(audio_device)); + if check_for_terminal_feature_flag(&args, "--file-audio"){ + let wav_ad = wav_file_audio_device::WavfileAudioDevice::new(44100, GB_FREQUENCY, "output.wav"); + devices.push(Box::new(wav_ad)); } - Result::Err(_)=>{ - info!("could not find bootrom... booting directly to rom"); - GameBoy::new(&mut mbc, joypad_provider, audio_devices, sdl_gfx_device) + let audio_devices = MultiAudioDevice::new(devices); + let mut mbc = initialize_mbc(&program_name); + let joypad_provider = SdlJoypadProvider::new(buttons_mapper); + + let mut gameboy = match fs::read("Dependencies/Init/dmg_boot.bin"){ + Result::Ok(file)=>{ + info!("found bootrom!"); + + let mut bootrom:[u8;BOOT_ROM_SIZE] = [0;BOOT_ROM_SIZE]; + for i in 0..BOOT_ROM_SIZE{ + bootrom[i] = file[i]; + } + + GameBoy::new_with_bootrom(&mut mbc, joypad_provider,audio_devices, spsc_gfx_device, bootrom) + } + Result::Err(_)=>{ + info!("could not find bootrom... booting directly to rom"); + + GameBoy::new(&mut mbc, joypad_provider, audio_devices, spsc_gfx_device) + } + }; + + info!("initialized gameboy successfully!"); + + loop{ + gameboy.cycle_frame(); } - }; - - info!("initialized gameboy successfully!"); + + drop(gameboy); + release_mbc(&program_name, mbc); + }).unwrap(); unsafe{ let mut event: std::mem::MaybeUninit = std::mem::MaybeUninit::uninit(); @@ -116,7 +144,11 @@ fn main() { } } - gameboy.cycle_frame(); + if !c.is_empty(){ + let pop = c.pop().unwrap(); + sdl_gfx_device.swap_buffer(&pop); + unparker.unpark(); + } let end = SDL_GetPerformanceCounter(); let elapsed_ms:f64 = (end - start) as f64 / SDL_GetPerformanceFrequency() as f64 * 1000.0; @@ -129,6 +161,4 @@ fn main() { SDL_Quit(); } - drop(gameboy); - release_mbc(program_name, mbc); -} +} \ No newline at end of file diff --git a/gb/src/sdl_gfx_device.rs b/gb/src/sdl_gfx_device.rs index d74ffa46..a689b182 100644 --- a/gb/src/sdl_gfx_device.rs +++ b/gb/src/sdl_gfx_device.rs @@ -1,6 +1,6 @@ use std::ffi::{CString, c_void}; -use lib_gb::ppu::gfx_device::GfxDevice; +use lib_gb::ppu::{gb_ppu::{SCREEN_HEIGHT, SCREEN_WIDTH}, gfx_device::GfxDevice}; use sdl2::sys::*; pub struct SdlGfxDevice{ @@ -60,7 +60,7 @@ impl SdlGfxDevice{ } impl GfxDevice for SdlGfxDevice{ - fn swap_buffer(&self, buffer:&[u32]) { + fn swap_buffer(&mut self, buffer:&[u32; SCREEN_HEIGHT * SCREEN_WIDTH]) { unsafe{ let extended_buffer = Self::extend_vec(buffer, self.sacle as usize, self.width as usize, self.height as usize); diff --git a/lib_gb/src/ppu/gfx_device.rs b/lib_gb/src/ppu/gfx_device.rs index 400f51a2..017acacf 100644 --- a/lib_gb/src/ppu/gfx_device.rs +++ b/lib_gb/src/ppu/gfx_device.rs @@ -1,3 +1,5 @@ +use super::gb_ppu::{SCREEN_HEIGHT, SCREEN_WIDTH}; + pub trait GfxDevice{ - fn swap_buffer(&self, buffer:&[u32]); + fn swap_buffer(&mut self, buffer:&[u32; SCREEN_HEIGHT * SCREEN_WIDTH]); } \ No newline at end of file From e2f05f105fb65f63d91ce45d121a2ae7410512e9 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sun, 12 Sep 2021 19:08:48 +0300 Subject: [PATCH 082/136] Improved the parking and the sync --- gb/src/main.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/gb/src/main.rs b/gb/src/main.rs index 23e6d045..611bb4a5 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -66,7 +66,9 @@ struct SpscGfxDevice{ impl GfxDevice for SpscGfxDevice{ fn swap_buffer(&mut self, buffer:&[u32; SCREEN_WIDTH * SCREEN_HEIGHT]) { self.producer.push(buffer.clone()).unwrap(); - self.parker.park(); + if self.producer.is_full(){ + self.parker.park(); + } } } @@ -146,8 +148,8 @@ fn main() { if !c.is_empty(){ let pop = c.pop().unwrap(); - sdl_gfx_device.swap_buffer(&pop); unparker.unpark(); + sdl_gfx_device.swap_buffer(&pop); } let end = SDL_GetPerformanceCounter(); From 770dd9c6f766412cb3cfb2db9b93eccf23cd55fa Mon Sep 17 00:00:00 2001 From: alloncm Date: Tue, 14 Sep 2021 02:08:35 +0300 Subject: [PATCH 083/136] Trying to improve perf not tested yet --- lib_gb/src/apu/frame_sequencer.rs | 2 +- lib_gb/src/apu/timer.rs | 8 ++------ 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/lib_gb/src/apu/frame_sequencer.rs b/lib_gb/src/apu/frame_sequencer.rs index c3ca4f6d..405cd26b 100644 --- a/lib_gb/src/apu/frame_sequencer.rs +++ b/lib_gb/src/apu/frame_sequencer.rs @@ -52,7 +52,7 @@ impl FrameSequencer{ // in a rare case where the length hasnt started the second iteration there might be a length step. // This will happen only after if the channel is activated after being not active. pub fn should_next_step_clock_length(&self)->bool{ - self.counter % 2 == 0 + self.counter & 1 == 0 } pub fn reset(&mut self){ diff --git a/lib_gb/src/apu/timer.rs b/lib_gb/src/apu/timer.rs index 7604b2e2..d1480692 100644 --- a/lib_gb/src/apu/timer.rs +++ b/lib_gb/src/apu/timer.rs @@ -13,12 +13,8 @@ impl Timer{ pub fn cycle(&mut self)->bool{ if self.cycles_to_tick != 0{ - self.cycle_counter += 1; - if self.cycle_counter >= self.cycles_to_tick{ - self.cycle_counter = 0; - - return true; - } + self.cycle_counter = (self.cycle_counter + 1) % self.cycles_to_tick; + return self.cycle_counter == 0; } return false; From bb06e5b3788786ae2e2ea41b81f84ae870bc9553 Mon Sep 17 00:00:00 2001 From: alloncm Date: Tue, 14 Sep 2021 02:09:17 +0300 Subject: [PATCH 084/136] Add bench framework chose criterion cause the regualr one is stil unstable. test with `cargo bench` --- Cargo.lock | 440 +++++++++++++++++++++++++++++++++++++ lib_gb/Cargo.toml | 7 + lib_gb/benches/my_bench.rs | 24 ++ 3 files changed, 471 insertions(+) create mode 100644 lib_gb/benches/my_bench.rs diff --git a/Cargo.lock b/Cargo.lock index 8907c423..087baf4b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,17 @@ dependencies = [ "memchr", ] +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + [[package]] name = "autocfg" version = "1.0.1" @@ -29,12 +40,39 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bstr" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90682c8d613ad3373e66de8c6411e0ae2ab2571e879d2efbf73558cc66f21279" +dependencies = [ + "lazy_static", + "memchr", + "regex-automata", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c59e7af012c713f529e7a3ee57ce9b31ddd858d4b512923602f74608b009631" + [[package]] name = "cache-padded" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba" +[[package]] +name = "cast" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c24dab4283a142afa2fdca129b80ad2c6284e073930f964c3a1293c225ee39a" +dependencies = [ + "rustc_version", +] + [[package]] name = "cc" version = "1.0.70" @@ -66,6 +104,17 @@ dependencies = [ "winapi", ] +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "bitflags", + "textwrap", + "unicode-width", +] + [[package]] name = "cmake" version = "0.1.45" @@ -84,6 +133,42 @@ dependencies = [ "cfg-if 1.0.0", ] +[[package]] +name = "criterion" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1604dafd25fba2fe2d5895a9da139f8dc9b319a5fe5354ca137cbbce4e178d10" +dependencies = [ + "atty", + "cast", + "clap", + "criterion-plot", + "csv", + "itertools", + "lazy_static", + "num-traits", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_cbor", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d00996de9f2f7559f7f4dc286073197f83e92256a59ed395f9aac01fe717da57" +dependencies = [ + "cast", + "itertools", +] + [[package]] name = "crossbeam" version = "0.8.1" @@ -152,6 +237,34 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "csv" +version = "1.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1" +dependencies = [ + "bstr", + "csv-core", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "csv-core" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90" +dependencies = [ + "memchr", +] + +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + [[package]] name = "encoding_rs" version = "0.8.28" @@ -194,6 +307,45 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "half" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" + +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "itertools" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" + +[[package]] +name = "js-sys" +version = "0.3.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1866b355d9c878e5e607473cbe3f63282c0b7aad2db1dbebf55076c686918254" +dependencies = [ + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -204,6 +356,7 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" name = "lib_gb" version = "1.0.0" dependencies = [ + "criterion", "log", ] @@ -280,6 +433,93 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + +[[package]] +name = "plotters" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a3fd9ec30b9749ce28cd91f255d569591cdf937fe280c312143e3c4bad6f2a" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d88417318da0eaf0fdcdb51a0ee6c3bed624333bff8f946733049380be67ac1c" + +[[package]] +name = "plotters-svg" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521fa9638fa597e1dc53e9412a4f9cefb01187ee1f7413076f9e6749e2885ba9" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "proc-macro2" +version = "1.0.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9f5105d4fdaab20335ca9565e106a5d9b82b6219b5ba735731124ac6711d23d" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quote" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rayon" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" +dependencies = [ + "autocfg", + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e" +dependencies = [ + "crossbeam-channel", + "crossbeam-deque", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + [[package]] name = "redox_syscall" version = "0.2.10" @@ -300,6 +540,12 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" + [[package]] name = "regex-syntax" version = "0.6.25" @@ -321,6 +567,30 @@ dependencies = [ "cache-padded", ] +[[package]] +name = "rustc_version" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + [[package]] name = "scopeguard" version = "1.1.0" @@ -354,6 +624,61 @@ dependencies = [ "version-compare", ] +[[package]] +name = "semver" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" + +[[package]] +name = "serde" +version = "1.0.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" + +[[package]] +name = "serde_cbor" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5" +dependencies = [ + "half", + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.130" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7f9e390c27c3c0ce8bc5d725f6e4d30a29d26659494aa4b17535f7522c5c950" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "1.0.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f107db402c2c2055242dbf4d2af0e69197202e9faacbef9571bbe47f5a1b84" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "tar" version = "0.4.37" @@ -365,6 +690,15 @@ dependencies = [ "xattr", ] +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + [[package]] name = "time" version = "0.1.44" @@ -376,6 +710,28 @@ dependencies = [ "winapi", ] +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + +[[package]] +name = "unicode-xid" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" + [[package]] name = "unidiff" version = "0.3.3" @@ -393,12 +749,77 @@ version = "0.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d63556a25bae6ea31b52e640d7c41d1ab27faba4ccb600013837a3d0b3994ca1" +[[package]] +name = "walkdir" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56" +dependencies = [ + "same-file", + "winapi", + "winapi-util", +] + [[package]] name = "wasi" version = "0.10.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" +[[package]] +name = "wasm-bindgen" +version = "0.2.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e68338db6becec24d3c7977b5bf8a48be992c934b5d07177e3931f5dc9b076c" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f34c405b4f0658583dba0c1c7c9b694f3cac32655db463b56c254a1c75269523" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d5a6580be83b19dc570a8f9c324251687ab2184e57086f71625feb57ec77c8" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3775a030dc6f5a0afd8a84981a21cc92a781eb429acef9ecce476d0c9113e92" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c279e376c7a8e8752a8f1eaa35b7b0bee6bb9fb0cdacfa97cc3f1f289c87e2b4" + [[package]] name = "wav" version = "0.6.0" @@ -408,6 +829,16 @@ dependencies = [ "riff", ] +[[package]] +name = "web-sys" +version = "0.3.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a84d70d1ec7d2da2d26a5bd78f4bca1b8c3254805363ce743b7a05bc30d195a" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "winapi" version = "0.3.9" @@ -424,6 +855,15 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" diff --git a/lib_gb/Cargo.toml b/lib_gb/Cargo.toml index ca607529..beaec5c7 100644 --- a/lib_gb/Cargo.toml +++ b/lib_gb/Cargo.toml @@ -5,3 +5,10 @@ authors = ["alloncm "] edition = "2018" [dependencies] log = "0.4" + +[dev-dependencies] +criterion = "0.3" + +[[bench]] +name = "my_bench" +harness = false \ No newline at end of file diff --git a/lib_gb/benches/my_bench.rs b/lib_gb/benches/my_bench.rs new file mode 100644 index 00000000..c053881c --- /dev/null +++ b/lib_gb/benches/my_bench.rs @@ -0,0 +1,24 @@ +use criterion::*; +use lib_gb::apu::{ + gb_apu::*, + audio_device::*, +}; + +pub fn criterion_bench(c: &mut Criterion){ + struct StubApu; + impl AudioDevice for StubApu{ + fn push_buffer(&mut self, _buffer:&[Sample]){} + } + + c.bench_function("test apu", |b| b.iter(||{ + let mut apu = GbApu::new(StubApu{}); + apu.enabled = true; + apu.sweep_tone_channel.enabled = true; + for _ in 0..100000{ + apu.cycle(255); + } + })); +} + +criterion_group!(benches, criterion_bench); +criterion_main!(benches); \ No newline at end of file From bafdfb82643b94ee5422c0286d96d427f6001e27 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 18 Sep 2021 03:26:55 +0300 Subject: [PATCH 085/136] Some cleaning and optimization - Use crossbeam-utils instead of the whole lib - Cache the analog sample in the channel instead of calculating it each time --- Cargo.lock | 26 +------------------------- gb/Cargo.toml | 2 +- gb/src/main.rs | 4 ++-- lib_gb/src/apu/channel.rs | 11 ++++++----- 4 files changed, 10 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 087baf4b..fba6b13d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -169,20 +169,6 @@ dependencies = [ "itertools", ] -[[package]] -name = "crossbeam" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae5588f6b3c3cb05239e90bd110f257254aecd01e4635400391aeae07497845" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-channel", - "crossbeam-deque", - "crossbeam-epoch", - "crossbeam-queue", - "crossbeam-utils", -] - [[package]] name = "crossbeam-channel" version = "0.5.1" @@ -217,16 +203,6 @@ dependencies = [ "scopeguard", ] -[[package]] -name = "crossbeam-queue" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b10ddc024425c88c2ad148c1b0fd53f4c6d38db9697c9f1588381212fa657c9" -dependencies = [ - "cfg-if 1.0.0", - "crossbeam-utils", -] - [[package]] name = "crossbeam-utils" version = "0.8.5" @@ -380,7 +356,7 @@ name = "magenboy" version = "1.0.0" dependencies = [ "chrono", - "crossbeam", + "crossbeam-utils", "fern", "lib_gb", "log", diff --git a/gb/Cargo.toml b/gb/Cargo.toml index ed0e0a4c..32547146 100644 --- a/gb/Cargo.toml +++ b/gb/Cargo.toml @@ -12,5 +12,5 @@ fern = "0.6.0" chrono = "0.4" sdl2 = {version = "0.34", features = ["bundled","static-link"]} wav = "0.6.0" -crossbeam = "0.8" +crossbeam-utils = "0.8" rtrb = "0.1" \ No newline at end of file diff --git a/gb/src/main.rs b/gb/src/main.rs index 611bb4a5..d6a5b2b2 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -60,7 +60,7 @@ fn check_for_terminal_feature_flag(args:&Vec::, flag:&str)->bool{ struct SpscGfxDevice{ producer: rtrb::Producer<[u32;SCREEN_HEIGHT * SCREEN_WIDTH ]>, - parker: crossbeam::sync::Parker, + parker: crossbeam_utils::sync::Parker, } impl GfxDevice for SpscGfxDevice{ @@ -85,7 +85,7 @@ fn main() { let mut sdl_gfx_device = sdl_gfx_device::SdlGfxDevice::new(SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32, "MagenBoy", SCREEN_SCALE); let (producer, mut c) = rtrb::RingBuffer::<[u32; SCREEN_HEIGHT * SCREEN_WIDTH]>::new(1).split(); - let parker = crossbeam::sync::Parker::new(); + let parker = crossbeam_utils::sync::Parker::new(); let unparker = parker.unparker().clone(); let spsc_gfx_device = SpscGfxDevice{producer, parker: parker}; diff --git a/lib_gb/src/apu/channel.rs b/lib_gb/src/apu/channel.rs index bc3e19e4..3ddc7fad 100644 --- a/lib_gb/src/apu/channel.rs +++ b/lib_gb/src/apu/channel.rs @@ -9,7 +9,7 @@ pub struct Channel{ pub sample_producer:Procuder, pub timer:Timer, - last_sample:u8, + last_sample:f32, } impl Channel{ @@ -21,7 +21,7 @@ impl Channel{ length_enable:false, timer: Timer::new(sample_producer.get_updated_frequency_ticks(0)), sample_producer, - last_sample: 0 + last_sample: 0.0 } } @@ -44,7 +44,7 @@ impl Channel{ self.timer.update_cycles_to_tick(self.sample_producer.get_updated_frequency_ticks(self.frequency)); self.sample_producer.reset(); - self.last_sample = 0; + self.last_sample = 0.0; } pub fn get_audio_sample(&mut self)->f32{ @@ -52,7 +52,8 @@ impl Channel{ let sample = if self.timer.cycle(){ self.timer.update_cycles_to_tick(self.sample_producer.get_updated_frequency_ticks(self.frequency)); - self.sample_producer.produce() + let s = self.sample_producer.produce(); + self.convert_digtial_to_analog(s) } else{ self.last_sample @@ -60,7 +61,7 @@ impl Channel{ self.last_sample = sample; - return self.convert_digtial_to_analog(self.last_sample); + return self.last_sample; } return 0.0; From a549639db661a2bf13d33dfe4c516bbf93b132d7 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 18 Sep 2021 03:31:24 +0300 Subject: [PATCH 086/136] Optimize the APU timer This optimization gained a crazy performance boost! Probably cause of the lack of division in the new solution. --- lib_gb/src/apu/timer.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib_gb/src/apu/timer.rs b/lib_gb/src/apu/timer.rs index d1480692..a0c0738b 100644 --- a/lib_gb/src/apu/timer.rs +++ b/lib_gb/src/apu/timer.rs @@ -11,9 +11,14 @@ impl Timer{ } } + // This function is a hot spot for the APU, almost every component uses the timer pub fn cycle(&mut self)->bool{ if self.cycles_to_tick != 0{ - self.cycle_counter = (self.cycle_counter + 1) % self.cycles_to_tick; + // The calculation used to be this: + // self.cycle_counter = (self.cycle_counter + 1) % self.cycles_to_tick; + // After benching with a profiler I found that those 2 lines are much faster, probably cause there is no division here + self.cycle_counter += 1; + self.cycle_counter = (self.cycle_counter != self.cycles_to_tick) as u16 * self.cycle_counter; return self.cycle_counter == 0; } From 1d2f1f896baf8a29e371fdfa981587fdba5fafd7 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 18 Sep 2021 15:09:46 +0300 Subject: [PATCH 087/136] Move the sample type behind Custom type I was checking optimization of replacing f32 with u16 or u32 but it seems that f32 is the better option, anyway if another platform will need replacing (or adding a compile condition should be easier). --- gb/src/audio_resampler.rs | 18 +++++++++--------- gb/src/multi_device_audio.rs | 2 +- gb/src/sdl_audio_device.rs | 8 ++++---- gb/src/wav_file_audio_device.rs | 4 ++-- lib_gb/benches/my_bench.rs | 4 ++-- lib_gb/src/apu/audio_device.rs | 17 +++++++++++++---- lib_gb/src/apu/channel.rs | 15 ++++++++------- lib_gb/src/apu/gb_apu.rs | 8 ++++---- lib_gb/src/apu/sound_terminal.rs | 10 +++++----- 9 files changed, 48 insertions(+), 38 deletions(-) diff --git a/gb/src/audio_resampler.rs b/gb/src/audio_resampler.rs index 0001d5e1..d82a796b 100644 --- a/gb/src/audio_resampler.rs +++ b/gb/src/audio_resampler.rs @@ -1,8 +1,8 @@ -use lib_gb::apu::audio_device::Sample; +use lib_gb::apu::audio_device::{SAMPLE_CONSTANT_DEFAULT, Sample, StereoSample}; pub struct AudioResampler{ to_skip:u32, - sampling_buffer:Vec, + sampling_buffer:Vec, sampling_counter:u32 } @@ -20,15 +20,15 @@ impl AudioResampler{ } } - pub fn resample(&mut self, buffer:&[Sample])->Vec{ + pub fn resample(&mut self, buffer:&[StereoSample])->Vec{ let mut output = Vec::new(); for sample in buffer.into_iter(){ self.sampling_buffer.push(*sample); self.sampling_counter += 1; if self.sampling_counter == self.to_skip { - let (interpulated_left_sample, interpulated_right_sample) = Self::interpolate_sample(&self.sampling_buffer); - let interpolated_sample = Sample{left_sample: interpulated_left_sample, right_sample: interpulated_right_sample}; + let interpolated_stereo_sample = Self::interpolate_sample(&self.sampling_buffer); + let interpolated_sample = StereoSample{left_sample: interpolated_stereo_sample.left_sample, right_sample: interpolated_stereo_sample.left_sample}; self.sampling_counter = 0; self.sampling_buffer.clear(); @@ -39,11 +39,11 @@ impl AudioResampler{ return output; } - fn interpolate_sample(samples:&[Sample])->(f32, f32){ + fn interpolate_sample(samples:&[StereoSample])->StereoSample{ - let interpulated_left_sample = samples.iter().fold(0.0, |acc, x| acc + x.left_sample) / samples.len() as f32; - let interpulated_right_sample = samples.iter().fold(0.0, |acc, x| acc + x.right_sample) / samples.len() as f32; + let interpulated_left_sample = samples.iter().fold(SAMPLE_CONSTANT_DEFAULT, |acc, x| acc + x.left_sample) / samples.len() as Sample; + let interpulated_right_sample = samples.iter().fold(SAMPLE_CONSTANT_DEFAULT, |acc, x| acc + x.right_sample) / samples.len() as Sample; - return (interpulated_left_sample, interpulated_right_sample); + return StereoSample{left_sample:interpulated_left_sample, right_sample:interpulated_right_sample}; } } \ No newline at end of file diff --git a/gb/src/multi_device_audio.rs b/gb/src/multi_device_audio.rs index fe169ad1..892c859b 100644 --- a/gb/src/multi_device_audio.rs +++ b/gb/src/multi_device_audio.rs @@ -11,7 +11,7 @@ impl MultiAudioDevice{ } impl AudioDevice for MultiAudioDevice{ - fn push_buffer(&mut self, buffer:&[Sample]) { + fn push_buffer(&mut self, buffer:&[StereoSample]) { for device in self.devices.iter_mut(){ device.push_buffer(buffer); } diff --git a/gb/src/sdl_audio_device.rs b/gb/src/sdl_audio_device.rs index 2346a9f4..da739b7b 100644 --- a/gb/src/sdl_audio_device.rs +++ b/gb/src/sdl_audio_device.rs @@ -11,7 +11,7 @@ pub struct SdlAudioDevie{ device_id: SDL_AudioDeviceID, resampler: AudioResampler, - buffer: Vec + buffer: Vec } impl SdlAudioDevie{ @@ -69,9 +69,9 @@ impl SdlAudioDevie{ } - fn push_audio_to_device(&self, audio:&[f32])->Result<(),&str>{ + fn push_audio_to_device(&self, audio:&[Sample])->Result<(),&str>{ let audio_ptr: *const c_void = audio.as_ptr() as *const c_void; - let data_byte_len = (audio.len() * std::mem::size_of::()) as u32; + let data_byte_len = (audio.len() * std::mem::size_of::()) as u32; unsafe{ while SDL_GetQueuedAudioSize(self.device_id) > BYTES_TO_WAIT{} @@ -87,7 +87,7 @@ impl SdlAudioDevie{ } impl AudioDevice for SdlAudioDevie{ - fn push_buffer(&mut self, buffer:&[Sample]){ + fn push_buffer(&mut self, buffer:&[StereoSample]){ for sample in self.resampler.resample(buffer){ self.buffer.push(sample.left_sample); diff --git a/gb/src/wav_file_audio_device.rs b/gb/src/wav_file_audio_device.rs index 713c581c..3e4a5389 100644 --- a/gb/src/wav_file_audio_device.rs +++ b/gb/src/wav_file_audio_device.rs @@ -6,7 +6,7 @@ pub struct WavfileAudioDevice{ target_frequency:u32, resampler: AudioResampler, filename:&'static str, - samples_buffer:Vec:: + samples_buffer:Vec:: } impl WavfileAudioDevice{ @@ -21,7 +21,7 @@ impl WavfileAudioDevice{ } impl AudioDevice for WavfileAudioDevice{ - fn push_buffer(&mut self, buffer:&[Sample]) { + fn push_buffer(&mut self, buffer:&[StereoSample]) { self.samples_buffer.append(self.resampler.resample(buffer).as_mut()); } } diff --git a/lib_gb/benches/my_bench.rs b/lib_gb/benches/my_bench.rs index c053881c..fde4c31c 100644 --- a/lib_gb/benches/my_bench.rs +++ b/lib_gb/benches/my_bench.rs @@ -7,14 +7,14 @@ use lib_gb::apu::{ pub fn criterion_bench(c: &mut Criterion){ struct StubApu; impl AudioDevice for StubApu{ - fn push_buffer(&mut self, _buffer:&[Sample]){} + fn push_buffer(&mut self, _buffer:&[StereoSample]){} } c.bench_function("test apu", |b| b.iter(||{ let mut apu = GbApu::new(StubApu{}); apu.enabled = true; apu.sweep_tone_channel.enabled = true; - for _ in 0..100000{ + for _ in 0..10{ apu.cycle(255); } })); diff --git a/lib_gb/src/apu/audio_device.rs b/lib_gb/src/apu/audio_device.rs index 76a2f988..a906d1f1 100644 --- a/lib_gb/src/apu/audio_device.rs +++ b/lib_gb/src/apu/audio_device.rs @@ -1,9 +1,18 @@ +pub type Sample = f32; +pub const SAMPLE_CONSTANT_DEFAULT:Sample = 0.0; + #[derive(Copy, Clone)] -pub struct Sample{ - pub left_sample:f32, - pub right_sample:f32 +pub struct StereoSample{ + pub left_sample:Sample, + pub right_sample:Sample +} + +impl StereoSample{ + pub const fn const_defualt()->Self{ + Self{left_sample:SAMPLE_CONSTANT_DEFAULT, right_sample:SAMPLE_CONSTANT_DEFAULT} + } } pub trait AudioDevice{ - fn push_buffer(&mut self, buffer:&[Sample]); + fn push_buffer(&mut self, buffer:&[StereoSample]); } \ No newline at end of file diff --git a/lib_gb/src/apu/channel.rs b/lib_gb/src/apu/channel.rs index 3ddc7fad..58f21c99 100644 --- a/lib_gb/src/apu/channel.rs +++ b/lib_gb/src/apu/channel.rs @@ -1,3 +1,4 @@ +use super::audio_device::{SAMPLE_CONSTANT_DEFAULT, Sample}; use super::sample_producer::SampleProducer; use super::timer::Timer; @@ -9,7 +10,7 @@ pub struct Channel{ pub sample_producer:Procuder, pub timer:Timer, - last_sample:f32, + last_sample:Sample, } impl Channel{ @@ -21,7 +22,7 @@ impl Channel{ length_enable:false, timer: Timer::new(sample_producer.get_updated_frequency_ticks(0)), sample_producer, - last_sample: 0.0 + last_sample: SAMPLE_CONSTANT_DEFAULT } } @@ -44,10 +45,10 @@ impl Channel{ self.timer.update_cycles_to_tick(self.sample_producer.get_updated_frequency_ticks(self.frequency)); self.sample_producer.reset(); - self.last_sample = 0.0; + self.last_sample = SAMPLE_CONSTANT_DEFAULT; } - pub fn get_audio_sample(&mut self)->f32{ + pub fn get_audio_sample(&mut self)->Sample{ if self.enabled{ let sample = if self.timer.cycle(){ @@ -64,10 +65,10 @@ impl Channel{ return self.last_sample; } - return 0.0; + return SAMPLE_CONSTANT_DEFAULT; } - fn convert_digtial_to_analog(&self, sample:u8)->f32{ - (sample as f32 / 7.5 ) - 1.0 + fn convert_digtial_to_analog(&self, sample:u8)->Sample{ + (sample as Sample / 7.5 ) - 1.0 } } diff --git a/lib_gb/src/apu/gb_apu.rs b/lib_gb/src/apu/gb_apu.rs index 1cc35c25..ff34f474 100644 --- a/lib_gb/src/apu/gb_apu.rs +++ b/lib_gb/src/apu/gb_apu.rs @@ -22,7 +22,7 @@ pub struct GbApu{ pub left_terminal:SoundTerminal, pub enabled:bool, - audio_buffer:[Sample;AUDIO_BUFFER_SIZE], + audio_buffer:[StereoSample;AUDIO_BUFFER_SIZE], current_t_cycle:u32, device:Device, last_enabled_state:bool @@ -36,7 +36,7 @@ impl GbApu{ wave_channel:Channel::::new(WaveSampleProducer::default()), tone_channel: Channel::::new(SquareSampleProducer::new()), noise_channel: Channel::::new(NoiseSampleProducer::default()), - audio_buffer:[Sample{left_sample:0.0, right_sample:0.0}; AUDIO_BUFFER_SIZE], + audio_buffer:[StereoSample::const_defualt(); AUDIO_BUFFER_SIZE], current_t_cycle:0, device:device, right_terminal: SoundTerminal::default(), @@ -56,7 +56,7 @@ impl GbApu{ let tick = self.frame_sequencer.cycle(); self.update_channels_for_frame_squencer(tick); - let mut samples:[f32;NUMBER_OF_CHANNELS] = [0.0;NUMBER_OF_CHANNELS]; + let mut samples:[Sample;NUMBER_OF_CHANNELS] = [SAMPLE_CONSTANT_DEFAULT;NUMBER_OF_CHANNELS]; samples[0] = self.sweep_tone_channel.get_audio_sample(); samples[1] = self.tone_channel.get_audio_sample(); samples[2] = self.wave_channel.get_audio_sample(); @@ -75,7 +75,7 @@ impl GbApu{ } else{ for _ in 0..t_cycles{ - self.audio_buffer[self.current_t_cycle as usize] = Sample{right_sample:0.0, left_sample:0.0}; + self.audio_buffer[self.current_t_cycle as usize] = StereoSample::const_defualt(); self.current_t_cycle += 1; self.push_buffer_if_full(); diff --git a/lib_gb/src/apu/sound_terminal.rs b/lib_gb/src/apu/sound_terminal.rs index 68b909b5..b685440c 100644 --- a/lib_gb/src/apu/sound_terminal.rs +++ b/lib_gb/src/apu/sound_terminal.rs @@ -1,4 +1,4 @@ -use super::sound_utils::NUMBER_OF_CHANNELS; +use super::{audio_device::{SAMPLE_CONSTANT_DEFAULT, Sample}, sound_utils::NUMBER_OF_CHANNELS}; pub struct SoundTerminal{ pub enabled:bool, @@ -17,16 +17,16 @@ impl Default for SoundTerminal{ } impl SoundTerminal{ - pub fn mix_terminal_samples(&self, samples:&[f32;NUMBER_OF_CHANNELS])->f32{ - let mut mixed_sample:f32 = 0.0; + pub fn mix_terminal_samples(&self, samples:&[Sample;NUMBER_OF_CHANNELS])->Sample{ + let mut mixed_sample:Sample = SAMPLE_CONSTANT_DEFAULT; for i in 0..NUMBER_OF_CHANNELS{ if self.channels[i]{ mixed_sample += samples[i]; } } - mixed_sample /= NUMBER_OF_CHANNELS as f32; + mixed_sample /= NUMBER_OF_CHANNELS as Sample; - return mixed_sample * (self.volume as f32 + 1.0); + return mixed_sample * (self.volume + 1) as Sample; } } \ No newline at end of file From 8b5b7bba6d22483cf6f386138737cb2f92dff215 Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Mon, 20 Sep 2021 17:00:08 +0300 Subject: [PATCH 088/136] Made the hash calculation function looks nicer Add comment and example of how to run this test code. --- lib_gb/tests/integration_tests.rs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/lib_gb/tests/integration_tests.rs b/lib_gb/tests/integration_tests.rs index b6a12b92..58a13b88 100644 --- a/lib_gb/tests/integration_tests.rs +++ b/lib_gb/tests/integration_tests.rs @@ -103,11 +103,20 @@ fn run_integration_test(program:Vec, frames_to_execute:u32, expected_hash:u6 } -/* -// This test is for clcualting the hash of a new test rom -#[test] -fn calc_hash(){ + +// This function is for clcualting the hash of a new test rom +/// # Examples +/// +///``` +///#[test] +///fn calc_custom_rom_hash(){ +/// calc_hash("path_to_rom"); +///} +///``` +#[allow(dead_code)] +fn calc_hash(rom_path:&str){ static mut FRAMES_COUNTER:u32 = 0; + static mut LAST_HASH:u64 = 0; struct GetHashGfxDevice; impl GfxDevice for GetHashGfxDevice{ fn swap_buffer(&self, buffer:&[u32]) { @@ -130,7 +139,7 @@ fn calc_hash(){ } } - let program = std::fs::read("C:/Users/Alon/Downloads/GameBoy/window_y_trigger_wx_offscreen.gb") + let program = std::fs::read(rom_path) .expect("Error could not find file"); let program = Vec::from(program); @@ -140,5 +149,4 @@ fn calc_hash(){ let mut gameboy = GameBoy::new(&mut mbc, StubJoypadProvider{}, StubAudioDevice{}, GetHashGfxDevice{}); loop {gameboy.cycle_frame();} -} -*/ +} \ No newline at end of file From 3f68cf6ff60599397c1ea05deca6b19ea879d54d Mon Sep 17 00:00:00 2001 From: alloncm Date: Mon, 20 Sep 2021 14:18:38 +0300 Subject: [PATCH 089/136] Change test name Convention is test and not run for tests --- lib_gb/tests/integration_tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_gb/tests/integration_tests.rs b/lib_gb/tests/integration_tests.rs index 58a13b88..21d9d7b5 100644 --- a/lib_gb/tests/integration_tests.rs +++ b/lib_gb/tests/integration_tests.rs @@ -54,7 +54,7 @@ fn test_turtle_window_y_trigger(){ } #[test] -fn run_turtle_window_y_trigger_wx_offscreen(){ +fn test_turtle_window_y_trigger_wx_offscreen(){ run_turtle_integration_test("window_y_trigger_wx_offscreen.gb", 15592061677463553443); } From 800755ff892061fabe7bbb18796bf3b83b3b5761 Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Mon, 20 Sep 2021 16:39:49 +0300 Subject: [PATCH 090/136] The gameboy can exit properly Add proper handling of closing the program --- gb/src/main.rs | 108 +++++++++++++++++++++++++++++-------------------- 1 file changed, 65 insertions(+), 43 deletions(-) diff --git a/gb/src/main.rs b/gb/src/main.rs index d6a5b2b2..3705032b 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -65,9 +65,11 @@ struct SpscGfxDevice{ impl GfxDevice for SpscGfxDevice{ fn swap_buffer(&mut self, buffer:&[u32; SCREEN_WIDTH * SCREEN_HEIGHT]) { - self.producer.push(buffer.clone()).unwrap(); - if self.producer.is_full(){ - self.parker.park(); + if !self.producer.is_abandoned(){ + self.producer.push(buffer.clone()).unwrap(); + if self.producer.is_full() && !self.producer.is_abandoned() { + self.parker.park(); + } } } } @@ -91,48 +93,14 @@ fn main() { let program_name = args[1].clone(); - - std::thread::Builder::new().name("Emualtion Thread".to_string()).spawn(move ||{ - - let audio_device = sdl_audio_device::SdlAudioDevie::new(44100); - let mut devices: Vec::> = Vec::new(); - devices.push(Box::new(audio_device)); - if check_for_terminal_feature_flag(&args, "--file-audio"){ - let wav_ad = wav_file_audio_device::WavfileAudioDevice::new(44100, GB_FREQUENCY, "output.wav"); - devices.push(Box::new(wav_ad)); - } - let audio_devices = MultiAudioDevice::new(devices); - let mut mbc = initialize_mbc(&program_name); - let joypad_provider = SdlJoypadProvider::new(buttons_mapper); - - let mut gameboy = match fs::read("Dependencies/Init/dmg_boot.bin"){ - Result::Ok(file)=>{ - info!("found bootrom!"); - - let mut bootrom:[u8;BOOT_ROM_SIZE] = [0;BOOT_ROM_SIZE]; - for i in 0..BOOT_ROM_SIZE{ - bootrom[i] = file[i]; - } - - GameBoy::new_with_bootrom(&mut mbc, joypad_provider,audio_devices, spsc_gfx_device, bootrom) - } - Result::Err(_)=>{ - info!("could not find bootrom... booting directly to rom"); - - GameBoy::new(&mut mbc, joypad_provider, audio_devices, spsc_gfx_device) - } - }; - - info!("initialized gameboy successfully!"); + let mut running = true; + // Casting to ptr cause you cant pass a raw ptr (*const/mut T) to another thread + let running_ptr:usize = (&running as *const bool) as usize; - loop{ - gameboy.cycle_frame(); - } - - drop(gameboy); - release_mbc(&program_name, mbc); - }).unwrap(); + let emualation_thread = std::thread::Builder::new().name("Emualtion Thread".to_string()).spawn( + move || emulation_thread_main(args, program_name, spsc_gfx_device, running_ptr) + ).unwrap(); unsafe{ let mut event: std::mem::MaybeUninit = std::mem::MaybeUninit::uninit(); @@ -161,6 +129,60 @@ fn main() { start = SDL_GetPerformanceCounter(); } + if !c.is_empty(){ + c.pop().unwrap(); + } + drop(c); + unparker.unpark(); + drop(unparker); + + std::ptr::write_volatile(&mut running as *mut bool, false); + emualation_thread.join().unwrap(); + SDL_Quit(); } +} + +// Receiving usize and not raw ptr cause in rust you cant pass a raw ptr to another thread +fn emulation_thread_main(args: Vec, program_name: String, spsc_gfx_device: SpscGfxDevice, running_ptr: usize) { + let audio_device = sdl_audio_device::SdlAudioDevie::new(44100); + let mut devices: Vec::> = Vec::new(); + devices.push(Box::new(audio_device)); + if check_for_terminal_feature_flag(&args, "--file-audio"){ + let wav_ad = wav_file_audio_device::WavfileAudioDevice::new(44100, GB_FREQUENCY, "output.wav"); + devices.push(Box::new(wav_ad)); + log::info!("Writing audio to file: output.wav"); + } + let audio_devices = MultiAudioDevice::new(devices); + let mut mbc = initialize_mbc(&program_name); + let joypad_provider = SdlJoypadProvider::new(buttons_mapper); + let mut gameboy = match fs::read("Dependencies/Init/dmg_boot.bin"){ + Result::Ok(file)=>{ + info!("found bootrom!"); + + let mut bootrom:[u8;BOOT_ROM_SIZE] = [0;BOOT_ROM_SIZE]; + for i in 0..BOOT_ROM_SIZE{ + bootrom[i] = file[i]; + } + + GameBoy::new_with_bootrom(&mut mbc, joypad_provider,audio_devices, spsc_gfx_device, bootrom) + } + Result::Err(_)=>{ + info!("could not find bootrom... booting directly to rom"); + + GameBoy::new(&mut mbc, joypad_provider, audio_devices, spsc_gfx_device) + } + }; + info!("initialized gameboy successfully!"); + + unsafe{ + let mut running = std::ptr::read_volatile(running_ptr as *const bool); + while running{ + gameboy.cycle_frame(); + running = std::ptr::read_volatile(running_ptr as *const bool); + } + } + drop(gameboy); + release_mbc(&program_name, mbc); + log::info!("Release the gameboy succefully"); } \ No newline at end of file From 40a9470f6a9ec7588b6e9a269947d4701130979b Mon Sep 17 00:00:00 2001 From: alloncm Date: Tue, 21 Sep 2021 18:30:10 +0300 Subject: [PATCH 091/136] Bug fix - updating the wave ram Previously the wave ram was not updated on write, which cause the wave ram samples to be blank. For some reason it did not cause any problems in windows. --- lib_gb/src/apu/apu_registers_updater.rs | 30 ++++++++++++++++++++++++- lib_gb/src/mmu/io_components.rs | 2 ++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/lib_gb/src/apu/apu_registers_updater.rs b/lib_gb/src/apu/apu_registers_updater.rs index c7f4a20d..bd4eb793 100644 --- a/lib_gb/src/apu/apu_registers_updater.rs +++ b/lib_gb/src/apu/apu_registers_updater.rs @@ -1,5 +1,4 @@ use crate::{mmu::io_ports::*, utils::bit_masks::*}; - use super::{ audio_device::AudioDevice, channel::Channel, @@ -175,6 +174,35 @@ pub fn set_nr24(channel:&mut Channel, fs:&FrameSequencer, } } +pub fn set_wave_ram(wave_channel: &mut Channel, address:u16, value:u8){ + // This is not the most accurate behaviour. + // Om DMG if accessing within a small time margin after the apu accessed this ram, + // you will access the current byte the apu accessed, otherwise it will do noting. + // On other hardware it will access the current byte the apu accessed, + // currently implementing it like the original DMG on CGB will need to handle it differently. + if !wave_channel.enabled{ + wave_channel.sample_producer.wave_samples[address as usize - 0x30] = value; + } + else{ + log::warn!("Writing wave channel when its on"); + } +} + +pub fn get_wave_ram(wave_channel: &Channel, address:u16)->u8{ + // This is not the most accurate behaviour. + // Om DMG if accessing within a small time margin after the apu accessed this ram, + // you will access the current byte the apu accessed, otherwise it will return 0xFF. + // On other hardware it will access the current byte the apu accessed, + // currently implementing it like the original DMG on CGB will need to handle it differently. + if !wave_channel.enabled{ + wave_channel.sample_producer.wave_samples[address as usize - 0x30] + } + else{ + log::warn!("Reading wave channel when its on"); + 0xFF + } +} + fn update_channel_conrol_register(channel:&mut Channel, dac_enabled:bool, control_register:u8, max_sound_length:u16, fs:&FrameSequencer){ diff --git a/lib_gb/src/mmu/io_components.rs b/lib_gb/src/mmu/io_components.rs index 25a3068e..a93e30b0 100644 --- a/lib_gb/src/mmu/io_components.rs +++ b/lib_gb/src/mmu/io_components.rs @@ -67,6 +67,7 @@ impl Memory for IoComponents{ value } 0x27..=0x2F => 0xFF, //Not used + 0x30..=0x3F => get_wave_ram(&self.apu.wave_channel, address), //PPU STAT_REGISTER_INDEX=> get_stat(&self.ppu), LY_REGISTER_INDEX=> get_ly(&self.ppu), @@ -114,6 +115,7 @@ impl Memory for IoComponents{ NR50_REGISTER_INDEX=> set_nr50(&mut self.apu, value), NR51_REGISTER_INDEX=> set_nr51(&mut self.apu, value), NR52_REGISTER_INDEX=> set_nr52(&mut self.apu, &mut self.ports,value), + 0x30..=0x3F => set_wave_ram(&mut self.apu.wave_channel, address, value), //PPU LCDC_REGISTER_INDEX=> handle_lcdcontrol_register(value, &mut self.ppu), STAT_REGISTER_INDEX=> { From 024c84d823a3c45126663ca375c62d8cbb2d0079 Mon Sep 17 00:00:00 2001 From: alloncm Date: Tue, 21 Sep 2021 18:30:34 +0300 Subject: [PATCH 092/136] Add bug fix branch convention to the ci file. --- .github/workflows/rust.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index f92cf6cf..9fd17576 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -4,7 +4,7 @@ on: push: branches: [ dev, master ] pull_request: - branches: [ master, dev, feature/* ] + branches: [ dev, master, feature/*, fix/* ] env: CARGO_TERM_COLOR: always From 7653c923db9e69e3b8df9842e6eb6f0a15346ba7 Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 24 Sep 2021 11:51:23 +0300 Subject: [PATCH 093/136] This fixes the apu probelm More info in the issue: #58 Might just move to u16 for the representation if the samples --- lib_gb/src/apu/sound_terminal.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_gb/src/apu/sound_terminal.rs b/lib_gb/src/apu/sound_terminal.rs index 68b909b5..454ae43b 100644 --- a/lib_gb/src/apu/sound_terminal.rs +++ b/lib_gb/src/apu/sound_terminal.rs @@ -27,6 +27,6 @@ impl SoundTerminal{ mixed_sample /= NUMBER_OF_CHANNELS as f32; - return mixed_sample * (self.volume as f32 + 1.0); + return mixed_sample * ((self.volume + 1) as f32 / 10.0); } } \ No newline at end of file From 6c65b5d71fe2bef1ed8161be2fc7c393b2b2c540 Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 24 Sep 2021 12:48:30 +0300 Subject: [PATCH 094/136] Replace magik numbers in io_component with consts --- lib_gb/src/mmu/io_components.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib_gb/src/mmu/io_components.rs b/lib_gb/src/mmu/io_components.rs index a93e30b0..fc18efc4 100644 --- a/lib_gb/src/mmu/io_components.rs +++ b/lib_gb/src/mmu/io_components.rs @@ -7,9 +7,9 @@ use crate::timer::gb_timer::GbTimer; use super::{access_bus::AccessBus, memory::*, oam_dma_transfer::OamDmaTransfer, ram::Ram}; use super::io_ports::*; - pub const IO_PORTS_SIZE:usize = 0x80; - +const WAVE_RAM_START_INDEX:u16 = 0x30; +const WAVE_RAM_END_INDEX:u16 = 0x3F; pub struct IoComponents{ pub ram: Ram, @@ -36,7 +36,6 @@ io_port_index!(OBP0_REGISTER_INDEX, OBP0_REGISTER_ADDRESS); io_port_index!(OBP1_REGISTER_INDEX, OBP1_REGISTER_ADDRESS); io_port_index!(IF_REGISTER_INDEX, IF_REGISTER_ADDRESS); - impl Memory for IoComponents{ fn read(&self, address:u16)->u8 { let mut value = self.ports[address as usize]; @@ -67,7 +66,7 @@ impl Memory for IoComponents{ value } 0x27..=0x2F => 0xFF, //Not used - 0x30..=0x3F => get_wave_ram(&self.apu.wave_channel, address), + WAVE_RAM_START_INDEX..=WAVE_RAM_END_INDEX => get_wave_ram(&self.apu.wave_channel, address), //PPU STAT_REGISTER_INDEX=> get_stat(&self.ppu), LY_REGISTER_INDEX=> get_ly(&self.ppu), @@ -115,7 +114,7 @@ impl Memory for IoComponents{ NR50_REGISTER_INDEX=> set_nr50(&mut self.apu, value), NR51_REGISTER_INDEX=> set_nr51(&mut self.apu, value), NR52_REGISTER_INDEX=> set_nr52(&mut self.apu, &mut self.ports,value), - 0x30..=0x3F => set_wave_ram(&mut self.apu.wave_channel, address, value), + WAVE_RAM_START_INDEX..=WAVE_RAM_END_INDEX => set_wave_ram(&mut self.apu.wave_channel, address, value), //PPU LCDC_REGISTER_INDEX=> handle_lcdcontrol_register(value, &mut self.ppu), STAT_REGISTER_INDEX=> { From 732518ac3471b180da1f8e839858e16d5a18feca Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 24 Sep 2021 16:32:43 +0300 Subject: [PATCH 095/136] The now tests compiles after the merge The merge broke some code in the tests --- lib_gb/tests/integration_tests.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib_gb/tests/integration_tests.rs b/lib_gb/tests/integration_tests.rs index 21d9d7b5..2881a952 100644 --- a/lib_gb/tests/integration_tests.rs +++ b/lib_gb/tests/integration_tests.rs @@ -1,6 +1,7 @@ use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; use std::io::Read; +use lib_gb::ppu::gb_ppu::{SCREEN_HEIGHT, SCREEN_WIDTH}; use lib_gb::{ apu::audio_device::AudioDevice, keypad::joypad_provider::JoypadProvider, machine::{gameboy::GameBoy, mbc_initializer::initialize_mbc}, ppu::gfx_device::GfxDevice @@ -12,7 +13,7 @@ struct CheckHashGfxDevice{ found_p:*mut bool, } impl GfxDevice for CheckHashGfxDevice{ - fn swap_buffer(&self, buffer:&[u32]) { + fn swap_buffer(&mut self, buffer:&[u32; SCREEN_HEIGHT * SCREEN_WIDTH]) { let mut s = DefaultHasher::new(); buffer.hash(&mut s); let hash = s.finish(); @@ -119,7 +120,7 @@ fn calc_hash(rom_path:&str){ static mut LAST_HASH:u64 = 0; struct GetHashGfxDevice; impl GfxDevice for GetHashGfxDevice{ - fn swap_buffer(&self, buffer:&[u32]) { + fn swap_buffer(&mut self, buffer:&[u32; SCREEN_HEIGHT * SCREEN_WIDTH]) { unsafe{ if FRAMES_COUNTER < 700{ FRAMES_COUNTER += 1; From 2c73bf7a8e7d3f76d347142ff50f1f0afcaf9508 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 9 Oct 2021 01:43:53 +0300 Subject: [PATCH 096/136] Move from rtrb to crossbeam channel Which turn to be faster (with blocking involved. --- Cargo.lock | 18 +----------------- gb/Cargo.toml | 3 +-- gb/src/main.rs | 46 +++++++++++++--------------------------------- 3 files changed, 15 insertions(+), 52 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 132ef785..0c74f177 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -97,12 +97,6 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "cache-padded" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba" - [[package]] name = "cast" version = "0.2.7" @@ -627,11 +621,10 @@ name = "magenboy" version = "1.0.0" dependencies = [ "chrono", - "crossbeam-utils", + "crossbeam-channel", "fern", "lib_gb", "log", - "rtrb", "sdl2", "wav", ] @@ -1018,15 +1011,6 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9b1a3d5f46d53f4a3478e2be4a5a5ce5108ea58b100dcd139830eae7f79a3a1" -[[package]] -name = "rtrb" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "318256ac02f7e11a48a10339ba5dca8bd7eb17496abf384e8ea909bb2ae5275f" -dependencies = [ - "cache-padded", -] - [[package]] name = "rustc_version" version = "0.4.0" diff --git a/gb/Cargo.toml b/gb/Cargo.toml index 32547146..595ceb6b 100644 --- a/gb/Cargo.toml +++ b/gb/Cargo.toml @@ -12,5 +12,4 @@ fern = "0.6.0" chrono = "0.4" sdl2 = {version = "0.34", features = ["bundled","static-link"]} wav = "0.6.0" -crossbeam-utils = "0.8" -rtrb = "0.1" \ No newline at end of file +crossbeam-channel = "0.5" \ No newline at end of file diff --git a/gb/src/main.rs b/gb/src/main.rs index 3705032b..39c2e1ce 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -58,19 +58,13 @@ fn check_for_terminal_feature_flag(args:&Vec::, flag:&str)->bool{ args.len() >= 3 && args.contains(&String::from(flag)) } -struct SpscGfxDevice{ - producer: rtrb::Producer<[u32;SCREEN_HEIGHT * SCREEN_WIDTH ]>, - parker: crossbeam_utils::sync::Parker, +struct MpmcGfxDevice{ + sender: crossbeam_channel::Sender<[u32;SCREEN_HEIGHT * SCREEN_WIDTH]> } -impl GfxDevice for SpscGfxDevice{ - fn swap_buffer(&mut self, buffer:&[u32; SCREEN_WIDTH * SCREEN_HEIGHT]) { - if !self.producer.is_abandoned(){ - self.producer.push(buffer.clone()).unwrap(); - if self.producer.is_full() && !self.producer.is_abandoned() { - self.parker.park(); - } - } +impl GfxDevice for MpmcGfxDevice{ + fn swap_buffer(&mut self, buffer:&[u32; SCREEN_HEIGHT * SCREEN_WIDTH]) { + self.sender.send(buffer.clone()).unwrap(); } } @@ -85,12 +79,9 @@ fn main() { } let mut sdl_gfx_device = sdl_gfx_device::SdlGfxDevice::new(SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32, "MagenBoy", SCREEN_SCALE); - - let (producer, mut c) = rtrb::RingBuffer::<[u32; SCREEN_HEIGHT * SCREEN_WIDTH]>::new(1).split(); - let parker = crossbeam_utils::sync::Parker::new(); - let unparker = parker.unparker().clone(); - let spsc_gfx_device = SpscGfxDevice{producer, parker: parker}; + let (s,r) = crossbeam_channel::bounded(1); + let mpmc_device = MpmcGfxDevice{sender:s}; let program_name = args[1].clone(); @@ -99,7 +90,7 @@ fn main() { let running_ptr:usize = (&running as *const bool) as usize; let emualation_thread = std::thread::Builder::new().name("Emualtion Thread".to_string()).spawn( - move || emulation_thread_main(args, program_name, spsc_gfx_device, running_ptr) + move || emulation_thread_main(args, program_name, mpmc_device, running_ptr) ).unwrap(); unsafe{ @@ -113,12 +104,10 @@ fn main() { break; } } + + let buffer = r.recv().unwrap(); + sdl_gfx_device.swap_buffer(&buffer); - if !c.is_empty(){ - let pop = c.pop().unwrap(); - unparker.unpark(); - sdl_gfx_device.swap_buffer(&pop); - } let end = SDL_GetPerformanceCounter(); let elapsed_ms:f64 = (end - start) as f64 / SDL_GetPerformanceFrequency() as f64 * 1000.0; @@ -129,13 +118,6 @@ fn main() { start = SDL_GetPerformanceCounter(); } - if !c.is_empty(){ - c.pop().unwrap(); - } - drop(c); - unparker.unpark(); - drop(unparker); - std::ptr::write_volatile(&mut running as *mut bool, false); emualation_thread.join().unwrap(); @@ -144,7 +126,7 @@ fn main() { } // Receiving usize and not raw ptr cause in rust you cant pass a raw ptr to another thread -fn emulation_thread_main(args: Vec, program_name: String, spsc_gfx_device: SpscGfxDevice, running_ptr: usize) { +fn emulation_thread_main(args: Vec, program_name: String, spsc_gfx_device: MpmcGfxDevice, running_ptr: usize) { let audio_device = sdl_audio_device::SdlAudioDevie::new(44100); let mut devices: Vec::> = Vec::new(); devices.push(Box::new(audio_device)); @@ -176,10 +158,8 @@ fn emulation_thread_main(args: Vec, program_name: String, spsc_gfx_devic info!("initialized gameboy successfully!"); unsafe{ - let mut running = std::ptr::read_volatile(running_ptr as *const bool); - while running{ + while std::ptr::read_volatile(running_ptr as *const bool){ gameboy.cycle_frame(); - running = std::ptr::read_volatile(running_ptr as *const bool); } } drop(gameboy); From 60d5f51ea97460b317fa7d877aadce33a6a8b5bd Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 9 Oct 2021 18:19:22 +0300 Subject: [PATCH 097/136] Reduce the apu bench loop to a sane number --- lib_gb/benches/my_bench.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_gb/benches/my_bench.rs b/lib_gb/benches/my_bench.rs index c053881c..430a12e2 100644 --- a/lib_gb/benches/my_bench.rs +++ b/lib_gb/benches/my_bench.rs @@ -14,7 +14,7 @@ pub fn criterion_bench(c: &mut Criterion){ let mut apu = GbApu::new(StubApu{}); apu.enabled = true; apu.sweep_tone_channel.enabled = true; - for _ in 0..100000{ + for _ in 0..100{ apu.cycle(255); } })); From 95a3aa05ae031dd6046b0bd730b6fd5f1875221c Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 9 Oct 2021 19:15:51 +0300 Subject: [PATCH 098/136] Add sample type In order to abstract the sample type primitive. --- gb/src/audio_resampler.rs | 17 ++++++++--------- gb/src/multi_device_audio.rs | 2 +- gb/src/sdl_audio_device.rs | 6 +++--- gb/src/wav_file_audio_device.rs | 4 ++-- lib_gb/benches/my_bench.rs | 2 +- lib_gb/src/apu/audio_device.rs | 11 +++++++---- lib_gb/src/apu/channel.rs | 11 ++++++----- lib_gb/src/apu/gb_apu.rs | 8 ++++---- lib_gb/src/apu/sound_terminal.rs | 6 +++--- lib_gb/tests/integration_tests.rs | 2 +- 10 files changed, 36 insertions(+), 33 deletions(-) diff --git a/gb/src/audio_resampler.rs b/gb/src/audio_resampler.rs index 0001d5e1..182d466a 100644 --- a/gb/src/audio_resampler.rs +++ b/gb/src/audio_resampler.rs @@ -1,8 +1,8 @@ -use lib_gb::apu::audio_device::Sample; +use lib_gb::apu::audio_device::{DEFAULT_SAPMPLE, Sample, StereoSample}; pub struct AudioResampler{ to_skip:u32, - sampling_buffer:Vec, + sampling_buffer:Vec, sampling_counter:u32 } @@ -20,15 +20,14 @@ impl AudioResampler{ } } - pub fn resample(&mut self, buffer:&[Sample])->Vec{ + pub fn resample(&mut self, buffer:&[StereoSample])->Vec{ let mut output = Vec::new(); for sample in buffer.into_iter(){ self.sampling_buffer.push(*sample); self.sampling_counter += 1; if self.sampling_counter == self.to_skip { - let (interpulated_left_sample, interpulated_right_sample) = Self::interpolate_sample(&self.sampling_buffer); - let interpolated_sample = Sample{left_sample: interpulated_left_sample, right_sample: interpulated_right_sample}; + let interpolated_sample = Self::interpolate_sample(&self.sampling_buffer); self.sampling_counter = 0; self.sampling_buffer.clear(); @@ -39,11 +38,11 @@ impl AudioResampler{ return output; } - fn interpolate_sample(samples:&[Sample])->(f32, f32){ + fn interpolate_sample(samples:&[StereoSample])->StereoSample{ - let interpulated_left_sample = samples.iter().fold(0.0, |acc, x| acc + x.left_sample) / samples.len() as f32; - let interpulated_right_sample = samples.iter().fold(0.0, |acc, x| acc + x.right_sample) / samples.len() as f32; + let interpulated_left_sample = samples.iter().fold(DEFAULT_SAPMPLE, |acc, x| acc + x.left_sample) / samples.len() as Sample; + let interpulated_right_sample = samples.iter().fold(DEFAULT_SAPMPLE, |acc, x| acc + x.right_sample) / samples.len() as Sample; - return (interpulated_left_sample, interpulated_right_sample); + return StereoSample{left_sample: interpulated_left_sample,right_sample: interpulated_right_sample}; } } \ No newline at end of file diff --git a/gb/src/multi_device_audio.rs b/gb/src/multi_device_audio.rs index fe169ad1..892c859b 100644 --- a/gb/src/multi_device_audio.rs +++ b/gb/src/multi_device_audio.rs @@ -11,7 +11,7 @@ impl MultiAudioDevice{ } impl AudioDevice for MultiAudioDevice{ - fn push_buffer(&mut self, buffer:&[Sample]) { + fn push_buffer(&mut self, buffer:&[StereoSample]) { for device in self.devices.iter_mut(){ device.push_buffer(buffer); } diff --git a/gb/src/sdl_audio_device.rs b/gb/src/sdl_audio_device.rs index 2346a9f4..6a78c0a4 100644 --- a/gb/src/sdl_audio_device.rs +++ b/gb/src/sdl_audio_device.rs @@ -11,7 +11,7 @@ pub struct SdlAudioDevie{ device_id: SDL_AudioDeviceID, resampler: AudioResampler, - buffer: Vec + buffer: Vec } impl SdlAudioDevie{ @@ -19,7 +19,7 @@ impl SdlAudioDevie{ let desired_audio_spec = SDL_AudioSpec{ freq: frequency, - format: AUDIO_F32SYS as u16, + format: AUDIO_S16SYS as u16, channels: 2, silence: 0, samples: BUFFER_SIZE as u16, @@ -87,7 +87,7 @@ impl SdlAudioDevie{ } impl AudioDevice for SdlAudioDevie{ - fn push_buffer(&mut self, buffer:&[Sample]){ + fn push_buffer(&mut self, buffer:&[StereoSample]){ for sample in self.resampler.resample(buffer){ self.buffer.push(sample.left_sample); diff --git a/gb/src/wav_file_audio_device.rs b/gb/src/wav_file_audio_device.rs index 713c581c..3e4a5389 100644 --- a/gb/src/wav_file_audio_device.rs +++ b/gb/src/wav_file_audio_device.rs @@ -6,7 +6,7 @@ pub struct WavfileAudioDevice{ target_frequency:u32, resampler: AudioResampler, filename:&'static str, - samples_buffer:Vec:: + samples_buffer:Vec:: } impl WavfileAudioDevice{ @@ -21,7 +21,7 @@ impl WavfileAudioDevice{ } impl AudioDevice for WavfileAudioDevice{ - fn push_buffer(&mut self, buffer:&[Sample]) { + fn push_buffer(&mut self, buffer:&[StereoSample]) { self.samples_buffer.append(self.resampler.resample(buffer).as_mut()); } } diff --git a/lib_gb/benches/my_bench.rs b/lib_gb/benches/my_bench.rs index 430a12e2..d26a5793 100644 --- a/lib_gb/benches/my_bench.rs +++ b/lib_gb/benches/my_bench.rs @@ -7,7 +7,7 @@ use lib_gb::apu::{ pub fn criterion_bench(c: &mut Criterion){ struct StubApu; impl AudioDevice for StubApu{ - fn push_buffer(&mut self, _buffer:&[Sample]){} + fn push_buffer(&mut self, _buffer:&[StereoSample]){} } c.bench_function("test apu", |b| b.iter(||{ diff --git a/lib_gb/src/apu/audio_device.rs b/lib_gb/src/apu/audio_device.rs index 76a2f988..f98036e5 100644 --- a/lib_gb/src/apu/audio_device.rs +++ b/lib_gb/src/apu/audio_device.rs @@ -1,9 +1,12 @@ +pub type Sample = f32; +pub const DEFAULT_SAPMPLE:Sample = 0 as Sample; + #[derive(Copy, Clone)] -pub struct Sample{ - pub left_sample:f32, - pub right_sample:f32 +pub struct StereoSample{ + pub left_sample:Sample, + pub right_sample:Sample } pub trait AudioDevice{ - fn push_buffer(&mut self, buffer:&[Sample]); + fn push_buffer(&mut self, buffer:&[StereoSample]); } \ No newline at end of file diff --git a/lib_gb/src/apu/channel.rs b/lib_gb/src/apu/channel.rs index 3ddc7fad..c87ff413 100644 --- a/lib_gb/src/apu/channel.rs +++ b/lib_gb/src/apu/channel.rs @@ -1,3 +1,4 @@ +use super::audio_device::{DEFAULT_SAPMPLE, Sample}; use super::sample_producer::SampleProducer; use super::timer::Timer; @@ -9,7 +10,7 @@ pub struct Channel{ pub sample_producer:Procuder, pub timer:Timer, - last_sample:f32, + last_sample:Sample, } impl Channel{ @@ -21,7 +22,7 @@ impl Channel{ length_enable:false, timer: Timer::new(sample_producer.get_updated_frequency_ticks(0)), sample_producer, - last_sample: 0.0 + last_sample: DEFAULT_SAPMPLE } } @@ -44,10 +45,10 @@ impl Channel{ self.timer.update_cycles_to_tick(self.sample_producer.get_updated_frequency_ticks(self.frequency)); self.sample_producer.reset(); - self.last_sample = 0.0; + self.last_sample = DEFAULT_SAPMPLE; } - pub fn get_audio_sample(&mut self)->f32{ + pub fn get_audio_sample(&mut self)->Sample{ if self.enabled{ let sample = if self.timer.cycle(){ @@ -64,7 +65,7 @@ impl Channel{ return self.last_sample; } - return 0.0; + return DEFAULT_SAPMPLE; } fn convert_digtial_to_analog(&self, sample:u8)->f32{ diff --git a/lib_gb/src/apu/gb_apu.rs b/lib_gb/src/apu/gb_apu.rs index 1cc35c25..7ca2f860 100644 --- a/lib_gb/src/apu/gb_apu.rs +++ b/lib_gb/src/apu/gb_apu.rs @@ -22,7 +22,7 @@ pub struct GbApu{ pub left_terminal:SoundTerminal, pub enabled:bool, - audio_buffer:[Sample;AUDIO_BUFFER_SIZE], + audio_buffer:[StereoSample;AUDIO_BUFFER_SIZE], current_t_cycle:u32, device:Device, last_enabled_state:bool @@ -36,7 +36,7 @@ impl GbApu{ wave_channel:Channel::::new(WaveSampleProducer::default()), tone_channel: Channel::::new(SquareSampleProducer::new()), noise_channel: Channel::::new(NoiseSampleProducer::default()), - audio_buffer:[Sample{left_sample:0.0, right_sample:0.0}; AUDIO_BUFFER_SIZE], + audio_buffer:[StereoSample{left_sample:DEFAULT_SAPMPLE, right_sample:DEFAULT_SAPMPLE}; AUDIO_BUFFER_SIZE], current_t_cycle:0, device:device, right_terminal: SoundTerminal::default(), @@ -56,7 +56,7 @@ impl GbApu{ let tick = self.frame_sequencer.cycle(); self.update_channels_for_frame_squencer(tick); - let mut samples:[f32;NUMBER_OF_CHANNELS] = [0.0;NUMBER_OF_CHANNELS]; + let mut samples:[Sample;NUMBER_OF_CHANNELS] = [DEFAULT_SAPMPLE ; NUMBER_OF_CHANNELS]; samples[0] = self.sweep_tone_channel.get_audio_sample(); samples[1] = self.tone_channel.get_audio_sample(); samples[2] = self.wave_channel.get_audio_sample(); @@ -75,7 +75,7 @@ impl GbApu{ } else{ for _ in 0..t_cycles{ - self.audio_buffer[self.current_t_cycle as usize] = Sample{right_sample:0.0, left_sample:0.0}; + self.audio_buffer[self.current_t_cycle as usize] = StereoSample{right_sample:DEFAULT_SAPMPLE, left_sample:DEFAULT_SAPMPLE}; self.current_t_cycle += 1; self.push_buffer_if_full(); diff --git a/lib_gb/src/apu/sound_terminal.rs b/lib_gb/src/apu/sound_terminal.rs index 454ae43b..e216d897 100644 --- a/lib_gb/src/apu/sound_terminal.rs +++ b/lib_gb/src/apu/sound_terminal.rs @@ -1,4 +1,4 @@ -use super::sound_utils::NUMBER_OF_CHANNELS; +use super::{audio_device::{DEFAULT_SAPMPLE, Sample}, sound_utils::NUMBER_OF_CHANNELS}; pub struct SoundTerminal{ pub enabled:bool, @@ -17,8 +17,8 @@ impl Default for SoundTerminal{ } impl SoundTerminal{ - pub fn mix_terminal_samples(&self, samples:&[f32;NUMBER_OF_CHANNELS])->f32{ - let mut mixed_sample:f32 = 0.0; + pub fn mix_terminal_samples(&self, samples:&[Sample;NUMBER_OF_CHANNELS])->Sample{ + let mut mixed_sample:Sample = DEFAULT_SAPMPLE; for i in 0..NUMBER_OF_CHANNELS{ if self.channels[i]{ mixed_sample += samples[i]; diff --git a/lib_gb/tests/integration_tests.rs b/lib_gb/tests/integration_tests.rs index 2881a952..7fe1eef5 100644 --- a/lib_gb/tests/integration_tests.rs +++ b/lib_gb/tests/integration_tests.rs @@ -29,7 +29,7 @@ impl GfxDevice for CheckHashGfxDevice{ struct StubAudioDevice; impl AudioDevice for StubAudioDevice{ - fn push_buffer(&mut self, _buffer:&[lib_gb::apu::audio_device::Sample]) {} + fn push_buffer(&mut self, _buffer:&[lib_gb::apu::audio_device::StereoSample]) {} } struct StubJoypadProvider; From 9ea63eade586da0a7bf47e8e0eb698922ceb2378 Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 15 Oct 2021 20:23:57 +0300 Subject: [PATCH 099/136] Move to i16 samples This should improve performance of the sound terminal --- gb/src/sdl_audio_device.rs | 2 +- gb/src/wav_file_audio_device.rs | 4 ++-- lib_gb/benches/my_bench.rs | 32 +++++++++++++++++++++++++++++--- lib_gb/src/apu/audio_device.rs | 2 +- lib_gb/src/apu/channel.rs | 7 +------ lib_gb/src/apu/sound_terminal.rs | 11 +++++------ 6 files changed, 39 insertions(+), 19 deletions(-) diff --git a/gb/src/sdl_audio_device.rs b/gb/src/sdl_audio_device.rs index 6a78c0a4..b3288994 100644 --- a/gb/src/sdl_audio_device.rs +++ b/gb/src/sdl_audio_device.rs @@ -69,7 +69,7 @@ impl SdlAudioDevie{ } - fn push_audio_to_device(&self, audio:&[f32])->Result<(),&str>{ + fn push_audio_to_device(&self, audio:&[Sample])->Result<(),&str>{ let audio_ptr: *const c_void = audio.as_ptr() as *const c_void; let data_byte_len = (audio.len() * std::mem::size_of::()) as u32; diff --git a/gb/src/wav_file_audio_device.rs b/gb/src/wav_file_audio_device.rs index 3e4a5389..230c2ae1 100644 --- a/gb/src/wav_file_audio_device.rs +++ b/gb/src/wav_file_audio_device.rs @@ -28,14 +28,14 @@ impl AudioDevice for WavfileAudioDevice{ impl Drop for WavfileAudioDevice{ fn drop(&mut self) { - let header = wav::header::Header::new(wav::WAV_FORMAT_IEEE_FLOAT, 2, self.target_frequency, 32); + let header = wav::header::Header::new(wav::WAV_FORMAT_PCM, 2, self.target_frequency, 16); let mut floats = Vec::with_capacity(self.samples_buffer.len() * 2); for sample in self.samples_buffer.iter(){ floats.push(sample.left_sample); floats.push(sample.right_sample); } - let data = wav::BitDepth::ThirtyTwoFloat(floats); + let data = wav::BitDepth::Sixteen(floats); let mut otuput_file = std::fs::File::create(self.filename).unwrap(); wav::write(header, &data, &mut otuput_file).unwrap(); } diff --git a/lib_gb/benches/my_bench.rs b/lib_gb/benches/my_bench.rs index d26a5793..555d8c28 100644 --- a/lib_gb/benches/my_bench.rs +++ b/lib_gb/benches/my_bench.rs @@ -1,7 +1,8 @@ use criterion::*; use lib_gb::apu::{ - gb_apu::*, - audio_device::*, + audio_device::*, channel::Channel, + gb_apu::*, sound_terminal::SoundTerminal, + square_sample_producer::SquareSampleProducer }; pub fn criterion_bench(c: &mut Criterion){ @@ -20,5 +21,30 @@ pub fn criterion_bench(c: &mut Criterion){ })); } -criterion_group!(benches, criterion_bench); +pub fn apu_sweep_tone_channel(c: &mut Criterion){ + + c.bench_function("test square channel", |b|b.iter(||{ + let mut channel = Channel::::new(SquareSampleProducer::new_with_sweep()); + channel.sound_length = 63; + channel.enabled = true; + channel.length_enable = true; + while channel.enabled{ + let _ = channel.get_audio_sample(); + channel.update_length_register(); + } + })); +} + +pub fn apu_sound_terminal(c:&mut Criterion){ + let mut sound_terminal = SoundTerminal::default(); + sound_terminal.channels = [true;4]; + sound_terminal.volume = 8; + c.bench_function("Sound terminal", |b| b.iter(||{ + + let samples:[Sample;4] = [100 as Sample,200 as Sample,5 as Sample,7 as Sample]; + let _ = sound_terminal.mix_terminal_samples(&samples); + })); +} + +criterion_group!(benches, criterion_bench, apu_sweep_tone_channel, apu_sound_terminal); criterion_main!(benches); \ No newline at end of file diff --git a/lib_gb/src/apu/audio_device.rs b/lib_gb/src/apu/audio_device.rs index f98036e5..38c88d34 100644 --- a/lib_gb/src/apu/audio_device.rs +++ b/lib_gb/src/apu/audio_device.rs @@ -1,4 +1,4 @@ -pub type Sample = f32; +pub type Sample = i16; pub const DEFAULT_SAPMPLE:Sample = 0 as Sample; #[derive(Copy, Clone)] diff --git a/lib_gb/src/apu/channel.rs b/lib_gb/src/apu/channel.rs index c87ff413..9d7d6a41 100644 --- a/lib_gb/src/apu/channel.rs +++ b/lib_gb/src/apu/channel.rs @@ -53,8 +53,7 @@ impl Channel{ let sample = if self.timer.cycle(){ self.timer.update_cycles_to_tick(self.sample_producer.get_updated_frequency_ticks(self.frequency)); - let s = self.sample_producer.produce(); - self.convert_digtial_to_analog(s) + self.sample_producer.produce() as Sample } else{ self.last_sample @@ -67,8 +66,4 @@ impl Channel{ return DEFAULT_SAPMPLE; } - - fn convert_digtial_to_analog(&self, sample:u8)->f32{ - (sample as f32 / 7.5 ) - 1.0 - } } diff --git a/lib_gb/src/apu/sound_terminal.rs b/lib_gb/src/apu/sound_terminal.rs index e216d897..a009cc1f 100644 --- a/lib_gb/src/apu/sound_terminal.rs +++ b/lib_gb/src/apu/sound_terminal.rs @@ -20,13 +20,12 @@ impl SoundTerminal{ pub fn mix_terminal_samples(&self, samples:&[Sample;NUMBER_OF_CHANNELS])->Sample{ let mut mixed_sample:Sample = DEFAULT_SAPMPLE; for i in 0..NUMBER_OF_CHANNELS{ - if self.channels[i]{ - mixed_sample += samples[i]; - } + // This code should add the samples[i] only if channels[i] it true. + // After profiling this code is faster than if and since this is a hot spot in the code + // Im writing it like this. + mixed_sample += samples[i] * self.channels[i] as u8 as Sample; } - mixed_sample /= NUMBER_OF_CHANNELS as f32; - - return mixed_sample * ((self.volume + 1) as f32 / 10.0); + return mixed_sample * ((self.volume + 1) as Sample); } } \ No newline at end of file From e519d50e2823acab9d45e3424deddb33ee6f4c81 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 16 Oct 2021 12:12:59 +0300 Subject: [PATCH 100/136] Fix compile error Casued by merge shit --- lib_gb/src/apu/audio_device.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_gb/src/apu/audio_device.rs b/lib_gb/src/apu/audio_device.rs index a4e684c2..63b0080e 100644 --- a/lib_gb/src/apu/audio_device.rs +++ b/lib_gb/src/apu/audio_device.rs @@ -9,7 +9,7 @@ pub struct StereoSample{ impl StereoSample{ pub const fn const_defualt()->Self{ - Self{left_sample:SAMPLE_CONSTANT_DEFAULT, right_sample:SAMPLE_CONSTANT_DEFAULT} + Self{left_sample:DEFAULT_SAPMPLE, right_sample:DEFAULT_SAPMPLE} } } From 68035793833e2a75dcc3cb140b7f0c220604ca57 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 16 Oct 2021 12:14:19 +0300 Subject: [PATCH 101/136] Use the const default sample --- lib_gb/src/apu/gb_apu.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib_gb/src/apu/gb_apu.rs b/lib_gb/src/apu/gb_apu.rs index 7ca2f860..e0b72c24 100644 --- a/lib_gb/src/apu/gb_apu.rs +++ b/lib_gb/src/apu/gb_apu.rs @@ -36,7 +36,7 @@ impl GbApu{ wave_channel:Channel::::new(WaveSampleProducer::default()), tone_channel: Channel::::new(SquareSampleProducer::new()), noise_channel: Channel::::new(NoiseSampleProducer::default()), - audio_buffer:[StereoSample{left_sample:DEFAULT_SAPMPLE, right_sample:DEFAULT_SAPMPLE}; AUDIO_BUFFER_SIZE], + audio_buffer:[StereoSample::const_defualt(); AUDIO_BUFFER_SIZE], current_t_cycle:0, device:device, right_terminal: SoundTerminal::default(), @@ -75,7 +75,7 @@ impl GbApu{ } else{ for _ in 0..t_cycles{ - self.audio_buffer[self.current_t_cycle as usize] = StereoSample{right_sample:DEFAULT_SAPMPLE, left_sample:DEFAULT_SAPMPLE}; + self.audio_buffer[self.current_t_cycle as usize] = StereoSample::const_defualt(); self.current_t_cycle += 1; self.push_buffer_if_full(); From fcd1eef35aa5307fa6e486ce5a344035559e26f6 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 16 Oct 2021 12:16:17 +0300 Subject: [PATCH 102/136] Normal the i16 samples too This is important since it can cause flickering in the sound --- lib_gb/src/apu/sound_terminal.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib_gb/src/apu/sound_terminal.rs b/lib_gb/src/apu/sound_terminal.rs index a009cc1f..326adebc 100644 --- a/lib_gb/src/apu/sound_terminal.rs +++ b/lib_gb/src/apu/sound_terminal.rs @@ -26,6 +26,8 @@ impl SoundTerminal{ mixed_sample += samples[i] * self.channels[i] as u8 as Sample; } + mixed_sample >>= 2; // Divide by 4 in order to normal the sample + return mixed_sample * ((self.volume + 1) as Sample); } } \ No newline at end of file From a143d41c91b6da17cfaa8f13dc0b5adadece50e9 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 16 Oct 2021 12:19:52 +0300 Subject: [PATCH 103/136] Lock the fps to the original with the audio device --- gb/src/main.rs | 12 ------------ gb/src/sdl_audio_device.rs | 4 +++- 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/gb/src/main.rs b/gb/src/main.rs index 39c2e1ce..c11893c4 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -12,8 +12,6 @@ use std::{fs, env, result::Result, vec::Vec}; use log::info; use sdl2::sys::*; -const FPS:f64 = GB_FREQUENCY as f64 / 70224.0; -const FRAME_TIME_MS:f64 = (1.0 / FPS) * 1000.0; const SCREEN_SCALE:u8 = 4; fn init_logger(debug:bool)->Result<(), fern::InitError>{ @@ -95,7 +93,6 @@ fn main() { unsafe{ let mut event: std::mem::MaybeUninit = std::mem::MaybeUninit::uninit(); - let mut start:u64 = SDL_GetPerformanceCounter(); loop{ if SDL_PollEvent(event.as_mut_ptr()) != 0{ @@ -107,15 +104,6 @@ fn main() { let buffer = r.recv().unwrap(); sdl_gfx_device.swap_buffer(&buffer); - - - let end = SDL_GetPerformanceCounter(); - let elapsed_ms:f64 = (end - start) as f64 / SDL_GetPerformanceFrequency() as f64 * 1000.0; - if elapsed_ms < FRAME_TIME_MS{ - SDL_Delay((FRAME_TIME_MS - elapsed_ms).floor() as u32); - } - - start = SDL_GetPerformanceCounter(); } std::ptr::write_volatile(&mut running as *mut bool, false); diff --git a/gb/src/sdl_audio_device.rs b/gb/src/sdl_audio_device.rs index 4238232f..dac40061 100644 --- a/gb/src/sdl_audio_device.rs +++ b/gb/src/sdl_audio_device.rs @@ -74,7 +74,9 @@ impl SdlAudioDevie{ let data_byte_len = (audio.len() * std::mem::size_of::()) as u32; unsafe{ - while SDL_GetQueuedAudioSize(self.device_id) > BYTES_TO_WAIT{} + while SDL_GetQueuedAudioSize(self.device_id) > BYTES_TO_WAIT{ + SDL_Delay(1); + } SDL_ClearError(); if SDL_QueueAudio(self.device_id, audio_ptr, data_byte_len) != 0{ From 74d3caa5db14d3d65b62e82ef1de169f020233fe Mon Sep 17 00:00:00 2001 From: alloncm Date: Sun, 17 Oct 2021 00:52:39 +0300 Subject: [PATCH 104/136] Went for i16 as it grants a greater optimizations --- gb/src/wav_file_audio_device.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gb/src/wav_file_audio_device.rs b/gb/src/wav_file_audio_device.rs index 230c2ae1..25f2b00d 100644 --- a/gb/src/wav_file_audio_device.rs +++ b/gb/src/wav_file_audio_device.rs @@ -29,13 +29,13 @@ impl AudioDevice for WavfileAudioDevice{ impl Drop for WavfileAudioDevice{ fn drop(&mut self) { let header = wav::header::Header::new(wav::WAV_FORMAT_PCM, 2, self.target_frequency, 16); - let mut floats = Vec::with_capacity(self.samples_buffer.len() * 2); + let mut samples = Vec::with_capacity(self.samples_buffer.len() * 2); for sample in self.samples_buffer.iter(){ - floats.push(sample.left_sample); - floats.push(sample.right_sample); + samples.push(sample.left_sample); + samples.push(sample.right_sample); } - let data = wav::BitDepth::Sixteen(floats); + let data = wav::BitDepth::Sixteen(samples); let mut otuput_file = std::fs::File::create(self.filename).unwrap(); wav::write(header, &data, &mut otuput_file).unwrap(); } From e1f9daefde203b53500ade51c8499e2c7d0a658f Mon Sep 17 00:00:00 2001 From: alloncm Date: Sun, 17 Oct 2021 00:53:58 +0300 Subject: [PATCH 105/136] Optimize the sound terminal * Inline it, for some reason it wont be inlined automaticaly * Instead of multiplying with 1 or 0 Im anding with 0xFFFF or 0 --- lib_gb/benches/my_bench.rs | 7 ++++--- lib_gb/src/apu/apu_registers_updater.rs | 4 ++-- lib_gb/src/apu/sound_terminal.rs | 17 ++++++++++++++--- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/lib_gb/benches/my_bench.rs b/lib_gb/benches/my_bench.rs index 555d8c28..09deb6b1 100644 --- a/lib_gb/benches/my_bench.rs +++ b/lib_gb/benches/my_bench.rs @@ -37,12 +37,13 @@ pub fn apu_sweep_tone_channel(c: &mut Criterion){ pub fn apu_sound_terminal(c:&mut Criterion){ let mut sound_terminal = SoundTerminal::default(); - sound_terminal.channels = [true;4]; + for i in 0..4{ + sound_terminal.set_channel_state(i, true); + } sound_terminal.volume = 8; c.bench_function("Sound terminal", |b| b.iter(||{ - let samples:[Sample;4] = [100 as Sample,200 as Sample,5 as Sample,7 as Sample]; - let _ = sound_terminal.mix_terminal_samples(&samples); + let _ = sound_terminal.mix_terminal_samples(black_box(&samples)); })); } diff --git a/lib_gb/src/apu/apu_registers_updater.rs b/lib_gb/src/apu/apu_registers_updater.rs index bd4eb793..ac8eebfe 100644 --- a/lib_gb/src/apu/apu_registers_updater.rs +++ b/lib_gb/src/apu/apu_registers_updater.rs @@ -54,10 +54,10 @@ pub fn set_nr50(apu:&mut GbApu, nr50:u8){ pub fn set_nr51(apu:&mut GbApu, nr51:u8){ for i in 0..NUMBER_OF_CHANNELS{ - apu.right_terminal.channels[i as usize] = nr51 & (1 << i) != 0; + apu.right_terminal.set_channel_state(i, nr51 & (1 << i) != 0); } for i in 0..NUMBER_OF_CHANNELS{ - apu.left_terminal.channels[i as usize] = nr51 & (0b1_0000 << i) != 0; + apu.left_terminal.set_channel_state(i, nr51 & (0b1_0000 << i) != 0); } } diff --git a/lib_gb/src/apu/sound_terminal.rs b/lib_gb/src/apu/sound_terminal.rs index 326adebc..d0f09f3c 100644 --- a/lib_gb/src/apu/sound_terminal.rs +++ b/lib_gb/src/apu/sound_terminal.rs @@ -1,29 +1,40 @@ use super::{audio_device::{DEFAULT_SAPMPLE, Sample}, sound_utils::NUMBER_OF_CHANNELS}; +type ChannelMask = u16; + +const ENABLE_MASK:ChannelMask = 0xFFFF; +const DISABLE_MASK:ChannelMask = 0x0; + pub struct SoundTerminal{ pub enabled:bool, pub volume:u8, - pub channels:[bool;NUMBER_OF_CHANNELS] + channel_masks:[ChannelMask;NUMBER_OF_CHANNELS] } impl Default for SoundTerminal{ fn default() -> Self { SoundTerminal{ enabled:false, - channels:[false;NUMBER_OF_CHANNELS], + channel_masks:[DISABLE_MASK;NUMBER_OF_CHANNELS], volume:0 } } } impl SoundTerminal{ + pub fn set_channel_state(&mut self, channel:usize, state:bool){ + self.channel_masks[channel] = state as u16 * ENABLE_MASK; + } + + // For some reason this function is not inlined on release mode + #[inline] pub fn mix_terminal_samples(&self, samples:&[Sample;NUMBER_OF_CHANNELS])->Sample{ let mut mixed_sample:Sample = DEFAULT_SAPMPLE; for i in 0..NUMBER_OF_CHANNELS{ // This code should add the samples[i] only if channels[i] it true. // After profiling this code is faster than if and since this is a hot spot in the code // Im writing it like this. - mixed_sample += samples[i] * self.channels[i] as u8 as Sample; + mixed_sample += samples[i] & self.channel_masks[i] as Sample; } mixed_sample >>= 2; // Divide by 4 in order to normal the sample From 0ad43373dab1a3a0be7ca116232af6ea1878b75a Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 22 Oct 2021 13:59:07 +0300 Subject: [PATCH 106/136] Update the readme Update thhe ppu and the apu dev status --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7dd2c8dd..f59b9484 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,9 @@ The main goal of this project is to be able to play Pokemon on my own emulator. ### Development Status - CPU - Cycle accurate CPU -- PPU - Scan line accurate PPU +- PPU - Cycle accurate fifo PPU - Timer - Mostly accurate timer -- APU - Mostly accurate APU +- APU - Cycle mostly accurate APU - Tests - [Blargg's cpu_instrs](https://github.com/retrio/gb-test-roms/tree/master/cpu_instrs) - :thumbsup: - [dmg-acid2](https://github.com/mattcurrie/dmg-acid2) - :thumbsup: From e1d134a626c2965d0972b7f899639459523908ee Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 22 Oct 2021 14:04:43 +0300 Subject: [PATCH 107/136] Optimize the mpmc gfx device There ware too much cloning whcih caused a BO in debug mode. Using pointer casted to usize to communicate between threads and a multi buffering in the ppu (to avoid race conditions, at least I hope) I managed to solve this issue in debug mode too! --- Cargo.lock | 4 ++-- gb/src/main.rs | 15 +++++++++------ lib_gb/src/ppu/gb_ppu.rs | 28 ++++++++++++++++++---------- 3 files changed, 29 insertions(+), 18 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0c74f177..4f75498b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -444,9 +444,9 @@ dependencies = [ [[package]] name = "half" -version = "1.7.1" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62aca2aba2d62b4a7f5b33f3712cb1b0692779a56fb510499d5c0aa594daeaf3" +checksum = "ac5956d4e63858efaec57e0d6c1c2f6a41e1487f830314a324ccd7e2223a7ca0" [[package]] name = "hashbrown" diff --git a/gb/src/main.rs b/gb/src/main.rs index c11893c4..0ce0daba 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -7,7 +7,7 @@ mod multi_device_audio; mod sdl_gfx_device; use crate::{mbc_handler::*, sdl_joypad_provider::*, multi_device_audio::*}; -use lib_gb::{GB_FREQUENCY, apu::audio_device::*, keypad::button::Button, machine::gameboy::GameBoy, mmu::gb_mmu::BOOT_ROM_SIZE, ppu::{gb_ppu::{SCREEN_HEIGHT, SCREEN_WIDTH}, gfx_device::GfxDevice}}; +use lib_gb::{GB_FREQUENCY, apu::audio_device::*, keypad::button::Button, machine::gameboy::GameBoy, mmu::gb_mmu::BOOT_ROM_SIZE, ppu::{gb_ppu::{BUFFERS_NUMBER, SCREEN_HEIGHT, SCREEN_WIDTH}, gfx_device::GfxDevice}}; use std::{fs, env, result::Result, vec::Vec}; use log::info; use sdl2::sys::*; @@ -57,12 +57,14 @@ fn check_for_terminal_feature_flag(args:&Vec::, flag:&str)->bool{ } struct MpmcGfxDevice{ - sender: crossbeam_channel::Sender<[u32;SCREEN_HEIGHT * SCREEN_WIDTH]> + sender: crossbeam_channel::Sender } impl GfxDevice for MpmcGfxDevice{ fn swap_buffer(&mut self, buffer:&[u32; SCREEN_HEIGHT * SCREEN_WIDTH]) { - self.sender.send(buffer.clone()).unwrap(); + if self.sender.send(buffer.as_ptr() as usize).is_err(){ + log::debug!("The receiver endpoint has been closed"); + } } } @@ -78,7 +80,7 @@ fn main() { let mut sdl_gfx_device = sdl_gfx_device::SdlGfxDevice::new(SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32, "MagenBoy", SCREEN_SCALE); - let (s,r) = crossbeam_channel::bounded(1); + let (s,r) = crossbeam_channel::bounded(BUFFERS_NUMBER - 1); let mpmc_device = MpmcGfxDevice{sender:s}; let program_name = args[1].clone(); @@ -103,9 +105,10 @@ fn main() { } let buffer = r.recv().unwrap(); - sdl_gfx_device.swap_buffer(&buffer); + sdl_gfx_device.swap_buffer(&*(buffer as *const [u32; SCREEN_WIDTH * SCREEN_HEIGHT])); } + drop(r); std::ptr::write_volatile(&mut running as *mut bool, false); emualation_thread.join().unwrap(); @@ -152,5 +155,5 @@ fn emulation_thread_main(args: Vec, program_name: String, spsc_gfx_devic } drop(gameboy); release_mbc(&program_name, mbc); - log::info!("Release the gameboy succefully"); + log::info!("released the gameboy succefully"); } \ No newline at end of file diff --git a/lib_gb/src/ppu/gb_ppu.rs b/lib_gb/src/ppu/gb_ppu.rs index 06758648..a26679ac 100644 --- a/lib_gb/src/ppu/gb_ppu.rs +++ b/lib_gb/src/ppu/gb_ppu.rs @@ -10,6 +10,8 @@ use super::fifo::{FIFO_SIZE, sprite_fetcher::*}; pub const SCREEN_HEIGHT: usize = 144; pub const SCREEN_WIDTH: usize = 160; +pub const BUFFERS_NUMBER:usize = 2; + const OAM_ENTRY_SIZE:u16 = 4; const OAM_MEMORY_SIZE:usize = 0xA0; @@ -39,7 +41,8 @@ pub struct GbPpu{ gfx_device: GFX, t_cycles_passed:u16, - screen_buffer: [u32; SCREEN_HEIGHT * SCREEN_WIDTH], + screen_buffers: [[u32; SCREEN_HEIGHT * SCREEN_WIDTH];BUFFERS_NUMBER], + current_screen_buffer_index:usize, push_lcd_buffer:Vec, screen_buffer_index:usize, pixel_x_pos:u8, @@ -61,7 +64,8 @@ impl GbPpu{ lcd_control: 0, bg_pos: Vec2::{x:0, y:0}, window_pos: Vec2::{x:0,y:0}, - screen_buffer:[0;SCREEN_HEIGHT * SCREEN_WIDTH], + screen_buffers:[[0;SCREEN_HEIGHT * SCREEN_WIDTH];BUFFERS_NUMBER], + current_screen_buffer_index:0, bg_color_mapping:[WHITE, LIGHT_GRAY, DARK_GRAY, BLACK], obj_color_mapping0: [None, Some(LIGHT_GRAY), Some(DARK_GRAY), Some(BLACK)], obj_color_mapping1: [None, Some(LIGHT_GRAY), Some(DARK_GRAY), Some(BLACK)], @@ -85,11 +89,10 @@ impl GbPpu{ } pub fn turn_off(&mut self){ - self.screen_buffer_index = 0; self.t_cycles_passed = 0; //This is an expensive operation! - unsafe{std::ptr::write_bytes(self.screen_buffer.as_mut_ptr(), 0xFF, self.screen_buffer.len())}; - self.gfx_device.swap_buffer(&self.screen_buffer); + unsafe{std::ptr::write_bytes(self.screen_buffers[self.current_screen_buffer_index].as_mut_ptr(), 0xFF, SCREEN_HEIGHT * SCREEN_WIDTH)}; + self.swap_buffer(); self.state = PpuState::Hblank; self.ly_register = 0; self.stat_triggered = false; @@ -114,18 +117,23 @@ impl GbPpu{ self.update_stat_register(if_register); - for pixel in self.push_lcd_buffer.iter(){ - self.screen_buffer[self.screen_buffer_index] = u32::from(*pixel); + for i in 0..self.push_lcd_buffer.len(){ + self.screen_buffers[self.current_screen_buffer_index][self.screen_buffer_index] = u32::from(self.push_lcd_buffer[i]); self.screen_buffer_index += 1; - if self.screen_buffer_index == self.screen_buffer.len(){ - self.gfx_device.swap_buffer(&self.screen_buffer); - self.screen_buffer_index = 0; + if self.screen_buffer_index == SCREEN_WIDTH * SCREEN_HEIGHT{ + self.swap_buffer(); } } self.push_lcd_buffer.clear(); } + fn swap_buffer(&mut self){ + self.gfx_device.swap_buffer(&self.screen_buffers[self.current_screen_buffer_index]); + self.screen_buffer_index = 0; + self.current_screen_buffer_index = (self.current_screen_buffer_index + 1) % BUFFERS_NUMBER; + } + fn update_stat_register(&mut self, if_register: &mut u8) { self.stat_register &= 0b1111_1100; self.stat_register |= self.state as u8; From 33b13a385b87c768a278b0dd493cfa908a109c65 Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 22 Oct 2021 18:16:49 +0300 Subject: [PATCH 108/136] Optimize the APU Instead of cycling every t_cycle the apu now cycle in m_cycle increasing its performance by almost 4! This will hust emulation accuracy but I think I can live with this minor hit :D --- gb/src/audio_resampler.rs | 5 ++-- gb/src/sdl_audio_device.rs | 5 ++-- lib_gb/benches/my_bench.rs | 2 +- lib_gb/src/apu/apu_registers_updater.rs | 6 ++++ lib_gb/src/apu/gb_apu.rs | 39 ++++++++++++------------- lib_gb/src/apu/timer.rs | 5 ++-- lib_gb/src/utils/mod.rs | 2 +- 7 files changed, 36 insertions(+), 28 deletions(-) diff --git a/gb/src/audio_resampler.rs b/gb/src/audio_resampler.rs index 182d466a..257dc6b4 100644 --- a/gb/src/audio_resampler.rs +++ b/gb/src/audio_resampler.rs @@ -8,7 +8,8 @@ pub struct AudioResampler{ impl AudioResampler{ pub fn new(original_frequency:u32, target_frequency:u32)->Self{ - let to_skip = original_frequency / target_frequency as u32; + // Calling round in order to get the nearest integer and resample as precise as possible + let to_skip = (original_frequency as f32 / target_frequency as f32).round() as u32; if to_skip == 0{ std::panic!("target freqency is too high: {}", target_frequency); } @@ -43,6 +44,6 @@ impl AudioResampler{ let interpulated_left_sample = samples.iter().fold(DEFAULT_SAPMPLE, |acc, x| acc + x.left_sample) / samples.len() as Sample; let interpulated_right_sample = samples.iter().fold(DEFAULT_SAPMPLE, |acc, x| acc + x.right_sample) / samples.len() as Sample; - return StereoSample{left_sample: interpulated_left_sample,right_sample: interpulated_right_sample}; + return StereoSample{left_sample: interpulated_left_sample, right_sample: interpulated_right_sample}; } } \ No newline at end of file diff --git a/gb/src/sdl_audio_device.rs b/gb/src/sdl_audio_device.rs index dac40061..4bd02189 100644 --- a/gb/src/sdl_audio_device.rs +++ b/gb/src/sdl_audio_device.rs @@ -6,6 +6,7 @@ use crate::audio_resampler::AudioResampler; //After twicking those numbers Iv reached this, this will affect fps which will affect sound tearing const BUFFER_SIZE:usize = 1024 * 2; const BYTES_TO_WAIT:u32 = BUFFER_SIZE as u32 * 8; +const VOLUME:Sample = 10 as Sample; pub struct SdlAudioDevie{ device_id: SDL_AudioDeviceID, @@ -92,8 +93,8 @@ impl AudioDevice for SdlAudioDevie{ fn push_buffer(&mut self, buffer:&[StereoSample]){ for sample in self.resampler.resample(buffer){ - self.buffer.push(sample.left_sample); - self.buffer.push(sample.right_sample); + self.buffer.push(sample.left_sample * VOLUME); + self.buffer.push(sample.right_sample * VOLUME); if self.buffer.len() == BUFFER_SIZE{ self.push_audio_to_device(&self.buffer).unwrap(); diff --git a/lib_gb/benches/my_bench.rs b/lib_gb/benches/my_bench.rs index 09deb6b1..5937c401 100644 --- a/lib_gb/benches/my_bench.rs +++ b/lib_gb/benches/my_bench.rs @@ -16,7 +16,7 @@ pub fn criterion_bench(c: &mut Criterion){ apu.enabled = true; apu.sweep_tone_channel.enabled = true; for _ in 0..100{ - apu.cycle(255); + apu.cycle(10); } })); } diff --git a/lib_gb/src/apu/apu_registers_updater.rs b/lib_gb/src/apu/apu_registers_updater.rs index ac8eebfe..1112863e 100644 --- a/lib_gb/src/apu/apu_registers_updater.rs +++ b/lib_gb/src/apu/apu_registers_updater.rs @@ -62,8 +62,14 @@ pub fn set_nr51(apu:&mut GbApu, nr51:u8){ } pub fn set_nr52(apu:&mut GbApu, ports:&mut [u8;IO_PORTS_SIZE], nr52:u8){ + let prev_apu_state = apu.enabled; apu.enabled = nr52 & BIT_7_MASK != 0; + // Apu turned off + if !apu.enabled && prev_apu_state{ + apu.reset(); + } + for i in NR10_REGISTER_INDEX..NR52_REGISTER_INDEX{ ports[i as usize] = 0; } diff --git a/lib_gb/src/apu/gb_apu.rs b/lib_gb/src/apu/gb_apu.rs index e0b72c24..1456e76b 100644 --- a/lib_gb/src/apu/gb_apu.rs +++ b/lib_gb/src/apu/gb_apu.rs @@ -23,7 +23,7 @@ pub struct GbApu{ pub enabled:bool, audio_buffer:[StereoSample;AUDIO_BUFFER_SIZE], - current_t_cycle:u32, + current_m_cycle:u32, device:Device, last_enabled_state:bool } @@ -37,7 +37,7 @@ impl GbApu{ tone_channel: Channel::::new(SquareSampleProducer::new()), noise_channel: Channel::::new(NoiseSampleProducer::default()), audio_buffer:[StereoSample::const_defualt(); AUDIO_BUFFER_SIZE], - current_t_cycle:0, + current_m_cycle:0, device:device, right_terminal: SoundTerminal::default(), left_terminal: SoundTerminal::default(), @@ -47,11 +47,8 @@ impl GbApu{ } pub fn cycle(&mut self, m_cycles_passed:u8){ - //converting m_cycles to t_cycles - let t_cycles = m_cycles_passed * 4; - if self.enabled{ - for _ in 0..t_cycles{ + for _ in 0..m_cycles_passed{ let tick = self.frame_sequencer.cycle(); self.update_channels_for_frame_squencer(tick); @@ -65,35 +62,37 @@ impl GbApu{ let left_sample = self.left_terminal.mix_terminal_samples(&samples); let right_sample = self.right_terminal.mix_terminal_samples(&samples); - self.audio_buffer[self.current_t_cycle as usize].left_sample = left_sample; - self.audio_buffer[self.current_t_cycle as usize].right_sample = right_sample; + self.audio_buffer[self.current_m_cycle as usize].left_sample = left_sample; + self.audio_buffer[self.current_m_cycle as usize].right_sample = right_sample; - self.current_t_cycle += 1; + self.current_m_cycle += 1; self.push_buffer_if_full(); } } else{ - for _ in 0..t_cycles{ - self.audio_buffer[self.current_t_cycle as usize] = StereoSample::const_defualt(); - self.current_t_cycle += 1; + for _ in 0..m_cycles_passed{ + self.audio_buffer[self.current_m_cycle as usize] = StereoSample::const_defualt(); + self.current_m_cycle += 1; self.push_buffer_if_full(); } - - self.tone_channel.reset(); - self.sweep_tone_channel.reset(); - self.wave_channel.reset(); - self.noise_channel.reset(); - self.frame_sequencer.reset(); } self.last_enabled_state = self.enabled; } + pub fn reset(&mut self){ + self.tone_channel.reset(); + self.sweep_tone_channel.reset(); + self.wave_channel.reset(); + self.noise_channel.reset(); + self.frame_sequencer.reset(); + } + fn push_buffer_if_full(&mut self){ - if self.current_t_cycle as usize >= AUDIO_BUFFER_SIZE{ - self.current_t_cycle = 0; + if self.current_m_cycle as usize >= AUDIO_BUFFER_SIZE{ + self.current_m_cycle = 0; self.device.push_buffer(&self.audio_buffer); } } diff --git a/lib_gb/src/apu/timer.rs b/lib_gb/src/apu/timer.rs index a0c0738b..ea7d3b67 100644 --- a/lib_gb/src/apu/timer.rs +++ b/lib_gb/src/apu/timer.rs @@ -3,11 +3,12 @@ pub struct Timer{ cycle_counter:u16 } +// By deviding by 4 (shifting right 2) Im losing precison in favor of performance impl Timer{ pub fn new(cycles_to_tick:u16)->Self{ Timer{ cycle_counter:0, - cycles_to_tick:cycles_to_tick + cycles_to_tick:cycles_to_tick >> 2 } } @@ -26,7 +27,7 @@ impl Timer{ } pub fn update_cycles_to_tick(&mut self, cycles_to_tick:u16){ - self.cycles_to_tick = cycles_to_tick; + self.cycles_to_tick = cycles_to_tick >> 2; self.cycle_counter = 0; } } \ No newline at end of file diff --git a/lib_gb/src/utils/mod.rs b/lib_gb/src/utils/mod.rs index 0dc5325e..7c0f5cd0 100644 --- a/lib_gb/src/utils/mod.rs +++ b/lib_gb/src/utils/mod.rs @@ -5,7 +5,7 @@ pub mod memory_registers; pub mod bit_masks; pub mod fixed_size_queue; -pub const GB_FREQUENCY:u32 = 4_194_304; +pub const GB_FREQUENCY:u32 = 4_194_304 / 4; pub fn create_default_array()->[T;SIZE]{ create_array(||T::default()) From d6e735016577f5e19c7cffe2f6044ed3132c69b8 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 23 Oct 2021 19:19:31 +0300 Subject: [PATCH 109/136] Add frame cap + turbo mode Add frame cap using SDL_Delay. Also add the option to configure the emulation speed (both ppu and apu) --- Cargo.lock | 26 +++++++++++++------------- gb/Cargo.toml | 6 +++++- gb/src/main.rs | 5 +++-- gb/src/sdl_audio_device.rs | 6 +++--- gb/src/sdl_gfx_device.rs | 27 +++++++++++++++++++++++++-- lib_gb/Cargo.toml | 1 + 6 files changed, 50 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4f75498b..ee7c6a38 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -412,6 +412,19 @@ dependencies = [ "slab", ] +[[package]] +name = "gb" +version = "1.0.0" +dependencies = [ + "chrono", + "crossbeam-channel", + "fern", + "lib_gb", + "log", + "sdl2", + "wav", +] + [[package]] name = "getrandom" version = "0.2.3" @@ -616,19 +629,6 @@ dependencies = [ "cfg-if 1.0.0", ] -[[package]] -name = "magenboy" -version = "1.0.0" -dependencies = [ - "chrono", - "crossbeam-channel", - "fern", - "lib_gb", - "log", - "sdl2", - "wav", -] - [[package]] name = "matches" version = "0.1.9" diff --git a/gb/Cargo.toml b/gb/Cargo.toml index 595ceb6b..e6ebe490 100644 --- a/gb/Cargo.toml +++ b/gb/Cargo.toml @@ -1,10 +1,14 @@ [package] -name = "magenboy" +name = "gb" version = "1.0.0" authors = ["alloncm "] edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[[bin]] +name = "magen_boy" +path = "src/main.rs" + [dependencies] lib_gb = {path = "../lib_gb/"} log = "0.4" diff --git a/gb/src/main.rs b/gb/src/main.rs index 0ce0daba..0ffd3b31 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -13,6 +13,7 @@ use log::info; use sdl2::sys::*; const SCREEN_SCALE:u8 = 4; +const TURBO_MUL:u8 = 2; fn init_logger(debug:bool)->Result<(), fern::InitError>{ let level = if debug {log::LevelFilter::Debug} else {log::LevelFilter::Info}; @@ -78,7 +79,7 @@ fn main() { Result::Err(error)=>std::panic!("error initing logger: {}", error) } - let mut sdl_gfx_device = sdl_gfx_device::SdlGfxDevice::new(SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32, "MagenBoy", SCREEN_SCALE); + let mut sdl_gfx_device = sdl_gfx_device::SdlGfxDevice::new(SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32, "MagenBoy", SCREEN_SCALE, TURBO_MUL); let (s,r) = crossbeam_channel::bounded(BUFFERS_NUMBER - 1); let mpmc_device = MpmcGfxDevice{sender:s}; @@ -118,7 +119,7 @@ fn main() { // Receiving usize and not raw ptr cause in rust you cant pass a raw ptr to another thread fn emulation_thread_main(args: Vec, program_name: String, spsc_gfx_device: MpmcGfxDevice, running_ptr: usize) { - let audio_device = sdl_audio_device::SdlAudioDevie::new(44100); + let audio_device = sdl_audio_device::SdlAudioDevie::new(44100, TURBO_MUL); let mut devices: Vec::> = Vec::new(); devices.push(Box::new(audio_device)); if check_for_terminal_feature_flag(&args, "--file-audio"){ diff --git a/gb/src/sdl_audio_device.rs b/gb/src/sdl_audio_device.rs index 4bd02189..4fe0607f 100644 --- a/gb/src/sdl_audio_device.rs +++ b/gb/src/sdl_audio_device.rs @@ -5,7 +5,7 @@ use crate::audio_resampler::AudioResampler; //After twicking those numbers Iv reached this, this will affect fps which will affect sound tearing const BUFFER_SIZE:usize = 1024 * 2; -const BYTES_TO_WAIT:u32 = BUFFER_SIZE as u32 * 8; +const BYTES_TO_WAIT:u32 = BUFFER_SIZE as u32 * 16; const VOLUME:Sample = 10 as Sample; pub struct SdlAudioDevie{ @@ -16,7 +16,7 @@ pub struct SdlAudioDevie{ } impl SdlAudioDevie{ - pub fn new(frequency:i32)->Self{ + pub fn new(frequency:i32, turbo_mul:u8)->Self{ let desired_audio_spec = SDL_AudioSpec{ freq: frequency, @@ -57,7 +57,7 @@ impl SdlAudioDevie{ return SdlAudioDevie{ device_id: device_id, buffer:Vec::with_capacity(BUFFER_SIZE), - resampler: AudioResampler::new(GB_FREQUENCY, frequency as u32) + resampler: AudioResampler::new(GB_FREQUENCY * turbo_mul as u32, frequency as u32) }; } diff --git a/gb/src/sdl_gfx_device.rs b/gb/src/sdl_gfx_device.rs index a689b182..dee2a51a 100644 --- a/gb/src/sdl_gfx_device.rs +++ b/gb/src/sdl_gfx_device.rs @@ -10,10 +10,14 @@ pub struct SdlGfxDevice{ width:u32, height:u32, sacle:u8, + frame_start_time:u64, + frame_time_ms:f64, + discard:u8, + turbo_mul:u8, } impl SdlGfxDevice{ - pub fn new(buffer_width:u32, buffer_height:u32, window_name:&str, screen_scale: u8)->Self{ + pub fn new(buffer_width:u32, buffer_height:u32, window_name:&str, screen_scale: u8, turbo_mul:u8)->Self{ let cs_wnd_name = CString::new(window_name).unwrap(); let (_window, renderer, texture): (*mut SDL_Window, *mut SDL_Renderer, *mut SDL_Texture) = unsafe{ @@ -38,7 +42,11 @@ impl SdlGfxDevice{ texture, height:buffer_height, width:buffer_width, - sacle:screen_scale + sacle:screen_scale, + frame_start_time: unsafe{SDL_GetPerformanceCounter()}, + frame_time_ms: (1.0/(60.0 as f64)) * 1_000.0, + discard:0, + turbo_mul } } @@ -61,6 +69,13 @@ impl SdlGfxDevice{ impl GfxDevice for SdlGfxDevice{ fn swap_buffer(&mut self, buffer:&[u32; SCREEN_HEIGHT * SCREEN_WIDTH]) { + if self.turbo_mul > 1{ + self.discard = (self.discard + 1) % self.turbo_mul; + if self.discard == 0{ + return; + } + } + unsafe{ let extended_buffer = Self::extend_vec(buffer, self.sacle as usize, self.width as usize, self.height as usize); @@ -73,6 +88,14 @@ impl GfxDevice for SdlGfxDevice{ //There is no need to call SDL_RenderClear since im replacing the whole buffer SDL_RenderCopy(self.renderer, self.texture, std::ptr::null(), std::ptr::null()); SDL_RenderPresent(self.renderer); + + let frame_end_time = SDL_GetPerformanceCounter(); + let elapsed = ((frame_end_time - self.frame_start_time) as f64 / (SDL_GetPerformanceFrequency() as f64)) * 1_000.0; + if elapsed < self.frame_time_ms{ + SDL_Delay(((self.frame_time_ms - elapsed).floor()) as Uint32); + } + + self.frame_start_time = SDL_GetPerformanceCounter(); } } } \ No newline at end of file diff --git a/lib_gb/Cargo.toml b/lib_gb/Cargo.toml index 44d6fcac..9ba19354 100644 --- a/lib_gb/Cargo.toml +++ b/lib_gb/Cargo.toml @@ -3,6 +3,7 @@ name = "lib_gb" version = "1.0.0" authors = ["alloncm "] edition = "2018" + [dependencies] log = "0.4" From a235e9604039beec99ec1ebc89a3d16cb95b14a8 Mon Sep 17 00:00:00 2001 From: alloncm Date: Wed, 27 Oct 2021 22:54:50 +0300 Subject: [PATCH 110/136] Fix some gfx device typos --- gb/src/main.rs | 2 +- gb/src/sdl_gfx_device.rs | 12 +++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/gb/src/main.rs b/gb/src/main.rs index 0ffd3b31..66a48d55 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -13,7 +13,7 @@ use log::info; use sdl2::sys::*; const SCREEN_SCALE:u8 = 4; -const TURBO_MUL:u8 = 2; +const TURBO_MUL:u8 = 1; fn init_logger(debug:bool)->Result<(), fern::InitError>{ let level = if debug {log::LevelFilter::Debug} else {log::LevelFilter::Info}; diff --git a/gb/src/sdl_gfx_device.rs b/gb/src/sdl_gfx_device.rs index dee2a51a..2990f58e 100644 --- a/gb/src/sdl_gfx_device.rs +++ b/gb/src/sdl_gfx_device.rs @@ -25,13 +25,13 @@ impl SdlGfxDevice{ let wind:*mut SDL_Window = SDL_CreateWindow( cs_wnd_name.as_ptr(), SDL_WINDOWPOS_UNDEFINED_MASK as i32, SDL_WINDOWPOS_UNDEFINED_MASK as i32, - buffer_width as i32 * 4, buffer_height as i32 * 4, 0); + buffer_width as i32 * screen_scale as i32, buffer_height as i32 * screen_scale as i32, 0); let rend: *mut SDL_Renderer = SDL_CreateRenderer(wind, -1, 0); let tex: *mut SDL_Texture = SDL_CreateTexture(rend, SDL_PixelFormatEnum::SDL_PIXELFORMAT_ARGB8888 as u32, SDL_TextureAccess::SDL_TEXTUREACCESS_STREAMING as i32, - buffer_width as i32 * 4, buffer_height as i32 * 4); + buffer_width as i32 * screen_scale as i32, buffer_height as i32 * screen_scale as i32); (wind, rend, tex) }; @@ -69,11 +69,9 @@ impl SdlGfxDevice{ impl GfxDevice for SdlGfxDevice{ fn swap_buffer(&mut self, buffer:&[u32; SCREEN_HEIGHT * SCREEN_WIDTH]) { - if self.turbo_mul > 1{ - self.discard = (self.discard + 1) % self.turbo_mul; - if self.discard == 0{ - return; - } + self.discard = (self.discard + 1) % self.turbo_mul; + if self.discard != 0{ + return; } unsafe{ From 19969eb1cf95bc8adcc579526803ed807bec2dc8 Mon Sep 17 00:00:00 2001 From: alloncm Date: Thu, 28 Oct 2021 01:36:35 +0300 Subject: [PATCH 111/136] In the middle of testing sdl2 pull api --- gb/src/audio_resampler.rs | 2 +- gb/src/main.rs | 2 +- gb/src/sdl_audio_device.rs | 102 +++++++++++++++++++++++++-------- lib_gb/src/apu/audio_device.rs | 7 ++- lib_gb/src/apu/gb_apu.rs | 2 +- 5 files changed, 86 insertions(+), 29 deletions(-) diff --git a/gb/src/audio_resampler.rs b/gb/src/audio_resampler.rs index 257dc6b4..f8582b7c 100644 --- a/gb/src/audio_resampler.rs +++ b/gb/src/audio_resampler.rs @@ -24,7 +24,7 @@ impl AudioResampler{ pub fn resample(&mut self, buffer:&[StereoSample])->Vec{ let mut output = Vec::new(); for sample in buffer.into_iter(){ - self.sampling_buffer.push(*sample); + self.sampling_buffer.push(sample.clone()); self.sampling_counter += 1; if self.sampling_counter == self.to_skip { diff --git a/gb/src/main.rs b/gb/src/main.rs index 66a48d55..a41038ce 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -12,7 +12,7 @@ use std::{fs, env, result::Result, vec::Vec}; use log::info; use sdl2::sys::*; -const SCREEN_SCALE:u8 = 4; +const SCREEN_SCALE:u8 = 1; const TURBO_MUL:u8 = 1; fn init_logger(debug:bool)->Result<(), fern::InitError>{ diff --git a/gb/src/sdl_audio_device.rs b/gb/src/sdl_audio_device.rs index 4fe0607f..028d2d05 100644 --- a/gb/src/sdl_audio_device.rs +++ b/gb/src/sdl_audio_device.rs @@ -1,23 +1,44 @@ -use std::{vec::Vec,mem::MaybeUninit,ffi::{CStr, c_void}}; +use std::{ffi::{CStr, c_void}, mem::MaybeUninit}; use lib_gb::{GB_FREQUENCY, apu::audio_device::*}; use sdl2::{sys::*,libc::c_char}; use crate::audio_resampler::AudioResampler; +use crossbeam_channel::{Receiver, Sender, bounded}; + //After twicking those numbers Iv reached this, this will affect fps which will affect sound tearing const BUFFER_SIZE:usize = 1024 * 2; const BYTES_TO_WAIT:u32 = BUFFER_SIZE as u32 * 16; const VOLUME:Sample = 10 as Sample; + +struct Data{ + pub rx: Receiver<[Sample;BUFFER_SIZE]>, + pub current_buf: Option<[Sample;BUFFER_SIZE]>, + pub current_buf_index:usize, +} pub struct SdlAudioDevie{ device_id: SDL_AudioDeviceID, resampler: AudioResampler, - buffer: Vec + buffer: [Sample;BUFFER_SIZE], + buffer_index:usize, + + tx: Sender<[Sample;BUFFER_SIZE]>, } impl SdlAudioDevie{ pub fn new(frequency:i32, turbo_mul:u8)->Self{ + let(s,r) = bounded(3); + let boxed_data = Box::new(Data{ + current_buf:Option::None, + current_buf_index:0, + rx:r + }); + + let leaked_data = Box::leak(boxed_data); + + let desired_audio_spec = SDL_AudioSpec{ freq: frequency, format: AUDIO_S16SYS as u16, @@ -26,8 +47,8 @@ impl SdlAudioDevie{ samples: BUFFER_SIZE as u16, padding: 0, size: 0, - callback: Option::None, - userdata: std::ptr::null_mut() + callback: Option::Some(audio_callback), + userdata: leaked_data as *mut Data as *mut c_void }; @@ -53,11 +74,12 @@ impl SdlAudioDevie{ id }; - return SdlAudioDevie{ device_id: device_id, - buffer:Vec::with_capacity(BUFFER_SIZE), - resampler: AudioResampler::new(GB_FREQUENCY * turbo_mul as u32, frequency as u32) + buffer:[DEFAULT_SAPMPLE;BUFFER_SIZE], + buffer_index:0, + resampler: AudioResampler::new(GB_FREQUENCY * turbo_mul as u32, frequency as u32), + tx:s, }; } @@ -70,22 +92,52 @@ impl SdlAudioDevie{ } - fn push_audio_to_device(&self, audio:&[Sample])->Result<(),&str>{ - let audio_ptr: *const c_void = audio.as_ptr() as *const c_void; - let data_byte_len = (audio.len() * std::mem::size_of::()) as u32; + fn push_audio_to_device(&self, audio:&[Sample; BUFFER_SIZE])->Result<(),&str>{ + // let audio_ptr: *const c_void = audio.as_ptr() as *const c_void; + // let data_byte_len = (audio.len() * std::mem::size_of::()) as u32; - unsafe{ - while SDL_GetQueuedAudioSize(self.device_id) > BYTES_TO_WAIT{ - SDL_Delay(1); - } + // unsafe{ + // while SDL_GetQueuedAudioSize(self.device_id) > BYTES_TO_WAIT{ + // SDL_Delay(1); + // } - SDL_ClearError(); - if SDL_QueueAudio(self.device_id, audio_ptr, data_byte_len) != 0{ - return Err(Self::get_sdl_error_message()); - } + // SDL_ClearError(); + // if SDL_QueueAudio(self.device_id, audio_ptr, data_byte_len) != 0{ + // return Err(Self::get_sdl_error_message()); + // } - Ok(()) - } + // Ok(()) + // } + self.tx.send(audio.clone()).unwrap(); + Ok(()) + } +} + +unsafe extern "C" fn audio_callback(userdata:*mut c_void, buffer:*mut u8, length:i32){ + let length = length as usize; + let s = &mut *(userdata as *mut Data); + + if s.current_buf.is_none(){ + s.current_buf = Some(s.rx.recv().unwrap()); + } + + let samples = s.current_buf.unwrap(); + let samples_size = (samples.len() * std::mem::size_of::()) - s.current_buf_index; + let samples_ptr = (samples.as_ptr() as *mut u8).add(s.current_buf_index); + std::ptr::copy_nonoverlapping(samples_ptr, buffer, std::cmp::min(length, samples_size)); + + if length > samples_size && s.rx.is_empty(){ + s.current_buf = Option::None; + s.current_buf_index = 0; + std::ptr::write_bytes(buffer.add(samples.len() as usize), 0, length - samples_size); + } + else if length > samples_size{ + s.current_buf = Option::None; + s.current_buf_index = 0; + audio_callback(userdata, buffer.add(samples_size), (length - samples_size) as i32); + } + else{ + s.current_buf_index = length; } } @@ -93,12 +145,12 @@ impl AudioDevice for SdlAudioDevie{ fn push_buffer(&mut self, buffer:&[StereoSample]){ for sample in self.resampler.resample(buffer){ - self.buffer.push(sample.left_sample * VOLUME); - self.buffer.push(sample.right_sample * VOLUME); - - if self.buffer.len() == BUFFER_SIZE{ + self.buffer[self.buffer_index] = sample.left_sample * VOLUME; + self.buffer[self.buffer_index + 1] = sample.right_sample * VOLUME; + self.buffer_index += 2; + if self.buffer_index == BUFFER_SIZE{ self.push_audio_to_device(&self.buffer).unwrap(); - self.buffer.clear(); + self.buffer_index = 0; } } } diff --git a/lib_gb/src/apu/audio_device.rs b/lib_gb/src/apu/audio_device.rs index 63b0080e..23474dcf 100644 --- a/lib_gb/src/apu/audio_device.rs +++ b/lib_gb/src/apu/audio_device.rs @@ -1,7 +1,6 @@ pub type Sample = i16; pub const DEFAULT_SAPMPLE:Sample = 0 as Sample; -#[derive(Copy, Clone)] pub struct StereoSample{ pub left_sample:Sample, pub right_sample:Sample @@ -13,6 +12,12 @@ impl StereoSample{ } } +impl Clone for StereoSample{ + fn clone(&self) -> Self { + Self{left_sample:self.left_sample,right_sample:self.right_sample} + } +} + pub trait AudioDevice{ fn push_buffer(&mut self, buffer:&[StereoSample]); } \ No newline at end of file diff --git a/lib_gb/src/apu/gb_apu.rs b/lib_gb/src/apu/gb_apu.rs index 1456e76b..a090f5b5 100644 --- a/lib_gb/src/apu/gb_apu.rs +++ b/lib_gb/src/apu/gb_apu.rs @@ -36,7 +36,7 @@ impl GbApu{ wave_channel:Channel::::new(WaveSampleProducer::default()), tone_channel: Channel::::new(SquareSampleProducer::new()), noise_channel: Channel::::new(NoiseSampleProducer::default()), - audio_buffer:[StereoSample::const_defualt(); AUDIO_BUFFER_SIZE], + audio_buffer:crate::utils::create_array(StereoSample::const_defualt), current_m_cycle:0, device:device, right_terminal: SoundTerminal::default(), From dd31bf6d3e30b03f384689981f7e3a56adc2c67a Mon Sep 17 00:00:00 2001 From: alloncm Date: Fri, 29 Oct 2021 22:29:51 +0300 Subject: [PATCH 112/136] Still in the middle --- gb/Cargo.toml | 7 ++++- gb/src/audio_resampler.rs | 45 ++++++++++++++++++++++----- gb/src/main.rs | 7 +++-- gb/src/multi_device_audio.rs | 2 +- gb/src/sdl_audio_device.rs | 11 +++++-- gb/src/sdl_audio_resampler.rs | 51 +++++++++++++++++++++++++++++++ gb/src/sdl_gfx_device.rs | 2 +- gb/src/wav_file_audio_device.rs | 5 ++- lib_gb/benches/my_bench.rs | 2 +- lib_gb/src/apu/audio_device.rs | 5 ++- lib_gb/src/apu/gb_apu.rs | 6 ++-- lib_gb/tests/integration_tests.rs | 3 +- 12 files changed, 122 insertions(+), 24 deletions(-) create mode 100644 gb/src/sdl_audio_resampler.rs diff --git a/gb/Cargo.toml b/gb/Cargo.toml index e6ebe490..22556d13 100644 --- a/gb/Cargo.toml +++ b/gb/Cargo.toml @@ -16,4 +16,9 @@ fern = "0.6.0" chrono = "0.4" sdl2 = {version = "0.34", features = ["bundled","static-link"]} wav = "0.6.0" -crossbeam-channel = "0.5" \ No newline at end of file +crossbeam-channel = "0.5" + +[features] +default = ["sdl-resample"] +sdl-resample = [] +push-audio = [] \ No newline at end of file diff --git a/gb/src/audio_resampler.rs b/gb/src/audio_resampler.rs index f8582b7c..2527dcf8 100644 --- a/gb/src/audio_resampler.rs +++ b/gb/src/audio_resampler.rs @@ -1,42 +1,71 @@ -use lib_gb::apu::audio_device::{DEFAULT_SAPMPLE, Sample, StereoSample}; +use lib_gb::apu::audio_device::{BUFFER_SIZE, DEFAULT_SAPMPLE, Sample, StereoSample}; pub struct AudioResampler{ to_skip:u32, sampling_buffer:Vec, - sampling_counter:u32 + sampling_counter:u32, + reminder_steps:f32, + reminder_counter:f32, + alternate_to_skip:u32, + skip_to_use:u32, } impl AudioResampler{ pub fn new(original_frequency:u32, target_frequency:u32)->Self{ // Calling round in order to get the nearest integer and resample as precise as possible - let to_skip = (original_frequency as f32 / target_frequency as f32).round() as u32; - if to_skip == 0{ + let div = original_frequency as f32 / target_frequency as f32; + + let lower_to_skip = div.floor() as u32; + let upper_to_skip = div.ceil() as u32; + let mut reminder = div.fract(); + let (to_skip, alt_to_skip) = if reminder < 0.5{ + (lower_to_skip, upper_to_skip) + } + else{ + reminder = 1.0 - reminder; + (upper_to_skip, lower_to_skip) + }; + + if lower_to_skip == 0{ std::panic!("target freqency is too high: {}", target_frequency); } AudioResampler{ to_skip:to_skip, - sampling_buffer:Vec::with_capacity(to_skip as usize), - sampling_counter: 0 + sampling_buffer:Vec::with_capacity(upper_to_skip as usize), + sampling_counter: 0, + reminder_steps:reminder, + reminder_counter:0.0, + alternate_to_skip: alt_to_skip, + skip_to_use:to_skip } } - pub fn resample(&mut self, buffer:&[StereoSample])->Vec{ + pub fn resample(&mut self, buffer:&[StereoSample; BUFFER_SIZE])->Vec{ let mut output = Vec::new(); for sample in buffer.into_iter(){ self.sampling_buffer.push(sample.clone()); self.sampling_counter += 1; - if self.sampling_counter == self.to_skip { + if self.sampling_counter == self.skip_to_use { let interpolated_sample = Self::interpolate_sample(&self.sampling_buffer); self.sampling_counter = 0; self.sampling_buffer.clear(); output.push(interpolated_sample); + if self.reminder_counter >= 1.0{ + self.skip_to_use = self.alternate_to_skip; + self.reminder_counter -= 1.0; + } + else{ + self.skip_to_use = self.to_skip; + self.reminder_counter += self.reminder_steps; + } } } return output; + } fn interpolate_sample(samples:&[StereoSample])->StereoSample{ diff --git a/gb/src/main.rs b/gb/src/main.rs index a41038ce..c02431e6 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -1,7 +1,10 @@ mod mbc_handler; mod sdl_joypad_provider; mod sdl_audio_device; +#[cfg(not(feature = "sdl-resample"))] mod audio_resampler; +#[cfg(feature = "sdl-resample")] +mod sdl_audio_resampler; mod wav_file_audio_device; mod multi_device_audio; mod sdl_gfx_device; @@ -12,8 +15,8 @@ use std::{fs, env, result::Result, vec::Vec}; use log::info; use sdl2::sys::*; -const SCREEN_SCALE:u8 = 1; -const TURBO_MUL:u8 = 1; +const SCREEN_SCALE:u8 = 4; +const TURBO_MUL:u8 = 2; fn init_logger(debug:bool)->Result<(), fern::InitError>{ let level = if debug {log::LevelFilter::Debug} else {log::LevelFilter::Info}; diff --git a/gb/src/multi_device_audio.rs b/gb/src/multi_device_audio.rs index 892c859b..e90ba597 100644 --- a/gb/src/multi_device_audio.rs +++ b/gb/src/multi_device_audio.rs @@ -11,7 +11,7 @@ impl MultiAudioDevice{ } impl AudioDevice for MultiAudioDevice{ - fn push_buffer(&mut self, buffer:&[StereoSample]) { + fn push_buffer(&mut self, buffer:&[StereoSample; BUFFER_SIZE]) { for device in self.devices.iter_mut(){ device.push_buffer(buffer); } diff --git a/gb/src/sdl_audio_device.rs b/gb/src/sdl_audio_device.rs index 028d2d05..a978920d 100644 --- a/gb/src/sdl_audio_device.rs +++ b/gb/src/sdl_audio_device.rs @@ -1,12 +1,15 @@ use std::{ffi::{CStr, c_void}, mem::MaybeUninit}; use lib_gb::{GB_FREQUENCY, apu::audio_device::*}; use sdl2::{sys::*,libc::c_char}; + +#[cfg(not(feature = "sdl-resample"))] use crate::audio_resampler::AudioResampler; +#[cfg(feature = "sdl-resample")] +use crate::sdl_audio_resampler::SdlAudioResampler as AudioResampler; use crossbeam_channel::{Receiver, Sender, bounded}; //After twicking those numbers Iv reached this, this will affect fps which will affect sound tearing -const BUFFER_SIZE:usize = 1024 * 2; const BYTES_TO_WAIT:u32 = BUFFER_SIZE as u32 * 16; const VOLUME:Sample = 10 as Sample; @@ -17,6 +20,7 @@ struct Data{ pub current_buf_index:usize, } pub struct SdlAudioDevie{ + // #[cfg(feature = "push_audio")] device_id: SDL_AudioDeviceID, resampler: AudioResampler, @@ -142,8 +146,9 @@ unsafe extern "C" fn audio_callback(userdata:*mut c_void, buffer:*mut u8, length } impl AudioDevice for SdlAudioDevie{ - fn push_buffer(&mut self, buffer:&[StereoSample]){ - for sample in self.resampler.resample(buffer){ + fn push_buffer(&mut self, buffer:&[StereoSample; BUFFER_SIZE]){ + let resample = self.resampler.resample(buffer); + for sample in resample{ self.buffer[self.buffer_index] = sample.left_sample * VOLUME; self.buffer[self.buffer_index + 1] = sample.right_sample * VOLUME; diff --git a/gb/src/sdl_audio_resampler.rs b/gb/src/sdl_audio_resampler.rs new file mode 100644 index 00000000..53a7194d --- /dev/null +++ b/gb/src/sdl_audio_resampler.rs @@ -0,0 +1,51 @@ +use std::mem::MaybeUninit; +use lib_gb::apu::audio_device::{BUFFER_SIZE, StereoSample}; +use sdl2::sys::*; + +pub struct SdlAudioResampler{ + original_frequency:u32, + target_frequency:u32, +} + +impl SdlAudioResampler{ + pub fn new(original_frequency:u32, target_frequency:u32)->Self{ + Self{ + original_frequency, + target_frequency, + } + } + + pub fn resample(&mut self, buffer:&[StereoSample; BUFFER_SIZE])->Vec{ + unsafe{ + let mut cvt = { + let mut cvt:MaybeUninit = MaybeUninit::uninit(); + SDL_BuildAudioCVT(cvt.as_mut_ptr(), AUDIO_S16 as u16, 2, self.original_frequency as i32, + AUDIO_S16 as u16, 2, self.target_frequency as i32); + cvt.assume_init() + }; + + if cvt.needed != 1{ + std::panic!("Cannot resample between freqs"); + } + + cvt.len = (BUFFER_SIZE * std::mem::size_of::()) as i32; + let mut buf:Vec:: = vec![0;(cvt.len * cvt.len_mult) as usize]; + + std::ptr::copy_nonoverlapping(buffer.as_ptr(), buf.as_mut_ptr() as *mut StereoSample, BUFFER_SIZE); + + cvt.buf = buf.as_mut_ptr(); + let status_code = SDL_ConvertAudio(&mut cvt); + if status_code != 0{ + std::panic!("error while converting audio, status code: {}", status_code); + } + + let buf_ptr = cvt.buf as *mut StereoSample; + let length = cvt.len_cvt as usize / std::mem::size_of::(); + let mut output = vec![StereoSample::const_defualt();length]; + + std::ptr::copy_nonoverlapping(buf_ptr, output.as_mut_ptr(), length); + + return output; + } + } +} \ No newline at end of file diff --git a/gb/src/sdl_gfx_device.rs b/gb/src/sdl_gfx_device.rs index 2990f58e..de049ef3 100644 --- a/gb/src/sdl_gfx_device.rs +++ b/gb/src/sdl_gfx_device.rs @@ -90,7 +90,7 @@ impl GfxDevice for SdlGfxDevice{ let frame_end_time = SDL_GetPerformanceCounter(); let elapsed = ((frame_end_time - self.frame_start_time) as f64 / (SDL_GetPerformanceFrequency() as f64)) * 1_000.0; if elapsed < self.frame_time_ms{ - SDL_Delay(((self.frame_time_ms - elapsed).floor()) as Uint32); + // SDL_Delay(((self.frame_time_ms - elapsed).floor()) as Uint32); } self.frame_start_time = SDL_GetPerformanceCounter(); diff --git a/gb/src/wav_file_audio_device.rs b/gb/src/wav_file_audio_device.rs index 25f2b00d..798d7ae4 100644 --- a/gb/src/wav_file_audio_device.rs +++ b/gb/src/wav_file_audio_device.rs @@ -1,6 +1,9 @@ use lib_gb::apu::audio_device::*; +#[cfg(not(feature = "sdl-resample"))] use crate::audio_resampler::AudioResampler; +#[cfg(feature = "sdl-resample")] +use crate::sdl_audio_resampler::SdlAudioResampler as AudioResampler; pub struct WavfileAudioDevice{ target_frequency:u32, @@ -21,7 +24,7 @@ impl WavfileAudioDevice{ } impl AudioDevice for WavfileAudioDevice{ - fn push_buffer(&mut self, buffer:&[StereoSample]) { + fn push_buffer(&mut self, buffer:&[StereoSample; BUFFER_SIZE]) { self.samples_buffer.append(self.resampler.resample(buffer).as_mut()); } } diff --git a/lib_gb/benches/my_bench.rs b/lib_gb/benches/my_bench.rs index 5937c401..0fa41513 100644 --- a/lib_gb/benches/my_bench.rs +++ b/lib_gb/benches/my_bench.rs @@ -8,7 +8,7 @@ use lib_gb::apu::{ pub fn criterion_bench(c: &mut Criterion){ struct StubApu; impl AudioDevice for StubApu{ - fn push_buffer(&mut self, _buffer:&[StereoSample]){} + fn push_buffer(&mut self, _buffer:&[StereoSample; BUFFER_SIZE]){} } c.bench_function("test apu", |b| b.iter(||{ diff --git a/lib_gb/src/apu/audio_device.rs b/lib_gb/src/apu/audio_device.rs index 23474dcf..a4f4939d 100644 --- a/lib_gb/src/apu/audio_device.rs +++ b/lib_gb/src/apu/audio_device.rs @@ -1,6 +1,9 @@ pub type Sample = i16; pub const DEFAULT_SAPMPLE:Sample = 0 as Sample; +pub const BUFFER_SIZE:usize = 2048; + +#[repr(C, packed)] pub struct StereoSample{ pub left_sample:Sample, pub right_sample:Sample @@ -19,5 +22,5 @@ impl Clone for StereoSample{ } pub trait AudioDevice{ - fn push_buffer(&mut self, buffer:&[StereoSample]); + fn push_buffer(&mut self, buffer:&[StereoSample; BUFFER_SIZE]); } \ No newline at end of file diff --git a/lib_gb/src/apu/gb_apu.rs b/lib_gb/src/apu/gb_apu.rs index a090f5b5..6233f458 100644 --- a/lib_gb/src/apu/gb_apu.rs +++ b/lib_gb/src/apu/gb_apu.rs @@ -10,8 +10,6 @@ use super::{ sound_utils::NUMBER_OF_CHANNELS }; -pub const AUDIO_BUFFER_SIZE:usize = 0x400; - pub struct GbApu{ pub wave_channel:Channel, pub sweep_tone_channel:Channel, @@ -22,7 +20,7 @@ pub struct GbApu{ pub left_terminal:SoundTerminal, pub enabled:bool, - audio_buffer:[StereoSample;AUDIO_BUFFER_SIZE], + audio_buffer:[StereoSample;BUFFER_SIZE], current_m_cycle:u32, device:Device, last_enabled_state:bool @@ -91,7 +89,7 @@ impl GbApu{ } fn push_buffer_if_full(&mut self){ - if self.current_m_cycle as usize >= AUDIO_BUFFER_SIZE{ + if self.current_m_cycle as usize >= BUFFER_SIZE{ self.current_m_cycle = 0; self.device.push_buffer(&self.audio_buffer); } diff --git a/lib_gb/tests/integration_tests.rs b/lib_gb/tests/integration_tests.rs index 7fe1eef5..53520119 100644 --- a/lib_gb/tests/integration_tests.rs +++ b/lib_gb/tests/integration_tests.rs @@ -1,6 +1,7 @@ use std::collections::hash_map::DefaultHasher; use std::hash::{Hash, Hasher}; use std::io::Read; +use lib_gb::apu::audio_device::BUFFER_SIZE; use lib_gb::ppu::gb_ppu::{SCREEN_HEIGHT, SCREEN_WIDTH}; use lib_gb::{ apu::audio_device::AudioDevice, keypad::joypad_provider::JoypadProvider, @@ -29,7 +30,7 @@ impl GfxDevice for CheckHashGfxDevice{ struct StubAudioDevice; impl AudioDevice for StubAudioDevice{ - fn push_buffer(&mut self, _buffer:&[lib_gb::apu::audio_device::StereoSample]) {} + fn push_buffer(&mut self, _buffer:&[lib_gb::apu::audio_device::StereoSample; BUFFER_SIZE]) {} } struct StubJoypadProvider; From c030275afe17d9dd403b339c18876503d751a780 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sun, 31 Oct 2021 01:57:02 +0300 Subject: [PATCH 113/136] Add push and pull audio options and add vsync * Audio pull by deafult and under the featue flag - push-audio * vsync is on by default and by passing `--no-vsync` it can be disabled --- gb/Cargo.toml | 3 +- gb/src/main.rs | 12 ++- gb/src/sdl_audio_device.rs | 188 ++++++++++++++++++++++++++----------- gb/src/sdl_gfx_device.rs | 22 ++--- 4 files changed, 151 insertions(+), 74 deletions(-) diff --git a/gb/Cargo.toml b/gb/Cargo.toml index 22556d13..d05dff1d 100644 --- a/gb/Cargo.toml +++ b/gb/Cargo.toml @@ -6,7 +6,7 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [[bin]] -name = "magen_boy" +name = "magenboy" path = "src/main.rs" [dependencies] @@ -19,6 +19,5 @@ wav = "0.6.0" crossbeam-channel = "0.5" [features] -default = ["sdl-resample"] sdl-resample = [] push-audio = [] \ No newline at end of file diff --git a/gb/src/main.rs b/gb/src/main.rs index c02431e6..7d3bcfe4 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -16,7 +16,7 @@ use log::info; use sdl2::sys::*; const SCREEN_SCALE:u8 = 4; -const TURBO_MUL:u8 = 2; +const TURBO_MUL:u8 = 1; fn init_logger(debug:bool)->Result<(), fern::InitError>{ let level = if debug {log::LevelFilter::Debug} else {log::LevelFilter::Info}; @@ -82,7 +82,8 @@ fn main() { Result::Err(error)=>std::panic!("error initing logger: {}", error) } - let mut sdl_gfx_device = sdl_gfx_device::SdlGfxDevice::new(SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32, "MagenBoy", SCREEN_SCALE, TURBO_MUL); + let mut sdl_gfx_device = sdl_gfx_device::SdlGfxDevice::new(SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32, + "MagenBoy", SCREEN_SCALE, TURBO_MUL, check_for_terminal_feature_flag(&args, "--no-vsync")); let (s,r) = crossbeam_channel::bounded(BUFFERS_NUMBER - 1); let mpmc_device = MpmcGfxDevice{sender:s}; @@ -122,7 +123,12 @@ fn main() { // Receiving usize and not raw ptr cause in rust you cant pass a raw ptr to another thread fn emulation_thread_main(args: Vec, program_name: String, spsc_gfx_device: MpmcGfxDevice, running_ptr: usize) { - let audio_device = sdl_audio_device::SdlAudioDevie::new(44100, TURBO_MUL); + + #[cfg(feature = "push-audio")] + let audio_device = sdl_audio_device::SdlPushAudioDevice::new(44100, TURBO_MUL); + #[cfg(not(feature = "push-audio"))] + let audio_device = sdl_audio_device::SdlPullAudioDevice::new(44100, TURBO_MUL); + let mut devices: Vec::> = Vec::new(); devices.push(Box::new(audio_device)); if check_for_terminal_feature_flag(&args, "--file-audio"){ diff --git a/gb/src/sdl_audio_device.rs b/gb/src/sdl_audio_device.rs index a978920d..a5bb44e3 100644 --- a/gb/src/sdl_audio_device.rs +++ b/gb/src/sdl_audio_device.rs @@ -7,41 +7,59 @@ use crate::audio_resampler::AudioResampler; #[cfg(feature = "sdl-resample")] use crate::sdl_audio_resampler::SdlAudioResampler as AudioResampler; -use crossbeam_channel::{Receiver, Sender, bounded}; +#[cfg(not(feature = "push-audio"))] +use crossbeam_channel::{Receiver, SendError, Sender, bounded}; //After twicking those numbers Iv reached this, this will affect fps which will affect sound tearing +#[cfg(feature = "push-audio")] const BYTES_TO_WAIT:u32 = BUFFER_SIZE as u32 * 16; const VOLUME:Sample = 10 as Sample; +fn get_sdl_error_message()->&'static str{ + unsafe{ + let error_message:*const c_char = SDL_GetError(); + + return CStr::from_ptr(error_message).to_str().unwrap(); + } +} +#[cfg(not(feature = "push-audio"))] struct Data{ - pub rx: Receiver<[Sample;BUFFER_SIZE]>, - pub current_buf: Option<[Sample;BUFFER_SIZE]>, - pub current_buf_index:usize, + rx: Receiver<[Sample;BUFFER_SIZE]>, + current_buf: Option<[Sample;BUFFER_SIZE]>, + current_buf_index:usize, } -pub struct SdlAudioDevie{ - // #[cfg(feature = "push_audio")] - device_id: SDL_AudioDeviceID, - resampler: AudioResampler, +#[cfg(not(feature = "push-audio"))] +pub struct SdlPullAudioDevice{ + resampler: AudioResampler, buffer: [Sample;BUFFER_SIZE], buffer_index:usize, - tx: Sender<[Sample;BUFFER_SIZE]>, + tarnsmiter: Sender<[Sample;BUFFER_SIZE]>, + + userdata: Data } -impl SdlAudioDevie{ +#[cfg(not(feature = "push-audio"))] +impl SdlPullAudioDevice{ pub fn new(frequency:i32, turbo_mul:u8)->Self{ - let(s,r) = bounded(3); - let boxed_data = Box::new(Data{ + // cap of less than 2 hurts the fps + let(s,r) = bounded(2); + let data = Data{ current_buf:Option::None, current_buf_index:0, rx:r - }); + }; - let leaked_data = Box::leak(boxed_data); - + let mut device = SdlPullAudioDevice{ + buffer:[DEFAULT_SAPMPLE;BUFFER_SIZE], + buffer_index:0, + resampler: AudioResampler::new(GB_FREQUENCY * turbo_mul as u32, frequency as u32), + tarnsmiter:s, + userdata:data + }; let desired_audio_spec = SDL_AudioSpec{ freq: frequency, @@ -52,19 +70,19 @@ impl SdlAudioDevie{ padding: 0, size: 0, callback: Option::Some(audio_callback), - userdata: leaked_data as *mut Data as *mut c_void + userdata: (&mut device.userdata) as *mut Data as *mut c_void }; let mut uninit_audio_spec:MaybeUninit = MaybeUninit::uninit(); - let device_id = unsafe{ + unsafe{ SDL_Init(SDL_INIT_AUDIO); SDL_ClearError(); let id = SDL_OpenAudioDevice(std::ptr::null(), 0, &desired_audio_spec, uninit_audio_spec.as_mut_ptr() , 0); if id == 0{ - std::panic!("{}",Self::get_sdl_error_message()); + std::panic!("{}", get_sdl_error_message()); } let init_audio_spec:SDL_AudioSpec = uninit_audio_spec.assume_init(); @@ -75,48 +93,17 @@ impl SdlAudioDevie{ //This will start the audio processing SDL_PauseAudioDevice(id, 0); - - id }; - return SdlAudioDevie{ - device_id: device_id, - buffer:[DEFAULT_SAPMPLE;BUFFER_SIZE], - buffer_index:0, - resampler: AudioResampler::new(GB_FREQUENCY * turbo_mul as u32, frequency as u32), - tx:s, - }; - } - fn get_sdl_error_message()->&'static str{ - unsafe{ - let error_message:*const c_char = SDL_GetError(); - - return CStr::from_ptr(error_message).to_str().unwrap(); - } + return device; } - - fn push_audio_to_device(&self, audio:&[Sample; BUFFER_SIZE])->Result<(),&str>{ - // let audio_ptr: *const c_void = audio.as_ptr() as *const c_void; - // let data_byte_len = (audio.len() * std::mem::size_of::()) as u32; - - // unsafe{ - // while SDL_GetQueuedAudioSize(self.device_id) > BYTES_TO_WAIT{ - // SDL_Delay(1); - // } - - // SDL_ClearError(); - // if SDL_QueueAudio(self.device_id, audio_ptr, data_byte_len) != 0{ - // return Err(Self::get_sdl_error_message()); - // } - - // Ok(()) - // } - self.tx.send(audio.clone()).unwrap(); - Ok(()) + fn push_audio_to_device(&self, audio:&[Sample; BUFFER_SIZE])->Result<(), SendError<[Sample; BUFFER_SIZE]>>{ + self.tarnsmiter.send(audio.clone()) } } +#[cfg(not(feature = "push-audio"))] unsafe extern "C" fn audio_callback(userdata:*mut c_void, buffer:*mut u8, length:i32){ let length = length as usize; let s = &mut *(userdata as *mut Data); @@ -145,7 +132,100 @@ unsafe extern "C" fn audio_callback(userdata:*mut c_void, buffer:*mut u8, length } } -impl AudioDevice for SdlAudioDevie{ +#[cfg(not(feature = "push-audio"))] +impl AudioDevice for SdlPullAudioDevice{ + fn push_buffer(&mut self, buffer:&[StereoSample; BUFFER_SIZE]){ + let resample = self.resampler.resample(buffer); + for sample in resample{ + + self.buffer[self.buffer_index] = sample.left_sample * VOLUME; + self.buffer[self.buffer_index + 1] = sample.right_sample * VOLUME; + self.buffer_index += 2; + if self.buffer_index == BUFFER_SIZE{ + self.push_audio_to_device(&self.buffer).unwrap(); + self.buffer_index = 0; + } + } + } +} + + +#[cfg(feature = "push-audio")] +pub struct SdlPushAudioDevice{ + device_id: SDL_AudioDeviceID, + resampler: AudioResampler, + + buffer: [Sample;BUFFER_SIZE], + buffer_index:usize, +} + +#[cfg(feature = "push-audio")] +impl SdlPushAudioDevice{ + pub fn new(frequency:i32, turbo_mul:u8)->Self{ + let desired_audio_spec = SDL_AudioSpec{ + freq: frequency, + format: AUDIO_S16SYS as u16, + channels: 2, + silence: 0, + samples: BUFFER_SIZE as u16, + padding: 0, + size: 0, + callback: Option::None, + userdata: std::ptr::null_mut() + }; + + + let mut uninit_audio_spec:MaybeUninit = MaybeUninit::uninit(); + + let device_id = unsafe{ + SDL_Init(SDL_INIT_AUDIO); + SDL_ClearError(); + let id = SDL_OpenAudioDevice(std::ptr::null(), 0, &desired_audio_spec, uninit_audio_spec.as_mut_ptr() , 0); + + if id == 0{ + std::panic!("{}", get_sdl_error_message()); + } + + let init_audio_spec:SDL_AudioSpec = uninit_audio_spec.assume_init(); + + if init_audio_spec.freq != frequency { + std::panic!("Error initializing audio could not use the frequency: {}", frequency); + } + + //This will start the audio processing + SDL_PauseAudioDevice(id, 0); + + id + }; + return SdlPushAudioDevice{ + device_id: device_id, + buffer:[DEFAULT_SAPMPLE;BUFFER_SIZE], + buffer_index:0, + resampler: AudioResampler::new(GB_FREQUENCY * turbo_mul as u32, frequency as u32) + }; + } + + fn push_audio_to_device(&self, audio:&[Sample; BUFFER_SIZE])->Result<(),&str>{ + let audio_ptr: *const c_void = audio.as_ptr() as *const c_void; + let data_byte_len = (audio.len() * std::mem::size_of::()) as u32; + + unsafe{ + while SDL_GetQueuedAudioSize(self.device_id) > BYTES_TO_WAIT{ + SDL_Delay(1); + } + + SDL_ClearError(); + if SDL_QueueAudio(self.device_id, audio_ptr, data_byte_len) != 0{ + return Err(get_sdl_error_message()); + } + + Ok(()) + } + } +} + +#[cfg(feature = "push-audio")] +impl AudioDevice for SdlPushAudioDevice{ fn push_buffer(&mut self, buffer:&[StereoSample; BUFFER_SIZE]){ let resample = self.resampler.resample(buffer); for sample in resample{ diff --git a/gb/src/sdl_gfx_device.rs b/gb/src/sdl_gfx_device.rs index de049ef3..ff8c66d3 100644 --- a/gb/src/sdl_gfx_device.rs +++ b/gb/src/sdl_gfx_device.rs @@ -10,14 +10,12 @@ pub struct SdlGfxDevice{ width:u32, height:u32, sacle:u8, - frame_start_time:u64, - frame_time_ms:f64, discard:u8, turbo_mul:u8, } impl SdlGfxDevice{ - pub fn new(buffer_width:u32, buffer_height:u32, window_name:&str, screen_scale: u8, turbo_mul:u8)->Self{ + pub fn new(buffer_width:u32, buffer_height:u32, window_name:&str, screen_scale: u8, turbo_mul:u8, disable_vsync:bool)->Self{ let cs_wnd_name = CString::new(window_name).unwrap(); let (_window, renderer, texture): (*mut SDL_Window, *mut SDL_Renderer, *mut SDL_Texture) = unsafe{ @@ -26,8 +24,12 @@ impl SdlGfxDevice{ cs_wnd_name.as_ptr(), SDL_WINDOWPOS_UNDEFINED_MASK as i32, SDL_WINDOWPOS_UNDEFINED_MASK as i32, buffer_width as i32 * screen_scale as i32, buffer_height as i32 * screen_scale as i32, 0); - - let rend: *mut SDL_Renderer = SDL_CreateRenderer(wind, -1, 0); + let mut flags = SDL_RendererFlags::SDL_RENDERER_ACCELERATED as u32; + if !disable_vsync{ + flags |= SDL_RendererFlags::SDL_RENDERER_PRESENTVSYNC as u32; + } + + let rend: *mut SDL_Renderer = SDL_CreateRenderer(wind, -1, flags); let tex: *mut SDL_Texture = SDL_CreateTexture(rend, SDL_PixelFormatEnum::SDL_PIXELFORMAT_ARGB8888 as u32, SDL_TextureAccess::SDL_TEXTUREACCESS_STREAMING as i32, @@ -43,8 +45,6 @@ impl SdlGfxDevice{ height:buffer_height, width:buffer_width, sacle:screen_scale, - frame_start_time: unsafe{SDL_GetPerformanceCounter()}, - frame_time_ms: (1.0/(60.0 as f64)) * 1_000.0, discard:0, turbo_mul } @@ -86,14 +86,6 @@ impl GfxDevice for SdlGfxDevice{ //There is no need to call SDL_RenderClear since im replacing the whole buffer SDL_RenderCopy(self.renderer, self.texture, std::ptr::null(), std::ptr::null()); SDL_RenderPresent(self.renderer); - - let frame_end_time = SDL_GetPerformanceCounter(); - let elapsed = ((frame_end_time - self.frame_start_time) as f64 / (SDL_GetPerformanceFrequency() as f64)) * 1_000.0; - if elapsed < self.frame_time_ms{ - // SDL_Delay(((self.frame_time_ms - elapsed).floor()) as Uint32); - } - - self.frame_start_time = SDL_GetPerformanceCounter(); } } } \ No newline at end of file From 6a7799c58c20f87febe81c8d346b591cacf778cf Mon Sep 17 00:00:00 2001 From: alloncm Date: Sun, 31 Oct 2021 01:57:19 +0300 Subject: [PATCH 114/136] Update the readme with cli flags :) --- README.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/README.md b/README.md index f59b9484..08636e24 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,17 @@ The main goal of this project is to be able to play Pokemon on my own emulator. **More will be added if neccessary (and by neccessary I mean if games I want to play will require them)** +## How to use + +```shell +magenboy [path_to_rom] [other_optional_flags] +``` + +### Optional flags +* `--log` - Print logs in debug mode to a file +* `--file-audio` - Saves the audio to a file +* `--no-vsync` - Disable vsync + ## GameBoy ### Development Status From c8e598a3b8215b4c2931103a1895b5e40828f734 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sun, 31 Oct 2021 23:21:15 +0200 Subject: [PATCH 115/136] Arrange the conditinal comp stuff There are 2 flags * sdl-resample - will compile the sdl resampler instead of my resampler * push-audio - will push the audio to the device instead of the device pulling it by himslef. --- gb/src/{ => audio}/audio_resampler.rs | 29 +-- gb/src/audio/mod.rs | 56 +++++ gb/src/{ => audio}/multi_device_audio.rs | 0 gb/src/{ => audio}/sdl_audio_resampler.rs | 9 +- gb/src/audio/sdl_pull_audio_device.rs | 133 +++++++++++ gb/src/audio/sdl_push_audio_device.rs | 101 ++++++++ gb/src/{ => audio}/wav_file_audio_device.rs | 16 +- gb/src/main.rs | 16 +- gb/src/sdl_audio_device.rs | 242 -------------------- 9 files changed, 322 insertions(+), 280 deletions(-) rename gb/src/{ => audio}/audio_resampler.rs (89%) create mode 100644 gb/src/audio/mod.rs rename gb/src/{ => audio}/multi_device_audio.rs (100%) rename gb/src/{ => audio}/sdl_audio_resampler.rs (87%) create mode 100644 gb/src/audio/sdl_pull_audio_device.rs create mode 100644 gb/src/audio/sdl_push_audio_device.rs rename gb/src/{ => audio}/wav_file_audio_device.rs (76%) delete mode 100644 gb/src/sdl_audio_device.rs diff --git a/gb/src/audio_resampler.rs b/gb/src/audio/audio_resampler.rs similarity index 89% rename from gb/src/audio_resampler.rs rename to gb/src/audio/audio_resampler.rs index 2527dcf8..6a59d0a0 100644 --- a/gb/src/audio_resampler.rs +++ b/gb/src/audio/audio_resampler.rs @@ -1,6 +1,7 @@ use lib_gb::apu::audio_device::{BUFFER_SIZE, DEFAULT_SAPMPLE, Sample, StereoSample}; +use super::AudioResampler; -pub struct AudioResampler{ +pub struct MagenAudioResampler{ to_skip:u32, sampling_buffer:Vec, sampling_counter:u32, @@ -10,8 +11,18 @@ pub struct AudioResampler{ skip_to_use:u32, } -impl AudioResampler{ - pub fn new(original_frequency:u32, target_frequency:u32)->Self{ +impl MagenAudioResampler{ + fn interpolate_sample(samples:&[StereoSample])->StereoSample{ + + let interpulated_left_sample = samples.iter().fold(DEFAULT_SAPMPLE, |acc, x| acc + x.left_sample) / samples.len() as Sample; + let interpulated_right_sample = samples.iter().fold(DEFAULT_SAPMPLE, |acc, x| acc + x.right_sample) / samples.len() as Sample; + + return StereoSample{left_sample: interpulated_left_sample, right_sample: interpulated_right_sample}; + } +} + +impl AudioResampler for MagenAudioResampler{ + fn new(original_frequency:u32, target_frequency:u32)->Self{ // Calling round in order to get the nearest integer and resample as precise as possible let div = original_frequency as f32 / target_frequency as f32; @@ -30,7 +41,7 @@ impl AudioResampler{ std::panic!("target freqency is too high: {}", target_frequency); } - AudioResampler{ + MagenAudioResampler{ to_skip:to_skip, sampling_buffer:Vec::with_capacity(upper_to_skip as usize), sampling_counter: 0, @@ -41,7 +52,7 @@ impl AudioResampler{ } } - pub fn resample(&mut self, buffer:&[StereoSample; BUFFER_SIZE])->Vec{ + fn resample(&mut self, buffer:&[StereoSample; BUFFER_SIZE])->Vec{ let mut output = Vec::new(); for sample in buffer.into_iter(){ self.sampling_buffer.push(sample.clone()); @@ -67,12 +78,4 @@ impl AudioResampler{ return output; } - - fn interpolate_sample(samples:&[StereoSample])->StereoSample{ - - let interpulated_left_sample = samples.iter().fold(DEFAULT_SAPMPLE, |acc, x| acc + x.left_sample) / samples.len() as Sample; - let interpulated_right_sample = samples.iter().fold(DEFAULT_SAPMPLE, |acc, x| acc + x.right_sample) / samples.len() as Sample; - - return StereoSample{left_sample: interpulated_left_sample, right_sample: interpulated_right_sample}; - } } \ No newline at end of file diff --git a/gb/src/audio/mod.rs b/gb/src/audio/mod.rs new file mode 100644 index 00000000..31dd91e0 --- /dev/null +++ b/gb/src/audio/mod.rs @@ -0,0 +1,56 @@ +pub mod multi_device_audio; +pub mod wav_file_audio_device; + +#[cfg(not(feature = "push-audio"))] +pub mod sdl_pull_audio_device; +#[cfg(feature = "push-audio")] +pub mod sdl_push_audio_device; + +use std::ffi::CStr; +use lib_gb::apu::audio_device::{AudioDevice, BUFFER_SIZE, Sample, StereoSample}; +use sdl2::{libc::c_char, sys::SDL_GetError}; + +#[cfg(feature = "sdl-resampler")] +pub type ChosenResampler = sdl_audio_resampler::SdlAudioResampler; +#[cfg(feature = "sdl-resampler")] +pub mod sdl_audio_resampler; + +#[cfg(not(feature = "sdl-resampler"))] +pub mod audio_resampler; +#[cfg(not(feature = "sdl-resampler"))] +pub type ChosenResampler = audio_resampler::MagenAudioResampler; + +fn get_sdl_error_message()->&'static str{ + unsafe{ + let error_message:*const c_char = SDL_GetError(); + + return CStr::from_ptr(error_message).to_str().unwrap(); + } +} + +pub trait AudioResampler{ + fn new(original_frequency:u32, target_frequency:u32)->Self; + fn resample(&mut self, buffer:&[StereoSample; BUFFER_SIZE])->Vec; +} + +trait SdlAudioDevice : AudioDevice{ + const VOLUME:Sample = 10 as Sample; + + fn push_buffer(&mut self, buffer:&[StereoSample; BUFFER_SIZE]){ + let resample = self.get_resampler().resample(buffer); + for sample in resample{ + let(buffer, index) = self.get_audio_buffer(); + buffer[*index] = sample.left_sample * Self::VOLUME; + buffer[*index + 1] = sample.left_sample * Self::VOLUME; + *index += 2; + if *index == BUFFER_SIZE{ + *index = 0; + self.full_buffer_callback().unwrap(); + } + } + } + + fn get_audio_buffer(&mut self)->(&mut [Sample;BUFFER_SIZE], &mut usize); + fn get_resampler(&mut self)->&mut AR; + fn full_buffer_callback(&self)->Result<(), String>; +} diff --git a/gb/src/multi_device_audio.rs b/gb/src/audio/multi_device_audio.rs similarity index 100% rename from gb/src/multi_device_audio.rs rename to gb/src/audio/multi_device_audio.rs diff --git a/gb/src/sdl_audio_resampler.rs b/gb/src/audio/sdl_audio_resampler.rs similarity index 87% rename from gb/src/sdl_audio_resampler.rs rename to gb/src/audio/sdl_audio_resampler.rs index 53a7194d..09bb02da 100644 --- a/gb/src/sdl_audio_resampler.rs +++ b/gb/src/audio/sdl_audio_resampler.rs @@ -1,21 +1,22 @@ use std::mem::MaybeUninit; use lib_gb::apu::audio_device::{BUFFER_SIZE, StereoSample}; use sdl2::sys::*; +use super::AudioResampler; pub struct SdlAudioResampler{ original_frequency:u32, target_frequency:u32, } -impl SdlAudioResampler{ - pub fn new(original_frequency:u32, target_frequency:u32)->Self{ +impl AudioResampler for SdlAudioResampler{ + fn new(original_frequency:u32, target_frequency:u32)->Self{ Self{ original_frequency, target_frequency, } } - - pub fn resample(&mut self, buffer:&[StereoSample; BUFFER_SIZE])->Vec{ + + fn resample(&mut self, buffer:&[StereoSample; BUFFER_SIZE])->Vec{ unsafe{ let mut cvt = { let mut cvt:MaybeUninit = MaybeUninit::uninit(); diff --git a/gb/src/audio/sdl_pull_audio_device.rs b/gb/src/audio/sdl_pull_audio_device.rs new file mode 100644 index 00000000..c45fd7b5 --- /dev/null +++ b/gb/src/audio/sdl_pull_audio_device.rs @@ -0,0 +1,133 @@ +use std::{ffi::c_void, mem::MaybeUninit}; +use lib_gb::{GB_FREQUENCY, apu::audio_device::*}; +use sdl2::sys::*; +use crate::audio::get_sdl_error_message; + +use super::{AudioResampler, SdlAudioDevice}; + +use crossbeam_channel::{Receiver, SendError, Sender, bounded}; + + +struct Data{ + rx: Receiver<[Sample;BUFFER_SIZE]>, + current_buf: Option<[Sample;BUFFER_SIZE]>, + current_buf_index:usize, +} + +pub struct SdlPullAudioDevice{ + resampler: AR, + buffer: [Sample;BUFFER_SIZE], + buffer_index:usize, + + tarnsmiter: Sender<[Sample;BUFFER_SIZE]>, + + userdata: Data +} + +impl SdlPullAudioDevice{ + pub fn new(frequency:i32, turbo_mul:u8)->Self{ + + // cap of less than 2 hurts the fps + let(s,r) = bounded(2); + let data = Data{ + current_buf:Option::None, + current_buf_index:0, + rx:r + }; + + let mut device = SdlPullAudioDevice{ + buffer:[DEFAULT_SAPMPLE;BUFFER_SIZE], + buffer_index:0, + resampler: AudioResampler::new(GB_FREQUENCY * turbo_mul as u32, frequency as u32), + tarnsmiter:s, + userdata:data + }; + + let desired_audio_spec = SDL_AudioSpec{ + freq: frequency, + format: AUDIO_S16SYS as u16, + channels: 2, + silence: 0, + samples: BUFFER_SIZE as u16, + padding: 0, + size: 0, + callback: Option::Some(audio_callback), + userdata: (&mut device.userdata) as *mut Data as *mut c_void + }; + + + let mut uninit_audio_spec:MaybeUninit = MaybeUninit::uninit(); + + unsafe{ + SDL_Init(SDL_INIT_AUDIO); + SDL_ClearError(); + let id = SDL_OpenAudioDevice(std::ptr::null(), 0, &desired_audio_spec, uninit_audio_spec.as_mut_ptr() , 0); + + if id == 0{ + std::panic!("{}", get_sdl_error_message()); + } + + let init_audio_spec:SDL_AudioSpec = uninit_audio_spec.assume_init(); + + if init_audio_spec.freq != frequency { + std::panic!("Error initializing audio could not use the frequency: {}", frequency); + } + + //This will start the audio processing + SDL_PauseAudioDevice(id, 0); + }; + + return device; + } + + fn push_audio_to_device(&self, audio:&[Sample; BUFFER_SIZE])->Result<(), SendError<[Sample; BUFFER_SIZE]>>{ + self.tarnsmiter.send(audio.clone()) + } +} + +impl SdlAudioDevice for SdlPullAudioDevice{ + fn get_audio_buffer(&mut self) ->(&mut [Sample;BUFFER_SIZE], &mut usize) { + (&mut self.buffer, &mut self.buffer_index) + } + fn get_resampler(&mut self) ->&mut AR { + &mut self.resampler + } + fn full_buffer_callback(&self) ->Result<(), String> { + self.push_audio_to_device(&self.buffer).map_err(|e|e.to_string()) + } +} + +impl AudioDevice for SdlPullAudioDevice{ + fn push_buffer(&mut self, buffer:&[StereoSample; BUFFER_SIZE]) { + SdlAudioDevice::push_buffer(self, buffer); + } +} + +unsafe extern "C" fn audio_callback(userdata:*mut c_void, buffer:*mut u8, length:i32){ + let length = length as usize; + let s = &mut *(userdata as *mut Data); + + if s.current_buf.is_none(){ + s.current_buf = Some(s.rx.recv().unwrap()); + } + + let samples = s.current_buf.unwrap(); + let samples_size = (samples.len() * std::mem::size_of::()) - s.current_buf_index; + let samples_ptr = (samples.as_ptr() as *mut u8).add(s.current_buf_index); + std::ptr::copy_nonoverlapping(samples_ptr, buffer, std::cmp::min(length, samples_size)); + + if length > samples_size && s.rx.is_empty(){ + s.current_buf = Option::None; + s.current_buf_index = 0; + std::ptr::write_bytes(buffer.add(samples.len() as usize), 0, length - samples_size); + } + else if length > samples_size{ + s.current_buf = Option::None; + s.current_buf_index = 0; + audio_callback(userdata, buffer.add(samples_size), (length - samples_size) as i32); + } + else{ + s.current_buf_index = length; + } +} + diff --git a/gb/src/audio/sdl_push_audio_device.rs b/gb/src/audio/sdl_push_audio_device.rs new file mode 100644 index 00000000..964bf253 --- /dev/null +++ b/gb/src/audio/sdl_push_audio_device.rs @@ -0,0 +1,101 @@ +use std::{ffi::c_void, mem::MaybeUninit, str::FromStr}; +use lib_gb::{GB_FREQUENCY, apu::audio_device::{AudioDevice, BUFFER_SIZE, DEFAULT_SAPMPLE, Sample, StereoSample}}; +use sdl2::sys::*; +use super::{SdlAudioDevice, get_sdl_error_message}; +use super::AudioResampler; + + +//After twicking those numbers Iv reached this, this will affect fps which will affect sound tearing +const BYTES_TO_WAIT:u32 = BUFFER_SIZE as u32 * 16; + +pub struct SdlPushAudioDevice{ + device_id: SDL_AudioDeviceID, + resampler: AR, + + buffer: [Sample;BUFFER_SIZE], + buffer_index:usize, +} + +impl SdlPushAudioDevice{ + pub fn new(frequency:i32, turbo_mul:u8)->Self{ + let desired_audio_spec = SDL_AudioSpec{ + freq: frequency, + format: AUDIO_S16SYS as u16, + channels: 2, + silence: 0, + samples: BUFFER_SIZE as u16, + padding: 0, + size: 0, + callback: Option::None, + userdata: std::ptr::null_mut() + }; + + + let mut uninit_audio_spec:MaybeUninit = MaybeUninit::uninit(); + + let device_id = unsafe{ + SDL_Init(SDL_INIT_AUDIO); + SDL_ClearError(); + let id = SDL_OpenAudioDevice(std::ptr::null(), 0, &desired_audio_spec, uninit_audio_spec.as_mut_ptr() , 0); + + if id == 0{ + std::panic!("{}", get_sdl_error_message()); + } + + let init_audio_spec:SDL_AudioSpec = uninit_audio_spec.assume_init(); + + if init_audio_spec.freq != frequency { + std::panic!("Error initializing audio could not use the frequency: {}", frequency); + } + + //This will start the audio processing + SDL_PauseAudioDevice(id, 0); + + id + }; + return SdlPushAudioDevice{ + device_id: device_id, + buffer:[DEFAULT_SAPMPLE;BUFFER_SIZE], + buffer_index:0, + resampler: AudioResampler::new(GB_FREQUENCY * turbo_mul as u32, frequency as u32) + }; + } + + fn push_audio_to_device(&self, audio:&[Sample; BUFFER_SIZE])->Result<(),&str>{ + let audio_ptr: *const c_void = audio.as_ptr() as *const c_void; + let data_byte_len = (audio.len() * std::mem::size_of::()) as u32; + + unsafe{ + while SDL_GetQueuedAudioSize(self.device_id) > BYTES_TO_WAIT{ + SDL_Delay(1); + } + + SDL_ClearError(); + if SDL_QueueAudio(self.device_id, audio_ptr, data_byte_len) != 0{ + return Err(get_sdl_error_message()); + } + + Ok(()) + } + } +} + +impl SdlAudioDevice for SdlPushAudioDevice{ + fn get_audio_buffer(&mut self) ->(&mut [Sample;BUFFER_SIZE], &mut usize) { + (&mut self.buffer, &mut self.buffer_index) + } + + fn get_resampler(&mut self) ->&mut AR { + &mut self.resampler + } + + fn full_buffer_callback(&self)->Result<(), String> { + self.push_audio_to_device(&self.buffer).map_err(|e|String::from_str(e).unwrap()) + } +} + +impl AudioDevice for SdlPushAudioDevice{ + fn push_buffer(&mut self, buffer:&[StereoSample; BUFFER_SIZE]) { + SdlAudioDevice::push_buffer(self, buffer); + } +} \ No newline at end of file diff --git a/gb/src/wav_file_audio_device.rs b/gb/src/audio/wav_file_audio_device.rs similarity index 76% rename from gb/src/wav_file_audio_device.rs rename to gb/src/audio/wav_file_audio_device.rs index 798d7ae4..e2377fcc 100644 --- a/gb/src/wav_file_audio_device.rs +++ b/gb/src/audio/wav_file_audio_device.rs @@ -1,18 +1,14 @@ use lib_gb::apu::audio_device::*; +use super::AudioResampler; -#[cfg(not(feature = "sdl-resample"))] -use crate::audio_resampler::AudioResampler; -#[cfg(feature = "sdl-resample")] -use crate::sdl_audio_resampler::SdlAudioResampler as AudioResampler; - -pub struct WavfileAudioDevice{ +pub struct WavfileAudioDevice{ target_frequency:u32, - resampler: AudioResampler, + resampler: AR, filename:&'static str, samples_buffer:Vec:: } -impl WavfileAudioDevice{ +impl WavfileAudioDevice{ pub fn new(target_freq:u32, original_freq:u32, filename:&'static str)->Self{ WavfileAudioDevice{ filename, @@ -23,13 +19,13 @@ impl WavfileAudioDevice{ } } -impl AudioDevice for WavfileAudioDevice{ +impl AudioDevice for WavfileAudioDevice{ fn push_buffer(&mut self, buffer:&[StereoSample; BUFFER_SIZE]) { self.samples_buffer.append(self.resampler.resample(buffer).as_mut()); } } -impl Drop for WavfileAudioDevice{ +impl Drop for WavfileAudioDevice{ fn drop(&mut self) { let header = wav::header::Header::new(wav::WAV_FORMAT_PCM, 2, self.target_frequency, 16); let mut samples = Vec::with_capacity(self.samples_buffer.len() * 2); diff --git a/gb/src/main.rs b/gb/src/main.rs index 7d3bcfe4..29cdd99c 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -1,15 +1,9 @@ mod mbc_handler; mod sdl_joypad_provider; -mod sdl_audio_device; -#[cfg(not(feature = "sdl-resample"))] -mod audio_resampler; -#[cfg(feature = "sdl-resample")] -mod sdl_audio_resampler; -mod wav_file_audio_device; -mod multi_device_audio; mod sdl_gfx_device; +mod audio; -use crate::{mbc_handler::*, sdl_joypad_provider::*, multi_device_audio::*}; +use crate::{audio::{ChosenResampler, multi_device_audio::*}, mbc_handler::*, sdl_joypad_provider::*}; use lib_gb::{GB_FREQUENCY, apu::audio_device::*, keypad::button::Button, machine::gameboy::GameBoy, mmu::gb_mmu::BOOT_ROM_SIZE, ppu::{gb_ppu::{BUFFERS_NUMBER, SCREEN_HEIGHT, SCREEN_WIDTH}, gfx_device::GfxDevice}}; use std::{fs, env, result::Result, vec::Vec}; use log::info; @@ -125,14 +119,14 @@ fn main() { fn emulation_thread_main(args: Vec, program_name: String, spsc_gfx_device: MpmcGfxDevice, running_ptr: usize) { #[cfg(feature = "push-audio")] - let audio_device = sdl_audio_device::SdlPushAudioDevice::new(44100, TURBO_MUL); + let audio_device = audio::sdl_push_audio_device::SdlPushAudioDevice::::new(44100, TURBO_MUL); #[cfg(not(feature = "push-audio"))] - let audio_device = sdl_audio_device::SdlPullAudioDevice::new(44100, TURBO_MUL); + let audio_device = audio::sdl_pull_audio_device::SdlPullAudioDevice::::new(44100, TURBO_MUL); let mut devices: Vec::> = Vec::new(); devices.push(Box::new(audio_device)); if check_for_terminal_feature_flag(&args, "--file-audio"){ - let wav_ad = wav_file_audio_device::WavfileAudioDevice::new(44100, GB_FREQUENCY, "output.wav"); + let wav_ad = audio::wav_file_audio_device::WavfileAudioDevice::::new(44100, GB_FREQUENCY, "output.wav"); devices.push(Box::new(wav_ad)); log::info!("Writing audio to file: output.wav"); } diff --git a/gb/src/sdl_audio_device.rs b/gb/src/sdl_audio_device.rs deleted file mode 100644 index a5bb44e3..00000000 --- a/gb/src/sdl_audio_device.rs +++ /dev/null @@ -1,242 +0,0 @@ -use std::{ffi::{CStr, c_void}, mem::MaybeUninit}; -use lib_gb::{GB_FREQUENCY, apu::audio_device::*}; -use sdl2::{sys::*,libc::c_char}; - -#[cfg(not(feature = "sdl-resample"))] -use crate::audio_resampler::AudioResampler; -#[cfg(feature = "sdl-resample")] -use crate::sdl_audio_resampler::SdlAudioResampler as AudioResampler; - -#[cfg(not(feature = "push-audio"))] -use crossbeam_channel::{Receiver, SendError, Sender, bounded}; - -//After twicking those numbers Iv reached this, this will affect fps which will affect sound tearing -#[cfg(feature = "push-audio")] -const BYTES_TO_WAIT:u32 = BUFFER_SIZE as u32 * 16; -const VOLUME:Sample = 10 as Sample; - -fn get_sdl_error_message()->&'static str{ - unsafe{ - let error_message:*const c_char = SDL_GetError(); - - return CStr::from_ptr(error_message).to_str().unwrap(); - } -} - -#[cfg(not(feature = "push-audio"))] -struct Data{ - rx: Receiver<[Sample;BUFFER_SIZE]>, - current_buf: Option<[Sample;BUFFER_SIZE]>, - current_buf_index:usize, -} - -#[cfg(not(feature = "push-audio"))] -pub struct SdlPullAudioDevice{ - resampler: AudioResampler, - buffer: [Sample;BUFFER_SIZE], - buffer_index:usize, - - tarnsmiter: Sender<[Sample;BUFFER_SIZE]>, - - userdata: Data -} - -#[cfg(not(feature = "push-audio"))] -impl SdlPullAudioDevice{ - pub fn new(frequency:i32, turbo_mul:u8)->Self{ - - // cap of less than 2 hurts the fps - let(s,r) = bounded(2); - let data = Data{ - current_buf:Option::None, - current_buf_index:0, - rx:r - }; - - let mut device = SdlPullAudioDevice{ - buffer:[DEFAULT_SAPMPLE;BUFFER_SIZE], - buffer_index:0, - resampler: AudioResampler::new(GB_FREQUENCY * turbo_mul as u32, frequency as u32), - tarnsmiter:s, - userdata:data - }; - - let desired_audio_spec = SDL_AudioSpec{ - freq: frequency, - format: AUDIO_S16SYS as u16, - channels: 2, - silence: 0, - samples: BUFFER_SIZE as u16, - padding: 0, - size: 0, - callback: Option::Some(audio_callback), - userdata: (&mut device.userdata) as *mut Data as *mut c_void - }; - - - let mut uninit_audio_spec:MaybeUninit = MaybeUninit::uninit(); - - unsafe{ - SDL_Init(SDL_INIT_AUDIO); - SDL_ClearError(); - let id = SDL_OpenAudioDevice(std::ptr::null(), 0, &desired_audio_spec, uninit_audio_spec.as_mut_ptr() , 0); - - if id == 0{ - std::panic!("{}", get_sdl_error_message()); - } - - let init_audio_spec:SDL_AudioSpec = uninit_audio_spec.assume_init(); - - if init_audio_spec.freq != frequency { - std::panic!("Error initializing audio could not use the frequency: {}", frequency); - } - - //This will start the audio processing - SDL_PauseAudioDevice(id, 0); - }; - - return device; - } - - fn push_audio_to_device(&self, audio:&[Sample; BUFFER_SIZE])->Result<(), SendError<[Sample; BUFFER_SIZE]>>{ - self.tarnsmiter.send(audio.clone()) - } -} - -#[cfg(not(feature = "push-audio"))] -unsafe extern "C" fn audio_callback(userdata:*mut c_void, buffer:*mut u8, length:i32){ - let length = length as usize; - let s = &mut *(userdata as *mut Data); - - if s.current_buf.is_none(){ - s.current_buf = Some(s.rx.recv().unwrap()); - } - - let samples = s.current_buf.unwrap(); - let samples_size = (samples.len() * std::mem::size_of::()) - s.current_buf_index; - let samples_ptr = (samples.as_ptr() as *mut u8).add(s.current_buf_index); - std::ptr::copy_nonoverlapping(samples_ptr, buffer, std::cmp::min(length, samples_size)); - - if length > samples_size && s.rx.is_empty(){ - s.current_buf = Option::None; - s.current_buf_index = 0; - std::ptr::write_bytes(buffer.add(samples.len() as usize), 0, length - samples_size); - } - else if length > samples_size{ - s.current_buf = Option::None; - s.current_buf_index = 0; - audio_callback(userdata, buffer.add(samples_size), (length - samples_size) as i32); - } - else{ - s.current_buf_index = length; - } -} - -#[cfg(not(feature = "push-audio"))] -impl AudioDevice for SdlPullAudioDevice{ - fn push_buffer(&mut self, buffer:&[StereoSample; BUFFER_SIZE]){ - let resample = self.resampler.resample(buffer); - for sample in resample{ - - self.buffer[self.buffer_index] = sample.left_sample * VOLUME; - self.buffer[self.buffer_index + 1] = sample.right_sample * VOLUME; - self.buffer_index += 2; - if self.buffer_index == BUFFER_SIZE{ - self.push_audio_to_device(&self.buffer).unwrap(); - self.buffer_index = 0; - } - } - } -} - - -#[cfg(feature = "push-audio")] -pub struct SdlPushAudioDevice{ - device_id: SDL_AudioDeviceID, - resampler: AudioResampler, - - buffer: [Sample;BUFFER_SIZE], - buffer_index:usize, -} - -#[cfg(feature = "push-audio")] -impl SdlPushAudioDevice{ - pub fn new(frequency:i32, turbo_mul:u8)->Self{ - let desired_audio_spec = SDL_AudioSpec{ - freq: frequency, - format: AUDIO_S16SYS as u16, - channels: 2, - silence: 0, - samples: BUFFER_SIZE as u16, - padding: 0, - size: 0, - callback: Option::None, - userdata: std::ptr::null_mut() - }; - - - let mut uninit_audio_spec:MaybeUninit = MaybeUninit::uninit(); - - let device_id = unsafe{ - SDL_Init(SDL_INIT_AUDIO); - SDL_ClearError(); - let id = SDL_OpenAudioDevice(std::ptr::null(), 0, &desired_audio_spec, uninit_audio_spec.as_mut_ptr() , 0); - - if id == 0{ - std::panic!("{}", get_sdl_error_message()); - } - - let init_audio_spec:SDL_AudioSpec = uninit_audio_spec.assume_init(); - - if init_audio_spec.freq != frequency { - std::panic!("Error initializing audio could not use the frequency: {}", frequency); - } - - //This will start the audio processing - SDL_PauseAudioDevice(id, 0); - - id - }; - return SdlPushAudioDevice{ - device_id: device_id, - buffer:[DEFAULT_SAPMPLE;BUFFER_SIZE], - buffer_index:0, - resampler: AudioResampler::new(GB_FREQUENCY * turbo_mul as u32, frequency as u32) - }; - } - - fn push_audio_to_device(&self, audio:&[Sample; BUFFER_SIZE])->Result<(),&str>{ - let audio_ptr: *const c_void = audio.as_ptr() as *const c_void; - let data_byte_len = (audio.len() * std::mem::size_of::()) as u32; - - unsafe{ - while SDL_GetQueuedAudioSize(self.device_id) > BYTES_TO_WAIT{ - SDL_Delay(1); - } - - SDL_ClearError(); - if SDL_QueueAudio(self.device_id, audio_ptr, data_byte_len) != 0{ - return Err(get_sdl_error_message()); - } - - Ok(()) - } - } -} - -#[cfg(feature = "push-audio")] -impl AudioDevice for SdlPushAudioDevice{ - fn push_buffer(&mut self, buffer:&[StereoSample; BUFFER_SIZE]){ - let resample = self.resampler.resample(buffer); - for sample in resample{ - - self.buffer[self.buffer_index] = sample.left_sample * VOLUME; - self.buffer[self.buffer_index + 1] = sample.right_sample * VOLUME; - self.buffer_index += 2; - if self.buffer_index == BUFFER_SIZE{ - self.push_audio_to_device(&self.buffer).unwrap(); - self.buffer_index = 0; - } - } - } -} \ No newline at end of file From 4e74636419b729323d5f0b3e31d348a101b69d08 Mon Sep 17 00:00:00 2001 From: alloncm Date: Mon, 1 Nov 2021 21:35:30 +0200 Subject: [PATCH 116/136] FIx an outdated test The apu timer test was outdated. The apu timer counts in tcycles but increment itself each time by 4 cycles and not 1. --- lib_gb/tests/timer.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib_gb/tests/timer.rs b/lib_gb/tests/timer.rs index d9ceebff..8d67b282 100644 --- a/lib_gb/tests/timer.rs +++ b/lib_gb/tests/timer.rs @@ -3,15 +3,16 @@ use lib_gb::apu::timer::Timer; #[test] fn timer_test(){ - let mut timer = Timer::new(512); + let count = 512; + let mut timer = Timer::new(count); let mut counter = 0; - for _ in 0..511{ + for _ in 0..(count/4)-1{ counter+=1; assert!(timer.cycle() == false); } counter+=1; assert!(timer.cycle() == true); - assert!(counter == 512) + assert!(counter == 512/4) } \ No newline at end of file From f1d27538592b91c7d2223dbb618f1f5bcae99b9f Mon Sep 17 00:00:00 2001 From: alloncm Date: Thu, 4 Nov 2021 20:52:17 +0200 Subject: [PATCH 117/136] Cr fixes Some renaming and other cosmetic changes like extracting to files. Also using cfg-if for ifdef like code --- Cargo.lock | 5 +- gb/Cargo.toml | 3 +- ..._resampler.rs => magen_audio_resampler.rs} | 2 - gb/src/audio/mod.rs | 38 ++++++++----- gb/src/audio/sdl_audio_resampler.rs | 3 +- gb/src/audio/sdl_pull_audio_device.rs | 55 +++++++++---------- gb/src/audio/sdl_push_audio_device.rs | 48 ++++++++-------- gb/src/main.rs | 23 ++------ gb/src/mpmc_gfx_device.rs | 19 +++++++ lib_gb/Cargo.toml | 2 +- .../benches/{my_bench.rs => lib_gb_bench.rs} | 0 lib_gb/src/apu/sound_terminal.rs | 1 + lib_gb/src/utils/mod.rs | 1 + 13 files changed, 104 insertions(+), 96 deletions(-) rename gb/src/audio/{audio_resampler.rs => magen_audio_resampler.rs} (99%) create mode 100644 gb/src/mpmc_gfx_device.rs rename lib_gb/benches/{my_bench.rs => lib_gb_bench.rs} (100%) diff --git a/Cargo.lock b/Cargo.lock index ee7c6a38..5351c143 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -416,6 +416,7 @@ dependencies = [ name = "gb" version = "1.0.0" dependencies = [ + "cfg-if 1.0.0", "chrono", "crossbeam-channel", "fern", @@ -1505,9 +1506,9 @@ checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" [[package]] name = "wav" -version = "0.6.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "549df97e073e1f901a489f159eb451bbe9c790e2f217394f4ce01acda0380b0c" +checksum = "a65e199c799848b4f997072aa4d673c034f80f40191f97fe2f0a23f410be1609" dependencies = [ "riff", ] diff --git a/gb/Cargo.toml b/gb/Cargo.toml index d05dff1d..05b7cb6d 100644 --- a/gb/Cargo.toml +++ b/gb/Cargo.toml @@ -15,8 +15,9 @@ log = "0.4" fern = "0.6.0" chrono = "0.4" sdl2 = {version = "0.34", features = ["bundled","static-link"]} -wav = "0.6.0" +wav = "1.0" crossbeam-channel = "0.5" +cfg-if = "1.0" [features] sdl-resample = [] diff --git a/gb/src/audio/audio_resampler.rs b/gb/src/audio/magen_audio_resampler.rs similarity index 99% rename from gb/src/audio/audio_resampler.rs rename to gb/src/audio/magen_audio_resampler.rs index 6a59d0a0..69f868c1 100644 --- a/gb/src/audio/audio_resampler.rs +++ b/gb/src/audio/magen_audio_resampler.rs @@ -13,7 +13,6 @@ pub struct MagenAudioResampler{ impl MagenAudioResampler{ fn interpolate_sample(samples:&[StereoSample])->StereoSample{ - let interpulated_left_sample = samples.iter().fold(DEFAULT_SAPMPLE, |acc, x| acc + x.left_sample) / samples.len() as Sample; let interpulated_right_sample = samples.iter().fold(DEFAULT_SAPMPLE, |acc, x| acc + x.right_sample) / samples.len() as Sample; @@ -76,6 +75,5 @@ impl AudioResampler for MagenAudioResampler{ } return output; - } } \ No newline at end of file diff --git a/gb/src/audio/mod.rs b/gb/src/audio/mod.rs index 31dd91e0..4889895c 100644 --- a/gb/src/audio/mod.rs +++ b/gb/src/audio/mod.rs @@ -1,25 +1,32 @@ pub mod multi_device_audio; pub mod wav_file_audio_device; -#[cfg(not(feature = "push-audio"))] -pub mod sdl_pull_audio_device; -#[cfg(feature = "push-audio")] -pub mod sdl_push_audio_device; + +cfg_if::cfg_if!{ + if #[cfg(feature = "push-audio")]{ + pub mod sdl_push_audio_device; + pub type ChosenAudioDevice = sdl_push_audio_device::SdlPushAudioDevice; + } + else{ + pub mod sdl_pull_audio_device; + pub type ChosenAudioDevice = sdl_pull_audio_device::SdlPullAudioDevice; + } +} +cfg_if::cfg_if!{ + if #[cfg(feature = "sdl-resampler")]{ + pub mod sdl_audio_resampler; + pub type ChosenResampler = sdl_audio_resampler::SdlAudioResampler; + } + else{ + pub mod magen_audio_resampler; + pub type ChosenResampler = magen_audio_resampler::MagenAudioResampler; + } +} use std::ffi::CStr; use lib_gb::apu::audio_device::{AudioDevice, BUFFER_SIZE, Sample, StereoSample}; use sdl2::{libc::c_char, sys::SDL_GetError}; -#[cfg(feature = "sdl-resampler")] -pub type ChosenResampler = sdl_audio_resampler::SdlAudioResampler; -#[cfg(feature = "sdl-resampler")] -pub mod sdl_audio_resampler; - -#[cfg(not(feature = "sdl-resampler"))] -pub mod audio_resampler; -#[cfg(not(feature = "sdl-resampler"))] -pub type ChosenResampler = audio_resampler::MagenAudioResampler; - fn get_sdl_error_message()->&'static str{ unsafe{ let error_message:*const c_char = SDL_GetError(); @@ -33,7 +40,7 @@ pub trait AudioResampler{ fn resample(&mut self, buffer:&[StereoSample; BUFFER_SIZE])->Vec; } -trait SdlAudioDevice : AudioDevice{ +pub trait ResampledAudioDevice : AudioDevice{ const VOLUME:Sample = 10 as Sample; fn push_buffer(&mut self, buffer:&[StereoSample; BUFFER_SIZE]){ @@ -53,4 +60,5 @@ trait SdlAudioDevice : AudioDevice{ fn get_audio_buffer(&mut self)->(&mut [Sample;BUFFER_SIZE], &mut usize); fn get_resampler(&mut self)->&mut AR; fn full_buffer_callback(&self)->Result<(), String>; + fn new(frequency:i32, turbo_mul:u8)->Self; } diff --git a/gb/src/audio/sdl_audio_resampler.rs b/gb/src/audio/sdl_audio_resampler.rs index 09bb02da..19691c8a 100644 --- a/gb/src/audio/sdl_audio_resampler.rs +++ b/gb/src/audio/sdl_audio_resampler.rs @@ -35,8 +35,7 @@ impl AudioResampler for SdlAudioResampler{ std::ptr::copy_nonoverlapping(buffer.as_ptr(), buf.as_mut_ptr() as *mut StereoSample, BUFFER_SIZE); cvt.buf = buf.as_mut_ptr(); - let status_code = SDL_ConvertAudio(&mut cvt); - if status_code != 0{ + if SDL_ConvertAudio(&mut cvt) != 0{ std::panic!("error while converting audio, status code: {}", status_code); } diff --git a/gb/src/audio/sdl_pull_audio_device.rs b/gb/src/audio/sdl_pull_audio_device.rs index c45fd7b5..527020a0 100644 --- a/gb/src/audio/sdl_pull_audio_device.rs +++ b/gb/src/audio/sdl_pull_audio_device.rs @@ -1,14 +1,12 @@ use std::{ffi::c_void, mem::MaybeUninit}; use lib_gb::{GB_FREQUENCY, apu::audio_device::*}; -use sdl2::sys::*; -use crate::audio::get_sdl_error_message; - -use super::{AudioResampler, SdlAudioDevice}; +use super::{AudioResampler, ResampledAudioDevice, get_sdl_error_message}; +use sdl2::sys::*; use crossbeam_channel::{Receiver, SendError, Sender, bounded}; -struct Data{ +struct UserData{ rx: Receiver<[Sample;BUFFER_SIZE]>, current_buf: Option<[Sample;BUFFER_SIZE]>, current_buf_index:usize, @@ -21,15 +19,21 @@ pub struct SdlPullAudioDevice{ tarnsmiter: Sender<[Sample;BUFFER_SIZE]>, - userdata: Data + userdata: UserData } impl SdlPullAudioDevice{ - pub fn new(frequency:i32, turbo_mul:u8)->Self{ + fn push_audio_to_device(&self, audio:&[Sample; BUFFER_SIZE])->Result<(), SendError<[Sample; BUFFER_SIZE]>>{ + self.tarnsmiter.send(audio.clone()) + } +} + +impl ResampledAudioDevice for SdlPullAudioDevice{ + fn new(frequency:i32, turbo_mul:u8)->Self{ // cap of less than 2 hurts the fps let(s,r) = bounded(2); - let data = Data{ + let data = UserData{ current_buf:Option::None, current_buf_index:0, rx:r @@ -52,7 +56,7 @@ impl SdlPullAudioDevice{ padding: 0, size: 0, callback: Option::Some(audio_callback), - userdata: (&mut device.userdata) as *mut Data as *mut c_void + userdata: (&mut device.userdata) as *mut UserData as *mut c_void }; @@ -79,13 +83,6 @@ impl SdlPullAudioDevice{ return device; } - - fn push_audio_to_device(&self, audio:&[Sample; BUFFER_SIZE])->Result<(), SendError<[Sample; BUFFER_SIZE]>>{ - self.tarnsmiter.send(audio.clone()) - } -} - -impl SdlAudioDevice for SdlPullAudioDevice{ fn get_audio_buffer(&mut self) ->(&mut [Sample;BUFFER_SIZE], &mut usize) { (&mut self.buffer, &mut self.buffer_index) } @@ -99,35 +96,35 @@ impl SdlAudioDevice for SdlPullAudioDevice{ impl AudioDevice for SdlPullAudioDevice{ fn push_buffer(&mut self, buffer:&[StereoSample; BUFFER_SIZE]) { - SdlAudioDevice::push_buffer(self, buffer); + ResampledAudioDevice::push_buffer(self, buffer); } } unsafe extern "C" fn audio_callback(userdata:*mut c_void, buffer:*mut u8, length:i32){ let length = length as usize; - let s = &mut *(userdata as *mut Data); + let safe_userdata = &mut *(userdata as *mut UserData); - if s.current_buf.is_none(){ - s.current_buf = Some(s.rx.recv().unwrap()); + if safe_userdata.current_buf.is_none(){ + safe_userdata.current_buf = Some(safe_userdata.rx.recv().unwrap()); } - let samples = s.current_buf.unwrap(); - let samples_size = (samples.len() * std::mem::size_of::()) - s.current_buf_index; - let samples_ptr = (samples.as_ptr() as *mut u8).add(s.current_buf_index); + let samples = safe_userdata.current_buf.unwrap(); + let samples_size = (samples.len() * std::mem::size_of::()) - safe_userdata.current_buf_index; + let samples_ptr = (samples.as_ptr() as *mut u8).add(safe_userdata.current_buf_index); std::ptr::copy_nonoverlapping(samples_ptr, buffer, std::cmp::min(length, samples_size)); - if length > samples_size && s.rx.is_empty(){ - s.current_buf = Option::None; - s.current_buf_index = 0; + if length > samples_size && safe_userdata.rx.is_empty(){ + safe_userdata.current_buf = Option::None; + safe_userdata.current_buf_index = 0; std::ptr::write_bytes(buffer.add(samples.len() as usize), 0, length - samples_size); } else if length > samples_size{ - s.current_buf = Option::None; - s.current_buf_index = 0; + safe_userdata.current_buf = Option::None; + safe_userdata.current_buf_index = 0; audio_callback(userdata, buffer.add(samples_size), (length - samples_size) as i32); } else{ - s.current_buf_index = length; + safe_userdata.current_buf_index = length; } } diff --git a/gb/src/audio/sdl_push_audio_device.rs b/gb/src/audio/sdl_push_audio_device.rs index 964bf253..33f1c5a1 100644 --- a/gb/src/audio/sdl_push_audio_device.rs +++ b/gb/src/audio/sdl_push_audio_device.rs @@ -1,9 +1,7 @@ use std::{ffi::c_void, mem::MaybeUninit, str::FromStr}; use lib_gb::{GB_FREQUENCY, apu::audio_device::{AudioDevice, BUFFER_SIZE, DEFAULT_SAPMPLE, Sample, StereoSample}}; use sdl2::sys::*; -use super::{SdlAudioDevice, get_sdl_error_message}; -use super::AudioResampler; - +use super::{AudioResampler, ResampledAudioDevice, get_sdl_error_message}; //After twicking those numbers Iv reached this, this will affect fps which will affect sound tearing const BYTES_TO_WAIT:u32 = BUFFER_SIZE as u32 * 16; @@ -17,7 +15,27 @@ pub struct SdlPushAudioDevice{ } impl SdlPushAudioDevice{ - pub fn new(frequency:i32, turbo_mul:u8)->Self{ + fn push_audio_to_device(&self, audio:&[Sample; BUFFER_SIZE])->Result<(),&str>{ + let audio_ptr: *const c_void = audio.as_ptr() as *const c_void; + let data_byte_len = (audio.len() * std::mem::size_of::()) as u32; + + unsafe{ + while SDL_GetQueuedAudioSize(self.device_id) > BYTES_TO_WAIT{ + SDL_Delay(1); + } + + SDL_ClearError(); + if SDL_QueueAudio(self.device_id, audio_ptr, data_byte_len) != 0{ + return Err(get_sdl_error_message()); + } + + Ok(()) + } + } +} + +impl ResampledAudioDevice for SdlPushAudioDevice{ + fn new(frequency:i32, turbo_mul:u8)->Self{ let desired_audio_spec = SDL_AudioSpec{ freq: frequency, format: AUDIO_S16SYS as u16, @@ -61,26 +79,6 @@ impl SdlPushAudioDevice{ }; } - fn push_audio_to_device(&self, audio:&[Sample; BUFFER_SIZE])->Result<(),&str>{ - let audio_ptr: *const c_void = audio.as_ptr() as *const c_void; - let data_byte_len = (audio.len() * std::mem::size_of::()) as u32; - - unsafe{ - while SDL_GetQueuedAudioSize(self.device_id) > BYTES_TO_WAIT{ - SDL_Delay(1); - } - - SDL_ClearError(); - if SDL_QueueAudio(self.device_id, audio_ptr, data_byte_len) != 0{ - return Err(get_sdl_error_message()); - } - - Ok(()) - } - } -} - -impl SdlAudioDevice for SdlPushAudioDevice{ fn get_audio_buffer(&mut self) ->(&mut [Sample;BUFFER_SIZE], &mut usize) { (&mut self.buffer, &mut self.buffer_index) } @@ -96,6 +94,6 @@ impl SdlAudioDevice for SdlPushAudioDevice{ impl AudioDevice for SdlPushAudioDevice{ fn push_buffer(&mut self, buffer:&[StereoSample; BUFFER_SIZE]) { - SdlAudioDevice::push_buffer(self, buffer); + ResampledAudioDevice::push_buffer(self, buffer); } } \ No newline at end of file diff --git a/gb/src/main.rs b/gb/src/main.rs index 29cdd99c..8d2b6a7f 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -1,9 +1,10 @@ mod mbc_handler; mod sdl_joypad_provider; mod sdl_gfx_device; +mod mpmc_gfx_device; mod audio; -use crate::{audio::{ChosenResampler, multi_device_audio::*}, mbc_handler::*, sdl_joypad_provider::*}; +use crate::{audio::{ChosenResampler, multi_device_audio::*, ResampledAudioDevice}, mbc_handler::*, mpmc_gfx_device::MpmcGfxDevice, sdl_joypad_provider::*}; use lib_gb::{GB_FREQUENCY, apu::audio_device::*, keypad::button::Button, machine::gameboy::GameBoy, mmu::gb_mmu::BOOT_ROM_SIZE, ppu::{gb_ppu::{BUFFERS_NUMBER, SCREEN_HEIGHT, SCREEN_WIDTH}, gfx_device::GfxDevice}}; use std::{fs, env, result::Result, vec::Vec}; use log::info; @@ -54,18 +55,6 @@ fn check_for_terminal_feature_flag(args:&Vec::, flag:&str)->bool{ args.len() >= 3 && args.contains(&String::from(flag)) } -struct MpmcGfxDevice{ - sender: crossbeam_channel::Sender -} - -impl GfxDevice for MpmcGfxDevice{ - fn swap_buffer(&mut self, buffer:&[u32; SCREEN_HEIGHT * SCREEN_WIDTH]) { - if self.sender.send(buffer.as_ptr() as usize).is_err(){ - log::debug!("The receiver endpoint has been closed"); - } - } -} - fn main() { let args: Vec = env::args().collect(); @@ -80,7 +69,7 @@ fn main() { "MagenBoy", SCREEN_SCALE, TURBO_MUL, check_for_terminal_feature_flag(&args, "--no-vsync")); let (s,r) = crossbeam_channel::bounded(BUFFERS_NUMBER - 1); - let mpmc_device = MpmcGfxDevice{sender:s}; + let mpmc_device = MpmcGfxDevice::new(s); let program_name = args[1].clone(); @@ -117,11 +106,7 @@ fn main() { // Receiving usize and not raw ptr cause in rust you cant pass a raw ptr to another thread fn emulation_thread_main(args: Vec, program_name: String, spsc_gfx_device: MpmcGfxDevice, running_ptr: usize) { - - #[cfg(feature = "push-audio")] - let audio_device = audio::sdl_push_audio_device::SdlPushAudioDevice::::new(44100, TURBO_MUL); - #[cfg(not(feature = "push-audio"))] - let audio_device = audio::sdl_pull_audio_device::SdlPullAudioDevice::::new(44100, TURBO_MUL); + let audio_device = audio::ChosenAudioDevice::::new(44100, TURBO_MUL); let mut devices: Vec::> = Vec::new(); devices.push(Box::new(audio_device)); diff --git a/gb/src/mpmc_gfx_device.rs b/gb/src/mpmc_gfx_device.rs new file mode 100644 index 00000000..a42aca95 --- /dev/null +++ b/gb/src/mpmc_gfx_device.rs @@ -0,0 +1,19 @@ +use lib_gb::ppu::{gb_ppu::{SCREEN_HEIGHT, SCREEN_WIDTH}, gfx_device::GfxDevice}; + +pub struct MpmcGfxDevice{ + sender: crossbeam_channel::Sender +} + +impl MpmcGfxDevice{ + pub fn new(sender:crossbeam_channel::Sender)->Self{ + Self{sender} + } +} + +impl GfxDevice for MpmcGfxDevice{ + fn swap_buffer(&mut self, buffer:&[u32; SCREEN_HEIGHT * SCREEN_WIDTH]) { + if self.sender.send(buffer.as_ptr() as usize).is_err(){ + log::debug!("The receiver endpoint has been closed"); + } + } +} \ No newline at end of file diff --git a/lib_gb/Cargo.toml b/lib_gb/Cargo.toml index 9ba19354..29631e6f 100644 --- a/lib_gb/Cargo.toml +++ b/lib_gb/Cargo.toml @@ -13,5 +13,5 @@ reqwest = { version = "0.11", features = ["blocking"] } zip = "0.5" [[bench]] -name = "my_bench" +name = "lib_gb_bench" harness = false \ No newline at end of file diff --git a/lib_gb/benches/my_bench.rs b/lib_gb/benches/lib_gb_bench.rs similarity index 100% rename from lib_gb/benches/my_bench.rs rename to lib_gb/benches/lib_gb_bench.rs diff --git a/lib_gb/src/apu/sound_terminal.rs b/lib_gb/src/apu/sound_terminal.rs index d0f09f3c..0d244859 100644 --- a/lib_gb/src/apu/sound_terminal.rs +++ b/lib_gb/src/apu/sound_terminal.rs @@ -39,6 +39,7 @@ impl SoundTerminal{ mixed_sample >>= 2; // Divide by 4 in order to normal the sample + // Adding +1 cause thats how to GB calculates the sound (0 still has volume) return mixed_sample * ((self.volume + 1) as Sample); } } \ No newline at end of file diff --git a/lib_gb/src/utils/mod.rs b/lib_gb/src/utils/mod.rs index 7c0f5cd0..722f0216 100644 --- a/lib_gb/src/utils/mod.rs +++ b/lib_gb/src/utils/mod.rs @@ -5,6 +5,7 @@ pub mod memory_registers; pub mod bit_masks; pub mod fixed_size_queue; +// Frequency in m_cycles (m_cycle = 4 t_cycles) pub const GB_FREQUENCY:u32 = 4_194_304 / 4; pub fn create_default_array()->[T;SIZE]{ From 7281d3dccd60852fd7187e77ffde8715c34739cb Mon Sep 17 00:00:00 2001 From: alloncm Date: Thu, 4 Nov 2021 21:10:58 +0200 Subject: [PATCH 118/136] Move the cvt struct to the sdl reasmpler Also fIxed a typo in the sdl-resample feature --- gb/src/audio/mod.rs | 2 +- gb/src/audio/sdl_audio_resampler.rs | 46 ++++++++++++++--------------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/gb/src/audio/mod.rs b/gb/src/audio/mod.rs index 4889895c..bf4a157c 100644 --- a/gb/src/audio/mod.rs +++ b/gb/src/audio/mod.rs @@ -13,7 +13,7 @@ cfg_if::cfg_if!{ } } cfg_if::cfg_if!{ - if #[cfg(feature = "sdl-resampler")]{ + if #[cfg(feature = "sdl-resample")]{ pub mod sdl_audio_resampler; pub type ChosenResampler = sdl_audio_resampler::SdlAudioResampler; } diff --git a/gb/src/audio/sdl_audio_resampler.rs b/gb/src/audio/sdl_audio_resampler.rs index 19691c8a..00e1c339 100644 --- a/gb/src/audio/sdl_audio_resampler.rs +++ b/gb/src/audio/sdl_audio_resampler.rs @@ -4,43 +4,43 @@ use sdl2::sys::*; use super::AudioResampler; pub struct SdlAudioResampler{ - original_frequency:u32, - target_frequency:u32, + cvt: SDL_AudioCVT } impl AudioResampler for SdlAudioResampler{ fn new(original_frequency:u32, target_frequency:u32)->Self{ - Self{ - original_frequency, - target_frequency, + let mut cvt = unsafe{ + let mut cvt:MaybeUninit = MaybeUninit::uninit(); + SDL_BuildAudioCVT(cvt.as_mut_ptr(), AUDIO_S16 as u16, 2, original_frequency as i32, + AUDIO_S16 as u16, 2, target_frequency as i32); + cvt.assume_init() + }; + + if cvt.needed != 1{ + std::panic!("Cannot resample between freqs"); } + + cvt.len = (BUFFER_SIZE * std::mem::size_of::()) as i32; + + log::error!("help"); + + Self{cvt} } fn resample(&mut self, buffer:&[StereoSample; BUFFER_SIZE])->Vec{ + let mut buf:Vec:: = vec![0;(self.cvt.len * self.cvt.len_mult) as usize]; + unsafe{ - let mut cvt = { - let mut cvt:MaybeUninit = MaybeUninit::uninit(); - SDL_BuildAudioCVT(cvt.as_mut_ptr(), AUDIO_S16 as u16, 2, self.original_frequency as i32, - AUDIO_S16 as u16, 2, self.target_frequency as i32); - cvt.assume_init() - }; - - if cvt.needed != 1{ - std::panic!("Cannot resample between freqs"); - } - - cvt.len = (BUFFER_SIZE * std::mem::size_of::()) as i32; - let mut buf:Vec:: = vec![0;(cvt.len * cvt.len_mult) as usize]; - std::ptr::copy_nonoverlapping(buffer.as_ptr(), buf.as_mut_ptr() as *mut StereoSample, BUFFER_SIZE); - cvt.buf = buf.as_mut_ptr(); - if SDL_ConvertAudio(&mut cvt) != 0{ + self.cvt.buf = buf.as_mut_ptr(); + let status_code = SDL_ConvertAudio(&mut self.cvt) != 0; + if status_code{ std::panic!("error while converting audio, status code: {}", status_code); } - let buf_ptr = cvt.buf as *mut StereoSample; - let length = cvt.len_cvt as usize / std::mem::size_of::(); + let buf_ptr = self.cvt.buf as *mut StereoSample; + let length = self.cvt.len_cvt as usize / std::mem::size_of::(); let mut output = vec![StereoSample::const_defualt();length]; std::ptr::copy_nonoverlapping(buf_ptr, output.as_mut_ptr(), length); From 001ad18b44f0a45fe6e0bd306589ee7d936244ee Mon Sep 17 00:00:00 2001 From: alloncm Date: Thu, 4 Nov 2021 21:19:23 +0200 Subject: [PATCH 119/136] Extract sdl device initizalizing to a util --- gb/src/audio/mod.rs | 30 +++++++++++++++++++++++---- gb/src/audio/sdl_pull_audio_device.rs | 27 ++++-------------------- gb/src/audio/sdl_push_audio_device.rs | 26 +++-------------------- 3 files changed, 33 insertions(+), 50 deletions(-) diff --git a/gb/src/audio/mod.rs b/gb/src/audio/mod.rs index bf4a157c..fe91c04a 100644 --- a/gb/src/audio/mod.rs +++ b/gb/src/audio/mod.rs @@ -1,7 +1,5 @@ pub mod multi_device_audio; pub mod wav_file_audio_device; - - cfg_if::cfg_if!{ if #[cfg(feature = "push-audio")]{ pub mod sdl_push_audio_device; @@ -23,9 +21,9 @@ cfg_if::cfg_if!{ } } -use std::ffi::CStr; +use std::{ffi::CStr, mem::MaybeUninit}; use lib_gb::apu::audio_device::{AudioDevice, BUFFER_SIZE, Sample, StereoSample}; -use sdl2::{libc::c_char, sys::SDL_GetError}; +use sdl2::{libc::c_char, sys::*}; fn get_sdl_error_message()->&'static str{ unsafe{ @@ -35,6 +33,30 @@ fn get_sdl_error_message()->&'static str{ } } +fn init_sdl_audio_device(audio_spec:&SDL_AudioSpec)->SDL_AudioDeviceID{ + let mut uninit_audio_spec:MaybeUninit = MaybeUninit::uninit(); + + unsafe{ + SDL_Init(SDL_INIT_AUDIO); + SDL_ClearError(); + let id = SDL_OpenAudioDevice(std::ptr::null(), 0, audio_spec, uninit_audio_spec.as_mut_ptr() , 0); + + if id == 0{ + std::panic!("{}", get_sdl_error_message()); + } + + let init_audio_spec:SDL_AudioSpec = uninit_audio_spec.assume_init(); + + if init_audio_spec.freq != audio_spec.freq { + std::panic!("Error initializing audio could not use the frequency: {}", audio_spec.freq); + } + + //This will start the audio processing + SDL_PauseAudioDevice(id, 0); + return id; + } +} + pub trait AudioResampler{ fn new(original_frequency:u32, target_frequency:u32)->Self; fn resample(&mut self, buffer:&[StereoSample; BUFFER_SIZE])->Vec; diff --git a/gb/src/audio/sdl_pull_audio_device.rs b/gb/src/audio/sdl_pull_audio_device.rs index 527020a0..3406e573 100644 --- a/gb/src/audio/sdl_pull_audio_device.rs +++ b/gb/src/audio/sdl_pull_audio_device.rs @@ -1,6 +1,6 @@ -use std::{ffi::c_void, mem::MaybeUninit}; +use std::ffi::c_void; use lib_gb::{GB_FREQUENCY, apu::audio_device::*}; -use super::{AudioResampler, ResampledAudioDevice, get_sdl_error_message}; +use super::{AudioResampler, ResampledAudioDevice, init_sdl_audio_device}; use sdl2::sys::*; use crossbeam_channel::{Receiver, SendError, Sender, bounded}; @@ -59,27 +59,8 @@ impl ResampledAudioDevice for SdlPullAudioDevice{ userdata: (&mut device.userdata) as *mut UserData as *mut c_void }; - - let mut uninit_audio_spec:MaybeUninit = MaybeUninit::uninit(); - - unsafe{ - SDL_Init(SDL_INIT_AUDIO); - SDL_ClearError(); - let id = SDL_OpenAudioDevice(std::ptr::null(), 0, &desired_audio_spec, uninit_audio_spec.as_mut_ptr() , 0); - - if id == 0{ - std::panic!("{}", get_sdl_error_message()); - } - - let init_audio_spec:SDL_AudioSpec = uninit_audio_spec.assume_init(); - - if init_audio_spec.freq != frequency { - std::panic!("Error initializing audio could not use the frequency: {}", frequency); - } - - //This will start the audio processing - SDL_PauseAudioDevice(id, 0); - }; + // Ignore device id + init_sdl_audio_device(&desired_audio_spec); return device; } diff --git a/gb/src/audio/sdl_push_audio_device.rs b/gb/src/audio/sdl_push_audio_device.rs index 33f1c5a1..d4c6ac58 100644 --- a/gb/src/audio/sdl_push_audio_device.rs +++ b/gb/src/audio/sdl_push_audio_device.rs @@ -1,7 +1,7 @@ -use std::{ffi::c_void, mem::MaybeUninit, str::FromStr}; +use std::{ffi::c_void, str::FromStr}; use lib_gb::{GB_FREQUENCY, apu::audio_device::{AudioDevice, BUFFER_SIZE, DEFAULT_SAPMPLE, Sample, StereoSample}}; use sdl2::sys::*; -use super::{AudioResampler, ResampledAudioDevice, get_sdl_error_message}; +use super::{AudioResampler, ResampledAudioDevice, get_sdl_error_message, init_sdl_audio_device}; //After twicking those numbers Iv reached this, this will affect fps which will affect sound tearing const BYTES_TO_WAIT:u32 = BUFFER_SIZE as u32 * 16; @@ -49,28 +49,8 @@ impl ResampledAudioDevice for SdlPushAudioDevice{ }; - let mut uninit_audio_spec:MaybeUninit = MaybeUninit::uninit(); + let device_id = init_sdl_audio_device(&desired_audio_spec); - let device_id = unsafe{ - SDL_Init(SDL_INIT_AUDIO); - SDL_ClearError(); - let id = SDL_OpenAudioDevice(std::ptr::null(), 0, &desired_audio_spec, uninit_audio_spec.as_mut_ptr() , 0); - - if id == 0{ - std::panic!("{}", get_sdl_error_message()); - } - - let init_audio_spec:SDL_AudioSpec = uninit_audio_spec.assume_init(); - - if init_audio_spec.freq != frequency { - std::panic!("Error initializing audio could not use the frequency: {}", frequency); - } - - //This will start the audio processing - SDL_PauseAudioDevice(id, 0); - - id - }; return SdlPushAudioDevice{ device_id: device_id, buffer:[DEFAULT_SAPMPLE;BUFFER_SIZE], From 985cd8557cf0e6a263e2523eed6721ec54a909f1 Mon Sep 17 00:00:00 2001 From: alloncm Date: Thu, 4 Nov 2021 21:36:44 +0200 Subject: [PATCH 120/136] Replace the clone in the audio pull --- gb/src/audio/mod.rs | 2 +- gb/src/audio/sdl_pull_audio_device.rs | 39 ++++++++++++++------------- gb/src/audio/sdl_push_audio_device.rs | 2 +- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/gb/src/audio/mod.rs b/gb/src/audio/mod.rs index fe91c04a..bb17c34e 100644 --- a/gb/src/audio/mod.rs +++ b/gb/src/audio/mod.rs @@ -81,6 +81,6 @@ pub trait ResampledAudioDevice : AudioDevice{ fn get_audio_buffer(&mut self)->(&mut [Sample;BUFFER_SIZE], &mut usize); fn get_resampler(&mut self)->&mut AR; - fn full_buffer_callback(&self)->Result<(), String>; + fn full_buffer_callback(&mut self)->Result<(), String>; fn new(frequency:i32, turbo_mul:u8)->Self; } diff --git a/gb/src/audio/sdl_pull_audio_device.rs b/gb/src/audio/sdl_pull_audio_device.rs index 3406e573..ae9ec239 100644 --- a/gb/src/audio/sdl_pull_audio_device.rs +++ b/gb/src/audio/sdl_pull_audio_device.rs @@ -3,36 +3,32 @@ use lib_gb::{GB_FREQUENCY, apu::audio_device::*}; use super::{AudioResampler, ResampledAudioDevice, init_sdl_audio_device}; use sdl2::sys::*; -use crossbeam_channel::{Receiver, SendError, Sender, bounded}; +use crossbeam_channel::{Receiver, Sender, bounded}; +const BUFFERS_NUMBER:usize = 3; struct UserData{ - rx: Receiver<[Sample;BUFFER_SIZE]>, - current_buf: Option<[Sample;BUFFER_SIZE]>, + rx: Receiver, + current_buf: Option, current_buf_index:usize, } pub struct SdlPullAudioDevice{ resampler: AR, - buffer: [Sample;BUFFER_SIZE], + buffers: [[Sample;BUFFER_SIZE];BUFFERS_NUMBER], + buffer_number_index:usize, buffer_index:usize, - tarnsmiter: Sender<[Sample;BUFFER_SIZE]>, + tarnsmiter: Sender, userdata: UserData } -impl SdlPullAudioDevice{ - fn push_audio_to_device(&self, audio:&[Sample; BUFFER_SIZE])->Result<(), SendError<[Sample; BUFFER_SIZE]>>{ - self.tarnsmiter.send(audio.clone()) - } -} - impl ResampledAudioDevice for SdlPullAudioDevice{ fn new(frequency:i32, turbo_mul:u8)->Self{ // cap of less than 2 hurts the fps - let(s,r) = bounded(2); + let(s,r) = bounded(BUFFERS_NUMBER - 1); let data = UserData{ current_buf:Option::None, current_buf_index:0, @@ -40,8 +36,9 @@ impl ResampledAudioDevice for SdlPullAudioDevice{ }; let mut device = SdlPullAudioDevice{ - buffer:[DEFAULT_SAPMPLE;BUFFER_SIZE], + buffers:[[DEFAULT_SAPMPLE;BUFFER_SIZE];BUFFERS_NUMBER], buffer_index:0, + buffer_number_index:0, resampler: AudioResampler::new(GB_FREQUENCY * turbo_mul as u32, frequency as u32), tarnsmiter:s, userdata:data @@ -64,15 +61,21 @@ impl ResampledAudioDevice for SdlPullAudioDevice{ return device; } + + fn full_buffer_callback(&mut self) ->Result<(), String> { + let result = self.tarnsmiter.send(self.buffers[self.buffer_number_index].as_ptr() as usize).map_err(|e|e.to_string()); + self.buffer_number_index = (self.buffer_number_index + 1) % BUFFERS_NUMBER; + + return result; + } + fn get_audio_buffer(&mut self) ->(&mut [Sample;BUFFER_SIZE], &mut usize) { - (&mut self.buffer, &mut self.buffer_index) + (&mut self.buffers[self.buffer_number_index], &mut self.buffer_index) } + fn get_resampler(&mut self) ->&mut AR { &mut self.resampler } - fn full_buffer_callback(&self) ->Result<(), String> { - self.push_audio_to_device(&self.buffer).map_err(|e|e.to_string()) - } } impl AudioDevice for SdlPullAudioDevice{ @@ -89,7 +92,7 @@ unsafe extern "C" fn audio_callback(userdata:*mut c_void, buffer:*mut u8, length safe_userdata.current_buf = Some(safe_userdata.rx.recv().unwrap()); } - let samples = safe_userdata.current_buf.unwrap(); + let samples = &*((safe_userdata.current_buf.unwrap()) as *const [Sample;BUFFER_SIZE]); let samples_size = (samples.len() * std::mem::size_of::()) - safe_userdata.current_buf_index; let samples_ptr = (samples.as_ptr() as *mut u8).add(safe_userdata.current_buf_index); std::ptr::copy_nonoverlapping(samples_ptr, buffer, std::cmp::min(length, samples_size)); diff --git a/gb/src/audio/sdl_push_audio_device.rs b/gb/src/audio/sdl_push_audio_device.rs index d4c6ac58..dbe1ec08 100644 --- a/gb/src/audio/sdl_push_audio_device.rs +++ b/gb/src/audio/sdl_push_audio_device.rs @@ -67,7 +67,7 @@ impl ResampledAudioDevice for SdlPushAudioDevice{ &mut self.resampler } - fn full_buffer_callback(&self)->Result<(), String> { + fn full_buffer_callback(&mut self)->Result<(), String> { self.push_audio_to_device(&self.buffer).map_err(|e|String::from_str(e).unwrap()) } } From 521d1ea7d74a205093007e972edf3cf719aa9c04 Mon Sep 17 00:00:00 2001 From: alloncm Date: Thu, 4 Nov 2021 21:47:03 +0200 Subject: [PATCH 121/136] I think it supposed to close it gracefuly --- gb/src/audio/sdl_pull_audio_device.rs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/gb/src/audio/sdl_pull_audio_device.rs b/gb/src/audio/sdl_pull_audio_device.rs index ae9ec239..efacca24 100644 --- a/gb/src/audio/sdl_pull_audio_device.rs +++ b/gb/src/audio/sdl_pull_audio_device.rs @@ -20,8 +20,8 @@ pub struct SdlPullAudioDevice{ buffer_index:usize, tarnsmiter: Sender, - - userdata: UserData + userdata: UserData, + device_id:SDL_AudioDeviceID, } impl ResampledAudioDevice for SdlPullAudioDevice{ @@ -41,7 +41,8 @@ impl ResampledAudioDevice for SdlPullAudioDevice{ buffer_number_index:0, resampler: AudioResampler::new(GB_FREQUENCY * turbo_mul as u32, frequency as u32), tarnsmiter:s, - userdata:data + userdata:data, + device_id:0 }; let desired_audio_spec = SDL_AudioSpec{ @@ -57,7 +58,7 @@ impl ResampledAudioDevice for SdlPullAudioDevice{ }; // Ignore device id - init_sdl_audio_device(&desired_audio_spec); + device.device_id = init_sdl_audio_device(&desired_audio_spec); return device; } @@ -84,6 +85,14 @@ impl AudioDevice for SdlPullAudioDevice{ } } +impl Drop for SdlPullAudioDevice{ + fn drop(&mut self) { + unsafe{ + SDL_CloseAudioDevice(self.device_id); + } + } +} + unsafe extern "C" fn audio_callback(userdata:*mut c_void, buffer:*mut u8, length:i32){ let length = length as usize; let safe_userdata = &mut *(userdata as *mut UserData); From 133caa1f30244d45564fde03927d9bc3c235b8d7 Mon Sep 17 00:00:00 2001 From: alloncm Date: Thu, 4 Nov 2021 21:49:57 +0200 Subject: [PATCH 122/136] Inline the apu timer According to the benchmark this has improved performance a bit :) --- lib_gb/src/apu/timer.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/lib_gb/src/apu/timer.rs b/lib_gb/src/apu/timer.rs index ea7d3b67..ba05996e 100644 --- a/lib_gb/src/apu/timer.rs +++ b/lib_gb/src/apu/timer.rs @@ -13,6 +13,7 @@ impl Timer{ } // This function is a hot spot for the APU, almost every component uses the timer + #[inline] pub fn cycle(&mut self)->bool{ if self.cycles_to_tick != 0{ // The calculation used to be this: From 1423bce3b33881919e0cad48c2e920f02837b875 Mon Sep 17 00:00:00 2001 From: alloncm Date: Thu, 4 Nov 2021 22:02:22 +0200 Subject: [PATCH 123/136] Remove the enable flag as it was a mistake This was due to missunderstanding of the docs. Those bits ware meant for the vin cartridge channel which no game ever used, So there is no reason to emulate it. --- lib_gb/src/apu/apu_registers_updater.rs | 3 --- lib_gb/src/apu/sound_terminal.rs | 2 -- 2 files changed, 5 deletions(-) diff --git a/lib_gb/src/apu/apu_registers_updater.rs b/lib_gb/src/apu/apu_registers_updater.rs index 1112863e..a864b130 100644 --- a/lib_gb/src/apu/apu_registers_updater.rs +++ b/lib_gb/src/apu/apu_registers_updater.rs @@ -44,9 +44,6 @@ pub fn set_nr44(channel:&mut Channel, fs:&FrameSequencer, n } pub fn set_nr50(apu:&mut GbApu, nr50:u8){ - apu.right_terminal.enabled = nr50 & BIT_3_MASK != 0; - apu.left_terminal.enabled = nr50 & BIT_7_MASK != 0; - apu.right_terminal.volume = nr50 & 0b111; apu.left_terminal.volume = (nr50 & 0b111_0000) >> 4; } diff --git a/lib_gb/src/apu/sound_terminal.rs b/lib_gb/src/apu/sound_terminal.rs index 0d244859..1da007bf 100644 --- a/lib_gb/src/apu/sound_terminal.rs +++ b/lib_gb/src/apu/sound_terminal.rs @@ -6,7 +6,6 @@ const ENABLE_MASK:ChannelMask = 0xFFFF; const DISABLE_MASK:ChannelMask = 0x0; pub struct SoundTerminal{ - pub enabled:bool, pub volume:u8, channel_masks:[ChannelMask;NUMBER_OF_CHANNELS] } @@ -14,7 +13,6 @@ pub struct SoundTerminal{ impl Default for SoundTerminal{ fn default() -> Self { SoundTerminal{ - enabled:false, channel_masks:[DISABLE_MASK;NUMBER_OF_CHANNELS], volume:0 } From dba81a94dd45787fed6948d9bc2e9c1c65249e7b Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 13 Nov 2021 18:09:51 +0200 Subject: [PATCH 124/136] Fix an issue where legit oam writes are ignored When developing the bootrom for this emu I have noticed that the ppu when constructed will stay on oam search mode instead hblank. This caused for legit writes to the oam (before the ppu is started for the first time) to be ignored --- lib_gb/src/ppu/gb_ppu.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib_gb/src/ppu/gb_ppu.rs b/lib_gb/src/ppu/gb_ppu.rs index a26679ac..4a5b2dd6 100644 --- a/lib_gb/src/ppu/gb_ppu.rs +++ b/lib_gb/src/ppu/gb_ppu.rs @@ -70,7 +70,7 @@ impl GbPpu{ obj_color_mapping0: [None, Some(LIGHT_GRAY), Some(DARK_GRAY), Some(BLACK)], obj_color_mapping1: [None, Some(LIGHT_GRAY), Some(DARK_GRAY), Some(BLACK)], ly_register:0, - state: PpuState::OamSearch, + state: PpuState::Hblank, //interrupts v_blank_interrupt_request:false, h_blank_interrupt_request:false, From 0893e7aab5a59fa90f3f43107e6d9966efa0adf9 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 13 Nov 2021 21:00:42 +0200 Subject: [PATCH 125/136] Add a feature to specify a bootrom path in the cmd --- README.md | 2 ++ gb/src/main.rs | 10 +++++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 08636e24..a42ec4a4 100644 --- a/README.md +++ b/README.md @@ -18,9 +18,11 @@ magenboy [path_to_rom] [other_optional_flags] ``` ### Optional flags + * `--log` - Print logs in debug mode to a file * `--file-audio` - Saves the audio to a file * `--no-vsync` - Disable vsync +* `--bootrom [path to bootrom file]` - Specify the path for a bootrom ## GameBoy diff --git a/gb/src/main.rs b/gb/src/main.rs index 8d2b6a7f..5ad75a4f 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -118,7 +118,15 @@ fn emulation_thread_main(args: Vec, program_name: String, spsc_gfx_devic let audio_devices = MultiAudioDevice::new(devices); let mut mbc = initialize_mbc(&program_name); let joypad_provider = SdlJoypadProvider::new(buttons_mapper); - let mut gameboy = match fs::read("Dependencies/Init/dmg_boot.bin"){ + let bootrom_path = if check_for_terminal_feature_flag(&args, "--bootrom"){ + let index = args.iter().position(|v| *v == String::from("--bootrom")).expect("Error! you must specify a value for the --bootrom parameter"); + args[index + 1].clone() + }else{ + String::from("Dependencies/Init/dmg_boot.bin") + }; + + println!("{}", bootrom_path); + let mut gameboy = match fs::read(bootrom_path){ Result::Ok(file)=>{ info!("found bootrom!"); From e0821591eaad4a4c7af41d87786430cede2c5a78 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sat, 13 Nov 2021 21:02:44 +0200 Subject: [PATCH 126/136] Update readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a42ec4a4..0ae770b2 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ magenboy [path_to_rom] [other_optional_flags] * `--log` - Print logs in debug mode to a file * `--file-audio` - Saves the audio to a file * `--no-vsync` - Disable vsync -* `--bootrom [path to bootrom file]` - Specify the path for a bootrom +* `--bootrom [path to bootrom file]` - Specify the path for a bootrom (If not specified the emualtor will look at `Dependencies/Init/dmg_boot.bin`) ## GameBoy From 2cf67cd839d9cda495ab6bc0b9cd1fb6e93f4870 Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Fri, 19 Nov 2021 18:54:44 +0200 Subject: [PATCH 127/136] Remove print and crash instead of overflow --- gb/src/main.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/gb/src/main.rs b/gb/src/main.rs index 5ad75a4f..528bced8 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -119,13 +119,12 @@ fn emulation_thread_main(args: Vec, program_name: String, spsc_gfx_devic let mut mbc = initialize_mbc(&program_name); let joypad_provider = SdlJoypadProvider::new(buttons_mapper); let bootrom_path = if check_for_terminal_feature_flag(&args, "--bootrom"){ - let index = args.iter().position(|v| *v == String::from("--bootrom")).expect("Error! you must specify a value for the --bootrom parameter"); - args[index + 1].clone() + let index = args.iter().position(|v| *v == String::from("--bootrom")).unwrap(); + args.get(index + 1).expect("Error! you must specify a value for the --bootrom parameter").clone() }else{ String::from("Dependencies/Init/dmg_boot.bin") }; - println!("{}", bootrom_path); let mut gameboy = match fs::read(bootrom_path){ Result::Ok(file)=>{ info!("found bootrom!"); From d4f241f55b8435fb3569a2bc290c51aa48cfe5de Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Fri, 19 Nov 2021 23:56:04 +0200 Subject: [PATCH 128/136] Add fullscreen option --- README.md | 1 + gb/src/main.rs | 4 ++-- gb/src/sdl_gfx_device.rs | 52 ++++++++++++++++------------------------ 3 files changed, 24 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index 08636e24..c471374d 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ magenboy [path_to_rom] [other_optional_flags] ### Optional flags * `--log` - Print logs in debug mode to a file * `--file-audio` - Saves the audio to a file +* `--full-screen` - Display the screen all over the screen * `--no-vsync` - Disable vsync ## GameBoy diff --git a/gb/src/main.rs b/gb/src/main.rs index 8d2b6a7f..61770741 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -65,8 +65,8 @@ fn main() { Result::Err(error)=>std::panic!("error initing logger: {}", error) } - let mut sdl_gfx_device = sdl_gfx_device::SdlGfxDevice::new(SCREEN_WIDTH as u32, SCREEN_HEIGHT as u32, - "MagenBoy", SCREEN_SCALE, TURBO_MUL, check_for_terminal_feature_flag(&args, "--no-vsync")); + let mut sdl_gfx_device = sdl_gfx_device::SdlGfxDevice::new("MagenBoy", SCREEN_SCALE, TURBO_MUL, + check_for_terminal_feature_flag(&args, "--no-vsync"), check_for_terminal_feature_flag(&args, "--full-screen")); let (s,r) = crossbeam_channel::bounded(BUFFERS_NUMBER - 1); let mpmc_device = MpmcGfxDevice::new(s); diff --git a/gb/src/sdl_gfx_device.rs b/gb/src/sdl_gfx_device.rs index ff8c66d3..ddb397fc 100644 --- a/gb/src/sdl_gfx_device.rs +++ b/gb/src/sdl_gfx_device.rs @@ -7,33 +7,44 @@ pub struct SdlGfxDevice{ _window_name: CString, renderer: *mut SDL_Renderer, texture: *mut SDL_Texture, - width:u32, - height:u32, - sacle:u8, discard:u8, turbo_mul:u8, } impl SdlGfxDevice{ - pub fn new(buffer_width:u32, buffer_height:u32, window_name:&str, screen_scale: u8, turbo_mul:u8, disable_vsync:bool)->Self{ + pub fn new(window_name:&str, screen_scale: u8, turbo_mul:u8, disable_vsync:bool, full_screen:bool)->Self{ let cs_wnd_name = CString::new(window_name).unwrap(); let (_window, renderer, texture): (*mut SDL_Window, *mut SDL_Renderer, *mut SDL_Texture) = unsafe{ SDL_Init(SDL_INIT_VIDEO); + + let window_flags = if full_screen{ + SDL_WindowFlags::SDL_WINDOW_FULLSCREEN_DESKTOP as u32 + } + else{ + 0 + }; + let wind:*mut SDL_Window = SDL_CreateWindow( cs_wnd_name.as_ptr(), SDL_WINDOWPOS_UNDEFINED_MASK as i32, SDL_WINDOWPOS_UNDEFINED_MASK as i32, - buffer_width as i32 * screen_scale as i32, buffer_height as i32 * screen_scale as i32, 0); - let mut flags = SDL_RendererFlags::SDL_RENDERER_ACCELERATED as u32; + SCREEN_WIDTH as i32 * screen_scale as i32, SCREEN_HEIGHT as i32 * screen_scale as i32, + window_flags); + + let mut render_flags = SDL_RendererFlags::SDL_RENDERER_ACCELERATED as u32; if !disable_vsync{ - flags |= SDL_RendererFlags::SDL_RENDERER_PRESENTVSYNC as u32; + render_flags |= SDL_RendererFlags::SDL_RENDERER_PRESENTVSYNC as u32; } - let rend: *mut SDL_Renderer = SDL_CreateRenderer(wind, -1, flags); + let rend: *mut SDL_Renderer = SDL_CreateRenderer(wind, -1, render_flags); + if SDL_RenderSetLogicalSize(rend, (SCREEN_WIDTH as u32) as i32, (SCREEN_HEIGHT as u32) as i32) != 0{ + std::panic!("Error while setting logical rendering"); + } + let tex: *mut SDL_Texture = SDL_CreateTexture(rend, SDL_PixelFormatEnum::SDL_PIXELFORMAT_ARGB8888 as u32, SDL_TextureAccess::SDL_TEXTUREACCESS_STREAMING as i32, - buffer_width as i32 * screen_scale as i32, buffer_height as i32 * screen_scale as i32); + SCREEN_WIDTH as i32 , SCREEN_HEIGHT as i32 ); (wind, rend, tex) }; @@ -42,29 +53,10 @@ impl SdlGfxDevice{ _window_name: cs_wnd_name, renderer, texture, - height:buffer_height, - width:buffer_width, - sacle:screen_scale, discard:0, turbo_mul } } - - fn extend_vec(vec:&[u32], scale:usize, w:usize, h:usize)->Vec{ - let mut new_vec = vec![0;vec.len()*scale*scale]; - for y in 0..h{ - let sy = y*scale; - for x in 0..w{ - let sx = x*scale; - for i in 0..scale{ - for j in 0..scale{ - new_vec[(sy+i)*(w*scale)+sx+j] = vec[y*w+x]; - } - } - } - } - return new_vec; - } } impl GfxDevice for SdlGfxDevice{ @@ -75,12 +67,10 @@ impl GfxDevice for SdlGfxDevice{ } unsafe{ - let extended_buffer = Self::extend_vec(buffer, self.sacle as usize, self.width as usize, self.height as usize); - let mut pixels: *mut c_void = std::ptr::null_mut(); let mut length: std::os::raw::c_int = 0; SDL_LockTexture(self.texture, std::ptr::null(), &mut pixels, &mut length); - std::ptr::copy_nonoverlapping(extended_buffer.as_ptr(),pixels as *mut u32, extended_buffer.len()); + std::ptr::copy_nonoverlapping(buffer.as_ptr(),pixels as *mut u32, buffer.len()); SDL_UnlockTexture(self.texture); //There is no need to call SDL_RenderClear since im replacing the whole buffer From a8d401ae4f6ae88f53c4c32d8eaf57c06834f86e Mon Sep 17 00:00:00 2001 From: Alon Cohen-Magen Date: Sat, 20 Nov 2021 00:58:22 +0200 Subject: [PATCH 129/136] Allow to dynamic link to sdl2 --- Cargo.lock | 4 ++-- gb/Cargo.toml | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5351c143..0660ac1c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -150,9 +150,9 @@ dependencies = [ [[package]] name = "cmake" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb6210b637171dfba4cda12e579ac6dc73f5165ad56133e5d72ef3131f320855" +checksum = "b7b858541263efe664aead4a5209a4ae5c5d2811167d4ed4ee0944503f8d2089" dependencies = [ "cc", ] diff --git a/gb/Cargo.toml b/gb/Cargo.toml index 05b7cb6d..ff059a64 100644 --- a/gb/Cargo.toml +++ b/gb/Cargo.toml @@ -14,11 +14,13 @@ lib_gb = {path = "../lib_gb/"} log = "0.4" fern = "0.6.0" chrono = "0.4" -sdl2 = {version = "0.34", features = ["bundled","static-link"]} +sdl2 = {package = "sdl2", version = "0.34"} wav = "1.0" crossbeam-channel = "0.5" cfg-if = "1.0" [features] +default = ["static-sdl"] sdl-resample = [] -push-audio = [] \ No newline at end of file +push-audio = [] +static-sdl = ["sdl2/bundled", "sdl2/static-link"] \ No newline at end of file From acca065ddbc8cc9069f4eed526ca323e49e36282 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sun, 21 Nov 2021 01:38:44 +0200 Subject: [PATCH 130/136] Remove redundant code in the Cargo.toml file --- gb/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gb/Cargo.toml b/gb/Cargo.toml index ff059a64..78cbc4d5 100644 --- a/gb/Cargo.toml +++ b/gb/Cargo.toml @@ -14,7 +14,7 @@ lib_gb = {path = "../lib_gb/"} log = "0.4" fern = "0.6.0" chrono = "0.4" -sdl2 = {package = "sdl2", version = "0.34"} +sdl2 = "0.34" wav = "1.0" crossbeam-channel = "0.5" cfg-if = "1.0" From 9be6045ed5f17608f053c379f52c727a0bbbc258 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sun, 21 Nov 2021 01:44:33 +0200 Subject: [PATCH 131/136] Remove the need for the Dependencies/Init If the flag is not use the system will look for dmg_boot.bin at the current working directory --- README.md | 2 +- gb/src/main.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0ae770b2..e122942f 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ magenboy [path_to_rom] [other_optional_flags] * `--log` - Print logs in debug mode to a file * `--file-audio` - Saves the audio to a file * `--no-vsync` - Disable vsync -* `--bootrom [path to bootrom file]` - Specify the path for a bootrom (If not specified the emualtor will look at `Dependencies/Init/dmg_boot.bin`) +* `--bootrom [path to bootrom file]` - Specify the path for a bootrom (If not specified the emualtor will look for `dmg_boot.bin` at the cwd) ## GameBoy diff --git a/gb/src/main.rs b/gb/src/main.rs index 528bced8..23693b75 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -122,7 +122,7 @@ fn emulation_thread_main(args: Vec, program_name: String, spsc_gfx_devic let index = args.iter().position(|v| *v == String::from("--bootrom")).unwrap(); args.get(index + 1).expect("Error! you must specify a value for the --bootrom parameter").clone() }else{ - String::from("Dependencies/Init/dmg_boot.bin") + String::from("dmg_boot.bin") }; let mut gameboy = match fs::read(bootrom_path){ From 6c16d50b1a6952c47050ed383c15bc1def9f8531 Mon Sep 17 00:00:00 2001 From: alloncm Date: Sun, 21 Nov 2021 01:46:34 +0200 Subject: [PATCH 132/136] Fix the readme full screen message --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c471374d..3d27016f 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ magenboy [path_to_rom] [other_optional_flags] ### Optional flags * `--log` - Print logs in debug mode to a file * `--file-audio` - Saves the audio to a file -* `--full-screen` - Display the screen all over the screen +* `--full-screen` - Full screen mode * `--no-vsync` - Disable vsync ## GameBoy From 998513901fa7f76656e6514b290f18d57a5563a2 Mon Sep 17 00:00:00 2001 From: alloncm Date: Thu, 25 Nov 2021 07:31:42 +0200 Subject: [PATCH 133/136] Add static_sacle feature --- gb/Cargo.toml | 3 ++- gb/src/main.rs | 2 +- gb/src/sdl_gfx_device.rs | 49 +++++++++++++++++++++++++++++++++++----- 3 files changed, 46 insertions(+), 8 deletions(-) diff --git a/gb/Cargo.toml b/gb/Cargo.toml index 05b7cb6d..1711f437 100644 --- a/gb/Cargo.toml +++ b/gb/Cargo.toml @@ -21,4 +21,5 @@ cfg-if = "1.0" [features] sdl-resample = [] -push-audio = [] \ No newline at end of file +push-audio = [] +static_scale = [] \ No newline at end of file diff --git a/gb/src/main.rs b/gb/src/main.rs index 61770741..7fdeb033 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -10,7 +10,7 @@ use std::{fs, env, result::Result, vec::Vec}; use log::info; use sdl2::sys::*; -const SCREEN_SCALE:u8 = 4; +const SCREEN_SCALE:usize = 4; const TURBO_MUL:u8 = 1; fn init_logger(debug:bool)->Result<(), fern::InitError>{ diff --git a/gb/src/sdl_gfx_device.rs b/gb/src/sdl_gfx_device.rs index ddb397fc..3eb6aca8 100644 --- a/gb/src/sdl_gfx_device.rs +++ b/gb/src/sdl_gfx_device.rs @@ -9,10 +9,12 @@ pub struct SdlGfxDevice{ texture: *mut SDL_Texture, discard:u8, turbo_mul:u8, + #[cfg(feature = "static_scale")] + screen_scale:usize, } impl SdlGfxDevice{ - pub fn new(window_name:&str, screen_scale: u8, turbo_mul:u8, disable_vsync:bool, full_screen:bool)->Self{ + pub fn new(window_name:&str, screen_scale: usize, turbo_mul:u8, disable_vsync:bool, full_screen:bool)->Self{ let cs_wnd_name = CString::new(window_name).unwrap(); let (_window, renderer, texture): (*mut SDL_Window, *mut SDL_Renderer, *mut SDL_Texture) = unsafe{ @@ -38,13 +40,26 @@ impl SdlGfxDevice{ let rend: *mut SDL_Renderer = SDL_CreateRenderer(wind, -1, render_flags); - if SDL_RenderSetLogicalSize(rend, (SCREEN_WIDTH as u32) as i32, (SCREEN_HEIGHT as u32) as i32) != 0{ - std::panic!("Error while setting logical rendering"); - } + let texture_width:i32; + let texture_height:i32; + cfg_if::cfg_if!{ + if #[cfg(feature = "static_scale")]{ + texture_height = SCREEN_HEIGHT as i32* screen_scale as i32; + texture_width = SCREEN_WIDTH as i32* screen_scale as i32; + } + else{ + if SDL_RenderSetLogicalSize(rend, (SCREEN_WIDTH as u32) as i32, (SCREEN_HEIGHT as u32) as i32) != 0{ + std::panic!("Error while setting logical rendering"); + } + texture_height = SCREEN_HEIGHT as i32; + texture_width = SCREEN_WIDTH as i32; + } + } + let tex: *mut SDL_Texture = SDL_CreateTexture(rend, SDL_PixelFormatEnum::SDL_PIXELFORMAT_ARGB8888 as u32, SDL_TextureAccess::SDL_TEXTUREACCESS_STREAMING as i32, - SCREEN_WIDTH as i32 , SCREEN_HEIGHT as i32 ); + texture_width, texture_height); (wind, rend, tex) }; @@ -54,9 +69,28 @@ impl SdlGfxDevice{ renderer, texture, discard:0, - turbo_mul + turbo_mul, + #[cfg(feature = "static_scale")] + screen_scale } } + + #[cfg(feature = "static_scale")] + fn extend_vec(vec:&[u32], scale:usize, w:usize, h:usize)->Vec{ + let mut new_vec = vec![0;vec.len()*scale*scale]; + for y in 0..h{ + let sy = y*scale; + for x in 0..w{ + let sx = x*scale; + for i in 0..scale{ + for j in 0..scale{ + new_vec[(sy+i)*(w*scale)+sx+j] = vec[y*w+x]; + } + } + } + } + return new_vec; + } } impl GfxDevice for SdlGfxDevice{ @@ -66,6 +100,9 @@ impl GfxDevice for SdlGfxDevice{ return; } + #[cfg(feature = "static_scale")] + let buffer = Self::extend_vec(buffer, self.screen_scale, SCREEN_WIDTH, SCREEN_HEIGHT); + unsafe{ let mut pixels: *mut c_void = std::ptr::null_mut(); let mut length: std::os::raw::c_int = 0; From ee71c1f8e294c49db354c35977f35bfccce45cbe Mon Sep 17 00:00:00 2001 From: alloncm Date: Thu, 25 Nov 2021 20:01:53 +0200 Subject: [PATCH 134/136] Change the feature flag name and add a warnning --- gb/Cargo.toml | 2 +- gb/src/sdl_gfx_device.rs | 13 ++++++++----- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/gb/Cargo.toml b/gb/Cargo.toml index 1711f437..2fd3e3e3 100644 --- a/gb/Cargo.toml +++ b/gb/Cargo.toml @@ -22,4 +22,4 @@ cfg-if = "1.0" [features] sdl-resample = [] push-audio = [] -static_scale = [] \ No newline at end of file +static-scale = [] \ No newline at end of file diff --git a/gb/src/sdl_gfx_device.rs b/gb/src/sdl_gfx_device.rs index 3eb6aca8..305bc774 100644 --- a/gb/src/sdl_gfx_device.rs +++ b/gb/src/sdl_gfx_device.rs @@ -9,7 +9,7 @@ pub struct SdlGfxDevice{ texture: *mut SDL_Texture, discard:u8, turbo_mul:u8, - #[cfg(feature = "static_scale")] + #[cfg(feature = "static-scale")] screen_scale:usize, } @@ -21,6 +21,9 @@ impl SdlGfxDevice{ SDL_Init(SDL_INIT_VIDEO); let window_flags = if full_screen{ + #[cfg(feature = "static-scale")] + log::warn!("Please notice that this binary have been compiled with the static-scale feature and you are running with the full screen option.\nThe rendering window might be in wrong scale."); + SDL_WindowFlags::SDL_WINDOW_FULLSCREEN_DESKTOP as u32 } else{ @@ -44,7 +47,7 @@ impl SdlGfxDevice{ let texture_height:i32; cfg_if::cfg_if!{ - if #[cfg(feature = "static_scale")]{ + if #[cfg(feature = "static-scale")]{ texture_height = SCREEN_HEIGHT as i32* screen_scale as i32; texture_width = SCREEN_WIDTH as i32* screen_scale as i32; } @@ -70,12 +73,12 @@ impl SdlGfxDevice{ texture, discard:0, turbo_mul, - #[cfg(feature = "static_scale")] + #[cfg(feature = "static-scale")] screen_scale } } - #[cfg(feature = "static_scale")] + #[cfg(feature = "static-scale")] fn extend_vec(vec:&[u32], scale:usize, w:usize, h:usize)->Vec{ let mut new_vec = vec![0;vec.len()*scale*scale]; for y in 0..h{ @@ -100,7 +103,7 @@ impl GfxDevice for SdlGfxDevice{ return; } - #[cfg(feature = "static_scale")] + #[cfg(feature = "static-scale")] let buffer = Self::extend_vec(buffer, self.screen_scale, SCREEN_WIDTH, SCREEN_HEIGHT); unsafe{ From 96efa54ba0006e7c74880a2f675f406bd3da97e4 Mon Sep 17 00:00:00 2001 From: alloncm Date: Thu, 25 Nov 2021 20:43:20 +0200 Subject: [PATCH 135/136] Improve logs --- gb/src/main.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gb/src/main.rs b/gb/src/main.rs index b9263a02..ce6dee6a 100644 --- a/gb/src/main.rs +++ b/gb/src/main.rs @@ -19,7 +19,7 @@ fn init_logger(debug:bool)->Result<(), fern::InitError>{ .format(|out, message, record| { out.finish(format_args!( "{}[{}] {}", - chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]"), + chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S.%f]"), record.level(), message )) From cabe5bf3eead8b6051ab2a3d06ecc47491b71579 Mon Sep 17 00:00:00 2001 From: alloncm Date: Thu, 25 Nov 2021 20:45:54 +0200 Subject: [PATCH 136/136] Indentation fix --- gb/src/sdl_gfx_device.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gb/src/sdl_gfx_device.rs b/gb/src/sdl_gfx_device.rs index 305bc774..28b51a5e 100644 --- a/gb/src/sdl_gfx_device.rs +++ b/gb/src/sdl_gfx_device.rs @@ -48,8 +48,8 @@ impl SdlGfxDevice{ cfg_if::cfg_if!{ if #[cfg(feature = "static-scale")]{ - texture_height = SCREEN_HEIGHT as i32* screen_scale as i32; - texture_width = SCREEN_WIDTH as i32* screen_scale as i32; + texture_height = SCREEN_HEIGHT as i32 * screen_scale as i32; + texture_width = SCREEN_WIDTH as i32 * screen_scale as i32; } else{ if SDL_RenderSetLogicalSize(rend, (SCREEN_WIDTH as u32) as i32, (SCREEN_HEIGHT as u32) as i32) != 0{