Sunday, December 15, 2019

Video Poker part 1

This is the first half of Chapter 8 of my "Creating HTML5 Games Using AnimateCC" eBook that I was releasing on my Blazing Games Development Blog but am now going to be posting here as I am now just going to have a single blog. For earlier posts in the series, visit the other blog. Unlike earlier posts, I want longer form posts so will be posting the first half of the chapter this month and next month will be posting the second half (instead of having 8 sections spread out).

Chapter 8 - Video Poker (part 1)

Game Layout

Video Poker is a simulation of a video-based gambling game found at many casinos. As such, it makes sense to have a background that reflects this fact by making the game display look like a cabinet with a monitor in it. This is done in two layers. The “Screenback” layer holds the screen and the “Console” layer holds the console (outer part of the machine). The figures below shows the “Screenback” layer and the “Console” layer.


The screen background layer (“Screenback”) is a radial gradient filled rectangle using shades of green to give the impression of older CRT displays. Perhaps this aesthetic reflects my age as pretty much any casino that you go into today will have more modern LCD or better displays. I like the older look, but that could be a reflection of your age. Choose the aesthetic that you, or better yet your target audience, would prefer.



The games are housed in a cabinet which I originally called a console thinking of the term for video game systems not the proper term for the physical machines but I only realized the mistake well after the game was finished so am leaving the incorrect terminology in the code. If in the future I was going to alter the code, a refactoring step to properly rename the layer may be worth the effort.

In order to actually play the game, we are going to need a few more layers. The layers used in the movie are “Code,” “Info,” “Bet,” “Console,” “FRCard,” “RCard,” “CCard,” “LCard,” “FLCard,” and “Screenback.” The combination of these layers results in our final layout as can be seen in figure below.



The Code layer is where we place our code and labels. The “Console” and “Screenback” layers were described above. The “FRCard” layer holds the card on the far right. The “RCard” layer holds the card on the right. And so on. We simply place a card object on each of the layers. The card instances are named, “FLCard\_movie,” “LCard\_movie,” “CCard\_movie,” “Rcard\_movie,” and “FRCard\_movie.” To make sure the cards are aligned vertically and spaced evenly, you can use the align submenu under the modify menu. To use this, select the objects you want to align by clicking on them while the shift key is down and then choose the alignment option of your choice.

Now, the info layer is going to contains a line of text that gives players instructions and results. Quite simply, we create this layer by creating a dynamic text object using a red 50 point “\_sans” font and naming the instance “results\_txt.” We also create dynamic text objects for the bet, last win and current balance. These will be created using a green 20 point “\_sans” font. The instances will be named “bet\_text,” “win\_txt,” and “balance\_txt.” Later on we will be adding Draw movies to this layer.

The bet layer is mostly blank. It only appears when the bet buttons are active. The rest of the time the player will only see the non-functioning buttons that are part of the console image.

We want to make sure that we import our deck class. This is done by going to the actions panel. Within that panel you can specify which scripts you wish to add to the project. Selecting the script option lets you add scripts to the game. The order of the scripts is important as it determines the loading order of the scripts so if one script depends on another script you may need to make sure the depended-on script is loaded first.

This leads to the problem of where to place the code. As with the NIM game, we are opting to write the code in the provided script section which has the advantage of keeping all the game logic within the .fla file but as we are loading an external file for the card library, some of you may be wondering if it would be possible to keep the code outside of flash. The answer to this is yes, and it is something that we will be doing with some of the other projects in this book.

Betting


Video poker, and poker in general for that matter, is a casino game. This means that betting is an important aspect of the game. In a casino, the video poker games I have seen are based around a unit of currency. That unit, be it nickles, quarters, or dollars is the amount of a bet. Most machines allow for more than one unit be bet at a time. I have decided that my virtual machine will allow for 1 to 5 units to be used.

As you seen last chapter, the background console already has buttons drawn. These are not usable. What we are going to do is create a button class that looks like the console button but lit up. This is a common casino convention to let the player know what options are currently selectable. This lit up button will be placed over top the console image so to the user it will appear as if the console has lit up the button. Figure \ref{fig:betbuttons} shows the four frames that make up one of the buttons.



Placing the buttons over their corresponding console button is simply a matter of zooming in the image and using the cursor keys to finely position the buttons. The button instance names are labeled “bet1\_btn,” “bet2\_btn,” “bet3\_btn,” “bet4\_btn” and “bet5\_btn.” Now we are ready to start adding code.

Within the global script player we start by creating the constructor function for our videoPokerGame class which will have additional functions added to it This code simply sets the players cumulative winnings to 0, the amount of the last win to 0, and creates a deck of cards using the card library that was created in the previous chapter.

// Global Scripts
videoPokerGame = new VideoPokerGame();
function VideoPokerGame()
{
console.log("Created Video Poker Game Instance");
this.cash = 0;
this.lastWin = 0;
this.myDeck = new spelchan.Deck();
}

One issue that I came across when first experimenting with Adobe Animate CC was when trying to access the stage variable so I could access variables in the movie clip that is currently active.  There may be a better way of doing this, but my quick solution is to simply pass the MovieClip instance to the initGame function whenever a game is being initialized.

The initGame function sets up base game variables, resets the current instructional message for the game, then sets up the cards and the listeners for the cards. The card values are stored in a hand array registering the appropriate MovieClip representation of the card. Each card has a draw message tied to it as well to let the player know which cards are going to be drawn. These are stored in the drawMovies array. .

VideoPokerGame.prototype.initGame = function(vpMovie) 
{
this.vpMovie = vpMovie;
this.selecting = false;
this.bet = 0;
this.message = "?";
this.hand = new Array(
new spelchan.Card(vpMovie.FLCard_movie),
new spelchan.Card(vpMovie.LCard_movie), 
new spelchan.Card(vpMovie.CCard_movie), 
new spelchan.Card(vpMovie.RCard_movie), 
new spelchan.Card(vpMovie.FRCard_movie)
);
this.drawMovie = new Array(vpMovie.draw1_movie, vpMovie.draw2_movie, vpMovie.draw3_movie,
vpMovie.draw4_movie, vpMovie.draw5_movie);
for (var cntr = 0; cntr < 5; ++cntr)
{
this.drawMovie[cntr].visible = false;
this.hand[cntr].cardMovie.addEventListener("click", this.handleCardClick.bind(this, cntr));
}
}

The initialization sets the message by calling an updateText function which not only displays the indicated message but also updates the betting, player cash balance, and the last amount that the player has won. This is done by simply setting the text value of the appropriate text objects that were used to make the text.

VideoPokerGame.prototype.updateText = function()
{
this.vpMovie.results_text.text = this.message;
this.vpMovie.bet_text.text = "Betting: " + this.bet;
this.vpMovie.balance_text.text = "Balance: " + this.cash;
this.vpMovie.win_text.text = "Last Win: " + this.lastWin;
}

The card MovieClips have an event listener attached to them. This simply sees if we are in a part of the game where you can select cards for the card draw and if so toggles whether that card should be drawn.

VideoPokerGame.prototype.handleCardClick = function(n, event) {
if (this.selecting != true)
return;
console.log("Should be toggling draw for slot " + n);
this.drawMovie[n].visible = !this.drawMovie[n].visible;
}

The code to call the game initialization needs to be added to the second frame of the games’ timeline on the Code layer. The easiest way of doing this is to right-click on the frame in the timeline and add a blank keyframe if one does not exist then right-click on that blank keyframe and select actions which should open the code editor for that frame. Once you have code on a frame, simply selecting it from the list of code segments on the left of the action window will let you quickly select the frame for which you wish to edit code. The code on this frame is simply a call to the global videoPokerGame game calling the initGame function that we created above.

videoPokerGame.initGame(this);

The third frame of the code layer we label “BetWait.” This will be the location the movie moves to whenever the player is required to make another bet. To handle the betting the frame calls a method in the global VideoPokerGame instance. The code to place on frame 4 is a simple function call.

videoPokerGame.startWaitForBets();


This function doesn’t exist in our VideoPokerGame class so we need to create it. The first thing the function needs to do is set up the message to a "Please make a bet..." message. We then make sure that none of the draw labels are visible by loping through them and turning off their visibility. The movie playback is stopped so that this frame will be shown until a bet has been made. This is where things get rather interesting. JavaScript handles scope a bit different from other object-oriented languages and as a result a call to a method within a class will be called with the wrong value for the this variable. To correct for this, we need to bind the correct reference to use. This information needs to be saved so that when we remove the event listener we are removing the proper listener.

The fact that we are binding things to the event call actually makes things a bit easier than they were In ActionScript as you are then able to add additional parameters to the function call so that the same handler can more easily handle multiple events. In our case we are going to have a handleBet event handler which will take the amount of the bet as it’s parameter.

Once the bindings are created, we simply add a “click” event listener to each of the five buttons using the boundedBet tied to that button.

VideoPokerGame.prototype.startWaitForBets = function(){
this.message = "Please make a bet...";
this.updateText();
for (var cntr = 0; cntr < 5; ++cntr)
{
this.drawMovie[cntr].visible = false;
}
this.vpMovie.stop();
// create bindings for bets
this.boundBet1 = this.handleBet.bind(this, 1);
this.boundBet2 = this.handleBet.bind(this, 2);
this.boundBet3 = this.handleBet.bind(this, 3);
this.boundBet4 = this.handleBet.bind(this, 4);
this.boundBet5 = this.handleBet.bind(this, 5);
// listen for bets
this.vpMovie.bet1_btn.addEventListener("click", this.boundBet1);
this.vpMovie.bet2_btn.addEventListener("click", this.boundBet2); 
this.vpMovie.bet3_btn.addEventListener("click", this.boundBet3);
this.vpMovie.bet4_btn.addEventListener("click", this.boundBet4);
this.vpMovie.bet5_btn.addEventListener("click", this.boundBet5);
}

When a bet has been made, the handleBet function get called with the amount of the bet. It simply records the amount of the bet, removes the event listeners from the bet buttons, clears the message, adjusts the player’s cash balance, then starts the game timeline which will advance into the ClearCards section of the game movie.

VideoPokerGame.prototype.handleBet = function(bet, event) {
this.bet = bet;
console.log("Made a bet of " + this.bet);
this.vpMovie.bet1_btn.removeEventListener("click", this.boundBet1);
this.vpMovie.bet2_btn.removeEventListener("click", this.boundBet2); 
this.vpMovie.bet3_btn.removeEventListener("click", this.boundBet3);
this.vpMovie.bet4_btn.removeEventListener("click", this.boundBet4);
this.vpMovie.bet5_btn.removeEventListener("click", this.boundBet5);
this.message = " ";
this.cash -= bet;
this.updateText();
this.vpMovie.play();
}


Revealing the Hand


The revealing of the initial drawing of cards is done in three phases. The first phase the cards are removed from the game play field. This is simply a matter of moving the cards, each card separated by a few frames, from it's current position to a position along the top of the screen while changing the size of the card to 20\%. This is done in the movie section labeled “ClearCards.” I placed the moves in frames 5, 8, 11, 14, and 17. A shuffle sound is added to the “Sound” layer to play a shuffling sound whle the cards are being removed. The figure below shows the timeline for the dealing phase of the game.



On frame 20 of the code layer we create a keyframe labeled “Deal.” In frame 20 we also call our video poker game instance to deal the cards. We also need to add a draw sound on frame 20, 25, 30, 35, and 40. The code on frame 20 is simply a call to our deal method.

videoPokerGame.deal();

The code for handling the dealing is in the global script.  The deal function simply makes use of the card library we created in the previous chapter to shuffle the deck then grabs the first five cards off the deck. The card is set to show the face just in case the face wasn’t showing.

VideoPokerGame.prototype.deal = function()
{
var cntr;
this.myDeck.shuffle();
for (cntr = 0; cntr < 5; ++cntr)
{
this.hand[cntr].setCard(this.myDeck.draw());
this.hand[cntr].showFace();
}
}

Finally we animate the cards going from their top position to their proper final locations. What I do is stagger the starting frame of the reveal animation by five frames, yet have the reveal animation take 10 frames. In other words, we have starting frames of 20, 25, 30, 35 and 40. Each card moves from the hidden spot at the top to it’s proper position and at the same time returns to it’s normal size. This is a simple motion tween for all five of the cards.

Next half of the chapter will be released next month where we implement the selection and scoring of hands.

Saturday, November 16, 2019

Freedom of Speech

While the latest patch to WordPress seems to have fixed my issues on spelchan.ca, fighting with it is not something I want to spend time on so I am going to consider that a failed experiment and will instead post my longer-form posts here once a month with my Blazing Game Development blog linking here when appropriate.

This is a different post from the one I was originally going to post due to recent events in Canada. The fact that stating an opinion that could be construed as controversial can lead to a person being fired is terrifying but is something that just happened here in Canada. Recently, a popular but often controversial hockey commentator named Don Cherry had a speech about how people need to support our Veterans by buying poppies. Clearly this is a valid statement as walking down the street before Remembrance Day you could see lots of people without poppies. The problem was that he focused his statements at a particular group of people, immigrants. This made his statements “racist.”  This resulted in people who were offended by his statements insisting that he be fired, and he was.

The thing is, some of our social improvements, such as religious freedom, women’s rights, and the LGBTQ movement were all politically incorrect. Atheism is still somewhat politically incorrect, which is strange since it is impossible to have religious freedom without  the option to not believe. These improvement could never have come about without freedom of speech. If we lose freedom of speech, then the people who don’t think those things are improvements could take those freedoms away.

I do not believe that having a politically incorrect opinion should be grounds for firing someone.  Even if that opinion is offensive to some other people. Quite frankly, any opinion can be considered to be offensive by someone who holds the opposite opinion.  “But he’s a racist!” Maybe, but so are a lot of other people many of whom like to call other people racists. In fact, there are leader’s of countries that are clearly racist, and I am not just talking about Trump.  Apparently it is okay to be a racist and run a country but racists should not be allowed to comment on Hockey game strategy.

Freedom of speech means that everyone should be free to express their opinions without fear of repercussions. This means that some people are going to say things that you disagree with. But guess what? You also have freedom of speech which means that you can use your speech to explain why that person is wrong. Calling someone a “racist” does not explain why they are wrong! In fact, even though Don Cherry was fired over his opinion, nobody bothered to look and see if there was any validity to his statements, which to me is the bigger problem. Truth should be what we strive for, even if it offends people. But in today’s politically correct climate, truth is often the victim. Are immigrants less likely to support our veterans’? If so is it simply due to them being unfamiliar with our customs in which case explaining the significance of wearing a poppy to them would likely solve the problem.

If when someone says something that you strongly disagree with then dismissing them as a “racist”, “sexist”, or whatever-ist does not help the situation. If they have a valid point then that point is still valid no matter what type of human that person is. We should be focused on what is being said and whether it is valid, not on who is saying it. It is impossible to get to the root of a problem if we are forbidden from talking about the problem. In the long term, stifling opinions – even incorrect ones – is far more harmful to society than offending people.

Now with that off my chest, next month - assuming something doesn't happen to keep me on the sandbox - I will post the first half of the next chapter of my HTML5 Game Development using Animate eBook.

Saturday, October 12, 2019

Moving blog to Canada

I have concluded that having two separate blogs is just too big of a pain. I am also thinking that I am better off with longer posts to better cover whatever topic I am discussing. This means that I will not be posting the new blog every week but instead will be cutting down to a long post once a month. Chapters from my animate (and possibly create.js) eBook will be posted once every 3 to 4 months, with posts on other subjects in between the chapter posts. The in-between posts will be about Spelchan.com content, game-jam or home-brew content, and articles related to what I am researching with the odd miscellaneous soapbox post appearing related to whatever thing got me angry enough to write a post.

This leads to the next question as to where these posts will appear. While I have nothing against blogger, I would prefer that it was on a site that I control. For that reason I set up WordPress on my Spelchan.ca site so my blog posts will appear on Spelchan.ca while my games appear on Spelchan.com. For the next while, I will probably be posting TLDR summaries on this blog for posts.

The first post will be on Halloween Liche and will be posted next Saturday. I am really tempted to have my first soap-box post next week due to the election in Canada but it is probably best that I avoid politics in my post, unless it is computer related such as DRM or taxing automation.

Saturday, September 28, 2019

2600 Collisions

One of the nicer things about the 2600 is the hardware collision detection. Having written software sprite-based engines where collision detection had to be done with software this is a nice feature. As the TIA must know what to draw in the pixel, and there are only 6 things that can collide, the hardware simply needs to do a series of and operations with the information it has and store that information in the appropriate register. As each of the 6 thing that can collide can’t collide with themselves and are reversible (missile1 hitting player0 is the same as player0 hitting missile1) that means that there are only 15 possible collisions which I outline in the table below.



By assigning each collision with a bit we can put all the possible TIA collisions into a 15 bit word or split it into 2 bytes. This is the way a modern system would do this if the API designer didn’t add a hundred layers of abstraction to the system. For some reason, the TIA designers decided to have the collision bits divided into 8 registers as shown in table \ref{tbl:collisionregs}. This lets you use the BIT instruction which sets the Negative flag if bit seven is set so BPL/BMI can be used to check this bit as well as setting the overflow flag if bit 6 is set allowing for BVC/BVS branching.



Once a collision register has been set it stays set until the CXCLR is written to. As you generally want the collision for the whole frame this is not a big issue but something that needs to be kept in mind if you want to figure out where on the screen the collision occurred.

The TIA emulator handles collisions by tracking the flags as a single integer with bits set up as per the first table. This is a global collisonState variable which is cleared by CXCLR. Whenever a pixel is being written, we create a mask that will be logically ORed to the collision state. It assumes that all collisions are going to happen.

var collisionMask = 32767

As we draw each sprite, we check to see if the element is drawn. If it is, the mask is unchanged, but if it is not then no collision with that component could have happened so we remove those bits using the collision mask as an indication of which bits should be ANDed out.

pixelColor = if (sprites[0].isPixelDrawn(column)) playfieldBallColor else {
collisionMask = collisionMask and sprites[0].collisionMask
pixelColor}
pixelColor = if (sprites[1].isPixelDrawn(column)) playerMissile1Color else {
collisionMask = collisionMask and sprites[1].collisionMask
pixelColor}
pixelColor = if (sprites[2].isPixelDrawn(column)) playerMissile1Color else {
collisionMask = collisionMask and sprites[2].collisionMask
pixelColor}
pixelColor = if (sprites[3].isPixelDrawn(column)) playerMissile0Color else {
collisionMask = collisionMask and sprites[3].collisionMask
pixelColor}
pixelColor = if (sprites[4].isPixelDrawn(column)) playerMissile0Color else {
collisionMask = collisionMask and sprites[4].collisionMask
pixelColor}

collisonState = collisonState or collisionMask

With that done, we now have all the information we need to read the collision registers so we implement the readRegister method to return the requested pair of collision bits as outlined in our second table.

private fun buildCollisionByte(bit7mask:Int, bit6mask:Int):Int {
val bit7 = if ((collisonState and bit7mask) == bit7mask) 128 else 0
val bit6 = if ((collisonState and bit6mask) == bit6mask) 64 else 0
return bit7 or bit6


fun readRegister(address:Int):Int {
return when(address) {
TIAPIARegs.CXBLPF -> buildCollisionByte(TIAPIARegs.ICOL_PFBL,0)
TIAPIARegs.CXM0FB -> buildCollisionByte(TIAPIARegs.ICOL_PFM0, TIAPIARegs.ICOL_BLM0)
TIAPIARegs.CXM1FB -> buildCollisionByte(TIAPIARegs.ICOL_PFM1, TIAPIARegs.ICOL_BLM1)
TIAPIARegs.CXM0P -> buildCollisionByte(TIAPIARegs.ICOL_P0M0, TIAPIARegs.ICOL_M0P1)
TIAPIARegs.CXM1P -> buildCollisionByte(TIAPIARegs.ICOL_P0M1, TIAPIARegs.ICOL_P1M1)
TIAPIARegs.CXP0FB -> buildCollisionByte(TIAPIARegs.ICOL_PFP0, TIAPIARegs.ICOL_BLP0)
TIAPIARegs.CXP1FB -> buildCollisionByte(TIAPIARegs.ICOL_PFP1, TIAPIARegs.ICOL_BLP1)
TIAPIARegs.CXPPMM -> buildCollisionByte(TIAPIARegs.ICOL_P0P1, TIAPIARegs.ICOL_M0M1)

// Unknown or unimplemented registers print warning
else -> { println("TIA register $address not implemented!"); 0}
}
}

This means that we now have collisions. Next fortnight things are changing as I make up my mind about the future of this blog.

Saturday, September 14, 2019

TIA Sprite Implementation

Now that we have a sprite class for the internal processing of the TIA chip Player Missile Graphics (which I will refer to as sprites), we need to implement the TIA registers that handle the various sprite activity and adjust our sprites to reflect the register settings. First, we create the 5 sprites that the TIA uses. This is done in an array class variable with constants set up in the TIAPIARegs singleton class that hold the index values of each sprite.

val sprites = arrayOf(PlayerMissileGraphic(1, 30570), // Ball
PlayerMissileGraphic(8, 15423), // Player 1
PlayerMissileGraphic(1, 1023), // Missile 1
PlayerMissileGraphic(8, 28377), // Player 0
PlayerMissileGraphic(1, 24007) ) // Missile 0

The most important thing for a sprite is to be able to be shown. The ball and missiles have an enable register which when bit 1 is set will show the sprite otherwise will hide the sprite. For the player sprites, set the byte that represents the image to 0 for the sprite not to be drawn and fill it with the display data to have the sprite draw something.

TIAPIARegs.ENABL -> sprites[TIAPIARegs.ISPRITE_BALL].drawingBits = if ((value and 2) == 2) 1 else 0
TIAPIARegs.ENAM0 -> sprites[TIAPIARegs.ISPRITE_MISSILE0].drawingBits = if ((value and 2) == 2) 1 else 0
TIAPIARegs.ENAM1 -> sprites[TIAPIARegs.ISPRITE_MISSILE1].drawingBits = if ((value and 2) == 2) 1 else 0
TIAPIARegs.GRP0 -> sprites[TIAPIARegs.ISPRITE_PLAYER0].drawingBits = value and 255
TIAPIARegs.GRP1 -> sprites[TIAPIARegs.ISPRITE_PLAYER1].drawingBits = value and 255

Being able to see the sprite is nice, but we need to control where on the scan-line the sprite will be displayed. It would be nice if we just had a register that held the position information of the sprites, but for some reason that is beyond me this is not how sprite positioning works on the TIA. To position a sprite you simply write anything into the RESXX register and it will position the sprite at the current color clock position or position 0 if called in the horizontal blanking interval.

TIAPIARegs.RESBL -> sprites[TIAPIARegs.ISPRITE_BALL].x = column
TIAPIARegs.RESP0 -> sprites[TIAPIARegs.ISPRITE_PLAYER0].x = column
TIAPIARegs.RESP1 -> sprites[TIAPIARegs.ISPRITE_PLAYER1].x = column
TIAPIARegs.RESM0 -> sprites[TIAPIARegs.ISPRITE_MISSILE0].x = column
TIAPIARegs.RESM1 -> sprites[TIAPIARegs.ISPRITE_MISSILE1].x = column
TIAPIARegs.RESMP0 -> sprites[TIAPIARegs.ISPRITE_MISSILE0].x = sprites[TIAPIARegs.ISPRITE_PLAYER0].x
TIAPIARegs.RESMP1 -> sprites[TIAPIARegs.ISPRITE_MISSILE1].x = sprites[TIAPIARegs.ISPRITE_PLAYER1].x

The problem with this is that each CPU cycle is 3 color clock cycles and a STA instruction takes at least 3 cycles to complete. This means that we need to adjust the horizontal position using the move delta. This is done at the start of the scanline by writing to HMOVE with any value. The amount each sprite moves is a signed nibble set by HMxx registers with a HMCLR register that sets all the HMxx registers to zero. We have a utility method to convert a nibble into a signed integer as follows

private fun convertNibbleToSignedInt(nibble:Int):Int {
val nib = nibble and 15
return if (nib > 7) nib - 16 else nib
}

And within the writeRegister when statement we have the following registers implemented:

TIAPIARegs.HMP0 -> sprites[TIAPIARegs.ISPRITE_PLAYER0].deltaX = convertNibbleToSignedInt(value shr 4)
TIAPIARegs.HMP1 -> sprites[TIAPIARegs.ISPRITE_PLAYER1].deltaX = convertNibbleToSignedInt(value shr 4)
TIAPIARegs.HMM0 -> sprites[TIAPIARegs.ISPRITE_MISSILE0].deltaX = convertNibbleToSignedInt(value shr 4)
TIAPIARegs.HMM1 -> sprites[TIAPIARegs.ISPRITE_MISSILE1].deltaX = convertNibbleToSignedInt(value shr 4)
TIAPIARegs.HMBL -> sprites[TIAPIARegs.ISPRITE_BALL].deltaX = convertNibbleToSignedInt(value shr 4)
TIAPIARegs.HMOVE -> {
for (cntr in 0..4)
sprites[cntr].hmove()
}
TIAPIARegs.HMCLR -> {
for (cntr in 0..4)
sprites[cntr].deltaX = 0
}

Sprite scaling and mirroring is done through the NUSIZx register with the copy/scale mode of the player sprites being the lower 3 bits and the scaling of missile for that player being two to the power of the two bit number occupying bits 4 and 5. In other words, we take the last three bits to form a number between 0 and 7 which is used to set the player sprite scaling and copies via the setePlayerScaleCopy method that we wrote in the previous article. The missile scaling is bits 4 and 5 which when shifted right become a number between 0 and 3, this is used as a power of two which can be done by left-shifting a 1 by the given number which would result in 1, 2, 4, or 8.

TIAPIARegs.NUSIZ0 -> {
sprites[TIAPIARegs.ISPRITE_PLAYER0].setPlayerScaleCopy(value and 7)
sprites[TIAPIARegs.ISPRITE_MISSILE0].scale = 1 shl ((value shr 4) and 3)
}
TIAPIARegs.NUSIZ1 -> {
sprites[TIAPIARegs.ISPRITE_PLAYER1].setPlayerScaleCopy(value and 7)
sprites[TIAPIARegs.ISPRITE_MISSILE1].scale = 1 shl ((value shr 4) and 3)
}

Finally, mirroring is simply setting the sprite mirror variable based on the state of bit 3 of the value passed to the REFx register.

TIAPIARegs.REFP0 -> sprites[TIAPIARegs.ISPRITE_PLAYER0].mirror = (value and 8) == 8
TIAPIARegs.REFP1 -> sprites[TIAPIARegs.ISPRITE_PLAYER1].mirror = (value and 8) == 8

That just about covers all the write registers for manipulating sprites, but there are also a set of eight readable registers that are used for handling collision detection. This is a bit complex of a subject, though is a lot easier to use than one would expect and will be covered next time.

Saturday, August 31, 2019

Sprites are Player Missile Graphics

To plan out the classes that I would use for creating the player and missile sprites I put together the following table which shows the various registers used for the different player and missiles as well as how the different objects compare to each other. Table 1 is a handy chart to have for programming the 2600 and wish I would have created it when I first started playing around with the 2600!


The original plan was to have a base class that held the common functionality and have the player class and missile/ball classes override this class. This is probably the “proper” way to do this but the classes are so similar that having them combined into a single class seemed like the easiest solution. A missile or ball is essentially a sprite that is only 1 pixel so by simply tracking the number of pixels as size, all the different player missile graphics can be incorporated.

Sprites have a collision mask that will be used for determining which collision flags they are part of. This will be explained later when we implement collisions but for now it can be thought of as simply a magic number with each bit in the number representing a particular pair of objects that can collide with each other.

Scale is how many pixels to draw for each pixel in the sprite. The 2600 supports scales of 1, 2, 4, and 8 with player sprites limited to a maximum scale of 4. The number of copies is how many times to draw the sprite while the gap is the distance (in multiples of the size) between the copies. The 2600 supports distances of 2, 4 and 8.

The sprite is placed on the screen at the x location with the deltaX variable being how much to alter the x position when an HMOVE command is issued. Normally we do not want the sprite to move so the default is 0.

The drawing bits are the byte used to represent the image in the sprite at that horizontal scanline. It can be considered a bit of a kludge when using missiles and balls as only the one bit is used. Mirroring is used to indicate which order the pixels should be drawn. For balls and missiles it will mirror the single pixel but as the result of that is the original image there is not much use for size 1 sprites.

class PlayerMissileGraphic(var size:Int, var collisionMask:Int) {
var scale = 1
var copies = 1
var distanceBetweenCopies = 0   // close = 2, med = 4 far = 8
var x = 0
var deltaX = 0
var drawingBits = 0
var delayedDraw = false
var mirror = false

The heart of the sprite class is the isPixelDrawn method which simply determines if the current state of the sprite would result in a pixel being drawn at the indicated location. This is simply a matter of looping through the number of copies of the sprite and for each copy being drawn figure out where it will be drawn. We know the number of pixels that a copy will occupy as it is simply the size times the scale so if the column being drawn is within that range we figure out which pixel in the image is at that location and if that bit is 1 then we are drawing otherwise we are not. Calculating which bit in the drawing data to draw depends on whether we are mirroring the sprite and the scale of the pixels.

fun isPixelDrawn(col:Int):Boolean {
if (col < x) return false
if (delayedDraw) return false

var pixelAtCol = false
for (copyCount in 1..copies) {
val copyStart = x + (copyCount - 1) * size * scale * distanceBetweenCopies
val copyEnd = copyStart + size * scale
if ((col >= copyStart) and (col < copyEnd)) {
val bitPos = (col - copyStart) / scale
val bit = if (mirror) 1 shl bitPos else (1 shl (size-1)) ushr bitPos
if ((bit and drawingBits) > 0) pixelAtCol = true
}
}

return pixelAtCol
}


The HMOVE command is processed simply by adjusting the x variable by the movement delta with a bit of clamping code to make sure it is within the range of the display. My clamping is done by nesting a max function within a min function by having the highest column be the minimum to compare against and 0 and the target column being the max to pick from. As a result the max will clip away negative values making them 0 while the min will clip away off-right-edge values making them the right edge.

fun hmove() {
x = min(159, max(0, x + deltaX))
}


The TIA player sprites have 8 different modes as shown by the chart below. While this could be handled by the TIA register calls, there would be duplication of code so I wrote a simple utility method that will set the sprite scale, copies, and distance based on a player sprite mode. This method is just a simple when statement which sets the parameters appropriately based on the indicated mode. The different play modes that the TIA recognizes is in table 2.



fun setPlayerScaleCopy(scmode:Int) {
when (scmode) {
TIAPIARegs.PMG_NORMAL ->
{scale = 1; copies = 1; distanceBetweenCopies = 1}
TIAPIARegs.PMG_TWO_CLOSE  ->
{scale = 1; copies = 2; distanceBetweenCopies = 2}
TIAPIARegs.PMG_TWO_MEDIUM  ->
{scale = 1; copies = 2; distanceBetweenCopies = 4}
TIAPIARegs.PMG_THREE_CLOSE ->
{scale = 1; copies = 3; distanceBetweenCopies = 2}
TIAPIARegs.PMG_TWO_WIDE  ->
{scale = 1; copies = 2; distanceBetweenCopies = 8}
TIAPIARegs.PMG_DOUBLE_SIZE  ->
{scale = 2; copies = 1; distanceBetweenCopies = 1}
TIAPIARegs.PMG_THREE_MEDIUM  ->
{scale = 1; copies = 3; distanceBetweenCopies = 4}
TIAPIARegs.PMG_QUAD_PLAYER  ->
{scale = 4; copies = 1; distanceBetweenCopies = 1}
}
}

We now have a class that can handle the sprites in the TIA but we still have to handle the TIA registers related to drawing the sprites and perform the drawing of the sprites when the color clock reaches them. This will be what we do next.

Saturday, August 17, 2019

The 2600 Play-field

The play-field is a 40-bit image that is stretched across the 160 pixels that make up a scan-line. This means that each play-field bit occupies 4 pixels. The original purpose behind the play-field is to create the map or maze that the player occupies. It is often used for other things such as drawing scores, background images or title screens. Anything that can be represented using crude graphics is fine.

The play-field is managed using the CTRLPF, PF0, PF1 and PF2 registers. The CTRLPF register controls how the play-field will be displayed controlling the display order of the play-field, whether the bits should be repeated or mirrored, and if the play-field should be drawn using the play-field color or drawn using player 0 color on the left and player 1 color on the right. This register also handles scaling of the ball which will be covered later.

The PF0, PF1 and PF2 registers hold the 20 bits that are to be drawn. These bits will either be repeated or mirrored based on the settings in CTRLPF. As with color, you can change the play-field bits in the middle of drawing so that the right side of the screen is different from the left side of the screen. The order of the bits is weird which is likely a by-product of keeping the transistor count of the chip down. The image below shows the order of the pixels that make up the display.



Essentially the play-field drawing starts with the 4 bits in the PF0 register drawn in low to high order. This is followed by the eight bits from PF1 in high to low order. Finally the PF2 bits are drawn in low to high order.  This is repeated or mirrored for the right half of the screen. As many games used symmetrical maps this made it easy to set up the play-field bits and simply mirror them. Asymmetric maps are a bit trickier as you need to set up the PF0, PF1, and PF2 registers for the left half of the screen then wait until the beam is past PF0 to change PF0 to the right side PF0, wait until the beam is past PF1 to change to the right side PF1 and wait until the beam is past PF2 to change to the right side PF2. While you could theoretically do this while mirroring, asymmetrical images are easier to do with repeated mode unless you don’t need to change PF2.

The hard part about implementing the play-field is dealing with the different orientations of the data. My internal representation of the play-field data is simply a 20 bit integer with the bits being the play-field bits from left to right. When one of the PF registers is called, the appropriate bits of this play-field value are set. This requires that we flip the bits, but this is a simple process of using and to see if a bit is set or to build the reversed byte and shifts to get the bits aligned properly.

fun reversePFBits(value:Int):Int {
var reversed = 0
var testBit = 1
for (cntr in 0..7) {
reversed *= 2
reversed += if ((testBit and value) > 0) 1 else 0
testBit *= 2
}
return reversed
}

The calls to the playfield register simply take the value that is to be written to the register and, if necessary, reverse the bits. It then masks out the bits in the playfield that the register represents, shifts the bits to the appropriate part of the playfield then uses a logical or to put the new value in place. The playfield control register CTRLPF simply sets flags to indicate how the playfield should be rendered. The flags are mirror to indicate if mirroring should be taking place, scoreMode to determine if we are drawing using the playfield color or the player 0 color on the left and the player 1 color on the right. The priorityPlayfield flag determines if the playfield should be drawn first or last.

TIAPIARegs.PF0 -> {
var bits = reversePFBits(value)
playfieldBits = (playfieldBits and 0xFFFF) or (bits shl(16))
}
TIAPIARegs.PF1 -> {
var bits = value
playfieldBits = (playfieldBits and 0xF00FF) or (bits shl(8))
}
TIAPIARegs.PF2 -> {
var bits = reversePFBits(value)
playfieldBits = (playfieldBits and 0xFFF00) or bits
}
TIAPIARegs.CTRLPF -> {
mirror = (value and 1) == 1             // bit 0 handles mirror mode
scoreMode = (value and 2) == 2          // bit 1 handles score mode
priorityPlayfield = (value and 4) == 4  // bit 2 Places playfield above sprites
}

To draw the play-field we simply determine which play-field pixel should be processed. The left is simply the column being drawn divided by 4. The right is the same (less 80) if repeated and the inverse if mirrored. Once we know the bit to be checked we can determine if there is a pixel there and if so set the color based on the scoreMode. We then adjust our collision mask which will be explained in the collisions section later but is essentially a set of bits indicating which collisions have happened.


val pfCol = if (column < 80) column / 4 else
if (mirror) (159 - column) / 4 else (column - 80) / 4
var pfPixelMask = 0x80000 shr pfCol
val shouldDrawPlayfield =(playfieldBits and pfPixelMask) > 0
val pfColor = if (scoreMode) {if (column < 80) playerMissile0Color else
playerMissile1Color } else playfieldBallColor
if ( ! shouldDrawPlayfield) collisionMask = collisionMask and 31668


At the start of the drawing of the pixel, we check to see if the playfield is normal priority and if so draw it.

if ((shouldDrawPlayfield) and ( ! priorityPlayfield )) pixelColor = pfColor
// TODO sprite drawing code here 
if ((shouldDrawPlayfield) and (priorityPlayfield)) pixelColor = pfColor


Finally, at the end of the drawing of a pixel, we see if the playfield is priority and if so draw it. Between these two checks is the sprite drawing which is the next thing to be implemented.

Saturday, August 3, 2019

Drawing Pixels on 2600 TIA

In the 2600, the TIA is drawing the display in real time by sending signals to the television. There was no frame buffer in the TIA. With an emulator, our situation is slightly different. As I want the TIA emulator to work in several different platforms, a cross-platform way of generating the display is needed. My solution is to simply have a 160 “pixel” raster line that the TIA emulator writes to. Whenever a scan-line is finished, or part way through drawing the scan-line if you are doing some type of stepping through the program, the class managing the display will grab this information and use the TIAColors information to build a display appropriate to the platform that is being rendered.

The figure below shows the logic that the TIA uses to determine which of the four colors to place on a pixel. We start with the background color. This would be the background color at the time that the pixel was drawn. As the color registers can be changed in the middle of rendering a line this can be a distinct color at different points along the scan-line.



The order that things are drawn depends on flags in the CTRLPF register. Normally the play-field is drawn right after the background but if bit 1 of the CTRLPF is set then the play-field and ball will be drawn after the players and missiles. Player 1 and the missile associated with that player is drawn before player 0. So if CTRLPF bit 1 is set the drawing order is background, player and missile 1, player and missile 0, play-field and ball. Otherwise the drawing order is background, play-field and ball , player and missile 1, player and missile 0.

This drawing order determines what the final pixel color will be in cases where multiple display objects overlap a given pixel. The 2600 goes even further by tracking the overlaps and setting a series of collision flags that can be tested to see if there were any collision between display objects when the line was being drawn.

This effectively means that we have six things to potentially draw in any given pixel. Three of these --  the ball, missile 0, and missile 1 – are simply a line so they can be grouped into a common class. The two player sprites are not that much more complicated so while they could be a separate class from the ball and missile, making a more complicated player missile graphics class to handle all five of these display objects makes sense.

This leaves us with the play-field. This is my favorite part of the 2600 rasterizer as it is the closest thing you have to traditional bitmap graphics.

Saturday, July 20, 2019

Color Me TIA

One of the big, if not the biggest, challenge of programming the 2600 was simply getting the scan-lines to be rendered properly. You are drawing the display as it is being shown and only have 76 cycles per scan line to set up the registers. As with many graphics processors, the TIA can be thought of as a state machine with the values contained in the registers at the end of a scan line being retained at the start of the next scan-line. This fact must be considered when writing your renderer, known as a kernel in 2600 parlance. A well-designed kernel will take advantage of this fact while graphical artifacts will result from a poorly designed kernel.

Quick math shows that 160 pixels with only 76 cycles means that more than a single pixel is being drawn per CPU cycle. The TIA has its own clock which runs three times the speed of the CPU. This means that there are 228 TIA cycles per scan-line. To distinguish TIA cycles from CPU cycles, the TIA cycles have been given the name color clocks. The first 68 of these color clock ticks are used for horizontal blanking so do not draw anything to the screen but does give the kernel time to set up some of the TIA registers. The remaining 160 color clocks draw the pixels of the display based on the register settings at the time a pixel is being drawn.

Pixels can be one of four colors based on what is being drawn in that location. The background color is what is used if there is nothing to draw in that location. The play-field color is the color of the play-field and the ball. Finally each player and their missile have a color.

The colors are selected from a palette of 128 colors by simply setting the appropriate color register to the desired color. The color byte is in the format HHHHLLLX with the 4 H bits being the Hue, the L bits being the Luminosity, and X being unused. The hue is the base color of the pixel while the luminosity is the brightness of the pixel. While the color could be calculated, with only 128 colors it is much quicker just to have a table of colors.

class TIAColors() {
val colorTable:Array<Int> = arrayOf(
// Hue 0
0xFF000000.toInt(),0xFF000000.toInt(),0xFF404040.toInt(), 0xFF404040.toInt(),0xFF6c6c6c.toInt(), 0xFF6c6c6c.toInt(),0xFF909090.toInt(), 0xFF909090.toInt(),
0xFFb0b0b0.toInt(),0xFFb0b0b0.toInt(),0xFFc8c8c8.toInt(),0xFFc8c8c8.toInt(),
0xFFdcdcdc.toInt(),0xFFdcdcdc.toInt(),0xFFececec.toInt(),0xFFececec.toInt(),
// Hue 1
0xFF444400.toInt(),0xFF444400.toInt(),0xFF646410.toInt(),0xFF646410.toInt(),
0xFF848424.toInt(),0xFF848424.toInt(),0xFFa0a034.toInt(),0xFFa0a034.toInt(),
0xFFb8b840.toInt(),0xFFb8b840.toInt(),0xFFd0d050.toInt(),0xFFd0d050.toInt(),
0xFFe8e85c.toInt(),0xFFe8e85c.toInt(),0xFFfcfc68.toInt(),0xFFfcfc68.toInt(),
//Hue 2
0xFF702800.toInt(),0xFF702800.toInt(),0xFF844414.toInt(),0xFF844414.toInt(),
0xFF985c28.toInt(),0xFF985c28.toInt(),0xFFac783c.toInt(),0xFFac783c.toInt(),
0xFFbc8c4c.toInt(),0xFFbc8c4c.toInt(),0xFFcca05c.toInt(),0xFFcca05c.toInt(),
0xFFdcb468.toInt(),0xFFdcb468.toInt(),0xFFecc878.toInt(),0xFFecc878.toInt(),
// Hue 3
0xFF841800.toInt(),0xFF841800.toInt(),0xFF983418.toInt(),0xFF983418.toInt(),
0xFFac5030.toInt(),0xFFac5030.toInt(),0xFFc06848.toInt(),0xFFc06848.toInt(),
0xFFd0805c.toInt(),0xFFd0805c.toInt(),0xFFe09470.toInt(),0xFFe09470.toInt(),
0xFFeca880.toInt(),0xFFeca880.toInt(),0xFFfcbc94.toInt(),0xFFfcbc94.toInt(),
// Hue 4
0xFF880000.toInt(),0xFF880000.toInt(),0xFF9c2020.toInt(),0xFF9c2020.toInt(),
0xFFb03c3c.toInt(),0xFFb03c3c.toInt(),0xFFc05858.toInt(),0xFFc05858.toInt(),
0xFFd07070.toInt(),0xFFd07070.toInt(),0xFFe08888.toInt(),0xFFe08888.toInt(),
0xFFeca0a0.toInt(),0xFFeca0a0.toInt(),0xFFfcb4b4.toInt(),0xFFfcb4b4.toInt(),
// Hue 5
0xFF78005c.toInt(),0xFF78005c.toInt(),0xFF8c2074.toInt(),0xFF8c2074.toInt(),
0xFFa03c88.toInt(),0xFFa03c88.toInt(),0xFFb0589c.toInt(),0xFFb0589c.toInt(),
0xFFc070b0.toInt(),0xFFc070b0.toInt(),0xFFd084c0.toInt(),0xFFd084c0.toInt(),
0xFFdc9cd0.toInt(),0xFFdc9cd0.toInt(),0xFFecb0e0.toInt(),0xFFecb0e0.toInt(),
// Hue 6
0xFF480078.toInt(),0xFF480078.toInt(),0xFF602090.toInt(),0xFF602090.toInt(),
0xFF783ca4.toInt(),0xFF783ca4.toInt(),0xFF8c58b8.toInt(),0xFF8c58b8.toInt(),
0xFFa070cc.toInt(),0xFFa070cc.toInt(),0xFFb484dc.toInt(),0xFFb484dc.toInt(),
0xFFc49cec.toInt(),0xFFc49cec.toInt(),0xFFd4b0fc.toInt(),0xFFd4b0fc.toInt(),
// Hue 7
0xFF140084.toInt(),0xFF140084.toInt(),0xFF302098.toInt(),0xFF302098.toInt(),
0xFF4c3cac.toInt(),0xFF4c3cac.toInt(),0xFF6858c0.toInt(),0xFF6858c0.toInt(),
0xFF7c70d0.toInt(),0xFF7c70d0.toInt(),0xFF9488e0.toInt(),0xFF9488e0.toInt(),
0xFFa8a0ec.toInt(),0xFFa8a0ec.toInt(),0xFFbcb4fc.toInt(),0xFFbcb4fc.toInt(),
// Hue 8
0xFF000088.toInt(),0xFF000088.toInt(),0xFF1c209c.toInt(),0xFF1c209c.toInt(),
0xFF3840b0.toInt(),0xFF3840b0.toInt(),0xFF505cc0.toInt(),0xFF505cc0.toInt(),
0xFF6874d0.toInt(),0xFF6874d0.toInt(),0xFF7c8ce0.toInt(),0xFF7c8ce0.toInt(),
0xFF90a4ec.toInt(),0xFF90a4ec.toInt(),0xFFa4b8fc.toInt(),0xFFa4b8fc.toInt(),
// Hue 9
0xFF00187c.toInt(),0xFF00187c.toInt(),0xFF1c3890.toInt(),0xFF1c3890.toInt(),
0xFF3854a8.toInt(),0xFF3854a8.toInt(),0xFF5070bc.toInt(),0xFF5070bc.toInt(),
0xFF6888cc.toInt(),0xFF6888cc.toInt(),0xFF7c9cdc.toInt(),0xFF7c9cdc.toInt(),
0xFF90b4ec.toInt(),0xFF90b4ec.toInt(),0xFFa4c8fc.toInt(),0xFFa4c8fc.toInt(),
// Hue 10
0xFF002c5c.toInt(),0xFF002c5c.toInt(),0xFF1c4c78.toInt(),0xFF1c4c78.toInt(),
0xFF386890.toInt(),0xFF386890.toInt(),0xFF5084ac.toInt(),0xFF5084ac.toInt(),
0xFF689cc0.toInt(),0xFF689cc0.toInt(),0xFF7cb4d4.toInt(),0xFF7cb4d4.toInt(),
0xFF90cce8.toInt(),0xFF90cce8.toInt(),0xFFa4e0fc.toInt(),0xFFa4e0fc.toInt(),
// Hue 11
0xFF003c2c.toInt(),0xFF003c2c.toInt(),0xFF1c5c48.toInt(),0xFF1c5c48.toInt(),
0xFF387c64.toInt(),0xFF387c64.toInt(),0xFF509c80.toInt(),0xFF509c80.toInt(),
0xFF68b494.toInt(),0xFF68b494.toInt(),0xFF7cd0ac.toInt(),0xFF7cd0ac.toInt(),
0xFF90e4c0.toInt(),0xFF90e4c0.toInt(),0xFFa4fcd4.toInt(),0xFFa4fcd4.toInt(),
// Hue 12
0xFF003c00.toInt(),0xFF003c00.toInt(),0xFF205c20.toInt(),0xFF205c20.toInt(),
0xFF407c40.toInt(),0xFF407c40.toInt(),0xFF5c9c5c.toInt(),0xFF5c9c5c.toInt(),
0xFF74b474.toInt(),0xFF74b474.toInt(),0xFF8cd08c.toInt(),0xFF8cd08c.toInt(),
0xFFa4e4a4.toInt(),0xFFa4e4a4.toInt(),0xFFb8fcb8.toInt(),0xFFb8fcb8.toInt(),
// Hue 13
0xFF143800.toInt(),0xFF143800.toInt(),0xFF345c1c.toInt(),0xFF345c1c.toInt(),
0xFF507c38.toInt(),0xFF507c38.toInt(),0xFF6c9850.toInt(),0xFF6c9850.toInt(),
0xFF84b468.toInt(),0xFF84b468.toInt(),0xFF9ccc7c.toInt(),0xFF9ccc7c.toInt(),
0xFFb4e490.toInt(),0xFFb4e490.toInt(),0xFFc8fca4.toInt(),0xFFc8fca4.toInt(),
// Hue 14
0xFF2c3000.toInt(),0xFF2c3000.toInt(),0xFF4c501c.toInt(),0xFF4c501c.toInt(),
0xFF687034.toInt(),0xFF687034.toInt(),0xFF848c4c.toInt(),0xFF848c4c.toInt(),
0xFF9ca864.toInt(),0xFF9ca864.toInt(),0xFFb4c078.toInt(),0xFFb4c078.toInt(),
0xFFccd488.toInt(),0xFFccd488.toInt(),0xFFe0ec9c.toInt(),0xFFe0ec9c.toInt(),
// Hue 15
0xFF442800.toInt(),0xFF442800.toInt(),0xFF644818.toInt(),0xFF644818.toInt(),
0xFF846830.toInt(),0xFF846830.toInt(),0xFFa08444.toInt(),0xFFa08444.toInt(),
0xFFb89c58.toInt(),0xFFb89c58.toInt(),0xFFd0b46c.toInt(),0xFFd0b46c.toInt(),
0xFFe8cc7c.toInt(),0xFFe8cc7c.toInt(),0xFFfce08c.toInt(),0xFFfce08c.toInt()
)

fun getARGB(indx:Int):Int {
if ((indx < 0) or (indx > 255))
return 0
else
return colorTable[indx]
}

fun getHTMLColor(indx:Int):String {
val noAlphaColor = getARGB(indx) and 0xFFFFFF
return "#${noAlphaColor.toString(16)}"
}
}

To aid in the selection of color values, my TIA testing program has a palette picker as seen in figure below.



This leaves the color clock. A simple function for handling a clock tick is simple enough to implement. I simply determine the pixel being rendered by subtracting the horizontal sync time from the color clock tick. If the column is negative, we don’t do anything otherwise we process the pixel at that column. The processing of the pixel will be implemented over the next several articles. Once the pixel is processed, we increment the color clock. If the color clock has reached it’s end we reset the color clock to zero and return true to indicate to the caller that the scanline is finished.

fun nextClockTick():Boolean {
// run current pixel
val column = colorClock - 68
if (column >= 0) {
var pixelColor = backgroundColor

// TODO render playfield
// TODO render player-missile graphics and set collisions

rasterLine[column] = pixelColor
}
++colorClock
return if (colorClock >= 228) {
colorClock = 0
true
} else false
}


Setting the colors is very simple as you simply set the appropriate color register to the desired value. These registers are COLUBK for the background color, COLUPF for the play-field and ball color, COLUP0 for player 0 and missile 0, and COLUP1 for player 1 and missile 1.

fun writeRegister(address:Int, value:Int) {
  when (address) {
    TIAPIARegs.COLUBK -> backgroundColor = value
    TIAPIARegs.COLUPF -> playfieldBallColor = value
    TIAPIARegs.COLUP0 -> playerMissile0Color = value
    TIAPIARegs.COLUP1 -> playerMissile1Color = value
    else -> println("TIA register $address not implemented!")
  }
}