home *** CD-ROM | disk | FTP | other *** search
/ 8bitfiles.net/archives / archives.tar / archives / genie-commodore-file-library / Information / INTERRUPT-ROUTINES < prev    next >
Encoding:
Text File  |  2019-04-13  |  21.3 KB  |  460 lines

  1. *********************************************************************
  2. This article is being presented through the *StarBoard* Journal of
  3. the FlagShip/StarShip, SIGS (Special Interest Groups) on the
  4. Delphi and GEnie telecommunications networks.  Permission is
  5. hereby granted to non-profit organizations only to reprint this
  6. article or pass it along electronically as long as proper credit
  7. is given to both the author and the *StarBoard* Journal.
  8. *********************************************************************
  9.  
  10.     INTERRUPTS ON THE COMMODORE 64
  11.  
  12.         By Roy Riggs aka DevilTM
  13.  
  14.  
  15. INTRODUCTION
  16.      This text file is a detailed explanation of interrupts.  It is
  17. written for an intermediate level machine language programmer as a
  18. HOW-TO write your own interrupt routines.  If you have been programming
  19. ML this has probably been an area that you have stayed away from.
  20. Interrupts can be intimidating at first, but after some hands-on
  21. experience you will find that they are no harder than any other
  22. programming technique.  Hopefully, this file will give you the push you
  23. need so that you will try writing your own interrupt routines.
  24.  
  25. WHAT YOU NEED TO KNOW
  26.      Before you continue you should have a fairly solid base in ML
  27. programming, at least an understanding of all the op codes, the status
  28. register, the stack, vectors, and common KERNAL routines.  If you
  29. don't, the author personally recommends Jim Butterfield's book of
  30. Machine Language for Commodore computers.  Please note all references,
  31. memory locations, and specifications in this file are for the Commodore
  32. 64 or the Commodore 128 if in 64 mode ONLY.
  33.  
  34. LEARNING THE BASICS
  35.      An interrupt is when the processor is told to stop what it is
  36. doing.  It is temporarily given a new set of instructions to process.
  37. This new set consists of the interrupt routines.  When the routines are
  38. finished, the original program continutes execution undisturbed.  On
  39. the C64 the special interrupt routines perform such vital functions as
  40. updating the system clock, checking for the RUN/STOP key, blinking the
  41. cursor, handling the cassette motor, and checking the keyboard for
  42. input.  The interrupt happens approximately 60 times a second and is an
  43. important part of the operating system.  If the interrupts were ever
  44. disabled the computer would lock up.
  45.  
  46. WHAT REALLY HAPPENS
  47.     When you first turn on the C64 the CIA #1 Timer B will start
  48. triggering an interrupt request (IRQ) every 1/60 of a second.  When
  49. this happens the processor finishes the instruction it is working on,
  50. saves the program counter and the status register on the stack, then
  51. JMPs indirectly to the address stored in $FFFE and $FFFF.  This is
  52. normally $FF48.  The first thing that the routine does is an SEI to
  53. keep the interrupt from interrupting itself.  Next it saves all the
  54. registers on the stack in the order of A, X, Y.  This is VERY
  55. important!  In order for the original program to run undisturbed, it
  56. will have to restore these values later.  Since this routine is also
  57. used by the BRK interrupt, the next thing it will do is test to see if
  58. the interrupt was from a BRK or an IRQ.  It does this by checking bit 4
  59. of the status register (SR).  If the B or BRK flag is set then it JMPs
  60. to the vector stored in $316-$317.  This is usually the break routine
  61. $FE66.  Otherwise it is an IRQ and is vectored thru $314-$315 to $EA31,
  62. the keyboard scan.  It will continue through the rest of the routines
  63. mentioned above until it is done, at which point it will pull the Y, X,
  64. and A back off the stack and execute a RTI return from interrupt.  The
  65. old program counter PC and the status register SR are restored; control
  66. is returned to the original program.
  67.  
  68. WHY DO WE NEED THEM?
  69.      A good question indeed.  Like any other programming technique,
  70. interrupts are designed to make certain tasks easier.  A common example
  71. is split-screen graphics.  Try and think of a way to make the top half
  72. of the screen bit mapped for pictures and the bottom for text.  You
  73. probably can't think of a way to do it without printing letters in
  74. high-res or drawing pictures with redefined characters.  Either way is
  75. very messy.  Raster interrupts will be explained later; they make this
  76. problem a piece of cake.  Since IRQs happen every 1/60 of a second they
  77. are very useful for timing critical code.  This is why so many music
  78. programs use interrupts, such a perfect way to insure a steady tempo
  79. regardless of what is happening elsewhere.  Finally, interrupts are
  80. sometimes the only way you can do something; for example, things that
  81. need to run as background process regardless of what application is
  82. executed.  If you wanted to make the border flash red every 2 seconds
  83. while running any BASIC program, interrupts would be the only way to
  84. solve the programming problems.
  85.  
  86. GETTING BACK TO BUSINESS
  87.      The basic idea when writing an interrupt routine is to make a
  88. wedge.  This means we will make a slight detour through our code (to do
  89. what we want) before going on with what normally happens.  Every time
  90. an interrupt occurs it will go through our code so we have a choice of
  91. doing whatever we want.  To do this we change the vector at $314-$315
  92. to point to our routine.  When we are finished we usually can just jump
  93. to the normal interrupt routines.  Interrupt wedges are typically
  94. broken into two parts.  The first part is typically very short and is
  95. used to initialize the wedge and reset the vectors.  The second part is
  96. also pretty short; it is the actual detour.  Lengthy code will slow the
  97. works down because it is run every 1/60 of a second.  It must take
  98. every interrupt, find out what caused it, service it appropriately,
  99. clear the interrupt, and either terminate by itself, or jump to the
  100. normal interrupt routines.
  101.  
  102. IMPORTANT NOTE:  Whenever you change an interrupt vector make sure you
  103. SEI.  Otherwise if an IRQ hits in the middle of changing it, the vector
  104. will point to nowhere.. CRASH!  Also don't forget CLI when you are
  105. done.  Quick review, SEI means set interrupt disable.  This means that
  106. no interrupts will occur until a CLI is executed.
  107.  
  108. LET'S GET STARTED
  109.      Now we know enough to write a simple interrupt routine.  The only
  110. way to learn is by doing, so here is some source code with lots of
  111. comments.  This routine will flash the border every half of a second.
  112. Don't just skim over this code; make sure you understand the purpose of
  113. every instruction.  Remember, by the time it gets to our routine it has
  114. already saved the registers.  Since we JMP to the normal routine when
  115. done, we do not have to worry about preserving them.
  116.  
  117.  
  118. 0100 *interrupt border flasher
  119. 0200 *by roy riggs  aka  deviltm
  120. 1000            .or $033c
  121. 1010 irq        .eq $0314
  122. 1020 vector     .eq $fc
  123. 1030 timer      .eq $fe
  124. 1040 border     .eq $d020
  125.  
  126. *RESET VECTOR AND INTIALIZE TIMER
  127.  
  128. 1050            lda irq      ;save old vector
  129. 1060            sta vector   ;for when we are done
  130. 1070            lda irq+1
  131. 1080            sta vector+1
  132. 1090            sei          ;can't forget this!
  133. 1100            lda #<start  ;point vector to our
  134. 1110            sta irq      ;routine instead of old one
  135. 1120            lda #>start
  136. 1130            sta irq+1
  137. 1140            cli          ;safe now
  138. 1150            lda #30      ;init timer for .5 seconds
  139. 1160            sta timer
  140. 1170            rts          ;go back
  141.  
  142. *THIS IS THE ACTUAL WEDGE
  143.  
  144. 1180 start      dec timer    ;countdown timer
  145. 1190            bne cont     ;time to change yet?
  146. 1200            inc border   ;yes, so flash it
  147. 1210            lda #30      ;init timer again
  148. 1220            sta timer
  149. 1230 cont       jmp (vector) ;continue on with normal IRQ
  150.  
  151.      Hopefully you got through that with little or no problem.  As
  152. mentioned earlier, knowledge of vectors is essential here.  Now is a
  153. good time to brush up on them if they gave you problems.  If you are
  154. really serious about learning this, you will spend the 5-10 minutes it
  155. takes to type that in.  Once you enter it, you will understand more by
  156. modifying the code than by just reading this file.
  157.  
  158. OTHER SOURCES OF INTERRUPT
  159.      Besides the CIA #1 Timer B, there are several other things that
  160. can cause an interrupt.  There are the raster interrupt,
  161. Sprite-Background collision, Sprite-Sprite collision, and the negative
  162. transition of the light pen.  These are not serviced in the normal
  163. interrupt routines, so we must write our own code to use them.
  164.  
  165. OK, SO HOW?
  166.      Like everything else, interrupts have special memory locations.
  167. Logically, the first thing we need is a way to tell exactly what caused
  168. the interrupt.  The story is told in the INTERRUPT STATUS REGISTER,
  169. which I called INTSTAT for short.  You do not want to get this mixed up
  170. with the normal status register SR.  The INTSTAT is stored in $D019 or
  171. 53273.  Each bit of the INTSTAT refers to a different interrupt.
  172.  
  173.  Name   Bit    Cause of Interrupt
  174. ---------------------------------
  175.  IRST    0     Raster
  176.  IMDC    1     Sprite-Background
  177.  IMMC    2     Sprite-Sprite
  178.  ILP     3     Light pen
  179.  
  180. Note:  the 7th bit is the IRQ and this bit is set on ALL interrupts.
  181.  
  182.      The SEI command provides an easy way of momentarily disabling ALL
  183. interrupts.  However, sometimes we want to turn off just some of them.
  184. This can be done with the INTERRUPT ENABLE REGISTER, or INTENAB.  It is
  185. at $D01A or 53274.  Its format is identical to the INTSTAT, but it
  186. performs a totally different function.  Care must be taken not to get
  187. them confused.  Remember, the INTSTAT shows what caused the interrupt.
  188. The INTENAB determines what CAN cause an interrupt.  To enable an
  189. interrupt from a source, its corresponding bit in INTENAB must be
  190. turned on;  (Use the same table above)  ie, set bit 2 to enable
  191. Sprite-Sprite interrupts.  [NOTE:  The INTSTAT can still be checked
  192. even if the interrupt for that bit is disabled.  If the condition is
  193. right, the INTSTAT will be set, but it will not trigger an IRQ.  Once
  194. again, you POKE the INTENAB to select which interrupts can occur and
  195. PEEK the INTSTAT to find what caused the interrupt].
  196.      Also when you write your own routines to service interrupts, you
  197. must remember to clear the interrupt when you are finished.  INTSTAT
  198. gets 'latched' which means, once a bit is set it stays set until
  199. cleared.  To clear the interrupt you write a 1 to the corresponding bit
  200. in INTSTAT.  This does seem backwards, but that is how it works; ie,
  201. after you get done handling a raster interrupt (bit 0):
  202.      LDA #$01  STA INTST
  203. This way if several bits are set you can handle them one at a time.
  204.      Now in the last program we jumped to the normal routine when done;
  205. this is a very good idea normally.  However, if the interrupt was not
  206. caused by the CIA timer, you should handle the entire interrupt
  207. yourself.  The reason is that those routines expect to be called every
  208. 1/60 of a second, no more or no less.  For example, if you are using
  209. raster interrupts (explained shortly) and call the normal IRQ routine
  210. when done every time, they will be executed more often than they
  211. should.  You will notice that the cursor blinks a lot faster and the
  212. system clock starts flying.  This apparent burst of speed is deceptive.
  213. The actual program that is running is really running slower!  To return
  214. from an interrupt you must restore the registers from the stack with:
  215. PLA, TAY, PLA, TAX, PLA, and RTI.  NOTE:  The status register and
  216. program counter will be pulled off the stack by the PROCESSSOR after
  217. the RTI.
  218.  
  219. ADVANCED WEDGE
  220.      The first program used only the normal IRQ.  Notice that all of
  221. the interrupts whether normal, raster, or sprite ALL go to the same
  222. vector.  We will have to make sure we find out why the interrupt
  223. occurred so we can service it appropriately.  To determine the source
  224. we just have to look at the bits of INTSTAT.
  225.  
  226. SPRITE INTERRUPTS
  227.      If you have ever written sprite collision routines, you probably
  228. had a hard time with them overlapping.  By the time your program gets
  229. around to checking them, they are already on top of each other.  Now
  230. with interrupts you will know the very instant they touch.  I will
  231. leave handling collisions up to the reader since interrupts are merely
  232. another method of detecting the collision.  After you find that the IRQ
  233. was from sprites you can basically use your old sprite collision
  234. routines.  An in depth look at sprites is out of the range of the topic
  235. at hand.
  236.  
  237. LIGHT PENS
  238.      Same deal as with sprites really.  Actually, it is probably easier
  239. to read the light pen normally than mess with interrupts.
  240.  
  241. RASTER INTERRUPTS
  242.      Raster interrupts are kind of tricky, and since they can only be
  243. accessed from interrupts, most readers are still in the dark.  Let's
  244. cover the basics here first.  The picture generated by your television
  245. or monitor is NOT projected all at once.  Look closely at your screen;
  246. notice how it is made of a bunch of horizontal lines?  These lines are
  247. called scan lines.  At the back of the picture tube is an electron gun.
  248. It is like a machine gun, continually firing electrons at the screen.
  249. The back of the screen is coated with phosphorus, and whenever an
  250. electron hits it, it flashes.  The gun sweeps back and forth across the
  251. screen from top to bottom.  It moves with such incredible speed that
  252. before the flash dies out in one spot, another electron will hit it.
  253. To the slow human eye all this appears to be a solid screen.  The
  254. raster is the current scan line that the gun is shooting at.  The value
  255. of the raster will vary from 0 - 262 on normal television; on a
  256. European television it goes from 0 - 312.  Since these values are
  257. greater than 255, we can not fit them all in one byte; therefore 9 bits
  258. are used to store the raster.  The lower 8 bits are stored in $D012,
  259. and the 9th bit is stored in bit 7 of $D011.  Note that the top of the
  260. screen corresponds with a value of 50 and the bottom is at 249.  The
  261. raster value changes so fast that even a machine language program can't
  262. check it fast enough if it is going to do anything else.  Luckily, we
  263. can tell the hardware to trigger an IRQ every time the raster equals a
  264. specific value; this is how you write split-screen applications.  For a
  265. screen half high-res and half text, set the screen to high-res mode and
  266. tell it to interrupt at a point halfway down the screen.  Using a wedge
  267. we an catch it when this happens and set the screen back to text.  By
  268. telling it to interrupt when we get back to the top we can change back
  269. to high-res.  By flipping back and forth like this we achieve the
  270. desired effect.  Our program doesn't even have to check the raster at
  271. all.
  272.  
  273. THATS NICE, BUT HOW DO YOU CODE THAT?
  274.      Here is how it is really done.  First we have to specifiy the line
  275. for the interrupt.  You do this by storing the value in the raster
  276. $D012 and bit 7 of $D011.  You CANNOT just ignore this extra bit!!
  277. [NOTE:  This is also where the current raster is stored..  You probably
  278. wonder how it can hold both values at once.  Remember we are dealing
  279. with an IA here not memory.  The IA takes the value and will remember
  280. it].  Next, we must enable the raster interrupts.  To do this, set bit
  281. 0 of the INTENAB.  Finally, we must have our own interrupt wedge
  282. installed.  It will have to determine whether this is a normal IRQ or
  283. from the raster.  If normal then JMP to the normal vector.  If it is
  284. raster then do whatever we want: clear the latch, reset where we want
  285. raster interrupts to occur, then restore the registers ourself and
  286. return from the interrupt.  Ready for the next program?  This program
  287. uses raster interrupts to divide the border into 2 parts determined by
  288. the cursor location.
  289.  
  290.  
  291. 1000 *raster border change
  292. 1005 *by roy riggs  aka deviltm
  293. 1010            .or $c000
  294. 1020 border     .eq $d020
  295. 1030 raster     .eq $d012
  296. 1050 intstat    .eq $d019
  297. 1060 intenab    .eq $d01a
  298. 1070 irq        .eq $0314
  299. 1080 vector     .eq $fd
  300. 1090 plot       .eq $fff0
  301.  
  302. *TIME TO SET IT ALL UP
  303.  
  304. 1100            lda irq      ;copy old vector
  305. 1110            sta vector
  306. 1120            lda irq+1
  307. 1130            sta vector+1
  308. 1140            sei          ;be careful
  309. 1150            lda #<start  ;wedge our routine in
  310. 1160            sta irq
  311. 1170            lda #>start
  312. 1180            sta irq+1
  313. 1190            lda #15      ;set the first raster
  314. 1200            jsr setras
  315. 1210            lda intenab  ;turn raster interrupts on
  316. 1220            ora #$01
  317. 1230            sta intenab
  318. 1240            lda #$00     ;set black border to start
  319. 1250            sta border
  320. 1260            cli          ;its safe now
  321. 1270            rts          ;go back
  322.  
  323. *THIS IS THE REAL THING!
  324.  
  325. 1280 start      lda intstat
  326. 1290            and #$01     ;check sourde of IRQ
  327. 1300            bne ras      ;was is raster?
  328. 1310            jmp (vector) ;nope, go on to normal
  329. 1320 ras        lda border   ;yep
  330. 1330            eor #$01     ;flip colour
  331. 1340            and #$01
  332. 1350            sta border
  333. 1360            bne switch   ;are we at top or bottom?
  334. 1370            lda #15      ;bottom, so set to top
  335. 1380            jsr setras
  336. 1390            jmp sw
  337. 1400 switch     sec          ;top
  338. 1410            jsr plot     ;where is cursor?
  339. 1420            txa
  340. 1430            asl          ;multiply by 8
  341. 1440            asl          ;cuz theres 8 scan lines
  342. 1450            asl          ;per character
  343. 1460            clc          ;top of screen starts at 50
  344. 1470            adc #50      ;(see page 157 of ref manual)
  345. 1480            jsr setras
  346. 1490 sw         lda #$01     ;clear latch
  347. 1500            sta intstat
  348. 1510            pla          ;restore everything
  349. 1520            tay
  350. 1530            pla
  351. 1540            tax
  352. 1550            pla
  353. 1560            rti          ;and away we go!
  354. 1570 setras     sta raster   ;set where the IRQ will occur
  355. 1580            lda raster-1 ;msb is stored here
  356. 1590            and #$7f     ;sets bit7=0
  357. 1600            sta raster-1
  358. 1610            rts
  359.  
  360.  
  361.  
  362. WRAPPING IT ALL UP
  363.      Here it is again from the top.  Interrupts are triggered every
  364. 1/60 of a second and by certain special conditions.  The processor
  365. stops and runs the interrupts routines vectored through $314-$315.
  366. These routines perform vital system functions.  Once the routines are
  367. completed, all the registers are restored and the processor returns to
  368. what it was doing.  In order to use the interrupts we need to insert a
  369. wedge into that vector.  (Don't forget, disable interrupts first and
  370. clear them afterwards!)  Wedges consist of two main parts.  The first
  371. part saves the old vector and inserts a new vector pointing to the
  372. second part of the wedge.  It also should initialize anything that the
  373. second needs and enable the interrupts we choose.  The second part is
  374. the meat of the program.  It must determine the cause of the interrupt
  375. and handle it.  Clear latches is necessary and, when finished it should
  376. return either by itself or through the normal IRQ routine.  Below is a
  377. flow chart for both parts.  The flowcharts are the skeleton for any
  378. interrupt routine you may write.  Leave out the branches you don't use
  379. and flush out the others to suit your purpose.
  380.  
  381.  
  382.             ****PART 1****
  383.  
  384.             SAVE OLD VECTOR
  385.                   I
  386.                  SEI
  387.                   I
  388.           POINT VECTOR TO PART2
  389.                   I
  390.         INTIALIZE DATA FOR PART2
  391.                   I
  392.            ENABLE INTERRUPTS
  393.                   I
  394.                  CLI
  395.                   I
  396.                 RETURN
  397.  
  398.  
  399.             ****PART 2****
  400.  
  401.             START
  402.               I          yes
  403.           NORMAL IRQ?------------I
  404.               I                  I
  405.               Ino                I
  406.               I                  I
  407.          WHAT CAUSED IT?         I
  408.          I      I      I         I
  409.     LIGHT PEN?  I   RASTER?    TIMING
  410.          I      I      I      SPECIFIC
  411.          I   SPRITE?   I        CODE
  412.          I      I      I         I
  413.          I      I      I         I
  414.       SERVICE   I   SERVICE      I
  415.          I   SERVICE   I       JUMP TO
  416.          I      I      I     NORMAL IRQ
  417.          I      I      I
  418.          I      I  SET RASTER
  419.          I      I FOR NEXT TIME
  420.          I      I      I
  421.       CLEAR INTERRUPT LATCH
  422.                 I
  423.         RESTORE REGISTERS
  424.        FROM STACK AND RTI
  425.  
  426.  
  427.  
  428. I THINK YOU FORGOT SOMETHING
  429.      Yes, I have been ignoring some things about interrupts, mainly
  430. because theese things are rarely used and your head is already swimming
  431. with enough stuff.  For those that insist on knowing everything,
  432. continue reading.
  433.      Only the IRQ interrupt has been explained so far, but there are
  434. two other interrupts: the BRK and the NMI.  BRK is triggered whenever
  435. the BRK command is executed.  It does not quite fulfill our definition
  436. of an interrupt, but it is similar.  Normally the BRK is vectored
  437. through locations $316-$317, which points to the same routine that is
  438. invoked by hitting RUN/STOP RESTORE.  NMI stands for non-maskable
  439. interrupts; this means it can not be turned off by the SEI command.
  440. The NMI can be triggered either by the RESTORE key or by CIA #2.  When
  441. triggered, it passes through the vector at $318-$319.  This leads to a
  442. routine that determines the source of the NMI.  If it was from CIA #2
  443. then it jumps to some RS-232 routines.  If it was the RESTORE key it
  444. checks to see if the RUN/STOP key is also pressed.  Surely you know
  445. what this does!  If the RUN/STOP key was not pressed, it simply
  446. returns.
  447.  
  448.  
  449.     The author gives permission for this file to be printed, copied, or
  450. reproduced in whole or part.  Please include the author's name if used.
  451. All source codes provided are written by the author.  These are now
  452. submitted to the public domain.
  453.  
  454. The author accepts no responsibility for damages resulting from gross
  455. negligence of English grammar, spelling, and punctuation, as this
  456. article was written late at night on his birthday. :)
  457.  
  458. Roy Riggs
  459. (DEVIL on GEnie)
  460.