altos/pong: Add initial pong implementation
authorKeith Packard <keithp@keithp.com>
Thu, 2 Nov 2017 16:15:34 +0000 (09:15 -0700)
committerKeith Packard <keithp@keithp.com>
Thu, 2 Nov 2017 16:15:34 +0000 (09:15 -0700)
This runs on the STM32L discover board.

Signed-off-by: Keith Packard <keithp@keithp.com>
src/pong/.gitignore [new file with mode: 0644]
src/pong/ao_pins.h [new file with mode: 0644]
src/pong/ao_pong.c [new file with mode: 0644]
src/pong/ao_pong_text.c [new file with mode: 0644]
src/pong/ao_pong_text.h [new file with mode: 0644]
src/pong/flash-loader/Makefile [new file with mode: 0644]
src/pong/flash-loader/ao_pins.h [new file with mode: 0644]
src/pong/make_number.5c [new file with mode: 0644]

diff --git a/src/pong/.gitignore b/src/pong/.gitignore
new file mode 100644 (file)
index 0000000..beaf4fd
--- /dev/null
@@ -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 (file)
index 0000000..ee81ad8
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * 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_ */
diff --git a/src/pong/ao_pong.c b/src/pong/ao_pong.c
new file mode 100644 (file)
index 0000000..174db29
--- /dev/null
@@ -0,0 +1,392 @@
+/*
+ * 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;
+}
diff --git a/src/pong/ao_pong_text.c b/src/pong/ao_pong_text.c
new file mode 100644 (file)
index 0000000..429b5e4
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * 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;
+       }
+}
diff --git a/src/pong/ao_pong_text.h b/src/pong/ao_pong_text.h
new file mode 100644 (file)
index 0000000..c604f98
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * 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_ */
diff --git a/src/pong/flash-loader/Makefile b/src/pong/flash-loader/Makefile
new file mode 100644 (file)
index 0000000..dc5b205
--- /dev/null
@@ -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 (file)
index 0000000..eb5fcb8
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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_ */
diff --git a/src/pong/make_number.5c b/src/pong/make_number.5c
new file mode 100644 (file)
index 0000000..3b1970c
--- /dev/null
@@ -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);