Sunday, March 15, 2020

Lab 4 - Pick Two pt.2

In Part 1 we looked at making an adder in 6502 assembly, this was one of the two tasks we chose for this lab, the other was a screen colour selector. The screen colour selector I personally found was a lot easier to code than the adder and took far lass code to implement. So without any delay here is the whole source code before we dive in to how it works.

; ROM routines
define        SCINIT        $ff81 ; initialize/clear screen
define        CHRIN        $ffcf ; input character from keyboard
define        CHROUT        $ffd2 ; output character to screen
define        SCREEN        $ffed ; get screen size
define        PLOT        $fff0 ; get/set cursor coordinates
  
        jsr SCINIT
        ldy #$00

initColours:
    lda colours,y
        beq doneInit
        jsr CHROUT
        iny
        bne initColours
doneInit:
    ldy #$00
    ldx #$00
    CLC
    jsr PLOT

    SEC
    jsr PLOT
    jsr flipSelect
   


checkIn:
    SEC
    jsr PLOT
    jsr CHRIN

    cmp #$80
    beq up
    cmp #$82
    bne checkIn
down:
    cpy #$0f
    beq checkIn
    jsr flipSelect
    iny
    jsr flipSelect
    jsr drawScreen
    jmp checkIn

up:
    cpy #$00
    beq checkIn
    jsr flipSelect
    dey
    jsr flipSelect
    jsr drawScreen
    jmp checkIn
   

flipSelect:
    ldx #$00
    CLC
    jsr PLOT
    SEC
    jsr PLOT
   
flipLoop:
    cmp #$20
    beq doneFlip
    eor #$80
    jsr CHROUT
    SEC
    jsr PLOT
    clc
    bcc flipLoop
   
doneFlip:
    rts

drawScreen:
    tya
    pha
    lda #$00     ; set pointer at $10 to $0200
        sta $10
        lda #$02
        sta $11
    pla
     
        ldx #$06     ; max value for $11
     
        ldy #$00     ; index

drawLoop:
    sta ($10),y  ; store colour
        iny          ; increment index
        bne drawLoop ; branch until page done
     
        inc $11      ; increment high byte of pointer
        cpx $11      ; compare with max value
        bne drawLoop ; continue if not done

    rts
   
    
   

colours:
dcb "B","L","A","C","K",10
dcb "W","H","I","T","E",10
dcb "R","E","D",10
dcb "C","Y","A","N",10
dcb "P","U","R","P","L","E",10
dcb "G","R","E","E","N",10
dcb "B","L","U","E",10
dcb "Y","E","L","L","O","W",10
dcb "O","R","A","N","G","E",10
dcb "B","R","O","W","N",10
dcb "L","I","G","H","T",95,"R","E","D",10
dcb "D","A","R","K",95,"G","R","E","Y",10
dcb "G","R","E","Y",10
dcb "L","I","G","H","T",95,"G","R","E","E","N",10
dcb "L","I","G","H","T",95,"B","L","U","E",10
dcb "L","I","G","H","T",95,"G","R","E","Y",00


This code, similar to the code in pt. 1 uses a main loop as the body of the program, though this one is a bit different and also isn't named main. Though this is getting ahead of ourselves the first thing the program does is initialize the screen, it does this by going through the colour names which are stored in memory and printing them to the screen, which was fairly easy to do considering the ROM routine CHROUT can read newline properly allowing the whole thing to be one block of memory.

The main loop for this program is called checkIn, this loop is checking for an input and when it receives it updates the screen accordingly, both up and down inputs work about the same so let's just look at up. When the up arrow in pressed checkIn calls the subroutine up. This subroutine will then check if up is valid (ie: not the top of the screen) and if so it will remove the selection, then select the proper line before changing the screen colour then returning to the checkIn loop.

Now of course up and down both call their own subroutines which I will explain now. flipSelect is a pretty interesting subroutine, it simply flips the high bit of every character in whatever line is in y. which will be the currently selected line. So the first time it is called it flips it off to deselect the line, then the second time it is called it flips it on selecting the new line. The drawScreen subroutine is one we have used a lot in the course so far. It simply takes what is currently in y, as established this will be the current selected element, and it will fill the screen with that colour. We made sure to align the colours on the screen with their places in memory so their y values line up with their colour values. This results in the correct colour being displayed

putting these few simple subroutines together along with the Rom routines and we have a very compact and easy to understand bit of code which can allow you to select a colour and display it on the screen.

Lab 4 - Pick Two pt.1

For this lab we were tasked with creating two programs from a list in 6502 assembly code. These tasks ranged in difficulty though some things were made far easier by the introduction of ROM routines which I will explain a bit later. The two tasks which out group decided on were the calculator and the colour selector. We chose these two for a couple of reasons, the largest being that we all felt most confident in out own ability to get done these two tasks over any others. So with our tasks in hand we set out to get an understanding on ROM Routines.

ROM routines are basically snippets of code saved in the memory of the chip, In order to access these you simply need to start a subroutine and the right address and it will run the subroutine as if it were code which you wrote. The Routines given to us did things which previously requited many lines of code now in just one with smart use of the various registers in order to supply input to these routines.

Now before getting to the code for the adder it is important to note that we were never able to get the blinking cursor to work properly, though the rest of the program works for sure. Now here is the full source code which will be explained bellow.

; ROM routines
define        SCINIT        $ff81 ; initialize/clear screen
define        CHRIN        $ffcf ; input character from keyboard
define        CHROUT        $ffd2 ; output character to screen
define        SCREEN        $ffed ; get screen size
define        PLOT        $fff0 ; get/set cursor coordinates

define        NUMBERA        $10;
define        NUMBERB        $20;

        jsr SCINIT
   
mainLoop:
    ldy #$00
    jsr char1
    jsr input
    jsr storeA
    ldy #$00
    jsr char2
    jsr input
    jsr storeB
    ldy #$00
    jsr charR
    jsr printAdd
    jmp mainLoop
   

input: 
    SEC
    jsr PLOT
    ldx #$15
    CLC
    jsr PLOT

   

inLoop:
    SEC
    jsr PLOT
    jsr CHRIN


charCheck:   
    cmp #$00
    beq inLoop

    cmp #$81
    beq right
   
    cmp #$83
    beq left

    cmp #$0d
    beq next

drawNum:
    cmp #$30
    bcc inLoop
   
    clc
    cmp #$3a
    bcs inLoop
   
    jsr CHROUT

    SEC
    jsr PLOT
    cpx #$17
    bne inLoop
    dex
    CLC
    jsr PLOT
    jmp inLoop


left:    cpx #$15
    beq inLoop
    jsr CHROUT
    jmp inLoop

right:    cpx #$16
    beq inLoop
    jsr CHROUT
    jmp inLoop

next:
    SEC
    jsr PLOT
    ldx #$15
    CLC
    jsr PLOT
    SEC
    jsr PLOT


    CLC
    SBC #$2F

    ASL
    ASL
    ASL
    ASL

    PHA
   

    ldx #$16
    CLC
    jsr PLOT
    SEC
    jsr PLOT

    CLC
    SBC #$2F
    PHA

    ldx #$00
    iny
    CLC
    jsr PLOT
    SEC
    jsr PLOT

    PLA
    TAX
    PLA


    rts

storeA:
    sta NUMBERA
    txa
    eor NUMBERA
    sta NUMBERA
    rts
   

storeB:
    sta NUMBERB
    txa
    eor NUMBERB
    sta NUMBERB
    rts

printAdd:
    SEC
    jsr PLOT
    ldx #$15
    CLC
    jsr PLOT
    SEC
    jsr PLOT
   
    SED
    lda NUMBERA
    adc NUMBERB
    CLD
    pha

    bcc outputAddition
    ldx #$14
    CLC
    jsr PLOT
    SEC
    jsr PLOT
    lda #$31
    jsr CHROUT
   
outputAddition:
    pla
    pha
    LSR
    LSR
    LSR
    LSR
    clc
    adc #$30
    jsr CHROUT

    pla
    and #$0F
    clc
    adc #$30
    jsr CHROUT

    SEC
    jsr PLOT
    ldx #$00
    iny
    CLC
    jsr PLOT
   
    rts

   

   
   

char1:  lda firstDigit,y
        beq charRet
        jsr CHROUT
        iny
        bne char1

char2:  lda secondDigit,y
        beq charRet
        jsr CHROUT
        iny
        bne char2

charR:  lda result,y
        beq charRet
        jsr CHROUT
        iny
        bne charR

charRet:
    rts



firstDigit:
dcb "E","N","T","E","R",32,"F","I","R","S","T",32,"D","I","G","I","T",":",32,32,32,"0","0"
dcb 00


secondDigit:
dcb "E","N","T","E","R",32,"S","E","C","O","N","D",32,"D","I","G","I","T",":",32,32,"0","0"
dcb 00

result:
dcb "R","E","S","U","L","T",":"
dcb 00


The Adder is actually quite a simple program especially thanks to ROM routines. The writing went through a few revisions before settling on the final result, this is due to poor user of subroutines in previous incarnations as well as unfamiliarity with the ROM routines. Now speaking of the ROM routines let's discuss what they are accomplishing for us here.

there are three important ROM routines to making this code as compact as it is, the first if CHROUT, this will spit out a character onto the screen and then move the cursor over one, very simple but extremely useful. It does this by simply checking the accumulator for the value and then putting it at the current cursor location, the next routine used is the compliment to CHROUT, CHRIN. As the name implies CHRIN takes a character input, this input is stored in the accumulator which means it can be used in conjunction with CHROUT to print input to the screen. The last important ROM routines we have is PLOT. This routine has two functions depending on the state of the carry flag. Either it gets the current cursor position and returns the value of the character there, or it sets the current cursor position based on what is currently in x and y.

Using these Tools the code goes through a main loop which utilizes a few subroutines in order to keep it as readable as possible. This main loop is only 12 lines but does all the work of the program vie the subroutines it calls. Let's take a closer look at it.

mainLoop:
    ldy #$00
    jsr char1
    jsr input
    jsr storeA
    ldy #$00
    jsr char2
    jsr input
    jsr storeB
    ldy #$00
    jsr charR
    jsr printAdd
    jmp mainLoop

So the first step is to set y to a known value, this allows char1 to work properly as it will print onto the screen the instructions to enter the first input and the start location is whatever y is. Next we get the first user input. This is done through quite complex code which I will explain further down. following this it stores the first number then it does it all again for the second number. After this it prints the result text followed by doing the actual addition and printing those results, this whole thing then loops allowing the program to keep taking inputs.

Now let's break down a couple of the more important components there, namely input, storeX and printAdd. input is how we get out user input and as we are simply making a calculator only certain characters are allowed we do this by limiting the values that can be read by CHRIN, if it gets anything else we simply ask it to try again. This ensures we are either getting a number, an arrow key, or the enter key. When a character is input, it will also do a check on whether or not the input field is on the second digit in order to ensure you can only write two digits. Next storeA and storeB which take the numbers provided to them and store them in different addresses, fairly simple. Lastly printAdd which does the actual adding. This subroutine switches to decimal mode before adding the two values stored in the previous subroutines together. It then checks to see if there was a carry and if there was it draws a 1 before the number, before then drawing the output of the addition.

 All of this put together and we have a working, though not perfect Adder in 6502 assembly.