ready for presentation (we've some smaller bugs..)
This commit is contained in:
parent
6ded692102
commit
57ea664f53
11 changed files with 144 additions and 96 deletions
6
.gitignore
vendored
Normal file
6
.gitignore
vendored
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
obj/
|
||||||
|
*.hex
|
||||||
|
*.bin
|
||||||
|
*.elf
|
||||||
|
*.lss
|
||||||
|
*.o
|
4
Makefile
4
Makefile
|
@ -62,7 +62,7 @@ TARGET = main
|
||||||
OBJDIR = obj
|
OBJDIR = obj
|
||||||
|
|
||||||
# List C source files here (C dependencies are automatically generated)
|
# List C source files here (C dependencies are automatically generated)
|
||||||
SRC = $(TARGET).c tetris.c display.c
|
SRC = $(TARGET).c tetris.c display.c conway.c
|
||||||
|
|
||||||
# List C++ source files here (C dependencies are automatically generated)
|
# List C++ source files here (C dependencies are automatically generated)
|
||||||
CPPSRC =
|
CPPSRC =
|
||||||
|
@ -248,7 +248,7 @@ AVRDUDE_WRITE_FLASH = -U flash:w:$(TARGET).hex
|
||||||
|
|
||||||
# Uncomment the following if you do /not/ wish a verification to be
|
# Uncomment the following if you do /not/ wish a verification to be
|
||||||
# performed after programming the device.
|
# performed after programming the device.
|
||||||
#AVRDUDE_NO_VERIFY = -V
|
AVRDUDE_NO_VERIFY = -V
|
||||||
|
|
||||||
# Increase verbosity level
|
# Increase verbosity level
|
||||||
#AVRDUDE_VERBOSE = -v -v
|
#AVRDUDE_VERBOSE = -v -v
|
||||||
|
|
24
conway.c
24
conway.c
|
@ -1,4 +1,9 @@
|
||||||
|
#include <avr/io.h>
|
||||||
|
#include <util/delay.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
|
#include "display.h"
|
||||||
#include "conway.h"
|
#include "conway.h"
|
||||||
|
|
||||||
volatile extern uint8_t *volatile display_buffer; /* Buffer für Display */
|
volatile extern uint8_t *volatile display_buffer; /* Buffer für Display */
|
||||||
|
@ -62,14 +67,8 @@ void conway_start() {
|
||||||
|
|
||||||
uint8_t i = 0;
|
uint8_t i = 0;
|
||||||
|
|
||||||
/* populate world */
|
|
||||||
/* with pattern
|
|
||||||
worlds[i][1] |= 0b00001100;
|
|
||||||
worlds[i][2] |= 0b00011000;
|
|
||||||
worlds[i][3] |= 0b00001000;*/
|
|
||||||
|
|
||||||
/* by random */
|
/* by random */
|
||||||
for (uint8_t q = 0; q < 8; q++) {
|
for (uint8_t q = 0; q < 32; q++) {
|
||||||
uint8_t row = rand() % 16;
|
uint8_t row = rand() % 16;
|
||||||
uint8_t col = rand() % 8;
|
uint8_t col = rand() % 8;
|
||||||
display_set(col, row, 1);
|
display_set(col, row, 1);
|
||||||
|
@ -83,8 +82,17 @@ void conway_start() {
|
||||||
i = 1 - i; // switch world
|
i = 1 - i; // switch world
|
||||||
|
|
||||||
if (~PINB & KEY_Y) {
|
if (~PINB & KEY_Y) {
|
||||||
_delay_ms(10);
|
|
||||||
return; // exit
|
return; // exit
|
||||||
}
|
}
|
||||||
|
if (~PINB & KEY_A) {
|
||||||
|
worlds[i][7] |= 0b00001100;
|
||||||
|
worlds[i][8] |= 0b00011000;
|
||||||
|
worlds[i][9] |= 0b00001000;
|
||||||
|
}
|
||||||
|
if (~PINB & KEY_B) {
|
||||||
|
worlds[i][7] |= 0b00010000;
|
||||||
|
worlds[i][8] |= 0b00001000;
|
||||||
|
worlds[i][9] |= 0b00111000;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
4
conway.h
4
conway.h
|
@ -1,4 +1,6 @@
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
uint8_t conway_cell_neighbours(uint8_t x, uint8_t y, uint8_t *world);
|
uint8_t conway_cell_neighbours(uint8_t x, uint8_t y, uint8_t *world);
|
||||||
uint8_t conway_next_cell_gen(uint8_t x, uint8_t y, uint8_t *world);
|
uint8_t conway_next_cell_gen(uint8_t x, uint8_t y, uint8_t *world);
|
||||||
void conway_next_gen(uint8_t *world, uint8_t *next_gen);
|
void conway_next_gen(uint8_t *world, uint8_t *next_gen);
|
||||||
void conway_start();
|
void conway_start( void );
|
||||||
|
|
91
display.c
91
display.c
|
@ -1,14 +1,30 @@
|
||||||
#include <avr/io.h>
|
#include <avr/io.h>
|
||||||
#include <avr/interrupt.h>
|
#include <avr/interrupt.h>
|
||||||
|
#include <util/delay.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "display.h"
|
#include "display.h"
|
||||||
|
#include "font.h"
|
||||||
#include "tetris.h"
|
#include "tetris.h"
|
||||||
|
|
||||||
volatile uint8_t *volatile display_buffer; /* Buffer für Display */
|
volatile uint8_t *volatile display_buffer; /* Buffer für Display */
|
||||||
|
|
||||||
extern volatile board_t brd;
|
|
||||||
extern volatile stone_t stn;
|
extern volatile stone_t stn;
|
||||||
|
|
||||||
|
void display_set(uint8_t col, uint8_t row, uint8_t val) {
|
||||||
|
if (val) {
|
||||||
|
display_buffer[row] |= (1 << col);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
display_buffer[row] &= ~(1 << col);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_toggle(uint8_t col, uint8_t row) {
|
||||||
|
display_buffer[row] ^= (1 << col);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialisiere Display im Multiplexing Modus
|
* Initialisiere Display im Multiplexing Modus
|
||||||
*/
|
*/
|
||||||
|
@ -35,32 +51,44 @@ uint8_t * display_print(char *text, uint8_t *buffer) {
|
||||||
|
|
||||||
strupr(text); /* Nur Großbuchstaben sind verfügbar */
|
strupr(text); /* Nur Großbuchstaben sind verfügbar */
|
||||||
|
|
||||||
for (uint16_t c = 0; c < len; c++) {
|
for (uint16_t c = len-1; c >= 0; c--) {
|
||||||
char chr = text[len-c-1];
|
char p = text[c];
|
||||||
uint8_t pattern;
|
char q = (p >= ' ' && p <= '_') ? p - ' ' : 0;
|
||||||
|
|
||||||
if (chr >= ' ' && chr <= '_')
|
|
||||||
pattern = chr - ' ';
|
|
||||||
else
|
|
||||||
pattern = 0; /* space */
|
|
||||||
|
|
||||||
for (uint8_t p = 0; p < 3; p++) {
|
mempcpy(buffer[c*4], font[q], 3);
|
||||||
buffer[p+c*4+16] = font[pattern][p];
|
|
||||||
}
|
|
||||||
//buffer[c*4+16] = 0; /* padding */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
void display_laufschrift(uint8_t *buffer, uint16_t bytes, uint8_t speed, uint8_t rounds) {
|
void display_laufschrift(char *text, uint8_t speed, uint8_t rounds) {
|
||||||
display_buffer = buffer;
|
uint16_t len = 4 * strlen(text) + 16; // 4 Bytes pro Character + 2 * 16 Bytes Padding
|
||||||
while(1) {
|
uint8_t *orig_buffer = display_buffer;
|
||||||
if (display_buffer == buffer) {
|
|
||||||
display_buffer = buffer+bytes-16;
|
volatile uint8_t *buffer = malloc(len);
|
||||||
if (rounds-- == 0) {
|
|
||||||
return;
|
memset(buffer, 0, len);
|
||||||
}
|
display_buffer = display_print("test", buffer);
|
||||||
|
|
||||||
|
while ( TRUE ) {
|
||||||
|
buffer[15]++;
|
||||||
|
_delay_ms(500);
|
||||||
|
}
|
||||||
|
|
||||||
|
//display_roll(buffer, len, speed, rounds);
|
||||||
|
|
||||||
|
display_buffer = orig_buffer; /* reset to old buffer */
|
||||||
|
free(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void display_roll(uint16_t bytes, uint8_t speed, uint8_t rounds) {
|
||||||
|
uint8_t *end_buffer = display_buffer;
|
||||||
|
display_buffer += bytes - 16;
|
||||||
|
|
||||||
|
while (rounds) {
|
||||||
|
if (display_buffer == end_buffer) {
|
||||||
|
display_buffer = end_buffer + bytes - 16;
|
||||||
|
rounds--;
|
||||||
}
|
}
|
||||||
|
|
||||||
display_buffer--;
|
display_buffer--;
|
||||||
|
@ -73,32 +101,27 @@ void display_laufschrift(uint8_t *buffer, uint16_t bytes, uint8_t speed, uint8_t
|
||||||
*/
|
*/
|
||||||
ISR(TIMER0_COMP_vect) {
|
ISR(TIMER0_COMP_vect) {
|
||||||
static uint8_t column;
|
static uint8_t column;
|
||||||
static uint8_t counter;
|
|
||||||
|
|
||||||
uint8_t row_mask = (1 << column);
|
uint8_t row_mask = (1 << column);
|
||||||
uint16_t column_mask = 0;
|
uint16_t column_mask = 0;
|
||||||
|
|
||||||
for (uint8_t i = 4; i < NUM_LINES; i++) {
|
for (uint8_t i = 0; i < 16; i++) {
|
||||||
if (row_mask & brd[i]) { /* fixed pixels, dimmed */
|
if (row_mask & display_buffer[i]) { /* fixed pixels, dimmed */
|
||||||
column_mask |= (1 << (i-4));
|
column_mask |= (1 << i);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tetris) { /* in tetris mode ? */
|
if (i+4 >= stn.pos_y && i < stn.pos_y) { /* in clipping of falling stone ? */
|
||||||
if (i >= tetris->stn.pos_y && i < tetris->stn.pos_y+4) { /* in clipping of falling stone ? */
|
if (row_mask & stn.clipping[i+4-stn.pos_y]) {
|
||||||
if (row_mask & tetris->stn.clipping[i-stn.pos_y]) {
|
column_mask |= (1 << i);
|
||||||
column_mask |= (1 << (i-4));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PORTD = 0;
|
||||||
PORTC = (uint8_t) column_mask;
|
PORTC = (uint8_t) column_mask;
|
||||||
PORTA = (uint8_t) (column_mask >> 8);
|
PORTA = (uint8_t) (column_mask >> 8);
|
||||||
PORTD = row_mask;
|
PORTD = row_mask;
|
||||||
|
|
||||||
column++;
|
column++;
|
||||||
if (column == 8) {
|
column %= 8;
|
||||||
column = 0;
|
|
||||||
counter++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
#ifndef _DISPLAY_H_
|
#ifndef _DISPLAY_H_
|
||||||
#define _DISPLAY_H_
|
#define _DISPLAY_H_
|
||||||
|
|
||||||
|
void display_set(uint8_t col, uint8_t row, uint8_t val);
|
||||||
|
void display_toggle(uint8_t col, uint8_t row);
|
||||||
|
|
||||||
void display_init( void );
|
void display_init( void );
|
||||||
void display_laufschrift(uint8_t *buffer, uint16_t bytes, uint8_t speed, uint8_t rounds);
|
void display_laufschrift(char * text, uint8_t speed, uint8_t rounds);
|
||||||
|
void display_roll(uint16_t bytes, uint8_t speed, uint8_t rounds);
|
||||||
uint8_t * display_print(char *text, uint8_t *buffer);
|
uint8_t * display_print(char *text, uint8_t *buffer);
|
||||||
|
|
||||||
#endif /* _DISPLAY_H_ */
|
#endif /* _DISPLAY_H_ */
|
||||||
|
|
2
font.h
2
font.h
|
@ -301,7 +301,7 @@ const uint8_t font[][3] = {
|
||||||
0b01111111,
|
0b01111111,
|
||||||
0b00000000
|
0b00000000
|
||||||
},
|
},
|
||||||
{ // \
|
{
|
||||||
0b00000011,
|
0b00000011,
|
||||||
0b00011100,
|
0b00011100,
|
||||||
0b01100000
|
0b01100000
|
||||||
|
|
44
main.c
44
main.c
|
@ -1,11 +1,16 @@
|
||||||
#include <avr/io.h>
|
#include <avr/io.h>
|
||||||
|
#include <util/delay.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "main.h"
|
#include "main.h"
|
||||||
#include "display.h"
|
#include "display.h"
|
||||||
#include "tetris.h"
|
#include "tetris.h"
|
||||||
|
#include "conway.h"
|
||||||
|
|
||||||
static uint8_t rwth_logo[] = {0x7c, 0x50, 0x2c, 0x00, 0x78, 0x04, 0x18, 0x04, 0x78, 0x40, 0x7c, 0x40, 0x7c, 0x10, 0x7c, 0x00};
|
volatile extern uint8_t *volatile display_buffer; /* Buffer für Display */
|
||||||
|
|
||||||
|
static uint8_t rwth_logo[] = {0x00, 0x7c, 0x10, 0x7c, 0x40, 0x7c, 0x40, 0x78, 0x04, 0x18, 0x04, 0x78, 0x00, 0x2c, 0x50, 0x7c};
|
||||||
|
static char bill_txt[] = "'Nobody will ever need more than 640k RAM!' - Bill Gates, 1981 ;-)";
|
||||||
|
|
||||||
uint8_t get_seed() {
|
uint8_t get_seed() {
|
||||||
uint8_t seed = 0;
|
uint8_t seed = 0;
|
||||||
|
@ -18,6 +23,22 @@ uint8_t get_seed() {
|
||||||
return seed;
|
return seed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void random_start() {
|
||||||
|
volatile uint8_t random_buffer[16];
|
||||||
|
display_buffer = memset(random_buffer, 0, 16);
|
||||||
|
|
||||||
|
while ( TRUE ) {
|
||||||
|
uint8_t row = rand() % 16;
|
||||||
|
uint8_t col = rand() % 8;
|
||||||
|
display_toggle(col, row);
|
||||||
|
|
||||||
|
if (~PINB & KEY_Y) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
_delay_ms(20);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int main( void ) {
|
int main( void ) {
|
||||||
display_init();
|
display_init();
|
||||||
srand(get_seed());
|
srand(get_seed());
|
||||||
|
@ -33,28 +54,17 @@ int main( void ) {
|
||||||
|
|
||||||
/* Demo 1: Tetris */
|
/* Demo 1: Tetris */
|
||||||
tetris_start();
|
tetris_start();
|
||||||
|
_delay_ms(300);
|
||||||
|
|
||||||
/* Demo 4: Conways Game of Life */
|
/* Demo 4: Conways Game of Life */
|
||||||
conway_start();
|
conway_start();
|
||||||
|
_delay_ms(300);
|
||||||
|
|
||||||
/* Demo 2: Laufschrift */
|
/* Demo 2: Laufschrift */
|
||||||
/*char text[] = "'Nobody will ever need more than 640k RAM!' - Bill Gates, 1981 ;-)";
|
/// display_laufschrift("test", 120, 1);
|
||||||
uint16_t len = 4*strlen(text)+32; // 4 Bytes pro Character + 2 * 16 Bytes Padding
|
|
||||||
volatile uint8_t text_buffer[len];
|
|
||||||
display_print(text, text_buffer);
|
|
||||||
|
|
||||||
// Starte Laufschrift
|
|
||||||
display_laufschrift(text_buffer, len, 120, 1);*/
|
|
||||||
|
|
||||||
/* Demo 3: Zufall */
|
/* Demo 3: Zufall */
|
||||||
/*volatile uint8_t random_buffer[16];
|
random_start();
|
||||||
display_buffer = memset(random_buffer, 0, 16);
|
_delay_ms(300);
|
||||||
srand(get_seed());
|
|
||||||
|
|
||||||
for (uint16_t i = 0; i < 450; i++) {
|
|
||||||
display_toggle(rand()%8, rand()%16);
|
|
||||||
display_buffer[0] = i;
|
|
||||||
_delay_ms(20);
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
BIN
tetris
BIN
tetris
Binary file not shown.
54
tetris.c
54
tetris.c
|
@ -4,9 +4,12 @@
|
||||||
|
|
||||||
#include "tetris.h"
|
#include "tetris.h"
|
||||||
|
|
||||||
volatile tetris_t *volatile tetris = NULL;
|
extern volatile uint8_t *volatile display_buffer; /* Buffer für Display */
|
||||||
|
|
||||||
//static int16_t scoring[] = {0, 40, 100, 300, 1200}; /* scoring multiplicator for 0-4 flushed lines */
|
volatile uint8_t *volatile brd = NULL; /* NULL if not in tetris mode */
|
||||||
|
volatile stone_t stn;
|
||||||
|
|
||||||
|
static int16_t scoring[] = {0, 40, 100, 300, 1200}; /* scoring multiplicator for 0-4 flushed lines */
|
||||||
|
|
||||||
static clipping_t shapes[][4] = { /* including 4 ccw rotations */
|
static clipping_t shapes[][4] = { /* including 4 ccw rotations */
|
||||||
{ // SHAPE_I
|
{ // SHAPE_I
|
||||||
|
@ -101,7 +104,7 @@ bool_t tetris_turn_stone() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
copy_clipping(tmp, stn.clipping);
|
tetris_copy_clipping(tmp, stn.clipping);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
@ -168,24 +171,19 @@ bool_t tetris_detect_collision(direction_t dir) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void tetris_start() {
|
void tetris_start() {
|
||||||
tetris_t state;
|
uint8_t debounce = 0;
|
||||||
|
uint8_t lines = 0;
|
||||||
uint8_t frames = 48;
|
uint8_t frames = 48;
|
||||||
|
uint8_t score = 0;
|
||||||
uint8_t level = 0;
|
uint8_t level = 0;
|
||||||
//uint16_t score = 0;
|
|
||||||
//uint16_t stones = 0;
|
|
||||||
uint16_t lines = 0;
|
|
||||||
uint8_t debounce;
|
|
||||||
|
|
||||||
tetris = &state;
|
brd = malloc(sizeof(board_t));
|
||||||
display_buffer = state.brd+4; /* skipping "virtual" lines */
|
display_buffer = brd+4; /* skipping first 4 "virtual" lines */
|
||||||
|
|
||||||
/* starting with empty board */
|
/* starting with empty board */
|
||||||
for (uint8_t i = 0; i < NUM_LINES; i++) {
|
memset(brd, 0, NUM_LINES);
|
||||||
brd[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (TRUE) { /* main loop */
|
while ( TRUE ) { /* main loop */
|
||||||
/* add new stone on the top of our board */
|
/* add new stone on the top of our board */
|
||||||
uint8_t shape = rand() % NUM_SHAPES;
|
uint8_t shape = rand() % NUM_SHAPES;
|
||||||
uint8_t orientation = rand() % 4;
|
uint8_t orientation = rand() % 4;
|
||||||
|
@ -195,7 +193,7 @@ void tetris_start() {
|
||||||
stn.pos_x = 0;
|
stn.pos_x = 0;
|
||||||
stn.pos_y = 0;
|
stn.pos_y = 0;
|
||||||
|
|
||||||
copy_clipping(shapes[shape][orientation], stn.clipping);
|
tetris_copy_clipping(shapes[shape][orientation], stn.clipping);
|
||||||
|
|
||||||
if (lines > (level + 1) * 10) {
|
if (lines > (level + 1) * 10) {
|
||||||
level++; /* next level */
|
level++; /* next level */
|
||||||
|
@ -233,16 +231,17 @@ void tetris_start() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (keys & KEY_Y) {
|
if (keys & KEY_Y) {
|
||||||
_delay_ms(10);
|
free(brd);
|
||||||
|
memset(stn.clipping, 0, 4);
|
||||||
return; // exit tetris
|
return; // exit tetris
|
||||||
}
|
}
|
||||||
|
|
||||||
if (keys) {
|
if (keys) {
|
||||||
debounce = 6
|
debounce = 9;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_delay_ms(900 / FRAMES); // sleep for 1 frame (10% for calculations)
|
_delay_ms(900 / FRAMES); // sleep for 1 frame (100ms approx for calculations)
|
||||||
}
|
}
|
||||||
} while (tetris_detect_collision(DIR_DOWN) == FALSE);
|
} while (tetris_detect_collision(DIR_DOWN) == FALSE);
|
||||||
|
|
||||||
|
@ -251,17 +250,18 @@ void tetris_start() {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* check for completed lines and calculate score */
|
/* check for completed lines and calculate score */
|
||||||
uint8_t flushed = flush_lines();
|
uint8_t flushed = tetris_flush_lines();
|
||||||
//score += (level + 1) * scoring[flushed];
|
score += (level + 1) * scoring[flushed];
|
||||||
//stones++;
|
|
||||||
lines += flushed;
|
lines += flushed;
|
||||||
|
|
||||||
if (brd[3] > 0) {
|
if (brd[3] > 0) {
|
||||||
//return; /* game over */
|
free(brd);
|
||||||
for (uint8_t i = 0; i < NUM_LINES; i++) { /* restart with empty board */
|
memset(stn.clipping, 0, 4);
|
||||||
brd[i] = 0; /* starting with empty board */
|
return; /* game over */
|
||||||
}
|
|
||||||
_delay_ms(1000);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(brd);
|
||||||
|
brd = NULL;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
5
tetris.h
5
tetris.h
|
@ -12,11 +12,6 @@
|
||||||
typedef uint8_t board_t[NUM_LINES];
|
typedef uint8_t board_t[NUM_LINES];
|
||||||
typedef uint8_t clipping_t[4];
|
typedef uint8_t clipping_t[4];
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
stone_t stn;
|
|
||||||
board_t brd;
|
|
||||||
} tetris_t;
|
|
||||||
|
|
||||||
/* Named according to: http://de.wikipedia.org/wiki/Tetris */
|
/* Named according to: http://de.wikipedia.org/wiki/Tetris */
|
||||||
typedef enum {
|
typedef enum {
|
||||||
SHAPE_I, /* line of four dots */
|
SHAPE_I, /* line of four dots */
|
||||||
|
|
Loading…
Add table
Reference in a new issue