diff --git a/src/Home/Tracker/Classes.ZC b/src/Home/Tracker/Classes.ZC new file mode 100644 index 00000000..a9978058 --- /dev/null +++ b/src/Home/Tracker/Classes.ZC @@ -0,0 +1,21 @@ +class NoteCell { + U8 note; // MIDI note number + U8 velocity; // Volume/Intensity + U8 instrument; + U8 effect; // Future expansion for effects +}; + +#define TRACK_LENGTH 64 // Length of each pattern in rows +class Pattern { + NoteCell cells[TRACK_LENGTH]; +}; + +#define MAX_PATTERNS 64 // Maximum patterns in a song +class Song { + U8 patternOrder[MAX_PATTERNS]; // Which pattern is played when + Pattern patterns[MAX_PATTERNS]; +}; + + +#define SAMPLE_RATE 44100 +#define PI 3.1415926535897932 // i know i can just use the pi symbol \ No newline at end of file diff --git a/src/Home/Tracker/Lib/AC97.ZC b/src/Home/Tracker/Lib/AC97.ZC new file mode 100755 index 00000000..16439993 --- /dev/null +++ b/src/Home/Tracker/Lib/AC97.ZC @@ -0,0 +1,240 @@ + +#define INT_LAST_VALID_ENTRY 1 << 2 +#define INT_IOC 1 << 3 +#define INT_FIFO_ERR 1 << 4 + +#define BDL_BUF_SIZE 2044 +#define MAX_BDLS 32 + +#define PCM_BUF_SIZE 2048 +#define PCM_IN 0 +#define PCM_OUT 1 +#define MIC_IN 2 + +// Native Audio Mixer registers (all U16) +#define RESET 0x00 // Reset Register +#define MASTER_VOL 0x02 // Set Master Output Volume +#define MIC_VOL 0x0E // Set Microphone Volume +#define PCM_VOL 0x18 // Set Output Volume of PCM patterns +#define REC_SLC 0x1A // Select Input Device +#define REC_GAIN 0x1C // Set Input Gain +#define MIC_GAIN 0x1E // Set Gain of Microphone +#define EXT_ID 0x28 // Supported extended functions +#define EXT_CTRL 0x2A // Enabling extended functions +#define EXT_FRONT_RATE 0x2C // Sample rate of front speaker + +// Native Audio Bus Master registers +#define PCM_INPUT_REG_BOX 0x00 // NABM register box for PCM IN (sizeof NABM register box) +#define PCM_OUTPUT_REG_BOX 0x10 // NABM register box for PCM OUT (sizeof NABM register box) +#define MIC_INPUT_REG_BOX 0x20 // NABM register box for Microphone (sizeof NABM register box) +#define GLOBAL_CTL 0x2C // Global Control Register (U32) +#define GLOBAL_STS 0x30 // Global Status Register (U32) + +// NABM register box registers +#define BUFFER_DSC_ADDR 0x00 // Physical Address of Buffer Descriptor List (U32) +#define CUR_ENTRY_VAL 0x04 // Number of Actual Processed Buffer Descriptor Entry (U8) +#define LAST_VALID_ENTRY 0x05 // Number of all Descriptor Entries (U8) +#define TRANSFER_STS 0x06 // Status of Transferring Data (U16) +#define CUR_IDX_PROC_SAMPLES 0x08 // Number of Transferred Samples in Actual Processed Entry (U16) +#define PRCSD_ENTRY 0x0A // Number of Actual Processed Buffer Entry (U8) +#define BUFFER_CNT 0x0B // Most Important Register for controlling Transfers (U8) + +class CAC97BufferDescriptorListEntry +{ + U32 addr; + U16 length; // length - 1 + U16 flags; +}; + +class CAC97BufferDescriptorList +{ + CAC97BufferDescriptorListEntry entries[32]; +}; + +class CAC97 +{ + CPCIInfo pci; + CAC97BufferDescriptorList *bdl[3]; + U16 nam; + U16 nabm; +}; + +CAC97 ac97; + +#define AUDIO_MAX_STREAMS 16 +#define AUDIO_OUTPUT_BUFFER_SIZE 1024 +#define AUDIO_STREAM_FIFO_SIZE 1048576 * 16 +#define AUDIO_STREAM_TYPE_INPUT 0 +#define AUDIO_STREAM_TYPE_OUTPUT 1 + +class CAudioStream +{ + CFifoI64 *data; +}; + +class CAudio +{ + CAudioStream output[AUDIO_MAX_STREAMS]; + I64 output_frames[AUDIO_MAX_STREAMS]; +}; + +CAudio audio; + +U0 AudioInit() +{ + I64 i = 0; + for (i = 0; i < AUDIO_MAX_STREAMS; i++) + audio.output[i].data = FifoI64New(AUDIO_STREAM_FIFO_SIZE, sys_task); +} + +AudioInit; + +I64 AudioAvailableOutputStreamGet() +{ + I64 stream = 0; + while (FifoI64Count(audio.output[stream].data)) + stream++; + if (stream > AUDIO_MAX_STREAMS - 1) + return -1; + return stream; +} + +I64 AudioSFXPlay(U32 *data, I64 length) +{ + I64 i; + I64 stream = AudioAvailableOutputStreamGet; + if (stream < 0) + return stream; + for (i = 0; i < length; i++) + FifoI64Ins(audio.output[stream].data, data[i]); + return stream; +} + +U0 AC97OutputMix(U32 *buf, I64 length = NULL) +{ + I64 i; + I64 j; + I64 acc_sample_L = 0; + I64 acc_sample_R = 0; + I64 acc_streams = 0; + U32 sample; + + if (!length) + length = AUDIO_OUTPUT_BUFFER_SIZE; + for (i = 0; i < length / 4; i++) + { + acc_sample_L = 0; + acc_sample_R = 0; + acc_streams = 0; + + for (j = 0; j < AUDIO_MAX_STREAMS; j++) + { + if (FifoI64Count(audio.output[j].data)) + { + FifoI64Remove(audio.output[j].data, &sample); + audio.output_frames[j]++; + acc_streams++; + acc_sample_L += sample.u16[0]; + acc_sample_R += sample.u16[1]; + } + } + + buf[i].i16[0] = ToI64(acc_sample_L / Sqrt(acc_streams)); + buf[i].i16[1] = ToI64(acc_sample_R / Sqrt(acc_streams)); + } +} + +U0 AC97BufferFill() +{ + I64 idx = InU8(ac97.nabm + PCM_OUTPUT_REG_BOX + LAST_VALID_ENTRY); + U32 *buf = ac97.bdl[PCM_OUT]->entries[idx].addr; + AC97OutputMix(buf, BDL_BUF_SIZE); + OutU8(ac97.nabm + PCM_OUTPUT_REG_BOX + LAST_VALID_ENTRY, ++idx); +} + +U0 AC97AudioProcess() +{ + U16 status = InU16(ac97.nabm + PCM_OUTPUT_REG_BOX + TRANSFER_STS); + if (status & INT_IOC) + { + AC97BufferFill; + OutU16(ac97.nabm + PCM_OUTPUT_REG_BOX + TRANSFER_STS, 0x1C); + } +} + +I64 AC97Init() +{ + I64 i; + I64 j; + // Scan for device + j = PCIClassFind(0x040100, 0); + if (j < 0) + { + device_not_found: SysLog("\n[AC'97] Device not found\n"); + return -1; + } + + PCIInfoGet(j, &ac97.pci); + + if (ac97.pci.vendor_id != 0x8086 || ac97.pci.device_id != 0x2415) + goto device_not_found; + + ac97.nam = ac97.pci.bar[0] &0xFFFFFF00; + ac97.nabm = ac97.pci.bar[1] &0xFFFFFF00; + + // Enable port IO, disable MMIO + PCIWriteU8(j.u8[2], j.u8[1], j.u8[0], 0x4, 5); + + OutU32(ac97.nabm + GLOBAL_CTL, 0x03); + OutU16(ac97.nam + RESET, 0xFFFF); + + // Set PCM Output to Max volume + OutU16(ac97.nam + PCM_VOL, 0x0000); + + // Allocate Buffer Descriptor Lists + ac97.bdl[PCM_IN] = CAllocAligned(sizeof(CAC97BufferDescriptorList), 4096, Fs->code_heap); + ac97.bdl[PCM_OUT] = CAllocAligned(sizeof(CAC97BufferDescriptorList), 4096, Fs->code_heap); + ac97.bdl[MIC_IN] = CAllocAligned(sizeof(CAC97BufferDescriptorList), 4096, Fs->code_heap); + + for (i = 0; i < MAX_BDLS; i++) + { + ac97.bdl[PCM_OUT]->entries[i].addr = + CAllocAligned(PCM_BUF_SIZE, 4096, Fs->code_heap); + ac97.bdl[PCM_OUT]->entries[i].length = BDL_BUF_SIZE / 2; + ac97.bdl[PCM_OUT]->entries[i].flags = 1 << 15; + } + + // Set addresses of Buffer Descriptor Lists + // OutU32(ac97.nabm + PCM_INPUT_REG_BOX + BUFFER_DSC_ADDR, ac97.bdl[PCM_IN]); + OutU32(ac97.nabm + PCM_OUTPUT_REG_BOX + BUFFER_DSC_ADDR, ac97.bdl[PCM_OUT]); + // OutU32(ac97.nabm + MIC_INPUT_REG_BOX + BUFFER_DSC_ADDR, ac97.bdl[MIC_IN]); + + // Set Master Volume + OutU16(ac97.nam + MASTER_VOL, 0x0F0F); + + // Stop playing sound + OutU8(ac97.nabm + PCM_OUTPUT_REG_BOX + BUFFER_CNT, 0); + + // Fill one buffers + AC97BufferFill; + + // Enable interrupt handler + //@pci_register_int_handler(&@ac97_int_handler); + + // Start playing sound + OutU8(ac97.nabm + PCM_OUTPUT_REG_BOX + BUFFER_CNT, 1); + + return 0; +} + +U0 AC97Task() +{ + while (1) + { + AC97AudioProcess; + Sleep(1); + } +} + +AC97Init; +Spawn(&AC97Task,, "AC97 Task", 1); \ No newline at end of file diff --git a/src/Home/Tracker/Lib/Debug.ZC b/src/Home/Tracker/Lib/Debug.ZC new file mode 100755 index 00000000..0ce16c86 --- /dev/null +++ b/src/Home/Tracker/Lib/Debug.ZC @@ -0,0 +1,21 @@ +class Debug { + Bool enabled; + I64 bookmark; + I64 counter; +}; + +Debug debug; +debug.bookmark = 0; +debug.counter = 0; +debug.enabled = FALSE; + +U0 debug_print(U8 *fmt, ...) { + if (!debug.enabled || debug.counter < debug.bookmark) { + debug.counter++; + return; + } + U8 *buf = StrPrintJoin(NULL, fmt, argc, argv); + "[%05d] %s", debug.counter, buf; + Free(buf); + debug.counter++; +} diff --git a/src/Home/Tracker/Lib/ELF64.ZC b/src/Home/Tracker/Lib/ELF64.ZC new file mode 100755 index 00000000..47f3b83e --- /dev/null +++ b/src/Home/Tracker/Lib/ELF64.ZC @@ -0,0 +1,355 @@ +#define EI_NIDENT 16 +#define EM_X86_64 0x3E +#define ET_EXEC 2 +#define ET_DYN 3 + +#define STT_OBJECT 1 +#define STT_FUNC 2 + +class Elf64_Ehdr { + U8 e_ident[EI_NIDENT]; /* Magic number and other info */ + U16 e_type; /* Object file type */ + U16 e_machine; /* Architecture */ + U32 e_version; /* Object file version */ + U64 e_entry; /* Entry point virtual address */ + U64 e_phoff; /* Program header table file offset */ + U64 e_shoff; /* Section header table file offset */ + U32 e_flags; /* Processor-specific flags */ + U16 e_ehsize; /* ELF header size in bytes */ + U16 e_phentsize; /* Program header table entry size */ + U16 e_phnum; /* Program header table entry count */ + U16 e_shentsize; /* Section header table entry size */ + U16 e_shnum; /* Section header table entry count */ + U16 e_shstrndx; /* Section header string table index */ +}; + +class Elf64_Shdr { + U32 sh_name; /* Section name (string tbl index) */ + U32 sh_type; /* Section type */ + U64 sh_flags; /* Section flags */ + U64 sh_addr; /* Section virtual addr at execution */ + U64 sh_offset; /* Section file offset */ + U64 sh_size; /* Section size in bytes */ + U32 sh_link; /* Link to another section */ + U32 sh_info; /* Additional section information */ + U64 sh_addralign; /* Section alignment */ + U64 sh_entsize; /* Entry size if section holds table */ +}; + +class Elf64_Sym { + U32 st_name; /* Symbol name (string tbl index) */ + U8 st_info; /* Symbol type and binding */ + U8 st_other; /* Symbol visibility */ + U16 st_shndx; /* Section index */ + U64 st_value; /* Symbol value */ + U64 st_size; /* Symbol size */ +}; + +class PLT_entry { + U8 pad[0x10]; +}; + +class RELA_entry { + U64 r_offset; + U64 r_info; + I64 r_addend; +}; + +class Elf { + union { + U8 *u8; + Elf64_Ehdr *ehdr; + } I64 size; + U8 *dynstr; + Elf64_Sym *dynsym; + PLT_entry *plt; + RELA_entry *rela_dyn; + RELA_entry *rela_plt; + Elf64_Sym *strtab; + Elf64_Sym *symtab; + I64 rela_dyn_size; + I64 rela_plt_size; + I64 strtab_size; + I64 symtab_size; +}; + +U0 (*_start)(); + +U0 unimplemented_symbol() { + I32 s = 0xDEADF00D; + "Unimplemented symbol: %s\n", s; + while (1) + Sleep(1); +} + +Bool is_valid_elf(Elf *elf) { + Bool res = TRUE; + if (MemCompare(elf->u8 + 1, "ELF", 3)) { + debug_print("Invalid signature (not ELF).\n"); + res = FALSE; + } + if (elf->ehdr->e_type != ET_EXEC && elf->ehdr->e_type != ET_DYN) { + debug_print("Invalid object file type.\n"); + res = FALSE; + } + if (elf->ehdr->e_machine != EM_X86_64) { + debug_print("Invalid architecture.\n"); + res = FALSE; + } + return res; +} + +U0 find_value(U8 *value) { + U64 addr = 0x0; + while (addr < 0x2000000) { + if (!MemCompare(addr, value, StrLen(value))) { + "found at 0x%08x\n", addr; + return; + } + addr++; + } + "not found\n"; +} + +U0 process_elf_section_header_table(Elf *elf) { + Elf64_Shdr *shdr = elf->u8 + elf->ehdr->e_shoff; + Elf64_Shdr *shdr_shstrtab = shdr + elf->ehdr->e_shstrndx; + U8 *shstrtab = elf->u8 + shdr_shstrtab->sh_offset; + I64 i = 0; + while (i < elf->ehdr->e_shnum) { + if (!StrCompare(shstrtab + shdr->sh_name, ".symtab")) { + debug_print("found symtab at 0x%08x, size = %d\n", shdr->sh_offset, + shdr->sh_size); + elf->symtab = elf->u8 + shdr->sh_offset; + elf->symtab_size = shdr->sh_size; + } + if (!StrCompare(shstrtab + shdr->sh_name, ".strtab")) { + debug_print("found strtab at 0x%08x, size = %d\n", shdr->sh_offset, + shdr->sh_size); + elf->strtab = elf->u8 + shdr->sh_offset; + elf->strtab_size = shdr->sh_size; + } + if (shdr->sh_addr) { + MemCopy(shdr->sh_addr, elf->u8 + shdr->sh_offset, shdr->sh_size); + if (!StrCompare(shstrtab + shdr->sh_name, ".dynstr")) + elf->dynstr = shdr->sh_addr; + if (!StrCompare(shstrtab + shdr->sh_name, ".dynsym")) + elf->dynsym = shdr->sh_addr; + if (!StrCompare(shstrtab + shdr->sh_name, ".plt")) + elf->plt = shdr->sh_addr; + if (!StrCompare(shstrtab + shdr->sh_name, ".rela.dyn")) { + elf->rela_dyn = shdr->sh_addr; + elf->rela_dyn_size = shdr->sh_size / shdr->sh_entsize; + } + if (!StrCompare(shstrtab + shdr->sh_name, ".rela.plt")) { + elf->rela_plt = shdr->sh_addr; + elf->rela_plt_size = shdr->sh_size / shdr->sh_entsize; + } + debug_print( + "MemCopy section '%s' to physical address 0x%06x, size = %d bytes\n", + shstrtab + shdr->sh_name, shdr->sh_addr, shdr->sh_size); + if (!StrCompare(shstrtab + shdr->sh_name, ".bss")) { + MemSet(shdr->sh_addr, NULL, shdr->sh_size); + debug_print("MemSet section '%s' at physical address 0x%06x to NULL, " + "size = %d bytes\n", + shstrtab + shdr->sh_name, shdr->sh_addr, shdr->sh_size); + } + } + shdr++; + i++; + } +} + +U0 process_elf_rela_dyn_entries(Elf *elf) { + I64 i; + U8 *entry_name; + RELA_entry *rela_dyn = elf->rela_dyn; + for (i = 0; i < elf->rela_dyn_size; i++) { + entry_name = elf->dynstr + elf->dynsym[(rela_dyn->r_info >> 32)].st_name; + debug_print("rela_dyn->r_offset = %08x\n", rela_dyn->r_offset); + debug_print("entry name = '%s'\n", entry_name); + if (!StrCompare(entry_name, "__libc_start_main")) { + *(rela_dyn->r_offset)(U64 *) = &_main; + debug_print("Set value for .rela.dyn entry '%s' to: &_main\n", + entry_name); + } + if (!StrCompare(entry_name, "stdin")) { + *(rela_dyn->r_offset)(U64 *) = 0; + debug_print("Set value for .rela.dyn entry '%s' to: %d\n", entry_name, 0); + } + if (!StrCompare(entry_name, "stdout")) { + *(rela_dyn->r_offset)(U64 *) = 1; + debug_print("Set value for .rela.dyn entry '%s' to: %d\n", entry_name, 1); + } + if (!StrCompare(entry_name, "stderr")) { + *(rela_dyn->r_offset)(U64 *) = 2; + debug_print("Set value for .rela.dyn entry '%s' to: %d\n", entry_name, 2); + } + rela_dyn++; + } +} + +CHashClass *get_symbol_hash_entry(U8 *entry_name) { + I64 i; + CHashSrcSym *sym; + CHashTable *tbl = Fs->hash_table; + while (tbl) { + for (i = 0; i < tbl->mask; i++) { + sym = tbl->body[i]; + while (sym) { + if (sym->type == HTT_CLASS) + if (!StrCompare(sym->str, entry_name)) + return sym; + sym = sym->next; + } + } + tbl = tbl->next; + } + return NULL; +} + +U0 process_elf_debug_symbols(Elf *elf) { + I64 i = 0; + I64 entry_bind; + U64 entry_name; + I64 entry_type; + CHashFun *hf; + CHashGlobalVar *hgv; + Elf64_Sym *symtab = elf->symtab; + entry_name = elf->strtab; + entry_name += symtab->st_name; + while (i < 253) { + entry_bind = symtab->st_info >> 4; + entry_type = symtab->st_info & 0xf; + switch (entry_type) { + case STT_OBJECT: + hgv = CAlloc(sizeof(CHashGlobalVar)); + hgv->str = entry_name; + hgv->type = HTT_GLOBAL_VAR; + hgv->data_addr = symtab->st_value; + hgv->size = symtab->st_size; + // TempleOS reboots if I nest a switch table here, for some reason, so we + // have to do this dumb shit instead... + hgv->var_class = get_symbol_hash_entry("U32"); + if (hgv->size == 1) + hgv->var_class = get_symbol_hash_entry("U8"); + if (hgv->size == 2) + hgv->var_class = get_symbol_hash_entry("U16"); + HashAdd(hgv, Fs->hash_table); + debug_print( + "Add global variable '%s' to hash table, addr = 0x%08x, size = %d\n", + entry_name, symtab->st_value, symtab->st_size); + break; + case STT_FUNC: + hf = CAlloc(sizeof(CHashFun)); + hf->str = entry_name; + hf->type = HTT_FUN; + hf->exe_addr = symtab->st_value; + hf->size = symtab->st_size; + HashAdd(hf, Fs->hash_table); + debug_print("Add function '%s' to hash table, addr = 0x%08x, size = %d\n", + entry_name, symtab->st_value, symtab->st_size); + break; + } + symtab++; + i++; + entry_name = elf->strtab; + entry_name += symtab->st_name; + } +} + +U64 get_symbol_address(U8 *entry_name) { + I64 i; + CHashSrcSym *sym; + CHashTable *tbl = Fs->hash_table; + while (tbl) { + for (i = 0; i < tbl->mask; i++) { + sym = tbl->body[i]; + while (sym) { + if (sym->type == HTT_GLOBAL_VAR) + if (!StrCompare(sym->str, entry_name)) + return sym(CHashGlobalVar *)->data_addr; + if (sym->type == HTT_FUN) + if (!StrCompare(sym->str, entry_name)) + return sym(CHashFun *)->exe_addr; + sym = sym->next; + } + } + tbl = tbl->next; + } + return NULL; +} + +U0 process_debug_patches(Elf *elf) { no_warn elf; } + +U0 process_elf_rela_plt_entries(Elf *elf) { + I64 i; + U32 handler; + U32 *patch; + U8 *entry_name; + Bool symbol_exists; + PLT_entry *plt = elf->plt; + RELA_entry *rela_plt = elf->rela_plt; + plt++; + for (i = 0; i < elf->rela_plt_size; i++) { + symbol_exists = FALSE; + entry_name = elf->dynstr + elf->dynsym[(rela_plt->r_info >> 32)].st_name; + handler = MAlloc(sizeof(unimplemented_symbol), Fs->code_heap); + MemCopy(handler, &unimplemented_symbol, sizeof(unimplemented_symbol)); + patch = handler + 0x0A; + *patch = entry_name; + PatchJmpRel32(plt, handler); + PatchCallRel32(handler + 0x16, &PrintErr); + PatchCallRel32(handler + 0x21, &_exit); + if (!StrCompare(entry_name, "__libc_start_main")) { + symbol_exists = TRUE; + PatchJmpRel32(plt, &_main); + debug_print("Set value for .rela.plt entry '%s' to &_main\n", entry_name); + } + if (get_symbol_address(entry_name)) { + symbol_exists = TRUE; + PatchJmpRel32(plt, get_symbol_address(entry_name)); + debug_print("Set value for .rela.plt entry '%s' to &%s\n", entry_name, + entry_name); + } + if (!symbol_exists) + debug_print( + "Set value for .rela.plt entry '%s' to &unimplemented_symbol\n", + entry_name); + rela_plt++; + plt++; + } +} + +U0 load_elf(...) { + if (argc < 1) { + PrintErr("Not enough arguments.\n"); + return; + } + if (!FileFind(argv[0])) { + PrintErr("File not found: %s\n", argv[0]); + return; + } + + Elf elf; + elf.u8 = FileRead(argv[0], &elf.size); + debug_print("Load file '%s', size = %d bytes\n", argv[0], elf.size); + + if (!is_valid_elf(&elf)) { + PrintErr("File is not a valid ELF x86-64 executable.\n"); + return; + } + + process_elf_section_header_table(&elf); + process_elf_rela_dyn_entries(&elf); + process_elf_rela_plt_entries(&elf); + process_elf_debug_symbols(&elf); + + _start = elf.ehdr->e_entry; + elf_argc = argc; + elf_argv = argv; + + process_debug_patches(&elf); + + //_start(); +} \ No newline at end of file diff --git a/src/Home/Tracker/Lib/LibC.ZC b/src/Home/Tracker/Lib/LibC.ZC new file mode 100755 index 00000000..4b44dc97 --- /dev/null +++ b/src/Home/Tracker/Lib/LibC.ZC @@ -0,0 +1,151 @@ +U64 find_u32_in_memory(U64 offset, U32 value) { + I64 i = 0; + while (MemCompare(offset + i, &value, 4)) + i++; + return offset + i; +} + +#define PUSH_SYSV_REGS \ + asm {PUSH RCX PUSH RDX PUSH RBX PUSH RBP PUSH RSI PUSH RDI PUSH R8 PUSH R9 PUSH \ + R10 PUSH R11 PUSH R12 PUSH R13 PUSH R14 PUSH R15} +#define POP_SYSV_REGS \ + asm {POP R15 POP R14 POP R13 POP R12 POP R11 POP R10 POP R9 POP R8 POP RDI POP \ + RSI POP RBP POP RBX POP RDX POP RCX} +#define MOV_ANS_RAX asm {MOV [&ans], RAX} +#define MOV_P0_RDI asm {MOV [&p0], RDI} +#define MOV_P1_RSI asm {MOV [&p1], RSI} +#define MOV_P2_RDX asm {MOV [&p2], RDX} +#define MOV_P3_RCX asm {MOV [&p3], RCX} +#define MOV_P4_R8 asm {MOV [&p4], R8} +#define MOV_P5_R9 asm {MOV [&p5], R9} +#define GET_SYSV_ARGS \ + MOV_P0_RDI MOV_P1_RSI MOV_P2_RDX MOV_P3_RCX MOV_P4_R8 MOV_P5_R9 + +I64 p0, p1, p2, p3, p4, p5; +I64 elf_argc; +U8 **elf_argv; + +#define stdin 0 +#define stdout 1 +#define stderr 2 + +asm { +_ELF_CALL:: + PUSH RBP + MOV RBP,RSP + MOV RAX,U64 SF_ARG1[RBP] + MOV RDI,U64 SF_ARG2[RBP] + MOV RSI,U64 SF_ARG3[RBP] + TEST RAX,RAX + JZ @@05 + CALL RAX +@@05: POP RBP + RET1 8 +} + +U0 _main() { + MOV_P0_RDI + CallInd(_ELF_CALL, p0, elf_argc, elf_argv); + MOV_ANS_RAX + throw('end', TRUE); +} + +U0 _exit() { + MOV_ANS_RAX + throw('end', TRUE); +} + +U0 free() { + PUSH_SYSV_REGS + GET_SYSV_ARGS + debug_print("called free(0x%08x)\n", p0); + Free(p0); + POP_SYSV_REGS +} + +U64 @malloc(I64 size) { + U64 ptr = NULL; + ptr = MAlloc(p0); + debug_print("malloc(%d), result: 0x%08x\n", size, ptr); + return ptr; +} + +U0 malloc() { + PUSH_SYSV_REGS + GET_SYSV_ARGS + debug_print("called: malloc(%d)\n", p0); + @malloc(p0); + POP_SYSV_REGS +} + +U0 memcpy() { + PUSH_SYSV_REGS + GET_SYSV_ARGS + debug_print("called: memcpy(0x%08x, 0x%08x, %d)\n", p0, p1, p2); + MemCopy(p0, p1, p2); + POP_SYSV_REGS +} + +U8 *@memmove(U8 *dest, U8 *src, I64 n) { + I64 i; + U8 *from = src; + U8 *to = dest; + if (from == to || n == 0) + return dest; + if (to > from && to - from < n) { + /* to overlaps with from */ + /* */ + /* */ + /* copy in reverse, to avoid overwriting from */ + for (i = n - 1; i >= 0; i--) + to[i] = from[i]; + return dest; + } + if (from > to && from - to < n) { + /* to overlaps with from */ + /* */ + /* */ + /* copy forwards, to avoid overwriting from */ + for (i = 0; i < n; i++) + to[i] = from[i]; + return dest; + } + MemCopy(dest, src, n); + return dest; +} + +U0 memmove() { + PUSH_SYSV_REGS + GET_SYSV_ARGS + debug_print("called: memmove(0x%08x, 0x%08x, %d)\n", p0, p1, p2); + @memmove(p0, p1, p2); + POP_SYSV_REGS +} + +U0 memset() { + PUSH_SYSV_REGS + GET_SYSV_ARGS + debug_print("called: memset(0x%08x, %d, %d)\n", p0, p1, p2); + MemSet(p0, p1, p2); + POP_SYSV_REGS +} + +U8 *@realloc(U8 *ptr, I64 size) { + U8 *new; + if (!ptr) { + new = MAlloc(size); + } else { + new = MAlloc(size); + MemCopy(new, ptr, size); + Free(ptr); + } + return new; +} + +U0 realloc() { + PUSH_SYSV_REGS + GET_SYSV_ARGS + debug_print("called: realloc(0x%08x, %d)\n", p0, p1); + @realloc(p0, p1); + POP_SYSV_REGS +} diff --git a/src/Home/Tracker/Lib/LibHolyC.ZC b/src/Home/Tracker/Lib/LibHolyC.ZC new file mode 100755 index 00000000..fd630350 --- /dev/null +++ b/src/Home/Tracker/Lib/LibHolyC.ZC @@ -0,0 +1,16 @@ +asm { +GCC_FUN_ADDR:: + DU64 0; +HOLYC_ARG1:: + DU64 0; +HOLYC_ARG2:: + DU64 0; +HOLYC_ARG3:: + DU64 0; +HOLYC_ARG4:: + DU64 0; +HOLYC_ARG5:: + DU64 0; +HOLYC_ARG6:: + DU64 0; +} diff --git a/src/Home/Tracker/Lib/Misc.ZC b/src/Home/Tracker/Lib/Misc.ZC new file mode 100755 index 00000000..ce0b83a7 --- /dev/null +++ b/src/Home/Tracker/Lib/Misc.ZC @@ -0,0 +1,30 @@ +I64 Cond(Bool cond, I64 true, I64 false) { + if (cond) + return true; + return false; +} + +U0 PatchCallRel32(U32 from, U32 to) { + *(from(U8 *)) = 0xE8; + *((from + 1)(I32 *)) = to - from - 5; +} + +U0 PatchJmpRel32(U32 from, U32 to) { + *(from(U8 *)) = 0xE9; + *((from + 1)(I32 *)) = to - from - 5; +} + +U0 EnableSSE() { + asm + { + MOV_EAX_CR0 + AND AX, 0xFFFB // clear coprocessor emulation CR0.EM + OR AX, 0x2 // set coprocessor monitoring CR0.MP + MOV_CR0_EAX + MOV_EAX_CR4 + OR AX, 3 << 9 // set CR4.OSFXSR and CR4.OSXMMEXCPT at the same time + MOV_CR4_EAX + } +} + +EnableSSE; diff --git a/src/Home/Tracker/Lib/Pci.ZC b/src/Home/Tracker/Lib/Pci.ZC new file mode 100755 index 00000000..f08266f9 --- /dev/null +++ b/src/Home/Tracker/Lib/Pci.ZC @@ -0,0 +1,98 @@ + +#define PCI_INTH_MAX 16 + +U64 pci_int_handlers[PCI_INTH_MAX]; + +class CPCIInfo +{ + U16 vendor_id; + U16 device_id; + U16 command; + U16 status; + U32 _class; + U32 bar[6]; + U32 cap_pointer; +}; + +class CPCICapability +{ + U8 cap_vndr; /*Generic PCI field: PCI_CAP_ID_VNDR */ + U8 cap_next; /*Generic PCI field: next ptr. */ + U8 cap_len; /*Generic PCI field: capability length */ + U8 cfg_type; /*Identifies the structure. */ + U8 bar; /*Where to find it. */ + U8 padding[3]; /*Pad to full dword. */ + U32 offset; /*Offset within bar. */ + U32 length; /*Length of the structure, in bytes. */ +}; + +U0 PCIInfoGet(I64 i, CPCIInfo *pci) +{ + I64 j; + pci->vendor_id = PCIReadU32(i.u8[2], i.u8[1], i.u8[0], 0x0) &0xFFFF; + pci->device_id = PCIReadU32(i.u8[2], i.u8[1], i.u8[0], 0x0) >> 16; + pci->command = PCIReadU32(i.u8[2], i.u8[1], i.u8[0], 0x4) &0xFFFF; + pci->status = PCIReadU32(i.u8[2], i.u8[1], i.u8[0], 0x4) >> 16; + pci->_class = PCIReadU32(i.u8[2], i.u8[1], i.u8[0], 0x8) >> 24; + for (j = 0; j < 6; j++) + pci->bar[j] = PCIReadU32(i.u8[2], i.u8[1], i.u8[0], 0x10 + (0x04 * j)); +} + +U0 PCIGetCapability(I64 i, CPCICapability *cap, I64 idx) +{ + I64 base = 0x40 + (idx * 16); + U32 u32; + u32 = PCIReadU32(i.u8[2], i.u8[1], i.u8[0], base); + cap->cap_vndr = u32.u8[0]; + cap->cap_next = u32.u8[1]; + cap->cap_len = u32.u8[2]; + cap->cfg_type = u32.u8[3]; + u32 = PCIReadU32(i.u8[2], i.u8[1], i.u8[0], base + 0x04); + cap->bar = u32.u8[0]; + cap->offset = PCIReadU32(i.u8[2], i.u8[1], i.u8[0], base + 0x08); + cap->length = PCIReadU32(i.u8[2], i.u8[1], i.u8[0], base + 0x0c); +} + +U0 PCIInterruptReroute(I64 base, I64 cpu) +{ + I64 i; + U8 *da = dev.uncached_alias + IOAPIC_REG; + U32 *_d = dev.uncached_alias + IOAPIC_DATA; + + for (i = 0; i < 4; i++) + { + *da = IOREDTAB + i * 2 + 1; + *_d = dev.mp_apic_ids[cpu] << 24; + *da = IOREDTAB + i * 2; + *_d = 0x4000 + base + i; + } +} + +I64 PCIInterruptHandlerRegister(U64 handler) +{ + if (!handler) + return -1; + I64 i = 0; + while (pci_int_handlers[i]) + i++; + if (i > PCI_INTH_MAX - 1) + return -1; + pci_int_handlers[i] = handler; + return 0; +} + +interrupt U0 PCIInterruptHandler() +{ + I64 i; + for (i = 0; i < PCI_INTH_MAX; i++) + if (pci_int_handlers[i]) + Call(pci_int_handlers[i]); + *(dev.uncached_alias + LAPIC_EOI)(U32 *) = 0; +} + +MemSet(&pci_int_handlers, NULL, sizeof(U64) * PCI_INTH_MAX); +// IntEntrySet(0x40, &PCIInterruptHandler, IDTET_IRQ); +// IntEntrySet(0x41, &PCIInterruptHandler, IDTET_IRQ); +// IntEntrySet(0x42, &PCIInterruptHandler, IDTET_IRQ); +// IntEntrySet(0x43, &PCIInterruptHandler, IDTET_IRQ); +//PCIInterruptReroute(0x40, 0); \ No newline at end of file diff --git a/src/Home/Tracker/Load.ZC b/src/Home/Tracker/Load.ZC new file mode 100755 index 00000000..3f288a46 --- /dev/null +++ b/src/Home/Tracker/Load.ZC @@ -0,0 +1,32 @@ +#define include_noreindex #include + +include_noreindex "Lib/Debug"; +include_noreindex "Lib/Misc"; + +include_noreindex "Lib/Pci"; +include_noreindex "Lib/AC97"; + +include_noreindex "Lib/LibC"; +include_noreindex "Lib/LibHolyC"; +include_noreindex "Lib/ELF64"; + +// load_elf("dr_mp3"); + +// include_noreindex "Lib/uPNG32"; +// include_noreindex "Player32"; + +//include_noreindex "Lib/uPNG"; +// include_noreindex "Player"; + +include_noreindex "Classes" +include_noreindex "MIDIHandling" +include_noreindex "WaveformGen" +// include_noreindex "UITracker" +include_noreindex "MusicTracker" + + +AutoComplete(0); + +MusicTracker; + +//InitPlayer; diff --git a/src/Home/Tracker/MIDIHandling.ZC b/src/Home/Tracker/MIDIHandling.ZC new file mode 100644 index 00000000..6099ae99 --- /dev/null +++ b/src/Home/Tracker/MIDIHandling.ZC @@ -0,0 +1,26 @@ +// precalculated, takes more memory but less CPU +F64 midi_to_freq[128] = { + 8.1758, 8.6620, 9.1770, 9.7227, 10.3009, 10.9134, 11.5623, 12.2499, + 12.9783, 13.7500, 14.5676, 15.4339, 16.3516, 17.3239, 18.3540, 19.4454, + 20.6017, 21.8268, 23.1247, 24.4997, 25.9565, 27.5000, 29.1352, 30.8677, + 32.7032, 34.6478, 36.7081, 38.8909, 41.2034, 43.6535, 46.2493, 48.9994, + 51.9131, 55.0000, 58.2705, 61.7354, 65.4064, 69.2957, 73.4162, 77.7817, + 82.4069, 87.3071, 92.4986, 97.9989, 103.8262, 110.0000, 116.5409, 123.4708, + 130.8128, 138.5913, 146.8324, 155.5635, 164.8138, 174.6141, 184.9972, 195.9977, + 207.6523, 220.0000, 233.0819, 246.9417, 261.6256, 277.1826, 293.6648, 311.1270, + 329.6276, 349.2282, 369.9944, 391.9954, 415.3047, 440.0000, 466.1638, 493.8833, + 523.2511, 554.3653, 587.3295, 622.2540, 659.2551, 698.4565, 739.9888, 783.9909, + 830.6094, 880.0000, 932.3275, 987.7666, 1046.5023, 1108.7305, 1174.6591, 1244.5079, + 1318.5102, 1396.9129, 1479.9777, 1567.9817, 1661.2188, 1760.0000, 1864.6550, 1975.5332, + 2093.0045, 2217.4610, 2349.3181, 2489.0159, 2637.0205, 2793.8259, 2959.9554, 3135.9635, + 3322.4376, 3520.0000, 3729.3101, 3951.0664, 4186.0090, 4434.9221, 4698.6363, 4978.0317, + 5274.0410, 5587.6517, 5919.9108, 6271.9270, 6644.8752, 7040.0000, 7458.6202, 7902.1328, + 8372.0181, 8869.8442, 9397.2726, 9956.0635, 10548.0820, 11175.3034, 11839.8215, 12543.8540 +}; + + +// slower cause uses CPU but takes less memory +F64 MidiToFreq(I64 note) { + // A4 (MIDI note 69) = 440 Hz, and there's a 2^(1/12) ratio between adjacent semitones. + return 440.0 * Pow(2.0, (note - 69.0) / 12.0); +} \ No newline at end of file diff --git a/src/Home/Tracker/MusicTracker.ZC b/src/Home/Tracker/MusicTracker.ZC new file mode 100644 index 00000000..e2330b5a --- /dev/null +++ b/src/Home/Tracker/MusicTracker.ZC @@ -0,0 +1,67 @@ +#include "MIDIHandling" +#include "WaveformGen" + +U0 AudioPlayNote(U8 note, U8 velocity) { + U32 buffer[44100]; // One second buffer at 44.1kHz sample rate. + F64 freq = MidiToFreq(note); + GenerateSineWave(buffer, SAMPLE_RATE, freq, velocity); + + // Play the buffer using your AC97 driver: + AudioSFXPlay(buffer, 44100); // Assuming your AudioSFXPlay function can handle this +} + +U0 EnterPattern(Pattern *pattern) { + I64 row, choice; + NoteCell *cell; + for (row = 0; row < TRACK_LENGTH; row++) { + cell = &pattern->cells[row]; + Print("Enter note for row %d (0-127, 0 for none): ", row); + // cell->note = InU8; + // if (cell->note) { + // Print("Enter velocity for note (1-127): "); + // cell->velocity = InU8; + // } + } +} + +U0 PlayPattern(Pattern *pattern) { + I64 row; + NoteCell *cell; + for (row = 0; row < TRACK_LENGTH; row++) { + cell = &pattern->cells[row]; + if (cell->note) { + // Here, use your audio driver to play the note + // Assume a function AudioPlayNote(note, velocity) exists + AudioPlayNote(cell->note, cell->velocity); + } + Sleep(100); // Adjust for tempo + } +} + +U0 MusicTracker() { + Song song; + // Clear(&song); + I64 choice, sc; + while (1) { + Print("\nMusic Tracker:\n"); + Print("1. Enter Pattern\n"); + Print("2. Play Pattern\n"); + Print("3. Exit\n"); + Print("Choice: "); + // choice = InU8; + choice = KeyGet(&sc); + + switch (choice) { + case '1': + EnterPattern(&song.patterns[0]); // Only one pattern for simplicity + break; + case '2': + PlayPattern(&song.patterns[0]); + break; + case '3': + return; + default: + Print("Invalid choice.\n"); + } + } +} diff --git a/src/Home/Tracker/Run.ZC b/src/Home/Tracker/Run.ZC new file mode 100755 index 00000000..54b7cb7c --- /dev/null +++ b/src/Home/Tracker/Run.ZC @@ -0,0 +1,2 @@ +Cd(__DIR__);; +#include "Load"; \ No newline at end of file diff --git a/src/Home/Tracker/UITracker.ZC b/src/Home/Tracker/UITracker.ZC new file mode 100644 index 00000000..48081d00 --- /dev/null +++ b/src/Home/Tracker/UITracker.ZC @@ -0,0 +1,82 @@ +//-------------------------- +// UI Functions +//-------------------------- + +Pattern myPattern; + +myPattern.cells[0].note = 60; // Middle C +myPattern.cells[0].velocity = 100; + +myPattern.cells[1].note = 62; // D +myPattern.cells[1].velocity = 100; + +myPattern.cells[2].note = 64; // E +myPattern.cells[2].velocity = 100; +U64 InU64() { + U8 buf[32]; + InStr(buf); + return ToI64(buf); +} + +U0 DisplayMainUI() { + // Simple function to display a main menu + DocClear; + Print("\nMusic Tracker UI\n"); + Print("1. Enter New Pattern\n"); + Print("2. Play Pattern\n"); + Print("3. Exit\n"); +} + +U0 HandleUserInput(Pattern *pattern) { + I64 choice, sc; + Print("Enter your choice: "); + // choice = InU64; + + switch (KeyGet(&sc)) { + case '1': + EnterPattern(pattern); + break; + case '2': + PlayPattern(pattern); + break; + case '3': + Print("Goodbye!\n"); + return; // Exit the loop and function + default: + Print("Invalid choice!\n"); + break; + } +} + +U0 ShowError(String msg) { + // Display error messages to the user + Print("Error: %s\n", msg); +} + +U0 RenderPattern(Pattern *pattern) { + // For simplicity, just listing the notes in the pattern + for (I64 i = 0; i < TRACK_LENGTH; i++) { + Print("Row %d: Note %d, Velocity %d\n", i, pattern->cells[i].note, pattern->cells[i].velocity); + } +} + +U0 SettingsMenu() { + // Display and handle the settings menu + // ... +} + +//-------------------------- +// Main UI Loop +//-------------------------- + +U0 UITrackerMainLoop(Pattern *pattern) { + while (1) { + DisplayMainUI(); + HandleUserInput(pattern); + } +} + +//-------------------------- +// Entry point to run the UI +//-------------------------- +UITrackerMainLoop(&myPattern); diff --git a/src/Home/Tracker/WaveformGen.ZC b/src/Home/Tracker/WaveformGen.ZC new file mode 100644 index 00000000..ccc47878 --- /dev/null +++ b/src/Home/Tracker/WaveformGen.ZC @@ -0,0 +1,20 @@ +U0 GenerateSineWave(U32 *buffer, I64 length, F64 freq, U8 velocity) { + I64 i; + F64 amplitude = (ToF64(velocity) / 127.0) * 0x7FFF; // Adjust the amplitude based on velocity + F64 phase_increment = (2.0 * PI * freq) / 44100.0; // Assuming a sample rate of 44.1kHz + + F64 phase = 0.0; + for (i = 0; i < length; i++) { + I16 sample_value = ToI64((Sin(phase) * amplitude)); // TODO should be ToI16 + buffer[i] = (sample_value << 16) | (sample_value & 0xFFFF); + phase += phase_increment; + if (phase >= 2.0 * PI) phase -= 2.0 * PI; + } +} + + +// I64 sample_rate = SAMPLE_RATE // whatever your sample rate is +// for (I64 i = 0; i < sample_duration * sample_rate; i++) { +// sample = sin(2.0 * PI * freq * i / sample_rate); +// // Then send 'sample' to your audio buffer/output. +// } \ No newline at end of file