SHARE
TWEET

Untitled

a guest Jul 19th, 2019 64 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. #pragma once
  2.  
  3. #include "cartridge.h"
  4. #include "display.h"
  5. #include "nmi_flipflop.h"
  6.  
  7. #include <array>
  8.  
  9. #define NTH_BIT(x, n) (((x) >> (n)) & 1)
  10.  
  11. enum ppu_map : uint8_t
  12. {
  13.     PPUCTRL   = 0x00,
  14.     PPUMASK   = 0x01,
  15.     PPUSTATUS = 0x02,
  16.     OAMADDR   = 0x03,
  17.     OAMDATA   = 0x04,
  18.     PPUSCROLL = 0x05,
  19.     PPUADDR   = 0x06,
  20.     PPUDATA   = 0x07
  21. };
  22.  
  23. constexpr uint32_t nes_palette[] =
  24. {
  25.     0x7C7C7C, 0xFC0000, 0xBC0000, 0xBC2844, 0x840094, 0x2000A8, 0x0010A8, 0x001488, //
  26.     0x003050, 0x007800, 0x006800, 0x005800, 0x584000, 0x000000, 0x000000, 0x000000, //
  27.     0xBCBCBC, 0xF87800, 0xF85800, 0xFC4468, 0xCC00D8, 0x5800E4, 0x0038F8, 0x105CE4, //
  28.     0x007CAC, 0x00B800, 0x00A800, 0x44A800, 0x888800, 0x000000, 0x000000, 0x000000, //
  29.     0xF8F8F8, 0xFCBC3C, 0xFC8868, 0xF87898, 0xF878F8, 0x9858F8, 0x5878F8, 0x44A0FC, //
  30.     0x00B8F8, 0x18F8B8, 0x54D858, 0x98F858, 0xD8E800, 0x787878, 0x000000, 0x000000, //
  31.     0xFCFCFC, 0xFCE4A4, 0xF8B8B8, 0xF8B8D8, 0xF8B8F8, 0xC0A4F8, 0xB0D0F0, 0xA8E0FC, //
  32.     0x78D8F8, 0x78F8D8, 0xB8F8B8, 0xD8F8B8, 0xFCFC00, 0xF8D8F8, 0x000000, 0x000000  //
  33. };
  34.  
  35. namespace PPU
  36. {
  37.     struct sprite
  38.     {
  39.         uint8_t id;
  40.         uint8_t x;
  41.         uint8_t y;
  42.         uint8_t tile;
  43.         uint8_t attributes;
  44.         uint8_t data_l;
  45.         uint8_t data_h;
  46.     };
  47.  
  48.     nes::cartridge *    cartridge;
  49.     platform::display * display;
  50.     nes::nmi_flipflop * nmi_flipflop;
  51.  
  52.     std::array<uint8_t , 0x0800> nametable_ram;
  53.     std::array<uint8_t , 0x20>   palette_ram;
  54.     std::array<uint8_t , 0x0100> oam_ram;
  55.     std::array<sprite  , 0x08>   oam_0;
  56.     std::array<sprite  , 0x08>   oam_1;
  57.     std::array<uint32_t, 0xF000> framebuffer;
  58.  
  59.     uint16_t
  60.         v,
  61.         t,
  62.         line,
  63.         dot,
  64.         address_buffer,
  65.         background_shift_register_l,
  66.         background_shift_register_h;
  67.  
  68.     uint8_t
  69.         x,
  70.         w,
  71.         data_latch,
  72.         data_buffer,
  73.         oam_address,
  74.         control_flags,
  75.         mask_flags,
  76.         status_flags,
  77.         name_buffer,
  78.         attribute_buffer,
  79.         background_tile_l,
  80.         background_tile_h,
  81.         attribute_shift_register_l,
  82.         attribute_shift_register_h,
  83.         odd_frame,
  84.         attribute_latch_l,
  85.         attribute_latch_h;
  86.  
  87.     void (*dot_process)();
  88.     void dot_0_0();
  89.     void dot_240_0();
  90.     void dot_nt_odd();
  91.     void dot_nt_even();
  92.  
  93.     inline bool background_enabled()
  94.     {
  95.         return !!(0x18 & mask_flags);
  96.     }
  97.     inline uint16_t nametable_address()
  98.     {
  99.         return 0x2000 | (v & 0x0FFF);
  100.     }
  101.     inline uint16_t attributes_address()
  102.     {
  103.         return (0x23C0)
  104.              | (0x0C00 & (v     ))
  105.              | (0x0038 & (v >> 4))
  106.              | (0x0007 & (v >> 2));
  107.     }
  108.     inline uint16_t background_address()
  109.     {
  110.         return (0x1000 & (control_flags <<  8))
  111.              | (0x0FF0 & (name_buffer   <<  4))
  112.              | (0x0007 & (v             >> 12));
  113.     }
  114.     inline uint16_t map_nametable_address(uint16_t address)
  115.     {
  116.         switch (cartridge->get_mirroring())
  117.         {
  118.         case nes::mirroring::Vertical:
  119.             return 0x07FF & address;
  120.         case nes::mirroring::Horizontal:
  121.             return (0x0400 & (address >> 1)) + (0x03FF & address);
  122.         default:
  123.             return address - 0x2000;
  124.         }
  125.     }
  126.     inline uint16_t map_palette_address(uint16_t address)
  127.     {
  128.         return (address & ((0x0003 & address) ? ~0x0000 : ~0x0010)) & 0x001F;
  129.     }
  130.     inline uint8_t read_nametable(uint16_t address)
  131.     {
  132.         return nametable_ram[address];
  133.     }
  134.     inline void write_nametable(uint16_t address, uint8_t value)
  135.     {
  136.         nametable_ram[address] = value;
  137.     }
  138.     inline uint8_t read_palette(uint16_t address)
  139.     {
  140.         return ((mask_flags & 0x01) ? 0x18 : 0xFF) & palette_ram[address];
  141.     }
  142.     inline void write_palette(uint16_t address, uint8_t value)
  143.     {
  144.         palette_ram[address] = value;
  145.     }
  146.     inline uint8_t read_memory(uint16_t address)
  147.     {
  148.         if (address & 0xC000) throw std::runtime_error{ std::string{ "Illegal ppu read at " } + std::to_string(address) };
  149.         if (~address & 0x3F00)
  150.         {
  151.             if (address & 0x2000) return read_nametable(map_nametable_address(address));
  152.             return cartridge->read_chr(address);
  153.         }
  154.         return read_palette(map_palette_address(address));
  155.     }
  156.     inline void write_memory(uint16_t address, uint8_t value)
  157.     {
  158.         if (address & 0xC000) throw std::runtime_error{ std::string{ "Illegal ppu read at " } + std::to_string(address) };
  159.         if (~address & 0x3F00)
  160.         {
  161.             if (address & 0x2000) return write_nametable(map_nametable_address(address), value);
  162.             return cartridge->write_chr(address, value);
  163.         }
  164.         return write_palette(map_palette_address(address), value);
  165.     }
  166.     inline void h_scroll()
  167.     {
  168.         if (!background_enabled()) return;
  169.         if ((0x001F & v) == 0x001F) v ^= 0x041F;
  170.         else v = (~0x001F & v) | (0x001F & (v + 0x0001));
  171.     }
  172.     inline void v_scroll()
  173.     {
  174.         if (!background_enabled()) return;
  175.         uint16_t v1 = ~0x7000 & v;
  176.         if ((0x7000 & v) == 0x7000)
  177.         {
  178.             v1 = ~0x03E0 & v1;
  179.             if ((0x03E0 & v) == 0x03E0) v = v1;
  180.             else if ((0x03E0 & v) == 0x03A0) v = v1 ^ 0x0800;
  181.             else v = v1 | (0x03E0 & (v + 0x0020));
  182.         }
  183.         else v = v1 | (0x7000 & (v + 0x1000));
  184.     }
  185.     inline void h_update()
  186.     {
  187.         if (!background_enabled()) return;
  188.         v = (~0x041F & v)
  189.           | ( 0x041F & t);
  190.     }
  191.     inline void v_update()
  192.     {
  193.         if (!background_enabled()) return;
  194.         v = (~0x7BE0 & v)
  195.           | ( 0x7BE0 & t);
  196.     }
  197.     inline void reload_shift_registers()
  198.     {
  199.         background_shift_register_l = (background_shift_register_l & 0xFF00) | background_tile_l;
  200.         background_shift_register_h = (background_shift_register_h & 0xFF00) | background_tile_h;
  201.         attribute_latch_l = (attribute_buffer & 0x01);
  202.         attribute_latch_h = (attribute_buffer & 0x02) >> 1;
  203.     }
  204.     inline uint8_t sprite_height()
  205.     {
  206.         return (0x20 & control_flags) ? 0x10 : 0x08;
  207.     }
  208.     inline void clear_oam_1()
  209.     {
  210.         for (int i = 0; i < 8; i++)
  211.         {
  212.             oam_1[i].id           = 0x40;
  213.             oam_1[i].y            = 0xFF;
  214.             oam_1[i].tile         = 0xFF;
  215.             oam_1[i].attributes   = 0xFF;
  216.             oam_1[i].x            = 0xFF;
  217.             oam_1[i].data_l       = 0x00;
  218.             oam_1[i].data_h       = 0x00;
  219.         }
  220.     }
  221.     inline void evaluate_sprites()
  222.     {
  223.         for (int i = 0, n = 0; i < 64; i++)
  224.         {
  225.             int sprite_line = line - oam_ram[i << 2];
  226.             if (sprite_line >= 0 && sprite_line < sprite_height())
  227.             {
  228.                 oam_1[n].id         = i;
  229.                 oam_1[n].y          = oam_ram[(i << 2) + 0x0000];
  230.                 oam_1[n].tile       = oam_ram[(i << 2) + 0x0001];
  231.                 oam_1[n].attributes = oam_ram[(i << 2) + 0x0002];
  232.                 oam_1[n].x          = oam_ram[(i << 2) + 0x0003];
  233.                 if(n++ == 8)
  234.                 {
  235.                     status_flags |= 0x20;
  236.                     return;
  237.                 }
  238.             }
  239.         }
  240.     }
  241.     inline void load_sprites()
  242.     {
  243.         for (int i = 0; i < 8; i++)
  244.         {
  245.             oam_0[i] = oam_1[i];
  246.             uint16_t address =
  247.                 (sprite_height() == 0x10)
  248.                     ? (0x1000 & (oam_0[i].tile << 12)) | (0x0FE0 & (oam_0[i].tile << 4))
  249.                     : (0x1000 & (control_flags        <<  9)) | (         (oam_0[i].tile << 4));
  250.  
  251.             uint8_t sprite_line = (line - oam_0[i].y) % sprite_height();
  252.             if (oam_0[i].attributes & 0x80) sprite_line ^= sprite_height() - 0x01;
  253.             address += sprite_line + (sprite_line & 0x08);
  254.             oam_0[i].data_l = read_memory(address + 0);
  255.             oam_0[i].data_h = read_memory(address + 8);
  256.         }
  257.     }
  258.     inline void pixel()
  259.     {
  260.         uint8_t  palette     = 0;
  261.         uint8_t  obj_palette  = 0;
  262.         uint8_t  obj_priority = 0;
  263.         int16_t  pxx         = dot - 2;
  264.  
  265.             if ((0x08 & mask_flags) && ((0x02 & mask_flags) || pxx >= 8))
  266.             {
  267.                 palette = (NTH_BIT(background_shift_register_h, 15 - x) << 1) | NTH_BIT(background_shift_register_l, 15 - x);
  268.                 if (palette) palette |= ((NTH_BIT(attribute_shift_register_h, 7 - x) << 1) | NTH_BIT(attribute_shift_register_l, 7 - x)) << 2;
  269.             }
  270.             if ((0x10 & mask_flags) && ((0x04 & mask_flags) || pxx >= 8))
  271.             {
  272.                 for (int i = 7; i >= 0; i--)
  273.                 {
  274.                     if (oam_0[i].id == 64) continue;
  275.                     unsigned spx = pxx - oam_0[i].x;
  276.                     if (spx >= 8) continue;
  277.                     if (oam_0[i].attributes & 0x40) spx ^= 7;
  278.                     uint8_t spr_palette = (NTH_BIT(oam_0[i].data_h, 7 - spx) << 1) | NTH_BIT(oam_0[i].data_l, 7 - spx);
  279.                     if (spr_palette == 0) continue; // Transparent pixel.
  280.                     if (oam_0[i].id == 0 && palette && pxx != 255) status_flags |= 0x40;
  281.                     spr_palette |= (oam_0[i].attributes & 3) << 2;
  282.                     obj_palette  = spr_palette + 16;
  283.                     obj_priority = oam_0[i].attributes & 0x20;
  284.                 }
  285.             }
  286.             if (obj_palette && (palette == 0 || obj_priority == 0)) palette = obj_palette;
  287.             framebuffer[line * 256 + pxx] = nes_palette[read_memory(0x3F00 + (background_enabled() ? palette : 0))];
  288.  
  289.         background_shift_register_l <<= 1;
  290.         background_shift_register_h <<= 1;
  291.         attribute_shift_register_l = (attribute_shift_register_l << 1) | attribute_latch_l;
  292.         attribute_shift_register_h = (attribute_shift_register_h << 1) | attribute_latch_h;
  293.     }
  294.  
  295.     void dot_340()
  296.     {
  297.         name_buffer = read_memory(address_buffer);
  298.  
  299.         dot = 0xFFFF;
  300.         if (line == 239) dot_process = &dot_240_0;
  301.         else dot_process = &dot_0_0;
  302.         (line = (line + 1) % 262) || (odd_frame = !odd_frame);
  303.     }
  304.     void dot_339()
  305.     {
  306.         address_buffer = nametable_address();
  307.         dot_process = &dot_340;
  308.     }
  309.     void dot_338()
  310.     {
  311.         name_buffer = read_memory(address_buffer);
  312.         dot_process = &dot_339;
  313.     }
  314.     void dot_321()
  315.     {
  316.         load_sprites();
  317.         address_buffer = nametable_address();
  318.         dot_process = &dot_nt_even;
  319.     }
  320.     void dot_0_258()
  321.     {
  322.         oam_address = 0;
  323.         if (dot == 320) dot_process = &dot_321;
  324.     }
  325.     void dot_261_280()
  326.     {
  327.         oam_address = 0;
  328.         v_update();
  329.         if (dot == 304) dot_process = &dot_0_258;
  330.     }
  331.     void dot_261_258()
  332.     {
  333.         oam_address = 0;
  334.         if (dot == 279) dot_process = &dot_261_280;
  335.     }
  336.     void dot_261_257()
  337.     {
  338.         pixel();
  339.         reload_shift_registers();
  340.         h_update();
  341.         dot_process = &dot_261_258;
  342.     }
  343.     void dot_0_257()
  344.     {
  345.         evaluate_sprites();
  346.         pixel();
  347.         reload_shift_registers();
  348.         h_update();
  349.         dot_process = &dot_0_258;
  350.     }
  351.     void dot_256()
  352.     {
  353.         pixel();
  354.         background_tile_h = read_memory(address_buffer);
  355.         v_scroll();
  356.         if (line == 261) dot_process = &dot_261_257;
  357.         else dot_process = &dot_0_257;
  358.     }
  359.     void dot_bgh_even()
  360.     {
  361.         pixel();
  362.         background_tile_h = read_memory(address_buffer);
  363.         h_scroll();
  364.         dot_process = &dot_nt_odd;
  365.     }
  366.     void dot_bgh_odd()
  367.     {
  368.         pixel();
  369.         address_buffer += 8;
  370.         if(dot == 255) dot_process = &dot_256;
  371.         else dot_process = &dot_bgh_even;
  372.     }
  373.     void dot_bgl_even()
  374.     {
  375.         pixel();
  376.         background_tile_l = read_memory(address_buffer);
  377.         dot_process = &dot_bgh_odd;
  378.     }
  379.     void dot_bgl_odd()
  380.     {
  381.         pixel();
  382.         address_buffer = background_address();
  383.         dot_process = &dot_bgl_even;
  384.     }
  385.     void dot_at_even()
  386.     {
  387.         pixel();
  388.         attribute_buffer = read_memory(address_buffer);
  389.         if (v & 0x02) attribute_buffer >>= 2;
  390.         if (v & 0x40) attribute_buffer >>= 4;
  391.         dot_process = &dot_bgl_odd;
  392.     }
  393.     void dot_at_odd()
  394.     {
  395.         pixel();
  396.         address_buffer = attributes_address();
  397.         dot_process = &dot_at_even;
  398.     }
  399.     void dot_nt_even()
  400.     {
  401.         pixel();
  402.         name_buffer = read_memory(address_buffer);
  403.         dot_process = &dot_at_odd;
  404.     }
  405.     void dot_nt_odd()
  406.     {
  407.         pixel();
  408.         address_buffer = nametable_address();
  409.         reload_shift_registers();
  410.         if(dot == 337) dot_process = &dot_338;
  411.         else dot_process = &dot_nt_even;
  412.     }
  413.     void dot_261_1()
  414.     {
  415.         status_flags &= ~0x60;
  416.         address_buffer = nametable_address();
  417.         status_flags &= ~0x80;
  418.         dot_process = &dot_nt_even;
  419.     }
  420.     void dot_261_0()
  421.     {
  422.         dot_process = dot_261_1;
  423.     }
  424.     void dot_241_2()
  425.     {
  426.         if (dot == 340)
  427.         {
  428.             dot = 0xFFFF;
  429.             if(line == 261) dot_process = dot_261_0;
  430.             (line = (line + 1) % 262) || (odd_frame = !odd_frame);
  431.         }
  432.     }
  433.     void dot_241_1()
  434.     {
  435.         status_flags |= 0x80;
  436.         if (control_flags & 0x80) nmi_flipflop->set();
  437.         dot_process = &dot_241_2;
  438.     }
  439.     void dot_241_0()
  440.     {
  441.         dot_process = &dot_241_1;
  442.     }
  443.     void dot_240_1()
  444.     {
  445.         if(dot == 340)
  446.         {
  447.             dot = 0xFFFF;
  448.             (line = (line + 1) % 262) || (odd_frame = !odd_frame);
  449.             dot_process = &dot_241_0;
  450.         }
  451.     }
  452.     void dot_240_0()
  453.     {
  454.         display->update_frame(framebuffer.data());
  455.         dot_process = &dot_240_1;
  456.     }
  457.     void dot_0_1()
  458.     {
  459.         clear_oam_1();
  460.         address_buffer = nametable_address();
  461.         dot_process = &dot_nt_even;
  462.     }
  463.     void dot_0_0()
  464.     {
  465.         if (line == 0 && background_enabled() && odd_frame)
  466.         {
  467.             ++dot;
  468.             dot_0_1();
  469.         }
  470.         else dot_process = &dot_0_1;
  471.     }
  472.  
  473.     void clock()
  474.     {
  475.         dot_process();
  476.         ++dot;
  477.     }
  478.     void write(uint8_t index, uint8_t value)
  479.     {
  480.         data_latch = value;
  481.  
  482.         switch (index)
  483.         {
  484.         case PPUCTRL:
  485.             control_flags  = value;
  486.             t = (~0x0C00 & t) | (0x0C00 & (control_flags << 10));
  487.             break;
  488.         case PPUMASK:
  489.             mask_flags = value;
  490.             break;
  491.         case OAMADDR:
  492.             oam_address = value;
  493.             break;
  494.         case OAMDATA:
  495.             oam_ram[oam_address] = value;
  496.             oam_address = (oam_address + 1) % 0x0100;
  497.             break;
  498.         case PPUSCROLL:
  499.             if ((w = !w))
  500.             {
  501.                 x = 0x07 & value;
  502.                 t = (~0x001F & t) | (0x001F & (value >> 3));
  503.             }
  504.             else
  505.             {
  506.                 t = (~(0x7000 | 0x03E0) & t) | (0x7000 & (value << 12)) | (0x03E0 & (value << 2));
  507.             }
  508.             break;
  509.         case PPUADDR:
  510.             if ((w = !w))
  511.             {
  512.                 t = (~0x3F00 & t) | (0x3F00 & (value << 8));
  513.             }
  514.             else
  515.             {
  516.                 t = (~0x00FF & t) | (0x00FF & value);
  517.                 v = t;
  518.             }
  519.             break;
  520.         case PPUDATA:
  521.             write_memory(0x3FFF & v, value);
  522.             v = (~0x3FFF & v) | (0x3FFF & (v + ((0x04 & control_flags) ? 0x0020 : 0x0001)));
  523.         }
  524.     }
  525.     uint8_t read(uint8_t index)
  526.     {
  527.         switch (index)
  528.         {
  529.         case PPUSTATUS:
  530.             data_latch = (0x1F & data_latch) | status_flags;
  531.             status_flags &= ~0x80;
  532.             w = 0;
  533.             break;
  534.         case OAMDATA:
  535.             data_latch = oam_ram[oam_address];
  536.             break;
  537.         case PPUDATA:
  538.             if (~v & 0x3F00)
  539.             {
  540.                 data_latch  = data_buffer;
  541.                 data_buffer = read_memory(0x3FFF & v);
  542.             }
  543.             else
  544.             {
  545.                 data_latch = read_memory(0x3FFF & v);
  546.                 data_buffer = read_memory(0x2FFF & v);
  547.             }
  548.             v = (~0x3FFF & v) | (0x3FFF & (v + ((0x04 & control_flags) ? 0x0020 : 0x0001)));
  549.         }
  550.         return data_latch;
  551.     }
  552.     void reset()
  553.     {
  554.         odd_frame    = false;
  555.         line         = 0;
  556.         dot_process  = dot_0_0;
  557.         dot          = 0;
  558.         control_flags         = 0;
  559.         mask_flags         = 0;
  560.         status_flags       = 0;
  561.  
  562.         framebuffer.fill(0);
  563.         nametable_ram.fill(0xFF);
  564.         oam_ram.fill(0);
  565.     }
  566. }
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top