Understanding 6502 assembly on the Commodore 64 - (22) Output Display alternative

     Before we go any further in our program,  lets have a bit more fun.  Though we're still hanging off of our housekeeping interrupt, we're using the screen output addresses to display our code.  It's been effective, but a bit boring.   We can use what we learned in creating sprites to spruce things up a bit.










     Once again you shall see how my lack of artistic skills are demonstrated.  What we'll need to do is create a sprite that looks like a lightbulb, or in my case, the Deathstar.   I refer to it as an LED in the code.  I have a good imagination.......


     We, can tuck this code away at the bottom of the program

LEDDATA:
.byte $01, $FF, $80, $07, $FF, $E0, $0F, $FF, $F0, $1F, $FF, $F8
.byte $3F, $FC, $FC, $7F, $F8, $7E, $7F, $FC, $FE, $FF, $FF, $FF 
.byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF 
.byte $FF, $FF, $FF, $FF, $FF, $FF, $7F, $FF, $FE, $7F, $FF, $FE 
.byte $3F, $FF, $FC, $1F, $FF, $F8, $0F, $FF, $F0, $07, $FF, $E0 
.byte $01, $FF, $80



     Im going to use all 8 sprites, one for each of the 8 bits of our hex value, we'll continue to use $A1 and $A2 as our demonstration values.     Since all of the sprites will be identical, theres no sense in wasting memory and cycles writing each one to a different location.  We can add the code once, and have all of the sprites point to that code.



     Once we add the code into memory we can quickly enable all sprites.  We are going to need some of our old labels from the sprite code.


SPRITEDATA = $2000 ; Sprite 0 through 7 data

SPENA = $D015      ; Sprite Enable Register


.......and our sprite loading and enabling code, our standard loop for loading the sprite into memory, and then enable all 8 of them.



ldx #$00           ; Initialize X=0

SPRITELOADER:      ; Load our sprite data into memory
lda LEDDATA,x      ; All 8 sptites will use the same data
sta SPRITEDATA,x
inx
cpx #$40
bne SPRITELOADER
lda #%11111111
sta SPENA          ; Activate all SPRITES
ldx #$00


     
We want all of the sprites to default to red, as well as to tell each sprite where their data is located.  They will all point to the same location for data.

More labels.....


SSDP0 = $07F8   ; Sprite Shape Data Pointers

SP0COL = $D027   ; Sprite 0 Color Register


     
Here is our code, this loop sets all sprites to red, and all to the same data pointer.  This code will go directly under our previous code.



SPRITESETUP:    ; F2 red F5 green

lda #$F2
sta SP0COL,x    ; turn color red for all sprites
lda #$80
sta SSDP0,x   ; Set the location to find SPRITE0 Data
inx             ; each sprite will point to the same data location
cpx #$08 
bne SPRITESETUP
ldx #$00
ldy #$00


     
This one is a bit more tricky,  at this point all of the sprites need to be assigned a location on the screen, we want the Y axis to be the same, so they are in one line, but we want the X axis to be different, so they are not all on top of each other.

Time for more labels:


SP0X = $D000   ; Sprite 0 Horizontal Position

SP0Y = $D001   ; Sprite 0 Vertical Position
OFFSET = $FE    ; SPRITE Y OFFSET

     
The initial value for offset must be our X starting point.  Ive chosen one off to the right of the screen. We should however initialize this value and store it. We can put it all the way at the top of our program where we wrote ldx #$00

ldx #$20

stx OFFSET         ; Store our inital offset for Y into OFFSET
ldx #$00           ; Initialize X=0



     
Now for our location code. X coordinate is easy, its stored as $DF for each sprite.  For Y we will add use the value in OFFSET, and each time, we will add $1F to that value and store the new value in OFFSET.

     
Register Y will be our counter for the loop.  We have already initialized it in SPRITESETUP at the end. Register X also previously initialized, will be used as the offset for our memory location to store X and Y coordinates.

     
Because of the way the memory map is played out, we must increment X twice in each iteration........

SP0X is D000

SP0Y is D001
SP1X is D002
SP1Y is D003
SP2X is D004
SP2Y is D005

....and so on and so on.  If we did not increment X twice, the subsequent X value would trample on the last Y value, as X would try to write its value in the next loop to D001.




SPRITELOCATE:

lda #$DF
sta SP0Y,x ; Set Y UP DOWN coordinate for SPRITE0
lda OFFSET
sta SP0X,x ; Set X left right coordinate for SPRITE0
adc #$1F            ; We offset $1F so the sprites are not on top of each 
sta OFFSET          ; other but in a row
inx
inx                 ; skip every other address to accomidate X AND Y
iny                 ; becuause SP0X is D000 and SP0Y is D001 and SP1X is D002
cpy #$08            ; etc etc
bne SPRITELOCATE


     
With this, we now have 8 red Deathstar looking sprites on the bottom screen.  Now a slight modification of our conversion code will get things rolling.


     
In CONVERSION, we used to store the HEX value for 0, which is $30.  lets change that for the value of RED ($F2) as understood by SP0COL.

CONVERTION:

lda OURHEXNUM   ; load our test hex number, this is a constant
and TESTBYTE    ; mask it with our test byte
bne STORE1      ; No, jsr to STORE1
lda #$F2        ; Load the color value of red into A
jmp CONTINUE    ; jump to CONTINUE


     
In STORE1, we change the value to green

STORE1:

lda #$F5       ; Load the color value of green into A


     
Now, instead of outputting to the screen, the values will be stored in their respective sprites, instead of their screen memory assignments.


CONTINUE:

;sta BIT7,x    ; Load the display value into A
sta SP0COL,x   ; Store the color to SP0COL
inx            ; Increment X for our loop
lda TESTBYTE   ; load testbyte into A                                                                                                                              
lsr            ; divide it by 2
sta TESTBYTE   ; store new testbyte back to its memory area
cpx #$08       ; is X=8?
bne CONVERTION ; No, LOOP back to CONVERSION
rts




     Ok, our new program is ready, but before we list it i'd like to add a second program within our code, that we can execute whilst our program is running.  The program will be very simple, It will load the display values of 1,2 and 3 into A X and Y, then it will loop endlessly, reprinting the values onto an area of the screen.  Once A X and Y are loaded, they are never re-loaded, which demonstrates the preservation of registers in our example.


*=$C0C0 ; SYS 49344 to begin      
nop
TESTCODE:
lda #$31    ;1
ldx #$32    ;2
ldy #$33    ;3

TESTLOOP:
sta $0700   ;display A
stx $0701   ;display X
sty $0702   ;display Y
jmp TESTLOOP


    So how did we preserve our A,X,and Y as well as the status flags, if we didn't push them onto the stack?  Well....... we didn't, Remember, we did not initiate this interrupt, it was already underway initiated by the kernels housekeeping processes, the preservation of the processor information was done for us.  In our test program, AX and Y only get loaded once, if they ever change, our output of 1 2 and 3 would not be displayed to the screen.




The final code:

; C64 Hex to Binary display converter in an interrupt optimized
; Outputs with sprites for a light bulb/ led like display
; 64TASS assembler style code for 6502
; Jordan Rubin 2014 http://technocoma.blogspot.com
;
; Takes the HEX value in OURHEXVAUE, converts it to Binary for display 
; on the screen as a binary number. Only does conversion if the value  
; in OURHEXVALUE has changed since the last time the program was run  
; The output is reflected in the 8 sprites at the bottom, red for 0  
; and green for 1.  
   
*=$C000 ; SYS 49152 to begin

;;;;;;;;;;;;;;;;;;;; main program memory areas

OURHEXVALUE = $A2  ; Enter the Hex value to be converted here
OURHEXNUM = $FB    ; This is where the constant OURHEXVALUE will be stored
TESTBYTE = $FC     ; This is where our test byte will be stored for lsr
OLDBYTE = $FD      ; Store our old byte here to test against new one
IRQLO  =$0314      ; LSB of interrupt vector
IRQHI =$0315       ; MSB of interrupt vector
;BIT7 = $0708      ; This is the location of the 7th bit, required room for
;               ; 8 contiguous bytes after the starting address
;               ; using 0708 dumps it right to screen ram, bottom center
CHROUT = $FFD2     ; Character out

;;;;;;;;;;;;;;;;;;; Sprite related memory areas

SPRITEDATA = $2000 ; Sprite 0 through 7 data
SPENA = $D015      ; Sprite Enable Register
SSDP0 = $07F8   ; Sprite Shape Data Pointers
SP0X = $D000   ; Sprite 0 Horizontal Position
SP0Y = $D001       ; Sprite 0 Vertical Position
SP0COL = $D027   ; Sprite 0 Color Register
OFFSET = $FE       ; SPRITE Y OFFSET

;;;;;;;;;;;;;;;;;;; This begins our sprite loading and initialization code
;;;;;;;;;;;;;;;;;;; Same data for all sprites, Same color for all sprites
;;;;;;;;;;;;;;;;;;; Sprite data location pointing to the same address for 
;;;;;;;;;;;;;;;;;;; all sprites.  Same Y axis for all sprites, and
;;;;;;;;;;;;;;;;;;; X axis starting at $20, staggered each one by $1F

ldx #$20
stx OFFSET         ; Store our inital offset for Y into OFFSET
ldx #$00           ; Initialize X=0

SPRITELOADER:      ; Load our sprite data into memory
lda LEDDATA,x      ; All 8 sptites will use the same data
sta SPRITEDATA,x
inx
cpx #$40
bne SPRITELOADER
lda #%11111111
sta SPENA           ; Activate all SPRITES
ldx #$00

SPRITESETUP:        ; F2 red F5 green
lda #$F2
sta SP0COL,x        ; turn color red for all sprites
lda #$80
sta SSDP0,x         ; Set the location to find SPRITE0 Data
inx                 ; each sprite will point to the same data location
cpx #$08 
bne SPRITESETUP
ldx #$00
ldy #$00

SPRITELOCATE:
lda #$DF
sta SP0Y,x          ; Set Y UP DOWN coordinate for SPRITE0
lda OFFSET
sta SP0X,x          ; Set X left right coordinate for SPRITE0
adc #$1F            ; We offset $1F so the sprites are not on top of each 
sta OFFSET          ; other but in a row
inx
inx                 ; skip every other address to accomidate X AND Y
iny                 ; becuause SP0X is D000 and SP0Y is D001 and SP1X is D002
cpy #$08            ; etc etc
bne SPRITELOCATE

;;;;;;;;;;;;;;;;;;;; We now have 8 red sprites on the bottom of the screen

INTERRUPTMOD:
sei                ; Disable interrupts
lda #>IRQ
sta IRQLO          ; Store in 0314
lda #>IRQ          ; Load the end location of our interrupt code into INTHI
sta IRQHI          ; Store in 0315
cli                ; restore interrupts
rts                ; return to BASIC like nothing happened


IRQ:               ; This is the code that we have derailed the sysytem to run


jsr PRETEST        ; Essentially, decide of our converter program should run or not
                   ; program will end and rts will bring it to right here.

jmp $EA31          ; Send the processor back to normally scheduled interrupt


PRETEST:
lda OURHEXVALUE   ; load current value in OURHEXVALUE
cmp OLDBYTE       ; Compare it against the previously stored Hex value
bne INIT          ; If they are the different, jump to INIT
rts               ; Go back to IRQ: and resume housekeeping chores

INIT:
; lda OURHEXVALUE ; Already loaded in PRETEST, Dont need this 
sta OURHEXNUM ; we will store the test number here perminantly       
sta OLDBYTE   ; store the value as a reference for next time
ldy #$80      ; Out first bit test for bit 7 must be 10000000 $80 
sty TESTBYTE  ; store our initial test byte here
ldx #$00      ; Initialize X for our loop

CONVERTION:
lda OURHEXNUM   ; load our test hex number, this is a constant
and TESTBYTE    ; mask it with our test byte
bne STORE1      ; No, jsr to STORE1
lda #$F2        ; Load the display value of 0 into A
jmp CONTINUE    ; jump to CONTINUE

STORE1:
lda #$F5        ; Load the display value of 1 into A

CONTINUE:
;sta BIT7,x     ; Load the display value into A
sta SP0COL,x    ; Store the color to SP0COL
inx             ; Increment X for our loop
lda TESTBYTE    ; load testbyte into A                                                                                                                              
lsr             ; divide it by 2
sta TESTBYTE    ; store new testbyte back to its memory area
cpx #$08        ; is X=8?
bne CONVERTION  ; No, LOOP back to CONVERSION
rts

*=$C0C0 ; SYS 49344 to begin      
nop
TESTCODE:
lda #$31
ldx #$32
ldy #$33
clc
TESTLOOP:
sta $0700
stx $0701
sty $0702
jmp TESTLOOP



LEDDATA:
.byte $01, $FF, $80, $07, $FF, $E0, $0F, $FF, $F0, $1F, $FF, $F8
.byte $3F, $FC, $FC, $7F, $F8, $7E, $7F, $FC, $FE, $FF, $FF, $FF 
.byte $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF, $FF 
.byte $FF, $FF, $FF, $FF, $FF, $FF, $7F, $FF, $FE, $7F, $FF, $FE 
.byte $3F, $FF, $FC, $1F, $FF, $F8, $0F, $FF, $F0, $07, $FF, $E0 
.byte $01, $FF, $80




NEXT----->

No comments:

Post a Comment