Understanding 6502 assembly on the Commodore 64 - (21) Optimizing our interrupt code

     In the last chapter, we modified our code to run our binary converter 60 times per second using the system interrupt, bypassing the address of the usual housekeeping code with our own.  We then Jumped back to the housekeeping code so it could proceed.  We didn't cause an interrupt, we just added our code in as part of the normal system interrupt  But In doing so we ran into a bit of a problem.  









     Every time the code ran, 60 times per second, it took the value of OURHEXVALUE and ran through the ENTIRE process with it.  What if OURHEXVALUE hadn't changed?????  It seems like an awful waste of processor cycles to conduct our conversion and subsequent output for no good reason.  If we look at our code we can see how with small modification, we can save ourselves a ton of unnecessary processor work.


     First of all, our goal is to check the value in OURHEXVALUE to see if it changed.  If it did not change, we might as well jump back to the housekeeping memory location and go no further.  If it has changed, then we can run our program to convert the code and update the screen.


    By storing the last value of OURHEXVALUE somewhere else, we can compare it to the current OURHEXNUM to see if a change occurred, and then proceed accordingly.  We will employ another zero page memory address to store the old OURHEXVALUE.


OLDBYTE = $FD      ; Store our old byte here to test against new one



     Lets not have IRQ: jump right to INIT:   lets have it jump somewhere else, a different block of code to decide if we will in fact EVER jump to INIT: or not.

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



     So now, instead of the IRQ jumping to INIT: it now jumps to PRETEST:


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


     So after we load OURHEXVALUE into A, we can compare it against the value in OLDBYTE.  If they are not equal, we branch to INIT: to run our conversion and display code.  If they are equal the RTS brings us back to the line just before JMP $EA31, where the computer resumes its housekeeping chores.

     In INIT: we no longer need lda OURHEXVALUE, this was loaded in PRETEST:. What we do need, while OURHEXVALUE is loaded into A, is save it in OLDBYTE, for our next cycle.


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



Essentially, in our modification:

Jumping to PRETEST: instead of INIT: cost us nothing
LDA OURHEXVALUE was added to PRETEST, but removed from INIT: costing us nothing
CMP OLDBYTE and BNE init were added: A few extra cycles
STA OLDBYTE added to INIT: a few extra cycles

     For a couple of extra cycles and a program a few bytes bigger, we have signifficantly reduced the load on the processor.  Obviously the advantage increased tremendously the slower OURHEXVALUE updates.  But the advantage only decreases marginally, the faster OURHEXVALUE updates.


When we run our code, we can see that if we press return over and over, the screen will not update the binary value, until it has changed from the old binary value.  




Our modified code:



; C64 Hex to Binary display converter in an interrupt optimized
; 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  
   
*=$C000 ; SYS 49152 to begin

OURHEXVALUE = $A1  ; 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



sei                ; Disable interrupts
lda #<IRQ          ; Load the beginning location of our interrupt code into INTLO
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:

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 #$30        ; Load the display value of 0 into A
jmp CONTINUE    ; jump to CONTINUE

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

CONTINUE:
sta BIT7,x     ; Load the display value into A
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



NEXT----->


Table of contents

No comments:

Post a Comment