Tuesday, December 29, 2015

Monday, December 28, 2015

Day 11: Juventus supporters can't jump!!!! (Part 2)


Today we will see briefly the conditional jumps, or branches. The name branch comes from the fact the code may or may not jump to a certain  location in memory, depending if a condition is true or not. Branching is the equivalent of the Basic IF GOTO construct.


The Status Register as I wrote yesterday, is the onboard cpu register which stores some flags. The flags are side-effects of instructions. Normally instructions will manipulate and store the data in the accumulator, but depending on the result the CPU will set (put to 1) or reset (put to 0) some bits in this register.


The most important bits of the Status Register are:


Zero Flag
Every instructions that results in a zero, including transfers to accumulator or the x and y registers, will set this flag to 1, while any other result will set it to 0.


Carry Flag
Indicates if a mathematical instruction result in a situation where you will have to carry its most significant byte to another byte. It’s not the same as the overflow flag.


Overflow Flag
Indicates when the result of the operation sets the 7th bit. It’s a more restricted case of the Carry Flag.


Negative Flag
Indicates if the result is to be considered “negative”.


There are three other flags that are not used with branching, but they are present in the Status Register.


Decimal Flag,
Iit’s used to set the computer mathematical operations in “decimal” mode. Normally the 6510 will operate on bits with normal binary maths. If the decimal flag is set then the CPU will instead use the encoding and rules of Binary Coded Decimal, which trades off some ram (every byte coded in BCD will store only one out of 100 values instead of one out of 255, so it’s less compact) for getting more precise calculations.


Break Flag,
To be used to test if the processor returned from a BRK (break) instruction. I don’t know much about this flag yet.


Interrupt flag,
This  indicates if the CPU is currently executing an interrupt sequence. Interrupts are special ways a machine has to hijack control from a central program to handle important events like input or output operations (for example, when the user presses a button or when the TV finishes rendering a screen).


How and why to test for the flags


The Zero Flag
Testing for the zero flag is useful, for example, in situations where you have to test a FOR-style loop or when you do comparisons on bytes.


BEQ
with BEQ you test on the result of the previous operation being EQUAL to zero (Z  = 1)


   LDX    #$20
LOOP
   DEX
   BEQ    EXIT
   JMP    LOOP
EXIT
   ...


BNE
with BNE you test on the result of the previous operation  being NOT EQUAL to zero (Z = 0)


   LDX    #$20
LOOP
   DEX
   BNE    LOOP
   ...


Note that a comparison between two numbers can also set the zero flag if both numbers are equal.


For BEQ:


LDX    #$20
LDA    #$20
STX    $03FF
CMP    $03FF
BEQ    OK
...


For BNE:


LDX    #$20
LDA    #$21
STX    $03FF
CMP    $03FF
BNE    OK
...


The Negatifve Flag.
The Negative flag is useful, again, for countdowns, or is useful too in mathematical operations or other comparisons. You can test the Negative Flag with the following operations:


BMI
You use this to test if the result is negative (N = 1)


LDX    #$20
LDA    #$1F
STX    $03FF
CMP    $03FF
BMI    OK
...


BPL
You use this to test if the result is positive (N = 0)


LDX    #$20
LDA    #$21
STX    $03FF
CMP    $03FF
BPL    OK
...


The Carry Flag.
The Carry flag is useful with mathematical calculations. With an 8 bit computer you are not limited to work on 8 bit values, but you can chain those values to represent an higher precision number spending a bit more memory. Note that the Carry flag is automatically used by several mathematical operations as well. You can also see a bit more info on the C64 Wiki.


BCS
By now you can imagine that Branch on Carry Set branches when the Carry flag is set (C = 1)


LDA    #$FF
ADC    #$01
BCS    OK
...


BCC
Branch on carry clear instead jumps only if the Carry flag is reset (C = 0)


LDA    #$FE
ADC    #$01
BCC    OK
...


The Overflow Flag
There is a more limited number of instructions that will set the overflow flag, actually there are only two mathematical instructions (ADC and SBC) that will set this (and the BIT comparison instruction, but apparently BIT is rarely used). The Overflow flag is set when a 7-bit operation makes the number overflow in the 8th bit. If you add 1 to 127, you will obtain 128, which is represented like this: 10000000. In that case the ADC will remind you that, if you are using 7-bit precision numbers, to check the overflow flag if you need to test for SIGNED numbers, but you can ignore this flag instead if you are using UNSIGNED numbers. As usual, the C64 wiki explains stuff better than me.


BVS
Branch on Overflow Set (V = 1)


LDA    #$7F
ADC    #$01
BVS    OK
...


BVC
Branch on Overflow Clear (or “reset”) (V = 0)


LDA    #$7E
ADC    #$01
BVC    OK
...


The following is the code I used to test the various snippets.


*=$0810
PREAMBLE
       CLD
MAIN ; you can change the parts between MAIN and KO
       LDA     #$7F
       ADC     #$01
       BVC     OK
KO ; if your calculation is wrong the program should print two black @'s
       LDA     #$00
       STA     $0400
       STA     $0401
       LDX     #$00     
       STX     $D800
       STX     $D801
       RTS
OK ; if your calculation is correct the code should print two white A's
       LDA     #$01    
       STA     $0400
       STA     $0401
       LDX     #$01     
       STX     $D800
       STX     $D801
       RTS

; The following is how the machine stores the SYS 2080 command on
; line 10 of the Basic interpreter
; 10 SYS 2064

*=$801
       BYTE    $0B, $08, $0A, $00, $9E, $20, $32, $30, $36, $34, $00, $00, $00

Sunday, December 27, 2015

Day 10: Juventus supporters can't jump!!!! (Part 1)

Code rarely goes in a straightforward motion. Actually to do something useful we must teach the code how to jump to the correct parts, to stop when it needs to, to call subroutines... Before seeing Jumps we need to know about three processor registers:

The Program Counter (PC) is a 16-bit register containing the address of the instruction the processor will read next.

When the current instruction is read, the processor calculates (based on the current opcode and the operands it believes come next) the address of the following instruction. When the cpu finishes processing the current instruction then it will use the value in the program counter to continue the operation.

The Stack Pointer (SP) is an 8-bit register containing an index which points to the current top of  a data structure called the stack.

The stack, on the 6510 cpu, is an area of memory reserved for keeping return addresses for subroutines, in case you need to make nested function calls. You can push data on the stack, and then get it back from it, or pop it, with a last-in-first-out precedence rule. The Stack Pointer only needs 8 bits, because it’s a fixed section of memory starting from $0100 and ending with $01FF.

The 6510 cpu reserves a very tiny space for its own stack because programming the 6510 you are expected to store the accumulator, the CPU status register and the CPU program counter. It’s a design choice that is sensible considering the era when it was created (its father, the 6502, was created in 1975) and the scarcity of RAM available for computer programmers. Modern CPUs allow programmers to keep complex data on the stack (function parameters, function results), and each process actually manages its own stack.

Note that the stack is built downwards, so the first value you will push (insert in it) will be in $01FF, the second $01FE.

The Status Register is an 8-bit register containing some flags. Several instructions will set or reset the single bits in the Status Register, and most jump instructions will test those bits to decide if to jump or not. We will see the Status Register more in detail with the next part, when we will talk about conditional jumps.

JMP - Unconditional Jump

JMP   LABEL   ; here we will use an assembler LABEL.
JMP   $0820   ; here we will directly point to a certain address
; in memory.

The unconditional jump resembles the GOTO instruction in basic. You are telling the CPU to set the Program Counter to the operand, either the label or the memory address.

Note that the assembler software calculates the correct memory address for a LABEL automatically. It’s better to use (and abuse) LABELs because if you need to amend your code you don’t need to calculate the memory area, or worse, the number of bytes you need to pass to the operand.

Example:

   LDA #$01
   JMP PRINT
RETURN_LABEL
    RTS
PRINT
   STA $0400
   STA $D800
JMP RETURN_LABEL

JSR - Jump to Subroutine

JSR LABEL
JSR $0820

Jump to Subroutine is an unconditional jump too, with a key difference from JMP: it’s used together with the RTS (return from subroutine) instruction.

JSR and RTS are conceptually equivalent to the basic GOSUB and RETURN statements.

Technically speaking, JSR will PUSH the current Program Counter on the stack, or better said record the two bytes of the PC on the stack and decrease the value in the SP register by two. RTS instead will POP the PC from the Stack, or better said, increase the value in the SP register  by 2 read from the stack the value of the last program counter recorded on it.

Example:

   LDA #$01
   JSR PRINT
   RTS ; this RTS halts execution of your program and 
; eventually  gives the control back to the C64 
; operating system
PRINT
   STA $0400
   STA $D800

   RTS ; this RTS “returns” from the PRINT subroutine

Next time we will see conditional jumps, or branches.

Extra: Behind the scenes of a C64 demo




An interesting lesson on what lies behind a C64 demo.

Thursday, December 17, 2015

Day 9, Towards Multichannel Sid Programming (Part 2)

Yesterday I was going to find how to write this program:

    GOSUB SET-UP-SID
# we set up the continue variables here
# we want to have them visible in the main loop
    CO% = 1 #
# we can also initialize further variables here
    [...]
LOOP: 
# this is how we can emulate a while-true cycle in basic
    IF CO% = 0 GOTO END-LOOP 
# now we will read the notes and upload them to the correct channel
    [...]
# now we will wait 1/4 of a second
    [...]
# and after it we jump back
    GOTO LOOP
END-LOOP: 
    GOSUB RESET-SID
    END

SET-UP-SID:
# rem here we will set up the channels with the correct instruments
    # We already did this
    RETURN

RESET-SID:
# rem here we will clear the SID
    # We already did this
    RETURN

And now I am going to continue it.

Please note that this formalism has nothing to do with how someone else would approach compuer programming. In the good old Basic Days planning the software was done using Flow charts. Nowadays Flow charts aren't neither taught nor used, and someone expects you to just fill random methods provided by your framework or choice in random points of the application.

How do we wait for one second?

I'm choosing to tackle this problem next because I need to have a sort of slowdown in place before reading the notes and putting them into the SID registers. The Commodore 64 basic gives two commands to read the time: TIME and TIME$. Due to architectural limitations they will give the time from the last system boot (including when you soft-reset the computer with SYS 64738). In addition to it the TIME is stopped when the computer is serializing data. As a last limitation I can think is that TIME is given pace from the current video signal coming out from the Commodore (an NTSC commodore will have a different timing from a PAL commodore, but I have to check).

The difference between the two instructions is that TIME$ will print the time as seconds and it should be text while TIME will print the time as 60th of seconds and will be a number.

We can type this program in and see the two commands in action.

10 FOR I = 1 TO 24
20 PRINT TIME ; TIME$
30 REM WE WANT A SLOWDOWN BEFORE
31 REM PRINTING THE NEXT VALUE OF TIME
35 FOR J = 1 TO 100 : NEXT J
40 NEXT I

Armed with this insight we can speculate that we can store the current time in a variable, test if 60 jiffies have passed and print the new time.

10 PRINT "{CLR/HOME}" ; TIME$ , TIME
20 TT = TIME + 60
30 IF TT =< TIME GOTO 10
40 REM IF YOU PRESS ANY KEY THE PROGRAM WILL END
41 GET A$ : IF A$ <> "" THEN END
50 GOTO 30

This is not perfect but for now it will do.

You can see that we have to change the central loop

# since this is a value that does not change we calculate it 
# here, just once
    DT = INT(60 / 4)
LOOP:
    TT = TIME + DT
# this is how we can emulate a while-true cycle in basic
    IF CO% = 0 GOTO END-LOOP 
# now we will read the notes and upload them to the correct 
# channel
    [...]
# now we will wait 1/4 of a second
# and after it we jump back
CHECK-TIME:
    IF TT =< TIME GOTO LOOP
    GOTO CHECK-TIME

This does not complete all the reasoning about time because now we will have to test if the time has come for reading our notes in the SID.

Reading and playing the note data.

The following part is tricky, because we still need to do the following things: finalize the data structure, understand about which channel we are reading the following note, and understand

Reading the documentation for the SID we see that for playing a single note we need to insert two values for each channel, one for the LOW frequency and the other for the HIGH frequency. The two frequencies will be combined into a single note.

We also decide that the delay, for now, will be "quarters of second", so when the program jumps to loop we will need to check if the delay has stopped. Remember also that we want to test if the channel is still active.

So we need to change this representation:

[note-1-pitch],[note-1-delay]

to this one:

[note-1-low],[note-1-high],[note-1-delay]

And when we read the data we will just do

READ LO
READ HI
READ DL

But before reading it we have to decide if we need to read a note, and after reading that note we need to decide when we have to read it next.

Each channel will have a timer. Each will be initialized, before entering the loop, with the current time.

T1 = TIME

If T1 is lower or equals to the current TIME then we will make the program read its values

    IF T1 > TIME SKIP
    READ LO # read the low frequency
    READ HI # read the high frequency
    READ DL # read the delay.
    POKE L1,LO # we put in the Sid 1 low register the value read by low
    POKE H1,HL # same but with the high value
    T1 = TT + DT * (DL - 1) # this is when we will read the next note.
SKIP:
    # we skipped here and here we will test channel 2

    [...] 

What does it happen if we want to shut off the channel? Or if the channel was shut off before?
We use another variable: C1, that we will initialize to 1 at the beginning of the program. So our micro-block becomes:

    IF C1 = 0 SKIP
    IF T1 > TIME SKIP
    READ LO # read the low frequency
    READ HI # read the high frequency
    READ DL # read the delay.
    IF DL = -1 THEN C1 = 0 

    IF DL = -1 THEN SKIP
    POKE L1,LO # we put in the Sid 1 low register the value read by low
    POKE H1,HL # same but with the high value
    T1 = TT + DT * (DL - 1) # this is when we will read the next note.
SKIP:
    [...] #we skipped here and here we will test for C2 and C3


Now we could copy the block and do the same things again

    IF C1 = 0 SKIP_1
    IF T1 > TIME SKIP_1
    READ LO
    READ HI
    READ DL
    IF DL = -1 THEN C1 = 0 

    IF DL = -1 THEN SKIP_1
    POKE L1,LO
    POKE H1,HL
    T1 = TT + DT * (DL - 1)
SKIP_1:
    IF C2 = 0 SKIP_2
    IF T2 > TIME SKIP_2
    READ LO
    READ HI
    READ DL
    IF DL = -1 THEN C2 = 0 

    IF DL = -1 THEN SKIP_2
    POKE L2,LO
    POKE H2,HL
    T2 = TT + DT * (DL - 1)
SKIP_2:
    IF C3 = 0 SKIP_3
    IF T3 > TIME SKIP_3
    READ LO
    READ HI
    READ DL
    IF DL = -1 THEN C3 = 0 

    IF DL = -1 THEN SKIP_3
    POKE L3,LO
    POKE H3,HL
    T3 = TT + DT * (DL - 1)
SKIP_3:


However as you see this wall of text is unreadable, and as such it's prone to inserting errors. It's better to have a subroutine to handle the common parts of this block of code.

    # channel 1
    CS = C1 # status of the channel
    TS = T1 # time of the channel

    LT = L1 # low frequency register of the channel
    HT = H1 # high frequency register of the channel
    GOSUB COMMON_LOADER
    C1 = CS
    T1 = TS

    # channel 2

    CS = C2
    TS = T2

    LT = L2
    HT = H1
    GOSUB COMMON_LOADER
    C2 = CS

    T2 = TS
    # channel 3
    CS = C3
    TS = T3

    LT = L3 
    HT = H3
    GOSUB COMMON_LOADER
    C3 = CS
    T3 = TS

    [..]

COMMON_LOADER:
    IF CS = 0 SKIP
    IF TS > TIME SKIP
    READ LO 
    READ HI 
    READ DL #note that we set the "off" channel in CS
    IF DL = -1 THEN CS = 0 
    IF DL = -1 THEN SKIP 
# the values of LT and HT will be L1 and H1 
# in the first channel, L2, H2 and L3, H3 
# in the other two
    POKE LT,LO 
# we put in the Sid 1 low register the value read by low
    POKE HT,HL 
# same but with the high value
    TS = TT + DT * (DL - 1) # this is when we will read the next note.
SKIP:
    RETURN

Tomorrow I will type in the program.