git.y1.nz

gbdk-2020

GameBoy Development Kit
download: https://git.y1.nz/archives/gbdk.tar.gz
README | Files | Log | Refs | LICENSE

commit 31e8698341e4f49238ad0d8f9dbabc92fa31749f
parent 5e1f97a8d737c239bc5bda6d304238b68089b43f
Author: Toxa <56631470+untoxa@users.noreply.github.com>
Date:   Tue, 17 Jan 2023 23:11:43 +0400

Merge pull request #468 from michel-iwaniec/nes_set_bkg_submap_attributes

NES: Add set_bkg_submap_attributes implementations
Diffstat:
Mgbdk-lib/include/nes/nes.h64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mgbdk-lib/libc/targets/mos6502/nes/Makefile2+-
Mgbdk-lib/libc/targets/mos6502/nes/global.s4++++
Agbdk-lib/libc/targets/mos6502/nes/set_tile_submap_attributes.s404+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
4 files changed, 473 insertions(+), 1 deletion(-)

diff --git a/gbdk-lib/include/nes/nes.h b/gbdk-lib/include/nes/nes.h @@ -539,6 +539,70 @@ inline void set_bkg_attributes(uint8_t x, uint8_t y, uint8_t w, uint8_t h, const { set_bkg_attributes_nes16x16(x >> 1, y >> 1, (w + 1) >> 1, (h + 1) >> 1, attributes); } +/** Sets a rectangular area of the Background Tile Map using a sub-region + from a source tile map. Useful for scrolling implementations of maps + larger than 32 x 30 tiles / 16x15 attributes. + + @param x X Start position in both the Source Attribute Map and hardware Background Map attribute coordinates. Range 0 - 255 + @param y Y Start position in both the Source Attribute Map and hardware Background Map attribute coordinates. Range 0 - 255 + @param w Width of area to set in Attributes. Range 1 - 127 + @param h Height of area to set in Attributes. Range 1 - 127 + @param map Pointer to source tile map data + @param map_w Width of source tile map in tiles. Range 1 - 127 + + Entries are copied from __map__ to the Background Attribute Map starting at + __x__, __y__ writing across for __w__ tiles and down for __h__ attributes, + using __map_w__ as the rowstride for the source attribute map. + + The __x__ and __y__ parameters are in Source Attribute Map Attribute + coordinates. The location tiles will be written to on the + hardware Background Map is derived from those, but only uses + the lower 5 bits of each axis, for range of 0-15 (they are + bit-masked: `x & 0xF` and `y & 0xF`). As a result the two + coordinate systems are aligned together. + + In order to transfer tile map data in a way where the + coordinate systems are not aligned, an offset from the + Source Attribute Map pointer can be passed in: + `(map_ptr + x + (y * map_width))`. + + For example, if you want the tile id at `1,2` from the source map to + show up at `0,0` on the hardware Background Map (instead of at `1,2`) + then modify the pointer address that is passed in: + `map_ptr + 1 + (2 * map_width)` + + Use this instead of @ref set_bkg_tiles when the source map is wider than + 32 tiles or when writing a width that does not match the source map width. + + One byte per source attribute map entry. + + Writes that exceed coordinate 15/14 on the x / y axis will wrap around to + the Left and Top edges. + + See @ref set_bkg_tiles for setting CGB attribute maps with @ref VBK_REG. + + @see SHOW_BKG + @see set_bkg_data, set_bkg_tiles, set_win_submap, set_tiles +*/ +void set_bkg_submap_attributes_nes16x16(uint8_t x, uint8_t y, uint8_t w, uint8_t h, const uint8_t *map, uint8_t map_w) OLDCALL; + +/** Sets a rectangular area of the Background Tile Map attributes using + a sub-region from a source tile map. Useful for scrolling implementations + of maps larger than 32 x 30 tiles. + + Please note that this is just a wrapper function for set_bkg_submap_attributes_nes16x16 + and divides the coordinates and dimensions by 2 to achieve this. + It is intended to make code more portable by using the same coordinate system + that systems with the much more common 8x8 attribute resolution would use. + + @see SHOW_BKG + @see set_bkg_data, set_bkg_tiles, set_win_submap, set_tiles +*/ +inline void set_bkg_submap_attributes(uint8_t x, uint8_t y, uint8_t w, uint8_t h, const uint8_t *attributes, uint8_t map_w) +{ + set_bkg_submap_attributes_nes16x16(x >> 1, y >> 1, (w + 1) >> 1, (h + 1) >> 1, attributes, map_w >> 1); +} + extern uint8_t _map_tile_offset; diff --git a/gbdk-lib/libc/targets/mos6502/nes/Makefile b/gbdk-lib/libc/targets/mos6502/nes/Makefile @@ -11,7 +11,7 @@ ASSRC = f_ibm_full.s f_ibm_sh.s f_italic.s f_min.s f_spect.s \ font.s font_color.s set_data.s set_1bit_data.s color.s mode.s \ metasprites.s metasprites_hide.s metasprites_hide_spr.s \ set_bk_ts.s set_tile_submap.s fill_rect_bk.s \ - set_bk_attributes.s flush_attributes.s \ + set_bk_attributes.s set_tile_submap_attributes.s flush_attributes.s \ nes_palettes.s \ pad.s pad_ex.s \ rle_decompress.s \ diff --git a/gbdk-lib/libc/targets/mos6502/nes/global.s b/gbdk-lib/libc/targets/mos6502/nes/global.s @@ -23,6 +23,10 @@ PPU_NT0 = 0x2000 PPU_AT0 = 0x23C0 + ATTRIBUTE_WIDTH = 16 + ATTRIBUTE_HEIGHT = 15 + ATTRIBUTE_PACKED_WIDTH = 8 + ATTRIBUTE_PACKED_HEIGHT = 8 ATTRIBUTE_MASK_TL = 0b00000011 ATTRIBUTE_MASK_TR = 0b00001100 ATTRIBUTE_MASK_BL = 0b00110000 diff --git a/gbdk-lib/libc/targets/mos6502/nes/set_tile_submap_attributes.s b/gbdk-lib/libc/targets/mos6502/nes/set_tile_submap_attributes.s @@ -0,0 +1,404 @@ + .include "global.s" + + .area OSEG (PAG, OVR) + _set_bkg_submap_attributes_nes16x16_PARM_3:: .ds 1 + _set_bkg_submap_attributes_nes16x16_PARM_4:: .ds 1 + _set_bkg_submap_attributes_nes16x16_PARM_5:: .ds 2 + _set_bkg_submap_attributes_nes16x16_PARM_6:: .ds 1 + .xpos: .ds 1 + .ypos: .ds 1 + .num_rows: .ds 1 + .src_tiles: .ds 2 + .x_odd: .ds 1 + .y_odd: .ds 1 + .num_columns: .ds 1 + .attribute_mask_map: .ds 1 + .attribute_mask_shadow: .ds 1 + .row_shl_3: .ds 1 + + .area _HOME + +.macro INC_X_WITH_WRAP + txa + clc + adc #1 + and #ATTRIBUTE_PACKED_WIDTH-1 + ora *.row_shl_3 + tax +.endm + +.macro INC_XPOS_WITH_WRAP + ldx *.xpos + clc + adc #1 + and #ATTRIBUTE_PACKED_WIDTH-1 + stx *.xpos +.endm + +.macro INC_ROW_SRC + ; .src_tiles += width + lda *.map_width + clc + adc *.src_tiles + sta *.src_tiles + lda #0 + adc *.src_tiles+1 + sta *.src_tiles+1 +.endm + +.macro INC_Y_WITH_WRAP + txa + clc + adc #(1 << 3) + and #0x3F + tax + INC_ROW_SRC +.endm + +_set_bkg_submap_attributes_nes16x16:: + .define .width "_set_bkg_submap_attributes_nes16x16_PARM_3" + .define .height "_set_bkg_submap_attributes_nes16x16_PARM_4" + .define .tiles "_set_bkg_submap_attributes_nes16x16_PARM_5" + .define .map_width "_set_bkg_submap_attributes_nes16x16_PARM_6" + lsr + sta *.xpos + ror *.x_odd + ; Attribute map stores 8 rows of bytes with 2x2 attributes in each byte continuously. + ; -> 16 rows of attributes + ; But for alignment reasons, the last row only stores 2x1 (bottom-left / bottom-right unused) + ; This extra row must be skipped to remap input y position into a new attribute table Y position + ; Rather than using a division to compensate, we use the following rules: + ; Y = y / 16 + ; For each AT n already covered vertically: + ; - increase Y by +1 + ; - also increase Y by +1 to skip 1 row of 16x16 attributes (occurs 1 coordinate earlier each AT) + ; Or in code: + ; n = y >> 4 + ; Y = y + n + (y & 0xF >= 15 - n) ? 1 : 0 + txa + lsr + lsr + lsr + lsr + sta *.tmp + eor #0xF + sta *.tmp+1 + txa + and #0xF + cmp *.tmp+1 + txa + adc *.tmp + ; + lsr + sta *.ypos + ror *.y_odd + lda *.tiles + clc + adc *.xpos + sta *.src_tiles + lda *.tiles+1 + adc #0 + sta *.src_tiles+1 + lda *.map_width + lsr + sta *.map_width + ldx *.ypos + jsr __muluchar + clc + adc *.src_tiles + sta *.src_tiles + txa + adc *.src_tiles+1 + sta *.src_tiles+1 + ; xpos %= ATTRIBUTE_PACKED_WIDTH + lda *.xpos + and #ATTRIBUTE_PACKED_WIDTH-1 + sta *.xpos + ; ypos %= ATTRIBUTE_PACKED_HEIGHT + lda *.ypos + and #ATTRIBUTE_PACKED_HEIGHT-1 + sta *.ypos + ; Prefer vertical stripes if height > width + lda *.height + cmp *.width + bcc _set_bkg_submap_attributes_horizontalStripes + beq _set_bkg_submap_attributes_horizontalStripes + jmp _set_bkg_submap_attributes_verticalStripes +_set_bkg_submap_attributes_horizontalStripes: + lda *.height + sta *.num_rows +_set_bkg_submap_attributes_horizontalStripes_rowLoop: + ; + ldy *.ypos + lda .bitmask_table,y + ora *_attribute_row_dirty + sta *_attribute_row_dirty + ; + jsr .process_row + jsr .inc_row + dec *.num_rows + bne _set_bkg_submap_attributes_horizontalStripes_rowLoop + jmp _flush_shadow_attributes + +_set_bkg_submap_attributes_verticalStripes: + jsr .inc_height_if_wrap + lda *.width + sta *.num_columns + ldy #0 +_set_bkg_submap_attributes_verticalStripes_columnLoop: + ; + ldx *.xpos + lda .bitmask_table,x + ora *_attribute_column_dirty + sta *_attribute_column_dirty + ; + jsr .process_column + INC_XPOS_WITH_WRAP + iny + dec *.num_columns + bne _set_bkg_submap_attributes_verticalStripes_columnLoop + jmp _flush_shadow_attributes + +.inc_row: + lda *.ypos + cmp #ATTRIBUTE_PACKED_HEIGHT-1 + beq 2$ + lda *.y_odd + adc #0x80 + sta *.y_odd + bmi 1$ + lda *.ypos + clc + adc #1 + and #ATTRIBUTE_PACKED_HEIGHT-1 + sta *.ypos + bpl .inc_row_src +1$: + rts +2$: + ; Skip last 16x16 row of attribute table (empty due to alignment) + lda #0 + sta *.ypos +.inc_row_src: + INC_ROW_SRC + rts + +.process_row: + ; Write only top part of row + lda #ATTRIBUTE_MASK_TL+ATTRIBUTE_MASK_TR + bit *.y_odd + bpl 1$ + ; Shift mask to write only bottom part of row + asl + asl + asl + asl +1$: + sta *.attribute_mask_map + ; Invert mask + eor #0xFF + sta *.attribute_mask_shadow +; +; width even, x even -> No pre-loop, no post-loop +; -> num_columns = w/2, pre = 0, post = 0 +; width even, x odd -> Pre-loop writing right half, post-loop writing left half +; -> num_columns = w/2-1, pre = 1, post = 1 +; width odd, x even -> No Pre-loop, post-loop writing left half +; -> num_columns = w/2, pre = 0, post = 1 +; width odd, x odd -> Pre-loop writing right half,no post-loop +; -> num_columns = w/2, pre = 1, post = 0 +; width 1, x even -> Pre-loop only. (special case) +; -> num_columns = w/2, pre = 1, post = 0 +; +; +-----+-----+-----+ +; | X\W | 0 | 1 | +; +-----+-----+-----+ +; | 0 | 0/0 | 0/1 | +; +-----+-----+-----+ +; | 1 | 1/1 | 1/0 | +; +-----+-----+-----+ +; +; pre? = x_odd +; post? = x_odd XOR width_odd + lda *.width + lsr + sta *.num_columns + lda *.ypos + asl + asl + asl + sta *.row_shl_3 + lda *.xpos + ora *.row_shl_3 + tax + ldy #0 + bit *.x_odd + bpl 2$ + ; Do a partial update of only TR+BR for the first byte + lda *.attribute_mask_map + and #ATTRIBUTE_MASK_TR+ATTRIBUTE_MASK_BR + pha + and [*.src_tiles],y + iny + sta *.tmp + pla + eor #0xFF + and _attribute_shadow,x + ora *.tmp + sta _attribute_shadow,x + INC_X_WITH_WRAP +;;; + lda *.width + lsr + bcs 2$ +;;; + dec *.num_columns +2$: + lda *.num_columns + beq .column_loop_done + +.column_loop: + lda [*.src_tiles],y + iny + and *.attribute_mask_map + sta *.tmp + lda _attribute_shadow,x + and *.attribute_mask_shadow + ora *.tmp + sta _attribute_shadow,x + INC_X_WITH_WRAP + dec *.num_columns + bne .column_loop + ; +.column_loop_done: + lda *.width + lsr + ror + eor *.x_odd + bpl 1$ + ; We have one remaining half-column (16 pixels wide) + ; Do a partial update of only TL+BL for the last column + lda *.attribute_mask_map + and #ATTRIBUTE_MASK_TL+ATTRIBUTE_MASK_BL + pha + and [*.src_tiles],y + iny + sta *.tmp + pla + eor #0xFF + and _attribute_shadow,x + ora *.tmp + sta _attribute_shadow,x +1$: + rts + +.inc_height_if_wrap: + ; Aligned AT handling: if ypos will wrap from 14 -> 0, then height should be += 1 + lda *.y_odd + cmp #0x80 + lda *.ypos + rol + tax + clc + adc *.height + cmp #ATTRIBUTE_HEIGHT + bcc 3$ + inc *.height +3$: + txa + lsr + sta *.ypos + ror *.y_odd + rts + +.process_column: + ; Write only left part of column + lda #ATTRIBUTE_MASK_TL+ATTRIBUTE_MASK_BL + bit *.x_odd + bpl 1$ + ; Shift mask to write only right part of column + asl + asl +1$: + sta *.attribute_mask_map + ; Invert mask + eor #0xFF + sta *.attribute_mask_shadow + lda *.height + lsr + sta *.num_rows + lda *.ypos + asl + asl + asl + sta *.row_shl_3 + lda *.xpos + ora *.row_shl_3 + tax + bit *.y_odd + bpl 2$ + ; Do a partial update of only BL+BR for the first byte + lda *.attribute_mask_map + and #ATTRIBUTE_MASK_BL+ATTRIBUTE_MASK_BR + pha + and [*.src_tiles],y + sta *.tmp + pla + eor #0xFF + and _attribute_shadow,x + ora *.tmp + sta _attribute_shadow,x + INC_Y_WITH_WRAP +;;; + lda *.height + lsr + bcs 2$ +;;; + dec *.num_rows +2$: + lda *.num_rows + beq .row_loop_done + +.row_loop: + lda [*.src_tiles],y + and *.attribute_mask_map + sta *.tmp + lda _attribute_shadow,x + and *.attribute_mask_shadow + ora *.tmp + sta _attribute_shadow,x + INC_Y_WITH_WRAP + dec *.num_rows + bne .row_loop + ; +.row_loop_done: + lda *.height + lsr + ror + eor *.y_odd + bpl 1$ + ; We have one remaining half-row (16 pixels tall) + ; Do a partial update of only TL+TR for the last byte + lda *.attribute_mask_map + and #ATTRIBUTE_MASK_TL+ATTRIBUTE_MASK_TR + pha + and [*.src_tiles],y + iny + sta *.tmp + pla + eor #0xFF + and _attribute_shadow,x + ora *.tmp + sta _attribute_shadow,x +1$: + rts + +.bitmask_table: +.db 0b00000001 +.db 0b00000010 +.db 0b00000100 +.db 0b00001000 +.db 0b00010000 +.db 0b00100000 +.db 0b01000000 +.db 0b10000000

This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.