From 4ed4e6a94c7f9d98d50eec6eb013f4349e5fc63b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 6 Oct 2016 17:41:33 +0300 Subject: [PATCH 1/5] Implement some more instructions, and fix typos. Implement 16-bit shrl (new instruction). Implement movl with a 16-bit literal value. Add the 6502 ROR instruction. --- src/main.cpp | 39 +++++++++++++++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 1e37092..718e323 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -112,6 +112,7 @@ struct mos6502 : ASMLine php, plp, lsr, + ror, AND, inc, dec, @@ -148,6 +149,7 @@ struct mos6502 : ASMLine case OpCode::php: case OpCode::plp: case OpCode::lsr: + case OpCode::ror: case OpCode::AND: case OpCode::inc: case OpCode::dec: @@ -184,6 +186,7 @@ struct mos6502 : ASMLine case OpCode::php: case OpCode::plp: case OpCode::lsr: + case OpCode::ror: case OpCode::AND: case OpCode::inc: case OpCode::dec: @@ -248,6 +251,8 @@ struct mos6502 : ASMLine return "plp"; case OpCode::lsr: return "lsr"; + case OpCode::ror: + return "ror"; case OpCode::AND: return "and"; case OpCode::inc: @@ -318,6 +323,7 @@ struct i386 : ASMLine movzbl, movzwl, shrb, + shrl, xorl, andl, andb, @@ -363,6 +369,7 @@ struct i386 : ASMLine if (o == "movzwl") return OpCode::movzwl; if (o == "movzbl") return OpCode::movzbl; if (o == "shrb") return OpCode::shrb; + if (o == "shrl") return OpCode::shrl; if (o == "xorl") return OpCode::xorl; if (o == "andl") return OpCode::andl; if (o == "ret") return OpCode::ret; @@ -475,6 +482,11 @@ void translate_instruction(std::vector &instructions, const i386::OpCod instructions.emplace_back(mos6502::OpCode::sta, get_register(o2.reg_num)); instructions.emplace_back(mos6502::OpCode::lda, get_register(o1.reg_num, 1)); instructions.emplace_back(mos6502::OpCode::sta, get_register(o2.reg_num, 1)); + } else if (o1.type == Operand::Type::literal && o2.type == Operand::Type::reg) { + instructions.emplace_back(mos6502::OpCode::lda, Operand(o1.type, "#<" + o1.value)); + instructions.emplace_back(mos6502::OpCode::sta, get_register(o2.reg_num)); + instructions.emplace_back(mos6502::OpCode::lda, Operand(o1.type, "#>" + o1.value)); + instructions.emplace_back(mos6502::OpCode::sta, get_register(o2.reg_num, 1)); } else { throw std::runtime_error("Cannot translate movl instruction"); } @@ -486,7 +498,7 @@ void translate_instruction(std::vector &instructions, const i386::OpCod instructions.emplace_back(mos6502::OpCode::sta, get_register(o2.reg_num)); instructions.emplace_back(mos6502::OpCode::sta, get_register(o2.reg_num, 1)); } else { - throw std::runtime_error("Cannot translate movl instruction"); + throw std::runtime_error("Cannot translate xorl instruction"); } break; case i386::OpCode::movb: @@ -550,6 +562,25 @@ void translate_instruction(std::vector &instructions, const i386::OpCod throw std::runtime_error("Cannot translate shrb instruction"); } break; + case i386::OpCode::shrl: + if (o1.type == Operand::Type::reg || o2.type == Operand::Type::reg) { + const auto do_shift = [&instructions](const int reg_num) { + instructions.emplace_back(mos6502::OpCode::lsr, get_register(reg_num, 1)); + instructions.emplace_back(mos6502::OpCode::ror, get_register(reg_num)); + }; + + if (o1.type == Operand::Type::literal) { + const auto count = parse_8bit_literal(o1.value); + for (int i = 0; i < count; ++i) { + do_shift(o2.reg_num); + } + } else { + do_shift(o1.reg_num); + } + } else { + throw std::runtime_error("Cannot translate shrl instruction"); + } + break; case i386::OpCode::testb: if (o1.type == Operand::Type::reg && o2.type == Operand::Type::reg && o1.reg_num == o2.reg_num) { // this just tests the register for 0 @@ -625,7 +656,7 @@ void translate_instruction(std::vector &instructions, const i386::OpCod instructions.emplace_back(mos6502::OpCode::lda, get_register(o2.reg_num)); instructions.emplace_back(mos6502::OpCode::cmp, Operand(o1.type, fixup_8bit_literal(o1.value))); } else { - throw std::runtime_error("Cannot translate cmb instruction"); + throw std::runtime_error("Cannot translate cmpb instruction"); } break; case i386::OpCode::andb: @@ -684,7 +715,7 @@ void translate_instruction(std::vector &instructions, const i386::OpCod instructions.emplace_back(mos6502::OpCode::lda, get_register(o1.reg_num, 1)); instructions.emplace_back(mos6502::OpCode::pha); } else { - throw std::runtime_error("Cannot translate sbb instruction"); + throw std::runtime_error("Cannot translate pushl instruction"); } break; @@ -701,7 +732,7 @@ void translate_instruction(std::vector &instructions, const i386::OpCod instructions.emplace_back(mos6502::OpCode::eor, Operand(Operand::Type::literal, "#$ff")); // invert the bits instructions.emplace_back(mos6502::OpCode::sta, get_register(o2.reg_num)); // place the value } else { - throw std::runtime_error("Cannot translate sbb instruction"); + throw std::runtime_error("Cannot translate sbbb instruction"); } break; From 504d13a5272a3d14b2ade5c67f08a65d06c12fcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Thu, 6 Oct 2016 17:46:33 +0300 Subject: [PATCH 2/5] Initialize the sprite data at compilation time, not at runtime. VIC_II::SPRITE_ALIGNMENT: The MOS 6566/6567/6569 VIC-II expects sprite data to be aligned at 64 bytes. VIC_II::SPRITE_STARTING_BANK: Remove. VIC_II::write_multi_color_pixel(), VIC_II::write_pixel(): Make static. Write to the specified memory address. VIC_II::Sprite::Sprite(): Refactored from VIC_II::make_sprite(). VIC_II::enable_sprite(): Take a Sprite reference. sBall, sBat: Sprite images, declared at global scope. FIXME: How to ensure that the VIC_II::Sprite addresses do not end up in the range 0x1000..0x1fff or 0x9000..0x9fff (where the character generator ROM is overriding RAM)? Any portable alternative to using GCC-style __attribute__((section("sprites"))) and a linker script? Note: Declaring the sprite images as static const objects in main() would cause clang++4.0-svn279916-1 to generate code for initializing them. So, we will declare the objects in the global scope. This has been tested on a Commodore 64 with the following steps: clang++-4.0 -m32 -O3 -std=c++1z -S pong.cpp x86-to-6502 < pong.s > pong.asm edit pong.asm to define code start at 0x900 and to adapt the output invoke some 6502 assembler SYS2304 to run the output on a Commodore 64 --- examples/pong.cpp | 162 ++++++++++++++++++++++++---------------------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/examples/pong.cpp b/examples/pong.cpp index a42fde7..499d7d9 100644 --- a/examples/pong.cpp +++ b/examples/pong.cpp @@ -121,10 +121,10 @@ namespace { struct VIC_II { + static constexpr uint8_t SPRITE_ALIGNMENT = 64; static constexpr uint16_t SPRITE_DATA_POINTERS = 2040; static constexpr uint16_t VIDEO_REGISTERS = 53248; static constexpr uint16_t VIDEO_MEMORY = 1024; - static constexpr uint8_t SPRITE_STARTING_BANK = 192; static constexpr uint16_t BORDER_COLOR = 53280; static constexpr uint16_t BACKGROUND_COLOR = 53281; static constexpr uint16_t SPRITE_POSITION_REGISTERS = VIDEO_REGISTERS; @@ -191,52 +191,63 @@ namespace { return Frame(*this, p1, p2); } - - void write_multi_color_pixel(uint16_t) + + static void write_multi_color_pixel(uint8_t*) { // 0th case } - void write_pixel(uint16_t) + static void write_pixel(uint8_t*) { // 0th case } template - void write_multi_color_pixel(uint16_t loc, uint8_t d1, uint8_t d2, - uint8_t d3, uint8_t d4, D ... d) + static void write_multi_color_pixel(uint8_t* mem, uint8_t d1, uint8_t d2, + uint8_t d3, uint8_t d4, D ... d) { - memory(loc) = (d1 << 6) | (d2 << 4) | (d3 << 2) | d4; - write_multi_color_pixel(loc + 1, d...); + *mem = (d1 << 6) | (d2 << 4) | (d3 << 2) | d4; + write_multi_color_pixel(mem + 1, d...); } template - void write_pixel(uint16_t loc, bool d1, bool d2, bool d3, bool d4, - bool d5, bool d6, bool d7, bool d8, D ... d) + static void write_pixel(uint8_t *mem, bool d1, bool d2, bool d3, bool d4, + bool d5, bool d6, bool d7, bool d8, D ... d) { - memory(loc) = (d1 << 7) | (d2 << 6) | (d3 << 5) | (d4 << 4) | (d5 << 3) | (d6 << 2) | (d7 << 1) | d8; - write_pixel(loc + 1, d...); + *mem = (d1 << 7) | (d2 << 6) | (d3 << 5) | (d4 << 4) | (d5 << 3) | (d6 << 2) | (d7 << 1) | d8; + write_pixel(mem + 1, d...); } - template - void make_sprite(uint8_t memory_loc, D ... d) - { - if constexpr(sizeof...(d) == 12 * 21) { - write_multi_color_pixel((SPRITE_STARTING_BANK + memory_loc) * 64, d...); - } else { - write_pixel((SPRITE_STARTING_BANK + memory_loc) * 64, d...); + struct Sprite { + alignas(SPRITE_ALIGNMENT) uint8_t memory[63]; + template + Sprite(D ... d) + { + if constexpr(sizeof...(d) == 12 * 21) { + write_multi_color_pixel(memory, d...); + } else { + write_pixel(memory, d...); + } } - } + }; /// /// New Code /// - void enable_sprite(const uint8_t sprite_number, const uint8_t memory_loc, + void enable_sprite(const uint8_t sprite_number, const Sprite& bitmap, const bool multicolor, const bool low_priority, const bool double_width, const bool double_height) { - memory(SPRITE_DATA_POINTERS + sprite_number) - = SPRITE_STARTING_BANK + memory_loc; +#if 0 // error: static_assert expression is not an integral constant + static_assert((std::ptrdiff_t(bitmap.memory) & 0x7000) != 0x1000, + "The addresses 0x1000 to 0x1fff and 0x9000 to 0x9fff" + "point to the character generator ROM, not RAM."); + static_assert(std::ptrdiff_t(bitmap.memory) < 0x4000, + "The data must be within the first (default)" + " 16KiB VIC-II bank."); +#endif + memory(SPRITE_DATA_POINTERS + sprite_number) + = (std::ptrdiff_t(bitmap.memory) & 0x3fff) / SPRITE_ALIGNMENT; set_bit(SPRITE_ENABLE_BITS, sprite_number, true); set_bit(SPRITE_EXPAND_HORIZONTAL, sprite_number, double_width); set_bit(SPRITE_EXPAND_VERTICAL, sprite_number, double_height); @@ -280,8 +291,56 @@ namespace { } +/// The ball image. This has to be declared const in the global scope, +/// or otherwise the data will not be initialized at compilation time. +const VIC_II::Sprite sBall( + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0, + 0,0,0,0,0,0,1,1,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, + 0,0,0,0,0,1,1,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0, + 0,0,0,0,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0, + 0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0, + 0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0, + 0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0, + 0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, + 0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0, + 0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0, + 0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0, + 0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0, + 0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, + 0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + ); - +/// The bat image. +const VIC_II::Sprite sBat( + 0,0,0,0,0,2,2,0,0,0,0,0, + 0,0,0,0,0,2,2,0,0,0,0,0, + 0,0,0,0,0,2,2,0,0,0,0,0, + 0,0,0,0,0,2,2,0,0,0,0,0, + 0,0,0,0,0,2,2,0,0,0,0,0, + 0,0,0,0,0,2,2,0,0,0,0,0, + 0,0,0,0,0,2,2,0,0,0,0,0, + 0,0,0,0,0,2,2,0,0,0,0,0, + 0,0,0,0,0,2,2,0,0,0,0,0, + 0,0,0,0,0,2,2,0,0,0,0,0, + 0,0,0,0,0,2,2,0,0,0,0,0, + 0,0,0,0,0,2,2,0,0,0,0,0, + 0,0,0,0,0,2,2,0,0,0,0,0, + 0,0,0,0,0,2,2,0,0,0,0,0, + 0,0,0,0,0,2,2,0,0,0,0,0, + 0,0,0,0,0,2,2,0,0,0,0,0, + 0,0,0,0,0,3,3,0,0,0,0,0, + 0,0,0,0,0,1,1,0,0,0,0,0, + 0,0,0,0,0,3,3,0,0,0,0,0, + 0,0,0,0,0,1,1,0,0,0,0,0, + 0,0,0,0,0,3,3,0,0,0,0,0 + ); int main() { @@ -306,58 +365,9 @@ int main() VIC_II vic; - vic.make_sprite(0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0, - 0,0,0,0,0,0,1,1,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, - 0,0,0,0,0,1,1,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0, - 0,0,0,0,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0, - 0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0, - 0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0, - 0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0, - 0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, - 0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0, - 0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0, - 0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0, - 0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0, - 0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, - 0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 - ); - - vic.make_sprite(1, - 0,0,0,0,0,2,2,0,0,0,0,0, - 0,0,0,0,0,2,2,0,0,0,0,0, - 0,0,0,0,0,2,2,0,0,0,0,0, - 0,0,0,0,0,2,2,0,0,0,0,0, - 0,0,0,0,0,2,2,0,0,0,0,0, - 0,0,0,0,0,2,2,0,0,0,0,0, - 0,0,0,0,0,2,2,0,0,0,0,0, - 0,0,0,0,0,2,2,0,0,0,0,0, - 0,0,0,0,0,2,2,0,0,0,0,0, - 0,0,0,0,0,2,2,0,0,0,0,0, - 0,0,0,0,0,2,2,0,0,0,0,0, - 0,0,0,0,0,2,2,0,0,0,0,0, - 0,0,0,0,0,2,2,0,0,0,0,0, - 0,0,0,0,0,2,2,0,0,0,0,0, - 0,0,0,0,0,2,2,0,0,0,0,0, - 0,0,0,0,0,2,2,0,0,0,0,0, - 0,0,0,0,0,3,3,0,0,0,0,0, - 0,0,0,0,0,1,1,0,0,0,0,0, - 0,0,0,0,0,3,3,0,0,0,0,0, - 0,0,0,0,0,1,1,0,0,0,0,0, - 0,0,0,0,0,3,3,0,0,0,0,0 - ); - - vic.enable_sprite(0, 0, false, true, false, false); - vic.enable_sprite(1, 1, true, false, false, true); - vic.enable_sprite(2, 1, true, false, false, true); - + vic.enable_sprite(0, sBall, false, true, false, false); + vic.enable_sprite(1, sBat, true, false, false, true); + vic.enable_sprite(2, sBat, true, false, false, true); vic.border() = vic.nearest_color<128,128,128>(colors).num; // 50% grey vic.background() = vic.nearest_color<0,0,0>(colors).num; // black vic.sprite_1_color() = vic.nearest_color<255,0,0>(colors).num; // red From 24df091a6f38aedbe9a102675972ea729cfc870f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Sun, 9 Oct 2016 18:03:30 +0300 Subject: [PATCH 3/5] Introduce VIC_II::HighResSprite and VIC_II::MultiColorSprite. write_multi_color_line(): Replaces write_multi_color_pixel(). write_line(): Replaces write_pixel(). The generated code is not affected by this change. --- examples/pong.cpp | 169 ++++++++++++++++++++++++++++------------------ 1 file changed, 105 insertions(+), 64 deletions(-) diff --git a/examples/pong.cpp b/examples/pong.cpp index 499d7d9..2d66523 100644 --- a/examples/pong.cpp +++ b/examples/pong.cpp @@ -192,42 +192,66 @@ namespace { return Frame(*this, p1, p2); } - static void write_multi_color_pixel(uint8_t*) + static void write_multi_color_line(uint8_t*) { // 0th case } - static void write_pixel(uint8_t*) + static void write_line(uint8_t*) { // 0th case } - template - static void write_multi_color_pixel(uint8_t* mem, uint8_t d1, uint8_t d2, - uint8_t d3, uint8_t d4, D ... d) + static constexpr uint8_t multi_color_byte(uint_least16_t bits) { - *mem = (d1 << 6) | (d2 << 4) | (d3 << 2) | d4; - write_multi_color_pixel(mem + 1, d...); + // 0aa0bb0cc0dd -> 0aabbccdd + return + (bits & 3) | + (bits >> 1 & 3 << 2) | + (bits >> 2 & 3 << 4) | + (bits >> 3 & 3 << 6); } template - static void write_pixel(uint8_t *mem, bool d1, bool d2, bool d3, bool d4, - bool d5, bool d6, bool d7, bool d8, D ... d) + static void write_multi_color_line(uint8_t* mem, uint_least64_t l, + D ... d) { - *mem = (d1 << 7) | (d2 << 6) | (d3 << 5) | (d4 << 4) | (d5 << 3) | (d6 << 2) | (d7 << 1) | d8; - write_pixel(mem + 1, d...); + mem[0] = multi_color_byte(uint_least16_t(l >> 24)); + mem[1] = multi_color_byte(uint_least16_t(l >> 12)); + mem[2] = multi_color_byte(uint_least16_t(l)); + + write_multi_color_line(mem + 3, d...); + } + + template + static void write_line(uint8_t *mem, uint_least32_t l, D ... d) + { + mem[0] = uint8_t(l >> 16); + mem[1] = uint8_t(l >> 8); + mem[2] = uint8_t(l); + write_line(mem + 3, d...); } struct Sprite { alignas(SPRITE_ALIGNMENT) uint8_t memory[63]; + }; + + struct HighResSprite : public Sprite + { template - Sprite(D ... d) + HighResSprite(D ... d) : Sprite() { - if constexpr(sizeof...(d) == 12 * 21) { - write_multi_color_pixel(memory, d...); - } else { - write_pixel(memory, d...); - } + static_assert(sizeof...(d) == 21); + write_line(memory, d...); + } + }; + + struct MultiColorSprite : public Sprite + { + template + MultiColorSprite(D ... d) : Sprite() + { + write_multi_color_line(memory, d...); } }; @@ -255,6 +279,23 @@ namespace { set_bit(SPRITE_PRIORITY, sprite_number, low_priority); } + void enable_sprite(const uint8_t sprite_number, + const HighResSprite& bitmap, + const bool low_priority, + const bool double_width, const bool double_height) + { + enable_sprite(sprite_number, bitmap, false, low_priority, + double_width, double_height); + } + void enable_sprite(const uint8_t sprite_number, + const MultiColorSprite& bitmap, + const bool low_priority, + const bool double_width, const bool double_height) + { + enable_sprite(sprite_number, bitmap, true, low_priority, + double_width, double_height); + } + auto sprite_collisions() { const auto collisions = memory(SPRITE_COLLISIONS); @@ -293,53 +334,53 @@ namespace { /// The ball image. This has to be declared const in the global scope, /// or otherwise the data will not be initialized at compilation time. -const VIC_II::Sprite sBall( - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0, - 0,0,0,0,0,0,1,1,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, - 0,0,0,0,0,1,1,0,0,0,1,1,1,1,1,1,1,1,1,0,0,0,0,0, - 0,0,0,0,0,1,1,1,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0, - 0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0, - 0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0, - 0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0, - 0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, - 0,0,0,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0,0, - 0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0, - 0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0, - 0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0, - 0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, - 0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 +const VIC_II::HighResSprite sBall( + 0b00000000000000000000000, + 0b00000000000000000000000, + 0b00000000111111000000000, + 0b00000011111111110000000, + 0b00000110111111111000000, + 0b00001100011111111100000, + 0b00001110111111111100000, + 0b00011111111111111110000, + 0b00011111111111111110000, + 0b00011111111111111110000, + 0b00000111111111111000000, + 0b00011000000000000110000, + 0b00011111111111111110000, + 0b00001111111111111100000, + 0b00001111111111111100000, + 0b00000111111111111000000, + 0b00000011111111110000000, + 0b00000000111111000000000, + 0b00000000000000000000000, + 0b00000000000000000000000, + 0b00000000000000000000000 ); /// The bat image. -const VIC_II::Sprite sBat( - 0,0,0,0,0,2,2,0,0,0,0,0, - 0,0,0,0,0,2,2,0,0,0,0,0, - 0,0,0,0,0,2,2,0,0,0,0,0, - 0,0,0,0,0,2,2,0,0,0,0,0, - 0,0,0,0,0,2,2,0,0,0,0,0, - 0,0,0,0,0,2,2,0,0,0,0,0, - 0,0,0,0,0,2,2,0,0,0,0,0, - 0,0,0,0,0,2,2,0,0,0,0,0, - 0,0,0,0,0,2,2,0,0,0,0,0, - 0,0,0,0,0,2,2,0,0,0,0,0, - 0,0,0,0,0,2,2,0,0,0,0,0, - 0,0,0,0,0,2,2,0,0,0,0,0, - 0,0,0,0,0,2,2,0,0,0,0,0, - 0,0,0,0,0,2,2,0,0,0,0,0, - 0,0,0,0,0,2,2,0,0,0,0,0, - 0,0,0,0,0,2,2,0,0,0,0,0, - 0,0,0,0,0,3,3,0,0,0,0,0, - 0,0,0,0,0,1,1,0,0,0,0,0, - 0,0,0,0,0,3,3,0,0,0,0,0, - 0,0,0,0,0,1,1,0,0,0,0,0, - 0,0,0,0,0,3,3,0,0,0,0,0 +const VIC_II::MultiColorSprite sBat( + 0000002200000, + 0000002200000, + 0000002200000, + 0000002200000, + 0000002200000, + 0000002200000, + 0000002200000, + 0000002200000, + 0000002200000, + 0000002200000, + 0000002200000, + 0000002200000, + 0000002200000, + 0000002200000, + 0000002200000, + 0000002200000, + 0000003300000, + 0000001100000, + 0000003300000, + 0000001100000, + 0000003300000 ); int main() @@ -365,9 +406,9 @@ int main() VIC_II vic; - vic.enable_sprite(0, sBall, false, true, false, false); - vic.enable_sprite(1, sBat, true, false, false, true); - vic.enable_sprite(2, sBat, true, false, false, true); + vic.enable_sprite(0, sBall, true, false, false); + vic.enable_sprite(1, sBat, false, false, true); + vic.enable_sprite(2, sBat, false, false, true); vic.border() = vic.nearest_color<128,128,128>(colors).num; // 50% grey vic.background() = vic.nearest_color<0,0,0>(colors).num; // black vic.sprite_1_color() = vic.nearest_color<255,0,0>(colors).num; // red From ee36c4c08f4021b5f6fc094ee0e3fcc404fc4f35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Sun, 9 Oct 2016 20:56:31 +0300 Subject: [PATCH 4/5] Define a constexpr VIC_II::Sprite constructor. write_multi_color_line(), write_line(): Remove. VIC_II::SpriteLine: A line of sprite bitmap data. The constructor converts one line of data at a time. VIC_II::Sprite: An array of SpriteLine. This change does not affect the generated binary code. In the assembler code generated by clang++-4.0, the binary data will be formatted in groups of 3 bytes instead of 3*21 bytes. --- examples/pong.cpp | 203 ++++++++++++++++++---------------------------- 1 file changed, 81 insertions(+), 122 deletions(-) diff --git a/examples/pong.cpp b/examples/pong.cpp index 2d66523..e60c42b 100644 --- a/examples/pong.cpp +++ b/examples/pong.cpp @@ -192,74 +192,49 @@ namespace { return Frame(*this, p1, p2); } - static void write_multi_color_line(uint8_t*) - { - // 0th case - } + template + struct SpriteLine { + uint8_t pixels[3]; - static void write_line(uint8_t*) - { - // 0th case - } + typedef uint_least64_t numeric_t; - static constexpr uint8_t multi_color_byte(uint_least16_t bits) - { - // 0aa0bb0cc0dd -> 0aabbccdd - return - (bits & 3) | - (bits >> 1 & 3 << 2) | - (bits >> 2 & 3 << 4) | - (bits >> 3 & 3 << 6); - } + /// Pixel format converter. + static constexpr uint8_t c(const numeric_t m) + { + if constexpr (multicolor) + // 0b00ii00jj00kk00ll -> 0biijjkkll + return uint8_t(m >> 0 & 3 << 0) | + uint8_t(m >> 2 & 3 << 2) | + uint8_t(m >> 4 & 3 << 4) | + uint8_t(m >> 6 & 3 << 6); + return uint8_t(m); + } - template - static void write_multi_color_line(uint8_t* mem, uint_least64_t l, - D ... d) - { - mem[0] = multi_color_byte(uint_least16_t(l >> 24)); - mem[1] = multi_color_byte(uint_least16_t(l >> 12)); - mem[2] = multi_color_byte(uint_least16_t(l)); + /// Input bits per byte. + constexpr static auto ibb = multicolor ? 16 : 8; - write_multi_color_line(mem + 3, d...); - } - - template - static void write_line(uint8_t *mem, uint_least32_t l, D ... d) - { - mem[0] = uint8_t(l >> 16); - mem[1] = uint8_t(l >> 8); - mem[2] = uint8_t(l); - write_line(mem + 3, d...); - } + /// Constructor. + constexpr SpriteLine(const numeric_t line) + : pixels{c(line >> (2 * ibb)), c(line >> ibb), c(line)} + {} + }; + template struct Sprite { - alignas(SPRITE_ALIGNMENT) uint8_t memory[63]; + typedef SpriteLine Line; + alignas(SPRITE_ALIGNMENT) Line lines[SPRITE_ALIGNMENT / sizeof(Line)]; }; - struct HighResSprite : public Sprite - { - template - HighResSprite(D ... d) : Sprite() - { - static_assert(sizeof...(d) == 21); - write_line(memory, d...); - } - }; - - struct MultiColorSprite : public Sprite - { - template - MultiColorSprite(D ... d) : Sprite() - { - write_multi_color_line(memory, d...); - } - }; + typedef Sprite HighResSprite; + typedef Sprite MultiColorSprite; /// /// New Code /// - void enable_sprite(const uint8_t sprite_number, const Sprite& bitmap, - const bool multicolor, const bool low_priority, + template + void enable_sprite(const uint8_t sprite_number, + const Sprite& bitmap, + const bool low_priority, const bool double_width, const bool double_height) { #if 0 // error: static_assert expression is not an integral constant @@ -271,7 +246,7 @@ namespace { " 16KiB VIC-II bank."); #endif memory(SPRITE_DATA_POINTERS + sprite_number) - = (std::ptrdiff_t(bitmap.memory) & 0x3fff) / SPRITE_ALIGNMENT; + = (std::ptrdiff_t(bitmap.lines) & 0x3fff) / SPRITE_ALIGNMENT; set_bit(SPRITE_ENABLE_BITS, sprite_number, true); set_bit(SPRITE_EXPAND_HORIZONTAL, sprite_number, double_width); set_bit(SPRITE_EXPAND_VERTICAL, sprite_number, double_height); @@ -279,23 +254,6 @@ namespace { set_bit(SPRITE_PRIORITY, sprite_number, low_priority); } - void enable_sprite(const uint8_t sprite_number, - const HighResSprite& bitmap, - const bool low_priority, - const bool double_width, const bool double_height) - { - enable_sprite(sprite_number, bitmap, false, low_priority, - double_width, double_height); - } - void enable_sprite(const uint8_t sprite_number, - const MultiColorSprite& bitmap, - const bool low_priority, - const bool double_width, const bool double_height) - { - enable_sprite(sprite_number, bitmap, true, low_priority, - double_width, double_height); - } - auto sprite_collisions() { const auto collisions = memory(SPRITE_COLLISIONS); @@ -332,56 +290,57 @@ namespace { } -/// The ball image. This has to be declared const in the global scope, -/// or otherwise the data will not be initialized at compilation time. -const VIC_II::HighResSprite sBall( - 0b00000000000000000000000, - 0b00000000000000000000000, - 0b00000000111111000000000, - 0b00000011111111110000000, - 0b00000110111111111000000, - 0b00001100011111111100000, - 0b00001110111111111100000, - 0b00011111111111111110000, - 0b00011111111111111110000, - 0b00011111111111111110000, - 0b00000111111111111000000, - 0b00011000000000000110000, - 0b00011111111111111110000, - 0b00001111111111111100000, - 0b00001111111111111100000, - 0b00000111111111111000000, - 0b00000011111111110000000, - 0b00000000111111000000000, - 0b00000000000000000000000, - 0b00000000000000000000000, - 0b00000000000000000000000 - ); +/// The ball image. +static const VIC_II::HighResSprite sBall +{{ + 0b000000000000000000000000, + 0b000000000000000000000000, + 0b000000000111111000000000, + 0b000000011111111110000000, + 0b000000110111111111000000, + 0b000001100011111111100000, + 0b000001110111111111100000, + 0b000011111111111111110000, + 0b000011111111111111110000, + 0b000011111111111111110000, + 0b000000111111111111000000, + 0b000011000000000000110000, + 0b000011111111111111110000, + 0b000001111111111111100000, + 0b000001111111111111100000, + 0b000000111111111111000000, + 0b000000011111111110000000, + 0b000000000111111000000000, + 0b000000000000000000000000, + 0b000000000000000000000000, + 0b000000000000000000000000 +}}; /// The bat image. -const VIC_II::MultiColorSprite sBat( - 0000002200000, - 0000002200000, - 0000002200000, - 0000002200000, - 0000002200000, - 0000002200000, - 0000002200000, - 0000002200000, - 0000002200000, - 0000002200000, - 0000002200000, - 0000002200000, - 0000002200000, - 0000002200000, - 0000002200000, - 0000002200000, - 0000003300000, - 0000001100000, - 0000003300000, - 0000001100000, - 0000003300000 - ); +static const VIC_II::MultiColorSprite sBat +{{ + 0x000002200000, + 0x000002200000, + 0x000002200000, + 0x000002200000, + 0x000002200000, + 0x000002200000, + 0x000002200000, + 0x000002200000, + 0x000002200000, + 0x000002200000, + 0x000002200000, + 0x000002200000, + 0x000002200000, + 0x000002200000, + 0x000002200000, + 0x000002200000, + 0x000003300000, + 0x000001100000, + 0x000003300000, + 0x000001100000, + 0x000003300000 +}}; int main() { From 617e11244e7a19c368a237520a0dad045df939fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20M=C3=A4kel=C3=A4?= Date: Sun, 9 Oct 2016 21:05:01 +0300 Subject: [PATCH 5/5] Move the sprite declarations to the main() function. This change does not affect the generated code, other than the assembler labels for the data. The data will still be initialized at compilation time. --- examples/pong.cpp | 103 +++++++++++++++++++++++----------------------- 1 file changed, 51 insertions(+), 52 deletions(-) diff --git a/examples/pong.cpp b/examples/pong.cpp index e60c42b..1efba7a 100644 --- a/examples/pong.cpp +++ b/examples/pong.cpp @@ -290,58 +290,6 @@ namespace { } -/// The ball image. -static const VIC_II::HighResSprite sBall -{{ - 0b000000000000000000000000, - 0b000000000000000000000000, - 0b000000000111111000000000, - 0b000000011111111110000000, - 0b000000110111111111000000, - 0b000001100011111111100000, - 0b000001110111111111100000, - 0b000011111111111111110000, - 0b000011111111111111110000, - 0b000011111111111111110000, - 0b000000111111111111000000, - 0b000011000000000000110000, - 0b000011111111111111110000, - 0b000001111111111111100000, - 0b000001111111111111100000, - 0b000000111111111111000000, - 0b000000011111111110000000, - 0b000000000111111000000000, - 0b000000000000000000000000, - 0b000000000000000000000000, - 0b000000000000000000000000 -}}; - -/// The bat image. -static const VIC_II::MultiColorSprite sBat -{{ - 0x000002200000, - 0x000002200000, - 0x000002200000, - 0x000002200000, - 0x000002200000, - 0x000002200000, - 0x000002200000, - 0x000002200000, - 0x000002200000, - 0x000002200000, - 0x000002200000, - 0x000002200000, - 0x000002200000, - 0x000002200000, - 0x000002200000, - 0x000002200000, - 0x000003300000, - 0x000001100000, - 0x000003300000, - 0x000001100000, - 0x000003300000 -}}; - int main() { const std::array colors = {{ @@ -365,6 +313,57 @@ int main() VIC_II vic; + /// The ball image. + static const VIC_II::HighResSprite sBall + {{ + 0b000000000000000000000000, + 0b000000000000000000000000, + 0b000000000111111000000000, + 0b000000011111111110000000, + 0b000000110111111111000000, + 0b000001100011111111100000, + 0b000001110111111111100000, + 0b000011111111111111110000, + 0b000011111111111111110000, + 0b000011111111111111110000, + 0b000000111111111111000000, + 0b000011000000000000110000, + 0b000011111111111111110000, + 0b000001111111111111100000, + 0b000001111111111111100000, + 0b000000111111111111000000, + 0b000000011111111110000000, + 0b000000000111111000000000, + 0b000000000000000000000000, + 0b000000000000000000000000, + 0b000000000000000000000000 + }}; + + /// The bat image. + static const VIC_II::MultiColorSprite sBat = {{ + 0x000002200000, + 0x000002200000, + 0x000002200000, + 0x000002200000, + 0x000002200000, + 0x000002200000, + 0x000002200000, + 0x000002200000, + 0x000002200000, + 0x000002200000, + 0x000002200000, + 0x000002200000, + 0x000002200000, + 0x000002200000, + 0x000002200000, + 0x000002200000, + 0x000003300000, + 0x000001100000, + 0x000003300000, + 0x000001100000, + 0x000003300000 + }}; + vic.enable_sprite(0, sBall, true, false, false); vic.enable_sprite(1, sBat, false, false, true); vic.enable_sprite(2, sBat, false, false, true);