scrolling and save/load

This commit is contained in:
2026-08-28 15:44:42 +02:00
parent 76e820926c
commit d8f7ebeaad
9 changed files with 178 additions and 39 deletions

32
.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,32 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceRoot}/tracker/build/tracker",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/tracker",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
},
{
"description": "Set Disassembly Flavor to Intel",
"text": "-gdb-set disassembly-flavor intel",
"ignoreFailures": true
}
]
}
]
}

View File

@@ -9,9 +9,10 @@ OBJFILES := $(SRCFILES:%.c=$(BUILDDIR)/%.c.o)
DEPFILES := $(SRCFILES:%.c=$(BUILDDIR)/%.c.d) DEPFILES := $(SRCFILES:%.c=$(BUILDDIR)/%.c.d)
CC := gcc CC := gcc
CFLAGS := -Wall -Wextra CFLAGS := -Wall -Wextra -g
CPPFLAGS := -I src $(shell pkg-config --cflags sdl2) CPPFLAGS := -I src $(shell pkg-config --cflags sdl2)
LDLIBS := $(shell pkg-config --libs sdl2) LDLIBS := $(shell pkg-config --libs sdl2)
LDFLAGS := -g
.PHONY: $(TARGET) all clean .PHONY: $(TARGET) all clean
all: $(TARGET) all: $(TARGET)

BIN
tracker/disk.bin Normal file

Binary file not shown.

View File

@@ -94,6 +94,18 @@ void serial_write(char c){
write(STDOUT_FILENO, &c, 1); write(STDOUT_FILENO, &c, 1);
} }
void disk_write(void * data, size_t len){
FILE * f = fopen("disk.bin", "wb");
fwrite(data, 1, len, f);
fclose(f);
}
void disk_read(void * data, size_t len){
FILE * f = fopen("disk.bin", "rb");
fread(data, 1, len, f);
fclose(f);
}
static void audio_callback(void *userdata, Uint8 *stream, int len){ static void audio_callback(void *userdata, Uint8 *stream, int len){
AudioState *state = userdata; AudioState *state = userdata;
int16_t *output = (int16_t *)stream; int16_t *output = (int16_t *)stream;

View File

@@ -2,4 +2,6 @@
#include <stdint.h> #include <stdint.h>
void psg_write(uint8_t value); void psg_write(uint8_t value);
void serial_write(char c); void serial_write(char c);
void disk_write(void * data, size_t len);
void disk_read(void * data, size_t len);

View File

@@ -54,13 +54,11 @@ static void flush_psg(){
static void process_row(){ static void process_row(){
row_t * current = &pattern->rows[row]; row_t * current = &pattern->rows[row];
for(uint8_t chan=0; chan<3; chan++){ for(uint8_t chan=0; chan<3; chan++){
if(current->channel[chan].note < 12 && current->channel[chan].note >= NR_NOTE_PERIODS+12){ if(current->channel[chan].note >= 12 && current->channel[chan].note < NR_NOTE_PERIODS+12){
// Only turn off note if it isnt 0
if(current->channel[chan].note != 0)
psg_state.channel[chan].vol = 15;
}else{
psg_state.channel[chan].tone = note_periods[current->channel[chan].note-12]; psg_state.channel[chan].tone = note_periods[current->channel[chan].note-12];
psg_state.channel[chan].vol = 15-current->channel[chan].volume; psg_state.channel[chan].vol = 0;
}else if(current->channel[chan].note == 0xff){
psg_state.channel[chan].vol = 15;
} }
} }
} }
@@ -103,19 +101,10 @@ void player_main(){
flush_psg(); flush_psg();
ui_init(); ui_init();
pattern_t p = { pattern_t p = {};
.speed = 12, p.speed = 6;
.rows = {
{.channel = {{60, 15}, {0, 0}, {0, 0}, {0, 0}}},
{.channel = {{67, 15}, {0, 0}, {0, 0}, {0, 0}}},
{.channel = {{69, 15}, {0, 0}, {0, 0}, {0, 0}}},
{.channel = {{67, 15}, {0, 0}, {0, 0}, {0, 0}}},
{.channel = {{60, 15}, {0, 0}, {0, 0}, {0, 0}}},
}
};
pattern = &p; pattern = &p;
ui_set_pattern(&p);
for(;;){ for(;;){
if(music_tick_interrupt_happened){ if(music_tick_interrupt_happened){
@@ -126,7 +115,7 @@ void player_main(){
input_interrupt_happened = 0; input_interrupt_happened = 0;
ui_input(input); ui_input(input);
} }
uint8_t action = do_ui(); uint8_t action = do_ui(playing, pattern, row);
if(action != ACTION_NONE){ if(action != ACTION_NONE){
if(action == ACTION_PLAY_ONCE){ if(action == ACTION_PLAY_ONCE){
tick = 0; tick = 0;
@@ -155,4 +144,12 @@ void player_main(){
} }
} }
} }
void save(){
disk_write(pattern, sizeof(pattern_t));
}
void load(){
disk_read(pattern, sizeof(pattern_t));
}

View File

@@ -2,11 +2,10 @@
#include <stdint.h> #include <stdint.h>
#define PSG_CLOCK 1000000 #define PSG_CLOCK 1000000
#define PATTERN_ROWS 16 #define PATTERN_ROWS 32
typedef struct{ typedef struct{
uint8_t note; uint8_t note;
uint8_t volume;
} cell_t; } cell_t;
typedef struct{ typedef struct{
@@ -20,4 +19,6 @@ typedef struct{
void player_main(); void player_main();
void music_tick_interrupt(); void music_tick_interrupt();
void input_interrupt(char input); void input_interrupt(char input);
void save();
void load();

View File

@@ -11,6 +11,9 @@
#define TRACK_ROWS 16 #define TRACK_ROWS 16
#define CHANNELS 4 #define CHANNELS 4
#define MODE_EDIT 0
#define MODE_PLAYING 1
static uint8_t dirty = 1; static uint8_t dirty = 1;
static uint8_t clear = 1; static uint8_t clear = 1;
@@ -22,6 +25,9 @@ static uint8_t input_octave = 4;
static uint8_t do_action = ACTION_NONE; static uint8_t do_action = ACTION_NONE;
static pattern_t * pattern = NULL; static pattern_t * pattern = NULL;
static uint8_t start_row = 0;
static uint8_t active_row = 0;
static uint8_t mode = MODE_EDIT;
static void serial_puts(char * s){ static void serial_puts(char * s){
@@ -173,7 +179,11 @@ static void draw_cell(uint8_t row, uint8_t channel){
static void draw_row(uint8_t row){ static void draw_row(uint8_t row){
uint8_t channel; uint8_t channel;
if(row == active_row && mode == MODE_PLAYING){
reverse_on();
}
print_hex_byte(row); print_hex_byte(row);
reverse_off();
serial_puts(" "); serial_puts(" ");
for (channel = 0; channel < CHANNELS; channel++) { for (channel = 0; channel < CHANNELS; channel++) {
@@ -199,20 +209,36 @@ static void draw(void){
cursor_hide(); cursor_hide();
serial_puts("Z80 TRACKER\r\n"); serial_puts("Z80 TRACKER\r\n");
serial_puts("O [");
serial_puts("O[");
print_hex_digit(input_octave); print_hex_digit(input_octave);
serial_puts("]\r\n"); serial_puts("] ");
if(mode == MODE_EDIT){
serial_puts("E ");
}else if(mode == MODE_PLAYING){
serial_puts("P ");
}else{
serial_puts("x ");
}
serial_puts("S[");
print_hex_byte(pattern->speed);
serial_puts("] ");
serial_puts("\r\n");
serial_puts(" CH1 CH2 CH3 NOISE\r\n"); serial_puts(" CH1 CH2 CH3 NOISE\r\n");
serial_puts(" ------- ------- ------- -------\r\n"); serial_puts(" ------- ------- ------- -------\r\n");
for (row = 0; row < TRACK_ROWS; row++) { for (row = start_row; row < TRACK_ROWS+start_row; row++) {
draw_row(row); draw_row(row);
} }
serial_puts("\r\n"); serial_puts("\r\n");
serial_puts("Arrows: move A-K: notes X: clear Space: off\r\n"); serial_puts("Arrows: move A-K: notes X: clear Space: off\r\n");
serial_puts("P: play O: stop Q: quit Numbers: oct\r\n"); serial_puts("P: play O: stop Q: quit Numbers: oct\r\n");
serial_puts("N: save M: load\r\n");
/* /*
* Keep the real terminal cursor out of the tracker area. * Keep the real terminal cursor out of the tracker area.
@@ -223,13 +249,19 @@ static void draw(void){
static void move_up(void){ static void move_up(void){
if (cursor_row > 0) if (cursor_row > 0)
cursor_row--; cursor_row--;
while(cursor_row < start_row){
start_row -= TRACK_ROWS;
}
dirty = 1; dirty = 1;
} }
static void move_down(void){ static void move_down(void){
if (cursor_row < TRACK_ROWS - 1) if (cursor_row < PATTERN_ROWS - 1)
cursor_row++; cursor_row++;
while(cursor_row >= start_row+TRACK_ROWS){
start_row += TRACK_ROWS;
}
dirty = 1; dirty = 1;
} }
@@ -288,7 +320,6 @@ static void enter_note(uint8_t note){
cell_t *cell = &pattern->rows[cursor_row].channel[cursor_channel]; cell_t *cell = &pattern->rows[cursor_row].channel[cursor_channel];
cell->note = note; cell->note = note;
cell->volume = 15;
/* /*
* Just give every entered note instrument 1 for now. * Just give every entered note instrument 1 for now.
@@ -299,8 +330,7 @@ static void enter_note(uint8_t note){
/* /*
* Tracker-style behavior: advance after entering a note. * Tracker-style behavior: advance after entering a note.
*/ */
if (cursor_row < TRACK_ROWS - 1) move_down();
cursor_row++;
dirty = 1; dirty = 1;
} }
@@ -375,6 +405,41 @@ void ui_input(char input){
return; return;
} }
/*
* , = dec speed
*/
if (input == ',') {
pattern->speed--;
dirty = 1;
return;
}
/*
* . = inc speed
*/
if (input == '.') {
pattern->speed++;
dirty = 1;
return;
}
/*
* N = save
*/
if (input == 'N' || input == 'n') {
save();
return;
}
/*
* M = save
*/
if (input == 'M' || input == 'm') {
load();
dirty = 1;
return;
}
/* /*
* Piano keyboard. * Piano keyboard.
*/ */
@@ -442,7 +507,37 @@ void ui_input(char input){
} }
} }
uint8_t do_ui(){ uint8_t do_ui(uint8_t _playing, pattern_t * _pattern, uint8_t _row){
pattern = _pattern;
if(_playing){
if(mode != MODE_PLAYING){
dirty = 1;
}
mode = MODE_PLAYING;
}else{
if(mode != MODE_EDIT){
dirty = 1;
start_row = 0;
}
mode = MODE_EDIT;
}
if(mode == MODE_PLAYING){
while(_row>=start_row+TRACK_ROWS){
start_row += TRACK_ROWS;
dirty = 1;
}
while(_row<start_row){
start_row -= TRACK_ROWS;
dirty = 1;
}
if(_row != active_row){
active_row = _row;
dirty = 1;
}
}
if(dirty){ if(dirty){
dirty = 0; dirty = 0;
draw(); draw();
@@ -455,10 +550,6 @@ uint8_t do_ui(){
return ACTION_NONE; return ACTION_NONE;
} }
void ui_set_pattern(pattern_t * _pattern){
pattern = _pattern;
}
void ui_init(){ void ui_init(){
uint8_t row; uint8_t row;
uint8_t channel; uint8_t channel;
@@ -469,4 +560,8 @@ void ui_init(){
cursor_row = 0; cursor_row = 0;
cursor_channel = 0; cursor_channel = 0;
input_state = 0; input_state = 0;
mode = MODE_EDIT;
start_row = 0;
active_row = 0;
} }

View File

@@ -8,6 +8,5 @@
#define ACTION_QUIT 3 #define ACTION_QUIT 3
void ui_input(char input); void ui_input(char input);
uint8_t do_ui(); uint8_t do_ui(uint8_t _playing, pattern_t * _pattern, uint8_t _row);
void ui_init(); void ui_init();
void ui_set_pattern(pattern_t * _pattern);