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!

Michał Taszycki

March 01, 2022
Tweet

More Decks by Michał Taszycki

Other Decks in Programming

Transcript

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

    at a time h tt p:/ /64bites.com/
  2. 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; }
  3. “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
  4. LDA adress LDA #value RAM ➞ A register STA address

    A register ➞ RAM The most important instructions
  5. 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)
  6. rts main: lda # sta background_color lda # sta border_color

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

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

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

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

    rts main: .macro border(color) { } :background( ) color lda # sta border_color GRAY DARK_GRAY color Macros
  11. .macro nops( ) { } .for (var i = 0;

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

    = 0; i < ; i++) { nop } :background(BLACK) :background(DARK_GRAY) 230 count count Delay
  13. // Disable IRQ handling sei // Enable IRQ handling cli

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

    Disable interrupts while we do it
  15. // 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
  16. // 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
  17. 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
  18. 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
  19. 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)
  20. .macro request_raster_irq(line_number, handler) { } lda #<handler sta irq_vector lda

    #>handler sta irq_vector + 1 Inject the adress of the handler
  21. .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
  22. .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
  23. .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
  24. 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
  25. :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!
  26. // 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
  27. .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
  28. 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
  29. 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)
  30. 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)
  31. 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)
  32. 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)
  33. 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)
  34. 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
  35. 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
  36. 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
  37. // Import music from a file .pc=$1000 music: .import binary

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

    a file .pc=$1000 music: .import binary "music.bin" Initialize the player
  39. 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
  40. 1 episode • 5-10 minute video • 1 small epiphany

    • Full transcript • Source code • Exercises $3.99
  41. Get everything • 108 videos • 235 exercises • 156

    000 lines of code • Best price • less than $2 per episode $199