diff options
-rw-r--r-- | basys3/basys3.srcs/ppu.vhd | 4 | ||||
-rw-r--r-- | basys3/basys3.srcs/ppu_addr_dec.vhd | 10 | ||||
-rw-r--r-- | basys3/basys3.srcs/ppu_consts.vhd | 36 | ||||
-rw-r--r-- | basys3/basys3.srcs/sources_1/ip/ppu_tmm/ppu_tmm.xci | 38 | ||||
-rw-r--r-- | basys3/basys3.xpr | 34 | ||||
-rw-r--r-- | docs/architecture.md | 53 |
6 files changed, 104 insertions, 71 deletions
diff --git a/basys3/basys3.srcs/ppu.vhd b/basys3/basys3.srcs/ppu.vhd index acf546f..c798400 100644 --- a/basys3/basys3.srcs/ppu.vhd +++ b/basys3/basys3.srcs/ppu.vhd @@ -79,7 +79,7 @@ architecture Behavioral of ppu is end component; component ppu_sprite_bg port( -- background sprite -- inputs - CLK : in std_logic; -- system clock + CLK : in std_logic; -- pipeline clock RESET : in std_logic; -- reset clock counter OE : in std_logic; -- output enable (of CIDX) X : in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x @@ -100,7 +100,7 @@ architecture Behavioral of ppu is end component; component ppu_sprite_fg port( -- foreground sprite -- inputs - CLK : in std_logic; -- system clock + CLK : in std_logic; -- pipeline clock RESET : in std_logic; -- reset internal memory and clock counters OE : in std_logic; -- output enable (of CIDX) X : in std_logic_vector(PPU_POS_H_WIDTH-1 downto 0); -- current screen pixel x diff --git a/basys3/basys3.srcs/ppu_addr_dec.vhd b/basys3/basys3.srcs/ppu_addr_dec.vhd index df83964..e0c374f 100644 --- a/basys3/basys3.srcs/ppu_addr_dec.vhd +++ b/basys3/basys3.srcs/ppu_addr_dec.vhd @@ -43,9 +43,9 @@ begin AUX_WEN <= AUX_RANGE and WEN; -- address ranges - TMM_RANGE <= '1' when not (ADDR(15) = '1' and ADDR(14) = '1') else '0'; - BAM_RANGE <= '1' when (ADDR(15) = '1' and ADDR(14) = '1') and (ADDR(11) = '0') else '0'; - FAM_RANGE <= '1' when (ADDR(15) = '1' and ADDR(14) = '1') and (ADDR(11) = '1' and ADDR(10) = '0') else '0'; - PAL_RANGE <= '1' when (ADDR(15) = '1' and ADDR(14) = '1') and (ADDR(11) = '1' and ADDR(10) = '1' and ADDR(9) = '0') else '0'; - AUX_RANGE <= '1' when (ADDR(15) = '1' and ADDR(14) = '1') and (ADDR(11) = '1' and ADDR(10) = '1' and ADDR(9) = '1') else '0'; + TMM_RANGE <= '1' when not ((ADDR(15) and ADDR(14) and ADDR(13)) or (ADDR(15) and ADDR(14) and ADDR(12))) else '0'; + BAM_RANGE <= '1' when TMM_RANGE = '0' and (ADDR(11) = '0') else '0'; + FAM_RANGE <= '1' when TMM_RANGE = '0' and (ADDR(11) = '1' and ADDR(10) = '0') else '0'; + PAL_RANGE <= '1' when TMM_RANGE = '0' and (ADDR(11) = '1' and ADDR(10) = '1' and ADDR(9) = '0') else '0'; + AUX_RANGE <= '1' when TMM_RANGE = '0' and (ADDR(11) = '1' and ADDR(10) = '1' and ADDR(9) = '1') else '0'; end Behavioral; diff --git a/basys3/basys3.srcs/ppu_consts.vhd b/basys3/basys3.srcs/ppu_consts.vhd index ab2ccf1..f4d428e 100644 --- a/basys3/basys3.srcs/ppu_consts.vhd +++ b/basys3/basys3.srcs/ppu_consts.vhd @@ -1,22 +1,32 @@ +-- https://docs.google.com/spreadsheets/d/1MU6K4c4PtMR_JXIpc3I0ZJdLZNnoFO7G2P3olCz6LSc package ppu_consts is constant PPU_RAM_BUS_ADDR_WIDTH : natural := 16; -- RAM bus address width constant PPU_RAM_BUS_DATA_WIDTH : natural := 16; -- RAM bus data width constant PPU_FG_SPRITE_COUNT : natural := 128; -- amount of foreground sprites constant PPU_COLOR_OUTPUT_DEPTH : natural := 4; -- VGA output channel depth - constant PPU_PALETTE_IDX_WIDTH : natural := 3; -- palette index width (within sprite) - constant PPU_PALETTE_WIDTH : natural := 3; -- palette index width (palette table) - constant PPU_PALETTE_CIDX_WIDTH : natural := PPU_PALETTE_IDX_WIDTH + PPU_PALETTE_WIDTH; -- global palette index width - constant PPU_TMM_ADDR_WIDTH : natural := 16; - constant PPU_TMM_DATA_WIDTH : natural := 16; - constant PPU_BAM_ADDR_WIDTH : natural := 11; - constant PPU_BAM_DATA_WIDTH : natural := 15; - constant PPU_FAM_ADDR_WIDTH : natural := 8; - constant PPU_FAM_DATA_WIDTH : natural := 16; - constant PPU_PAL_ADDR_WIDTH : natural := 6; - constant PPU_PAL_DATA_WIDTH : natural := 12; - constant PPU_AUX_ADDR_WIDTH : natural := 1; - constant PPU_AUX_DATA_WIDTH : natural := 16; + constant PPU_PALETTE_COLOR_WIDTH : natural := 3; -- palette index width (within sprite) + constant PPU_PALETTE_INDEX_WIDTH : natural := 3; -- palette index width (palette table) + constant PPU_PALETTE_CIDX_WIDTH : natural := PPU_PALETTE_COLOR_WIDTH + PPU_PALETTE_INDEX_WIDTH; -- global palette index width + constant PPU_TMM_ADDR_WIDTH : natural := 16; -- tilemap memory ram bus address width + constant PPU_TMM_DATA_WIDTH : natural := 15; -- tilemap memory ram bus data width + constant PPU_BAM_ADDR_WIDTH : natural := 11; -- background attribute memory ram bus address width + constant PPU_BAM_DATA_WIDTH : natural := 15; -- background attribute memory ram bus data width + constant PPU_FAM_ADDR_WIDTH : natural := 8; -- foreground attribute memory ram bus address width + constant PPU_FAM_DATA_WIDTH : natural := 16; -- foreground attribute memory ram bus data width + constant PPU_PAL_ADDR_WIDTH : natural := 6; -- palette memory ram bus address width + constant PPU_PAL_DATA_WIDTH : natural := 12; -- palette memory ram bus data width + constant PPU_AUX_ADDR_WIDTH : natural := 1; -- auxiliary memory ram bus address width + constant PPU_AUX_DATA_WIDTH : natural := 16; -- auxiliary memory ram bus data width constant PPU_POS_H_WIDTH : natural := 9; -- amount of bits for horizontal screen offset constant PPU_POS_V_WIDTH : natural := 8; -- amount of bits for vertical screen offset + constant PPU_SPRITE_WIDTH : natural := 16; -- sprite width (pixels) + constant PPU_SPRITE_HEIGHT : natural := 16; -- sprite height (pixels) + constant PPU_SCREEN_WIDTH : natural := 320; -- absolute screen width (pixels) + constant PPU_SCREEN_HEIGHT : natural := 240; -- absolute screen height (pixels) + constant PPU_BG_CANVAS_TILES_H : natural := 40; -- amount of tiles (horizontally) on background canvas + constant PPU_BG_CANVAS_TILES_V : natural := 30; -- amount of tiles (vertically) on background canvas + constant PPU_TILE_INDEX_WIDTH : natural := 10; -- amount of bits needed to index a tile from TMM memory + constant PPU_PIXELS_PER_TILE_WORD : natural := 5; -- amount of pixels defined in one word in TMM memory + constant PPU_TILE_SIZE : natural := 48; end package ppu_consts; diff --git a/basys3/basys3.srcs/sources_1/ip/ppu_tmm/ppu_tmm.xci b/basys3/basys3.srcs/sources_1/ip/ppu_tmm/ppu_tmm.xci index 24d2b58..9663635 100644 --- a/basys3/basys3.srcs/sources_1/ip/ppu_tmm/ppu_tmm.xci +++ b/basys3/basys3.srcs/sources_1/ip/ppu_tmm/ppu_tmm.xci @@ -14,7 +14,7 @@ "Use_AXI_ID": [ { "value": "false", "resolve_type": "user", "format": "bool", "enabled": false, "usage": "all" } ], "AXI_ID_Width": [ { "value": "4", "resolve_type": "user", "format": "long", "enabled": false, "usage": "all" } ], "Memory_Type": [ { "value": "Single_Port_RAM", "resolve_type": "user", "usage": "all" } ], - "PRIM_type_to_Implement": [ { "value": "BRAM", "resolve_type": "user", "usage": "all" } ], + "PRIM_type_to_Implement": [ { "value": "BRAM", "resolve_type": "user", "enabled": false, "usage": "all" } ], "Enable_32bit_Address": [ { "value": "false", "resolve_type": "user", "format": "bool", "usage": "all" } ], "ecctype": [ { "value": "No_ECC", "resolve_type": "user", "enabled": false, "usage": "all" } ], "ECC": [ { "value": "false", "resolve_type": "user", "format": "bool", "enabled": false, "usage": "all" } ], @@ -32,13 +32,13 @@ "Algorithm": [ { "value": "Minimum_Area", "resolve_type": "user", "usage": "all" } ], "Primitive": [ { "value": "8kx2", "resolve_type": "user", "enabled": false, "usage": "all" } ], "Assume_Synchronous_Clk": [ { "value": "false", "resolve_type": "user", "format": "bool", "enabled": false, "usage": "all" } ], - "Write_Width_A": [ { "value": "16", "resolve_type": "user", "format": "long", "usage": "all" } ], - "Write_Depth_A": [ { "value": "49152", "value_src": "user", "resolve_type": "user", "format": "long", "usage": "all" } ], - "Read_Width_A": [ { "value": "16", "resolve_type": "user", "usage": "all" } ], + "Write_Width_A": [ { "value": "15", "value_src": "user", "resolve_type": "user", "format": "long", "usage": "all" } ], + "Write_Depth_A": [ { "value": "53248", "value_src": "user", "resolve_type": "user", "format": "long", "usage": "all" } ], + "Read_Width_A": [ { "value": "15", "resolve_type": "user", "usage": "all" } ], "Operating_Mode_A": [ { "value": "WRITE_FIRST", "resolve_type": "user", "usage": "all" } ], "Enable_A": [ { "value": "Always_Enabled", "value_src": "user", "resolve_type": "user", "usage": "all" } ], - "Write_Width_B": [ { "value": "16", "resolve_type": "user", "enabled": false, "usage": "all" } ], - "Read_Width_B": [ { "value": "16", "resolve_type": "user", "enabled": false, "usage": "all" } ], + "Write_Width_B": [ { "value": "15", "resolve_type": "user", "enabled": false, "usage": "all" } ], + "Read_Width_B": [ { "value": "15", "resolve_type": "user", "enabled": false, "usage": "all" } ], "Operating_Mode_B": [ { "value": "WRITE_FIRST", "resolve_type": "user", "enabled": false, "usage": "all" } ], "Enable_B": [ { "value": "Always_Enabled", "resolve_type": "user", "enabled": false, "usage": "all" } ], "Register_PortA_Output_of_Memory_Primitives": [ { "value": "true", "resolve_type": "user", "format": "bool", "usage": "all" } ], @@ -110,10 +110,10 @@ "C_USE_BYTE_WEA": [ { "value": "0", "resolve_type": "generated", "format": "long", "usage": "all" } ], "C_WEA_WIDTH": [ { "value": "1", "resolve_type": "generated", "format": "long", "usage": "all" } ], "C_WRITE_MODE_A": [ { "value": "WRITE_FIRST", "resolve_type": "generated", "usage": "all" } ], - "C_WRITE_WIDTH_A": [ { "value": "16", "resolve_type": "generated", "format": "long", "usage": "all" } ], - "C_READ_WIDTH_A": [ { "value": "16", "resolve_type": "generated", "format": "long", "usage": "all" } ], - "C_WRITE_DEPTH_A": [ { "value": "49152", "resolve_type": "generated", "format": "long", "usage": "all" } ], - "C_READ_DEPTH_A": [ { "value": "49152", "resolve_type": "generated", "format": "long", "usage": "all" } ], + "C_WRITE_WIDTH_A": [ { "value": "15", "resolve_type": "generated", "format": "long", "usage": "all" } ], + "C_READ_WIDTH_A": [ { "value": "15", "resolve_type": "generated", "format": "long", "usage": "all" } ], + "C_WRITE_DEPTH_A": [ { "value": "53248", "resolve_type": "generated", "format": "long", "usage": "all" } ], + "C_READ_DEPTH_A": [ { "value": "53248", "resolve_type": "generated", "format": "long", "usage": "all" } ], "C_ADDRA_WIDTH": [ { "value": "16", "resolve_type": "generated", "format": "long", "usage": "all" } ], "C_HAS_RSTB": [ { "value": "0", "resolve_type": "generated", "format": "long", "usage": "all" } ], "C_RST_PRIORITY_B": [ { "value": "CE", "resolve_type": "generated", "usage": "all" } ], @@ -124,10 +124,10 @@ "C_USE_BYTE_WEB": [ { "value": "0", "resolve_type": "generated", "format": "long", "usage": "all" } ], "C_WEB_WIDTH": [ { "value": "1", "resolve_type": "generated", "format": "long", "usage": "all" } ], "C_WRITE_MODE_B": [ { "value": "WRITE_FIRST", "resolve_type": "generated", "usage": "all" } ], - "C_WRITE_WIDTH_B": [ { "value": "16", "resolve_type": "generated", "format": "long", "usage": "all" } ], - "C_READ_WIDTH_B": [ { "value": "16", "resolve_type": "generated", "format": "long", "usage": "all" } ], - "C_WRITE_DEPTH_B": [ { "value": "49152", "resolve_type": "generated", "format": "long", "usage": "all" } ], - "C_READ_DEPTH_B": [ { "value": "49152", "resolve_type": "generated", "format": "long", "usage": "all" } ], + "C_WRITE_WIDTH_B": [ { "value": "15", "resolve_type": "generated", "format": "long", "usage": "all" } ], + "C_READ_WIDTH_B": [ { "value": "15", "resolve_type": "generated", "format": "long", "usage": "all" } ], + "C_WRITE_DEPTH_B": [ { "value": "53248", "resolve_type": "generated", "format": "long", "usage": "all" } ], + "C_READ_DEPTH_B": [ { "value": "53248", "resolve_type": "generated", "format": "long", "usage": "all" } ], "C_ADDRB_WIDTH": [ { "value": "16", "resolve_type": "generated", "format": "long", "usage": "all" } ], "C_HAS_MEM_OUTPUT_REGS_A": [ { "value": "1", "resolve_type": "generated", "format": "long", "usage": "all" } ], "C_HAS_MEM_OUTPUT_REGS_B": [ { "value": "0", "resolve_type": "generated", "format": "long", "usage": "all" } ], @@ -153,9 +153,9 @@ "C_EN_SHUTDOWN_PIN": [ { "value": "0", "resolve_type": "generated", "format": "long", "usage": "all" } ], "C_EN_SAFETY_CKT": [ { "value": "1", "resolve_type": "generated", "format": "long", "usage": "all" } ], "C_DISABLE_WARN_BHV_RANGE": [ { "value": "0", "resolve_type": "generated", "format": "long", "usage": "all" } ], - "C_COUNT_36K_BRAM": [ { "value": "22", "resolve_type": "generated", "usage": "all" } ], - "C_COUNT_18K_BRAM": [ { "value": "1", "resolve_type": "generated", "usage": "all" } ], - "C_EST_POWER_SUMMARY": [ { "value": "Estimated Power for IP : 16.534446 mW", "resolve_type": "generated", "usage": "all" } ] + "C_COUNT_36K_BRAM": [ { "value": "19", "resolve_type": "generated", "usage": "all" } ], + "C_COUNT_18K_BRAM": [ { "value": "8", "resolve_type": "generated", "usage": "all" } ], + "C_EST_POWER_SUMMARY": [ { "value": "Estimated Power for IP : 13.861152 mW", "resolve_type": "generated", "usage": "all" } ] }, "project_parameters": { "ARCHITECTURE": [ { "value": "artix7" } ], @@ -189,8 +189,8 @@ "rsta": [ { "direction": "in", "driver_value": "0" } ], "wea": [ { "direction": "in", "size_left": "0", "size_right": "0", "driver_value": "0" } ], "addra": [ { "direction": "in", "size_left": "15", "size_right": "0", "driver_value": "0" } ], - "dina": [ { "direction": "in", "size_left": "15", "size_right": "0", "driver_value": "0" } ], - "douta": [ { "direction": "out", "size_left": "15", "size_right": "0" } ], + "dina": [ { "direction": "in", "size_left": "14", "size_right": "0", "driver_value": "0" } ], + "douta": [ { "direction": "out", "size_left": "14", "size_right": "0" } ], "rsta_busy": [ { "direction": "out" } ] }, "interfaces": { diff --git a/basys3/basys3.xpr b/basys3/basys3.xpr index 90e7823..8dcc698 100644 --- a/basys3/basys3.xpr +++ b/basys3/basys3.xpr @@ -61,20 +61,20 @@ <Option Name="IPStaticSourceDir" Val="$PIPUSERFILESDIR/ipstatic"/> <Option Name="EnableBDX" Val="FALSE"/> <Option Name="DSABoardId" Val="basys3"/> - <Option Name="WTXSimLaunchSim" Val="21"/> + <Option Name="WTXSimLaunchSim" Val="27"/> <Option Name="WTModelSimLaunchSim" Val="0"/> <Option Name="WTQuestaLaunchSim" Val="0"/> <Option Name="WTIesLaunchSim" Val="0"/> <Option Name="WTVcsLaunchSim" Val="0"/> <Option Name="WTRivieraLaunchSim" Val="0"/> <Option Name="WTActivehdlLaunchSim" Val="0"/> - <Option Name="WTXSimExportSim" Val="3"/> - <Option Name="WTModelSimExportSim" Val="3"/> - <Option Name="WTQuestaExportSim" Val="3"/> + <Option Name="WTXSimExportSim" Val="4"/> + <Option Name="WTModelSimExportSim" Val="4"/> + <Option Name="WTQuestaExportSim" Val="4"/> <Option Name="WTIesExportSim" Val="0"/> - <Option Name="WTVcsExportSim" Val="3"/> - <Option Name="WTRivieraExportSim" Val="3"/> - <Option Name="WTActivehdlExportSim" Val="3"/> + <Option Name="WTVcsExportSim" Val="4"/> + <Option Name="WTRivieraExportSim" Val="4"/> + <Option Name="WTActivehdlExportSim" Val="4"/> <Option Name="GenerateIPUpgradeLog" Val="TRUE"/> <Option Name="XSimRadix" Val="hex"/> <Option Name="XSimTimeUnit" Val="ns"/> @@ -148,9 +148,15 @@ <Attr Name="UsedIn" Val="simulation"/> </FileInfo> </File> + <File Path="$PSRCDIR/ppu_sprite_bg.vhd"> + <FileInfo> + <Attr Name="UsedIn" Val="synthesis"/> + <Attr Name="UsedIn" Val="simulation"/> + </FileInfo> + </File> <Config> <Option Name="DesignMode" Val="RTL"/> - <Option Name="TopModule" Val="ppu"/> + <Option Name="TopModule" Val="ppu_sprite_bg"/> <Option Name="dataflowViewerSettings" Val="min_width=16"/> </Config> </FileSet> @@ -163,7 +169,7 @@ <FileSet Name="sim_1" Type="SimulationSrcs" RelSrcDir="$PSRCDIR/sim_1" RelGenDir="$PGENDIR/sim_1"> <Filter Type="Srcs"/> <File Path="$PSRCDIR/ppu_addr_dec_tb.vhd"> - <FileInfo SFType="VHDL2008"> + <FileInfo> <Attr Name="UsedIn" Val="synthesis"/> <Attr Name="UsedIn" Val="simulation"/> </FileInfo> @@ -201,7 +207,7 @@ </File> <Config> <Option Name="DesignMode" Val="RTL"/> - <Option Name="TopModule" Val="ppu_aux_tb"/> + <Option Name="TopModule" Val="ppu_addr_dec_tb"/> <Option Name="TopLib" Val="xil_defaultlib"/> <Option Name="TransportPathDelay" Val="0"/> <Option Name="TransportIntDelay" Val="0"/> @@ -270,9 +276,7 @@ <Runs Version="1" Minor="19"> <Run Id="synth_1" Type="Ft3:Synth" SrcSet="sources_1" Part="xc7a35tcpg236-1" ConstrsSet="constrs_1" Description="Vivado Synthesis Defaults" AutoIncrementalCheckpoint="true" WriteIncrSynthDcp="false" State="current" IncludeInArchive="true" IsChild="false" AutoIncrementalDir="$PSRCDIR/utils_1/imports/synth_1" AutoRQSDir="$PSRCDIR/utils_1/imports/synth_1"> <Strategy Version="1" Minor="2"> - <StratHandle Name="Vivado Synthesis Defaults" Flow="Vivado Synthesis 2022"> - <Desc>Vivado Synthesis Defaults</Desc> - </StratHandle> + <StratHandle Name="Vivado Synthesis Defaults" Flow="Vivado Synthesis 2022"/> <Step Id="synth_design"/> </Strategy> <ReportStrategy Name="Vivado Synthesis Default Reports" Flow="Vivado Synthesis 2022"/> @@ -301,9 +305,7 @@ </Run> <Run Id="impl_1" Type="Ft2:EntireDesign" Part="xc7a35tcpg236-1" ConstrsSet="constrs_1" Description="Default settings for Implementation." AutoIncrementalCheckpoint="false" WriteIncrSynthDcp="false" State="current" SynthRun="synth_1" IncludeInArchive="true" IsChild="false" GenFullBitstream="true" AutoIncrementalDir="$PSRCDIR/utils_1/imports/impl_1" AutoRQSDir="$PSRCDIR/utils_1/imports/impl_1"> <Strategy Version="1" Minor="2"> - <StratHandle Name="Vivado Implementation Defaults" Flow="Vivado Implementation 2022"> - <Desc>Default settings for Implementation.</Desc> - </StratHandle> + <StratHandle Name="Vivado Implementation Defaults" Flow="Vivado Implementation 2022"/> <Step Id="init_design"/> <Step Id="opt_design"/> <Step Id="power_opt_design"/> diff --git a/docs/architecture.md b/docs/architecture.md index a60e0da..56bbb62 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -227,16 +227,17 @@ Important notes: - The PPU's memory bus has 16-bit addresses and 16-bit words. - Some memory regions use physical word sizes smaller than 16-bits, so "unneeded" bits will be discarded by the PPU. -- All "fields" or words containing multiple bit strings representing different - pieces of information are ordered from MSB to LSB. +- Apparent size means the amount of addresses in a given memory region. As + mentioned earlier, the exact word sizes of a memory area can vary, though + this is not visible to the CPU as all data is presented as 16-bit words. -|Address offset|Size (16-bit words)|Alias|Description| +|Address offset|Apparent size|Alias|Description| |-|-|-|-| -|`0x0000`|`0xc000`|TMM |[tilemap memory][TMM]| -|`0xc000`|`0x04b0`|BAM |[background attribute memory][BAM]| -|`0xc800`|`0x0100`|FAM |[foreground attribute memory][FAM]| -|`0xcc00`|`0x0040`|PAL |[palettes][PAL]| -|`0xce00`|`0x0002`|AUX |[auxiliary memory][AUX]| +|`0x0000`|`0xd000`|TMM |[tilemap memory][TMM]| +|`0xd000`|`0x04b0`|BAM |[background attribute memory][BAM]| +|`0xd800`|`0x0100`|FAM |[foreground attribute memory][FAM]| +|`0xdc00`|`0x0040`|PAL |[palettes][PAL]| +|`0xde00`|`0x0002`|AUX |[auxiliary memory][AUX]| This table contains the "official" PPU register offsets and sizes. Due to the way the address decoder works, some of these memory regions might be duplicated @@ -247,24 +248,44 @@ there is no address validity checking. [TMM]: #tilemap-memory ### Tilemap memory -- Each sprite takes up 768 bits spread across 48 16-bit words -- Sprites and pixels are stored adjacently in memory without padding -- Pixel order is from top-left to bottom-right in (English) reading order. +- Each sprite takes up 768 bits spread across 52 15-bit words (with one + discarded padding bit per word) +- Pixel index order is from top-left to bottom-right in (English) reading + order. +- Bits `14 downto 3` of the byte with the highest address for a given tile are + not used +- To calculate TMM address $a$ for any given pixel $p$ of tile with index $t$, + compute $a=52*t+\left\lfloor\frac{p}{5}\right\rfloor$ + +Word format: + +|Range (VHDL)|Description| +|-|-| +|`15`|(discarded)| +|`14 downto 12`|pixel $n+4$| +|`11 downto 9`|pixel $n+3$| +|`8 downto 6`|pixel $n+2$| +|`5 downto 3`|pixel $n+1$| +|`2 downto 0`|pixel $n+0$| + [BAM]: #background-attribute-memory ### Background attribute memory - 15-bit words (MSB discarded in hardware) -- Address indicates which background sprite is currently targeted in reading order - e.g. $\textrm{addr} = c000_{\textrm{hex}} + x + y*w$ where $x$ and $y$ are the background tile, and $w$ is the amount of horizontal tiles fit on the background layer (40) +- Address indicates which background sprite is currently targeted in reading + order + e.g. $\textrm{addr} = c000_{\textrm{hex}} + x + y*w$ where $x$ and $y$ + are the background tile, and $w$ is the amount of horizontal tiles fit on the + background layer (40) Word format: |Range (VHDL)|Description| |-|-| -|`15`|Flip horizontally| -|`14`|Flip vertically| -|`13`|(unused)| +|`15`|(discarded)| +|`14`|Flip horizontally| +|`13`|Flip vertically| |`12 downto 10`|Palette index for tile| |`9 downto 0`|Tilemap index| |