1
0
mirror of https://github.com/lefticus/6502-cpp.git synced 2024-06-09 20:29:28 +00:00

Reorganize file structure

This commit is contained in:
Jason Turner 2016-08-24 16:55:08 -06:00
parent 74b2f129bf
commit e3ebc954fa
4 changed files with 211 additions and 442 deletions

View File

@ -11,12 +11,32 @@ constexpr uint16_t SPRITE_EXPAND_VERTICAL = VIDEO_REGISTERS + 23;
constexpr uint16_t SPRITE_POSITION_REGISTERS = VIDEO_REGISTERS;
constexpr uint16_t SPRITE_COLLISIONS = VIDEO_REGISTERS + 30;
constexpr uint16_t SPRITE_MULTICOLOR = VIDEO_REGISTERS + 28;
constexpr uint16_t SPRITE_PRIORITY = VIDEO_REGISTERS + 27;
constexpr uint16_t VIDEO_MEMORY = 1024;
constexpr uint8_t SPRITE_STARTING_BANK = 192;
constexpr uint16_t BORDER_COLOR = 53280;
constexpr uint16_t BACKGROUND_COLOR = 53281;
constexpr uint16_t SPRITE_0_COLOR = VIDEO_REGISTERS + 39;
constexpr uint16_t SPRITE_1_COLOR = SPRITE_0_COLOR + 1;
constexpr uint16_t SPRITE_2_COLOR = SPRITE_1_COLOR + 1;
constexpr uint16_t SPRITE_3_COLOR = SPRITE_2_COLOR + 1;
constexpr uint16_t SPRITE_4_COLOR = SPRITE_3_COLOR + 1;
constexpr uint16_t SPRITE_5_COLOR = SPRITE_4_COLOR + 1;
constexpr uint16_t SPRITE_6_COLOR = SPRITE_5_COLOR + 1;
constexpr uint16_t SPRITE_7_COLOR = SPRITE_6_COLOR + 1;
namespace {
struct Color
{
uint8_t num;
uint8_t r;
uint8_t g;
uint8_t b;
};
volatile uint8_t &memory(const uint16_t loc)
{
return *reinterpret_cast<uint8_t *>(loc);
@ -71,14 +91,15 @@ template<typename ... D>
}
void enable_sprite(const uint8_t sprite_number, const uint8_t memory_loc,
const bool multicolor,
const bool multicolor, const bool low_priority,
const bool double_width, const bool double_height)
{
set_bit(SPRITE_ENABLE_BITS, sprite_number, true);
memory(SPRITE_DATA_POINTERS + sprite_number) = SPRITE_STARTING_BANK + memory_loc;
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);
set_bit(SPRITE_MULTICOLOR, sprite_number, multicolor);
set_bit(SPRITE_PRIORITY, sprite_number, low_priority);
}
void display(uint8_t x, uint8_t y, uint8_t val)
@ -102,6 +123,11 @@ auto joystick(const uint8_t port) {
bool left;
bool right;
bool fire;
auto direction_vector() const
{
return std::make_pair(left?-1:(right?1:0), up?-1:(down?1:0));
}
};
if (port == 2) {
@ -122,10 +148,51 @@ volatile uint8_t &sprite_y(const uint8_t sprite_num)
return memory(SPRITE_POSITION_REGISTERS + sprite_num * 2 + 1);
};
template<typename T>
auto square(T t) {
return t * t;
}
template<uint8_t r, uint8_t g, uint8_t b>
auto color_distance(const Color &lhs, const Color &rhs)
{
return (square(lhs.r - r) + square(lhs.g - g) + square(lhs.b - b))
< (square(rhs.r - r) + square(rhs.g - g) + square(rhs.b - b));
}
template<uint8_t r, uint8_t g, uint8_t b, typename Colors>
auto nearest_color(const Colors &colors)
{
return *std::min_element(std::begin(colors), std::end(colors), color_distance<r,g,b>);
}
}
int main()
{
// http://www.pepto.de/projects/colorvic/
constexpr std::array<Color, 16> colors {{
Color{0, 0x00, 0x00, 0x00},
Color{1, 0xFF, 0xFF, 0xFF},
Color{2, 0x88, 0x39, 0x32},
Color{3, 0x67, 0xB6, 0xBD},
Color{4, 0x8B, 0x3F, 0x96},
Color{5, 0x55, 0xA0, 0x49},
Color{6, 0x40, 0x31, 0x8D},
Color{7, 0xBF, 0xCE, 0x72},
Color{8, 0x8B, 0x54, 0x29},
Color{9, 0x57, 0x42, 0x00},
Color{10, 0xB8, 0x69, 0x62},
Color{11, 0x50, 0x50, 0x50},
Color{12, 0x78, 0x78, 0x78},
Color{13, 0x94, 0xE0, 0x89},
Color{14, 0x78, 0x69, 0xC4},
Color{15, 0x9F, 0x9F, 0x9F}
}};
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,
@ -150,7 +217,9 @@ int main()
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
);
enable_sprite(0, 0, false, false, false);
enable_sprite(0, 0, false, true, false, false);
make_sprite(1,
0,0,0,0,0,2,2,0,0,0,0,0,
@ -176,10 +245,11 @@ int main()
0,0,0,0,0,3,3,0,0,0,0,0
);
enable_sprite(1, 1, true, false, true);
enable_sprite(2, 1, true, false, true);
enable_sprite(1, 1, true, false, false, true);
enable_sprite(2, 1, true, false, false, true);
const auto sprite_collisions = []() {
const auto collisions = memory(SPRITE_COLLISIONS);
memory(SPRITE_COLLISIONS) = 0;
@ -198,7 +268,7 @@ int main()
test_bit(collisions, 4),test_bit(collisions, 5),test_bit(collisions, 6),test_bit(collisions, 7)};
};
std::pair<int8_t, int8_t> ball_vec{1,1};
std::pair<int8_t, int8_t> ball_velocity{1,1};
const auto reset_ball = [&]{
sprite_x(0) = 255/2;
@ -206,28 +276,27 @@ int main()
};
reset_ball();
sprite_x(1) = 15;
sprite_y(1) = 255/2;
sprite_x(2) = 255;
sprite_y(2) = 255/2;
memory(53280) = 12;
memory(53281) = 0;
memory(BORDER_COLOR) = nearest_color<128,128,128>(colors).num;
memory(BACKGROUND_COLOR) = nearest_color<0,0,0>(colors).num;
memory(SPRITE_1_COLOR) = nearest_color<255,0,0>(colors).num;
memory(SPRITE_2_COLOR) = nearest_color<0,255,0>(colors).num;
struct Player
{
Player(const uint8_t num, const uint8_t startx, const uint8_t starty)
: player_num(num),
x(sprite_x(player_num)),
y(sprite_y(player_num))
{
x = startx;
y = starty;
}
void update_position()
{
if (const auto joy = joystick(player_num);
joy.up)
{
sprite_y(player_num) += 3;
} else if (joy.down) {
sprite_y(player_num) -= 3;
}
y += joystick(player_num).direction_vector().second * 3;
};
void scored() {
@ -235,52 +304,70 @@ int main()
}
const uint8_t player_num;
volatile uint8_t &x;
volatile uint8_t &y;
uint8_t score = '0';
};
Player p1{1};
Player p2{2};
Player p1(1, 15, 255/2);
Player p2(2, 255, 255/2);
const auto raster_off_screen = [](){
return memory(53266) == 250 && !test_bit(memory(53265),7);
};
while (true) {
if (raster_off_screen())
struct Frame
{
Frame(Player &p1, Player &p2)
: player1(p1), player2(p2)
{
if (const auto collisions = sprite_collisions();
collisions.sprite0 && (collisions.sprite1 || collisions.sprite2))
{
std::get<0>(ball_vec) *= -1; //invert ball x velocity
// "bounce" ball out of collision area
sprite_x(0) += std::get<0>(ball_vec);
}
// Update paddle positions
p1.update_position();
p2.update_position();
// ball hit the top or bottom wall
if (const auto ball_y = sprite_y(0) += std::get<1>(ball_vec);
ball_y == 45 || ball_y == 235)
{
std::get<1>(ball_vec) *= -1;
}
if (const auto ball_x = sprite_x(0) += std::get<0>(ball_vec);
ball_x == 1) {
// ball hit left wall, player 2 scored
p2.scored();
reset_ball();
} else if (ball_x == 255) {
// ball hit right wall, player 1 scored
p1.scored();
reset_ball();
}
display(10, 12, p1.score);
display(30, 12, p2.score);
while (!(memory(53266) == 250 && !test_bit(memory(53265),7))) {}
}
}
~Frame() {
display(10, 12, player1.score);
display(20, 12, player2.score);
}
Player &player1;
Player &player2;
};
while (true) {
Frame rt(p1, p2);
if (auto [s0, s1, s2, s3, s4, s5, s6, s7] = sprite_collisions();
s0 && (s1 || s2))
{
std::get<0>(ball_velocity) *= -1; //invert ball x velocity
// "bounce" ball out of collision area
sprite_x(0) += std::get<0>(ball_velocity);
}
// Update paddle positions
p1.update_position();
p2.update_position();
// ball hit the top or bottom wall
if (const auto ball_y = sprite_y(0) += std::get<1>(ball_velocity);
ball_y == 45 || ball_y == 235)
{
std::get<1>(ball_velocity) *= -1;
}
const auto score = [reset_ball](auto &player){
player.scored();
reset_ball();
};
if (const auto ball_x = sprite_x(0) += std::get<0>(ball_velocity);
ball_x == 1) {
// ball hit left wall, player 2 scored
score(p2);
} else if (ball_x == 255) {
// ball hit right wall, player 1 scored
score(p1);
}
}
}

7
make_file.sh Executable file
View File

@ -0,0 +1,7 @@
#! /bin/bash
~/llvm-build/bin/clang++ -std=c++1z -c -O3 -o- -Wall -Wextra -m32 -march=i386 -ggdb -S $1 > $1.x86.asm
cat $1.x86.asm | ~/x86-to-6502-build/x86-to-6502 > $1.6502.asm
cat $1.6502.asm | sed -e "/^\t\..*$/d" > $1.asm
~/Downloads/TMPx_v1.1.0-STYLE/linux-x86_64/tmpx $1.asm

View File

@ -292,7 +292,10 @@ struct mos6502 : ASMLine
return text; // + ':';
case ASMLine::Type::Directive:
case ASMLine::Type::Instruction:
return '\t' + text + ' ' + op.value + "; " + comment;
{
const std::string line = '\t' + text + ' ' + op.value;
return line + std::string(static_cast<size_t>(std::max(15 - static_cast<int>(line.size()), 1)), ' ') + "; " + comment;
}
};
throw std::runtime_error("Unable to render: " + text);
}
@ -506,6 +509,10 @@ void translate_instruction(std::vector<mos6502> &instructions, const i386::OpCod
instructions.emplace_back(mos6502::OpCode::lda, get_register(o1.reg_num));
instructions.emplace_back(mos6502::OpCode::ORA, get_register(o2.reg_num));
instructions.emplace_back(mos6502::OpCode::sta, get_register(o2.reg_num));
} else if (o1.type == Operand::Type::literal && o2.type == Operand::Type::reg) {
instructions.emplace_back(mos6502::OpCode::lda, Operand(o1.type, fixup_8bit_literal(o1.value)));
instructions.emplace_back(mos6502::OpCode::ORA, get_register(o2.reg_num));
instructions.emplace_back(mos6502::OpCode::sta, get_register(o2.reg_num));
} else {
throw std::runtime_error("Cannot translate orb instruction");
}
@ -732,14 +739,24 @@ void to_mos6502(const i386 &i, std::vector<mos6502> &instructions)
instructions.emplace_back(i.type, i.text);
return;
case ASMLine::Type::Directive:
// instructions.emplace_back(i.type, i.text);
instructions.emplace_back(i.type, i.text);
return;
case ASMLine::Type::Instruction:
// instructions.emplace_back(ASMLine::Type::Directive, "; " + i.line_text);
const auto head = instructions.size();
translate_instruction(instructions, i.opcode, i.operand1, i.operand2);
for_each(std::next(instructions.begin(), head), instructions.end(), [ text = i.line_text ](auto &ins){ ins.comment = text; });
auto text = i.line_text;
if (text[0] == '\t') {
text.erase(0, 1);
}
for_each(std::next(instructions.begin(), head), instructions.end(),
[ text ](auto &ins){
ins.comment = text;
}
);
return;
}
} catch (const std::exception &e) {
@ -753,20 +770,38 @@ bool optimize(std::vector<mos6502> &instructions)
return false;
}
const auto next_instruction = [&instructions](auto i) {
do {
++i;
} while (i < instructions.size() && instructions[i].type == ASMLine::Type::Directive);
return i;
};
for (size_t op = 0; op < instructions.size() - 1; ++op)
{
if (instructions[op].opcode == mos6502::OpCode::tya
&& instructions[op+1].opcode == mos6502::OpCode::tay)
// look for a transfer of Y -> A immediately followed by A -> Y
if (instructions[op].opcode == mos6502::OpCode::tya)
{
instructions.erase(std::next(std::begin(instructions), op + 1), std::next(std::begin(instructions), op+2));
return true;
next_instruction(op);
if (instructions[op].opcode == mos6502::OpCode::tay) {
instructions.erase(std::next(std::begin(instructions), op), std::next(std::begin(instructions), op+1));
return true;
}
}
if (instructions[op].opcode == mos6502::OpCode::sta
&& instructions[op+1].opcode == mos6502::OpCode::lda
&& instructions[op].op == instructions[op+1].op)
}
for (size_t op = 0; op < instructions.size() - 1; ++op)
{
// look for a store A -> loc immediately followed by loc -> A
if (instructions[op].opcode == mos6502::OpCode::sta)
{
instructions.erase(std::next(std::begin(instructions), op + 1), std::next(std::begin(instructions), op+2));
return true;
const auto next = next_instruction(op);
if (instructions[next].opcode == mos6502::OpCode::lda
&& instructions[next].op == instructions[op].op)
{
instructions.erase(std::next(std::begin(instructions), next), std::next(std::begin(instructions), next+1));
return true;
}
}
}
@ -777,7 +812,9 @@ bool optimize(std::vector<mos6502> &instructions)
{
const auto operand = instructions[op].op;
auto op2 = op+1;
while (op2 < instructions.size() && instructions[op2].opcode == mos6502::OpCode::sta) {
// look for multiple stores of the same value
while (op2 < instructions.size() && (instructions[op2].opcode == mos6502::OpCode::sta
|| instructions[op2].type == ASMLine::Type::Directive)) {
++op2;
}
if (instructions[op2].opcode == mos6502::OpCode::lda
@ -810,12 +847,14 @@ bool fix_long_branches(std::vector<mos6502> &instructions, int &branch_patch_cou
{
++branch_patch_count;
const auto going_to = instructions[op].op.value;
const auto new_pos = "branch_patch_" + std::to_string(branch_patch_count);
const auto new_pos = "patch_" + std::to_string(branch_patch_count);
// uh-oh too long of a branch, have to convert this to a jump...
if (instructions[op].opcode == mos6502::OpCode::bne) {
const auto comment = instructions[op].comment;
instructions[op] = mos6502(mos6502::OpCode::beq, Operand(Operand::Type::literal, new_pos));
instructions.insert(std::next(std::begin(instructions), op + 1), mos6502(mos6502::OpCode::jmp, Operand(Operand::Type::literal, going_to)));
instructions.insert(std::next(std::begin(instructions), op + 2), mos6502(ASMLine::Type::Label, new_pos));
instructions[op].comment = instructions[op+1].comment = instructions[op+2].comment = comment;
return true;
} else {
throw std::runtime_error("Don't know how to reorg this branch");
@ -938,7 +977,6 @@ int main()
instructions.erase(
std::remove_if(std::begin(instructions), std::end(instructions),
[&used_labels](const auto &i){
// if (i.type == ASMLine::Type::Directive) return true;
if (i.type == ASMLine::Type::Label) {
if (used_labels.count(i.text) == 0) {
// remove all unused labels that aren't 'main'
@ -952,18 +990,6 @@ int main()
);
// remove everything up to the first label
// this will probably leave some dead code around at some point
// but it's a start
/*
instructions.erase(
std::begin(instructions),
std::find_if(std::begin(instructions),
std::end(instructions),
[](const auto &i){ return i.type == ASMLine::Type::Label; }
)
);
*/
const auto new_labels =
[&used_labels](){
@ -972,24 +998,17 @@ int main()
auto newl = l;
std::transform(newl.begin(), newl.end(), newl.begin(), [](const auto c) { return std::tolower(c); });
newl.erase(std::remove_if(newl.begin(), newl.end(), [](const auto c){ return !std::isalnum(c); }), std::end(newl));
// std::cout << "Old label: '" << l << "' new label: '" << newl << "'\n";
result.emplace(std::make_pair(l, newl));
}
return result;
}();
// for (const auto &i : instructions)
// {
// std::cout << "'" << i.text << "' '" << i.operand1.value << "' '" << i.operand2.value << "'\n";
// }
for (auto &i : instructions)
{
if (i.type == ASMLine::Type::Label)
{
// std::cout << "Updating label " << i.text << '\n';;
i.text = new_labels.at(i.text);
}
@ -1004,10 +1023,6 @@ int main()
}
}
// for (const auto &i : instructions)
// {
// std::cout << "'" << i.text << "' '" << i.operand1.value << "' '" << i.operand2.value << "'\n";
// }
std::vector<mos6502> new_instructions;
@ -1037,5 +1052,4 @@ int main()
{
std::cout << i.to_string() << '\n';
}
}

View File

@ -1,339 +0,0 @@
#include <cstdint>
#include <array>
#include <utility>
#include <algorithm>
constexpr uint16_t SPRITE_DATA_POINTERS = 2040;
constexpr uint16_t VIDEO_REGISTERS = 53248;
constexpr uint16_t SPRITE_ENABLE_BITS = VIDEO_REGISTERS + 21;
constexpr uint16_t SPRITE_EXPAND_HORIZONTAL = VIDEO_REGISTERS + 29;
constexpr uint16_t SPRITE_EXPAND_VERTICAL = VIDEO_REGISTERS + 23;
constexpr uint16_t SPRITE_POSITION_REGISTERS = VIDEO_REGISTERS;
constexpr uint16_t SPRITE_COLLISIONS = VIDEO_REGISTERS + 30;
constexpr uint16_t SPRITE_MULTICOLOR = VIDEO_REGISTERS + 28;
constexpr uint16_t VIDEO_MEMORY = 1024;
constexpr auto starting_bank = 192;
namespace {
volatile uint8_t &memory(const uint16_t loc)
{
return *reinterpret_cast<uint8_t *>(loc);
}
struct Color
{
enum class Name : uint8_t {
Black = 0,
White = 1,
Red = 2,
Cyan = 3,
Purple = 4,
Green = 5,
Blue = 6,
Yellow = 7,
Orange = 8,
Brown = 9,
LightRed = 10,
DarkGrey = 11,
Grey = 12,
LightGreen = 13,
LightBlue = 14,
LightGrey = 15
};
constexpr Color(const Name t_name, const uint8_t t_r, const uint8_t t_g, const uint8_t t_b)
: name(t_name), r(t_r), g(t_g), b(t_b)
{
}
Name name;
uint8_t r;
uint8_t g;
uint8_t b;
};
template<typename T>
constexpr auto square(T t)
{
return t*t;
}
constexpr auto distance(const Color &lhs, const uint8_t t_r, const uint8_t t_g, const uint8_t t_b)
{
// http://stackoverflow.com/questions/4754506/color-similarity-distance-in-rgba-color-space
return square(lhs.r - t_r) + square(lhs.g - t_g) + square(lhs.b - t_b);
}
template<typename T>
constexpr auto distance_table(T colors, const uint8_t t_r, const uint8_t t_g, const uint8_t t_b)
{
std::array<int, colors.size()> distances{};
auto pos = std::begin(distances);
for (const auto &color : colors) {
*pos = distance(color, t_r, t_g, t_b);
++pos;
}
return distances;
}
template<typename T>
constexpr Color nearest_color(T colors,
const uint8_t t_r, const uint8_t t_g, const uint8_t t_b)
{
const auto ds = distance_table(colors, t_r, t_g, t_b);
const auto distance = std::min_element(std::begin(ds), std::end(ds)) - std::begin(ds);
return colors[distance];
/*
return *std::min_element(std::begin(colors), std::end(colors),
[&](const auto &lhs, const auto &rhs)
{
return distance(lhs, t_r, t_g, t_b) < distance(rhs, t_r, t_g, t_b);
});
*/
}
void clear_bit(const uint16_t loc, const uint8_t bitnum)
{
memory(loc) &= (0xFF ^ (1 << bitnum));
}
void set_bit(const uint16_t loc, const uint8_t bitnum)
{
memory(loc) |= (1 << bitnum);
}
void write_multi_color_pixel(uint16_t)
{
}
template<typename D1, typename D2, typename D3, typename D4, typename ... D >
void write_multi_color_pixel(uint16_t loc, D1 d1, D2 d2, D3 d3, D4 d4, D ... d)
{
memory(loc) = (d1 << 6) | (d2 << 4) | (d3 << 2) | d4;
write_multi_color_pixel(loc + 1, d...);
}
constexpr bool test_bit(const uint8_t data, const uint8_t bit)
{
return (data & (1 << bit)) != 0;
};
template<typename ... D>
void make_sprite(uint8_t memory_loc, D ... d)
{
write_multi_color_pixel((starting_bank + memory_loc) * 64, d...);
}
void enable_sprite(const uint8_t sprite_number, const uint8_t memory_loc,
const bool multicolor,
const bool double_width, const bool double_height)
{
set_bit(SPRITE_ENABLE_BITS, sprite_number);
memory(SPRITE_DATA_POINTERS + sprite_number) = starting_bank + memory_loc;
if (double_width) {
set_bit(SPRITE_EXPAND_HORIZONTAL, sprite_number);
} else {
clear_bit(SPRITE_EXPAND_HORIZONTAL, sprite_number);
}
if (double_height) {
set_bit(SPRITE_EXPAND_VERTICAL, sprite_number);
} else {
clear_bit(SPRITE_EXPAND_VERTICAL, sprite_number);
}
if (multicolor) {
set_bit(SPRITE_MULTICOLOR, sprite_number);
} else {
clear_bit(SPRITE_MULTICOLOR, sprite_number);
}
}
void display_int(uint8_t x, uint8_t y, uint8_t val)
{
memory(VIDEO_MEMORY + y * 40 + x) = val;
}
}
int main()
{
constexpr std::array<Color, 16> colors {Color{Color::Name::Black, 0,0,0},
Color{Color::Name::White, 255,255,255},
Color{Color::Name::Red, 104, 55, 43},
Color{Color::Name::Cyan, 112, 164, 178},
Color{Color::Name::Purple, 111, 61, 134},
Color{Color::Name::Green, 88, 141, 67},
Color{Color::Name::Blue, 53, 40, 121},
Color{Color::Name::Yellow, 184, 199, 111},
Color{Color::Name::Orange, 111, 79, 37},
Color{Color::Name::Brown, 67, 57, 0},
Color{Color::Name::LightRed, 154, 103, 89},
Color{Color::Name::DarkGrey, 68, 68, 68},
Color{Color::Name::Grey, 108, 108, 108},
Color{Color::Name::LightGreen, 154, 210, 132},
Color{Color::Name::LightBlue, 108, 94, 181},
Color{Color::Name::LightGrey, 149, 149, 149}};
//constexpr auto nearest_black = nearest_color(colors, 0, 0, 0);
//constexpr auto nearest_red = nearest_color(colors, 255, 255, 0);
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,2,2,2,2,0,0,0,0,
0,0,0,2,2,2,2,2,2,0,0,0,
0,0,2,2,2,1,1,2,2,2,0,0,
0,0,2,2,1,1,1,1,2,2,0,0,
0,0,2,2,1,1,1,1,2,2,0,0,
0,0,2,2,1,1,1,1,2,2,0,0,
0,0,2,2,1,1,1,1,2,2,0,0,
0,0,2,2,1,1,1,1,2,2,0,0,
0,0,2,2,1,1,1,1,2,2,0,0,
0,0,2,2,1,1,1,1,2,2,0,0,
0,0,2,2,2,1,1,2,2,2,0,0,
0,0,0,2,2,2,2,2,2,0,0,0,
0,0,0,2,2,2,2,2,2,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0
);
enable_sprite(0, 0, true, false, false);
// start timer
memory(56590) = 1;
const auto joy = [](const uint8_t d){
struct State{
State(const uint8_t portdata)
: up(!test_bit(portdata,0)),
down(!test_bit(portdata,1)),
left(!test_bit(portdata,2)),
right(!test_bit(portdata,3)),
fire(!test_bit(portdata,4))
{
}
auto direction_vector() const
{
const auto dir = [](const bool neg, const bool pos) {
if (neg) return -1;
if (pos) return 1;
return 0;
};
return std::make_pair(dir(left, right), dir(up, down));
}
bool up;
bool down;
bool left;
bool right;
bool fire;
};
return State(d);
};
const auto joy_port2 = [joy](){
return joy(memory(56320));
};
const auto joy_port1 = [joy](){
return joy(memory(56321));
};
const auto sprite_x = [](const uint8_t sprite_num) -> decltype(auto)
{
return (memory(SPRITE_POSITION_REGISTERS + sprite_num * 2));
};
const auto sprite_y = [](const uint8_t sprite_num) -> decltype(auto)
{
return (memory(SPRITE_POSITION_REGISTERS + sprite_num * 2 + 1));
};
const auto sprite_collisions = []() {
const auto collisions = memory(SPRITE_COLLISIONS);
memory(SPRITE_COLLISIONS) = 0;
struct Col_Data {
bool sprite0;
bool sprite1;
bool sprite2;
bool sprite3;
bool sprite4;
bool sprite5;
bool sprite6;
bool sprite7;
};
return Col_Data{test_bit(collisions, 0),test_bit(collisions, 1),test_bit(collisions, 2),test_bit(collisions, 3),
test_bit(collisions, 4),test_bit(collisions, 5),test_bit(collisions, 6),test_bit(collisions, 7)};
};
std::pair<int8_t, int8_t> ball_vec{1,1};
uint8_t player1 = 0;
uint8_t player2 = 0;
sprite_x(0) = 50;
sprite_y(0) = 50;
while (true) {
if (memory(56325) == 0) {
// Move ball
const auto ball_x = sprite_x(0) += std::get<0>(ball_vec);
const auto ball_y = sprite_y(0) += std::get<1>(ball_vec);
// Update paddle positions
const auto vec = joy_port2().direction_vector();
sprite_y(1) += std::get<1>(vec);
const auto vec2 = joy_port1().direction_vector();
sprite_y(2) += std::get<1>(vec2);
if (const auto col_data = sprite_collisions();
(col_data.sprite1 || col_data.sprite2) && col_data.sprite0) {
// ball hit a paddle
std::get<0>(ball_vec) *= -1;
}
// ball hit the top or bottom wall
if (ball_y == 30 || ball_y == 240) {
std::get<1>(ball_vec) *= -1;
}
if (ball_x == 1) {
// ball hit left wall, player 2 scored
++player2;
} else if (ball_x == 254) {
// ball hit right wall, player 1 scored
++player1;
}
display_int(10, 3, player1);
display_int(30, 3, player2);
}
}
}