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
Table of contents
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