@@ -18,7 +18,7 @@ namespace gb
1818
1919 static constexpr auto LINE_CYCLES = 456 ;
2020 static constexpr auto VBLANK_LINE = 144 ;
21- static constexpr auto LINE_MAX = 153 ;
21+ static constexpr auto MAX_LINES = 153 ;
2222
2323 /* Private Implementation */
2424
@@ -33,6 +33,25 @@ namespace gb
3333 LCD
3434 };
3535
36+ /* *
37+ Data needed for HDMA transfer
38+ */
39+ struct Hdma
40+ {
41+ Hdma () :
42+ transfer_active (false ),
43+ source (0 ),
44+ destination (0 ),
45+ length (0 )
46+ {
47+ }
48+
49+ bool transfer_active;
50+ uint16_t source;
51+ uint16_t destination;
52+ uint16_t length;
53+ };
54+
3655 using CgbPalette = std::array<std::array<gb::Pixel, 4 >, 8 >;
3756
3857 Impl (MMU::Ptr& mmu) :
@@ -46,8 +65,7 @@ namespace gb
4665 vblank_provider_(*mmu.get(), InterruptProvider::Interrupt::VBLANK),
4766 stat_provider_(*mmu.get(), InterruptProvider::Interrupt::LCDSTAT),
4867 tilemap_(*mmu.get(), palette_),
49- cgb_enabled_(mmu->cgbEnabled ()),
50- hdma_transfer_start_(false )
68+ cgb_enabled_(mmu->cgbEnabled ())
5169 {
5270 mmu->addWriteHandler (memorymap::LCDC_REGISTER, std::bind (&Impl::lcdcWriteHandler, this , std::placeholders::_1, std::placeholders::_2));
5371 mmu->addWriteHandler (memorymap::BGPD, std::bind (&Impl::paletteWriteHandler, this , std::placeholders::_1, std::placeholders::_2));
@@ -67,8 +85,6 @@ namespace gb
6785 // check if the HBLANK period is over
6886 if (hasElapsed (HBLANK_CYCLES))
6987 {
70- // render the current scan line
71- renderScanline ();
7288 // update the scan line
7389 updateLY ();
7490 // check if LY matches LYC
@@ -97,7 +113,12 @@ namespace gb
97113 case Mode::LCD:
98114 if (hasElapsed (LCD_TRANSFER_CYCLES))
99115 {
116+ // render the current scan line
117+ renderScanline ();
118+
100119 mode_ = Mode::HBLANK;
120+ // perform an hdma transfer
121+ doHdma ();
101122 checkStatInterrupts (ime);
102123 }
103124 break ;
@@ -116,6 +137,8 @@ namespace gb
116137 break ;
117138 }
118139
140+ // update LCDC stat mode
141+ stat_ = (stat_ & 0xFC ) | (static_cast <uint8_t >(mode_));
119142 }
120143
121144 void setRenderCallback (RenderScanlineCallback callback)
@@ -153,11 +176,10 @@ namespace gb
153176 auto background_palette = palette_.get (mmu_->read (memorymap::BGP_REGISTER));
154177
155178 // get lcd config
156- const auto lcdc = mmu_->read (memorymap::LCDC_REGISTER);
157179
158- const auto background_enabled = IS_SET (lcdc , memorymap::LCDC::BG_DISPLAY_ON) != 0 ;
159- const auto window_enabled = IS_SET (lcdc , memorymap::LCDC::WINDOW_ON) != 0 ;
160- const auto sprites_enabled = IS_SET (lcdc , memorymap::LCDC::OBJ_ON) != 0 ;
180+ const auto background_enabled = IS_SET (lcdc_ , memorymap::LCDC::BG_DISPLAY_ON) != 0 ;
181+ const auto window_enabled = IS_SET (lcdc_ , memorymap::LCDC::WINDOW_ON) != 0 ;
182+ const auto sprites_enabled = IS_SET (lcdc_ , memorymap::LCDC::OBJ_ON) != 0 ;
161183
162184 // get background tile line
163185 const auto background = tilemap_.getBackground (line_, cgb_enabled_);
@@ -181,8 +203,9 @@ namespace gb
181203
182204 auto color_number = tileinfo & 0x03 ;
183205 auto color_palette = (tileinfo >> 2 ) & 0x07 ;
206+ auto priority = (tileinfo >> 5 );
184207
185- color_line[pixel_idx] = color_number;
208+ color_line[pixel_idx] = color_number | (priority << 2 ) ;
186209
187210 if (cgb_enabled_)
188211 {
@@ -214,7 +237,7 @@ namespace gb
214237
215238 void updateLY ()
216239 {
217- line_ = (line_ + 1 ) % LINE_MAX ;
240+ line_ = (line_ + 1 ) % MAX_LINES ;
218241 mmu_->write ((uint8_t )line_, memorymap::LY_REGISTER);
219242 }
220243
@@ -299,22 +322,56 @@ namespace gb
299322
300323 void hdma5WriteHandler (uint8_t value, uint16_t addr)
301324 {
302- if (IS_BIT_CLR (value, 7 ))
303- {
304- uint16_t src = WORD (mmu_->read (memorymap::HDMA1), mmu_->read (memorymap::HDMA2));
305- uint16_t dest = WORD (mmu_->read (memorymap::HDMA3), mmu_->read (memorymap::HDMA4));
306- uint16_t length = ((value & 0x7F ) + 1 ) * 0x10 ;
325+ uint16_t src = WORD (mmu_->read (memorymap::HDMA1), mmu_->read (memorymap::HDMA2)) & 0xFFF0 ;
326+ uint16_t dest = WORD (((mmu_->read (memorymap::HDMA3) & 0x1F ) | 0x80 ), mmu_->read (memorymap::HDMA4)) & 0xFFF0 ;
327+ uint16_t length = ((value & 0x7F ) + 1 ) * 0x10 ;
307328
329+ if (IS_BIT_CLR (value, 7 ) && !hdma_.transfer_active )
330+ {
331+ // perform a general purpose DMA
308332 mmu_->dma (dest, src, length);
309333 }
334+ else if (IS_BIT_CLR (value, 7 ) && hdma_.transfer_active )
335+ {
336+ // disable an active hdma transfer
337+ hdma_.transfer_active = false ;
338+ }
310339 else
311340 {
312- hdma_transfer_start_ = true ;
341+ // initialize an HDMA transfer
342+ hdma_.source = src;
343+ hdma_.destination = dest;
344+ hdma_.length = length;
345+ hdma_.transfer_active = true ;
313346 }
314347
315348 hdma5_ = value;
316349 }
317350
351+ void doHdma ()
352+ {
353+ if (hdma_.transfer_active )
354+ {
355+ // hdma only works between this range
356+ if (line_ >= 0 && line_ <= 143 )
357+ {
358+ // transfer $10 bytes
359+ mmu_->dma (hdma_.destination , hdma_.source , 0x10 );
360+ // advance source $10 bytes
361+ hdma_.source += 0x10 ;
362+ // advance destination $10 bytes
363+ hdma_.destination += 0x10 ;
364+ // count down the length
365+ hdma_.length -= 0x10 ;
366+
367+ if (hdma_.length == 0 )
368+ {
369+ hdma_.transfer_active = false ;
370+ }
371+ }
372+ }
373+ }
374+
318375 void compareLyToLyc (bool ime)
319376 {
320377 auto lyc = mmu_->read (memorymap::LYC_REGISTER);
@@ -359,6 +416,7 @@ namespace gb
359416 uint8_t & lcdc_;
360417 uint8_t & stat_;
361418 uint8_t & hdma5_;
419+ Hdma hdma_;
362420
363421 InterruptProvider vblank_provider_;
364422 InterruptProvider stat_provider_;
@@ -369,7 +427,6 @@ namespace gb
369427 RenderScanlineCallback render_scanline_;
370428
371429 bool cgb_enabled_;
372- bool hdma_transfer_start_;
373430
374431 CgbPalette cgb_background_palettes_;
375432 CgbPalette cgb_sprite_palette_;
0 commit comments