From: Keith Packard Date: Thu, 2 Nov 2017 16:15:34 +0000 (-0700) Subject: altos/pong: Add initial pong implementation X-Git-Url: https://git.gag.com/?p=fw%2Faltos;a=commitdiff_plain;h=afedaefd6ae7bbc744458572770f42439f978e6c altos/pong: Add initial pong implementation This runs on the STM32L discover board. Signed-off-by: Keith Packard --- diff --git a/src/pong/.gitignore b/src/pong/.gitignore new file mode 100644 index 00000000..beaf4fdc --- /dev/null +++ b/src/pong/.gitignore @@ -0,0 +1,3 @@ +ao_pong_font.h +*.elf +ao_product.h diff --git a/src/pong/ao_pins.h b/src/pong/ao_pins.h new file mode 100644 index 00000000..ee81ad88 --- /dev/null +++ b/src/pong/ao_pins.h @@ -0,0 +1,131 @@ +/* + * Copyright © 2012 Keith Packard + * + * 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_ */ diff --git a/src/pong/ao_pong.c b/src/pong/ao_pong.c new file mode 100644 index 00000000..174db29e --- /dev/null +++ b/src/pong/ao_pong.c @@ -0,0 +1,392 @@ +/* + * Copyright © 2011 Keith Packard + * + * 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 +#include +#include +#include +#include +#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; +} diff --git a/src/pong/ao_pong_text.c b/src/pong/ao_pong_text.c new file mode 100644 index 00000000..429b5e42 --- /dev/null +++ b/src/pong/ao_pong_text.c @@ -0,0 +1,71 @@ +/* + * Copyright © 2016 Keith Packard + * + * 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; + } +} diff --git a/src/pong/ao_pong_text.h b/src/pong/ao_pong_text.h new file mode 100644 index 00000000..c604f984 --- /dev/null +++ b/src/pong/ao_pong_text.h @@ -0,0 +1,26 @@ +/* + * Copyright © 2017 Keith Packard + * + * 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_ */ diff --git a/src/pong/flash-loader/Makefile b/src/pong/flash-loader/Makefile new file mode 100644 index 00000000..dc5b2058 --- /dev/null +++ b/src/pong/flash-loader/Makefile @@ -0,0 +1,8 @@ +# +# AltOS flash loader build +# +# + +TOPDIR=../.. +HARDWARE=pong +include $(TOPDIR)/stm/Makefile-flash.defs diff --git a/src/pong/flash-loader/ao_pins.h b/src/pong/flash-loader/ao_pins.h new file mode 100644 index 00000000..eb5fcb8b --- /dev/null +++ b/src/pong/flash-loader/ao_pins.h @@ -0,0 +1,36 @@ +/* + * Copyright © 2013 Keith Packard + * + * 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 + +/* 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_ */ diff --git a/src/pong/make_number.5c b/src/pong/make_number.5c new file mode 100644 index 00000000..3b1970cc --- /dev/null +++ b/src/pong/make_number.5c @@ -0,0 +1,104 @@ +#!/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);