Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Learn RASTER SHADERS on Commodore 64

Learn RASTER SHADERS on Commodore 64

Register at
🔗 https://64bites.com/raster-live
to receive recorded video, source code and free lessons.

You will learn:
1. How to use two most important assembly instructions - [REDACTED] and [REDACTED]
2. How to draw Raster Bars?
3. How to stabilize the raster interrupts?
4. How to open borders?
5. How to draw under the border?
6. How to display more than 8 sprites?
7. How to play sid music?

Video cards can execute small programs whenever a pixel is displayed on screen.
Commodore 64 can execute small programs whenever a raster line is displayed on TV.

Pixel shaders can be used to create realistic lighting, special effects and even compute physics!
‘Raster shaders’ can be used to split screen, display more sprites and even play sound!

85977ebfe59c2ee669f2196930f1a701?s=128

Michał Taszycki

March 01, 2022
Tweet

More Decks by Michał Taszycki

Other Decks in Programming

Transcript

  1. “RASTER SHADERS” on

  2. I keep old computers alive mehowte Michał Taszycki on

  3. I can teach you to program Commodore 64 one bite

    at a time h tt p:/ /64bites.com/
  4. I have bad news for you…

  5. Commodore 64 belongs to the past…

  6. Today

  7. Back then

  8. Today

  9. Back then

  10. Back then

  11. Back then?

  12. Wait!

  13. Today!

  14. Today!

  15. HOW?!

  16. Pixel Shader varying vec3 N; varying vec3 v; void main(void)

    { vec3 L = normalize(gl_LightSource[0].position.xyz - v); vec4 Idiff = gl_FrontLightProduct[0].diffuse * max(dot(N,L), 0.0); Idiff = clamp(Idiff, 0.0, 1.0); gl_FragColor = Idiff; }
  17. “Raster Shader” irq: lda #$ff sta $d019 ldx #$05 d:

    dex bne d ldx #$00 c: ldy #$08 a: lda colors,x sta $d020 sta $d021 inx dey beq c txa ldx #$07 b: dex bne b tax cpx #$8c bcc a
  18. Enough with theory!

  19. Let’s code!

  20. A minimal program :BasicUpstart2(main) main: rts

  21. None
  22. The most important instructions

  23. LDA adress LDA #value RAM ➞ A register STA address

    A register ➞ RAM The most important instructions
  24. rts main: lda # sta background_color lda # sta border_color

    GRAY DARK_GRAY Border & Backgound Colors .label border_color = $d020 .label background_color = $d021 :BasicUpstart2(main)
  25. Macros rts main: lda # sta background_color lda # sta

    border_color GRAY DARK_GRAY
  26. rts main: lda # sta background_color lda # sta border_color

    GRAY DARK_GRAY .macro background(color) { } Macros
  27. lda # sta background_color rts main: :background( ) color lda

    # sta border_color GRAY DARK_GRAY .macro background(color) { } Macros
  28. lda # sta background_color rts main: :background( ) color lda

    # sta border_color GRAY DARK_GRAY .macro background(color) { } Macros
  29. lda # sta background_color rts main: .macro border(color) { }

    :background( ) color lda # sta border_color GRAY DARK_GRAY .macro background(color) { } Macros
  30. :border( ) .macro background(color) { } lda # sta background_color

    rts main: .macro border(color) { } :background( ) color lda # sta border_color GRAY DARK_GRAY color Macros
  31. None
  32. Meet Raster Bars

  33. What’s the secret behind Raster Bars?

  34. Doing nothing!

  35. Raster Bars // long delay :background(BLACK) :background(DARK_GRAY)

  36. Delay :background(BLACK) :background(DARK_GRAY) nop

  37. :background(BLACK) :background(DARK_GRAY) nop nop Delay

  38. :background(BLACK) :background(DARK_GRAY) nop nop nop Delay

  39. :background(BLACK) :background(DARK_GRAY) nop // ... many more nops nop nop

    Delay
  40. :background(BLACK) :background(DARK_GRAY) .for (var i = 0; i < ;

    i++) { nop } 230 Delay
  41. .macro nops( ) { } .for (var i = 0;

    i < ; i++) { nop } :background(BLACK) :background(DARK_GRAY) 230 count Delay
  42. :nops( ) .macro nops( ) { } .for (var i

    = 0; i < ; i++) { nop } :background(BLACK) :background(DARK_GRAY) 230 count count Delay
  43. This will draw the bar… Somewhere :(

  44. We need to start drawing at a specific Raster Line

  45. We need to invoke a “Raster Shader”

  46. We’ll get there but first…

  47. Take a look at the cursor

  48. Take a look at the cursor WHY DOES IT BLINK?

  49. Meet Interrupts Small routines (let’s call them “shaders”) invoked by

    a signal (CIA TIMER, VIC CHIP)
  50. Here’s our “Shader” raster_bars_irq: jmp irq_handler :nops( ) :background(BLACK) :background(DARK_GRAY)

    230
  51. // Disable IRQ handling sei // Enable IRQ handling cli

    Now we need to attach it to a signal
  52. // Disable IRQ handling sei // Enable IRQ handling cli

    Disable interrupts while we do it
  53. // Disable IRQ handling sei // inject the handler lda

    #<raster_bars_irq sta irq_vector lda #>raster_bars_irq sta irq_vector + 1 // Enable IRQ handling cli Inject interrupt handler
  54. None
  55. // inject the handler lda #<raster_bars_irq sta irq_vector lda #>raster_bars_irq

    sta irq_vector + 1 Let’s Use another type of IRQ cli sei
  56. cli sei First delete the injection

  57. cli sei // Disable the interrupt generated by timers lda

    #%01111111 sta cia1_interrupt_control_register sta cia2_interrupt_control_register // Negate already scheduled interrupts lda cia1_interrupt_control_register lda cia2_interrupt_control_register Disable Timer IRQs
  58. But Enable Raster IRQs cli sei // Disable the interrupt

    generated by timers lda #%01111111 sta cia1_interrupt_control_register sta cia2_interrupt_control_register // Negate already scheduled interrupts lda cia1_interrupt_control_register lda cia2_interrupt_control_register // Enable raster interrupts lda #%00000001 sta vic2_interrupt_control_register
  59. Let’s Choose Raster Line cli sei // Disable the interrupt

    generated by timers lda #%01111111 sta cia1_interrupt_control_register sta cia2_interrupt_control_register // Negate already scheduled interrupts lda cia1_interrupt_control_register lda cia2_interrupt_control_register // Enable raster interrupts lda #%00000001 sta vic2_interrupt_control_register :request_raster_irq(106, raster_bars_irq)
  60. .macro request_raster_irq(line_number, handler) { } How to Choose Raster Line

  61. .macro request_raster_irq(line_number, handler) { } lda #<handler sta irq_vector lda

    #>handler sta irq_vector + 1 Inject the adress of the handler
  62. .macro request_raster_irq(line_number, handler) { } lda #<handler sta irq_vector lda

    #>handler sta irq_vector + 1 lda #line_number sta vic2_rasterline_register Choose Raster Line
  63. .macro request_raster_irq(line_number, handler) { } lda #<handler sta irq_vector lda

    #>handler sta irq_vector + 1 .if (line_number > 255) { :set_bits(screen_control_register, %10000000) } else { :clear_bits(screen_control_register, %10000000) } lda #line_number sta vic2_rasterline_register It might not fit in a byte
  64. .macro set_bits(address, bit_mask) { lda address ora #bit_mask sta address

    } .macro clear_bits(address, bit_mask) { lda address and #255 - bit_mask sta address } Helper macros to toggle bits
  65. Raster IRQs need to be Acknowledged jmp irq_handler raster_bars_irq: :background(BLACK)

    :nops(230) :background(DARK_GRAY)
  66. raster_bars_irq: :background(BLACK) :nops(230) :background(DARK_GRAY) lda #%00000001 sta vic2_interrupt_status_register jmp irq_handler

    By touching special register
  67. raster_bars_irq: :background(BLACK) :nops(230) :background(DARK_GRAY) :exit_irq_handler() .macro exit_irq_handler() { } lda

    #%00000001 sta vic2_interrupt_status_register jmp irq_handler Let’s put it in a macro
  68. None
  69. We need stabilization :exit_irq_handler() raster_bars_irq: :background(BLACK) :nops(230) :background(DARK_GRAY)

  70. But stabilization is… complicated :exit_irq_handler() raster_bars_irq: :background(BLACK) :nops(230) :background(DARK_GRAY)

  71. Let’s fake it ;) :exit_irq_handler() raster_bars_irq: :nops(14) :background(BLACK) :nops(230) :background(DARK_GRAY)

  72. We’ll hide jittering under the border ;) :exit_irq_handler() raster_bars_irq: :nops(14)

    :background(BLACK) :nops(230) :background(DARK_GRAY)
  73. None
  74. Let’s make more bars! :exit_irq_handler() raster_bars_irq:

  75. :nops(14) :background(BLACK) :nops(4) :background(DARK_GRAY) :nops(29) :background(GRAY) :nops(29) :background(LIGHT_GRAY) :nops(29) :background(WHITE)

    :exit_irq_handler() raster_bars_irq: Let’s make more bars!
  76. :nops(14) :background(BLACK) :nops(4) :background(DARK_GRAY) :nops(29) :background(GRAY) :nops(29) :background(LIGHT_GRAY) :nops(29) :background(WHITE)

    :exit_irq_handler() raster_bars_irq: // ... Let’s make more bars!
  77. :nops(14) :background(BLACK) :nops(4) :background(DARK_GRAY) :nops(29) :background(GRAY) :nops(29) :background(LIGHT_GRAY) :nops(29) :background(WHITE)

    :exit_irq_handler() raster_bars_irq: // ... // ... Let’s make more bars!
  78. :nops(14) :background(BLACK) :nops(4) :background(DARK_GRAY) :nops(29) :background(GRAY) :nops(29) :background(LIGHT_GRAY) :nops(29) :background(WHITE)

    :exit_irq_handler() raster_bars_irq: :nops(29) :background(LIGHT_GRAY) :nops(29) :background(GRAY) :nops(29) :background(DARK_GRAY) :nops(29) :background(BLACK) :nops(4) :background(DARK_GRAY) // ... // ... Let’s make more bars!
  79. None
  80. Meet Sprites Hardware-accelerated bitmaps

  81. // Enable all 8 sprites lda #%11111111 sta sprites.enable_bits Sprites

    on/off
  82. // Enable all 8 sprites lda #%11111111 sta sprites.enable_bits .for

    (var i = 0; i < 8; i++) { :set_sprite_position_x(i, 60 + 32 * i) } Sprites’ positions
  83. .macro set_sprite_position_x(index, position_x) { } Sprite position

  84. .macro set_sprite_position_x(index, position_x) { } lda #position_x sta sprites.positions +

    index*2 + 0 Sprite position
  85. .macro set_sprite_position_x(index, position_x) { } .if (position_x > 255) {

    :set_bits(sprites.position_x_high_bits,1<<index) } else { :clear_bits(sprites.position_x_high_bits,1<<index) } lda #position_x sta sprites.positions + index*2 + 0 Sprite position
  86. Another “Shader”? raster_bars_irq: :exit_irq_handler() // ... :nops(14)

  87. sprite_row_irq1: :exit_irq_handler() // ... raster_bars_irq: :exit_irq_handler() // ... :nops(14) Another

    Interrupt
  88. sprite_row_irq1: :exit_irq_handler() // ... raster_bars_irq: :exit_irq_handler() // ... :request_raster_irq(60, sprite_row_irq1)

    :nops(14) Schedule 2nd from the 1st
  89. sprite_row_irq1: :exit_irq_handler() // ... :request_raster_irq(106, raster_bars_irq) raster_bars_irq: :exit_irq_handler() // ...

    :request_raster_irq(60, sprite_row_irq1) :nops(14) Schedule 1st from the 2nd
  90. sprite_row_irq1: :exit_irq_handler() // Set the y-position, bitmaps and colors :sprites_row(70,

    0, 3) raster_bars_irq: :exit_irq_handler() // ... :request_raster_irq(60, sprite_row_irq1) :request_raster_irq(106, raster_bars_irq) :nops(14) Vertical Sprites’ positions
  91. None
  92. By the way

  93. WTH is Raster time?

  94. A simple way to measure the complexity of a routine

    Raster time
  95. sprite_row_irq1: :exit_irq_handler() // Set the y-position, bitmaps and colors :sprites_row(70,

    0, 3) Raster time raster_bars_irq: :exit_irq_handler() // ... :request_raster_irq(60, sprite_row_irq1) :request_raster_irq(106, raster_bars_irq) :nops(14)
  96. sprite_row_irq1: :exit_irq_handler() // Set the y-position, bitmaps and colors :sprites_row(70,

    0, 3) Raster time raster_bars_irq: :exit_irq_handler() // ... :request_raster_irq(60, sprite_row_irq1) :request_raster_irq(106, raster_bars_irq) :border(RED) :border(GRAY) :nops(14)
  97. sprite_row_irq1: :exit_irq_handler() // Set the y-position, bitmaps and colors :sprites_row(70,

    0, 3) Raster time raster_bars_irq: :exit_irq_handler() // ... :request_raster_irq(60, sprite_row_irq1) :request_raster_irq(106, raster_bars_irq) :nops(14) :border(RED) :border(GRAY) :border(GRAY) :border(ORANGE)
  98. sprite_row_irq1: :exit_irq_handler() // Set the y-position, bitmaps and colors :sprites_row(70,

    0, 3) Raster time raster_bars_irq: :exit_irq_handler() // ... :request_raster_irq(60, sprite_row_irq1) :request_raster_irq(106, raster_bars_irq) :nops(14) :border(RED) :border(GRAY) :border(GRAY) :border(ORANGE) WATCH OUT! (SYNCHRONIZATION IS OFF)
  99. sprite_row_irq1: :exit_irq_handler() // Set the y-position, bitmaps and colors :sprites_row(70,

    0, 3) Raster time raster_bars_irq: :exit_irq_handler() // ... :request_raster_irq(60, sprite_row_irq1) :request_raster_irq(106, raster_bars_irq) :nops(7) :border(RED) :border(GRAY) :border(GRAY) :border(ORANGE)
  100. None
  101. Let’s display more sprites!

  102. Wait, what?

  103. More than 8?

  104. More than 8?

  105. Interrupt chain raster_bars_irq: :request_raster_irq( ) :exit_irq_handler() 60, sprite_row_irq1 sprite_row_irq1: //

    ... // ...
  106. raster_bars_irq: :request_raster_irq( ) :exit_irq_handler() sprite_row_irq2: :border(CYAN) :sprites_row(160, 8, 12) :border(GRAY)

    :request_raster_irq( ) :exit_irq_handler() 60, sprite_row_irq1 sprite_row_irq1: // ... // ... Another interrupt handler
  107. raster_bars_irq: :request_raster_irq( ) :exit_irq_handler() sprite_row_irq2: :border(CYAN) :sprites_row(160, 8, 12) :border(GRAY)

    :request_raster_irq( ) :exit_irq_handler() 60, sprite_row_irq1 sprite_row_irq1: // ... // ... 150, sprite_row_irq2 Let’s add it to the chain
  108. None
  109. Let’s open the border!

  110. Thin Green Line… :request_raster_irq(248, open_border_irq) open_border_irq: :border(GREEN) :nops(14) :border(GRAY) :request_raster_irq(60,

    sprite_row_irq1) :exit_irq_handler()
  111. None
  112. None
  113. Before reaching the line sprite_row_irq2: :request_raster_irq(248, open_border_irq) :exit_irq_handler() open_border_irq: :request_raster_irq(60,

    sprite_row_irq1) :exit_irq_handler()
  114. sprite_row_irq2: :request_raster_irq(248, open_border_irq) :exit_irq_handler() open_border_irq: :request_raster_irq(60, sprite_row_irq1) :exit_irq_handler() // Enable

    25 lines mode :set_bits(screen_control_register, %00001000) Make border thin
  115. sprite_row_irq2: :request_raster_irq(248, open_border_irq) :exit_irq_handler() open_border_irq: :request_raster_irq(60, sprite_row_irq1) :exit_irq_handler() // Enable

    25 lines mode :set_bits(screen_control_register, %00001000) After we’ve passed that line
  116. sprite_row_irq2: :request_raster_irq(248, open_border_irq) :exit_irq_handler() open_border_irq: :request_raster_irq(60, sprite_row_irq1) :exit_irq_handler() // Enable

    24 lines mode :clear_bits(screen_control_register, %00001000) // Enable 25 lines mode :set_bits(screen_control_register, %00001000) Make border wide
  117. None
  118. What’s the point? You can’t draw any characters in this

    area :(
  119. But you can draw a bar code ;) lda #%10110101

    sta border_pattern
  120. None
  121. And Sprites!

  122. None
  123. Let’s play SID music

  124. What’s a SID file?

  125. It’s like an mp3 for C64…

  126. But the player is included!

  127. // Import music from a file .pc=$1000 music: .import binary

    "music.bin" Import SID file into the memory
  128. main: // initialize music jsr music // Import music from

    a file .pc=$1000 music: .import binary "music.bin" Initialize the player
  129. main: // initialize music jsr music // Import music from

    a file .pc=$1000 music: .import binary "music.bin" play_music: :border(YELLOW) jsr $music + 3 :border(GRAY) :request_raster_irq(248, open_border_irq) :exit_irq_handler() Play a tiny bit of music
  130. None
  131. How did you like it?

  132. Want more?

  133. I can teach you to program Commodore 64 one bite

    at a time
  134. Short & Sweet Commodore 64 programming lessons

  135. Tons of Small Epiphanies

  136. Short Focused Deep

  137. BASIC to Assembly

  138. Difficult Concepts Explained

  139. Step By Step

  140. Modern Programming Techniques

  141. Useful For Games

  142. Useful For Demos

  143. 1 episode • 5-10 minute video • 1 small epiphany

    • Full transcript • Source code • Exercises $3.99
  144. Bundles of 9 episodes Seasons

  145. None
  146. 1 season • 9 videos • 9 small epiphanies •

    Lower price $19.99
  147. but…

  148. You can also…

  149. Get them all!

  150. Get them all!

  151. of short video lessons Huge library

  152. None
  153. Videos 108

  154. Exercises 235

  155. Lines of code 156 000

  156. None
  157. Get everything • 108 videos • 235 exercises • 156

    000 lines of code • Best price • less than $2 per episode $199
  158. Get everything less than $2 per episode $199 64bites.com/all