text: /*
   * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
   *
   * SPDX-License-Identifier: BSD-3-Clause
   */
  
  #include <stdio.h>
  
  #include "pico.h"
  #include "pico/scanvideo.h"
  #include "pico/scanvideo/composable_scanline.h"
  #include "pico/multicore.h"
  #include "pico/sync.h"
  #include "pico/stdlib.h"
  
  #define DUAL_CORE_RENDER
  #define VGA_MODE vga_mode_320x240_60
  extern const struct scanvideo_pio_program video_24mhz_composable;
  
  // to make sure only one core updates the state when the frame number changes
  // todo note we should actually make sure here that the other core isn't still rendering (i.e. all must arrive before either can proceed - a la barrier)
  static struct mutex frame_logic_mutex;
  
  static void frame_update_logic();
  static void render_scanline(struct scanvideo_scanline_buffer *dest, int core);
  
  // "Worker thread" for each core
  void render_loop() {
      static uint32_t last_frame_num = 0;
      int core_num = get_core_num();
      printf("Rendering on core %d\n", core_num);
      while (true) {
          struct scanvideo_scanline_buffer *scanline_buffer = scanvideo_begin_scanline_generation(true);
          mutex_enter_blocking(&frame_logic_mutex);
          uint32_t frame_num = scanvideo_frame_number(scanline_buffer->scanline_id);
          // Note that with multiple cores we may have got here not for the first
          // scanline, however one of the cores will do this logic first before either
          // does the actual generation
          if (frame_num != last_frame_num) {
              last_frame_num = frame_num;
              frame_update_logic();
          }
          mutex_exit(&frame_logic_mutex);
  
          render_scanline(scanline_buffer, core_num);
  
          // Release the rendered buffer into the wild
          scanvideo_end_scanline_generation(scanline_buffer);
      }
  }
  
  struct semaphore video_setup_complete;
  
  void core1_func() {
      sem_acquire_blocking(&video_setup_complete);
      render_loop();
  }
  
  int vga_main(void) {
      mutex_init(&frame_logic_mutex);
      sem_init(&video_setup_complete, 0, 1);
  
      // Core 1 will wait for us to finish video setup, and then start rendering
  #ifdef DUAL_CORE_RENDER
      multicore_launch_core1(core1_func);
  #endif
  
      scanvideo_setup(&VGA_MODE);
      scanvideo_timing_enable(true);
  
      sem_release(&video_setup_complete);
      render_loop();
      return 0;
  }
  
  void frame_update_logic() {
  
  }
  
  #define MIN_COLOR_RUN 3
  
  int32_t single_color_scanline(uint32_t *buf, size_t buf_length, int width, uint32_t color16) {
      assert(buf_length >= 2);
  
      assert(width >= MIN_COLOR_RUN);
      // | jmp color_run | color | count-3 |  buf[0] =
      buf[0] = COMPOSABLE_COLOR_RUN | (color16 << 16);
      buf[1] = (width - MIN_COLOR_RUN) | (COMPOSABLE_RAW_1P << 16);
      // note we must end with a black pixel
      buf[2] = 0 | (COMPOSABLE_EOL_ALIGN << 16);
  
      return 3;
  }
  
  void render_scanline(struct scanvideo_scanline_buffer *dest, int core) {
      uint32_t *buf = dest->data;
      size_t buf_length = dest->data_max;
  
      int l = scanvideo_scanline_number(dest->scanline_id);
      uint16_t bgcolour = (uint16_t) l << 2;
      dest->data_used = single_color_scanline(buf, buf_length, VGA_MODE.width, bgcolour);
      dest->status = SCANLINE_OK;
  }
  
  
  int main(void) {
      // Re init uart now that clk_peri has changed
      setup_default_uart();
  
      return vga_main();
  }
  