Warning: preg_match(): Compilation failed: group name must start with a non-digit at offset 8 in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 722

Warning: preg_match(): Compilation failed: group name must start with a non-digit at offset 8 in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 722

Warning: preg_match_all(): Compilation failed: group name must start with a non-digit at offset 4 in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 700

Warning: Invalid argument supplied for foreach() in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 701

Warning: preg_replace(): Compilation failed: group name must start with a non-digit at offset 4 in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 705

Warning: preg_match_all(): Compilation failed: group name must start with a non-digit at offset 4 in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 700

Warning: Invalid argument supplied for foreach() in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 701

Warning: preg_replace(): Compilation failed: group name must start with a non-digit at offset 4 in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 705

Warning: preg_match(): Compilation failed: group name must start with a non-digit at offset 8 in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 722

Warning: preg_match_all(): Compilation failed: group name must start with a non-digit at offset 4 in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 700

Warning: Invalid argument supplied for foreach() in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 701

Warning: preg_replace(): Compilation failed: group name must start with a non-digit at offset 4 in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 705

Warning: preg_match_all(): Compilation failed: group name must start with a non-digit at offset 4 in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 700

Warning: Invalid argument supplied for foreach() in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 701

Warning: preg_replace(): Compilation failed: group name must start with a non-digit at offset 4 in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 705

Warning: preg_match_all(): Compilation failed: group name must start with a non-digit at offset 4 in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 700

Warning: Invalid argument supplied for foreach() in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 701

Warning: preg_replace(): Compilation failed: group name must start with a non-digit at offset 4 in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 705

Warning: preg_match_all(): Compilation failed: group name must start with a non-digit at offset 4 in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 700

Warning: Invalid argument supplied for foreach() in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 701

Warning: preg_replace(): Compilation failed: group name must start with a non-digit at offset 4 in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 705

Warning: preg_match(): Compilation failed: group name must start with a non-digit at offset 8 in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 722

Warning: preg_match(): Compilation failed: group name must start with a non-digit at offset 8 in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 722

Warning: preg_match(): Compilation failed: group name must start with a non-digit at offset 8 in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 722

Warning: preg_match(): Compilation failed: group name must start with a non-digit at offset 8 in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 722

Warning: preg_match_all(): Compilation failed: group name must start with a non-digit at offset 4 in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 700

Warning: Invalid argument supplied for foreach() in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 701

Warning: preg_replace(): Compilation failed: group name must start with a non-digit at offset 4 in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 705

Warning: preg_match_all(): Compilation failed: group name must start with a non-digit at offset 4 in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 700

Warning: Invalid argument supplied for foreach() in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 701

Warning: preg_replace(): Compilation failed: group name must start with a non-digit at offset 4 in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 705

Warning: preg_match(): Compilation failed: group name must start with a non-digit at offset 8 in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 722

Warning: preg_match(): Compilation failed: group name must start with a non-digit at offset 8 in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 722
Introducing Interrupts - Intellivision Wiki

Introducing Interrupts


Warning: preg_match(): Compilation failed: group name must start with a non-digit at offset 8 in /home/content/30/6867330/html/intellivision/wiki/includes/MagicWord.php on line 722
From Intellivision Wiki

Jump to: navigation, search
(The Interrupt Handler)
m (Protected "Introducing Interrupts" ([edit=autoconfirmed] (indefinite) [move=autoconfirmed] (indefinite)))
 
Line 1: Line 1:
 +
[[Category: Programming]][[Category: Tutorial]]
 +
Interrupts are a way for a device in the system to ask the CPU to stop what  
Interrupts are a way for a device in the system to ask the CPU to stop what  
it's currently doing and do something on its behalf, or to inform the CPU
it's currently doing and do something on its behalf, or to inform the CPU
Line 77: Line 79:
         when we return from the interrupt.
         when we return from the interrupt.
     </LI>
     </LI>
-
     <LI>The CPU then jumps to location $1004.  The hardware determines
+
     <LI>The CPU then jumps to location $1004.  The hardware sets
         this address, and there's no easy way to change this address on
         this address, and there's no easy way to change this address on
         the Intellivision.
         the Intellivision.
Line 200: Line 202:
What value gets left in <CODE>VAR</CODE>?  In this case, it looks like the  
What value gets left in <CODE>VAR</CODE>?  In this case, it looks like the  
decrement never happened.  That's because the <CODE>[[MVO]] R0, VAR</CODE>  
decrement never happened.  That's because the <CODE>[[MVO]] R0, VAR</CODE>  
-
writes the incremented value after <CODE>[[MVO]] R1, VAR</CODE> wrote its
+
writes the incremented value after <CODE>[[MVO]] R1, VAR</CODE> wrote the decremented
value.  Also, the decremented value isn't even the right value.
value.  Also, the decremented value isn't even the right value.
<br/><br/>
<br/><br/>
Line 208: Line 210:
call analogy above, this is similar to carrying a hot bowl of soup:  You  
call analogy above, this is similar to carrying a hot bowl of soup:  You  
don't want to spill the soup, and carrying it requires your full attention,  
don't want to spill the soup, and carrying it requires your full attention,  
-
so you want to do so without interruption.
+
so you want to do so without interruption until you safely set down the soup.
<br/><br/>
<br/><br/>
To fix the example above, you can wrap the critical code with  
To fix the example above, you can wrap the critical code with  
Line 436: Line 438:
interrupts and read the time with interrupts off.  This copy of the time  
interrupts and read the time with interrupts off.  This copy of the time  
is guaranteed to be consistent no matter what.  For the purposes of this
is guaranteed to be consistent no matter what.  For the purposes of this
-
example, we'll read the time and put a copy of it on the stack with [[PSHR]].
+
example, we'll read the time and put a copy of it on the stack with <CODE>[[PSHR]]</CODE>.
         [[DIS]]                    ; Disable ints (begin critical section)
         [[DIS]]                    ; Disable ints (begin critical section)
Line 463: Line 465:
To display the time, we'll use the [http://sdk-1600.spatula-city.org/ SDK-1600]
To display the time, we'll use the [http://sdk-1600.spatula-city.org/ SDK-1600]
routine <CODE>PRNUM16</CODE>, found  
routine <CODE>PRNUM16</CODE>, found  
-
[http://sdk-1600.spatula-city.org/examples/library/prnum16.asm here].  The
+
[[prnum16.asm|here]].  The
<CODE>PRNUM16.z</CODE> routine prints the number in R0 in a fixed width field,
<CODE>PRNUM16.z</CODE> routine prints the number in R0 in a fixed width field,
complete with leading zeroes.  It prints the value to the screen, starting at  
complete with leading zeroes.  It prints the value to the screen, starting at  
Line 674: Line 676:
with 0 as well.  It seems like we should be able to combine the two acts.
with 0 as well.  It seems like we should be able to combine the two acts.
<br/><br/>
<br/><br/>
-
It turns out we can.  Addresses $01F0 - $01FF refer to the [[Programmable
+
It turns out we can.  Addresses $01F0 - $01FF refer to the [[Programmable Sound Generator]] (aka. PSG).  We can safely write 0s to all of its locations.
-
Sound Generator]] (aka. PSG).  We can safely write 0s to all of its locations.
+
In fact, this is the preferred way to initialize the PSG when its state is
In fact, this is the preferred way to initialize the PSG when its state is
otherwise unknown.  Thus, we can combine the <CODE>FILLZERO</CODE> and  
otherwise unknown.  Thus, we can combine the <CODE>FILLZERO</CODE> and  
Line 689: Line 690:
us already.)
us already.)
<br/><br/>
<br/><br/>
 +
=== Setting up the Interrupt Handler ===
=== Setting up the Interrupt Handler ===
Line 707: Line 709:
and INCLUDE directives for all of the library functions.  This particular
and INCLUDE directives for all of the library functions.  This particular
example needs  
example needs  
-
<CODE>[http://sdk-1600.spatula-city.org/examples/library/fillmem.asm fillmem.asm]</CODE>  
+
<CODE>[[fillmem.asm]]</CODE>  
and  
and  
-
<CODE>[http://sdk-1600.spatula-city.org/examples/library/prnum16.asm prnum16.asm]</CODE> from SDK-1600.  Download these files and put them in a directory.
+
<CODE>[[prnum16.asm]]</CODE> from SDK-1600.  Download these files and put them in a directory.
Then put the following source code in a new file named <CODE>elapsed.asm</CODE>
Then put the following source code in a new file named <CODE>elapsed.asm</CODE>
in the same directory.  This source code contains all of the snippets from
in the same directory.  This source code contains all of the snippets from
Line 882: Line 884:
The background color behind the timer display is brown.  Why is this?  How
The background color behind the timer display is brown.  Why is this?  How
might you change this?
might you change this?
 +
 +
= Example 2: A simple "wait timer" =
 +
 +
The purpose of this example is to demonstrate how to introduces pauses in
 +
a program.  The "wait timer" consists of two parts:  A short bit of code
 +
in the ISR that decrements a count if it's non-zero, and a separate function
 +
that sets the count and waits for it to become zero.
 +
<br/><br/>
 +
The wait-timer serves two purposes:
 +
<UL>
 +
<LI>Long waits can space events out.  This can be useful if you want to display a message for a moment or otherwise choreograph events.</LI>
 +
<LI>Short waits (such as 1 tic) are useful for waiting to ensure that the screen has updated since changing something that the
 +
interrupt handler might have acted on.  That is, it's a way of figuring out
 +
that an interrupt has occurred.</LI>
 +
</UL>
 +
We'll see an example of the first way of using the wait timer in this example.
 +
Future tutorials will reuse this mechanism for both purposes, which is the
 +
main reason I'm introducing it here.
 +
<br/><br/>
 +
== The Interrupt Handler Code ==
 +
 +
The wait timer code can be inserted into any interrupt handler.  The code
 +
for the wait timer is very simple.  It decrements the value of the variable
 +
<CODE>WTIMER</CODE> if it's non-zero, as shown below:
 +
 +
        [[MVI]]    WTIMER, R0      ; Get current timer count
 +
        [[DECR]]    R0              ; Decrement it
 +
        [[BMI]]    @@expired      ; If it went negative, it's expired
 +
        [[MVO]]    R0,    WTIMER  ; Store updated count
 +
@@expired:
 +
 +
 +
This code can be inserted into any ISR easily.  Alternately, you can set up
 +
many ISRs to call this at the end of their run before exiting.  That's
 +
entirely up to how you want your program to work.  In this example, we'll have
 +
a simple ISR that enables the screen, counts down the wait timer, and then
 +
exits.  The entire ISR is shown below:
 +
 +
 +
;; ======================================================================== ;;
 +
;;  MYISR -- A simple interrupt service routine                            ;;
 +
;; ======================================================================== ;;
 +
MYISR  PROC
 +
        [[MVO]]    R0,    $20    ; Enable the display
 +
         
 +
        [[MVI]]    WTIMER, R0      ; Get current timer count
 +
        [[DECR]]    R0              ; Decrement it
 +
        [[BMI]]    @@expired      ; If it went negative, it's expired
 +
        [[MVO]]    R0,    WTIMER  ; Store updated count
 +
@@expired:
 +
       
 +
        [[JR]]      R5              ; return from interrupt
 +
        ENDP
 +
 +
A more complicated program would clearly have more stuff in its interrupt
 +
handler.  We'll see this in future tutorials.
 +
<br/><br/>
 +
== The Wait Code ==
 +
 +
Programs will call the following function to actually wait for something to
 +
happen.  This function has two entry points.  <CODE>WAIT</CODE> will wait
 +
for the number of tics specified in the word after the <CODE>[[CALL]]</CODE>.
 +
(See
 +
[[Introducing_the_Instruction_Set_Part_3#Passing_Arguments_via_Return_Address|the branch tutorial]] for an explanation of this technique.)  The second entry
 +
point expects the number of tics to wait to be in R0.
 +
 +
;; ======================================================================== ;;
 +
;;  WAIT -- Wait for some number of tics                                    ;;
 +
;;                                                                          ;;
 +
;;  INPUTS for WAIT                                                        ;;
 +
;;      1 decle after call:  Number of tics to wait                        ;;
 +
;;                                                                          ;;
 +
;;  INPUTS for WAIT.1                                                      ;;
 +
;;      R0  Number of tics to wait                                          ;;
 +
;;                                                                          ;;
 +
;;  OUTPUTS                                                                ;;
 +
;;      R0  Zeroed                                                          ;;
 +
;;                                                                          ;;
 +
;;  NOTE                                                                    ;;
 +
;;      Requires WTIMER code in ISR, and interrupts enabled.                ;;
 +
;;                                                                          ;;
 +
;; ======================================================================== ;;
 +
WAIT    PROC
 +
        [[MVI@]]    R5,    R0          ; Get # of tics to wait from after CALL
 +
@@1:    [[MVO]]    R0,    WTIMER      ; Set up wait timer
 +
 
 +
        [[CLRR]]    R0                  ; \
 +
@@wait: [[CMP]]    WTIMER, R0          ;  |- Wait for WTIMER = 0
 +
        [[BNEQ]]    @@wait              ; /
 +
 
 +
        [[JR]]      R5                  ; return
 +
        ENDP
 +
 +
== The Rest ==
 +
 +
For this example, we'll display "Hello" and "World" about one second apart in
 +
as we did previously in the [[Hello World Tutorial]].  We'll also use the
 +
an infinite loop.  We'll reuse [http://sdk-1600.spatula-city.org/ SDK-1600's]
 +
[[print.asm|PRINT]] and [[fillmem.asm|CLRSCR]]
 +
functions.  This results in the following code:
 +
 +
MAIN    PROC
 +
 
 +
        [[CALL]]    CLRSCR              ; Clear the screen
 +
 
 +
        [[MVII]]    #MYISR, R0          ; \
 +
        [[MVO]]    R0,    $100        ;  |_ Write out "MYISR" to $100-$101
 +
        [[SWAP]]    R0                  ;  |  [[Double Byte Data]].
 +
        [[MVO]]    R0,    $101        ; /
 +
        [[EIS]]                        ; Make sure interrupts are enabled
 +
 
 +
@@loop:
 +
        ;; Print "Hello" at row #5, column #7. 
 +
        [[CALL]]    PRINT.fls
 +
        DECLE  7, $200 + 5*20 + 7
 +
        STRING  "Hello", 0
 +
 
 +
        ;; Wait for 1 second
 +
        [[CALL]]    WAIT
 +
        DECLE  60
 +
 
 +
        ;; Print "World" at row #5, column #7. 
 +
        [[CALL]]    PRINT.fls
 +
        DECLE  7, $200 + 5*20 + 7
 +
        STRING  "world", 0
 +
 
 +
        ;; Wait for 1 second
 +
        [[CALL]]    WAIT
 +
        DECLE  60
 +
 
 +
        ;; Do it again
 +
        [[B]]  @@loop
 +
 
 +
        ENDP
 +
 +
 +
There really isn't much to this example, is there?
 +
 +
== Putting it All Together ==
 +
 +
The source listing below puts all the fragments above into a complete program.
 +
Mainly, this just adds the cartridge header, the <CODE>INCLUDE</CODE>
 +
directives to include [[print.asm]] and [[fillmem.asm]]
 +
from SDK-1600, and assigns <CODE>WTIMER</CODE> a location in 8-bit memory. 
 +
The bold portions are the new portions.
 +
 +
<B>
 +
WTIMER  EQU    $102    ; Put WTIMER into 8-bit memory
 +
 +
        ROMW    16      ; 16-bit ROM
 +
        ORG    $5000  ; Standard ROM memory map starts at $5000
 +
 +
;----------------------------------------------------------------------------
 +
; EXEC-friendly ROM header.
 +
;----------------------------------------------------------------------------
 +
ROMHDR: BIDECLE ZERO            ; MOB picture base  (points to NULL list)
 +
        BIDECLE ZERO            ; Process table      (points to NULL list)
 +
        BIDECLE MAIN            ; Program start address
 +
        BIDECLE ZERO            ; Bkgnd picture base (points to NULL list)
 +
        BIDECLE ONES            ; GRAM pictures      (points to NULL list)
 +
        BIDECLE TITLE          ; Cartridge title/date
 +
        DECLE  $03C0          ; Flags:  No ECS title, run code after title,
 +
                                ; ... no clicks
 +
ZERO:  DECLE  $0000          ; Screen border control
 +
        DECLE  $0000          ; 0 = color stack, 1 = f/b mode
 +
ONES:  DECLE  1, 1, 1, 1, 1  ; Color stack initialization
 +
;----------------------------------------------------------------------------
 +
 +
TITLE  STRING  $107, "Wait Timer Demo", 0  ; Title string and date (2007)
 +
 +
MAIN    PROC
 +
</B>
 +
        [[CALL]]    CLRSCR              ; Clear the screen
 +
 
 +
        [[MVII]]    #MYISR, R0          ; \
 +
        [[MVO]]    R0,    $100        ;  |_ Write out "MYISR" to $100-$101
 +
        [[SWAP]]    R0                  ;  |  [[Double Byte Data]].
 +
        [[MVO]]    R0,    $101        ; /
 +
        [[EIS]]                        ; Make sure interrupts are enabled
 +
 
 +
@@loop:
 +
        ;; Print "Hello" at row #5, column #7. 
 +
        [[CALL]]    PRINT.FLS
 +
        DECLE  7, $200 + 5*20 + 7
 +
        STRING  "Hello", 0
 +
 
 +
        ;; Wait for 1 second
 +
        [[CALL]]    WAIT
 +
        DECLE  60
 +
 
 +
        ;; Print "World" at row #5, column #7. 
 +
        [[CALL]]    PRINT.FLS
 +
        DECLE  7, $200 + 5*20 + 7
 +
        STRING  "world", 0
 +
 
 +
        ;; Wait for 1 second
 +
        [[CALL]]    WAIT
 +
        DECLE  60
 +
 
 +
        ;; Do it again
 +
        [[B]]  @@loop
 +
 
 +
        ENDP
 +
 
 +
;; ======================================================================== ;;
 +
;;  MYISR -- A simple interrupt service routine                            ;;
 +
;; ======================================================================== ;;
 +
MYISR  PROC
 +
        [[MVO]]    R0,    $20    ; Enable the display
 +
         
 +
        [[MVI]]    WTIMER, R0      ; Get current timer count
 +
        [[DECR]]    R0              ; Decrement it
 +
        [[BMI]]    @@expired      ; If it went negative, it's expired
 +
        [[MVO]]    R0,    WTIMER  ; Store updated count
 +
@@expired:
 +
       
 +
        [[JR]]      R5              ; return from interrupt
 +
        ENDP
 +
 
 +
;; ======================================================================== ;;
 +
;;  WAIT -- Wait for some number of tics                                    ;;
 +
;;                                                                          ;;
 +
;;  INPUTS for WAIT                                                        ;;
 +
;;      1 decle after call:  Number of tics to wait                        ;;
 +
;;                                                                          ;;
 +
;;  INPUTS for WAIT.1                                                      ;;
 +
;;      R0  Number of tics to wait                                          ;;
 +
;;                                                                          ;;
 +
;;  OUTPUTS                                                                ;;
 +
;;      R0  Zeroed                                                          ;;
 +
;;                                                                          ;;
 +
;;  NOTE                                                                    ;;
 +
;;      Requires WTIMER code in ISR, and interrupts enabled.                ;;
 +
;;                                                                          ;;
 +
;; ======================================================================== ;;
 +
WAIT    PROC
 +
        [[MVI@]]    R5,    R0          ; Get # of tics to wait from after CALL
 +
@@1:    [[MVO]]    R0,    WTIMER      ; Set up wait timer
 +
 
 +
        [[CLRR]]    R0                  ; \
 +
@@wait: [[CMP]]    WTIMER, R0          ;  |- Wait for WTIMER = 0
 +
        [[BNEQ]]    @@wait              ; /
 +
 
 +
        [[JR]]      R5                  ; return
 +
        ENDP
 +
<B>
 +
;; ======================================================================== ;;
 +
;;  Library includes                                                        ;;
 +
;; ======================================================================== ;;
 +
        INCLUDE    "fillmem.asm"
 +
        INCLUDE    "print.asm"
 +
</B>
 +
 +
Copy this out to a file, <CODE>wtdemo.asm</CODE>, along with copies of
 +
<CODE>print.asm</CODE> and <CODE>fillmem.asm</CODE> that were linked to above.
 +
Then assemble with:
 +
 +
    as1600 -o wtdemo -l wtdemo.lst wtdemo.asm
 +
 +
Ta da!  When you run this program, you should get a display that alternates
 +
between "Hello" and "World" in the middle of the screen with a 1 second
 +
delay between each.

Latest revision as of 08:42, 4 December 2010

Personal tools
Namespaces
Variants
Actions
Navigation
Toolbox