├── .devcontainer ├── Dockerfile ├── devcontainer.json └── libpcm.a ├── .gitignore ├── LICENSE.md ├── MODs └── ReadMe.txt ├── Makefile ├── README.md ├── cdfh.c ├── cdfh.h ├── crt0.s ├── files.h ├── files.s ├── font.s ├── hw_md.h ├── hw_md.s ├── main.c ├── mod-info ├── module-exp.c ├── module.c ├── module.h ├── pcm-io.s ├── pcm.c └── pcm.h /.devcontainer/Dockerfile: -------------------------------------------------------------------------------- 1 | # See here for image contents: https://github.com/microsoft/vscode-dev-containers/tree/v0.231.6/containers/ubuntu/.devcontainer/base.Dockerfile 2 | 3 | # [Choice] Ubuntu version (use hirsuite or bionic on local arm64/Apple Silicon): hirsute, focal, bionic 4 | ARG VARIANT="focal" 5 | FROM mcr.microsoft.com/vscode/devcontainers/base:0-${VARIANT} 6 | 7 | # [Optional] Uncomment this section to install additional OS packages. 8 | # RUN apt-get update && export DEBIAN_FRONTEND=noninteractive \ 9 | # && apt-get -y install --no-install-recommends 10 | 11 | RUN sudo apt update && apt install -y curl zstd make jq 12 | 13 | RUN curl -sL --fail -H "Accept: application/vnd.github.v3+json" \ 14 | "https://api.github.com/repos/viciious/32XDK/releases/tags/20190504" \ 15 | | jq -r ".assets | .[] | select(.name | test(\"chillys-sega-devkit-20190504-opt.tar.zst$\")) | .url" \ 16 | | xargs curl -sL --fail -H "Accept: application/octet-stream" -O /dev/stdout | zstdcat | tar -C / -xa 17 | 18 | RUN sudo apt -y install file 19 | 20 | RUN sudo dpkg --add-architecture i386 21 | 22 | RUN sudo apt update 23 | 24 | RUN sudo apt install -y libx11-6:i386 25 | 26 | RUN sudo apt update 27 | 28 | RUN sudo apt -y install zlib1g:i386 29 | 30 | RUN sudo wget "http://ppa.launchpad.net/linuxuprising/libpng12/ubuntu/pool/main/libp/libpng/libpng12-0_1.2.54-1ubuntu1.1+1~ppa0~eoan_i386.deb" 31 | 32 | RUN sudo dpkg -i libpng12-0_1.2.54-1ubuntu1.1+1~ppa0~eoan_i386.deb 33 | 34 | RUN sudo apt update 35 | 36 | RUN sudo apt -y install lib32stdc++6 37 | 38 | COPY ["libpcm.a", "/opt/toolchains/sega/m68k-elf/lib/"] 39 | 40 | RUN sudo apt update 41 | 42 | RUN sudo apt -y install genisoimage 43 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: 2 | // https://github.com/microsoft/vscode-dev-containers/tree/v0.231.6/containers/ubuntu 3 | { 4 | "name": "Ubuntu", 5 | "build": { 6 | "dockerfile": "Dockerfile", 7 | // Update 'VARIANT' to pick an Ubuntu version: hirsute, focal, bionic 8 | // Use hirsute or bionic on local arm64/Apple Silicon. 9 | "args": { "VARIANT": "focal" } 10 | }, 11 | 12 | // Set *default* container specific settings.json values on container create. 13 | "settings": {}, 14 | 15 | 16 | // Add the IDs of extensions you want installed when the container is created in the array below. 17 | "extensions": [ 18 | "ms-vscode.cpptools", 19 | "steventattersall.m68k", 20 | "ms-vscode.cpptools-extension-pack" 21 | ], 22 | 23 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 24 | // "forwardPorts": [], 25 | 26 | // Use 'postCreateCommand' to run commands after the container is created. 27 | // "postCreateCommand": "uname -a", 28 | 29 | // Comment out to connect as root instead. More info: https://aka.ms/vscode-remote/containers/non-root. 30 | "remoteUser": "vscode", 31 | "features": { 32 | "git": "os-provided" 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /.devcontainer/libpcm.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matteusbeus/ModPlayer/621f5a07b7d94ed91751b828e928b31dfc1d7a56/.devcontainer/libpcm.a -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.mod 2 | *.32x 3 | *.bin 4 | *.elf 5 | *.map 6 | *.o 7 | *.wad 8 | *.zip 9 | *.md 10 | *.xdelta 11 | *.iso 12 | *.cue 13 | *.elf 14 | .github 15 | .vscode 16 | .cd -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Joseph Fenton 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MODs/ReadMe.txt: -------------------------------------------------------------------------------- 1 | Mod files go in this folder. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ifdef $(GENDEV) 2 | ROOTDIR = $(GENDEV) 3 | else 4 | ROOTDIR = /opt/toolchains/sega 5 | endif 6 | 7 | LDSCRIPTSDIR = $(ROOTDIR)/ldscripts 8 | 9 | BOOTBLOCKDIR = $(ROOTDIR)/bootblocks 10 | #BOOTBLOCK = $(BOOTBLOCKDIR)/US_BOOT.BIN 11 | BOOTBLOCK = $(BOOTBLOCKDIR)/EU_BOOT.BIN 12 | #BOOTBLOCK = $(BOOTBLOCKDIR)/JP_BOOT.BIN 13 | 14 | LIBPATH = -L$(ROOTDIR)/m68k-elf/lib -L$(ROOTDIR)/m68k-elf/lib/gcc/m68k-elf/4.6.2 -L$(ROOTDIR)/m68k-elf/m68k-elf/lib 15 | INCPATH = -I. -I../include -I$(ROOTDIR)/m68k-elf/include -I$(ROOTDIR)/m68k-elf/m68k-elf/include 16 | 17 | CCFLAGS = -m68000 -Wall -O1 -c -fomit-frame-pointer 18 | HWFLAGS = -m68000 -Wall -O1 -c -fomit-frame-pointer 19 | LDFLAGS = -T $(LDSCRIPTSDIR)/cd.ld -Wl,-Map=output.map -nostdlib 20 | ASFLAGS = -m68000 --register-prefix-optional 21 | 22 | PREFIX = $(ROOTDIR)/m68k-elf/bin/m68k-elf- 23 | CC = $(PREFIX)gcc 24 | AS = $(PREFIX)as 25 | LD = $(PREFIX)ld 26 | OBJC = $(PREFIX)objcopy 27 | 28 | ASMZ80 = $(ROOTDIR)/bin/zasm 29 | FLAGSZ80 = -vb2 30 | 31 | DD = dd 32 | RM = rm -rf 33 | 34 | TARGET = CDModPlayer 35 | LIBS = $(LIBPATH) -lpcm -lc -lgcc -lnosys 36 | OBJS = crt0.o main.o module.o cdfh.o hw_md.o 37 | FILES = 38 | 39 | all: $(TARGET).bin 40 | 41 | $(TARGET).bin: $(TARGET).elf 42 | $(OBJC) -O binary $< temp.bin 43 | $(DD) if=temp.bin of=$@ bs=2048 conv=sync 44 | 45 | $(TARGET).elf: $(OBJS) $(FILES) 46 | $(CC) $(LDFLAGS) $(OBJS) $(LIBS) $(FILES) -o $(TARGET).elf 47 | 48 | %.o80: %.s80 49 | $(ASMZ80) $(FLAGSZ80) -o $@ $< 50 | 51 | module.o: module.c 52 | $(CC) $(HWFLAGS) -DPROC_PATTERNS $(INCPATH) $< -o $@ 53 | 54 | %.o: %.c 55 | $(CC) $(CCFLAGS) $(INCPATH) $< -o $@ 56 | 57 | %.o: %.s 58 | $(AS) $(ASFLAGS) $(INCPATH) $< -o $@ 59 | 60 | cd: $(TARGET).bin 61 | mkdir -p cd 62 | mkdir -p cd/MODs 63 | cp $(TARGET).bin cd/APP.BIN 64 | cp -r MODs/* cd/MODs/ 65 | genisoimage -sysid "SEGA SEGACD" -volid "CDMODPLAYER" -generic-boot $(BOOTBLOCK) -full-iso9660-filenames -o $(TARGET).iso cd 66 | 67 | clean: 68 | $(RM) *.o *.o80 *.bin *.elf *.map *.log *.iso cd 69 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mod Player for the Sega Mega CD 2 | 3 | ## Description 4 | 5 | This project shows an example of how the Sega Mega CD can be used to play MOD files. The code is based upon Ami-PlayMOD by Massimiliano Scarano. 6 | 7 | A couple of important pointers: 8 | * Each sample must be under 128KiB 9 | * If the total size of all samples is larger than 64KiB. The biggest sample is halved and the base note value adjusted to play at the correct pitch. This is repeated until all samples fit into 64KiB. This means overall larger samples will deteriorate in quality. 10 | 11 | ## "Mod Player for the Sega Mega CD" Credits 12 | * Programming : Chilly Willy 13 | * Base Docker Code : Victor Luchits 14 | * Additional Docker Code for compatibility : Matt B (Matteusbeus) 15 | 16 | ## "Ami-PlayMOD" Credits 17 | * Programming : Massimiliano Scarano 18 | 19 | ## 20 | 21 | ## Links 22 | https://aminet.net/package/mus/play/Ami-PlayMOD_V1_0.lha 23 | 24 | https://github.com/viciious/d32xr/tree/master/.devcontainer 25 | 26 | ## License 27 | All original code is available under the MIT license. 28 | -------------------------------------------------------------------------------- /cdfh.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "hw_md.h" 7 | #include "cdfh.h" 8 | 9 | //---------------------------------------------------------------------- 10 | // SegaCD File Handler - by Chilly Willy 11 | // Inspired by the MikMod READER structure. 12 | //---------------------------------------------------------------------- 13 | 14 | static uint8_t cd_Eof(CDFileHandle_t *handle) 15 | { 16 | if (!handle) 17 | return 1; 18 | 19 | if (handle->pos >= handle->length) 20 | return 1; 21 | 22 | return 0; 23 | } 24 | 25 | static int32_t cd_Read(CDFileHandle_t *handle, void *ptr, int32_t size) 26 | { 27 | int32_t pos, blk, len, read = 0; 28 | uint8_t *dst = ptr; 29 | 30 | if (!handle) 31 | return 0; 32 | 33 | while (size != 0) 34 | { 35 | if (handle->Eof(handle)) 36 | return read; 37 | 38 | pos = handle->pos; 39 | blk = (pos >> 11) + handle->offset; 40 | if (handle->block != blk) 41 | { 42 | read_cd(blk, 1, (void *)0x6800); 43 | handle->block = blk; 44 | } 45 | 46 | len = 0x800 - (pos & 0x7FF); 47 | if (len > size) 48 | len = size; 49 | if (len > (handle->length - pos)) 50 | len = (handle->length - pos); 51 | 52 | memcpy(dst, (char *)0x6800 + (pos & 0x7FF), len); 53 | 54 | handle->pos += len; 55 | dst += len; 56 | read += len; 57 | size -= len; 58 | } 59 | 60 | return read; 61 | } 62 | 63 | static uint8_t cd_Get(CDFileHandle_t *handle) 64 | { 65 | int32_t pos, blk; 66 | 67 | if (handle->Eof(handle)) 68 | return 0; 69 | 70 | pos = handle->pos; 71 | blk = (pos >> 11) + handle->offset; 72 | if (handle->block != blk) 73 | { 74 | read_cd(blk, 1, (void *)0x6800); 75 | handle->block = blk; 76 | } 77 | 78 | handle->pos++; 79 | return ((uint8_t*)0x6800)[pos & 0x7FF]; 80 | } 81 | 82 | static int32_t cd_Seek(CDFileHandle_t *handle, int32_t offset, int32_t whence) 83 | { 84 | int32_t pos; 85 | 86 | if (!handle) 87 | return -1; 88 | 89 | pos = handle->pos; 90 | switch(whence) 91 | { 92 | case SEEK_CUR: 93 | pos += offset; 94 | break; 95 | case SEEK_SET: 96 | pos = offset; 97 | break; 98 | case SEEK_END: 99 | pos = handle->length - offset - 1; 100 | break; 101 | } 102 | if (pos < 0) 103 | handle->pos = 0; 104 | else if (pos > handle->length) 105 | handle->pos = handle->length; 106 | else 107 | handle->pos = pos; 108 | 109 | return handle->pos; 110 | } 111 | 112 | static int32_t cd_Tell(CDFileHandle_t *handle) 113 | { 114 | return handle ? handle->pos : 0; 115 | } 116 | 117 | 118 | CDFileHandle_t *cd_handle_from_offset(int32_t offset, int32_t length) 119 | { 120 | CDFileHandle_t *handle = (CDFileHandle_t*)malloc(sizeof(CDFileHandle_t)); 121 | if (handle) 122 | { 123 | handle->Eof = &cd_Eof; 124 | handle->Read = &cd_Read; 125 | handle->Get = &cd_Get; 126 | handle->Seek = &cd_Seek; 127 | handle->Tell = &cd_Tell; 128 | handle->offset = offset; 129 | handle->length = length; 130 | handle->block = -1; // nothing read yet 131 | handle->pos = 0; 132 | } 133 | return handle; 134 | } 135 | 136 | CDFileHandle_t *cd_handle_from_name(char *name) 137 | { 138 | int32_t i; 139 | char temp[256]; 140 | 141 | CDFileHandle_t *handle = (CDFileHandle_t*)malloc(sizeof(CDFileHandle_t)); 142 | if (handle) 143 | { 144 | handle->Eof = &cd_Eof; 145 | handle->Read = &cd_Read; 146 | handle->Get = &cd_Get; 147 | handle->Seek = &cd_Seek; 148 | handle->Tell = &cd_Tell; 149 | 150 | i = strlen(name); 151 | while (i && (name[i] != '/')) 152 | i--; 153 | if (name[i] == '/') 154 | { 155 | if (i) 156 | { 157 | strncpy(temp, name, i); 158 | temp[i] = 0; 159 | } 160 | else 161 | { 162 | strcpy(temp, "/"); 163 | } 164 | if (set_cwd(temp) < 0) 165 | { 166 | // error setting working directory 167 | free(handle); 168 | return NULL; 169 | } 170 | strncpy(temp, &name[i+1], 255); 171 | temp[255] = 0; 172 | } 173 | else 174 | { 175 | strncpy(temp, name, 255); 176 | temp[255] = 0; 177 | } 178 | 179 | if (find_dir_entry(temp) < 0) 180 | { 181 | // error finding entry 182 | free(handle); 183 | return NULL; 184 | } 185 | 186 | handle->offset = global_vars->DENTRY_OFFSET; 187 | handle->length = global_vars->DENTRY_LENGTH; 188 | handle->block = -1; // nothing read yet 189 | handle->pos = 0; 190 | } 191 | return handle; 192 | } 193 | 194 | void delete_cd_handle(CDFileHandle_t *handle) 195 | { 196 | if (handle) 197 | free(handle); 198 | } 199 | 200 | -------------------------------------------------------------------------------- /cdfh.h: -------------------------------------------------------------------------------- 1 | #ifndef _CDFH_H 2 | #define _CDFH_H 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | typedef struct CDFileHandle { 11 | int32_t (*Seek)(struct CDFileHandle *handle, int32_t offset, int32_t whence); 12 | int32_t (*Tell)(struct CDFileHandle *handle); 13 | int32_t (*Read)(struct CDFileHandle *handle, void *ptr, int32_t size); 14 | uint8_t (*Get)(struct CDFileHandle *handle); 15 | uint8_t (*Eof)(struct CDFileHandle *handle); 16 | int32_t offset; // start block of file 17 | int32_t length; // length of file 18 | int32_t block; // current block in buffer 19 | int32_t pos; // current position in file 20 | } CDFileHandle_t; 21 | 22 | extern CDFileHandle_t *cd_handle_from_name(char *name); 23 | extern CDFileHandle_t *cd_handle_from_offset(int32_t offset, int32_t length); 24 | extern void delete_cd_handle(CDFileHandle_t *handle); 25 | 26 | #ifdef __cplusplus 27 | } 28 | #endif 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /crt0.s: -------------------------------------------------------------------------------- 1 | | SEGA CD support code 2 | | by Chilly Willy 3 | 4 | .text 5 | 6 | | CD startup at 0x8000 7 | 8 | .global _start 9 | _start: 10 | move #0x2700,sr /* disable interrupts */ 11 | 12 | | Clear BSS 13 | lea __bss_start,a0 14 | lea __bss_end,a1 15 | moveq #0,d0 16 | 1: 17 | move.l d0,(a0)+ 18 | cmpa.l a0,a1 19 | bhi.b 1b 20 | 21 | move.l sp,__stack_save /* save BIOS stack pointer */ 22 | lea __stack,a0 23 | movea.l a0,sp /* set stack pointer to top of Program RAM */ 24 | link.w a6,#-8 /* set up initial stack frame */ 25 | 26 | jsr init_hardware /* initialize the console hardware */ 27 | 28 | jsr __INIT_SECTION__ /* do all program initializers */ 29 | jsr main /* call program main() */ 30 | jsr __FINI_SECTION__ /* do all program finishers */ 31 | 32 | movea.l __stack_save,sp /* restore BIOS stack pointer */ 33 | moveq #0,d0 34 | rts 35 | 36 | 37 | .data 38 | 39 | .align 4 40 | 41 | __stack_save: 42 | .long 0 43 | 44 | .text 45 | 46 | -------------------------------------------------------------------------------- /files.h: -------------------------------------------------------------------------------- 1 | #define NUM_FILES 5 2 | 3 | extern char *fileName[NUM_FILES]; 4 | extern int fileSize[NUM_FILES]; 5 | extern int filePtr[NUM_FILES]; 6 | -------------------------------------------------------------------------------- /files.s: -------------------------------------------------------------------------------- 1 | .text 2 | 3 | .align 4 4 | 5 | -------------------------------------------------------------------------------- /font.s: -------------------------------------------------------------------------------- 1 | /* ASCII font from Steph's Mini DevKit */ 2 | 3 | font_data: 4 | | 0x20 5 | .long 0x00000000 6 | .long 0x00000000 7 | .long 0x00000000 8 | .long 0x00000000 9 | .long 0x00000000 10 | .long 0x00000000 11 | .long 0x00000000 12 | .long 0x00000000 13 | 14 | 15 | .long 0x00000000 16 | .long 0x000FF000 17 | .long 0x000FF000 18 | .long 0x000FF000 19 | .long 0x000FF000 20 | .long 0x00000000 21 | .long 0x000FF000 22 | .long 0x00000000 23 | 24 | 25 | .long 0x00000000 26 | .long 0x0FF00FF0 27 | .long 0x0FF00FF0 28 | .long 0x0FF00FF0 29 | .long 0x00000000 30 | .long 0x00000000 31 | .long 0x00000000 32 | .long 0x00000000 33 | 34 | 35 | .long 0x00000000 36 | .long 0x0FF00FF0 37 | .long 0xFFFFFFFF 38 | .long 0x0FF00FF0 39 | .long 0x0FF00FF0 40 | .long 0xFFFFFFFF 41 | .long 0x0FF00FF0 42 | .long 0x00000000 43 | 44 | 45 | .long 0x000FF000 46 | .long 0x00FFFFF0 47 | .long 0x0FF00000 48 | .long 0x00FFFF00 49 | .long 0x00000FF0 50 | .long 0x0FFFFF00 51 | .long 0x000FF000 52 | .long 0x00000000 53 | 54 | 55 | .long 0x00000000 56 | .long 0x0FF00FF0 57 | .long 0x0FF0FF00 58 | .long 0x000FF000 59 | .long 0x00FF0000 60 | .long 0x0FF00FF0 61 | .long 0x0F000FF0 62 | .long 0x00000000 63 | 64 | 65 | .long 0x000FFF00 66 | .long 0x00FF0FF0 67 | .long 0x000FFF00 68 | .long 0x00FFF000 69 | .long 0x0FF0FFFF 70 | .long 0x0FF00FF0 71 | .long 0x00FFF0FF 72 | .long 0x00000000 73 | 74 | 75 | .long 0x00000000 76 | .long 0x000FF000 77 | .long 0x000FF000 78 | .long 0x000FF000 79 | .long 0x00000000 80 | .long 0x00000000 81 | .long 0x00000000 82 | .long 0x00000000 83 | 84 | 85 | .long 0x00000000 86 | .long 0x0000FFF0 87 | .long 0x000FFF00 88 | .long 0x000FF000 89 | .long 0x000FF000 90 | .long 0x000FFF00 91 | .long 0x0000FFF0 92 | .long 0x00000000 93 | 94 | 95 | .long 0x00000000 96 | .long 0x0FFF0000 97 | .long 0x00FFF000 98 | .long 0x000FF000 99 | .long 0x000FF000 100 | .long 0x00FFF000 101 | .long 0x0FFF0000 102 | .long 0x00000000 103 | 104 | 105 | .long 0x00000000 106 | .long 0x0FF00FF0 107 | .long 0x00FFFF00 108 | .long 0xFFFFFFFF 109 | .long 0x00FFFF00 110 | .long 0x0FF00FF0 111 | .long 0x00000000 112 | .long 0x00000000 113 | 114 | 115 | .long 0x00000000 116 | .long 0x000FF000 117 | .long 0x000FF000 118 | .long 0x0FFFFFF0 119 | .long 0x000FF000 120 | .long 0x000FF000 121 | .long 0x00000000 122 | .long 0x00000000 123 | 124 | 125 | .long 0x00000000 126 | .long 0x00000000 127 | .long 0x00000000 128 | .long 0x00000000 129 | .long 0x00000000 130 | .long 0x000FF000 131 | .long 0x000FF000 132 | .long 0x00FF0000 133 | 134 | 135 | .long 0x00000000 136 | .long 0x00000000 137 | .long 0x00000000 138 | .long 0x0FFFFFF0 139 | .long 0x00000000 140 | .long 0x00000000 141 | .long 0x00000000 142 | .long 0x00000000 143 | 144 | 145 | .long 0x00000000 146 | .long 0x00000000 147 | .long 0x00000000 148 | .long 0x00000000 149 | .long 0x00000000 150 | .long 0x000FF000 151 | .long 0x000FF000 152 | .long 0x00000000 153 | 154 | 155 | .long 0x00000000 156 | .long 0x00000FF0 157 | .long 0x0000FF00 158 | .long 0x000FF000 159 | .long 0x00FF0000 160 | .long 0x0FF00000 161 | .long 0x0F000000 162 | .long 0x00000000 163 | 164 | 165 | .long 0x00000000 166 | .long 0x00FFFF00 167 | .long 0x0FF00FF0 168 | .long 0x0FF0FFF0 169 | .long 0x0FFF0FF0 170 | .long 0x0FF00FF0 171 | .long 0x00FFFF00 172 | .long 0x00000000 173 | 174 | 175 | .long 0x00000000 176 | .long 0x000FF000 177 | .long 0x00FFF000 178 | .long 0x000FF000 179 | .long 0x000FF000 180 | .long 0x000FF000 181 | .long 0x0FFFFFF0 182 | .long 0x00000000 183 | 184 | 185 | .long 0x00000000 186 | .long 0x00FFFF00 187 | .long 0x0FF00FF0 188 | .long 0x0000FF00 189 | .long 0x000FF000 190 | .long 0x00FF0000 191 | .long 0x0FFFFFF0 192 | .long 0x00000000 193 | 194 | 195 | .long 0x00000000 196 | .long 0x0FFFFFF0 197 | .long 0x0000FF00 198 | .long 0x000FF000 199 | .long 0x0000FF00 200 | .long 0x0FF00FF0 201 | .long 0x00FFFF00 202 | .long 0x00000000 203 | 204 | 205 | .long 0x00000000 206 | .long 0x0000FF00 207 | .long 0x000FFF00 208 | .long 0x00FFFF00 209 | .long 0x0FF0FF00 210 | .long 0x0FFFFFF0 211 | .long 0x0000FF00 212 | .long 0x00000000 213 | 214 | 215 | .long 0x00000000 216 | .long 0x0FFFFFF0 217 | .long 0x0FF00000 218 | .long 0x0FFFFF00 219 | .long 0x00000FF0 220 | .long 0x0FF00FF0 221 | .long 0x00FFFF00 222 | .long 0x00000000 223 | 224 | 225 | .long 0x00000000 226 | .long 0x00FFFF00 227 | .long 0x0FF00000 228 | .long 0x0FFFFF00 229 | .long 0x0FF00FF0 230 | .long 0x0FF00FF0 231 | .long 0x00FFFF00 232 | .long 0x00000000 233 | 234 | 235 | .long 0x00000000 236 | .long 0x0FFFFFF0 237 | .long 0x00000FF0 238 | .long 0x0000FF00 239 | .long 0x000FF000 240 | .long 0x00FF0000 241 | .long 0x00FF0000 242 | .long 0x00000000 243 | 244 | 245 | .long 0x00000000 246 | .long 0x00FFFF00 247 | .long 0x0FF00FF0 248 | .long 0x00FFFF00 249 | .long 0x0FF00FF0 250 | .long 0x0FF00FF0 251 | .long 0x00FFFF00 252 | .long 0x00000000 253 | 254 | 255 | .long 0x00000000 256 | .long 0x00FFFF00 257 | .long 0x0FF00FF0 258 | .long 0x00FFFFF0 259 | .long 0x00000FF0 260 | .long 0x0000FF00 261 | .long 0x00FFF000 262 | .long 0x00000000 263 | 264 | 265 | .long 0x00000000 266 | .long 0x00000000 267 | .long 0x000FF000 268 | .long 0x000FF000 269 | .long 0x00000000 270 | .long 0x000FF000 271 | .long 0x000FF000 272 | .long 0x00000000 273 | 274 | 275 | .long 0x00000000 276 | .long 0x00000000 277 | .long 0x000FF000 278 | .long 0x000FF000 279 | .long 0x00000000 280 | .long 0x000FF000 281 | .long 0x000FF000 282 | .long 0x00FF0000 283 | 284 | 285 | .long 0x00000FF0 286 | .long 0x0000FF00 287 | .long 0x000FF000 288 | .long 0x00FF0000 289 | .long 0x000FF000 290 | .long 0x0000FF00 291 | .long 0x00000FF0 292 | .long 0x00000000 293 | 294 | 295 | .long 0x00000000 296 | .long 0x00000000 297 | .long 0x0FFFFFF0 298 | .long 0x00000000 299 | .long 0x00000000 300 | .long 0x0FFFFFF0 301 | .long 0x00000000 302 | .long 0x00000000 303 | 304 | 305 | .long 0x0FF00000 306 | .long 0x00FF0000 307 | .long 0x000FF000 308 | .long 0x0000FF00 309 | .long 0x000FF000 310 | .long 0x00FF0000 311 | .long 0x0FF00000 312 | .long 0x00000000 313 | 314 | 315 | .long 0x00000000 316 | .long 0x00FFFF00 317 | .long 0x0FF00FF0 318 | .long 0x0000FF00 319 | .long 0x000FF000 320 | .long 0x00000000 321 | .long 0x000FF000 322 | .long 0x00000000 323 | 324 | 325 | .long 0x00000000 326 | .long 0x00FFFF00 327 | .long 0x0FF00FF0 328 | .long 0x0FF0FFF0 329 | .long 0x0FF0FFF0 330 | .long 0x0FF00000 331 | .long 0x00FFFFF0 332 | .long 0x00000000 333 | 334 | 335 | .long 0x00000000 336 | .long 0x000FF000 337 | .long 0x00FFFF00 338 | .long 0x0FF00FF0 339 | .long 0x0FF00FF0 340 | .long 0x0FFFFFF0 341 | .long 0x0FF00FF0 342 | .long 0x00000000 343 | 344 | 345 | .long 0x00000000 346 | .long 0x0FFFFF00 347 | .long 0x0FF00FF0 348 | .long 0x0FFFFF00 349 | .long 0x0FF00FF0 350 | .long 0x0FF00FF0 351 | .long 0x0FFFFF00 352 | .long 0x00000000 353 | 354 | 355 | .long 0x00000000 356 | .long 0x00FFFF00 357 | .long 0x0FF00FF0 358 | .long 0x0FF00000 359 | .long 0x0FF00000 360 | .long 0x0FF00FF0 361 | .long 0x00FFFF00 362 | .long 0x00000000 363 | 364 | 365 | .long 0x00000000 366 | .long 0x0FFFF000 367 | .long 0x0FF0FF00 368 | .long 0x0FF00FF0 369 | .long 0x0FF00FF0 370 | .long 0x0FF0FF00 371 | .long 0x0FFFF000 372 | .long 0x00000000 373 | 374 | 375 | .long 0x00000000 376 | .long 0x0FFFFFF0 377 | .long 0x0FF00000 378 | .long 0x0FFFFF00 379 | .long 0x0FF00000 380 | .long 0x0FF00000 381 | .long 0x0FFFFFF0 382 | .long 0x00000000 383 | 384 | 385 | .long 0x00000000 386 | .long 0x0FFFFFF0 387 | .long 0x0FF00000 388 | .long 0x0FFFFF00 389 | .long 0x0FF00000 390 | .long 0x0FF00000 391 | .long 0x0FF00000 392 | .long 0x00000000 393 | 394 | 395 | .long 0x00000000 396 | .long 0x00FFFFF0 397 | .long 0x0FF00000 398 | .long 0x0FF00000 399 | .long 0x0FF0FFF0 400 | .long 0x0FF00FF0 401 | .long 0x00FFFFF0 402 | .long 0x00000000 403 | 404 | 405 | .long 0x00000000 406 | .long 0x0FF00FF0 407 | .long 0x0FF00FF0 408 | .long 0x0FFFFFF0 409 | .long 0x0FF00FF0 410 | .long 0x0FF00FF0 411 | .long 0x0FF00FF0 412 | .long 0x00000000 413 | 414 | 415 | .long 0x00000000 416 | .long 0x0FFFFFF0 417 | .long 0x000FF000 418 | .long 0x000FF000 419 | .long 0x000FF000 420 | .long 0x000FF000 421 | .long 0x0FFFFFF0 422 | .long 0x00000000 423 | 424 | 425 | .long 0x00000000 426 | .long 0x00000FF0 427 | .long 0x00000FF0 428 | .long 0x00000FF0 429 | .long 0x00000FF0 430 | .long 0x0FF00FF0 431 | .long 0x00FFFF00 432 | .long 0x00000000 433 | 434 | 435 | .long 0x00000000 436 | .long 0x0FF00FF0 437 | .long 0x0FF0FF00 438 | .long 0x0FFFF000 439 | .long 0x0FFFF000 440 | .long 0x0FF0FF00 441 | .long 0x0FF00FF0 442 | .long 0x00000000 443 | 444 | 445 | .long 0x00000000 446 | .long 0x0FF00000 447 | .long 0x0FF00000 448 | .long 0x0FF00000 449 | .long 0x0FF00000 450 | .long 0x0FF00000 451 | .long 0x0FFFFFF0 452 | .long 0x00000000 453 | 454 | 455 | .long 0x00000000 456 | .long 0x0FF000FF 457 | .long 0x0FFF0FFF 458 | .long 0x0FFFFFFF 459 | .long 0x0FF0F0FF 460 | .long 0x0FF000FF 461 | .long 0x0FF000FF 462 | .long 0x00000000 463 | 464 | 465 | .long 0x00000000 466 | .long 0x0FF00FF0 467 | .long 0x0FFF0FF0 468 | .long 0x0FFFFFF0 469 | .long 0x0FFFFFF0 470 | .long 0x0FF0FFF0 471 | .long 0x0FF00FF0 472 | .long 0x00000000 473 | 474 | 475 | .long 0x00000000 476 | .long 0x00FFFF00 477 | .long 0x0FF00FF0 478 | .long 0x0FF00FF0 479 | .long 0x0FF00FF0 480 | .long 0x0FF00FF0 481 | .long 0x00FFFF00 482 | .long 0x00000000 483 | 484 | 485 | .long 0x00000000 486 | .long 0x0FFFFF00 487 | .long 0x0FF00FF0 488 | .long 0x0FF00FF0 489 | .long 0x0FFFFF00 490 | .long 0x0FF00000 491 | .long 0x0FF00000 492 | .long 0x00000000 493 | 494 | 495 | .long 0x00000000 496 | .long 0x00FFFF00 497 | .long 0x0FF00FF0 498 | .long 0x0FF00FF0 499 | .long 0x0FF00FF0 500 | .long 0x0FF0FF00 501 | .long 0x00FF0FF0 502 | .long 0x00000000 503 | 504 | 505 | .long 0x00000000 506 | .long 0x0FFFFF00 507 | .long 0x0FF00FF0 508 | .long 0x0FF00FF0 509 | .long 0x0FFFFF00 510 | .long 0x0FF0FF00 511 | .long 0x0FF00FF0 512 | .long 0x00000000 513 | 514 | 515 | .long 0x00000000 516 | .long 0x00FFFF00 517 | .long 0x0FF00000 518 | .long 0x00FFFF00 519 | .long 0x00000FF0 520 | .long 0x00000FF0 521 | .long 0x00FFFF00 522 | .long 0x00000000 523 | 524 | 525 | .long 0x00000000 526 | .long 0x0FFFFFF0 527 | .long 0x000FF000 528 | .long 0x000FF000 529 | .long 0x000FF000 530 | .long 0x000FF000 531 | .long 0x000FF000 532 | .long 0x00000000 533 | 534 | 535 | .long 0x00000000 536 | .long 0x0FF00FF0 537 | .long 0x0FF00FF0 538 | .long 0x0FF00FF0 539 | .long 0x0FF00FF0 540 | .long 0x0FF00FF0 541 | .long 0x0FFFFFF0 542 | .long 0x00000000 543 | 544 | 545 | .long 0x00000000 546 | .long 0x0FF00FF0 547 | .long 0x0FF00FF0 548 | .long 0x0FF00FF0 549 | .long 0x0FF00FF0 550 | .long 0x00FFFF00 551 | .long 0x000FF000 552 | .long 0x00000000 553 | 554 | 555 | .long 0x00000000 556 | .long 0x0FF000FF 557 | .long 0x0FF000FF 558 | .long 0x0FF0F0FF 559 | .long 0x0FFFFFFF 560 | .long 0x0FFF0FFF 561 | .long 0x0FF000FF 562 | .long 0x00000000 563 | 564 | 565 | .long 0x00000000 566 | .long 0x0FF00FF0 567 | .long 0x0FF00FF0 568 | .long 0x00FFFF00 569 | .long 0x00FFFF00 570 | .long 0x0FF00FF0 571 | .long 0x0FF00FF0 572 | .long 0x00000000 573 | 574 | 575 | .long 0x00000000 576 | .long 0x0FF00FF0 577 | .long 0x0FF00FF0 578 | .long 0x00FFFF00 579 | .long 0x000FF000 580 | .long 0x000FF000 581 | .long 0x000FF000 582 | .long 0x00000000 583 | 584 | 585 | .long 0x00000000 586 | .long 0x0FFFFFF0 587 | .long 0x0000FF00 588 | .long 0x000FF000 589 | .long 0x00FF0000 590 | .long 0x0FF00000 591 | .long 0x0FFFFFF0 592 | .long 0x00000000 593 | 594 | 595 | .long 0x00000000 596 | .long 0x000FFFF0 597 | .long 0x000FF000 598 | .long 0x000FF000 599 | .long 0x000FF000 600 | .long 0x000FF000 601 | .long 0x000FFFF0 602 | .long 0x00000000 603 | 604 | 605 | .long 0x00000000 606 | .long 0x0F000000 607 | .long 0x0FF00000 608 | .long 0x00FF0000 609 | .long 0x000FF000 610 | .long 0x0000FF00 611 | .long 0x00000FF0 612 | .long 0x00000000 613 | 614 | 615 | .long 0x00000000 616 | .long 0x0FFFF000 617 | .long 0x000FF000 618 | .long 0x000FF000 619 | .long 0x000FF000 620 | .long 0x000FF000 621 | .long 0x0FFFF000 622 | .long 0x00000000 623 | 624 | 625 | .long 0x00000000 626 | .long 0x0000F000 627 | .long 0x000FFF00 628 | .long 0x00FF0FF0 629 | .long 0x0FF000FF 630 | .long 0x00000000 631 | .long 0x00000000 632 | .long 0x00000000 633 | 634 | 635 | .long 0x00000000 636 | .long 0x00000000 637 | .long 0x00000000 638 | .long 0x00000000 639 | .long 0x00000000 640 | .long 0x00000000 641 | .long 0xFFFFFFFF 642 | .long 0x00000000 643 | 644 | 645 | .long 0x00000000 646 | .long 0x000FF000 647 | .long 0x00FFFF00 648 | .long 0x0FFFFFF0 649 | .long 0x0FFFFFF0 650 | .long 0x00FFFF00 651 | .long 0x000FF000 652 | .long 0x00000000 653 | 654 | 655 | .long 0x00000000 656 | .long 0x00000000 657 | .long 0x00FFFF00 658 | .long 0x00000FF0 659 | .long 0x00FFFFF0 660 | .long 0x0FF00FF0 661 | .long 0x00FFFFF0 662 | .long 0x00000000 663 | 664 | 665 | .long 0x00000000 666 | .long 0x0FF00000 667 | .long 0x0FF00000 668 | .long 0x0FFFFF00 669 | .long 0x0FF00FF0 670 | .long 0x0FF00FF0 671 | .long 0x0FFFFF00 672 | .long 0x00000000 673 | 674 | 675 | .long 0x00000000 676 | .long 0x00000000 677 | .long 0x00FFFF00 678 | .long 0x0FF00000 679 | .long 0x0FF00000 680 | .long 0x0FF00000 681 | .long 0x00FFFF00 682 | .long 0x00000000 683 | 684 | 685 | .long 0x00000000 686 | .long 0x00000FF0 687 | .long 0x00000FF0 688 | .long 0x00FFFFF0 689 | .long 0x0FF00FF0 690 | .long 0x0FF00FF0 691 | .long 0x00FFFFF0 692 | .long 0x00000000 693 | 694 | 695 | .long 0x00000000 696 | .long 0x00000000 697 | .long 0x00FFFF00 698 | .long 0x0FF00FF0 699 | .long 0x0FFFFFF0 700 | .long 0x0FF00000 701 | .long 0x00FFFF00 702 | .long 0x00000000 703 | 704 | 705 | .long 0x00000000 706 | .long 0x0000FFF0 707 | .long 0x000FF000 708 | .long 0x00FFFFF0 709 | .long 0x000FF000 710 | .long 0x000FF000 711 | .long 0x000FF000 712 | .long 0x00000000 713 | 714 | 715 | .long 0x00000000 716 | .long 0x00000000 717 | .long 0x00FFFFF0 718 | .long 0x0FF00FF0 719 | .long 0x0FF00FF0 720 | .long 0x00FFFFF0 721 | .long 0x00000FF0 722 | .long 0x0FFFFF00 723 | 724 | 725 | .long 0x00000000 726 | .long 0x0FF00000 727 | .long 0x0FF00000 728 | .long 0x0FFFFF00 729 | .long 0x0FF00FF0 730 | .long 0x0FF00FF0 731 | .long 0x0FF00FF0 732 | .long 0x00000000 733 | 734 | 735 | .long 0x00000000 736 | .long 0x000FF000 737 | .long 0x00000000 738 | .long 0x00FFF000 739 | .long 0x000FF000 740 | .long 0x000FF000 741 | .long 0x00FFFF00 742 | .long 0x00000000 743 | 744 | 745 | .long 0x00000000 746 | .long 0x00000FF0 747 | .long 0x00000000 748 | .long 0x00000FF0 749 | .long 0x00000FF0 750 | .long 0x00000FF0 751 | .long 0x00000FF0 752 | .long 0x00FFFF00 753 | 754 | 755 | .long 0x00000000 756 | .long 0x0FF00000 757 | .long 0x0FF00000 758 | .long 0x0FF0FF00 759 | .long 0x0FFFF000 760 | .long 0x0FF0FF00 761 | .long 0x0FF00FF0 762 | .long 0x00000000 763 | 764 | 765 | .long 0x00000000 766 | .long 0x00FFF000 767 | .long 0x000FF000 768 | .long 0x000FF000 769 | .long 0x000FF000 770 | .long 0x000FF000 771 | .long 0x00FFFF00 772 | .long 0x00000000 773 | 774 | 775 | .long 0x00000000 776 | .long 0x00000000 777 | .long 0x0FF00FF0 778 | .long 0x0FFFFFFF 779 | .long 0x0FFFFFFF 780 | .long 0x0FF0F0FF 781 | .long 0x0FF000FF 782 | .long 0x00000000 783 | 784 | 785 | .long 0x00000000 786 | .long 0x00000000 787 | .long 0x0FFFFF00 788 | .long 0x0FF00FF0 789 | .long 0x0FF00FF0 790 | .long 0x0FF00FF0 791 | .long 0x0FF00FF0 792 | .long 0x00000000 793 | 794 | 795 | .long 0x00000000 796 | .long 0x00000000 797 | .long 0x00FFFF00 798 | .long 0x0FF00FF0 799 | .long 0x0FF00FF0 800 | .long 0x0FF00FF0 801 | .long 0x00FFFF00 802 | .long 0x00000000 803 | 804 | 805 | .long 0x00000000 806 | .long 0x00000000 807 | .long 0x0FFFFF00 808 | .long 0x0FF00FF0 809 | .long 0x0FF00FF0 810 | .long 0x0FFFFF00 811 | .long 0x0FF00000 812 | .long 0x0FF00000 813 | 814 | 815 | .long 0x00000000 816 | .long 0x00000000 817 | .long 0x00FFFFF0 818 | .long 0x0FF00FF0 819 | .long 0x0FF00FF0 820 | .long 0x00FFFFF0 821 | .long 0x00000FF0 822 | .long 0x00000FF0 823 | 824 | 825 | .long 0x00000000 826 | .long 0x00000000 827 | .long 0x0FFFFF00 828 | .long 0x0FF00FF0 829 | .long 0x0FF00000 830 | .long 0x0FF00000 831 | .long 0x0FF00000 832 | .long 0x00000000 833 | 834 | 835 | .long 0x00000000 836 | .long 0x00000000 837 | .long 0x00FFFFF0 838 | .long 0x0FF00000 839 | .long 0x00FFFF00 840 | .long 0x00000FF0 841 | .long 0x0FFFFF00 842 | .long 0x00000000 843 | 844 | 845 | .long 0x00000000 846 | .long 0x000FF000 847 | .long 0x0FFFFFF0 848 | .long 0x000FF000 849 | .long 0x000FF000 850 | .long 0x000FF000 851 | .long 0x0000FFF0 852 | .long 0x00000000 853 | 854 | 855 | .long 0x00000000 856 | .long 0x00000000 857 | .long 0x0FF00FF0 858 | .long 0x0FF00FF0 859 | .long 0x0FF00FF0 860 | .long 0x0FF00FF0 861 | .long 0x00FFFFF0 862 | .long 0x00000000 863 | 864 | 865 | .long 0x00000000 866 | .long 0x00000000 867 | .long 0x0FF00FF0 868 | .long 0x0FF00FF0 869 | .long 0x0FF00FF0 870 | .long 0x00FFFF00 871 | .long 0x000FF000 872 | .long 0x00000000 873 | 874 | 875 | .long 0x00000000 876 | .long 0x00000000 877 | .long 0x0FF000FF 878 | .long 0x0FF0F0FF 879 | .long 0x0FFFFFFF 880 | .long 0x00FFFFF0 881 | .long 0x00FF0FF0 882 | .long 0x00000000 883 | 884 | 885 | .long 0x00000000 886 | .long 0x00000000 887 | .long 0x0FF00FF0 888 | .long 0x00FFFF00 889 | .long 0x000FF000 890 | .long 0x00FFFF00 891 | .long 0x0FF00FF0 892 | .long 0x00000000 893 | 894 | 895 | .long 0x00000000 896 | .long 0x00000000 897 | .long 0x0FF00FF0 898 | .long 0x0FF00FF0 899 | .long 0x0FF00FF0 900 | .long 0x00FFFFF0 901 | .long 0x0000FF00 902 | .long 0x0FFFF000 903 | 904 | 905 | .long 0x00000000 906 | .long 0x00000000 907 | .long 0x0FFFFFF0 908 | .long 0x0000FF00 909 | .long 0x000FF000 910 | .long 0x00FF0000 911 | .long 0x0FFFFFF0 912 | .long 0x00000000 913 | 914 | | 7B 915 | .long 0x00000000 916 | .long 0x0000FFF0 917 | .long 0x000FF000 918 | .long 0x000FF000 919 | .long 0x00FFF000 920 | .long 0x000FF000 921 | .long 0x0000FFF0 922 | .long 0x00000000 923 | 924 | | 7C 925 | .long 0x000FF000 926 | .long 0x000FF000 927 | .long 0x000FF000 928 | .long 0x000FF000 929 | .long 0x000FF000 930 | .long 0x000FF000 931 | .long 0x000FF000 932 | .long 0x000FF000 933 | 934 | | 7D 935 | .long 0x00000000 936 | .long 0x0FFF0000 937 | .long 0x000FF000 938 | .long 0x000FF000 939 | .long 0x000FFF00 940 | .long 0x000FF000 941 | .long 0x0FFF0000 942 | .long 0x00000000 943 | 944 | | 7E 945 | .long 0x00000000 946 | .long 0x00FFF0FF 947 | .long 0x0FF0FFF0 948 | .long 0x00000000 949 | .long 0x00000000 950 | .long 0x00000000 951 | .long 0x00000000 952 | .long 0x00000000 953 | 954 | | 7F 955 | .long 0x0000F000 956 | .long 0x000FF000 957 | .long 0x00FFF000 958 | .long 0x0FFFF000 959 | .long 0x00FFF000 960 | .long 0x000FF000 961 | .long 0x0000F000 962 | .long 0x00000000 963 | 964 | | 80 965 | .long 0x00000000 966 | .long 0x00000000 967 | .long 0x00000000 968 | .long 0x000FFFFF 969 | .long 0x000FFFFF 970 | .long 0x000FF000 971 | .long 0x000FF000 972 | .long 0x000FF000 973 | 974 | | 81 975 | .long 0x000FF000 976 | .long 0x000FF000 977 | .long 0x000FF000 978 | .long 0x000FFFFF 979 | .long 0x000FFFFF 980 | .long 0x000FF000 981 | .long 0x000FF000 982 | .long 0x000FF000 983 | 984 | | 82 985 | .long 0x00000000 986 | .long 0x00000000 987 | .long 0x00000000 988 | .long 0xFFFFFFFF 989 | .long 0xFFFFFFFF 990 | .long 0x00000000 991 | .long 0x00000000 992 | .long 0x00000000 993 | 994 | | 83 995 | .long 0x000FF000 996 | .long 0x000FF000 997 | .long 0x000FF000 998 | .long 0xFFFFF000 999 | .long 0xFFFFF000 1000 | .long 0x00000000 1001 | .long 0x00000000 1002 | .long 0x00000000 1003 | 1004 | | 84 1005 | .long 0x000FF000 1006 | .long 0x000FF000 1007 | .long 0x000FF000 1008 | .long 0xFFFFF000 1009 | .long 0xFFFFF000 1010 | .long 0x000FF000 1011 | .long 0x000FF000 1012 | .long 0x000FF000 1013 | 1014 | | 85 1015 | .long 0x00000000 1016 | .long 0x00000000 1017 | .long 0x00000000 1018 | .long 0xFFFFF000 1019 | .long 0xFFFFF000 1020 | .long 0x000FF000 1021 | .long 0x000FF000 1022 | .long 0x000FF000 1023 | 1024 | | 86 1025 | .long 0x000FF000 1026 | .long 0x000FF000 1027 | .long 0x000FF000 1028 | .long 0x000FFFFF 1029 | .long 0x000FFFFF 1030 | .long 0x00000000 1031 | .long 0x00000000 1032 | .long 0x00000000 1033 | 1034 | | 87 1035 | .long 0x000FF000 1036 | .long 0x000FF000 1037 | .long 0x000FF000 1038 | .long 0xFFFFFFFF 1039 | .long 0xFFFFFFFF 1040 | .long 0x000FF000 1041 | .long 0x000FF000 1042 | .long 0x000FF000 1043 | 1044 | | 88 1045 | .long 0x00000000 1046 | .long 0x00000000 1047 | .long 0x00000000 1048 | .long 0xFFFFFFFF 1049 | .long 0xFFFFFFFF 1050 | .long 0x000FF000 1051 | .long 0x000FF000 1052 | .long 0x000FF000 1053 | 1054 | | 89 1055 | .long 0x000FF000 1056 | .long 0x000FF000 1057 | .long 0x000FF000 1058 | .long 0xFFFFFFFF 1059 | .long 0xFFFFFFFF 1060 | .long 0x00000000 1061 | .long 0x00000000 1062 | .long 0x00000000 1063 | 1064 | | 8A 1065 | .long 0x00000000 1066 | .long 0x00000000 1067 | .long 0x00000000 1068 | .long 0x00000000 1069 | .long 0xFFFFFFFF 1070 | .long 0xFFFFFFFF 1071 | .long 0xFFFFFFFF 1072 | .long 0xFFFFFFFF 1073 | -------------------------------------------------------------------------------- /hw_md.h: -------------------------------------------------------------------------------- 1 | #ifndef _HW_MD_H 2 | #define _HW_MD_H 3 | 4 | #define SEGA_CTRL_BUTTONS 0x0FFF 5 | #define SEGA_CTRL_UP 0x0001 6 | #define SEGA_CTRL_DOWN 0x0002 7 | #define SEGA_CTRL_LEFT 0x0004 8 | #define SEGA_CTRL_RIGHT 0x0008 9 | #define SEGA_CTRL_B 0x0010 10 | #define SEGA_CTRL_C 0x0020 11 | #define SEGA_CTRL_A 0x0040 12 | #define SEGA_CTRL_START 0x0080 13 | #define SEGA_CTRL_Z 0x0100 14 | #define SEGA_CTRL_Y 0x0200 15 | #define SEGA_CTRL_X 0x0400 16 | #define SEGA_CTRL_MODE 0x0800 17 | 18 | #define SEGA_CTRL_TYPE 0xF000 19 | #define SEGA_CTRL_THREE 0x0000 20 | #define SEGA_CTRL_SIX 0x1000 21 | #define SEGA_CTRL_NONE 0xF000 22 | 23 | /* default text colors */ 24 | #define TEXT_WHITE 0x0000 25 | #define TEXT_GREEN 0x2000 26 | #define TEXT_RED 0x4000 27 | 28 | /* Z80 control flags */ 29 | #define Z80_BUS_REQUEST 0x0100 30 | #define Z80_BUS_RELEASE 0x0000 31 | #define Z80_ASSERT_RESET 0x0000 32 | #define Z80_CLEAR_RESET 0x0100 33 | 34 | #define GET_PAD(p) (*(volatile unsigned short *)(0xFF8018 + p*2)) 35 | #define GET_TICKS (*(volatile unsigned int *)0xFF801C) 36 | 37 | // Global Variable offsets - must match boot loader 38 | typedef struct 39 | { 40 | int VBLANK_HANDLER; 41 | int VBLANK_PARAM; 42 | int INIT_CD; 43 | int READ_CD; 44 | int SET_CWD; 45 | int FIRST_DIR_SEC; 46 | int NEXT_DIR_SEC; 47 | int FIND_DIR_ENTRY; 48 | int NEXT_DIR_ENTRY; 49 | int LOAD_FILE; 50 | short DISC_TYPE; 51 | short DIR_ENTRY; 52 | int CWD_OFFSET; 53 | int CWD_LENGTH; 54 | int CURR_OFFSET; 55 | int CURR_LENGTH; 56 | int ROOT_OFFSET; 57 | int ROOT_LENGTH; 58 | int DENTRY_OFFSET; 59 | int DENTRY_LENGTH; 60 | short DENTRY_FLAGS; 61 | char DENTRY_NAME[256]; 62 | char TEMP_NAME[256]; 63 | } globals_t; 64 | 65 | extern globals_t *global_vars; 66 | 67 | // CDFS Error codes - must match boot loader 68 | enum { 69 | ERR_READ_FAILED = -2, 70 | ERR_NO_PVD = -3, 71 | ERR_NO_MORE_ENTRIES = -4, 72 | ERR_BAD_ENTRY = -5, 73 | ERR_NAME_NOT_FOUND = -6, 74 | ERR_NO_DISC = -7 75 | }; 76 | 77 | // MD Hardware Calls 78 | enum { 79 | MD_CMD_INIT_HW = 1, 80 | MD_CMD_SET_SR, 81 | MD_CMD_GET_PAD, 82 | MD_CMD_CLEAR_B, 83 | MD_CMD_SET_VRAM, 84 | MD_CMD_NEXT_VRAM, 85 | MD_CMD_COPY_VRAM, 86 | MD_CMD_CLEAR_A, 87 | MD_CMD_PUT_STR, 88 | MD_CMD_PUT_CHR, 89 | MD_CMD_DELAY, 90 | MD_CMD_SET_PALETTE, 91 | MD_CMD_Z80_BUSREQUEST, 92 | MD_CMD_Z80_RESET, 93 | MD_CMD_Z80_MEMCLR, 94 | MD_CMD_Z80_MEMCPY, 95 | MD_CMD_DMA_SCREEN, 96 | MD_CMD_INIT_32X, 97 | MD_CMD_GET_COMM32X, 98 | MD_CMD_SET_COMM32X, 99 | MD_CMD_DMA_TO_32X, 100 | MD_CMD_CPY_TO_32X, 101 | MD_CMD_END 102 | }; 103 | 104 | #ifdef __cplusplus 105 | extern "C" { 106 | #endif 107 | 108 | extern int do_md_cmd0(int cmd); 109 | extern int do_md_cmd1(int cmd, int arg1); 110 | extern int do_md_cmd2(int cmd, int arg1, int arg2); 111 | extern int do_md_cmd3(int cmd, int arg1, int arg2, int arg3); 112 | extern int do_md_cmd4(int cmd, int arg1, int arg2, int arg3, int arg4); 113 | extern void send_md_cmd0(int cmd); 114 | extern void send_md_cmd1(int cmd, int arg1); 115 | extern void send_md_cmd2(int cmd, int arg1, int arg2); 116 | extern void send_md_cmd3(int cmd, int arg1, int arg2, int arg3); 117 | extern void send_md_cmd4(int cmd, int arg1, int arg2, int arg3, int arg4); 118 | extern int wait_md_cmd(void); 119 | extern void switch_banks(void); 120 | extern void sub_delay(int cnt); 121 | 122 | extern int init_cd(void); 123 | extern int read_cd(int lba, int len, void *buffer); 124 | extern int set_cwd(char *path); 125 | extern int first_dir_sec(void); 126 | extern int next_dir_sec(void); 127 | extern int find_dir_entry(char *name); 128 | extern int next_dir_entry(void); 129 | extern int load_file(char *filename, void *buffer); 130 | 131 | #ifdef __cplusplus 132 | } 133 | #endif 134 | 135 | #endif 136 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "hw_md.h" 7 | #include "cdfh.h" 8 | #include "module.h" 9 | 10 | #define MAX_ENTRIES 256 11 | 12 | typedef struct 13 | { 14 | int offset; 15 | int length; 16 | char flags; 17 | char name[247]; 18 | } entry_t; 19 | 20 | entry_t gDirTable[MAX_ENTRIES]; 21 | 22 | short gCount = 0; 23 | short gFirst = 0; 24 | short gCurrent = 0; 25 | short gFilter = 1; 26 | short gStage = 0; 27 | 28 | char *temp = (char *)0x0C0000; 29 | 30 | static char *flt_strings[] = { 31 | "running average ", 32 | "sample and hold ", 33 | "exponential dropoff", 34 | }; 35 | 36 | static char *err_strings[] = { 37 | "", 38 | "couldn't load MOD header", 39 | "unrecognized or wrong # channels", 40 | "couldn't allocate pattern buffer", 41 | "couldn't read pattern data" 42 | }; 43 | 44 | //---------------------------------------------------------------------- 45 | 46 | int getCurrDir(void) 47 | { 48 | int r = 0; 49 | int i = 0; 50 | 51 | while (!r) 52 | { 53 | r = next_dir_entry(); 54 | if (!r && memcmp(global_vars->DENTRY_NAME, ".", 2)) 55 | { 56 | gDirTable[i].offset = global_vars->DENTRY_OFFSET; 57 | gDirTable[i].length = global_vars->DENTRY_LENGTH; 58 | gDirTable[i].flags = global_vars->DENTRY_FLAGS >> 8; 59 | strcpy(gDirTable[i].name, global_vars->DENTRY_NAME); 60 | i++; 61 | } 62 | 63 | if (r == ERR_NO_MORE_ENTRIES) 64 | r = next_dir_sec(); 65 | } 66 | 67 | return i; 68 | } 69 | 70 | int main(void) 71 | { 72 | int i, j; 73 | unsigned short buttons, changed, previous = 0; 74 | Mod_t module; 75 | CDFileHandle_t *handle; 76 | 77 | strcpy(temp, "MOD Player"); 78 | switch_banks(); 79 | do_md_cmd4(MD_CMD_PUT_STR, 0x200000, TEXT_WHITE, 15, 1); 80 | 81 | strcpy(temp, "Resample filter: "); 82 | strcat(temp, flt_strings[gFilter]); 83 | switch_banks(); 84 | do_md_cmd4(MD_CMD_PUT_STR, 0x200000, TEXT_GREEN, 1, 25); 85 | 86 | init_cd(); // init CD 87 | set_cwd("/"); // set current directory to root 88 | gCount = getCurrDir(); 89 | 90 | gStage = 0; 91 | 92 | while (1) 93 | { 94 | 95 | if (gStage == 0) 96 | { 97 | 98 | char *p = temp; 99 | 100 | // update display with directory entries 101 | memset(p, 0, 20*64*2); 102 | if (gCount) 103 | for (i = 0; i < 20; i++) 104 | { 105 | if ((gFirst + i) >= gCount) 106 | break; 107 | 108 | p = &temp[i*64*2 + 2]; // left justified 109 | if (gDirTable[gFirst + i].flags & 2) 110 | { 111 | // directory 112 | *p++ = ((gFirst + i) == gCurrent) ? TEXT_WHITE >> 8 : TEXT_GREEN >> 8; 113 | *p++ = '[' - 0x20; 114 | for (j = 0; j < 36; j++) 115 | { 116 | if (gDirTable[gFirst + i].name[j] == '\0') 117 | break; 118 | *p++ = ((gFirst + i) == gCurrent) ? TEXT_WHITE >> 8 : TEXT_GREEN >> 8; 119 | *p++ = gDirTable[gFirst + i].name[j] - 0x20; 120 | } 121 | *p++ = ((gFirst + i) == gCurrent) ? TEXT_WHITE >> 8 : TEXT_GREEN >> 8; 122 | *p++ = ']' - 0x20; 123 | } 124 | else 125 | { 126 | for (j = 0; j < 38; j++) 127 | { 128 | if (gDirTable[gFirst + i].name[j] == '\0') 129 | break; 130 | *p++ = ((gFirst + i) == gCurrent) ? TEXT_WHITE >> 8 : TEXT_GREEN >> 8; 131 | *p++ = gDirTable[gFirst + i].name[j] - 0x20; 132 | } 133 | } 134 | } 135 | switch_banks(); 136 | sub_delay(1); // wait until vblank to update vram 137 | i = do_md_cmd3(MD_CMD_COPY_VRAM, 0xC000 + 3*64*2, 0x200000, 20*64); // update plane A name table (20 lines of text) 138 | if (i < 0) 139 | { 140 | if (i == -1) 141 | strcpy(temp, "unknown command"); 142 | else if (i == -2) 143 | strcpy(temp, "wait cmd done timeout"); 144 | else if (i == -3) 145 | strcpy(temp, "wait cmd ack timeout"); 146 | else 147 | strcpy(temp, "unknown error"); 148 | switch_banks(); 149 | do_md_cmd4(MD_CMD_PUT_STR, 0x200000, TEXT_RED, 1, 24); 150 | sub_delay(120); 151 | strcpy(temp, " "); 152 | switch_banks(); 153 | do_md_cmd4(MD_CMD_PUT_STR, 0x200000, TEXT_GREEN, 1, 24); 154 | } 155 | 156 | sub_delay(7); 157 | 158 | gStage = 1; 159 | 160 | } 161 | else if (gStage == 1) 162 | { 163 | 164 | buttons = GET_PAD(0); 165 | 166 | changed = (buttons & SEGA_CTRL_BUTTONS) ^ previous; 167 | 168 | if (buttons & SEGA_CTRL_DOWN) 169 | { 170 | // DOWN pressed 171 | gCurrent++; 172 | if ((gCurrent - gFirst) >= 20) 173 | gFirst = gCurrent; 174 | 175 | if (gCurrent >= gCount) 176 | gFirst = gCurrent = 0; // wrap around to first entry 177 | 178 | gStage = 0; 179 | 180 | } 181 | 182 | if (buttons & SEGA_CTRL_UP) 183 | { 184 | 185 | // UP pressed 186 | gCurrent--; 187 | if (gCurrent < gFirst) 188 | { 189 | gFirst = gCurrent - 19; 190 | if (gFirst < 0) 191 | gFirst = 0; 192 | } 193 | 194 | if (gCurrent < 0) 195 | { 196 | gCurrent = gCount - 1; // wrap around to last entry 197 | gFirst = gCurrent - 19; 198 | if (gFirst < 0) 199 | gFirst = 0; 200 | } 201 | 202 | gStage = 0; 203 | 204 | } 205 | 206 | if (!changed) 207 | continue; // no change in buttons 208 | previous = buttons & SEGA_CTRL_BUTTONS; 209 | 210 | if (changed & SEGA_CTRL_A) 211 | { 212 | if (!(buttons & SEGA_CTRL_A)) 213 | { 214 | // A just released 215 | if (gDirTable[gCurrent].flags & 2) 216 | { 217 | // directory 218 | global_vars->CWD_OFFSET = gDirTable[gCurrent].offset; 219 | global_vars->CWD_LENGTH = gDirTable[gCurrent].length; 220 | global_vars->CURR_OFFSET = -1; 221 | first_dir_sec(); 222 | gCount = getCurrDir(); 223 | gFirst = gCurrent = 0; 224 | gStage = 0; 225 | } 226 | else 227 | { 228 | 229 | sprintf(temp, "Loading Mod\n"); 230 | switch_banks(); 231 | do_md_cmd4(MD_CMD_PUT_STR, 0x200000, TEXT_GREEN, 1, 24); 232 | 233 | handle = cd_handle_from_offset(gDirTable[gCurrent].offset, gDirTable[gCurrent].length); 234 | 235 | if (handle == NULL) 236 | { 237 | sprintf(temp, "failed to create cd handle\n"); 238 | switch_banks(); 239 | do_md_cmd4(MD_CMD_PUT_STR, 0x200000, TEXT_RED, 1, 24); 240 | while (1) ; 241 | } 242 | 243 | /* load module */ 244 | i = InitMOD(handle, &module, gFilter); 245 | 246 | if (!i) 247 | { 248 | /* start module */ 249 | sprintf(temp, "Playing %s - %d chan", module.Title, module.NumberOfChannels); 250 | switch_banks(); 251 | do_md_cmd4(MD_CMD_PUT_STR, 0x200000, TEXT_GREEN, 1, 24); 252 | 253 | StartMOD(&module, 0); 254 | sub_delay(20); 255 | while (CheckMOD(&module)) 256 | { 257 | sub_delay(2); 258 | buttons = GET_PAD(0); 259 | changed = (buttons & SEGA_CTRL_BUTTONS) ^ previous; 260 | if (!changed) 261 | continue; // no change in buttons 262 | previous = buttons & SEGA_CTRL_BUTTONS; 263 | if (changed & SEGA_CTRL_C) 264 | if (!(buttons & SEGA_CTRL_C)) 265 | break; // C just released - stop playing 266 | } 267 | StopMOD(&module); 268 | ExitMOD(&module); 269 | delete_cd_handle(handle); 270 | 271 | strcpy(temp, " "); 272 | switch_banks(); 273 | do_md_cmd4(MD_CMD_PUT_STR, 0x200000, TEXT_GREEN, 1, 24); 274 | } 275 | else 276 | { 277 | sprintf(temp, "Error: %s\n", err_strings[i]); 278 | switch_banks(); 279 | do_md_cmd4(MD_CMD_PUT_STR, 0x200000, TEXT_RED, 1, 24); 280 | sub_delay(300); 281 | strcpy(temp, " "); 282 | switch_banks(); 283 | do_md_cmd4(MD_CMD_PUT_STR, 0x200000, TEXT_GREEN, 1, 24); 284 | } 285 | } 286 | } 287 | } 288 | 289 | if (changed & SEGA_CTRL_MODE) 290 | { 291 | if (!(buttons & SEGA_CTRL_MODE)) 292 | { 293 | // MODE just released 294 | gFilter = gFilter < 2 ? gFilter + 1: 0; 295 | strcpy(temp, "Resample filter: "); 296 | strcat(temp, flt_strings[gFilter]); 297 | switch_banks(); 298 | do_md_cmd4(MD_CMD_PUT_STR, 0x200000, TEXT_GREEN, 1, 25); 299 | } 300 | } 301 | 302 | } 303 | 304 | } 305 | 306 | return 0; 307 | } 308 | 309 | -------------------------------------------------------------------------------- /mod-info: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/matteusbeus/ModPlayer/621f5a07b7d94ed91751b828e928b31dfc1d7a56/mod-info -------------------------------------------------------------------------------- /module-exp.c: -------------------------------------------------------------------------------- 1 | /* 2 | SegaCD MOD support code by Chilly Willy, based on 3 | 4 | Ami-PlayMOD_V1_0_20090417.c (c) 2009 Massimiliano Scarano mscarano@libero.it 5 | 6 | which had the following restriction: 7 | If you use the source code, you should always mention me as the original author. 8 | */ 9 | 10 | 11 | /* Includes ==================================================================================== */ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "hw_md.h" 19 | #include "cdfh.h" 20 | #include "pcm.h" 21 | #include "module.h" 22 | 23 | 24 | /* Defines ===================================================================================== */ 25 | 26 | /* define if handle 6 or 8 channel mods */ 27 | #define HANDLE_EXTRA_CHANNELS 28 | /* define if handle panning */ 29 | //#define HANDLE_PANNING 30 | /* define if want a little cross-talk between sides (only if no panning) */ 31 | //#define CROSS_TALK 3 32 | 33 | 34 | /* Function prototypes ========================================================================= */ 35 | 36 | /* 37 | Called by sound sub-system at 50 Hz (or BPM * 2 / 5) 38 | returns 1 if not playing 39 | */ 40 | uint16_t PlayMOD(Mod_t* Mod_p); 41 | 42 | static void UpdateNote(Mod_t* Mod_p, uint8_t* Data_p); 43 | static void UpdateEffect(Mod_t* Mod_p); 44 | static void DoVibrato(Mod_t* Mod_p, uint8_t Channel); 45 | static void DoTremolo(Mod_t* Mod_p, uint8_t Channel); 46 | static void DoPorta(Mod_t* Mod_p, uint8_t Channel); 47 | 48 | /* 49 | System specific functions 50 | */ 51 | static Mod_t* SetMOD(Mod_t* Mod_p); 52 | static void SetSample(uint16_t Channel, uint8_t Sample, uint32_t SampleOffset); 53 | static void SetFrequency(uint16_t Channel, uint32_t Period); 54 | static void SetVolume(uint16_t Channel, uint32_t Volume, int32_t Pan); 55 | 56 | 57 | /* Global variables ============================================================================ */ 58 | 59 | /* 60 | From Amiga ProTracker playroutine source code 61 | 62 | - Amiga PERIODS represent delay times before fetching the next sample (in units of system clock ticks @ ~3.5 MHz) 63 | */ 64 | static const uint16_t AmigaPeriodsTable[ MOD_FINE_TUNE_VALUES ][ MOD_NUMBER_OF_NOTES ] = 65 | { 66 | /* ; Tuning 0, Normal */ 67 | { 856,808,762,720,678,640,604,570,538,508,480,453, /* ; C-1 to B-1 */ 68 | 428,404,381,360,339,320,302,285,269,254,240,226, /* ; C-2 to B-2 */ 69 | 214,202,190,180,170,160,151,143,135,127,120,113 }, /* ; C-3 to B-3 */ 70 | /* ; Tuning 1 */ 71 | { 850,802,757,715,674,637,601,567,535,505,477,450, /* ; same as above */ 72 | 425,401,379,357,337,318,300,284,268,253,239,225, /* ; but with */ 73 | 213,201,189,179,169,159,150,142,134,126,119,113 }, /* ; finetune +1 */ 74 | /* ; Tuning 2 */ 75 | { 844,796,752,709,670,632,597,563,532,502,474,447, /* ; etc, */ 76 | 422,398,376,355,335,316,298,282,266,251,237,224, /* ; finetune +2 */ 77 | 211,199,188,177,167,158,149,141,133,125,118,112 }, 78 | /* ; Tuning 3 */ 79 | { 838,791,746,704,665,628,592,559,528,498,470,444, 80 | 419,395,373,352,332,314,296,280,264,249,235,222, 81 | 209,198,187,176,166,157,148,140,132,125,118,111 }, 82 | /* ; Tuning 4 */ 83 | { 832,785,741,699,660,623,588,555,524,495,467,441, 84 | 416,392,370,350,330,312,294,278,262,247,233,220, 85 | 208,196,185,175,165,156,147,139,131,124,117,110 }, 86 | /* ; Tuning 5 */ 87 | { 826,779,736,694,655,619,584,551,520,491,463,437, 88 | 413,390,368,347,328,309,292,276,260,245,232,219, 89 | 206,195,184,174,164,155,146,138,130,123,116,109 }, 90 | /* ; Tuning 6 */ 91 | { 820,774,730,689,651,614,580,547,516,487,460,434, 92 | 410,387,365,345,325,307,290,274,258,244,230,217, 93 | 205,193,183,172,163,154,145,137,129,122,115,109 }, 94 | /* ; Tuning 7 */ 95 | { 814,768,725,684,646,610,575,543,513,484,457,431, 96 | 407,384,363,342,323,305,288,272,256,242,228,216, 97 | 204,192,181,171,161,152,144,136,128,121,114,108 }, 98 | /* ; Tuning -8 */ 99 | { 907,856,808,762,720,678,640,604,570,538,508,480, 100 | 453,428,404,381,360,339,320,302,285,269,254,240, 101 | 226,214,202,190,180,170,160,151,143,135,127,120 }, 102 | /* ; Tuning -7 */ 103 | { 900,850,802,757,715,675,636,601,567,535,505,477, 104 | 450,425,401,379,357,337,318,300,284,268,253,238, 105 | 225,212,200,189,179,169,159,150,142,134,126,119 }, 106 | /* ; Tuning -6 */ 107 | { 894,844,796,752,709,670,632,597,563,532,502,474, 108 | 447,422,398,376,355,335,316,298,282,266,251,237, 109 | 223,211,199,188,177,167,158,149,141,133,125,118 }, 110 | /* ; Tuning -5 */ 111 | { 887,838,791,746,704,665,628,592,559,528,498,470, 112 | 444,419,395,373,352,332,314,296,280,264,249,235, 113 | 222,209,198,187,176,166,157,148,140,132,125,118 }, 114 | /* ; Tuning -4 */ 115 | { 881,832,785,741,699,660,623,588,555,524,494,467, 116 | 441,416,392,370,350,330,312,294,278,262,247,233, 117 | 220,208,196,185,175,165,156,147,139,131,123,117 }, 118 | /* ; Tuning -3 */ 119 | { 875,826,779,736,694,655,619,584,551,520,491,463, 120 | 437,413,390,368,347,328,309,292,276,260,245,232, 121 | 219,206,195,184,174,164,155,146,138,130,123,116 }, 122 | /* ; Tuning -2 */ 123 | { 868,820,774,730,689,651,614,580,547,516,487,460, 124 | 434,410,387,365,345,325,307,290,274,258,244,230, 125 | 217,205,193,183,172,163,154,145,137,129,122,115 }, 126 | /* ; Tuning -1 */ 127 | { 862,814,768,725,684,646,610,575,543,513,484,457, 128 | 431,407,384,363,342,323,305,288,272,256,242,228, 129 | 216,203,192,181,171,161,152,144,136,128,121,114 } 130 | }; 131 | 132 | 133 | /* 134 | From Amiga ProTracker playroutine source code 135 | 136 | for Vibrato and Tremolo 137 | */ 138 | static const uint8_t SineTable[ 32 ] = 139 | { 140 | 0, 24, 49, 74, 97, 120, 141, 161, 141 | 180, 197, 212, 224, 235, 244, 250, 253, 142 | 255, 253, 250, 244, 235, 224, 212, 197, 143 | 180, 161, 141, 120, 97, 74, 49, 24 144 | }; 145 | 146 | 147 | /* 148 | Global volume for playing Modules 149 | */ 150 | static uint8_t gVolume = 16; 151 | 152 | static Mod_t *gMod = NULL; 153 | 154 | typedef struct voice { 155 | uint8_t pending; 156 | uint8_t vol; 157 | uint8_t pan; 158 | uint8_t start; 159 | uint16_t offset; 160 | uint16_t loop; 161 | uint16_t period; 162 | } voice_t; 163 | 164 | #define PENDING_START 1 165 | #define PENDING_STOP 2 166 | #define PENDING_VOL 4 167 | #define PENDING_PER 8 168 | 169 | voice_t voices[8]; 170 | 171 | static void callback(void); 172 | 173 | 174 | /* Module handler ============================================================================== */ 175 | 176 | /* 177 | Initialize Module structure 178 | */ 179 | uint8_t InitMOD(CDFileHandle_t *File_p, Mod_t* Mod_p) 180 | { 181 | const uint8_t ModSignature[ MOD_SIGNATURE_LENGTH ] = {'M', '.', 'K', '.'}; 182 | const uint8_t AltSignature[ MOD_SIGNATURE_LENGTH ] = {'4', 'C', 'H', 'N'}; 183 | #ifdef HANDLE_EXTRA_CHANNELS 184 | const uint8_t Mod6Signature[ MOD_SIGNATURE_LENGTH ] = {'6', 'C', 'H', 'N'}; 185 | const uint8_t Mod8Signature[ MOD_SIGNATURE_LENGTH ] = {'8', 'C', 'H', 'N'}; 186 | #endif 187 | uint8_t Cnt, Iw, Ix, Iy; 188 | uint8_t Sample; 189 | uint8_t Order; 190 | uint32_t Soffset, Iz; 191 | uint8_t *Data_p = (uint8_t *)0x0C0000; /* use word ram for MOD processing */ 192 | 193 | memset( Mod_p, 0, sizeof(Mod_t) ); /* main data structure set to 0 */ 194 | 195 | /* load MOD header from CD */ 196 | if (File_p->Read(File_p, Data_p, 1084) != 1084) 197 | return 1; /* couldn't load MOD header */ 198 | 199 | /* check signature */ 200 | for ( Cnt = 0; Cnt < MOD_SIGNATURE_LENGTH; Cnt++ ) 201 | { 202 | if ( ModSignature[ Cnt ] != Data_p[ Cnt + MOD_SIGNATURE_OFFSET ] ) 203 | { 204 | break; /* no match */ 205 | } 206 | } 207 | if ( MOD_SIGNATURE_LENGTH == Cnt ) 208 | { 209 | Mod_p->NumberOfChannels = 4; /* we have a 4 channel mod */ 210 | } 211 | 212 | if ( !Mod_p->NumberOfChannels ) 213 | { 214 | /* check for alternate 4 channel signature */ 215 | for ( Cnt = 0; Cnt < MOD_SIGNATURE_LENGTH; Cnt++ ) 216 | { 217 | if ( AltSignature[ Cnt ] != Data_p[ Cnt + MOD_SIGNATURE_OFFSET ] ) 218 | { 219 | break; /* no match */ 220 | } 221 | } 222 | if ( MOD_SIGNATURE_LENGTH == Cnt ) 223 | { 224 | Mod_p->NumberOfChannels = 4; /* we have a 4 channel mod */ 225 | } 226 | } 227 | 228 | #ifdef HANDLE_EXTRA_CHANNELS 229 | if ( !Mod_p->NumberOfChannels ) 230 | { 231 | /* check for 6 channel signature */ 232 | for ( Cnt = 0; Cnt < MOD_SIGNATURE_LENGTH; Cnt++ ) 233 | { 234 | if ( Mod6Signature[ Cnt ] != Data_p[ Cnt + MOD_SIGNATURE_OFFSET ] ) 235 | { 236 | break; /* no match */ 237 | } 238 | } 239 | if ( MOD_SIGNATURE_LENGTH == Cnt ) 240 | { 241 | Mod_p->NumberOfChannels = 6; /* we have a 6 channel mod */ 242 | } 243 | } 244 | 245 | if ( !Mod_p->NumberOfChannels ) 246 | { 247 | /* check for 8 channel signature */ 248 | for ( Cnt = 0; Cnt < MOD_SIGNATURE_LENGTH; Cnt++ ) 249 | { 250 | if ( Mod8Signature[ Cnt ] != Data_p[ Cnt + MOD_SIGNATURE_OFFSET ] ) 251 | { 252 | break; /* no match */ 253 | } 254 | } 255 | if ( MOD_SIGNATURE_LENGTH == Cnt ) 256 | { 257 | Mod_p->NumberOfChannels = 8; /* we have a 8 channel mod */ 258 | } 259 | } 260 | #endif 261 | 262 | if ( !Mod_p->NumberOfChannels ) 263 | { 264 | return 2; /* not recognized, or not supported number of channels */ 265 | } 266 | 267 | for ( Sample = 0; Sample < MOD_NUMBER_OF_SAMPLES; Sample++ ) 268 | { 269 | uint8_t Byte1, Byte2; 270 | 271 | Byte1 = Data_p[ 20 + 22 + 30 * Sample ]; 272 | Byte2 = Data_p[ 20 + 23 + 30 * Sample ]; 273 | Mod_p->Inst[ Sample ].SampleLength = ( (Byte1 * 0x0100) + Byte2 ) * 2; 274 | 275 | Mod_p->Inst[ Sample ].FineTune = Data_p[ 20 + 24 + 30 * Sample ]; 276 | 277 | Mod_p->Inst[ Sample ].Volume = Data_p[ 20 + 25 + 30 * Sample ]; 278 | 279 | Byte1 = Data_p[ 20 + 26 + 30 * Sample ]; 280 | Byte2 = Data_p[ 20 + 27 + 30 * Sample ]; 281 | Mod_p->Inst[ Sample ].LoopStart = ( (Byte1 * 0x0100) + Byte2 ) * 2; 282 | 283 | Byte1 = Data_p[ 20 + 28 + 30 * Sample ]; 284 | Byte2 = Data_p[ 20 + 29 + 30 * Sample ]; 285 | Mod_p->Inst[ Sample ].LoopLength = ( (Byte1 * 0x0100) + Byte2 ) * 2; 286 | } /* for Sample */ 287 | 288 | Mod_p->SongLength = Data_p[ 950 ]; /* this is the number of orders in a song */ 289 | 290 | /* get NumberOfPatterns */ 291 | Mod_p->NumberOfPatterns = 0; 292 | for ( Order = 0; Order < MOD_NUMBER_OF_ORDERS; Order++ ) 293 | { 294 | Mod_p->Orders[ Order ] = Data_p[ 952 + Order ]; 295 | if ( Mod_p->Orders[ Order ] > Mod_p->NumberOfPatterns ) 296 | { 297 | Mod_p->NumberOfPatterns = Mod_p->Orders[ Order ]; 298 | } 299 | } /* for Order */ 300 | 301 | Mod_p->NumberOfPatterns += 1; /* [ 0 ; Mod_p->NumberOfPatterns ] */ 302 | if (Mod_p->NumberOfPatterns * MOD_ROWS_PER_CHANNEL * Mod_p->NumberOfChannels * 4 > 131072) 303 | return 3; /* pattern data too big */ 304 | 305 | /* allocate memory for patterns */ 306 | Mod_p->PatternsBuff_p = (uint8_t*)malloc(Mod_p->NumberOfPatterns * MOD_ROWS_PER_CHANNEL * Mod_p->NumberOfChannels * 6); 307 | if (!Mod_p->PatternsBuff_p) 308 | return 4; /* couldn't allocate pattern buffer */ 309 | 310 | /* Pattern data starts at offset 1084 - read and decode into pattern buffer */ 311 | if ( File_p->Read(File_p, Data_p, Mod_p->NumberOfPatterns * MOD_ROWS_PER_CHANNEL * Mod_p->NumberOfChannels * 4) != 312 | (Mod_p->NumberOfPatterns * MOD_ROWS_PER_CHANNEL * Mod_p->NumberOfChannels * 4) ) 313 | { 314 | free(Mod_p->PatternsBuff_p); 315 | return 5; /* couldn't read pattern data */ 316 | } 317 | 318 | Iz = 0; 319 | for ( Ix = 0; Ix < Mod_p->NumberOfPatterns; Ix++ ) 320 | { 321 | for ( Iy = 0; Iy < MOD_ROWS_PER_CHANNEL; Iy++ ) 322 | { 323 | uint32_t *Div_p = (uint32_t *)&Data_p[(Ix * MOD_ROWS_PER_CHANNEL * Mod_p->NumberOfChannels * 4) + (Iy * Mod_p->NumberOfChannels * 4)]; 324 | uint8_t Channel; 325 | for ( Channel = 0; Channel < Mod_p->NumberOfChannels; Channel++ ) 326 | { 327 | uint32_t division; 328 | uint8_t SampleNumber; 329 | uint8_t EffectNumber; 330 | uint8_t EffectParameter; 331 | uint8_t NoteColumn; 332 | uint16_t PeriodFrequency; 333 | 334 | /* Amiga-fied mess ;-) */ 335 | division = Div_p[ Channel ]; 336 | /* 337 | +-------------------------------------+ 338 | | Byte 0 Byte 1 Byte 2 Byte 3 | 339 | |-------------------------------------| 340 | |aaaaBBBB CCCCCCCCC DDDDeeee FFFFFFFFF| 341 | +-------------------------------------+ 342 | aaaaDDDD = sample number 343 | BBBBCCCCCCCC = sample period value 344 | eeee = effect number 345 | FFFFFFFF = effect parameters 346 | */ 347 | SampleNumber = ((division >> 24) & 0x00F0) | ((division >> 12) & 0x000F); 348 | PeriodFrequency = ((division >> 16) & 0x0FFF); 349 | EffectNumber = (division >> 8) & 0x000F; 350 | EffectParameter = division & 0x00FF; 351 | NoteColumn = 0xFF; // note doesn't exist 352 | /* look up table here */ 353 | for ( Cnt = 0; Cnt < MOD_NUMBER_OF_NOTES; Cnt++ ) 354 | { 355 | if ( AmigaPeriodsTable[ 0 ][ Cnt ] == Mod_p->Notes[ Channel ].PeriodFrequency ) 356 | { 357 | NoteColumn = Cnt; // note exists 358 | break; 359 | } 360 | } /* for Cnt */ 361 | Mod_p->PatternsBuff_p[Iz + 0] = SampleNumber; 362 | Mod_p->PatternsBuff_p[Iz + 1] = EffectNumber; 363 | Mod_p->PatternsBuff_p[Iz + 2] = EffectParameter; 364 | Mod_p->PatternsBuff_p[Iz + 3] = NoteColumn; 365 | Mod_p->PatternsBuff_p[Iz + 4] = PeriodFrequency >> 8; 366 | Mod_p->PatternsBuff_p[Iz + 5] = PeriodFrequency & 0x00FF; 367 | Iz += 6; 368 | } 369 | } 370 | } 371 | 372 | /* fit samples to sound ram */ 373 | Iz = 0; 374 | do 375 | { 376 | if (Iz > MOD_PCM_RAM) 377 | { 378 | // find largest sample and cut it in half 379 | Iy = 0; 380 | Iz = Mod_p->Inst[ 0 ].SampleLength >> Mod_p->Inst[ 0 ].Factor; 381 | for ( Sample = 1; Sample < MOD_NUMBER_OF_SAMPLES; Sample++ ) 382 | if ( (Mod_p->Inst[ Sample ].SampleLength >> Mod_p->Inst[ Sample ].Factor) > Iz ) 383 | { 384 | Iy = Sample; 385 | Iz = Mod_p->Inst[ Sample ].SampleLength >> Mod_p->Inst[ Sample ].Factor; 386 | } 387 | Mod_p->Inst[ Iy ].Factor++; 388 | } 389 | 390 | Iz = 0; 391 | /* find total size of samples given factors */ 392 | for ( Sample = 0; Sample < MOD_NUMBER_OF_SAMPLES; Sample++ ) 393 | Iz += ((Mod_p->Inst[ Sample ].SampleLength >> Mod_p->Inst[ Sample ].Factor) + 32 + 255) & ~255; 394 | } while (Iz > MOD_PCM_RAM); 395 | 396 | /* upload samples to sound ram */ 397 | pcm_reset(); 398 | for ( Sample = 0, Iw = 0; Sample < MOD_NUMBER_OF_SAMPLES; Sample++ ) 399 | { 400 | if ( Mod_p->Inst[ Sample ].SampleLength > 0 ) 401 | { 402 | /* load sample data */ 403 | if (Mod_p->Inst[ Sample ].Factor) 404 | { 405 | /* resample data */ 406 | Iy = 0; 407 | Soffset = Mod_p->Inst[ Sample ].SampleLength; 408 | while (Soffset > 0) 409 | { 410 | for (Ix = 0, Iz = 0; (Ix < (1 << Mod_p->Inst[ Sample ].Factor)) && Soffset; Ix++, Soffset--) 411 | Iz += File_p->Get(File_p); 412 | Data_p[Iy++] = Iz >> Mod_p->Inst[ Sample ].Factor; 413 | } 414 | } 415 | else 416 | File_p->Read(File_p, Data_p, Mod_p->Inst[ Sample ].SampleLength); 417 | 418 | Mod_p->Inst[ Sample ].SampleHandle = Iw; 419 | pcm_load_samples(Iw, (int8_t*)Data_p, Mod_p->Inst[ Sample ].SampleLength >> Mod_p->Inst[ Sample ].Factor); 420 | Iw = pcm_next_block(Iw, Mod_p->Inst[ Sample ].SampleLength >> Mod_p->Inst[ Sample ].Factor); 421 | } 422 | else 423 | { 424 | Mod_p->Inst[ Sample ].SampleHandle = 0xFF; 425 | } 426 | } /* for Sample */ 427 | 428 | memset(voices, 0, sizeof(voices)); 429 | 430 | Mod_p->Speed = 6; /* default speed */ 431 | Mod_p->Tick = 6; /* force immediate processing */ 432 | Mod_p->Order = 0; /* default order */ 433 | Mod_p->Row = 0; /* default row */ 434 | Mod_p->BeatsPerMinute = 125; 435 | Mod_p->IsPlaying = 0; 436 | 437 | return 0; /* no errors */ 438 | } 439 | 440 | 441 | /* 442 | Cleanup MOD - stop playing and release voices 443 | */ 444 | void ExitMOD(Mod_t* Mod_p) 445 | { 446 | if (Mod_p) 447 | { 448 | if (Mod_p->IsPlaying) 449 | StopMOD(Mod_p); 450 | 451 | /* quiet sound channels */ 452 | pcm_reset(); 453 | 454 | free(Mod_p->PatternsBuff_p); 455 | 456 | /* make sure struct cannot be reused without init */ 457 | memset(Mod_p, 0, sizeof(Mod_t)); 458 | } 459 | } 460 | 461 | 462 | /* 463 | Start playing Module 464 | loop = bool = TRUE = loop forever, = FALSE = play once 465 | returns the previously set Module, if any 466 | */ 467 | Mod_t* StartMOD(Mod_t* Mod_p, uint8_t loop) 468 | { 469 | if (Mod_p) 470 | { 471 | pcm_start_timer(&callback); 472 | Mod_p->IsPlaying = loop ? 0x81 : 1; 473 | return SetMOD(Mod_p); 474 | } 475 | return (Mod_t*)NULL; 476 | } 477 | 478 | 479 | /* 480 | Pause/resume Module 481 | pause = bool = TRUE = pause, = FALSE = resume 482 | */ 483 | void PauseMOD(Mod_t* Mod_p, uint8_t pause) 484 | { 485 | uint8_t i; 486 | 487 | if (Mod_p) 488 | { 489 | if ( pause ) 490 | { 491 | asm("move.w #0x2700,%sr"); 492 | Mod_p->IsPlaying &= 0xFE; 493 | for (i=0; iNumberOfChannels; i++) 494 | { 495 | pcm_set_ctrl(0x40 + i); 496 | pcm_set_env(0); 497 | } 498 | pcm_set_ctrl(0xC0); 499 | asm("move.w #0x2000,%sr"); 500 | } 501 | else 502 | { 503 | asm("move.w #0x2700,%sr"); 504 | Mod_p->IsPlaying |= 1; 505 | for (i=0; iNumberOfChannels; i++) 506 | voices[i].pending |= PENDING_VOL; 507 | asm("move.w #0x2000,%sr"); 508 | } 509 | } 510 | } 511 | 512 | 513 | /* 514 | Stop playing Module 515 | */ 516 | void StopMOD(Mod_t* Mod_p) 517 | { 518 | if (Mod_p) 519 | { 520 | pcm_stop_timer(); 521 | Mod_p->IsPlaying = 0; 522 | SetMOD(0); 523 | } 524 | } 525 | 526 | 527 | /* 528 | Wait for Module to play through once 529 | */ 530 | void WaitMOD(Mod_t* Mod_p) 531 | { 532 | do 533 | { 534 | if (!Mod_p->IsPlaying) 535 | break; 536 | if (Mod_p->IsPlaying & 0x80) 537 | if (!Mod_p->Row && !Mod_p->Order) 538 | break; 539 | } while (1); 540 | } 541 | 542 | 543 | /* 544 | Check Module play status 545 | returns 0 if done playing 546 | */ 547 | uint8_t CheckMOD(Mod_t* Mod_p) 548 | { 549 | if (Mod_p->IsPlaying & 0x80) 550 | return (!Mod_p->Row && !Mod_p->Order) ? 0 : 1; 551 | return Mod_p->IsPlaying; 552 | } 553 | 554 | 555 | /* 556 | Set global volume 557 | vol = 0 = off, = 16 = max 558 | returns the previous volume setting 559 | */ 560 | uint8_t VolumeMOD(uint8_t vol) 561 | { 562 | uint8_t prev = gVolume; 563 | gVolume = vol > 16 ? 16: vol; 564 | return prev; 565 | } 566 | 567 | 568 | /* 569 | +----------------------------------------------------------------------------+ 570 | | === Playing the MOD === | 571 | +----------------------------------------------------------------------------+ 572 | 573 | timer function, it contains the player logic, it has to be as fast as possible 574 | 575 | The SPEED of a song is the base on how your mod is played. 576 | Each row of a pattern is updated every SPEED number of clock ticks. 577 | */ 578 | uint16_t PlayMOD(Mod_t* Mod_p) 579 | { 580 | Mod_p->Pattern = Mod_p->Orders[ Mod_p->Order ]; /* pattern for current order */ 581 | 582 | Mod_p->Tick++; 583 | 584 | if (Mod_p->Tick >= Mod_p->Speed) 585 | { 586 | if ( !Mod_p->PatternDelay ) /* if there is no pattern delay */ 587 | { 588 | UpdateNote( Mod_p, &Mod_p->PatternsBuff_p[ Mod_p->Pattern * MOD_ROWS_PER_CHANNEL * Mod_p->NumberOfChannels + Mod_p->Row * Mod_p->NumberOfChannels * 6 ] ); /* process 1 note for every channel */ 589 | Mod_p->Row += 1; /* increment row */ 590 | } 591 | else 592 | { 593 | Mod_p->PatternDelay--; /* else decrement pattern delay */ 594 | } 595 | 596 | Mod_p->Tick = 0; 597 | 598 | if ( Mod_p->Row >= MOD_ROWS_PER_CHANNEL ) 599 | { 600 | /* process next pattern */ 601 | Mod_p->Order++; 602 | if (Mod_p->Order >= Mod_p->SongLength) 603 | { 604 | /* finished playing the mod */ 605 | if (Mod_p->IsPlaying & 0x80) 606 | { 607 | /* loop - reset to defaults */ 608 | Mod_p->Speed = 6; 609 | Mod_p->Tick = 6; /* force immediate processing */ 610 | Mod_p->Order = 0; 611 | Mod_p->Row = 0; 612 | Mod_p->BeatsPerMinute = 125; 613 | } 614 | else 615 | { 616 | Mod_p->IsPlaying = 0; /* song done playing */ 617 | } 618 | } 619 | else 620 | { 621 | Mod_p->Pattern = Mod_p->Orders[ Mod_p->Order ]; 622 | Mod_p->Row = 0; 623 | } 624 | } 625 | } 626 | else 627 | { 628 | Mod_p->Row -= 1; /* decrement row */ 629 | /* update the tick based effects */ 630 | UpdateEffect( Mod_p ); 631 | Mod_p->Row += 1; /* increment row */ 632 | } 633 | 634 | return Mod_p->IsPlaying ? 0 : 1; 635 | } 636 | 637 | 638 | /* 639 | process division - 1 note for every channel 640 | */ 641 | static void UpdateNote(Mod_t* Mod_p, uint8_t* Data_p) 642 | { 643 | uint8_t Channel; /* counter */ 644 | uint8_t EParX; /* Effect Parameter X */ 645 | uint8_t EParY; /* Effect Parameter Y */ 646 | uint8_t JumpFlag, BreakFlag; /* boolean flags for effects JUMP_TO_PATTERN and PATTERN_BREAK on the same row */ 647 | 648 | JumpFlag = FALSE; 649 | BreakFlag = FALSE; 650 | 651 | for ( Channel = 0; Channel < Mod_p->NumberOfChannels; Channel++ ) 652 | { 653 | Mod_p->Notes[ Channel ].SampleNumber = Data_p[0]; 654 | Mod_p->Notes[ Channel ].EffectNumber = Data_p[1]; 655 | Mod_p->Notes[ Channel ].EffectParameter = Data_p[2]; 656 | Mod_p->Notes[ Channel ].NoteExists = (Data_p[3] == 0xFF) ? FALSE : TRUE; 657 | Mod_p->Notes[ Channel ].PeriodFrequencyCol = (Data_p[3] == 0xFF) ? 0 : Data_p[3]; 658 | Mod_p->Notes[ Channel ].PeriodFrequency = (Data_p[4] << 8) + Data_p[5]; 659 | 660 | EParX = Mod_p->Notes[ Channel ].EffectParameter >> 4; 661 | EParY = Mod_p->Notes[ Channel ].EffectParameter & 0xF; 662 | 663 | Mod_p->Channels[ Channel ].SampleOffset = 2; 664 | 665 | if ( Mod_p->Notes[ Channel ].SampleNumber > 0 ) 666 | { 667 | Mod_p->Channels[ Channel ].LastInstrument = Mod_p->Notes[ Channel ].SampleNumber - 1; 668 | 669 | if ( !( ( Mod_p->Notes[ Channel ].EffectNumber == EFFECT_EXTENDED_EFFECTS ) && ( EParX == EFFECT_DELAY_NOTE ) ) ) 670 | { 671 | Mod_p->Channels[ Channel ].Volume = Mod_p->Inst[ Mod_p->Notes[ Channel ].SampleNumber - 1 ].Volume; /* default volume of sample */ 672 | } 673 | } 674 | 675 | if ( Mod_p->Notes[ Channel ].NoteExists == TRUE ) 676 | { 677 | if ( ( Mod_p->Channels[ Channel ].WaveformControl & 0x0F ) < 4 ) 678 | { 679 | Mod_p->Channels[ Channel ].VibratoPosition = 0; 680 | } 681 | if ( ( Mod_p->Channels[ Channel ].WaveformControl >> 4 ) < 4 ) 682 | { 683 | Mod_p->Channels[ Channel ].TremoloPosition = 0; 684 | } 685 | 686 | /* if not a porta effect, then set the channels frequency to the looked up amiga value + or - any finetune */ 687 | if ( (Mod_p->Notes[ Channel ].EffectNumber != EFFECT_PORTA_TO_NOTE) && 688 | (Mod_p->Notes[ Channel ].EffectNumber != EFFECT_PORTA_PLUS_VOL_SLIDE) ) 689 | { 690 | uint8_t FineTune; 691 | 692 | FineTune = Mod_p->Inst[ Mod_p->Channels[ Channel ].LastInstrument ].FineTune; 693 | Mod_p->Channels[ Channel ].PeriodFrequency = AmigaPeriodsTable[ FineTune ][ Mod_p->Notes[ Channel ].PeriodFrequencyCol ]; 694 | } /* if */ 695 | 696 | //Mod_p->Channels[ Channel ].SampleOffset = 2; 697 | } /* if */ 698 | 699 | /* skip effects ? */ 700 | if ( !( ( Mod_p->Notes[ Channel ].EffectNumber == 0 ) && ( Mod_p->Notes[ Channel ].EffectParameter == 0 ) ) ) 701 | { 702 | /* process the non tick based effects also grab parameters for tick based effects */ 703 | switch ( Mod_p->Notes[ Channel ].EffectNumber ) 704 | { 705 | case EFFECT_ARPEGGIO: /* not processed on tick 0 */ 706 | case EFFECT_PORTA_UP: /* not processed on tick 0 */ 707 | case EFFECT_PORTA_DOWN: /* not processed on tick 0 */ 708 | break; 709 | 710 | case EFFECT_PORTA_TO_NOTE: /* reviewed OK */ 711 | if ( Mod_p->Notes[ Channel ].EffectParameter > 0 ) 712 | { 713 | Mod_p->Channels[ Channel ].PortaSpeed = Mod_p->Notes[ Channel ].EffectParameter; 714 | } 715 | /* no break statement here */ 716 | 717 | case EFFECT_PORTA_PLUS_VOL_SLIDE: /* reviewed OK */ 718 | if ( Mod_p->Notes[ Channel ].NoteExists == TRUE ) 719 | { 720 | Mod_p->Channels[ Channel ].PortaTo = Mod_p->Notes[ Channel ].PeriodFrequency; 721 | } 722 | break; 723 | 724 | case EFFECT_VIBRATO: /* reviewed OK */ 725 | if ( EParX > 0 ) 726 | { 727 | Mod_p->Channels[ Channel ].VibratoSpeed = EParX; 728 | } 729 | if ( EParY > 0 ) 730 | { 731 | Mod_p->Channels[ Channel ].VibratoDepth = EParY; 732 | } 733 | break; 734 | 735 | case EFFECT_VIBRATO_PLUS_VOL_SLIDE: /* not processed on tick 0 */ 736 | break; 737 | 738 | case EFFECT_TREMOLO: /* reviewed OK */ 739 | if ( EParX > 0 ) 740 | { 741 | Mod_p->Channels[ Channel ].TremoloSpeed = EParX; 742 | } 743 | if ( EParY > 0 ) 744 | { 745 | Mod_p->Channels[ Channel ].TremoloDepth = EParY; 746 | } 747 | break; 748 | 749 | case EFFECT_PAN: /* reviewed OK */ /* not a ProTracker effect */ 750 | Mod_p->Channels[ Channel ].PanValue = Mod_p->Notes[ Channel ].EffectParameter; 751 | break; 752 | 753 | case EFFECT_SAMPLE_OFFSET: /* reviewed OK */ 754 | if ( Mod_p->Notes[ Channel ].EffectParameter > 0 ) 755 | { 756 | Mod_p->Channels[ Channel ].SampleOffset = ( Mod_p->Notes[ Channel ].EffectParameter << 8 ); 757 | } 758 | if ( Mod_p->Channels[ Channel ].SampleOffset > Mod_p->Inst[ Mod_p->Channels[ Channel ].LastInstrument ].SampleLength ) 759 | { 760 | Mod_p->Channels[ Channel ].SampleOffset = Mod_p->Inst[ Mod_p->Channels[ Channel ].LastInstrument ].SampleLength; 761 | /* this will result in silence */ 762 | } 763 | break; 764 | 765 | case EFFECT_VOLUME_SLIDE: /* not processed on tick 0 */ 766 | break; 767 | 768 | case EFFECT_JUMP_TO_PATTERN: /* reviewed OK */ 769 | Mod_p->Order = Mod_p->Notes[ Channel ].EffectParameter; 770 | Mod_p->Row = -1; 771 | if ( Mod_p->Order >= Mod_p->SongLength ) 772 | { 773 | Mod_p->Order = 0; 774 | } 775 | // if ( Mod_p->Order == 0 ) 776 | // { 777 | /* loop the mod */ 778 | if (!(Mod_p->IsPlaying & 0x80)) 779 | { 780 | Mod_p->IsPlaying = 0; /* song done playing */ 781 | return; 782 | } 783 | // } 784 | JumpFlag = TRUE; 785 | break; 786 | 787 | case EFFECT_SET_VOLUME: /* reviewed OK */ 788 | Mod_p->Channels[ Channel ].Volume = Mod_p->Notes[ Channel ].EffectParameter; 789 | if ( Mod_p->Channels[ Channel ].Volume < 0 ) 790 | { 791 | Mod_p->Channels[ Channel ].Volume = 0; 792 | } 793 | if ( Mod_p->Channels[ Channel ].Volume > 64 ) 794 | { 795 | Mod_p->Channels[ Channel ].Volume = 64; 796 | } 797 | break; 798 | 799 | case EFFECT_PATTERN_BREAK: /* reviewed OK */ 800 | Mod_p->Row = ( EParX * 10 ) + EParY; /* get row number */ 801 | if ( Mod_p->Row == 0 ) 802 | { 803 | Mod_p->Row = -1; 804 | } 805 | else if ( Mod_p->Row >= MOD_ROWS_PER_CHANNEL ) 806 | { 807 | Mod_p->Row = -1; 808 | } 809 | else 810 | { 811 | Mod_p->Row -= 1; 812 | } 813 | 814 | if ( ! JumpFlag && ! BreakFlag ) 815 | { 816 | Mod_p->Order++; 817 | 818 | if ( Mod_p->Order >= Mod_p->SongLength ) 819 | { 820 | Mod_p->Order = 0; 821 | /* repeat the mod */ 822 | if (!(Mod_p->IsPlaying & 0x80)) 823 | { 824 | Mod_p->IsPlaying = 0; /* song done playing */ 825 | return; 826 | } 827 | } 828 | } 829 | BreakFlag = TRUE; 830 | break; 831 | 832 | case EFFECT_EXTENDED_EFFECTS: 833 | switch (EParX) 834 | { 835 | case EFFECT_FINE_PORTA_UP: /* reviewed OK */ 836 | Mod_p->Channels[ Channel ].PeriodFrequency -= EParY; 837 | break; 838 | 839 | case EFFECT_FINE_PORTA_DOWN: /* reviewed OK */ 840 | Mod_p->Channels[ Channel ].PeriodFrequency += EParY; 841 | break; 842 | 843 | case EFFECT_GLISSANDO_CONTROL: /* not implemented not even in fmoddoc2 */ 844 | break; 845 | 846 | case EFFECT_SET_VIBRATO_WAVEFORM: /* reviewed OK */ 847 | Mod_p->Channels[ Channel ].WaveformControl &= 0xF0; 848 | Mod_p->Channels[ Channel ].WaveformControl |= EParY; 849 | break; 850 | 851 | case EFFECT_SET_FINETUNE: /* reviewed OK */ 852 | Mod_p->Inst[ Mod_p->Channels[ Channel ].LastInstrument ].FineTune = EParY; 853 | break; 854 | 855 | case EFFECT_PATTERN_LOOP: /* reviewed OK */ 856 | if ( EParY == 0 ) 857 | { 858 | Mod_p->Channels[ Channel ].PatLoopRow = Mod_p->Row; 859 | } 860 | else 861 | { 862 | if ( ! Mod_p->Channels[ Channel ].PatLoopNo ) 863 | { 864 | Mod_p->Channels[ Channel ].PatLoopNo = EParY; 865 | } 866 | else 867 | { 868 | Mod_p->Channels[ Channel ].PatLoopNo--; 869 | } 870 | if ( Mod_p->Channels[ Channel ].PatLoopNo ) 871 | { 872 | Mod_p->Row = Mod_p->Channels[ Channel ].PatLoopRow - 1; 873 | } 874 | } 875 | break; 876 | 877 | case EFFECT_SET_TREMOLO_WAVEFORM: /* reviewed OK */ 878 | Mod_p->Channels[ Channel ].WaveformControl &= 0x0F; 879 | Mod_p->Channels[ Channel ].WaveformControl |= (EParY << 4); 880 | break; 881 | 882 | case EFFECT_POSITION_PANNING: /* cw - fixed */ 883 | Mod_p->Channels[ Channel ].PanValue = (EParY << 4) | EParY; 884 | break; 885 | 886 | case EFFECT_RETRIG_NOTE: /* not processed on tick 0 */ 887 | break; 888 | 889 | case EFFECT_FINE_VOLUME_SLIDE_UP: /* reviewed OK */ 890 | Mod_p->Channels[ Channel ].Volume += EParY; 891 | if ( Mod_p->Channels[ Channel ].Volume > 64 ) 892 | { 893 | Mod_p->Channels[ Channel ].Volume = 64; 894 | } 895 | break; 896 | 897 | case EFFECT_FINE_VOLUME_SLIDE_DOWN: /* reviewed OK */ 898 | Mod_p->Channels[ Channel ].Volume -= EParY; 899 | if ( Mod_p->Channels[ Channel ].Volume < 0 ) 900 | { 901 | Mod_p->Channels[ Channel ].Volume = 0; 902 | } 903 | break; 904 | 905 | case EFFECT_CUT_NOTE: /* not processed on tick 0 */ 906 | break; 907 | 908 | case EFFECT_DELAY_NOTE: /* not processed on tick 0 */ /* reviewed OK */ 909 | continue; /* process next note */ 910 | break; 911 | 912 | case EFFECT_PATTERN_DELAY: /* reviewed OK */ 913 | Mod_p->PatternDelay = EParY; 914 | break; 915 | 916 | default: 917 | break; 918 | } 919 | break; 920 | 921 | case EFFECT_SET_SPEED: /* cw - update for bpm */ 922 | if ( Mod_p->Notes[ Channel ].EffectParameter < 0x20 ) 923 | { 924 | if ( Mod_p->Notes[ Channel ].EffectParameter ) 925 | { 926 | Mod_p->Speed = Mod_p->Notes[ Channel ].EffectParameter; 927 | } 928 | } 929 | else 930 | { 931 | Mod_p->BeatsPerMinute = Mod_p->Notes[ Channel ].EffectParameter; 932 | } 933 | break; 934 | 935 | default: 936 | /* effect not defined */ 937 | break; 938 | } /* switch */ 939 | } /* if */ 940 | 941 | if ( Mod_p->Notes[ Channel ].EffectNumber != EFFECT_SET_TREMOLO_WAVEFORM ) 942 | { 943 | /* set volume */ 944 | SetVolume( Channel, Mod_p->Channels[ Channel ].Volume, Mod_p->Channels[ Channel ].PanValue ); 945 | } 946 | 947 | if ( Mod_p->Channels[ Channel ].PeriodFrequency > 0 ) 948 | { 949 | /* set frequency */ 950 | SetFrequency( Channel, Mod_p->Channels[ Channel ].PeriodFrequency ); 951 | } 952 | 953 | if ( Mod_p->Notes[ Channel ].NoteExists == TRUE ) 954 | { 955 | if ( ( Mod_p->Notes[ Channel ].EffectNumber != EFFECT_PORTA_TO_NOTE ) && 956 | ( Mod_p->Notes[ Channel ].EffectNumber != EFFECT_PORTA_PLUS_VOL_SLIDE ) ) 957 | { 958 | /* set sample */ 959 | SetSample( Channel, Mod_p->Channels[ Channel ].LastInstrument, Mod_p->Channels[ Channel ].SampleOffset ); 960 | } 961 | } /* if */ 962 | } /* for Channel */ 963 | 964 | return; 965 | } 966 | 967 | 968 | /* 969 | update tick based effects 970 | */ 971 | static void UpdateEffect(Mod_t* Mod_p) 972 | { 973 | uint8_t Channel; /* counter */ 974 | uint8_t EParX; /* Effect Parameter X */ 975 | uint8_t EParY; /* Effect Parameter Y */ 976 | uint8_t Tick = Mod_p->Tick; /* current music tick */ 977 | 978 | for ( Channel = 0; Channel < Mod_p->NumberOfChannels; Channel++ ) 979 | { 980 | EParX = Mod_p->Notes[ Channel ].EffectParameter >> 4; 981 | EParY = Mod_p->Notes[ Channel ].EffectParameter & 0xF; 982 | 983 | switch ( Mod_p->Notes[ Channel ].EffectNumber ) 984 | { 985 | 986 | case EFFECT_ARPEGGIO: /* effect used in chip tunes */ /* reviewed OK */ 987 | if ( Mod_p->Notes[ Channel ].EffectParameter > 0 ) 988 | { 989 | switch ( Tick % 3 ) 990 | { 991 | case 0: 992 | SetFrequency( Channel, Mod_p->Channels[ Channel ].PeriodFrequency ); 993 | break; 994 | 995 | case 1: 996 | SetFrequency( Channel, Mod_p->Channels[ Channel ].PeriodFrequency + EParX ); 997 | break; 998 | 999 | case 2: 1000 | SetFrequency( Channel, Mod_p->Channels[ Channel ].PeriodFrequency + EParY ); 1001 | break; 1002 | } /* switch */ 1003 | } /* if */ 1004 | break; 1005 | 1006 | case EFFECT_PORTA_UP: /* reviewed OK */ 1007 | Mod_p->Channels[ Channel ].PeriodFrequency -= Mod_p->Notes[ Channel ].EffectParameter; 1008 | if ( Mod_p->Channels[ Channel ].PeriodFrequency < 113 ) 1009 | { 1010 | Mod_p->Channels[ Channel ].PeriodFrequency = 113; /* stop at B-3 */ 1011 | } 1012 | SetFrequency( Channel, Mod_p->Channels[ Channel ].PeriodFrequency ); 1013 | break; 1014 | 1015 | case EFFECT_PORTA_DOWN: /* reviewed OK */ 1016 | Mod_p->Channels[ Channel ].PeriodFrequency += Mod_p->Notes[ Channel ].EffectParameter; 1017 | if ( Mod_p->Channels[ Channel ].PeriodFrequency > 856 ) 1018 | { 1019 | Mod_p->Channels[ Channel ].PeriodFrequency = 856; /* stop at C-1 */ 1020 | } 1021 | SetFrequency( Channel, Mod_p->Channels[ Channel ].PeriodFrequency ); 1022 | break; 1023 | 1024 | case EFFECT_PORTA_TO_NOTE: /* reviewed OK */ 1025 | DoPorta( Mod_p, Channel ); 1026 | break; 1027 | 1028 | case EFFECT_VIBRATO: /* reviewed OK */ 1029 | DoVibrato( Mod_p, Channel ); 1030 | break; 1031 | 1032 | case EFFECT_PORTA_PLUS_VOL_SLIDE: /* reviewed OK */ 1033 | DoPorta( Mod_p, Channel ); 1034 | 1035 | if ( EParX > 0 ) 1036 | { 1037 | Mod_p->Channels[ Channel ].Volume += EParX; 1038 | } 1039 | if ( EParY > 0 ) 1040 | { 1041 | Mod_p->Channels[ Channel ].Volume -= EParY; 1042 | } 1043 | if ( Mod_p->Channels[ Channel ].Volume < 0 ) 1044 | { 1045 | Mod_p->Channels[ Channel ].Volume = 0; 1046 | } 1047 | if ( Mod_p->Channels[ Channel ].Volume > 64 ) 1048 | { 1049 | Mod_p->Channels[ Channel ].Volume = 64; 1050 | } 1051 | SetVolume( Channel, Mod_p->Channels[ Channel ].Volume, Mod_p->Channels[ Channel ].PanValue ); 1052 | break; 1053 | 1054 | case EFFECT_VIBRATO_PLUS_VOL_SLIDE: /* reviewed OK */ 1055 | DoVibrato( Mod_p, Channel ); 1056 | 1057 | if ( EParX > 0 ) 1058 | { 1059 | Mod_p->Channels[ Channel ].Volume += EParX; 1060 | } 1061 | if ( EParY > 0 ) 1062 | { 1063 | Mod_p->Channels[ Channel ].Volume -= EParY; 1064 | } 1065 | if ( Mod_p->Channels[ Channel ].Volume < 0 ) 1066 | { 1067 | Mod_p->Channels[ Channel ].Volume = 0; 1068 | } 1069 | if ( Mod_p->Channels[ Channel ].Volume > 64 ) 1070 | { 1071 | Mod_p->Channels[ Channel ].Volume = 64; 1072 | } 1073 | SetVolume( Channel, Mod_p->Channels[ Channel ].Volume, Mod_p->Channels[ Channel ].PanValue ); 1074 | break; 1075 | 1076 | case EFFECT_TREMOLO: /* reviewed OK */ 1077 | DoTremolo( Mod_p, Channel ); 1078 | break; 1079 | 1080 | case EFFECT_VOLUME_SLIDE: /* reviewed OK */ 1081 | if ( EParX > 0 ) 1082 | { 1083 | Mod_p->Channels[ Channel ].Volume += EParX; 1084 | } 1085 | if ( EParY > 0 ) 1086 | { 1087 | Mod_p->Channels[ Channel ].Volume -= EParY; 1088 | } 1089 | if ( Mod_p->Channels[ Channel ].Volume < 0 ) 1090 | { 1091 | Mod_p->Channels[ Channel ].Volume = 0; 1092 | } 1093 | if ( Mod_p->Channels[ Channel ].Volume > 64 ) 1094 | { 1095 | Mod_p->Channels[ Channel ].Volume = 64; 1096 | } 1097 | SetVolume( Channel, Mod_p->Channels[ Channel ].Volume, Mod_p->Channels[ Channel ].PanValue ); 1098 | break; 1099 | 1100 | case EFFECT_EXTENDED_EFFECTS: 1101 | switch (EParX) 1102 | { 1103 | case EFFECT_RETRIG_NOTE: /* I am not 100% sure about this effect */ 1104 | if ( !EParY ) 1105 | { 1106 | break; 1107 | } 1108 | if ( !( Tick % EParY ) ) 1109 | { 1110 | SetSample( Channel, Mod_p->Channels[ Channel ].LastInstrument, Mod_p->Channels[ Channel ].SampleOffset ); 1111 | } 1112 | break; 1113 | 1114 | case EFFECT_CUT_NOTE: /* reviewed OK */ 1115 | if ( Tick == EParY ) 1116 | { 1117 | Mod_p->Channels[ Channel ].Volume = 0; 1118 | SetVolume( Channel, Mod_p->Channels[ Channel ].Volume, Mod_p->Channels[ Channel ].PanValue ); 1119 | } 1120 | break; 1121 | 1122 | case EFFECT_DELAY_NOTE: /* I am not 100% sure about this effect */ 1123 | if ( Tick == EParY ) 1124 | { 1125 | if ( Mod_p->Notes[ Channel ].SampleNumber > 0 ) 1126 | { 1127 | Mod_p->Channels[ Channel ].Volume = Mod_p->Inst[ Mod_p->Channels[ Channel ].LastInstrument ].Volume; 1128 | } 1129 | if ( Mod_p->Inst[ Mod_p->Notes[ Channel ].SampleNumber - 1 ].Volume <= 64 ) 1130 | { 1131 | Mod_p->Channels[ Channel ].Volume = Mod_p->Inst[ Mod_p->Notes[ Channel ].SampleNumber - 1 ].Volume; 1132 | } 1133 | 1134 | SetFrequency( Channel, Mod_p->Channels[ Channel ].PeriodFrequency ); 1135 | SetVolume( Channel, Mod_p->Channels[ Channel ].Volume, Mod_p->Channels[ Channel ].PanValue ); 1136 | SetSample( Channel, Mod_p->Channels[ Channel ].LastInstrument, Mod_p->Channels[ Channel ].SampleOffset ); 1137 | } /* if */ 1138 | break; 1139 | 1140 | default: 1141 | break; 1142 | } 1143 | break; 1144 | 1145 | default: 1146 | break; 1147 | } /* switch */ 1148 | } /* for Channel */ 1149 | 1150 | return; 1151 | } 1152 | 1153 | 1154 | /* 1155 | */ 1156 | static void DoVibrato(Mod_t* Mod_p, uint8_t Channel) 1157 | { 1158 | uint16_t Delta = 0; 1159 | uint8_t Temp; 1160 | 1161 | Temp = ( Mod_p->Channels[ Channel ].VibratoPosition & 31 ); /* Temp will be the index */ 1162 | 1163 | switch ( Mod_p->Channels[ Channel ].WaveformControl & 3 ) 1164 | { 1165 | case 0: 1166 | Delta = SineTable[ Temp ]; /* look up sine table */ 1167 | break; 1168 | 1169 | case 1: 1170 | Temp <<= 3; /* ramp down */ 1171 | if ( Mod_p->Channels[ Channel ].VibratoPosition < 0 ) 1172 | { 1173 | Temp = 255 - Temp; 1174 | } 1175 | Delta = Temp; 1176 | break; 1177 | 1178 | case 2: 1179 | Delta = 255; /* square */ 1180 | break; 1181 | 1182 | case 3: 1183 | Delta = rand() & 255; /* random */ 1184 | break; 1185 | } /* switch */ 1186 | 1187 | Delta *= Mod_p->Channels[ Channel ].VibratoDepth; 1188 | Delta >>= 7; 1189 | //Delta <<= 2; /* we use 4*periods so make vibrato 4 times bigger */ 1190 | 1191 | if ( Mod_p->Channels[ Channel ].VibratoPosition >= 0 ) 1192 | { 1193 | SetFrequency( Channel, Mod_p->Channels[ Channel ].PeriodFrequency + Delta ); 1194 | } 1195 | else 1196 | { 1197 | SetFrequency( Channel, Mod_p->Channels[ Channel ].PeriodFrequency - Delta ); 1198 | } 1199 | 1200 | Mod_p->Channels[ Channel ].VibratoPosition += Mod_p->Channels[ Channel ].VibratoSpeed; 1201 | if ( Mod_p->Channels[ Channel ].VibratoPosition > 31 ) 1202 | { 1203 | Mod_p->Channels[ Channel ].VibratoPosition -= 64; 1204 | } 1205 | 1206 | return; 1207 | } 1208 | 1209 | 1210 | /* 1211 | */ 1212 | static void DoTremolo(Mod_t* Mod_p, uint8_t Channel) 1213 | { 1214 | uint16_t Delta; 1215 | uint8_t Temp; 1216 | 1217 | Temp = ( Mod_p->Channels[ Channel ].TremoloPosition & 31 ); /* Temp will be the index */ 1218 | 1219 | switch ( ( Mod_p->Channels[ Channel ].WaveformControl >> 4 ) & 3 ) 1220 | { 1221 | case 0: 1222 | Delta = SineTable[ Temp ]; /* look up sine table */ 1223 | break; 1224 | 1225 | case 1: 1226 | Temp <<= 3; /* ramp down */ 1227 | if ( Mod_p->Channels[ Channel ].TremoloPosition < 0 ) 1228 | { 1229 | Temp = 255 - Temp; 1230 | } 1231 | Delta = Temp; 1232 | break; 1233 | 1234 | case 2: 1235 | Delta = 255; /* square */ 1236 | break; 1237 | 1238 | case 3: 1239 | Delta = rand() & 255; /* random */ 1240 | break; 1241 | } /* switch */ 1242 | 1243 | Delta *= Mod_p->Channels[ Channel ].TremoloDepth; 1244 | Delta >>= 6; 1245 | 1246 | if ( Mod_p->Channels[ Channel ].TremoloPosition >= 0 ) 1247 | { 1248 | if ( Mod_p->Channels[ Channel ].Volume + Delta > 64 ) 1249 | { 1250 | Delta = 64 - Mod_p->Channels[ Channel ].Volume; 1251 | } 1252 | 1253 | SetVolume( Channel, Mod_p->Channels[ Channel ].Volume + Delta, Mod_p->Channels[ Channel ].PanValue ); 1254 | } 1255 | else 1256 | { 1257 | if ( (int16_t) ( Mod_p->Channels[ Channel ].Volume - Delta < 0 ) ) 1258 | { 1259 | Delta = Mod_p->Channels[ Channel ].Volume; 1260 | } 1261 | 1262 | SetVolume( Channel, Mod_p->Channels[ Channel ].Volume - Delta, Mod_p->Channels[ Channel ].PanValue ); 1263 | } 1264 | 1265 | Mod_p->Channels[ Channel ].TremoloPosition += Mod_p->Channels[ Channel ].TremoloSpeed; 1266 | if ( Mod_p->Channels[ Channel ].TremoloPosition > 31 ) 1267 | { 1268 | Mod_p->Channels[ Channel ].TremoloPosition -= 64; 1269 | } 1270 | 1271 | return; 1272 | } 1273 | 1274 | 1275 | /* 1276 | */ 1277 | static void DoPorta(Mod_t* Mod_p, uint8_t Channel) 1278 | { 1279 | /* slide pitch down if it needs too */ 1280 | if ( Mod_p->Channels[ Channel ].PeriodFrequency < Mod_p->Channels[ Channel ].PortaTo ) 1281 | { 1282 | Mod_p->Channels[ Channel ].PeriodFrequency += Mod_p->Channels[ Channel ].PortaSpeed; 1283 | if ( Mod_p->Channels[ Channel ].PeriodFrequency > Mod_p->Channels[ Channel ].PortaTo ) 1284 | { 1285 | Mod_p->Channels[ Channel ].PeriodFrequency = Mod_p->Channels[ Channel ].PortaTo; 1286 | } 1287 | } 1288 | 1289 | /* slide pitch up if it needs too */ 1290 | if ( Mod_p->Channels[ Channel ].PeriodFrequency > Mod_p->Channels[ Channel ].PortaTo ) 1291 | { 1292 | Mod_p->Channels[ Channel ].PeriodFrequency -= Mod_p->Channels[ Channel ].PortaSpeed; 1293 | if ( Mod_p->Channels[ Channel ].PeriodFrequency < Mod_p->Channels[ Channel ].PortaTo ) 1294 | { 1295 | Mod_p->Channels[ Channel ].PeriodFrequency = Mod_p->Channels[ Channel ].PortaTo; 1296 | } 1297 | } 1298 | 1299 | SetFrequency( Channel, Mod_p->Channels[ Channel ].PeriodFrequency ); 1300 | 1301 | return; 1302 | } 1303 | 1304 | 1305 | /* System specific functions =================================================================== */ 1306 | 1307 | 1308 | static Mod_t* SetMOD(Mod_t* Mod_p) 1309 | { 1310 | Mod_t *pmod = gMod; 1311 | /* once set, PlayMOD() starts being called */ 1312 | gMod = Mod_p; 1313 | return pmod; 1314 | } 1315 | 1316 | 1317 | static void SetSample(uint16_t Channel, uint8_t Sample, uint32_t SampleOffset) 1318 | { 1319 | uint16_t loop = gMod->Inst[ Sample ].LoopStart >> gMod->Inst[ Sample ].Factor; 1320 | voices[Channel].start = gMod->Inst[ Sample ].SampleHandle; 1321 | voices[Channel].offset = SampleOffset >> gMod->Inst[ Sample ].Factor; 1322 | voices[Channel].loop = loop ? loop : (gMod->Inst[ Sample ].SampleLength >> gMod->Inst[ Sample ].Factor) - 1; 1323 | voices[Channel].pending |= PENDING_START; 1324 | voices[Channel].pending &= ~PENDING_STOP; 1325 | } 1326 | 1327 | 1328 | static void SetFrequency(uint16_t Channel, uint32_t Period) 1329 | { 1330 | voices[Channel].period = Period; 1331 | voices[Channel].pending |= PENDING_PER; 1332 | } 1333 | 1334 | 1335 | static void SetVolume(uint16_t Channel, uint32_t Volume, int32_t Pan) 1336 | { 1337 | uint16_t left, right; 1338 | uint16_t env, pan; 1339 | uint16_t chan = Channel & 3; 1340 | 1341 | env = (Volume * gVolume) >> 2; // 0 to 256 1342 | if (env > 255) 1343 | env = 255; // clamp to 255 1344 | 1345 | #ifdef HANDLE_PANNING 1346 | pan = pcm_lcf(Pan); // Linear Cross Fade pan value 1347 | #else 1348 | if ( (chan == 0) || (chan == 3) ) 1349 | { 1350 | #ifdef CROSS_TALK 1351 | right = 15 >> CROSS_TALK; 1352 | #else 1353 | right = 0; 1354 | #endif 1355 | left = 15; 1356 | } 1357 | else 1358 | { 1359 | right = 15; 1360 | #ifdef CROSS_TALK 1361 | left = 15 >> CROSS_TALK; 1362 | #else 1363 | left = 0; 1364 | #endif 1365 | } 1366 | pan = (right << 4) | left; 1367 | #endif 1368 | 1369 | voices[Channel].vol = env; 1370 | voices[Channel].pan = pan; 1371 | voices[Channel].pending |= PENDING_VOL; 1372 | } 1373 | 1374 | 1375 | static void callback(void) 1376 | { 1377 | static uint16_t tempo = 0; 1378 | uint8_t i; 1379 | 1380 | if (gMod) 1381 | PlayMOD(gMod); 1382 | 1383 | if (tempo != gMod->BeatsPerMinute) 1384 | { 1385 | tempo = gMod->BeatsPerMinute; 1386 | pcm_set_timer(tempo); 1387 | } 1388 | 1389 | /* update channels */ 1390 | for (i = 0; i < gMod->NumberOfChannels; i++) 1391 | { 1392 | pcm_set_ctrl(0x40 + i); // turn off pcm chip briefly and select channel 1393 | 1394 | if (voices[i].pending & PENDING_STOP) 1395 | { 1396 | pcm_set_off(i); 1397 | pcm_set_env(0); 1398 | voices[i].pending &= ~PENDING_STOP; 1399 | } 1400 | 1401 | if (voices[i].pending & PENDING_START) 1402 | { 1403 | pcm_set_off(i); 1404 | pcm_set_start(voices[i].start, voices[i].offset); 1405 | pcm_set_loop(voices[i].loop); 1406 | voices[i].pending |= PENDING_VOL|PENDING_PER; 1407 | } 1408 | 1409 | if (voices[i].pending & PENDING_VOL) 1410 | { 1411 | pcm_set_env(voices[i].vol); 1412 | pcm_set_pan(voices[i].pan); 1413 | voices[i].pending &= ~PENDING_VOL; 1414 | } 1415 | 1416 | if (voices[i].pending & PENDING_PER) 1417 | { 1418 | pcm_set_env(voices[i].period); 1419 | voices[i].pending &= ~PENDING_PER; 1420 | } 1421 | 1422 | if (voices[i].pending & PENDING_START) 1423 | { 1424 | pcm_set_on(i); 1425 | voices[i].pending &= ~PENDING_START; 1426 | } 1427 | } 1428 | pcm_set_ctrl(0xC0); // restart pcm chip 1429 | } 1430 | -------------------------------------------------------------------------------- /module.c: -------------------------------------------------------------------------------- 1 | /* 2 | SegaCD MOD support code by Chilly Willy, based on 3 | 4 | Ami-PlayMOD_V1_0_20090417.c (c) 2009 Massimiliano Scarano mscarano@libero.it 5 | 6 | which had the following restriction: 7 | If you use the source code, you should always mention me as the original author. 8 | */ 9 | 10 | 11 | /* Includes ==================================================================================== */ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "hw_md.h" 19 | #include "cdfh.h" 20 | #include "pcm.h" 21 | #include "module.h" 22 | 23 | 24 | /* Defines ===================================================================================== */ 25 | 26 | /* define if handle 6 or 8 channel mods */ 27 | #define HANDLE_EXTRA_CHANNELS 28 | /* define if handle panning */ 29 | #define HANDLE_PANNING 30 | /* define if want a little cross-talk between sides (only if no panning) */ 31 | //#define CROSS_TALK 240 32 | 33 | 34 | /* Function prototypes ========================================================================= */ 35 | 36 | /* 37 | Called by sound sub-system at 50 Hz (or BPM * 2 / 5) 38 | returns 1 if not playing 39 | */ 40 | uint16_t PlayMOD(Mod_t* Mod_p); 41 | 42 | static void UpdateNote(Mod_t* Mod_p, uint32_t* Div_p); 43 | static void UpdateEffect(Mod_t* Mod_p); 44 | static void DoVibrato(Mod_t* Mod_p, uint8_t Channel); 45 | static void DoTremolo(Mod_t* Mod_p, uint8_t Channel); 46 | static void DoPorta(Mod_t* Mod_p, uint8_t Channel); 47 | 48 | /* 49 | System specific functions 50 | */ 51 | static Mod_t* SetMOD(Mod_t* Mod_p); 52 | static void SetSample(uint16_t Channel, uint8_t Sample); 53 | static void SetFrequency(uint16_t Channel, uint32_t Period); 54 | static void SetVolume(uint16_t Channel, uint32_t Volume, int32_t Pan); 55 | 56 | 57 | /* Global variables ============================================================================ */ 58 | 59 | /* 60 | From Amiga ProTracker playroutine source code 61 | 62 | - Amiga PERIODS represent delay times before fetching the next sample (in units of system clock ticks @ ~3.5 MHz) 63 | */ 64 | static const uint16_t AmigaPeriodsTable[ MOD_FINE_TUNE_VALUES ][ MOD_NUMBER_OF_NOTES ] = 65 | { 66 | /* ; Tuning 0, Normal */ 67 | { 856,808,762,720,678,640,604,570,538,508,480,453, /* ; C-1 to B-1 */ 68 | 428,404,381,360,339,320,302,285,269,254,240,226, /* ; C-2 to B-2 */ 69 | 214,202,190,180,170,160,151,143,135,127,120,113 }, /* ; C-3 to B-3 */ 70 | /* ; Tuning 1 */ 71 | { 850,802,757,715,674,637,601,567,535,505,477,450, /* ; same as above */ 72 | 425,401,379,357,337,318,300,284,268,253,239,225, /* ; but with */ 73 | 213,201,189,179,169,159,150,142,134,126,119,113 }, /* ; finetune +1 */ 74 | /* ; Tuning 2 */ 75 | { 844,796,752,709,670,632,597,563,532,502,474,447, /* ; etc, */ 76 | 422,398,376,355,335,316,298,282,266,251,237,224, /* ; finetune +2 */ 77 | 211,199,188,177,167,158,149,141,133,125,118,112 }, 78 | /* ; Tuning 3 */ 79 | { 838,791,746,704,665,628,592,559,528,498,470,444, 80 | 419,395,373,352,332,314,296,280,264,249,235,222, 81 | 209,198,187,176,166,157,148,140,132,125,118,111 }, 82 | /* ; Tuning 4 */ 83 | { 832,785,741,699,660,623,588,555,524,495,467,441, 84 | 416,392,370,350,330,312,294,278,262,247,233,220, 85 | 208,196,185,175,165,156,147,139,131,124,117,110 }, 86 | /* ; Tuning 5 */ 87 | { 826,779,736,694,655,619,584,551,520,491,463,437, 88 | 413,390,368,347,328,309,292,276,260,245,232,219, 89 | 206,195,184,174,164,155,146,138,130,123,116,109 }, 90 | /* ; Tuning 6 */ 91 | { 820,774,730,689,651,614,580,547,516,487,460,434, 92 | 410,387,365,345,325,307,290,274,258,244,230,217, 93 | 205,193,183,172,163,154,145,137,129,122,115,109 }, 94 | /* ; Tuning 7 */ 95 | { 814,768,725,684,646,610,575,543,513,484,457,431, 96 | 407,384,363,342,323,305,288,272,256,242,228,216, 97 | 204,192,181,171,161,152,144,136,128,121,114,108 }, 98 | /* ; Tuning -8 */ 99 | { 907,856,808,762,720,678,640,604,570,538,508,480, 100 | 453,428,404,381,360,339,320,302,285,269,254,240, 101 | 226,214,202,190,180,170,160,151,143,135,127,120 }, 102 | /* ; Tuning -7 */ 103 | { 900,850,802,757,715,675,636,601,567,535,505,477, 104 | 450,425,401,379,357,337,318,300,284,268,253,238, 105 | 225,212,200,189,179,169,159,150,142,134,126,119 }, 106 | /* ; Tuning -6 */ 107 | { 894,844,796,752,709,670,632,597,563,532,502,474, 108 | 447,422,398,376,355,335,316,298,282,266,251,237, 109 | 223,211,199,188,177,167,158,149,141,133,125,118 }, 110 | /* ; Tuning -5 */ 111 | { 887,838,791,746,704,665,628,592,559,528,498,470, 112 | 444,419,395,373,352,332,314,296,280,264,249,235, 113 | 222,209,198,187,176,166,157,148,140,132,125,118 }, 114 | /* ; Tuning -4 */ 115 | { 881,832,785,741,699,660,623,588,555,524,494,467, 116 | 441,416,392,370,350,330,312,294,278,262,247,233, 117 | 220,208,196,185,175,165,156,147,139,131,123,117 }, 118 | /* ; Tuning -3 */ 119 | { 875,826,779,736,694,655,619,584,551,520,491,463, 120 | 437,413,390,368,347,328,309,292,276,260,245,232, 121 | 219,206,195,184,174,164,155,146,138,130,123,116 }, 122 | /* ; Tuning -2 */ 123 | { 868,820,774,730,689,651,614,580,547,516,487,460, 124 | 434,410,387,365,345,325,307,290,274,258,244,230, 125 | 217,205,193,183,172,163,154,145,137,129,122,115 }, 126 | /* ; Tuning -1 */ 127 | { 862,814,768,725,684,646,610,575,543,513,484,457, 128 | 431,407,384,363,342,323,305,288,272,256,242,228, 129 | 216,203,192,181,171,161,152,144,136,128,121,114 } 130 | }; 131 | 132 | 133 | /* 134 | From Amiga ProTracker playroutine source code 135 | 136 | for Vibrato and Tremolo 137 | */ 138 | static const uint8_t SineTable[ 32 ] = 139 | { 140 | 0, 24, 49, 74, 97, 120, 141, 161, 141 | 180, 197, 212, 224, 235, 244, 250, 253, 142 | 255, 253, 250, 244, 235, 224, 212, 197, 143 | 180, 161, 141, 120, 97, 74, 49, 24 144 | }; 145 | 146 | 147 | /* 148 | Global volume for playing Modules 149 | */ 150 | static uint8_t gVolume = 12; 151 | 152 | static Mod_t *gMod = NULL; 153 | 154 | typedef struct voice { 155 | uint8_t pending; 156 | uint8_t vol; 157 | uint8_t pan; 158 | uint8_t start; 159 | uint16_t offset; 160 | uint16_t loop; 161 | uint32_t period; 162 | uint8_t factor; 163 | } voice_t; 164 | 165 | #define PENDING_START 1 166 | #define PENDING_STOP 2 167 | #define PENDING_VOL 4 168 | #define PENDING_PER 8 169 | 170 | voice_t voices[8]; 171 | 172 | static void callback(void); 173 | 174 | 175 | /* Module handler ============================================================================== */ 176 | 177 | /* 178 | Initialize Module structure 179 | */ 180 | uint8_t InitMOD(CDFileHandle_t *File_p, Mod_t* Mod_p, uint8_t filter) 181 | { 182 | const uint8_t ModSignature[ MOD_SIGNATURE_LENGTH ] = {'M', '.', 'K', '.'}; 183 | const uint8_t AltSignature[ MOD_SIGNATURE_LENGTH ] = {'4', 'C', 'H', 'N'}; 184 | const uint8_t FltSignature[ MOD_SIGNATURE_LENGTH ] = {'F', 'L', 'T', '4'}; 185 | #ifdef HANDLE_EXTRA_CHANNELS 186 | const uint8_t Mod6Signature[ MOD_SIGNATURE_LENGTH ] = {'6', 'C', 'H', 'N'}; 187 | const uint8_t Mod8Signature[ MOD_SIGNATURE_LENGTH ] = {'8', 'C', 'H', 'N'}; 188 | #endif 189 | uint8_t Cnt, Iw, Ix; 190 | uint8_t Sample; 191 | uint8_t Order; 192 | uint32_t Soffset, Iy, Iz; 193 | uint8_t *Data_p = (uint8_t *)0x0C0000; /* use word ram for MOD processing */ 194 | 195 | memset( Mod_p, 0, sizeof(Mod_t) ); /* main data structure set to 0 */ 196 | 197 | /* load MOD header from CD */ 198 | if (File_p->Read(File_p, Data_p, 1084) != 1084) 199 | return 1; /* couldn't load MOD header */ 200 | 201 | /* check signature */ 202 | for ( Cnt = 0; Cnt < MOD_SIGNATURE_LENGTH; Cnt++ ) 203 | { 204 | if ( ModSignature[ Cnt ] != Data_p[ Cnt + MOD_SIGNATURE_OFFSET ] ) 205 | { 206 | break; /* no match */ 207 | } 208 | } 209 | if ( MOD_SIGNATURE_LENGTH == Cnt ) 210 | { 211 | Mod_p->NumberOfChannels = 4; /* we have a 4 channel mod */ 212 | } 213 | 214 | if ( !Mod_p->NumberOfChannels ) 215 | { 216 | /* check for alternate 4 channel signature */ 217 | for ( Cnt = 0; Cnt < MOD_SIGNATURE_LENGTH; Cnt++ ) 218 | { 219 | if ( AltSignature[ Cnt ] != Data_p[ Cnt + MOD_SIGNATURE_OFFSET ] ) 220 | { 221 | break; /* no match */ 222 | } 223 | } 224 | if ( MOD_SIGNATURE_LENGTH == Cnt ) 225 | { 226 | Mod_p->NumberOfChannels = 4; /* we have a 4 channel mod */ 227 | } 228 | } 229 | 230 | if ( !Mod_p->NumberOfChannels ) 231 | { 232 | /* check for StarTrekker 4 channel signature */ 233 | for ( Cnt = 0; Cnt < MOD_SIGNATURE_LENGTH; Cnt++ ) 234 | { 235 | if ( FltSignature[ Cnt ] != Data_p[ Cnt + MOD_SIGNATURE_OFFSET ] ) 236 | { 237 | break; /* no match */ 238 | } 239 | } 240 | if ( MOD_SIGNATURE_LENGTH == Cnt ) 241 | { 242 | Mod_p->NumberOfChannels = 4; /* we have a 4 channel mod */ 243 | } 244 | } 245 | 246 | #ifdef HANDLE_EXTRA_CHANNELS 247 | if ( !Mod_p->NumberOfChannels ) 248 | { 249 | /* check for 6 channel signature */ 250 | for ( Cnt = 0; Cnt < MOD_SIGNATURE_LENGTH; Cnt++ ) 251 | { 252 | if ( Mod6Signature[ Cnt ] != Data_p[ Cnt + MOD_SIGNATURE_OFFSET ] ) 253 | { 254 | break; /* no match */ 255 | } 256 | } 257 | if ( MOD_SIGNATURE_LENGTH == Cnt ) 258 | { 259 | Mod_p->NumberOfChannels = 6; /* we have a 6 channel mod */ 260 | } 261 | } 262 | 263 | if ( !Mod_p->NumberOfChannels ) 264 | { 265 | /* check for 8 channel signature */ 266 | for ( Cnt = 0; Cnt < MOD_SIGNATURE_LENGTH; Cnt++ ) 267 | { 268 | if ( Mod8Signature[ Cnt ] != Data_p[ Cnt + MOD_SIGNATURE_OFFSET ] ) 269 | { 270 | break; /* no match */ 271 | } 272 | } 273 | if ( MOD_SIGNATURE_LENGTH == Cnt ) 274 | { 275 | Mod_p->NumberOfChannels = 8; /* we have a 8 channel mod */ 276 | } 277 | } 278 | #endif 279 | 280 | if ( !Mod_p->NumberOfChannels ) 281 | { 282 | return 2; /* not recognized, or not supported number of channels */ 283 | } 284 | 285 | memcpy(Mod_p->Title, Data_p, 20); /* copy the module title */ 286 | if (!Mod_p->Title[0]) 287 | strcpy(Mod_p->Title, "untitled"); 288 | 289 | for ( Sample = 0; Sample < MOD_NUMBER_OF_SAMPLES; Sample++ ) 290 | { 291 | uint8_t Byte1, Byte2; 292 | 293 | Byte1 = Data_p[ 20 + 22 + 30 * Sample ]; 294 | Byte2 = Data_p[ 20 + 23 + 30 * Sample ]; 295 | Mod_p->Inst[ Sample ].SampleLength = ( (Byte1 * 0x0100) + Byte2 ) * 2; 296 | 297 | Mod_p->Inst[ Sample ].FineTune = Data_p[ 20 + 24 + 30 * Sample ]; 298 | 299 | Mod_p->Inst[ Sample ].Volume = Data_p[ 20 + 25 + 30 * Sample ]; 300 | 301 | Byte1 = Data_p[ 20 + 26 + 30 * Sample ]; 302 | Byte2 = Data_p[ 20 + 27 + 30 * Sample ]; 303 | Mod_p->Inst[ Sample ].LoopStart = ( (Byte1 * 0x0100) + Byte2 ) * 2; 304 | 305 | Byte1 = Data_p[ 20 + 28 + 30 * Sample ]; 306 | Byte2 = Data_p[ 20 + 29 + 30 * Sample ]; 307 | Mod_p->Inst[ Sample ].LoopLength = ( (Byte1 * 0x0100) + Byte2 ) * 2; 308 | } /* for Sample */ 309 | 310 | Mod_p->SongLength = Data_p[ 950 ]; /* this is the number of orders in a song */ 311 | 312 | /* get NumberOfPatterns */ 313 | Mod_p->NumberOfPatterns = 0; 314 | for ( Order = 0; Order < MOD_NUMBER_OF_ORDERS; Order++ ) 315 | { 316 | Mod_p->Orders[ Order ] = Data_p[ 952 + Order ]; 317 | if ( Mod_p->Orders[ Order ] > Mod_p->NumberOfPatterns ) 318 | { 319 | Mod_p->NumberOfPatterns = Mod_p->Orders[ Order ]; 320 | } 321 | } /* for Order */ 322 | 323 | Mod_p->NumberOfPatterns += 1; /* [ 0 ; Mod_p->NumberOfPatterns ] */ 324 | 325 | /* allocate memory for patterns */ 326 | Mod_p->PatternsBuff_p = (uint32_t*)malloc(Mod_p->NumberOfPatterns * MOD_ROWS_PER_CHANNEL * Mod_p->NumberOfChannels * 4); 327 | if (!Mod_p->PatternsBuff_p) 328 | return 3; /* couldn't allocate pattern buffer */ 329 | 330 | /* Pattern data starts at offset 1084 - read into pattern buffer */ 331 | if ( File_p->Read(File_p, Mod_p->PatternsBuff_p, Mod_p->NumberOfPatterns * MOD_ROWS_PER_CHANNEL * Mod_p->NumberOfChannels * 4) != (Mod_p->NumberOfPatterns * MOD_ROWS_PER_CHANNEL * Mod_p->NumberOfChannels * 4) ) 332 | { 333 | free(Mod_p->PatternsBuff_p); 334 | return 4; /* couldn't read pattern data */ 335 | } 336 | 337 | #ifdef PROC_PATTERNS 338 | { 339 | uint8_t Pattern, Row, Chan; 340 | for (Pattern = 0; Pattern < Mod_p->NumberOfPatterns; Pattern++) 341 | { 342 | for (Row = 0; Row < MOD_ROWS_PER_CHANNEL; Row++) 343 | { 344 | uint32_t *Div_p = &Mod_p->PatternsBuff_p[ Pattern * MOD_ROWS_PER_CHANNEL * Mod_p->NumberOfChannels + Row * Mod_p->NumberOfChannels ]; 345 | 346 | for ( Chan = 0; Chan < Mod_p->NumberOfChannels; Chan++ ) 347 | { 348 | uint32_t division; 349 | uint16_t PeriodFrequency; 350 | uint8_t Cnt; 351 | 352 | /* Amiga-fied mess ;-) */ 353 | division = Div_p[ Chan ]; 354 | /* 355 | +-------------------------------------+ 356 | | Byte 0 Byte 1 Byte 2 Byte 3 | 357 | |-------------------------------------| 358 | |aaaaBBBB CCCCCCCCC DDDDeeee FFFFFFFFF| 359 | +-------------------------------------+ 360 | aaaaDDDD = sample number 361 | BBBBCCCCCCCC = sample period value 362 | eeee = effect number 363 | FFFFFFFF = effect parameters 364 | */ 365 | PeriodFrequency = ((division >> 16) & 0x0FFF); 366 | 367 | /* look up table here */ 368 | for ( Cnt = 0; Cnt < MOD_NUMBER_OF_NOTES; Cnt++ ) 369 | { 370 | if ( AmigaPeriodsTable[ 0 ][ Cnt ] == PeriodFrequency ) 371 | { 372 | PeriodFrequency = Cnt + 4060; 373 | /* insert new period */ 374 | division &= 0xF000FFFF; 375 | division |= PeriodFrequency << 16; 376 | Div_p[ Chan ] = division; 377 | break; 378 | } 379 | } 380 | } 381 | } 382 | } 383 | } 384 | #endif 385 | 386 | /* fit samples to sound ram */ 387 | Ix = Iz = 0; 388 | do 389 | { 390 | if (Iz > MOD_PCM_RAM) 391 | { 392 | // find largest sample and cut it in half 393 | Iy = 0xFFFFFFFF; 394 | Iz = 0; 395 | for ( Sample = 0; Sample < MOD_NUMBER_OF_SAMPLES; Sample++ ) 396 | { 397 | if ((Mod_p->Inst[ Sample ].SampleLength >> Mod_p->Inst[ Sample ].Factor) <= (1024 - 32)) 398 | continue; // skip small samples 399 | if (Mod_p->Inst[ Sample ].Factor != Ix) 400 | continue; // skip while some samples still not resampled 401 | if ((Mod_p->Inst[ Sample ].SampleLength >> Mod_p->Inst[ Sample ].Factor) > Iz) 402 | { 403 | Iy = Sample; 404 | Iz = Mod_p->Inst[ Sample ].SampleLength >> Mod_p->Inst[ Sample ].Factor; 405 | } 406 | } 407 | if (Iy == 0xFFFFFFFF) 408 | Ix++; // next level of resampling 409 | else 410 | Mod_p->Inst[ Iy ].Factor++; 411 | } 412 | 413 | Iz = 0; 414 | /* find total size of samples given factors */ 415 | for ( Sample = 0; Sample < MOD_NUMBER_OF_SAMPLES; Sample++ ) 416 | Iz += ((Mod_p->Inst[ Sample ].SampleLength >> Mod_p->Inst[ Sample ].Factor) + 32 + 255) & ~255; 417 | } while (Iz > MOD_PCM_RAM); 418 | 419 | /* upload samples to sound ram */ 420 | pcm_reset(); 421 | for ( Sample = 0, Iw = 0; Sample < MOD_NUMBER_OF_SAMPLES; Sample++ ) 422 | { 423 | if ( Mod_p->Inst[ Sample ].SampleLength > 0 ) 424 | { 425 | /* load sample data */ 426 | File_p->Read(File_p, Data_p, Mod_p->Inst[ Sample ].SampleLength); 427 | if (Mod_p->Inst[ Sample ].Factor) 428 | { 429 | /* resample data */ 430 | if (filter == 1) 431 | { 432 | /* filter coeff = .25, .5, .25 */ 433 | for (Ix = 0; Ix < Mod_p->Inst[ Sample ].Factor; Ix++) 434 | { 435 | int16_t s = 0, a = 0; 436 | Iy = Iz = 0; 437 | Soffset = Mod_p->Inst[ Sample ].SampleLength >> Ix; 438 | while (Soffset <= Mod_p->Inst[ Sample ].SampleLength) 439 | { 440 | s = a + ((int8_t)Data_p[Iy++] >> 1); 441 | a = (int8_t)Data_p[Iy++] >> 2; 442 | Data_p[Iz++] = s + a; 443 | Soffset -= 2; 444 | } 445 | } 446 | } 447 | else if (filter == 2) 448 | { 449 | /* filter coeff = .5, .25, .125 */ 450 | for (Ix = 0; Ix < Mod_p->Inst[ Sample ].Factor; Ix++) 451 | { 452 | int16_t a = 0; 453 | Iy = Iz = 0; 454 | Soffset = Mod_p->Inst[ Sample ].SampleLength >> Ix; 455 | while (Soffset <= Mod_p->Inst[ Sample ].SampleLength) 456 | { 457 | a = (a + (int8_t)Data_p[Iy++]) >> 1; 458 | Data_p[Iz++] = a; 459 | a = (a + (int8_t)Data_p[Iy++]) >> 1; 460 | Soffset -= 2; 461 | } 462 | } 463 | } 464 | else 465 | { 466 | /* regular downsample by N (running average) */ 467 | Iy = Iz = 0; 468 | Soffset = Mod_p->Inst[ Sample ].SampleLength; 469 | while (Soffset > 0) 470 | { 471 | int16_t s = 0; 472 | for (Ix = 0; (Ix < (1 << Mod_p->Inst[ Sample ].Factor)) && Soffset; Ix++, Soffset--) 473 | s += (int8_t)Data_p[Iy++]; 474 | Data_p[Iz++] = s >> Mod_p->Inst[ Sample ].Factor; 475 | } 476 | } 477 | } 478 | 479 | Mod_p->Inst[ Sample ].SampleHandle = Iw; 480 | pcm_load_samples(Iw, (int8_t*)Data_p, Mod_p->Inst[ Sample ].SampleLength >> Mod_p->Inst[ Sample ].Factor); 481 | Iw = pcm_next_block(Iw, Mod_p->Inst[ Sample ].SampleLength >> Mod_p->Inst[ Sample ].Factor); 482 | } 483 | else 484 | { 485 | Mod_p->Inst[ Sample ].SampleHandle = 0xFF; 486 | } 487 | } /* for Sample */ 488 | Mod_p->NextBlock = Iw; /* save last returned next pcm block value */ 489 | 490 | memset(voices, 0, sizeof(voices)); 491 | 492 | Mod_p->Speed = 6; /* default speed */ 493 | Mod_p->Tick = 6; /* force immediate processing */ 494 | Mod_p->Order = 0; /* default order */ 495 | Mod_p->Row = 0; /* default row */ 496 | Mod_p->TimeScale = 0x10; /* 1.0 */ 497 | Mod_p->BeatsPerMinute = 125; 498 | Mod_p->IsPlaying = 0; 499 | 500 | return 0; /* no errors */ 501 | } 502 | 503 | 504 | /* 505 | Cleanup MOD - stop playing and release voices 506 | */ 507 | void ExitMOD(Mod_t* Mod_p) 508 | { 509 | if (Mod_p) 510 | { 511 | if (Mod_p->IsPlaying) 512 | StopMOD(Mod_p); 513 | 514 | /* quiet sound channels */ 515 | pcm_reset(); 516 | 517 | free(Mod_p->PatternsBuff_p); 518 | 519 | /* make sure struct cannot be reused without init */ 520 | memset(Mod_p, 0, sizeof(Mod_t)); 521 | } 522 | } 523 | 524 | 525 | /* 526 | Start playing Module 527 | loop = bool = TRUE = loop forever, = FALSE = play once 528 | returns the previously set Module, if any 529 | */ 530 | Mod_t* StartMOD(Mod_t* Mod_p, uint8_t loop) 531 | { 532 | if (Mod_p) 533 | { 534 | pcm_start_timer(&callback); 535 | Mod_p->IsPlaying = loop ? 0x81 : 1; 536 | return SetMOD(Mod_p); 537 | } 538 | return (Mod_t*)gMod; 539 | } 540 | 541 | 542 | /* 543 | Pause/resume Module 544 | pause = bool = TRUE = pause, = FALSE = resume 545 | */ 546 | void PauseMOD(Mod_t* Mod_p, uint8_t pause) 547 | { 548 | uint8_t i; 549 | 550 | if (Mod_p) 551 | { 552 | if ( pause ) 553 | { 554 | asm("move.w #0x2700,%sr"); 555 | Mod_p->IsPlaying &= 0xFE; 556 | for (i=0; iNumberOfChannels; i++) 557 | { 558 | pcm_set_ctrl(0xC0 + i); 559 | pcm_set_env(0); 560 | } 561 | asm("move.w #0x2000,%sr"); 562 | } 563 | else 564 | { 565 | asm("move.w #0x2700,%sr"); 566 | Mod_p->IsPlaying |= 1; 567 | for (i=0; iNumberOfChannels; i++) 568 | voices[i].pending |= PENDING_VOL; 569 | asm("move.w #0x2000,%sr"); 570 | } 571 | } 572 | } 573 | 574 | 575 | /* 576 | Stop playing Module 577 | */ 578 | void StopMOD(Mod_t* Mod_p) 579 | { 580 | if (Mod_p) 581 | { 582 | pcm_stop_timer(); 583 | Mod_p->IsPlaying = 0; 584 | SetMOD(0); 585 | } 586 | } 587 | 588 | 589 | /* 590 | Wait for Module to play through once 591 | */ 592 | void WaitMOD(Mod_t* Mod_p) 593 | { 594 | do 595 | { 596 | if (!Mod_p->IsPlaying) 597 | break; 598 | if (Mod_p->IsPlaying & 0x80) 599 | if (!Mod_p->Row && !Mod_p->Order) 600 | break; 601 | } while (1); 602 | } 603 | 604 | 605 | /* 606 | Check Module play status 607 | returns 0 if done playing 608 | */ 609 | uint8_t CheckMOD(Mod_t* Mod_p) 610 | { 611 | if (Mod_p->IsPlaying & 0x80) 612 | return (!Mod_p->Row && !Mod_p->Order) ? 0 : 1; 613 | return Mod_p->IsPlaying; 614 | } 615 | 616 | 617 | /* 618 | Set global volume 619 | vol = 0 = off, = 16 = max 620 | returns the previous volume setting 621 | */ 622 | uint8_t VolumeMOD(uint8_t vol) 623 | { 624 | uint8_t prev = gVolume; 625 | gVolume = vol > 16 ? 16: vol; 626 | return prev; 627 | } 628 | 629 | 630 | /* 631 | Set current position in Module 632 | order = position to start playing at 633 | returns the current order playing 634 | */ 635 | uint8_t JumpMOD(Mod_t* Mod_p, uint8_t order) 636 | { 637 | if (!Mod_p) 638 | return 0; 639 | 640 | uint8_t rv = Mod_p->Order; 641 | 642 | Mod_p->Tick = Mod_p->Speed; /* force immediate processing */ 643 | Mod_p->Order = order; 644 | Mod_p->Row = 0; 645 | 646 | return rv; 647 | } 648 | 649 | 650 | /* 651 | Set current BPM scale for Module 652 | scale = 2.4 fixed point number for scaling BPM 653 | returns the current scale 654 | */ 655 | uint8_t TimeScaleMOD(Mod_t* Mod_p, uint8_t scale) 656 | { 657 | if (!Mod_p) 658 | return 0x10; 659 | 660 | uint8_t rv = Mod_p->TimeScale; 661 | 662 | Mod_p->TimeScale = scale < 0x40 ? scale : 0x3F; 663 | 664 | return rv; 665 | } 666 | 667 | /* 668 | +----------------------------------------------------------------------------+ 669 | | === Playing the MOD === | 670 | +----------------------------------------------------------------------------+ 671 | 672 | timer function, it contains the player logic, it has to be as fast as possible 673 | 674 | The SPEED of a song is the base on how your mod is played. 675 | Each row of a pattern is updated every SPEED number of clock ticks. 676 | */ 677 | uint16_t PlayMOD(Mod_t* Mod_p) 678 | { 679 | Mod_p->Pattern = Mod_p->Orders[ Mod_p->Order ]; /* pattern for current order */ 680 | 681 | Mod_p->Tick++; 682 | 683 | if (Mod_p->Tick >= Mod_p->Speed) 684 | { 685 | if ( !Mod_p->PatternDelay ) /* if there is no pattern delay */ 686 | { 687 | UpdateNote( Mod_p, &Mod_p->PatternsBuff_p[ Mod_p->Pattern * MOD_ROWS_PER_CHANNEL * Mod_p->NumberOfChannels + Mod_p->Row * Mod_p->NumberOfChannels ] ); /* process 1 note for every channel */ 688 | Mod_p->Row += 1; /* increment row */ 689 | } 690 | else 691 | { 692 | Mod_p->PatternDelay--; /* else decrement pattern delay */ 693 | } 694 | 695 | Mod_p->Tick = 0; 696 | 697 | if ( Mod_p->Row >= MOD_ROWS_PER_CHANNEL ) 698 | { 699 | /* process next pattern */ 700 | Mod_p->Order++; 701 | if (Mod_p->Order >= Mod_p->SongLength) 702 | { 703 | /* finished playing the mod */ 704 | if (Mod_p->IsPlaying & 0x80) 705 | { 706 | /* loop - reset to defaults */ 707 | Mod_p->Speed = 6; 708 | Mod_p->Tick = 6; /* force immediate processing */ 709 | Mod_p->Order = 0; 710 | Mod_p->Row = 0; 711 | Mod_p->TimeScale = 0x10; 712 | Mod_p->BeatsPerMinute = 125; 713 | } 714 | else 715 | { 716 | Mod_p->IsPlaying = 0; /* song done playing */ 717 | } 718 | } 719 | else 720 | { 721 | Mod_p->Pattern = Mod_p->Orders[ Mod_p->Order ]; 722 | Mod_p->Row = 0; 723 | } 724 | } 725 | } 726 | else 727 | { 728 | Mod_p->Row -= 1; /* decrement row */ 729 | /* update the tick based effects */ 730 | UpdateEffect( Mod_p ); 731 | Mod_p->Row += 1; /* increment row */ 732 | } 733 | 734 | return Mod_p->IsPlaying ? 0 : 1; 735 | } 736 | 737 | 738 | /* 739 | process division - 1 note for every channel 740 | */ 741 | static void UpdateNote(Mod_t* Mod_p, uint32_t* Div_p) 742 | { 743 | uint8_t Channel; /* counter */ 744 | uint8_t EParX; /* Effect Parameter X */ 745 | uint8_t EParY; /* Effect Parameter Y */ 746 | uint8_t JumpFlag, BreakFlag; /* boolean flags for effects JUMP_TO_PATTERN and PATTERN_BREAK on the same row */ 747 | #ifndef PROC_PATTERNS 748 | uint8_t Cnt; 749 | #endif 750 | 751 | JumpFlag = FALSE; 752 | BreakFlag = FALSE; 753 | 754 | for ( Channel = 0; Channel < Mod_p->NumberOfChannels; Channel++ ) 755 | { 756 | uint32_t division; 757 | 758 | /* Amiga-fied mess ;-) */ 759 | division = Div_p[ Channel ]; 760 | /* 761 | +-------------------------------------+ 762 | | Byte 0 Byte 1 Byte 2 Byte 3 | 763 | |-------------------------------------| 764 | |aaaaBBBB CCCCCCCCC DDDDeeee FFFFFFFFF| 765 | +-------------------------------------+ 766 | aaaaDDDD = sample number 767 | BBBBCCCCCCCC = sample period value 768 | eeee = effect number 769 | FFFFFFFF = effect parameters 770 | */ 771 | Mod_p->Notes[ Channel ].SampleNumber = ((division >> 24) & 0x00F0) | ((division >> 12) & 0x000F); 772 | Mod_p->Notes[ Channel ].PeriodFrequency = ((division >> 16) & 0x0FFF); 773 | Mod_p->Notes[ Channel ].EffectNumber = (division >> 8) & 0x000F; 774 | Mod_p->Notes[ Channel ].EffectParameter = division & 0x00FF; 775 | Mod_p->Notes[ Channel ].NoteExists = FALSE; 776 | 777 | #ifdef PROC_PATTERNS 778 | if ( Mod_p->Notes[ Channel ].PeriodFrequency >= 4060 ) 779 | { 780 | Mod_p->Notes[ Channel ].NoteExists = TRUE; 781 | Mod_p->Notes[ Channel ].PeriodFrequencyCol = Mod_p->Notes[ Channel ].PeriodFrequency - 4060; 782 | Mod_p->Notes[ Channel ].PeriodFrequency = AmigaPeriodsTable[ 0 ][ Mod_p->Notes[ Channel ].PeriodFrequencyCol ]; 783 | } 784 | #else 785 | /* look up table here */ 786 | for ( Cnt = 0; Cnt < MOD_NUMBER_OF_NOTES; Cnt++ ) 787 | { 788 | if ( AmigaPeriodsTable[ 0 ][ Cnt ] == Mod_p->Notes[ Channel ].PeriodFrequency ) 789 | { 790 | Mod_p->Notes[ Channel ].NoteExists = TRUE; 791 | Mod_p->Notes[ Channel ].PeriodFrequencyCol = Cnt; 792 | break; 793 | } 794 | } /* for Cnt */ 795 | #endif 796 | 797 | EParX = Mod_p->Notes[ Channel ].EffectParameter >> 4; 798 | EParY = Mod_p->Notes[ Channel ].EffectParameter & 0xF; 799 | 800 | Mod_p->Channels[ Channel ].SampleOffset = 2; 801 | 802 | if ( Mod_p->Notes[ Channel ].SampleNumber > 0 ) 803 | { 804 | Mod_p->Channels[ Channel ].LastInstrument = Mod_p->Notes[ Channel ].SampleNumber - 1; 805 | 806 | if ( !( ( Mod_p->Notes[ Channel ].EffectNumber == EFFECT_EXTENDED_EFFECTS ) && ( EParX == EFFECT_DELAY_NOTE ) ) ) 807 | { 808 | Mod_p->Channels[ Channel ].Volume = Mod_p->Inst[ Mod_p->Notes[ Channel ].SampleNumber - 1 ].Volume; /* default volume of sample */ 809 | } 810 | } 811 | 812 | if ( Mod_p->Notes[ Channel ].NoteExists == TRUE ) 813 | { 814 | if ( ( Mod_p->Channels[ Channel ].WaveformControl & 0x0F ) < 4 ) 815 | { 816 | Mod_p->Channels[ Channel ].VibratoPosition = 0; 817 | } 818 | if ( ( Mod_p->Channels[ Channel ].WaveformControl >> 4 ) < 4 ) 819 | { 820 | Mod_p->Channels[ Channel ].TremoloPosition = 0; 821 | } 822 | 823 | /* if not a porta effect, then set the channels frequency to the looked up amiga value + or - any finetune */ 824 | if ( (Mod_p->Notes[ Channel ].EffectNumber != EFFECT_PORTA_TO_NOTE) && 825 | (Mod_p->Notes[ Channel ].EffectNumber != EFFECT_PORTA_PLUS_VOL_SLIDE) ) 826 | { 827 | uint8_t FineTune; 828 | 829 | FineTune = Mod_p->Inst[ Mod_p->Channels[ Channel ].LastInstrument ].FineTune; 830 | Mod_p->Channels[ Channel ].PeriodFrequency = AmigaPeriodsTable[ FineTune ][ Mod_p->Notes[ Channel ].PeriodFrequencyCol ]; 831 | } /* if */ 832 | 833 | //Mod_p->Channels[ Channel ].SampleOffset = 2; 834 | } /* if */ 835 | 836 | /* skip effects ? */ 837 | if ( !( ( Mod_p->Notes[ Channel ].EffectNumber == 0 ) && ( Mod_p->Notes[ Channel ].EffectParameter == 0 ) ) ) 838 | { 839 | /* process the non tick based effects also grab parameters for tick based effects */ 840 | switch ( Mod_p->Notes[ Channel ].EffectNumber ) 841 | { 842 | case EFFECT_ARPEGGIO: /* not processed on tick 0 */ 843 | case EFFECT_PORTA_UP: /* not processed on tick 0 */ 844 | case EFFECT_PORTA_DOWN: /* not processed on tick 0 */ 845 | break; 846 | 847 | case EFFECT_PORTA_TO_NOTE: /* reviewed OK */ 848 | if ( Mod_p->Notes[ Channel ].EffectParameter > 0 ) 849 | { 850 | Mod_p->Channels[ Channel ].PortaSpeed = Mod_p->Notes[ Channel ].EffectParameter; 851 | } 852 | /* no break statement here */ 853 | 854 | case EFFECT_PORTA_PLUS_VOL_SLIDE: /* reviewed OK */ 855 | if ( Mod_p->Notes[ Channel ].NoteExists == TRUE ) 856 | { 857 | Mod_p->Channels[ Channel ].PortaTo = Mod_p->Notes[ Channel ].PeriodFrequency; 858 | } 859 | break; 860 | 861 | case EFFECT_VIBRATO: /* reviewed OK */ 862 | if ( EParX > 0 ) 863 | { 864 | Mod_p->Channels[ Channel ].VibratoSpeed = EParX; 865 | } 866 | if ( EParY > 0 ) 867 | { 868 | Mod_p->Channels[ Channel ].VibratoDepth = EParY; 869 | } 870 | break; 871 | 872 | case EFFECT_VIBRATO_PLUS_VOL_SLIDE: /* not processed on tick 0 */ 873 | break; 874 | 875 | case EFFECT_TREMOLO: /* reviewed OK */ 876 | if ( EParX > 0 ) 877 | { 878 | Mod_p->Channels[ Channel ].TremoloSpeed = EParX; 879 | } 880 | if ( EParY > 0 ) 881 | { 882 | Mod_p->Channels[ Channel ].TremoloDepth = EParY; 883 | } 884 | break; 885 | 886 | case EFFECT_PAN: /* reviewed OK */ /* not a ProTracker effect */ 887 | Mod_p->Channels[ Channel ].PanValue = Mod_p->Notes[ Channel ].EffectParameter; 888 | break; 889 | 890 | case EFFECT_SAMPLE_OFFSET: /* reviewed OK */ 891 | if ( Mod_p->Notes[ Channel ].EffectParameter > 0 ) 892 | { 893 | Mod_p->Channels[ Channel ].SampleOffset = ( Mod_p->Notes[ Channel ].EffectParameter << 8 ); 894 | } 895 | if ( Mod_p->Channels[ Channel ].SampleOffset > Mod_p->Inst[ Mod_p->Channels[ Channel ].LastInstrument ].SampleLength ) 896 | { 897 | Mod_p->Channels[ Channel ].SampleOffset = Mod_p->Inst[ Mod_p->Channels[ Channel ].LastInstrument ].SampleLength; 898 | /* this will result in silence */ 899 | } 900 | break; 901 | 902 | case EFFECT_VOLUME_SLIDE: /* not processed on tick 0 */ 903 | break; 904 | 905 | case EFFECT_JUMP_TO_PATTERN: /* reviewed OK */ 906 | Mod_p->Order = Mod_p->Notes[ Channel ].EffectParameter; 907 | Mod_p->Row = -1; 908 | if ( Mod_p->Order >= Mod_p->SongLength ) 909 | { 910 | Mod_p->Order = 0; 911 | } 912 | // if ( Mod_p->Order == 0 ) 913 | // { 914 | /* loop the mod */ 915 | if (!(Mod_p->IsPlaying & 0x80)) 916 | { 917 | Mod_p->IsPlaying = 0; /* song done playing */ 918 | return; 919 | } 920 | // } 921 | JumpFlag = TRUE; 922 | break; 923 | 924 | case EFFECT_SET_VOLUME: /* reviewed OK */ 925 | Mod_p->Channels[ Channel ].Volume = Mod_p->Notes[ Channel ].EffectParameter; 926 | if ( Mod_p->Channels[ Channel ].Volume < 0 ) 927 | { 928 | Mod_p->Channels[ Channel ].Volume = 0; 929 | } 930 | if ( Mod_p->Channels[ Channel ].Volume > 64 ) 931 | { 932 | Mod_p->Channels[ Channel ].Volume = 64; 933 | } 934 | break; 935 | 936 | case EFFECT_PATTERN_BREAK: /* reviewed OK */ 937 | Mod_p->Row = ( EParX * 10 ) + EParY; /* get row number */ 938 | if ( Mod_p->Row == 0 ) 939 | { 940 | Mod_p->Row = -1; 941 | } 942 | else if ( Mod_p->Row >= MOD_ROWS_PER_CHANNEL ) 943 | { 944 | Mod_p->Row = -1; 945 | } 946 | else 947 | { 948 | Mod_p->Row -= 1; 949 | } 950 | 951 | if ( !JumpFlag && !BreakFlag ) 952 | { 953 | Mod_p->Order++; 954 | 955 | if ( Mod_p->Order >= Mod_p->SongLength ) 956 | { 957 | Mod_p->Order = 0; 958 | /* repeat the mod */ 959 | if (!(Mod_p->IsPlaying & 0x80)) 960 | { 961 | Mod_p->IsPlaying = 0; /* song done playing */ 962 | return; 963 | } 964 | } 965 | } 966 | BreakFlag = TRUE; 967 | break; 968 | 969 | case EFFECT_EXTENDED_EFFECTS: 970 | switch (EParX) 971 | { 972 | case EFFECT_FINE_PORTA_UP: /* reviewed OK */ 973 | Mod_p->Channels[ Channel ].PeriodFrequency -= EParY; 974 | break; 975 | 976 | case EFFECT_FINE_PORTA_DOWN: /* reviewed OK */ 977 | Mod_p->Channels[ Channel ].PeriodFrequency += EParY; 978 | break; 979 | 980 | case EFFECT_GLISSANDO_CONTROL: /* not implemented not even in fmoddoc2 */ 981 | break; 982 | 983 | case EFFECT_SET_VIBRATO_WAVEFORM: /* reviewed OK */ 984 | Mod_p->Channels[ Channel ].WaveformControl &= 0xF0; 985 | Mod_p->Channels[ Channel ].WaveformControl |= EParY; 986 | break; 987 | 988 | case EFFECT_SET_FINETUNE: /* reviewed OK */ 989 | Mod_p->Inst[ Mod_p->Channels[ Channel ].LastInstrument ].FineTune = EParY; 990 | break; 991 | 992 | case EFFECT_PATTERN_LOOP: /* reviewed OK */ 993 | if ( EParY == 0 ) 994 | { 995 | Mod_p->Channels[ Channel ].PatLoopRow = Mod_p->Row; 996 | } 997 | else 998 | { 999 | if ( ! Mod_p->Channels[ Channel ].PatLoopNo ) 1000 | { 1001 | Mod_p->Channels[ Channel ].PatLoopNo = EParY; 1002 | } 1003 | else 1004 | { 1005 | Mod_p->Channels[ Channel ].PatLoopNo--; 1006 | } 1007 | if ( Mod_p->Channels[ Channel ].PatLoopNo ) 1008 | { 1009 | Mod_p->Row = Mod_p->Channels[ Channel ].PatLoopRow - 1; 1010 | } 1011 | } 1012 | break; 1013 | 1014 | case EFFECT_SET_TREMOLO_WAVEFORM: /* reviewed OK */ 1015 | Mod_p->Channels[ Channel ].WaveformControl &= 0x0F; 1016 | Mod_p->Channels[ Channel ].WaveformControl |= (EParY << 4); 1017 | break; 1018 | 1019 | case EFFECT_POSITION_PANNING: /* cw - fixed */ 1020 | Mod_p->Channels[ Channel ].PanValue = (EParY << 4) | EParY; 1021 | break; 1022 | 1023 | case EFFECT_RETRIG_NOTE: /* not processed on tick 0 */ 1024 | break; 1025 | 1026 | case EFFECT_FINE_VOLUME_SLIDE_UP: /* reviewed OK */ 1027 | Mod_p->Channels[ Channel ].Volume += EParY; 1028 | if ( Mod_p->Channels[ Channel ].Volume > 64 ) 1029 | { 1030 | Mod_p->Channels[ Channel ].Volume = 64; 1031 | } 1032 | break; 1033 | 1034 | case EFFECT_FINE_VOLUME_SLIDE_DOWN: /* reviewed OK */ 1035 | Mod_p->Channels[ Channel ].Volume -= EParY; 1036 | if ( Mod_p->Channels[ Channel ].Volume < 0 ) 1037 | { 1038 | Mod_p->Channels[ Channel ].Volume = 0; 1039 | } 1040 | break; 1041 | 1042 | case EFFECT_CUT_NOTE: /* not processed on tick 0 */ 1043 | break; 1044 | 1045 | case EFFECT_DELAY_NOTE: /* not processed on tick 0 */ /* reviewed OK */ 1046 | continue; /* process next note */ 1047 | break; 1048 | 1049 | case EFFECT_PATTERN_DELAY: /* reviewed OK */ 1050 | Mod_p->PatternDelay = EParY; 1051 | break; 1052 | 1053 | default: 1054 | break; 1055 | } 1056 | break; 1057 | 1058 | case EFFECT_SET_SPEED: /* cw - update for bpm */ 1059 | if ( Mod_p->Notes[ Channel ].EffectParameter < 0x20 ) 1060 | { 1061 | if ( Mod_p->Notes[ Channel ].EffectParameter ) 1062 | { 1063 | Mod_p->Speed = Mod_p->Notes[ Channel ].EffectParameter; 1064 | } 1065 | } 1066 | else 1067 | { 1068 | Mod_p->BeatsPerMinute = Mod_p->Notes[ Channel ].EffectParameter; 1069 | } 1070 | break; 1071 | 1072 | default: 1073 | /* effect not defined */ 1074 | break; 1075 | } /* switch */ 1076 | } /* if */ 1077 | 1078 | if ( Mod_p->Notes[ Channel ].EffectNumber != EFFECT_SET_TREMOLO_WAVEFORM ) 1079 | { 1080 | /* set volume */ 1081 | SetVolume( Channel, Mod_p->Channels[ Channel ].Volume, Mod_p->Channels[ Channel ].PanValue ); 1082 | } 1083 | 1084 | if ( Mod_p->Channels[ Channel ].PeriodFrequency > 0 ) 1085 | { 1086 | /* set frequency */ 1087 | SetFrequency( Channel, Mod_p->Channels[ Channel ].PeriodFrequency ); 1088 | } 1089 | 1090 | if ( Mod_p->Notes[ Channel ].NoteExists == TRUE ) 1091 | { 1092 | if ( ( Mod_p->Notes[ Channel ].EffectNumber != EFFECT_PORTA_TO_NOTE ) && 1093 | ( Mod_p->Notes[ Channel ].EffectNumber != EFFECT_PORTA_PLUS_VOL_SLIDE ) ) 1094 | { 1095 | /* set sample */ 1096 | SetSample( Channel, Mod_p->Channels[ Channel ].LastInstrument ); 1097 | } 1098 | } /* if */ 1099 | } /* for Channel */ 1100 | 1101 | return; 1102 | } 1103 | 1104 | 1105 | /* 1106 | update tick based effects 1107 | */ 1108 | static void UpdateEffect(Mod_t* Mod_p) 1109 | { 1110 | uint8_t Channel; /* counter */ 1111 | uint8_t EParX; /* Effect Parameter X */ 1112 | uint8_t EParY; /* Effect Parameter Y */ 1113 | uint8_t Tick = Mod_p->Tick; /* current music tick */ 1114 | 1115 | for ( Channel = 0; Channel < Mod_p->NumberOfChannels; Channel++ ) 1116 | { 1117 | EParX = Mod_p->Notes[ Channel ].EffectParameter >> 4; 1118 | EParY = Mod_p->Notes[ Channel ].EffectParameter & 0xF; 1119 | 1120 | switch ( Mod_p->Notes[ Channel ].EffectNumber ) 1121 | { 1122 | 1123 | case EFFECT_ARPEGGIO: /* effect used in chip tunes */ /* reviewed OK */ 1124 | if ( Mod_p->Notes[ Channel ].EffectParameter > 0 ) 1125 | { 1126 | switch ( Tick % 3 ) 1127 | { 1128 | case 0: 1129 | SetFrequency( Channel, Mod_p->Channels[ Channel ].PeriodFrequency ); 1130 | break; 1131 | 1132 | case 1: 1133 | SetFrequency( Channel, Mod_p->Channels[ Channel ].PeriodFrequency + EParX ); 1134 | break; 1135 | 1136 | case 2: 1137 | SetFrequency( Channel, Mod_p->Channels[ Channel ].PeriodFrequency + EParY ); 1138 | break; 1139 | } /* switch */ 1140 | } /* if */ 1141 | break; 1142 | 1143 | case EFFECT_PORTA_UP: /* reviewed OK */ 1144 | Mod_p->Channels[ Channel ].PeriodFrequency -= Mod_p->Notes[ Channel ].EffectParameter; 1145 | if ( Mod_p->Channels[ Channel ].PeriodFrequency < 113 ) 1146 | { 1147 | Mod_p->Channels[ Channel ].PeriodFrequency = 113; /* stop at B-3 */ 1148 | } 1149 | SetFrequency( Channel, Mod_p->Channels[ Channel ].PeriodFrequency ); 1150 | break; 1151 | 1152 | case EFFECT_PORTA_DOWN: /* reviewed OK */ 1153 | Mod_p->Channels[ Channel ].PeriodFrequency += Mod_p->Notes[ Channel ].EffectParameter; 1154 | if ( Mod_p->Channels[ Channel ].PeriodFrequency > 856 ) 1155 | { 1156 | Mod_p->Channels[ Channel ].PeriodFrequency = 856; /* stop at C-1 */ 1157 | } 1158 | SetFrequency( Channel, Mod_p->Channels[ Channel ].PeriodFrequency ); 1159 | break; 1160 | 1161 | case EFFECT_PORTA_TO_NOTE: /* reviewed OK */ 1162 | DoPorta( Mod_p, Channel ); 1163 | break; 1164 | 1165 | case EFFECT_VIBRATO: /* reviewed OK */ 1166 | DoVibrato( Mod_p, Channel ); 1167 | break; 1168 | 1169 | case EFFECT_PORTA_PLUS_VOL_SLIDE: /* reviewed OK */ 1170 | DoPorta( Mod_p, Channel ); 1171 | 1172 | if ( EParX > 0 ) 1173 | { 1174 | Mod_p->Channels[ Channel ].Volume += EParX; 1175 | } 1176 | if ( EParY > 0 ) 1177 | { 1178 | Mod_p->Channels[ Channel ].Volume -= EParY; 1179 | } 1180 | if ( Mod_p->Channels[ Channel ].Volume < 0 ) 1181 | { 1182 | Mod_p->Channels[ Channel ].Volume = 0; 1183 | } 1184 | if ( Mod_p->Channels[ Channel ].Volume > 64 ) 1185 | { 1186 | Mod_p->Channels[ Channel ].Volume = 64; 1187 | } 1188 | SetVolume( Channel, Mod_p->Channels[ Channel ].Volume, Mod_p->Channels[ Channel ].PanValue ); 1189 | break; 1190 | 1191 | case EFFECT_VIBRATO_PLUS_VOL_SLIDE: /* reviewed OK */ 1192 | DoVibrato( Mod_p, Channel ); 1193 | 1194 | if ( EParX > 0 ) 1195 | { 1196 | Mod_p->Channels[ Channel ].Volume += EParX; 1197 | } 1198 | if ( EParY > 0 ) 1199 | { 1200 | Mod_p->Channels[ Channel ].Volume -= EParY; 1201 | } 1202 | if ( Mod_p->Channels[ Channel ].Volume < 0 ) 1203 | { 1204 | Mod_p->Channels[ Channel ].Volume = 0; 1205 | } 1206 | if ( Mod_p->Channels[ Channel ].Volume > 64 ) 1207 | { 1208 | Mod_p->Channels[ Channel ].Volume = 64; 1209 | } 1210 | SetVolume( Channel, Mod_p->Channels[ Channel ].Volume, Mod_p->Channels[ Channel ].PanValue ); 1211 | break; 1212 | 1213 | case EFFECT_TREMOLO: /* reviewed OK */ 1214 | DoTremolo( Mod_p, Channel ); 1215 | break; 1216 | 1217 | case EFFECT_VOLUME_SLIDE: /* reviewed OK */ 1218 | if ( EParX > 0 ) 1219 | { 1220 | Mod_p->Channels[ Channel ].Volume += EParX; 1221 | } 1222 | if ( EParY > 0 ) 1223 | { 1224 | Mod_p->Channels[ Channel ].Volume -= EParY; 1225 | } 1226 | if ( Mod_p->Channels[ Channel ].Volume < 0 ) 1227 | { 1228 | Mod_p->Channels[ Channel ].Volume = 0; 1229 | } 1230 | if ( Mod_p->Channels[ Channel ].Volume > 64 ) 1231 | { 1232 | Mod_p->Channels[ Channel ].Volume = 64; 1233 | } 1234 | SetVolume( Channel, Mod_p->Channels[ Channel ].Volume, Mod_p->Channels[ Channel ].PanValue ); 1235 | break; 1236 | 1237 | case EFFECT_EXTENDED_EFFECTS: 1238 | switch (EParX) 1239 | { 1240 | case EFFECT_RETRIG_NOTE: /* I am not 100% sure about this effect */ 1241 | if ( !EParY ) 1242 | { 1243 | break; 1244 | } 1245 | if ( !( Tick % EParY ) ) 1246 | { 1247 | SetSample( Channel, Mod_p->Channels[ Channel ].LastInstrument ); 1248 | } 1249 | break; 1250 | 1251 | case EFFECT_CUT_NOTE: /* reviewed OK */ 1252 | if ( Tick == EParY ) 1253 | { 1254 | Mod_p->Channels[ Channel ].Volume = 0; 1255 | SetVolume( Channel, Mod_p->Channels[ Channel ].Volume, Mod_p->Channels[ Channel ].PanValue ); 1256 | } 1257 | break; 1258 | 1259 | case EFFECT_DELAY_NOTE: /* I am not 100% sure about this effect */ 1260 | if ( Tick == EParY ) 1261 | { 1262 | if ( Mod_p->Notes[ Channel ].SampleNumber > 0 ) 1263 | { 1264 | Mod_p->Channels[ Channel ].Volume = Mod_p->Inst[ Mod_p->Channels[ Channel ].LastInstrument ].Volume; 1265 | } 1266 | if ( Mod_p->Inst[ Mod_p->Notes[ Channel ].SampleNumber - 1 ].Volume <= 64 ) 1267 | { 1268 | Mod_p->Channels[ Channel ].Volume = Mod_p->Inst[ Mod_p->Notes[ Channel ].SampleNumber - 1 ].Volume; 1269 | } 1270 | 1271 | SetFrequency( Channel, Mod_p->Channels[ Channel ].PeriodFrequency ); 1272 | SetVolume( Channel, Mod_p->Channels[ Channel ].Volume, Mod_p->Channels[ Channel ].PanValue ); 1273 | SetSample( Channel, Mod_p->Channels[ Channel ].LastInstrument ); 1274 | } /* if */ 1275 | break; 1276 | 1277 | default: 1278 | break; 1279 | } 1280 | break; 1281 | 1282 | default: 1283 | break; 1284 | } /* switch */ 1285 | } /* for Channel */ 1286 | 1287 | return; 1288 | } 1289 | 1290 | 1291 | /* 1292 | */ 1293 | static void DoVibrato(Mod_t* Mod_p, uint8_t Channel) 1294 | { 1295 | uint16_t Delta = 0; 1296 | uint8_t Temp; 1297 | 1298 | Temp = ( Mod_p->Channels[ Channel ].VibratoPosition & 31 ); /* Temp will be the index */ 1299 | 1300 | switch ( Mod_p->Channels[ Channel ].WaveformControl & 3 ) 1301 | { 1302 | case 0: 1303 | Delta = SineTable[ Temp ]; /* look up sine table */ 1304 | break; 1305 | 1306 | case 1: 1307 | Temp <<= 3; /* ramp down */ 1308 | if ( Mod_p->Channels[ Channel ].VibratoPosition < 0 ) 1309 | { 1310 | Temp = 255 - Temp; 1311 | } 1312 | Delta = Temp; 1313 | break; 1314 | 1315 | case 2: 1316 | Delta = 255; /* square */ 1317 | break; 1318 | 1319 | case 3: 1320 | Delta = rand() & 255; /* random */ 1321 | break; 1322 | } /* switch */ 1323 | 1324 | Delta *= Mod_p->Channels[ Channel ].VibratoDepth; 1325 | Delta >>= 7; 1326 | //Delta <<= 2; /* we use 4*periods so make vibrato 4 times bigger */ 1327 | 1328 | if ( Mod_p->Channels[ Channel ].VibratoPosition >= 0 ) 1329 | { 1330 | SetFrequency( Channel, Mod_p->Channels[ Channel ].PeriodFrequency + Delta ); 1331 | } 1332 | else 1333 | { 1334 | SetFrequency( Channel, Mod_p->Channels[ Channel ].PeriodFrequency - Delta ); 1335 | } 1336 | 1337 | Mod_p->Channels[ Channel ].VibratoPosition += Mod_p->Channels[ Channel ].VibratoSpeed; 1338 | if ( Mod_p->Channels[ Channel ].VibratoPosition > 31 ) 1339 | { 1340 | Mod_p->Channels[ Channel ].VibratoPosition -= 64; 1341 | } 1342 | 1343 | return; 1344 | } 1345 | 1346 | 1347 | /* 1348 | */ 1349 | static void DoTremolo(Mod_t* Mod_p, uint8_t Channel) 1350 | { 1351 | uint16_t Delta = 0; 1352 | uint8_t Temp; 1353 | 1354 | Temp = ( Mod_p->Channels[ Channel ].TremoloPosition & 31 ); /* Temp will be the index */ 1355 | 1356 | switch ( ( Mod_p->Channels[ Channel ].WaveformControl >> 4 ) & 3 ) 1357 | { 1358 | case 0: 1359 | Delta = SineTable[ Temp ]; /* look up sine table */ 1360 | break; 1361 | 1362 | case 1: 1363 | Temp <<= 3; /* ramp down */ 1364 | if ( Mod_p->Channels[ Channel ].TremoloPosition < 0 ) 1365 | { 1366 | Temp = 255 - Temp; 1367 | } 1368 | Delta = Temp; 1369 | break; 1370 | 1371 | case 2: 1372 | Delta = 255; /* square */ 1373 | break; 1374 | 1375 | case 3: 1376 | Delta = rand() & 255; /* random */ 1377 | break; 1378 | } /* switch */ 1379 | 1380 | Delta *= Mod_p->Channels[ Channel ].TremoloDepth; 1381 | Delta >>= 6; 1382 | 1383 | if ( Mod_p->Channels[ Channel ].TremoloPosition >= 0 ) 1384 | { 1385 | if ( Mod_p->Channels[ Channel ].Volume + Delta > 64 ) 1386 | { 1387 | Delta = 64 - Mod_p->Channels[ Channel ].Volume; 1388 | } 1389 | 1390 | SetVolume( Channel, Mod_p->Channels[ Channel ].Volume + Delta, Mod_p->Channels[ Channel ].PanValue ); 1391 | } 1392 | else 1393 | { 1394 | if ( (int16_t) ( Mod_p->Channels[ Channel ].Volume - Delta < 0 ) ) 1395 | { 1396 | Delta = Mod_p->Channels[ Channel ].Volume; 1397 | } 1398 | 1399 | SetVolume( Channel, Mod_p->Channels[ Channel ].Volume - Delta, Mod_p->Channels[ Channel ].PanValue ); 1400 | } 1401 | 1402 | Mod_p->Channels[ Channel ].TremoloPosition += Mod_p->Channels[ Channel ].TremoloSpeed; 1403 | if ( Mod_p->Channels[ Channel ].TremoloPosition > 31 ) 1404 | { 1405 | Mod_p->Channels[ Channel ].TremoloPosition -= 64; 1406 | } 1407 | 1408 | return; 1409 | } 1410 | 1411 | 1412 | /* 1413 | */ 1414 | static void DoPorta(Mod_t* Mod_p, uint8_t Channel) 1415 | { 1416 | /* slide pitch down if it needs too */ 1417 | if ( Mod_p->Channels[ Channel ].PeriodFrequency < Mod_p->Channels[ Channel ].PortaTo ) 1418 | { 1419 | Mod_p->Channels[ Channel ].PeriodFrequency += Mod_p->Channels[ Channel ].PortaSpeed; 1420 | if ( Mod_p->Channels[ Channel ].PeriodFrequency > Mod_p->Channels[ Channel ].PortaTo ) 1421 | { 1422 | Mod_p->Channels[ Channel ].PeriodFrequency = Mod_p->Channels[ Channel ].PortaTo; 1423 | } 1424 | } 1425 | 1426 | /* slide pitch up if it needs too */ 1427 | if ( Mod_p->Channels[ Channel ].PeriodFrequency > Mod_p->Channels[ Channel ].PortaTo ) 1428 | { 1429 | Mod_p->Channels[ Channel ].PeriodFrequency -= Mod_p->Channels[ Channel ].PortaSpeed; 1430 | if ( Mod_p->Channels[ Channel ].PeriodFrequency < Mod_p->Channels[ Channel ].PortaTo ) 1431 | { 1432 | Mod_p->Channels[ Channel ].PeriodFrequency = Mod_p->Channels[ Channel ].PortaTo; 1433 | } 1434 | } 1435 | 1436 | SetFrequency( Channel, Mod_p->Channels[ Channel ].PeriodFrequency ); 1437 | 1438 | return; 1439 | } 1440 | 1441 | 1442 | /* System specific functions =================================================================== */ 1443 | 1444 | 1445 | static Mod_t* SetMOD(Mod_t* Mod_p) 1446 | { 1447 | Mod_t *pmod = gMod; 1448 | /* once set, PlayMOD() starts being called */ 1449 | gMod = Mod_p; 1450 | return pmod; 1451 | } 1452 | 1453 | 1454 | static void SetSample(uint16_t Channel, uint8_t Sample) 1455 | { 1456 | uint8_t factor = gMod->Inst[ Sample ].Factor; 1457 | uint8_t start = gMod->Inst[ Sample ].SampleHandle; 1458 | uint32_t offset = gMod->Channels[ Channel ].SampleOffset; 1459 | uint32_t loop = gMod->Inst[ Sample ].LoopStart; 1460 | uint32_t looplen = gMod->Inst[ Sample ].LoopLength; 1461 | uint32_t length = gMod->Inst[ Sample ].SampleLength; 1462 | 1463 | if (length == 0) 1464 | { 1465 | voices[Channel].pending |= PENDING_STOP; 1466 | return; 1467 | } 1468 | 1469 | voices[Channel].start = start; 1470 | voices[Channel].offset = offset >> factor; 1471 | voices[Channel].loop = looplen > 2 ? loop >> factor : (length >> factor) - 1; 1472 | voices[Channel].factor = factor; 1473 | voices[Channel].pending |= PENDING_START; 1474 | voices[Channel].pending &= ~PENDING_STOP; 1475 | } 1476 | 1477 | 1478 | static void SetFrequency(uint16_t Channel, uint32_t Period) 1479 | { 1480 | voices[Channel].period = Period << voices[Channel].factor; 1481 | voices[Channel].pending |= PENDING_PER; 1482 | } 1483 | 1484 | 1485 | static void SetVolume(uint16_t Channel, uint32_t Volume, int32_t Pan) 1486 | { 1487 | uint16_t env, pan; 1488 | uint16_t chan = Channel & 3; 1489 | 1490 | if (Volume == 0) 1491 | { 1492 | voices[Channel].pending |= PENDING_STOP; 1493 | return; 1494 | } 1495 | 1496 | env = (Volume * gVolume) >> 2; // 0 to 256 1497 | if (env > 255) 1498 | env = 255; // clamp to 255 1499 | 1500 | #ifdef HANDLE_PANNING 1501 | pan = Pan; 1502 | #else 1503 | if ( (chan == 0) || (chan == 3) ) 1504 | { 1505 | #ifdef CROSS_TALK 1506 | pan = ~CROSS_TALK & 255; 1507 | #else 1508 | pan = 0; 1509 | #endif 1510 | } 1511 | else 1512 | { 1513 | #ifdef CROSS_TALK 1514 | pan = CROSS_TALK; 1515 | #else 1516 | pan = 255; 1517 | #endif 1518 | } 1519 | #endif 1520 | 1521 | voices[Channel].vol = env; 1522 | voices[Channel].pan = pan; 1523 | voices[Channel].pending |= PENDING_VOL; 1524 | } 1525 | 1526 | 1527 | static void callback(void) 1528 | { 1529 | static uint16_t tempo = 0; 1530 | static uint8_t scale = 0; 1531 | uint8_t i; 1532 | 1533 | if (gMod) 1534 | PlayMOD(gMod); 1535 | 1536 | if ((tempo != gMod->BeatsPerMinute) || (scale != gMod->TimeScale)) 1537 | { 1538 | tempo = gMod->BeatsPerMinute; 1539 | scale = gMod->TimeScale; 1540 | 1541 | uint16_t bpm = 0; 1542 | if (scale & 0x20) 1543 | bpm += (tempo << 1); 1544 | if (scale & 0x10) 1545 | bpm += tempo; 1546 | if (scale & 0x08) 1547 | bpm += (tempo >> 1); 1548 | if (scale & 0x04) 1549 | bpm += (tempo >> 2); 1550 | if (scale & 0x02) 1551 | bpm += (tempo >> 3); 1552 | if (scale & 0x01) 1553 | bpm += (tempo >> 4); 1554 | 1555 | pcm_set_timer(bpm); 1556 | } 1557 | 1558 | /* update channels */ 1559 | for (i = 0; i < gMod->NumberOfChannels; i++) 1560 | { 1561 | pcm_set_ctrl(0xC0 + i); // turn on pcm chip and select channel 1562 | 1563 | if (voices[i].pending & PENDING_STOP) 1564 | { 1565 | pcm_set_off(i); 1566 | pcm_set_env(0); 1567 | voices[i].pending &= ~PENDING_STOP; 1568 | } 1569 | 1570 | if (voices[i].pending & PENDING_START) 1571 | { 1572 | pcm_set_off(i); 1573 | pcm_set_start(voices[i].start, voices[i].offset); 1574 | pcm_set_loop((voices[i].start << 8) + voices[i].loop); 1575 | voices[i].pending |= PENDING_VOL|PENDING_PER; 1576 | } 1577 | 1578 | if (voices[i].pending & PENDING_VOL) 1579 | { 1580 | pcm_set_env(voices[i].vol); 1581 | pcm_set_pan(voices[i].pan); 1582 | voices[i].pending &= ~PENDING_VOL; 1583 | } 1584 | 1585 | if (voices[i].pending & PENDING_PER) 1586 | { 1587 | pcm_set_period(voices[i].period); 1588 | voices[i].pending &= ~PENDING_PER; 1589 | } 1590 | 1591 | if (voices[i].pending & PENDING_START) 1592 | { 1593 | pcm_set_on(i); 1594 | voices[i].pending &= ~PENDING_START; 1595 | } 1596 | } 1597 | } 1598 | -------------------------------------------------------------------------------- /module.h: -------------------------------------------------------------------------------- 1 | #ifndef _MODULE_H_ 2 | #define _MODULE_H_ 3 | 4 | #include 5 | 6 | 7 | #define AMIGA_PAL_CPU_CLOCK ( 7093789 ) /* Hz unit (~7 MHz) */ 8 | 9 | #ifndef FALSE 10 | #define FALSE ( (uint8_t) 0 ) 11 | #endif 12 | #ifndef TRUE 13 | #define TRUE ( (uint8_t) 1 ) 14 | #endif 15 | 16 | #define MOD_PCM_RAM ( (uint32_t)65536 ) /* change to leave space for sound effects */ 17 | 18 | /* MOD enum Constants */ 19 | #define MOD_SIGNATURE_LENGTH ( (uint8_t) 4 ) 20 | #define MOD_NAME_LENGTH ( (uint8_t) 20 ) 21 | #define MOD_SAMPLE_NAME_LENGTH ( (uint8_t) 22 ) 22 | #define MOD_NUMBER_OF_SAMPLES ( (uint8_t) 31 ) 23 | #define MOD_ROWS_PER_CHANNEL ( (uint8_t) 64 ) 24 | #define MOD_NUMBER_OF_ORDERS ( (uint8_t) 128 ) 25 | #define MOD_SIGNATURE_OFFSET ( (int16_t) 1080 ) /* 0x0438 */ 26 | #define MOD_FINE_TUNE_VALUES ( (uint8_t) 16 ) 27 | #define MOD_NUMBER_OF_NOTES ( (uint8_t) 36 ) 28 | #define MOD_NUMBER_OF_CHANNELS ( (uint8_t) 8 ) 29 | #define MOD_TIMER_SPEED ( (uint8_t) 50 ) /* timer speed, we need a speed of 50 Hz = 50 ticks per second */ 30 | 31 | /* EFFECTS */ 32 | #define EFFECT_ARPEGGIO ( (uint8_t) 0x00 ) /* Effect 0xy (Arpeggio) */ 33 | #define EFFECT_PORTA_UP ( (uint8_t) 0x01 ) /* Effect 1xy (Porta Up) */ 34 | #define EFFECT_PORTA_DOWN ( (uint8_t) 0x02 ) /* Effect 2xy (Porta Down) */ 35 | #define EFFECT_PORTA_TO_NOTE ( (uint8_t) 0x03 ) /* Effect 3xy (Porta To Note) */ 36 | #define EFFECT_VIBRATO ( (uint8_t) 0x04 ) /* Effect 4xy (Vibrato) */ 37 | #define EFFECT_PORTA_PLUS_VOL_SLIDE ( (uint8_t) 0x05 ) /* Effect 5xy (Porta + Vol Slide) */ 38 | #define EFFECT_VIBRATO_PLUS_VOL_SLIDE ( (uint8_t) 0x06 ) /* Effect 6xy (Vibrato + Vol Slide) */ 39 | #define EFFECT_TREMOLO ( (uint8_t) 0x07 ) /* Effect 7xy (Tremolo) */ 40 | #define EFFECT_PAN ( (uint8_t) 0x08 ) /* Effect 8xy (Pan) */ 41 | #define EFFECT_SAMPLE_OFFSET ( (uint8_t) 0x09 ) /* Effect 9xy (Sample Offset) */ 42 | #define EFFECT_VOLUME_SLIDE ( (uint8_t) 0x0A ) /* Effect Axy (Volume Slide) */ 43 | #define EFFECT_JUMP_TO_PATTERN ( (uint8_t) 0x0B ) /* Effect Bxy (Jump To Pattern) */ 44 | #define EFFECT_SET_VOLUME ( (uint8_t) 0x0C ) /* Effect Cxy (Set Volume) */ 45 | #define EFFECT_PATTERN_BREAK ( (uint8_t) 0x0D ) /* Effect Dxy (Pattern Break) */ 46 | #define EFFECT_EXTENDED_EFFECTS ( (uint8_t) 0x0E ) /* Extended Effects */ 47 | #define EFFECT_SET_SPEED ( (uint8_t) 0x0F ) /* Effect Fxy (Set Speed) */ 48 | 49 | #define EFFECT_SET_FILTER ( (uint8_t) 0x00 ) /* Effect E0x (Set Filter) */ 50 | #define EFFECT_FINE_PORTA_UP ( (uint8_t) 0x01 ) /* Effect E1x (Fine Porta Up) */ 51 | #define EFFECT_FINE_PORTA_DOWN ( (uint8_t) 0x02 ) /* Effect E2x (Fine Porta Down) */ 52 | #define EFFECT_GLISSANDO_CONTROL ( (uint8_t) 0x03 ) /* Effect E3x (Glissando Control) */ 53 | #define EFFECT_SET_VIBRATO_WAVEFORM ( (uint8_t) 0x04 ) /* Effect E4x (Set Vibrato Waveform) */ 54 | #define EFFECT_SET_FINETUNE ( (uint8_t) 0x05 ) /* Effect E5x (Set Finetune) */ 55 | #define EFFECT_PATTERN_LOOP ( (uint8_t) 0x06 ) /* Effect E6x (Pattern Loop) */ 56 | #define EFFECT_SET_TREMOLO_WAVEFORM ( (uint8_t) 0x07 ) /* Effect E7x (Set Tremolo WaveForm) */ 57 | #define EFFECT_POSITION_PANNING ( (uint8_t) 0x08 ) /* Effect E8x (Position Panning) */ 58 | #define EFFECT_RETRIG_NOTE ( (uint8_t) 0x09 ) /* Effect E9x (Retrig Note) */ 59 | #define EFFECT_FINE_VOLUME_SLIDE_UP ( (uint8_t) 0x0A ) /* Effect EAx (Fine Volume Slide Up) */ 60 | #define EFFECT_FINE_VOLUME_SLIDE_DOWN ( (uint8_t) 0x0B ) /* Effect EBx (Fine Volume Slide Down) */ 61 | #define EFFECT_CUT_NOTE ( (uint8_t) 0x0C ) /* Effect ECx (Cut Note) */ 62 | #define EFFECT_DELAY_NOTE ( (uint8_t) 0x0D ) /* Effect EDx (Delay Note) */ 63 | #define EFFECT_PATTERN_DELAY ( (uint8_t) 0x0E ) /* Effect EEx (Pattern Delay) */ 64 | #define EFFECT_INVERT_LOOP ( (uint8_t) 0x0F ) /* Effect EFx (Invert Loop) */ 65 | 66 | 67 | typedef struct Note { 68 | uint8_t SampleNumber; 69 | uint8_t EffectNumber; 70 | uint8_t EffectParameter; 71 | uint8_t NoteExists; /* boolean flag */ 72 | uint8_t PeriodFrequencyCol; /* column position in AmigaPeriodsTable[][] */ 73 | uint16_t PeriodFrequency; 74 | } Note_t; 75 | 76 | typedef struct Channel { 77 | uint8_t LastInstrument; 78 | int8_t Volume; /* default volume of sample */ 79 | uint8_t PortaSpeed; 80 | uint8_t VibratoSpeed; 81 | uint8_t VibratoDepth; 82 | uint8_t TremoloSpeed; 83 | uint8_t TremoloDepth; 84 | uint8_t WaveformControl; /* upper 4 bits for the tremolo wavecontrol, and the lower 4 bits for the vibrato wavecontrol */ 85 | int8_t VibratoPosition; 86 | int8_t TremoloPosition; 87 | uint8_t PatLoopRow; 88 | uint8_t PatLoopNo; 89 | int16_t PanValue; /* not used unless HANDLE_PANNING defined */ 90 | int16_t PeriodFrequency; 91 | int16_t PortaTo; /* note to port to value */ 92 | uint32_t SampleOffset; 93 | } Channel_t; 94 | 95 | typedef struct Sample { 96 | uint8_t FineTune; 97 | uint8_t Volume; 98 | uint8_t Factor; /* shift for scaling sample to fit pcm ram */ 99 | 100 | uint32_t SampleHandle; /* associate Sample with data (may be a pointer, or a handle for a loaded sample) */ 101 | uint32_t LoopStart; 102 | uint32_t LoopLength; 103 | uint32_t SampleLength; /* The maximum size for a sample on the Amiga is 128K. */ 104 | } Sample_t; 105 | 106 | /* main data structure */ 107 | typedef struct Module { 108 | uint8_t IsPlaying; /* flags - b0 = 1 = playing, 0 = stopped; b7 = 1 = loop at end */ 109 | uint8_t SongLength; /* song length (1 to 128) */ 110 | uint8_t NumberOfPatterns; /* number of physical patterns (1 to 64) */ 111 | uint8_t NumberOfChannels; /* number of channels (4, 6, or 8) */ 112 | 113 | uint8_t Speed; /* number of ticks (or times the function is called) between each time a new row is processed */ 114 | uint8_t Tick; /* current music tick */ 115 | int8_t Row; /* current row number value 0-63 */ 116 | uint8_t Order; /* current value */ 117 | uint8_t Pattern; /* current value */ 118 | uint8_t PatternDelay; /* number of notes to delay pattern for */ 119 | 120 | uint8_t Pad; 121 | uint8_t TimeScale; /* scale for BPM (2.4 fixed point number) */ 122 | uint16_t BeatsPerMinute; /* beats per minute */ 123 | 124 | uint32_t* PatternsBuff_p; /* start of pattern data (1 to 64 * (64 * 4 * # channels)) */ 125 | 126 | uint8_t Orders[ MOD_NUMBER_OF_ORDERS ]; /* pattern playing orders (128 entries of 0 to 63) */ 127 | 128 | Sample_t Inst[ MOD_NUMBER_OF_SAMPLES ]; /* instrument headers */ 129 | Channel_t Channels[ MOD_NUMBER_OF_CHANNELS ]; /* run time channel info */ 130 | Note_t Notes[ MOD_NUMBER_OF_CHANNELS ]; /* run time note info */ 131 | char Title[22]; /* module title (max of 20 chars) */ 132 | uint8_t NextBlock; /* next block in sram for PCM samples */ 133 | } Mod_t; 134 | 135 | 136 | /* 137 | Initialize Module struct for playing 138 | returns 0 if no error 139 | */ 140 | uint8_t InitMOD(CDFileHandle_t *File_p, Mod_t* Mod_p, uint8_t filter); 141 | 142 | /* 143 | Cleanup MOD - stop playing and release voices 144 | */ 145 | void ExitMOD(Mod_t* Mod_p); 146 | 147 | /* 148 | Start playing Module 149 | loop = bool = TRUE = loop forever, = FALSE = play once 150 | returns the previously playing Module, if any 151 | */ 152 | Mod_t* StartMOD(Mod_t* Mod_p, uint8_t loop); 153 | 154 | /* 155 | Pause/resume Module 156 | pause = bool = TRUE = pause, = FALSE = resume 157 | */ 158 | void PauseMOD(Mod_t* Mod_p, uint8_t pause); 159 | 160 | /* 161 | Stop playing Module 162 | */ 163 | void StopMOD(Mod_t* Mod_p); 164 | 165 | /* 166 | Wait for Module to play through once 167 | */ 168 | void WaitMOD(Mod_t* Mod_p); 169 | 170 | /* 171 | Check Module play status 172 | returns 0 if done playing 173 | */ 174 | uint8_t CheckMOD(Mod_t* Mod_p); 175 | 176 | /* 177 | Set global volume 178 | vol = 0 = off, = 16 = max 179 | returns the previous volume setting 180 | */ 181 | uint8_t VolumeMOD(uint8_t vol); 182 | 183 | /* 184 | Set current position in Module 185 | order = position to start playing at 186 | returns the current order playing 187 | */ 188 | uint8_t JumpMOD(Mod_t* Mod_p, uint8_t order); 189 | 190 | /* 191 | Set current BPM scale for Module 192 | scale = 2.4 fixed point number for scaling BPM 193 | returns the current scale 194 | */ 195 | uint8_t TimeScaleMOD(Mod_t* Mod_p, uint8_t scale); 196 | 197 | #endif 198 | -------------------------------------------------------------------------------- /pcm-io.s: -------------------------------------------------------------------------------- 1 | 2 | /* PCM channel increment */ 3 | .equ PCM_FDL, 0xFF0005 4 | .equ PCM_FDH, 0xFF0007 5 | 6 | /* General use timer */ 7 | .equ TIMER, 0x8030 8 | .equ INT_MASK, 0x8032 9 | .equ _LEVEL3, 0x5F82 /* TIMER INTERRUPT jump vector */ 10 | 11 | 12 | .text 13 | .align 2 14 | 15 | 16 | | uint8_t pcm_lcf(uint8_t pan); 17 | | Convert pan setting to simple linear crossfade volume settings 18 | .global pcm_lcf 19 | pcm_lcf: 20 | move.l 4(sp),d0 /* pan */ 21 | addq.b #8,d0 22 | bcc.b 0f 23 | move.l 4(sp),d0 24 | 0: 25 | andi.w #0x00F0,d0 /* right pan volume nibble */ 26 | 27 | move.l 4(sp),d1 28 | not.b d1 29 | addq.b #8,d1 30 | bcc.b 1f 31 | move.l 4(sp),d1 32 | not.b d1 33 | 1: 34 | lsr.b #4,d1 /* left pan volume nibble */ 35 | or.b d1,d0 36 | rts 37 | 38 | 39 | | void pcm_delay(void); 40 | | Delay after writing PCM register 41 | .global pcm_delay 42 | pcm_delay: 43 | moveq #20,d1 44 | 0: 45 | dbra d1,0b 46 | rts 47 | 48 | 49 | | void pcm_set_period(uint32_t period); 50 | .global pcm_set_period 51 | pcm_set_period: 52 | move.l 4(sp),d1 53 | cmpi.l #4,d1 54 | blo.b 0f /* saturate increment to 65535 */ 55 | addq.l #1,d1 56 | move.l #446304,d0 57 | divu.w d1,d0 58 | lsr.w #1,d0 /* incr = (446304 / Period + 1) >> 1 */ 59 | bra.b 1f 60 | 0: 61 | move.w #65535,d0 62 | 1: 63 | move.b d0,PCM_FDL.l 64 | bsr.b pcm_delay 65 | lsr.w #8,d0 66 | move.b d0,PCM_FDH.l 67 | bra.b pcm_delay 68 | 69 | 70 | | void pcm_set_freq(uint32_t freq); 71 | .global pcm_set_freq 72 | pcm_set_freq: 73 | move.l 4(sp),d0 74 | cmpi.l #1041648,d0 75 | bhs.b 0f /* saturate increment to 65535 */ 76 | lsl.l #8,d0 77 | lsl.l #3,d0 /* shift freq for fixed point result */ 78 | move.w #32552,d1 79 | divu.w d1,d0 /* incr = (freq << 11) / 32552 */ 80 | bra.b 1f 81 | 0: 82 | move.w #65535,d0 83 | 1: 84 | move.b d0,PCM_FDL.l 85 | bsr.b pcm_delay 86 | lsr.w #8,d0 87 | move.b d0,PCM_FDH.l 88 | bra.b pcm_delay 89 | 90 | 91 | | void pcm_set_timer(uint16_t bpm); 92 | | TIMER = 32552 / (bpm * 2) - 1 93 | | should be 32552 * 5 / (bpm * 2) - 1, but that's too big for TIMER, so 94 | | we leave out the * 5 and compensate in the int handler 95 | .global pcm_set_timer 96 | pcm_set_timer: 97 | move.l #32552,d0 98 | move.l 4(sp),d1 99 | add.w d1,d1 100 | beq.b 0f /* safety check... passing 0 will just exit */ 101 | divu.w d1,d0 102 | subq.w #1,d0 103 | ble.b 0f /* safety check */ 104 | move.w d0,TIMER.w 105 | 0: 106 | rts 107 | 108 | 109 | | void pcm_stop_timer(void); 110 | .global pcm_stop_timer 111 | pcm_stop_timer: 112 | move #0x2700,sr /* disable interrupts */ 113 | 114 | move.w #0,TIMER.w /* stop General Timer */ 115 | move.w INT_MASK.w,d0 116 | andi.w #0xFFF7,d0 117 | move.w d0,INT_MASK.w /* disable General Timer interrupt */ 118 | 119 | move #0x2000,sr /* enable interrupts */ 120 | rts 121 | 122 | 123 | | void pcm_start_timer(void (*callback)(void)); 124 | .global pcm_start_timer 125 | pcm_start_timer: 126 | move #0x2700,sr /* disable interrupts */ 127 | 128 | move.l 4(sp),int3_callback /* set callback vector */ 129 | move.w #0,int3_cntr /* clear int counter */ 130 | 131 | move.w #0x4EF9,_LEVEL3.w 132 | move.l #timer_int,_LEVEL3+2.w /* set level 3 int vector for timer */ 133 | 134 | move.w #129,TIMER.w /* 125 BPM */ 135 | move.w INT_MASK.w,d0 136 | ori.w #0x0008,d0 137 | move.w d0,INT_MASK.w /* enable General Timer interrupt */ 138 | 139 | move #0x2000,sr /* enable interrupts */ 140 | rts 141 | 142 | 143 | timer_int: 144 | move.l d0,-(sp) 145 | move.w int3_cntr,d0 146 | addq.w #1,d0 147 | cmpi.w #5,d0 148 | blo.b 0f /* once every 5 ints for actual beats per minute rate */ 149 | 150 | movem.l d1/a0-a1,-(sp) 151 | movea.l int3_callback,a1 152 | jsr (a1) 153 | movem.l (sp)+,d1/a0-a1 154 | moveq #0,d0 155 | 0: 156 | move.w d0,int3_cntr 157 | move.l (sp)+,d0 158 | rte 159 | 160 | 161 | .data 162 | 163 | 164 | .align 4 165 | 166 | int3_callback: 167 | .long 0 168 | 169 | int3_cntr: 170 | .word 0 171 | 172 | -------------------------------------------------------------------------------- /pcm.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "pcm.h" 4 | 5 | // PCM chip registers 6 | #define PCM_ENV *((volatile uint8_t *)0xFF0001) 7 | #define PCM_PAN *((volatile uint8_t *)0xFF0003) 8 | #define PCM_FDL *((volatile uint8_t *)0xFF0005) 9 | #define PCM_FDH *((volatile uint8_t *)0xFF0007) 10 | #define PCM_LSL *((volatile uint8_t *)0xFF0009) 11 | #define PCM_LSH *((volatile uint8_t *)0xFF000B) 12 | #define PCM_START *((volatile uint8_t *)0xFF000D) 13 | #define PCM_CTRL *((volatile uint8_t *)0xFF000F) 14 | #define PCM_ONOFF *((volatile uint8_t *)0xFF0011) 15 | #define PCM_WAVE *((volatile uint8_t *)0xFF2001) 16 | 17 | static uint8_t loop_markers[32] = { 18 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 19 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 20 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 21 | 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF 22 | }; 23 | 24 | #define BLK_PAD (32 + 255) 25 | #define BLK_SHIFT 8 26 | 27 | static uint8_t ChanOff; 28 | 29 | static void pcm_cpy(uint16_t doff, void *src, uint16_t len, uint16_t conv) 30 | { 31 | uint8_t *sptr = (uint8_t *)src; 32 | 33 | while (len > 0) 34 | { 35 | uint8_t *wptr = (uint8_t *)&PCM_WAVE; 36 | uint16_t woff = doff & 0x0FFF; 37 | uint16_t wblen = 0x1000 - woff; 38 | wptr += (woff << 1); 39 | 40 | PCM_CTRL = 0x80 + (doff >> 12); // make sure PCM chip is ON to write wave memory, and set wave bank 41 | pcm_delay(); 42 | 43 | if (wblen > len) 44 | wblen = len; 45 | doff += wblen; 46 | len -= wblen; 47 | while (wblen > 0) 48 | { 49 | int16_t s = (int8_t)*sptr++; 50 | if (conv) 51 | { 52 | // convert from 8-bit signed samples to sign/magnitude samples 53 | if (s < 0) 54 | { 55 | s = -s; 56 | if (s > 127) s = 127; // clamp to -127 57 | } 58 | else 59 | { 60 | if (s > 126) s = 126; // clamp to 126 61 | s |= 128; 62 | } 63 | } 64 | *wptr++ = (uint8_t)(s & 255); 65 | wptr++; 66 | wblen--; 67 | } 68 | } 69 | } 70 | 71 | void pcm_load_samples(uint8_t start, int8_t *samples, uint16_t length) 72 | { 73 | ChanOff = 0xFF; 74 | PCM_ONOFF = 0xFF; // turn off all channels 75 | pcm_cpy(start << BLK_SHIFT, samples, length, 1); 76 | pcm_cpy((start << BLK_SHIFT) + length, loop_markers, 32, 0); 77 | } 78 | 79 | uint16_t pcm_next_block(uint8_t start, uint16_t length) 80 | { 81 | return start + ((length + BLK_PAD) >> BLK_SHIFT); 82 | } 83 | 84 | void pcm_reset(void) 85 | { 86 | uint16_t i; 87 | 88 | ChanOff = 0xFF; 89 | PCM_ONOFF = 0xFF; // turn off all channels 90 | pcm_delay(); 91 | 92 | for (i=0; i<8; i++) 93 | { 94 | PCM_CTRL = 0xC0 + i; // turn on pcm chip and select channel 95 | pcm_delay(); 96 | PCM_ENV = 0x00; // channel env off 97 | pcm_delay(); 98 | PCM_PAN = 0x00; 99 | pcm_delay(); 100 | PCM_FDL = 0x00; 101 | pcm_delay(); 102 | PCM_FDH = 0x00; 103 | pcm_delay(); 104 | PCM_LSL = 0x00; 105 | pcm_delay(); 106 | PCM_LSH = 0x00; 107 | pcm_delay(); 108 | PCM_START = 0x00; 109 | pcm_delay(); 110 | } 111 | } 112 | 113 | void pcm_set_ctrl(uint8_t val) 114 | { 115 | PCM_CTRL = val; 116 | pcm_delay(); 117 | } 118 | 119 | void pcm_set_off(uint8_t index) 120 | { 121 | ChanOff |= (1 << index); 122 | PCM_ONOFF = ChanOff; 123 | pcm_delay(); 124 | } 125 | 126 | void pcm_set_on(uint8_t index) 127 | { 128 | ChanOff &= ~(1 << index); 129 | PCM_ONOFF = ChanOff; 130 | pcm_delay(); 131 | } 132 | 133 | void pcm_set_start(uint8_t start, uint16_t offset) 134 | { 135 | PCM_START = start + (offset >> BLK_SHIFT); 136 | pcm_delay(); 137 | } 138 | 139 | void pcm_set_loop(uint16_t loopstart) 140 | { 141 | PCM_LSL = loopstart & 0x00FF; // low byte 142 | pcm_delay(); 143 | PCM_LSH = loopstart >> 8; // high byte 144 | pcm_delay(); 145 | } 146 | 147 | void pcm_set_env(uint8_t vol) 148 | { 149 | PCM_ENV = vol; 150 | pcm_delay(); 151 | } 152 | 153 | void pcm_set_pan(uint8_t pan) 154 | { 155 | PCM_PAN = pcm_lcf(pan); 156 | pcm_delay(); 157 | } 158 | 159 | -------------------------------------------------------------------------------- /pcm.h: -------------------------------------------------------------------------------- 1 | #ifndef _PCM_H 2 | #define _PCM_H 3 | 4 | #include 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | /* from pcm.c */ 11 | extern void pcm_load_samples(uint8_t start, int8_t *samples, uint16_t length); 12 | extern uint16_t pcm_next_block(uint8_t start, uint16_t length); 13 | extern void pcm_reset(void); 14 | extern void pcm_set_ctrl(uint8_t val); 15 | extern void pcm_set_off(uint8_t index); 16 | extern void pcm_set_on(uint8_t index); 17 | extern void pcm_set_start(uint8_t start, uint16_t offset); 18 | extern void pcm_set_loop(uint16_t loopstart); 19 | extern void pcm_set_env(uint8_t vol); 20 | extern void pcm_set_pan(uint8_t pan); 21 | /* from pcm-io.s */ 22 | extern uint8_t pcm_lcf(uint8_t pan); 23 | extern void pcm_delay(void); 24 | extern void pcm_set_period(uint32_t period); 25 | extern void pcm_set_freq(uint32_t freq); 26 | extern void pcm_set_timer(uint16_t bpm); 27 | extern void pcm_stop_timer(void); 28 | extern void pcm_start_timer(void (*callback)(void)); 29 | 30 | #ifdef __cplusplus 31 | } 32 | #endif 33 | 34 | #endif 35 | --------------------------------------------------------------------------------