From d8f7ebeaade09ec277d5a215fd04f46417427ecd Mon Sep 17 00:00:00 2001 From: Joppe Blondel Date: Fri, 28 Aug 2026 15:44:42 +0200 Subject: [PATCH] scrolling and save/load --- .vscode/launch.json | 32 ++++++++++++ tracker/Makefile | 3 +- tracker/disk.bin | Bin 0 -> 129 bytes tracker/src/main.c | 12 +++++ tracker/src/main.h | 4 +- tracker/src/player.c | 35 ++++++------- tracker/src/player.h | 7 +-- tracker/src/ui.c | 119 ++++++++++++++++++++++++++++++++++++++----- tracker/src/ui.h | 5 +- 9 files changed, 178 insertions(+), 39 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 tracker/disk.bin diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..c816ae7 --- /dev/null +++ b/.vscode/launch.json @@ -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 + } + ] + } + ] +} \ No newline at end of file diff --git a/tracker/Makefile b/tracker/Makefile index 8b34f8b..50679c4 100644 --- a/tracker/Makefile +++ b/tracker/Makefile @@ -9,9 +9,10 @@ OBJFILES := $(SRCFILES:%.c=$(BUILDDIR)/%.c.o) DEPFILES := $(SRCFILES:%.c=$(BUILDDIR)/%.c.d) CC := gcc -CFLAGS := -Wall -Wextra +CFLAGS := -Wall -Wextra -g CPPFLAGS := -I src $(shell pkg-config --cflags sdl2) LDLIBS := $(shell pkg-config --libs sdl2) +LDFLAGS := -g .PHONY: $(TARGET) all clean all: $(TARGET) diff --git a/tracker/disk.bin b/tracker/disk.bin new file mode 100644 index 0000000000000000000000000000000000000000..2a4400ca202aeebe57567aa46e647bf3a7aeb0c8 GIT binary patch literal 129 fcmcCvU|_HVVh1301Y&0(b_HU0Aoc)aS_3u!?Rf$j literal 0 HcmV?d00001 diff --git a/tracker/src/main.c b/tracker/src/main.c index 9e0c17b..4aee118 100644 --- a/tracker/src/main.c +++ b/tracker/src/main.c @@ -94,6 +94,18 @@ void serial_write(char c){ 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){ AudioState *state = userdata; int16_t *output = (int16_t *)stream; diff --git a/tracker/src/main.h b/tracker/src/main.h index 756e0d0..0a78142 100644 --- a/tracker/src/main.h +++ b/tracker/src/main.h @@ -2,4 +2,6 @@ #include void psg_write(uint8_t value); -void serial_write(char c); \ No newline at end of file +void serial_write(char c); +void disk_write(void * data, size_t len); +void disk_read(void * data, size_t len); \ No newline at end of file diff --git a/tracker/src/player.c b/tracker/src/player.c index 4a3abf6..1d0728a 100644 --- a/tracker/src/player.c +++ b/tracker/src/player.c @@ -54,13 +54,11 @@ static void flush_psg(){ static void process_row(){ row_t * current = &pattern->rows[row]; for(uint8_t chan=0; chan<3; chan++){ - 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{ + if(current->channel[chan].note >= 12 && current->channel[chan].note < NR_NOTE_PERIODS+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(); ui_init(); - pattern_t p = { - .speed = 12, - .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_t p = {}; + p.speed = 6; pattern = &p; - ui_set_pattern(&p); for(;;){ if(music_tick_interrupt_happened){ @@ -126,7 +115,7 @@ void player_main(){ input_interrupt_happened = 0; ui_input(input); } - uint8_t action = do_ui(); + uint8_t action = do_ui(playing, pattern, row); if(action != ACTION_NONE){ if(action == ACTION_PLAY_ONCE){ tick = 0; @@ -155,4 +144,12 @@ void player_main(){ } } -} \ No newline at end of file +} + +void save(){ + disk_write(pattern, sizeof(pattern_t)); +} + +void load(){ + disk_read(pattern, sizeof(pattern_t)); +} diff --git a/tracker/src/player.h b/tracker/src/player.h index b7dc56a..1300b99 100644 --- a/tracker/src/player.h +++ b/tracker/src/player.h @@ -2,11 +2,10 @@ #include #define PSG_CLOCK 1000000 -#define PATTERN_ROWS 16 +#define PATTERN_ROWS 32 typedef struct{ uint8_t note; - uint8_t volume; } cell_t; typedef struct{ @@ -20,4 +19,6 @@ typedef struct{ void player_main(); void music_tick_interrupt(); -void input_interrupt(char input); \ No newline at end of file +void input_interrupt(char input); +void save(); +void load(); \ No newline at end of file diff --git a/tracker/src/ui.c b/tracker/src/ui.c index bf1e4af..70ae6cf 100644 --- a/tracker/src/ui.c +++ b/tracker/src/ui.c @@ -11,6 +11,9 @@ #define TRACK_ROWS 16 #define CHANNELS 4 +#define MODE_EDIT 0 +#define MODE_PLAYING 1 + static uint8_t dirty = 1; static uint8_t clear = 1; @@ -22,6 +25,9 @@ static uint8_t input_octave = 4; static uint8_t do_action = ACTION_NONE; 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){ @@ -173,7 +179,11 @@ static void draw_cell(uint8_t row, uint8_t channel){ static void draw_row(uint8_t row){ uint8_t channel; + if(row == active_row && mode == MODE_PLAYING){ + reverse_on(); + } print_hex_byte(row); + reverse_off(); serial_puts(" "); for (channel = 0; channel < CHANNELS; channel++) { @@ -199,20 +209,36 @@ static void draw(void){ cursor_hide(); serial_puts("Z80 TRACKER\r\n"); - serial_puts("O ["); + + serial_puts("O["); 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(" ------- ------- ------- -------\r\n"); - for (row = 0; row < TRACK_ROWS; row++) { + for (row = start_row; row < TRACK_ROWS+start_row; row++) { draw_row(row); } serial_puts("\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("N: save M: load\r\n"); /* * Keep the real terminal cursor out of the tracker area. @@ -223,13 +249,19 @@ static void draw(void){ static void move_up(void){ if (cursor_row > 0) cursor_row--; + while(cursor_row < start_row){ + start_row -= TRACK_ROWS; + } dirty = 1; } static void move_down(void){ - if (cursor_row < TRACK_ROWS - 1) + if (cursor_row < PATTERN_ROWS - 1) cursor_row++; + while(cursor_row >= start_row+TRACK_ROWS){ + start_row += TRACK_ROWS; + } dirty = 1; } @@ -288,7 +320,6 @@ static void enter_note(uint8_t note){ cell_t *cell = &pattern->rows[cursor_row].channel[cursor_channel]; cell->note = note; - cell->volume = 15; /* * 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. */ - if (cursor_row < TRACK_ROWS - 1) - cursor_row++; + move_down(); dirty = 1; } @@ -375,6 +405,41 @@ void ui_input(char input){ 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. */ @@ -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