Friday, August 8, 2014

NES Trivia Screen

As usual I am only going to cover the snippets of code I consider most relevant for the discussion. For those of you who want to see all the code, it is located at @link to source code here@.

The core of the game is fairly simple. We start by clearing the screen and displaying the label for the current question. As the label always takes the form of “Question ???” we save a bit of memory by having the label string only create the ??? part of the string generating the full string on the fly. The label is underlined. This is done by taking advantage of the fact that the PrintString function returns the length of the printed string in the Y register. Notice that the use of macros makes the code quite a bit more human-readable.

CallClearScreen ' ',0,0
CallStrNCpy baseLabel, SCRATCH_PAGE, 0
LDY #0
LDA [QUESTION_INFO_PTR],Y
STA SOURCE_PTR
INY
LDA [QUESTION_INFO_PTR],Y
STA SOURCE_PTR+1
JSR AppendString

; once created we print it
CallSetScreenXY 2,1,0
CallPrintString SCRATCH_PAGE
; underline Title
TYA
PHA ; save length of string to underline
CallSetScreenXY 2,2,0
PLA ; retrieve length of label string
TAX ; transfer to appropriate parameter for call
LDA #'='
JSR PrintRepeatedChar

Printing the question, answers, and later the explanation is all pretty much the same code but with different pointers being used, this is ideal code for a function. The desired string to be printed is part of the question info structure. We set Y to the offset within this structure to the pointer for the string we want printed. In case you are wondering, the term for a pointer to a pointer is called a handle. We copy this pointer to the zero-page SOURCE_PTR variable then call the PrintString function.

PrintQuestionInfoString:
LDA [QUESTION_INFO_PTR],Y
STA SOURCE_PTR
INY
LDA [QUESTION_INFO_PTR],Y
STA SOURCE_PTR+1
JSR PrintString

Now, as we did for underlining, we take advantage of the returned length value to determine how many rows were used for printing the string. I should point out that this is not always correct as the answers are indented by a couple of characters. This generally is not a problem but is something that has to be kept in mind when creating the data for the game. Divide the length printed by 32 to determine how many lines were printed. It is important to remember that the remainder from the divide is dropped. This means we need to add 1 to the result. As we are adding a blank line between rows this means we need to add 2 to get the appropriate next row.

TYA ; divide printed length by 32
LSR A
LSR A
LSR A
LSR A
LSR A
CLC ; add it to current printed row so we know what line we are on
ADC CURRENT_ROW
CLC ; add 2 so we know appropriate row for next print
ADC #2
STA CURRENT_ROW
RTS

While tracking the row for printing is important, we also need this information for handling the player choice. After calling the above function to print the question, we store the returned row number which will be the location of the first answer. We then call the function again for each of the answers storing the rows in subsequent memory locations. This is the equivalent of creating an array of rows. You may have noticed that there is an extra row stored. This will be used to display the results and explanation.

The players choice is represented by a right arrow sprite. The column the sprite appears on is set to 4 when the sprite is set up and never changes. The vertical placement of this sprite is based on the current choice that is selected which is stored in the CURRENT_ANSWER variable. The value in this variable is used to index the rows table we stored the row in above to determine the Y coordinate of the sprite.

LDY CURRENT_ANSWER
LDA ANSWER_ROWS,Y
ASL A
ASL A
ASL A
STA SPRITE_PAGE

At this point we have the question displayed with an arrow to the currently selected answer. Getting player input and determining if the selected question is our next task.

No comments: