This runs on the STM32L discover board.
Signed-off-by: Keith Packard <keithp@keithp.com>
--- /dev/null
+ao_pong_font.h
+*.elf
+ao_product.h
--- /dev/null
+/*
+ * Copyright © 2012 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+/* Bridge SB17 on the board and use the MCO from the other chip */
+#define AO_HSE 8000000
+#define AO_HSE_BYPASS 1
+
+/* PLLVCO = 96MHz (so that USB will work) */
+#define AO_PLLMUL 12
+#define AO_RCC_CFGR_PLLMUL (STM_RCC_CFGR_PLLMUL_12)
+
+/* SYSCLK = 24MHz */
+#define AO_PLLDIV 4
+#define AO_RCC_CFGR_PLLDIV (STM_RCC_CFGR_PLLDIV_4)
+
+/* HCLK = 24MHZ (CPU clock) */
+#define AO_AHB_PRESCALER 1
+#define AO_RCC_CFGR_HPRE_DIV STM_RCC_CFGR_HPRE_DIV_1
+
+/* Run APB1 at HCLK/1 */
+#define AO_APB1_PRESCALER 1
+#define AO_RCC_CFGR_PPRE1_DIV STM_RCC_CFGR_PPRE2_DIV_1
+
+/* Run APB2 at HCLK/1 */
+#define AO_APB2_PRESCALER 1
+#define AO_RCC_CFGR_PPRE2_DIV STM_RCC_CFGR_PPRE2_DIV_1
+
+#define HAS_SERIAL_1 0
+#define USE_SERIAL_1_STDIN 0
+#define SERIAL_1_PB6_PB7 1
+#define SERIAL_1_PA9_PA10 0
+
+#define HAS_SERIAL_2 0
+#define USE_SERIAL_2_STDIN 0
+#define SERIAL_2_PA2_PA3 0
+#define SERIAL_2_PD5_PD6 1
+
+#define HAS_SERIAL_3 0
+#define USE_SERIAL_3_STDIN 1
+#define SERIAL_3_PB10_PB11 0
+#define SERIAL_3_PC10_PC11 0
+#define SERIAL_3_PD8_PD9 1
+
+#define HAS_SPI_1 1
+#define SPI_1_PB3_PB4_PB5 1
+#define SPI_1_OSPEEDR STM_OSPEEDR_10MHz
+
+#define HAS_SPI_2 0
+
+#define HAS_USB 1
+#define HAS_BEEP 0
+#define PACKET_HAS_SLAVE 0
+
+#define AO_BOOT_CHAIN 1
+
+#define LOW_LEVEL_DEBUG 0
+
+#define LED_PORT_ENABLE STM_RCC_AHBENR_GPIOBEN
+#define LED_PORT (&stm_gpiob)
+#define LED_PIN_GREEN 7
+#define LED_PIN_BLUE 6
+#define AO_LED_GREEN (1 << LED_PIN_GREEN)
+#define AO_LED_BLUE (1 << LED_PIN_BLUE)
+#define AO_LED_PANIC AO_LED_BLUE
+
+#define LEDS_AVAILABLE (AO_LED_BLUE | AO_LED_GREEN)
+
+#define AO_NONMASK_INTERRUPT 1
+
+#define STM_DMA1_3_STOLEN 1
+
+#define HAS_ADC 1
+#define HAS_ADC_TEMP 0
+
+#define AO_ADC_RING 32
+
+struct ao_adc {
+ uint16_t player[2];
+};
+
+#define AO_ADC_DUMP(p) \
+ printf("tick: %5u player_1: %5d player_2: %5d\n", \
+ (p)->tick, \
+ (p)->adc.player[0], (p)->adc.player[1])
+
+#define AO_ADC_PLAYER_1 2
+#define AO_ADC_PIN0_PORT (&stm_gpioa)
+#define AO_ADC_PIN0_PIN 2
+
+#define AO_ADC_PLAYER_2 3
+#define AO_ADC_PIN1_PORT (&stm_gpioa)
+#define AO_ADC_PIN1_PIN 3
+
+#define AO_ADC_RCC_AHBENR ((1 << STM_RCC_AHBENR_GPIOAEN))
+
+#define AO_DATA_RING 32
+#define AO_NUM_ADC 2
+
+#define AO_ADC_SQ1 AO_ADC_PLAYER_1
+#define AO_ADC_SQ2 AO_ADC_PLAYER_2
+
+#define AO_EVENT 1
+
+#define AO_BUTTON_COUNT 1
+#define AO_BUTTON_MODE AO_EXTI_MODE_PULL_NONE
+
+#define AO_BUTTON_0_PORT &stm_gpioa
+#define AO_BUTTON_0 0
+
+#define AO_TICK_TYPE uint32_t
+#define AO_TICK_SIGNED int32_t
+
+#endif /* _AO_PINS_H_ */
--- /dev/null
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include <ao_exti.h>
+#include <ao_event.h>
+#include <ao_button.h>
+#include <ao_boot.h>
+#include <ao_vga.h>
+#include "ao_pong_text.h"
+
+// uint8_t ao_sensor_errors;
+
+#define BALL_SPEED 3
+
+#define WIN_SCORE 11
+
+#define BALL_WIDTH 5
+#define BALL_HEIGHT 5
+
+#define PADDLE_WIDTH 5
+#define PADDLE_HEIGHT 25
+#define PADDLE_OFFSET 20
+
+struct point {
+ int x, y;
+};
+
+struct rect {
+ int x, y, w, h;
+};
+
+static struct ao_data ao_data;
+
+static int player_value[2];
+static int player_score[2];
+
+#define player_filter(old, new) (((old) * 7 + (new)) >> 3)
+
+static struct rect player[2];
+
+static struct rect ball;
+
+static int ball_e;
+static int ball_dx, ball_dy;
+static int ball_step_x, ball_step_y;
+
+static int
+intersect(struct rect *a, struct rect *b)
+{
+ return (a->x <= b->x + b->w && b->x <= a->x + a->w &&
+ a->y <= b->y + b->h && b->y <= a->y + a->h);
+}
+
+static int
+ao_ball_step(void)
+{
+ int p;
+
+ /* Move the ball */
+ ball_e += ball_dy;
+ ball.x += ball_step_x;
+ while (ball_e >= ball_dx) {
+ ball_e -= ball_dx;
+ ball.y += ball_step_y;
+ }
+
+ /* player missed */
+ if (ball.x <= 0)
+ return 1;
+
+ if (ball.x >= AO_VGA_WIDTH - BALL_WIDTH)
+ return -1;
+
+ /* bounce off walls */
+ if (ball.y < 0 || ball.y + ball.h > AO_VGA_HEIGHT) {
+ ball_step_y = -ball_step_y;
+ ball.y += ball_step_y;
+ }
+
+ /* bounce off paddles */
+
+ for (p = 0; p < 2; p++) {
+ if (intersect(&ball, &player[p])) {
+ int dy = 2 * (ball.y - player[p].y) + ball.h - player[p].h;
+ int dx = 20;
+
+ ball_step_x = -ball_step_x;
+ ball.x += ball_step_x;
+ ball_step_y = 1;
+ if (dy < 0) {
+ ball_step_y = -1;
+ dy = -dy;
+ }
+ ball_e = 0;
+ ball_dx = dx;
+ ball_dy = dy;
+ }
+ }
+
+ return 0;
+}
+
+#define AO_ADC_MAX 4095
+
+static void
+ao_paddle_set(int p, int value)
+{
+ int pos = value * (AO_VGA_HEIGHT - PADDLE_HEIGHT) / AO_ADC_MAX;
+
+ player[p].y = pos;
+}
+
+enum pong_state {
+ pong_start,
+ pong_serve,
+ pong_volley,
+ pong_endgame,
+};
+
+static enum pong_state pong_state;
+static int pong_timer;
+static int pong_server;
+
+#define PONG_SERVE_WAIT 90
+
+static enum pong_state
+ao_pong_start(void)
+{
+ int p;
+
+ pong_timer = 1;
+
+ pong_server = ao_time() & 1;
+
+ for (p = 0; p < 2; p++)
+ player_score[p] = 0;
+
+ return pong_serve;
+}
+
+static enum pong_state
+ao_pong_serve(void)
+{
+ int seed;
+ if (--pong_timer > 0)
+ return pong_serve;
+
+ seed = ao_time();
+
+ ball.y = (AO_VGA_HEIGHT - BALL_HEIGHT) / 2;
+
+ if (pong_server) {
+ ball.x = player[1].x - BALL_WIDTH;
+ ball_step_x = -BALL_SPEED;
+ } else {
+ ball.x = player[0].x + PADDLE_WIDTH;
+ ball_step_x = BALL_SPEED;
+ }
+
+ ball.w = BALL_WIDTH;
+ ball.h = BALL_HEIGHT;
+
+ ball_dx = 100;
+ ball_dy = (seed & 7) * 10;
+ ball_e = 0;
+
+ seed >>= 3;
+
+ if (seed & 1) {
+ ball_step_y = BALL_SPEED;
+ } else {
+ ball_step_y = -BALL_SPEED;
+ }
+
+ return pong_volley;
+}
+
+static enum pong_state
+ao_pong_volley(void)
+{
+ int miss = ao_ball_step();
+ int point;
+
+ if (miss == 0)
+ return pong_volley;
+
+ point = (miss + 1) >> 1;
+ player_score[point]++;
+ if (player_score[point] == WIN_SCORE)
+ return pong_endgame;
+
+ pong_server = point;
+ pong_timer = PONG_SERVE_WAIT;
+ return pong_serve;
+}
+
+static void
+ao_pong_step(void)
+{
+ int p;
+
+ /* Paddles are always active */
+ for (p = 0; p < 2; p++) {
+ player_value[p] = player_filter(player_value[p], ao_data.adc.player[p]);
+ ao_paddle_set(p, player_value[p]);
+ }
+ switch (pong_state) {
+ case pong_start:
+ pong_state = ao_pong_start();
+ break;
+ case pong_serve:
+ pong_state = ao_pong_serve();
+ break;
+ case pong_volley:
+ pong_state = ao_pong_volley();
+ break;
+ case pong_endgame:
+ break;
+ }
+}
+
+static void
+ao_pong_rect(struct rect *r, uint32_t fill)
+{
+ ao_rect(&ao_vga_bitmap,
+ r->x, r->y, r->w, r->h, fill, AO_COPY);
+}
+
+static void
+ao_pong_score(int p, int value, uint32_t fill)
+{
+ int x = AO_VGA_WIDTH / 2 + (p * 2 - 1) * 50 - 24;
+ int a, b;
+ char c[4];
+
+ if (fill != 0) {
+ a = value % 10;
+ b = value / 10;
+ if (b)
+ c[0] = b + '0';
+ else
+ c[0] = ' ';
+ c[1] = a + '0';
+ c[2] = '\0';
+ ao_pong_text(&ao_vga_bitmap, x, 30, c);
+ }
+}
+
+#define NET_WIDTH 2
+#define NET_HEIGHT 8
+
+static void
+ao_pong_net(uint32_t fill)
+{
+ int n;
+
+ if (fill == 0)
+ return;
+
+ for (n = NET_HEIGHT/2; n < AO_VGA_HEIGHT - NET_HEIGHT; n += NET_HEIGHT * 2) {
+ ao_rect(&ao_vga_bitmap,
+ (AO_VGA_WIDTH - NET_WIDTH) >> 1,
+ n,
+ NET_WIDTH,
+ NET_HEIGHT,
+ 1, AO_COPY);
+ }
+}
+
+static void
+ao_pong_draw(uint32_t fill)
+{
+ int p;
+
+ for (p = 0; p < 2; p++) {
+ ao_pong_rect(&player[p], fill);
+ ao_pong_score(p, player_score[p], fill);
+ }
+ if (fill == 0 || pong_state == pong_volley)
+ ao_pong_rect(&ball, fill);
+ ao_pong_net(fill);
+}
+
+static void
+ao_pong_redraw(void)
+{
+ ao_pong_draw(0);
+ ao_pong_step();
+ ao_pong_draw(1);
+}
+
+static void
+ao_pong_setup(void)
+{
+ int p;
+
+ ao_rect(&ao_vga_bitmap,
+ 0, 0, AO_VGA_WIDTH, AO_VGA_HEIGHT,
+ 0, AO_COPY);
+
+ ball.w = BALL_WIDTH;
+ ball.h = BALL_HEIGHT;
+
+ for (p = 0; p < 2; p++) {
+ if (p)
+ player[p].x = AO_VGA_WIDTH - PADDLE_OFFSET - PADDLE_WIDTH;
+ else
+ player[p].x = PADDLE_OFFSET;
+ player[p].y = (AO_VGA_HEIGHT - PADDLE_HEIGHT) / 2;
+ player[p].w = PADDLE_WIDTH;
+ player[p].h = PADDLE_HEIGHT;
+ }
+
+ pong_state = pong_endgame;
+}
+
+static struct ao_task pong_task;
+
+static void
+ao_pong(void)
+{
+ ao_pong_setup();
+ for (;;) {
+ ao_vga_vblank = 0;
+ while (ao_vga_vblank == 0)
+ ao_sleep(&ao_vga_vblank);
+ ao_data = ao_data_ring[ao_data_ring_prev(ao_data_head)];
+ ao_pong_redraw();
+ }
+}
+
+static struct ao_task event_task;
+
+static void
+ao_event(void) {
+ struct ao_event event;
+
+ for (;;) {
+ ao_event_get(&event);
+ if (event.value == 0)
+ pong_state = pong_start;
+ }
+}
+
+int
+main(void)
+{
+ ao_clock_init();
+
+ ao_task_init();
+
+ ao_led_init(LEDS_AVAILABLE);
+ ao_led_on(AO_LED_GREEN);
+ ao_led_off(AO_LED_BLUE);
+ ao_timer_init();
+ ao_dma_init();
+ ao_cmd_init();
+ ao_spi_init();
+ ao_exti_init();
+ ao_button_init();
+ ao_vga_init();
+
+ ao_timer_set_adc_interval(1);
+
+ ao_adc_init();
+ ao_usb_init();
+
+ ao_add_task(&pong_task, ao_pong, "pong");
+
+ ao_add_task(&event_task, ao_event, "event");
+
+ ao_vga_enable(1);
+
+ ao_start_scheduler();
+ return 0;
+}
--- /dev/null
+/*
+ * Copyright © 2016 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include "ao.h"
+#include "ao_draw.h"
+#include "ao_draw_int.h"
+#include "ao_pong_text.h"
+
+static const uint16_t numbers[] = {
+#include "ao_pong_font.h"
+};
+
+#define GLYPH_WIDTH 16
+#define GLYPH_SPACING 20
+#define GLYPH_HEIGHT 24
+#define GLYPH_ASCENT 24
+
+const struct ao_font ao_pong_font = {
+ .width = GLYPH_SPACING,
+ .height = GLYPH_HEIGHT,
+ .ascent = GLYPH_ASCENT,
+ .descent = GLYPH_HEIGHT - GLYPH_ASCENT,
+};
+
+void
+ao_pong_text(const struct ao_bitmap *dst,
+ int16_t x,
+ int16_t y,
+ char *string)
+{
+ static uint32_t src[GLYPH_HEIGHT];
+ char c;
+ int h;
+
+ struct ao_bitmap src_bitmap = {
+ .base = src,
+ .stride = 1,
+ .width = GLYPH_WIDTH,
+ .height = GLYPH_HEIGHT
+ };
+
+ y -= GLYPH_ASCENT;
+
+ while ((c = *string++)) {
+ if (c == ' ') {
+ ao_rect(dst, x, y, GLYPH_WIDTH, GLYPH_HEIGHT, 0, AO_COPY);
+ } else {
+ const uint16_t *n = &numbers[(c - '0') * GLYPH_HEIGHT];
+
+ for (h = 0; h < GLYPH_HEIGHT; h++)
+ src[h] = n[h];
+
+ ao_copy(dst,
+ x, y, GLYPH_WIDTH, GLYPH_HEIGHT,
+ &src_bitmap,
+ 0, 0, AO_COPY);
+ }
+ x += GLYPH_SPACING;
+ }
+}
--- /dev/null
+/*
+ * Copyright © 2017 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#ifndef _AO_PONG_TEXT_
+#define _AO_PONG_TEXT_
+
+void
+ao_pong_text(const struct ao_bitmap *dst,
+ int16_t x,
+ int16_t y,
+ char *string);
+
+extern const struct ao_font ao_pong_font;
+
+#endif /* _AO_PONG_TEXT_H_ */
--- /dev/null
+#
+# AltOS flash loader build
+#
+#
+
+TOPDIR=../..
+HARDWARE=pong
+include $(TOPDIR)/stm/Makefile-flash.defs
--- /dev/null
+/*
+ * Copyright © 2013 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#ifndef _AO_PINS_H_
+#define _AO_PINS_H_
+
+/* Bridge SB17 on the board and use the MCO from the other chip */
+#define AO_HSE 8000000
+#define AO_HSE_BYPASS 1
+
+#include <ao_flash_stm_pins.h>
+
+/* Use the 'user switch' to force boot loader on power on */
+
+#define AO_BOOT_PIN 1
+#define AO_BOOT_APPLICATION_GPIO stm_gpioa
+#define AO_BOOT_APPLICATION_PIN 0
+#define AO_BOOT_APPLICATION_VALUE 0
+#define AO_BOOT_APPLICATION_MODE 0
+
+#endif /* _AO_PINS_H_ */
--- /dev/null
+#!/usr/bin/nickle
+
+int[10,6,4] numbers = {
+ {
+{ 1, 1, 1, 1, },
+{ 1, 0, 0, 1, },
+{ 1, 0, 0, 1, },
+{ 1, 0, 0, 1, },
+{ 1, 0, 0, 1, },
+{ 1, 1, 1, 1, },
+ },
+ {
+{ 0, 0, 0, 1, },
+{ 0, 0, 0, 1, },
+{ 0, 0, 0, 1, },
+{ 0, 0, 0, 1, },
+{ 0, 0, 0, 1, },
+{ 0, 0, 0, 1, },
+ },
+ {
+{ 1, 1, 1, 1, },
+{ 0, 0, 0, 1, },
+{ 1, 1, 1, 1, },
+{ 1, 0, 0, 0, },
+{ 1, 0, 0, 0, },
+{ 1, 1, 1, 1, },
+ },
+ {
+{ 1, 1, 1, 1, },
+{ 0, 0, 0, 1, },
+{ 1, 1, 1, 1, },
+{ 0, 0, 0, 1, },
+{ 0, 0, 0, 1, },
+{ 1, 1, 1, 1, },
+ },
+ {
+{ 1, 0, 0, 1, },
+{ 1, 0, 0, 1, },
+{ 1, 1, 1, 1, },
+{ 0, 0, 0, 1, },
+{ 0, 0, 0, 1, },
+{ 0, 0, 0, 1, },
+ },
+ {
+{ 1, 1, 1, 1, },
+{ 1, 0, 0, 0, },
+{ 1, 1, 1, 1, },
+{ 0, 0, 0, 1, },
+{ 0, 0, 0, 1, },
+{ 1, 1, 1, 1, },
+ },
+ {
+{ 1, 0, 0, 0, },
+{ 1, 0, 0, 0, },
+{ 1, 1, 1, 1, },
+{ 1, 0, 0, 1, },
+{ 1, 0, 0, 1, },
+{ 1, 1, 1, 1, },
+ },
+ {
+{ 1, 1, 1, 1, },
+{ 0, 0, 0, 1, },
+{ 0, 0, 0, 1, },
+{ 0, 0, 0, 1, },
+{ 0, 0, 0, 1, },
+{ 0, 0, 0, 1, },
+ },
+ {
+{ 1, 1, 1, 1, },
+{ 1, 0, 0, 1, },
+{ 1, 1, 1, 1, },
+{ 1, 0, 0, 1, },
+{ 1, 0, 0, 1, },
+{ 1, 1, 1, 1, },
+ },
+ {
+{ 1, 1, 1, 1, },
+{ 1, 0, 0, 1, },
+{ 1, 1, 1, 1, },
+{ 0, 0, 0, 1, },
+{ 0, 0, 0, 1, },
+{ 0, 0, 0, 1, },
+ },
+ };
+
+void
+make_number(int i) {
+
+ printf("/* %d */\n", i);
+ for (int row = 0; row < 6; row++) {
+ for (int copy = 0; copy < 4; copy++) {
+ int val = 0;
+ for (int col = 0; col < 4; col ++) {
+ if(numbers[i,row,col] == 1)
+ val |= (0x0f << (col * 4));
+ }
+ printf("0x%04x,\n", val);
+ }
+ }
+ printf("\n");
+}
+
+for (int i = 0; i < 10; i++)
+ make_number(i);