2017-01-28 21:19:08 +00:00
|
|
|
//
|
|
|
|
// TIA.hpp
|
|
|
|
// Clock Signal
|
|
|
|
//
|
|
|
|
// Created by Thomas Harte on 28/01/2017.
|
|
|
|
// Copyright © 2017 Thomas Harte. All rights reserved.
|
|
|
|
//
|
|
|
|
|
|
|
|
#ifndef TIA_hpp
|
|
|
|
#define TIA_hpp
|
|
|
|
|
|
|
|
#include <cstdint>
|
2017-01-29 02:46:40 +00:00
|
|
|
#include "../CRTMachine.hpp"
|
2017-01-28 21:19:08 +00:00
|
|
|
|
|
|
|
namespace Atari2600 {
|
|
|
|
|
|
|
|
class TIA {
|
|
|
|
public:
|
2017-01-29 02:46:40 +00:00
|
|
|
TIA();
|
|
|
|
~TIA();
|
|
|
|
|
2017-02-13 00:55:02 +00:00
|
|
|
// The supplied hook is for unit testing only; if instantiated with a line_end_function then it will
|
|
|
|
// be called with the latest collision buffer upon the conclusion of each line. What's a collision
|
|
|
|
// buffer? It's an implementation detail. If you're not writing a unit test, leave it alone.
|
|
|
|
TIA(std::function<void(uint8_t *output_buffer)> line_end_function);
|
2017-02-13 01:32:53 +00:00
|
|
|
TIA(bool create_crt);
|
2017-02-13 00:55:02 +00:00
|
|
|
|
2017-01-29 19:19:26 +00:00
|
|
|
enum class OutputMode {
|
|
|
|
NTSC, PAL
|
|
|
|
};
|
|
|
|
|
2017-02-05 22:51:56 +00:00
|
|
|
/*!
|
|
|
|
Advances the TIA by @c number_of_cycles cycles. Any queued setters take effect in the
|
|
|
|
first cycle performed.
|
|
|
|
*/
|
2017-01-28 21:19:08 +00:00
|
|
|
void run_for_cycles(int number_of_cycles);
|
2017-01-29 19:19:26 +00:00
|
|
|
void set_output_mode(OutputMode output_mode);
|
2017-01-28 21:19:08 +00:00
|
|
|
|
2017-01-30 03:16:23 +00:00
|
|
|
void set_sync(bool sync);
|
|
|
|
void set_blank(bool blank);
|
2017-01-29 02:46:40 +00:00
|
|
|
void reset_horizontal_counter(); // Reset is delayed by four cycles.
|
|
|
|
|
2017-02-08 03:14:45 +00:00
|
|
|
/*!
|
|
|
|
@returns the number of cycles between (current TIA time) + from_offset to the current or
|
|
|
|
next horizontal blanking period. Returns numbers in the range [0, 227].
|
|
|
|
*/
|
2017-01-29 02:46:40 +00:00
|
|
|
int get_cycles_until_horizontal_blank(unsigned int from_offset);
|
2017-01-28 21:19:08 +00:00
|
|
|
|
|
|
|
void set_background_colour(uint8_t colour);
|
|
|
|
|
|
|
|
void set_playfield(uint16_t offset, uint8_t value);
|
|
|
|
void set_playfield_control_and_ball_size(uint8_t value);
|
|
|
|
void set_playfield_ball_colour(uint8_t colour);
|
|
|
|
|
|
|
|
void set_player_number_and_size(int player, uint8_t value);
|
|
|
|
void set_player_graphic(int player, uint8_t value);
|
|
|
|
void set_player_reflected(int player, bool reflected);
|
|
|
|
void set_player_delay(int player, bool delay);
|
|
|
|
void set_player_position(int player);
|
|
|
|
void set_player_motion(int player, uint8_t motion);
|
|
|
|
void set_player_missile_colour(int player, uint8_t colour);
|
|
|
|
|
|
|
|
void set_missile_enable(int missile, bool enabled);
|
|
|
|
void set_missile_position(int missile);
|
|
|
|
void set_missile_position_to_player(int missile);
|
|
|
|
void set_missile_motion(int missile, uint8_t motion);
|
|
|
|
|
|
|
|
void set_ball_enable(bool enabled);
|
|
|
|
void set_ball_delay(bool delay);
|
|
|
|
void set_ball_position();
|
|
|
|
void set_ball_motion(uint8_t motion);
|
|
|
|
|
|
|
|
void move();
|
|
|
|
void clear_motion();
|
|
|
|
|
|
|
|
uint8_t get_collision_flags(int offset);
|
|
|
|
void clear_collision_flags();
|
2017-01-29 02:46:40 +00:00
|
|
|
|
|
|
|
virtual std::shared_ptr<Outputs::CRT::CRT> get_crt() { return crt_; }
|
|
|
|
|
|
|
|
private:
|
|
|
|
std::shared_ptr<Outputs::CRT::CRT> crt_;
|
2017-02-13 00:55:02 +00:00
|
|
|
std::function<void(uint8_t *output_buffer)> line_end_function_;
|
2017-01-29 18:47:36 +00:00
|
|
|
|
2017-01-31 03:42:27 +00:00
|
|
|
// the master counter; counts from 0 to 228 with all visible pixels being in the final 160
|
2017-01-29 18:47:36 +00:00
|
|
|
int horizontal_counter_;
|
2017-01-30 03:16:23 +00:00
|
|
|
|
2017-01-31 03:42:27 +00:00
|
|
|
// contains flags to indicate whether sync or blank are currently active
|
2017-01-30 03:16:23 +00:00
|
|
|
int output_mode_;
|
|
|
|
|
2017-02-05 22:51:56 +00:00
|
|
|
// keeps track of the target pixel buffer for this line and when it was acquired, and a corresponding collision buffer
|
2017-02-12 23:16:50 +00:00
|
|
|
// alignas(alignof(uint32_t)) uint8_t collision_buffer_[160];
|
|
|
|
std::vector<uint8_t> collision_buffer_;
|
2017-02-05 22:51:56 +00:00
|
|
|
enum class CollisionType : uint8_t {
|
2017-02-06 23:29:00 +00:00
|
|
|
Playfield = (1 << 0),
|
|
|
|
Ball = (1 << 1),
|
|
|
|
Player0 = (1 << 2),
|
|
|
|
Player1 = (1 << 3),
|
|
|
|
Missile0 = (1 << 4),
|
|
|
|
Missile1 = (1 << 5)
|
|
|
|
};
|
|
|
|
|
|
|
|
int collision_flags_;
|
|
|
|
int collision_flags_by_buffer_vaules_[64];
|
|
|
|
|
|
|
|
// colour mapping tables
|
|
|
|
enum class ColourMode {
|
|
|
|
Standard = 0,
|
|
|
|
ScoreLeft,
|
|
|
|
ScoreRight,
|
|
|
|
OnTop
|
|
|
|
};
|
|
|
|
uint8_t colour_mask_by_mode_collision_flags_[4][64]; // maps from [ColourMode][CollisionMark] to colour_pallete_ entry
|
|
|
|
|
|
|
|
enum class ColourIndex {
|
|
|
|
Background = 0,
|
|
|
|
PlayfieldBall,
|
2017-02-07 02:48:41 +00:00
|
|
|
PlayerMissile0,
|
|
|
|
PlayerMissile1
|
2017-02-05 22:51:56 +00:00
|
|
|
};
|
2017-02-06 23:29:00 +00:00
|
|
|
uint8_t colour_palette_[4];
|
2017-01-29 19:19:26 +00:00
|
|
|
|
2017-01-31 03:42:27 +00:00
|
|
|
// playfield state
|
2017-01-31 02:38:58 +00:00
|
|
|
int background_half_mask_;
|
2017-02-06 23:29:00 +00:00
|
|
|
enum class PlayfieldPriority {
|
|
|
|
Standard,
|
|
|
|
Score,
|
|
|
|
OnTop
|
|
|
|
} playfield_priority_;
|
2017-02-01 01:30:32 +00:00
|
|
|
uint32_t background_[2]; // contains two 20-bit bitfields representing the background state;
|
|
|
|
// at index 0 is the left-hand side of the playfield with bit 0 being
|
|
|
|
// the first bit to display, bit 1 the second, etc. Index 1 contains
|
|
|
|
// a mirror image of index 0. If the playfield is being displayed in
|
|
|
|
// mirroring mode, background_[0] will be output on the left and
|
|
|
|
// background_[1] on the right; otherwise background_[0] will be
|
|
|
|
// output twice.
|
2017-01-30 13:08:03 +00:00
|
|
|
|
2017-01-31 03:42:27 +00:00
|
|
|
// player state
|
|
|
|
struct Player {
|
|
|
|
int size; // 0 = normal, 1 = double, 2 = quad
|
|
|
|
int copy_flags; // a bit field, corresponding to the first few values of NUSIZ
|
2017-02-17 01:52:01 +00:00
|
|
|
uint8_t graphic[2]; // the player graphic; 1 = new, 0 = current
|
2017-01-31 03:42:27 +00:00
|
|
|
int reverse_mask; // 7 for a reflected player, 0 for normal
|
2017-02-17 01:52:01 +00:00
|
|
|
int graphic_index;
|
2017-02-12 19:01:50 +00:00
|
|
|
|
|
|
|
int pixel_position;
|
2017-02-19 01:18:50 +00:00
|
|
|
int pixel_delay;
|
2017-02-13 00:55:02 +00:00
|
|
|
|
2017-02-19 01:18:50 +00:00
|
|
|
Player() : size(0), copy_flags(0), graphic{0, 0}, reverse_mask(false), pixel_position(32), graphic_index(0), pixel_delay(0) {}
|
2017-01-31 03:42:27 +00:00
|
|
|
} player_[2];
|
|
|
|
|
|
|
|
// missile state
|
|
|
|
struct Missile {
|
|
|
|
int size; // 0 = 1 pixel, 1 = 2 pixels, etc
|
|
|
|
} missile_[2];
|
2017-02-09 23:37:19 +00:00
|
|
|
|
|
|
|
// movement
|
|
|
|
bool horizontal_blank_extend_;
|
2017-02-11 18:16:53 +00:00
|
|
|
int motion_[5];
|
2017-02-17 01:28:37 +00:00
|
|
|
int motion_step_[5];
|
|
|
|
int motion_time_[5];
|
2017-02-11 18:16:53 +00:00
|
|
|
int position_[5];
|
2017-02-10 12:23:43 +00:00
|
|
|
bool is_moving_[5];
|
|
|
|
enum class MotionIndex : uint8_t {
|
|
|
|
Ball,
|
|
|
|
Player0,
|
|
|
|
Player1,
|
|
|
|
Missile0,
|
|
|
|
Missile1
|
|
|
|
};
|
2017-02-17 01:28:37 +00:00
|
|
|
inline int perform_border_motion(int identity, int start, int end);
|
|
|
|
inline void perform_motion_step(int identity);
|
2017-02-10 01:53:42 +00:00
|
|
|
|
|
|
|
// drawing methods and state
|
|
|
|
inline void output_for_cycles(int number_of_cycles);
|
|
|
|
inline void output_line();
|
|
|
|
|
|
|
|
inline void draw_playfield(int start, int end);
|
2017-02-10 12:23:43 +00:00
|
|
|
inline void draw_player(Player &player, CollisionType collision_identity, const int position_identity, int start, int end);
|
2017-02-17 01:28:37 +00:00
|
|
|
inline void draw_player_visible(Player &player, CollisionType collision_identity, const int position_identity, int start, int end);
|
2017-02-10 12:23:43 +00:00
|
|
|
|
|
|
|
inline void update_motion(int start, int end);
|
2017-02-10 01:53:42 +00:00
|
|
|
|
|
|
|
int pixels_start_location_;
|
|
|
|
uint8_t *pixel_target_;
|
|
|
|
inline void output_pixels(int start, int end);
|
2017-01-28 21:19:08 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* TIA_hpp */
|