diff --git a/firmware/CFFA1_API.s b/firmware/CFFA1_API.s new file mode 100644 index 0000000..183feb9 --- /dev/null +++ b/firmware/CFFA1_API.s @@ -0,0 +1,259 @@ +;------------------------------------------------------------------------------ +; CFFA1_API.s 10-Feb-2013 +; Released with CFFA1 firmware v1.1. +; +; Equates for calling the CFFA1 API. +;------------------------------------------------------------------------------ + +CFFA1_ID1 = $AFDC ; contains $CF when CFFA1 card is present +CFFA1_ID2 = $AFDD ; contains $FA when CFFA1 card is present + +; +; FirmwareVersion $01 = CFFA1 firmware 1.0 +; FirmwareVersion $02 = CFFA1 firmware 1.1 +; +FirmwareVersion = $02 + +;------------------------------------------------------------------------------ +; Entry points to the CFFA1 firmware: +; +; MenuExitToMonitor +; JMP here to display the CFFA1 menu. +; Quit puts the user into the monitor. +; +; MenuExitToBASIC +; JMP here to display the CFFA1 menu. +; Quit puts the user into BASIC. +; +; Menu +; JSR here to display the CFFA1 menu. +; Quit returns control to your code. +; +; CFBlockDriver +; JSR here to read or write a block, after setting up pdCommandCode +; and other inputs (see below). +; Result: CLC, A = 0 +; SEC, A = error code +; +; CFFA1_API +; JSR here to call one of many functions provided by the firmware. +; See "Function selectors for CFFA1_API" below. +; +;------------------------------------------------------------------------------ +MenuExitToMonitor = $9000 +MenuExitToBASIC = $9003 +Menu = $9006 +CFBlockDriver = $9009 +CFFA1_API = $900C +MenuWithSerialIO = $9012 + +;------------------------------------------------------------------------------ +; Inputs for CFBlockDriver - ProDOS block interface locations +;------------------------------------------------------------------------------ +pdCommandCode = $42 ; see below +pdUnitNumber = $43 ; always set this to 0 for firmware 1.0 +pdIOBufferLow = $44 +pdIOBufferHigh = $45 +pdBlockNumberLow = $46 +pdBlockNumberHigh = $47 + +; +; Values for pdCommandCode +; +PRODOS_STATUS = $00 +PRODOS_READ = $01 +PRODOS_WRITE = $02 +PRODOS_FORMAT = $03 + + +;------------------------------------------------------------------------------ +; Function selectors for CFFA1_API. +; +; Load one of these values into X: +; +; ldx #CFFA1_xxxxx +; jsr CFFA1_API +; +; Result: CLC, A = 0 +; SEC, A = error code +; +; Certain functions have additional outputs, as described below. +; +;------------------------------------------------------------------------------ +; +; CFFA1_Version: +; Output: X = current firmware version +; Y = oldest compatible firmware version +; +; CFFA1_Menu: +; Result: Runs the CFFA1 menu and returns when the user chooses Quit. +; +; CFFA1_DisplayError: +; Input: A = an error code +; Result: Prints out a carriage return, the 2-digit hex error code, +; and a description of that error, if available. +; +; CFFA1_OpenDir: +; Input: None (operates on the current prefix directory) +; Result: Prepares for one or more calls to ReadDir. +; +; CFFA1_ReadDir: +; Setup: You have to call OpenDir before calling ReadDir. +; Result: If no error, EntryPtr points to the next occupied directory entry. +; +; CFFA1_FindDirEntry: +; Input: Filename = name to search for +; Result: If no error, EntryPtr points at the found item's directory entry. +; +; CFFA1_WriteFile: +; Input: Filename = name for new file (will be replaced if it already exists) +; Destination = starting address +; FileSize = number of bytes to write +; Filetype = type for new file +; Auxtype = auxiliary type for new file +; +; CFFA1_ReadFile: +; Input: Filename = file to read into memory +; Destination = starting address ($0000 to use the file's Auxtype value) +; +; CFFA1_SaveBASICFile: +; Input: Filename +; +; CFFA1_LoadBASICFile: +; Input: Filename +; +; CFFA1_Rename: +; Input: OldFilename = original name +; Filename = new name +; +; CFFA1_Delete: +; Input: Filename = file or empty directory to delete +; +; CFFA1_NewDirectoryAtRoot: +; Input: Filename = name for new directory +; +; CFFA1_FormatDrive: +; Input: Filename = name for new volume +; A = drive number (always set to 0 for firmware 1.0) +; Y = $77 (just to help avoid accidental formatting) +; Result: Disk volume is erased and given the specified name. +; +;------------------------------------------------------------------------------ +CFFA1_Version = $00 +CFFA1_Menu = $02 +CFFA1_DisplayError = $04 + +CFFA1_OpenDir = $10 +CFFA1_ReadDir = $12 +CFFA1_FindDirEntry = $14 + +CFFA1_WriteFile = $20 +CFFA1_ReadFile = $22 +CFFA1_SaveBASICFile = $24 +CFFA1_LoadBASICFile = $26 +CFFA1_Rename = $28 +CFFA1_Delete = $2A +CFFA1_NewDirectoryAtRoot = $2C +CFFA1_FormatDrive = $2E + + +;------------------------------------------------------------------------------ +; Zero-page inputs and results for API functions +; +; Filename and OldFilename point to strings that begin with a length byte (from +; 1 to 15), and each character must have its high bit off. For example: +; +; Filename = $80 $280: 05 48 45 4C 4C 4F +; Filename+1 = $02 'H' 'E' 'L' 'L' 'O' +;------------------------------------------------------------------------------ +Destination = $00 ; 2 bytes +Filename = Destination+2 ; 2 bytes +OldFilename = Filename+2 ; 2 bytes +Filetype = OldFilename+2 ; 1 byte +Auxtype = Filetype+1 ; 2 bytes +FileSize = Auxtype+2 ; 2 bytes +EntryPtr = FileSize+2 ; 2 bytes + + +;------------------------------------------------------------------------------ +; +; ProDOS low-level return codes +; +;------------------------------------------------------------------------------ +PRODOS_NO_ERROR = $00 ; No error +PRODOS_BADCMD = $01 ; Bad Command (not implemented) +PRODOS_IO_ERROR = $27 ; I/O error +PRODOS_NO_DEVICE = $28 ; No Device Connected +PRODOS_WRITE_PROTECT = $2B ; Write Protected +PRODOS_BADBLOCK = $2D ; Invalid block number requested +PRODOS_OFFLINE = $2F ; Device off-line +; +; High-level return codes +; +eBadPathSyntax = $40 +eDirNotFound = $44 +eFileNotFound = $46 +eDuplicateFile = $47 +eVolumeFull = $48 +eDirectoryFull = $49 +eFileFormat = $4A +eBadStrgType = $4B +eFileLocked = $4E +eNotProDOS = $52 +eBadBufferAddr = $56 +eBakedBitmap = $5A +eUnknownBASICFormat = $FE +eUnimplemented = $FF + + +;------------------------------------------------------------------------------ +; ProDOS directory entry structure offsets +;------------------------------------------------------------------------------ +oFiletype = $10 +oKeyBlock = $11 +oBlockCount = $13 +oFileSize = $15 +oCreateDateTime = $18 +oVersion = $1C +oMinVersion = $1D +oAccess = $1E +oAuxtype = $1F +oModDateTime = $21 +oHeaderPointer = $25 + +oDirLinkPrevious = $00 +oDirLinkNext = $02 +oVolStorageType = $04 +oVolVersion = $20 +oVolAccess = $22 +oVolEntryLength = $23 +oVolEntriesPerBlock = $24 +oVolFileCount = $25 +oVolBitmapNumber = $27 +oVolTotalBlocks = $29 + +; +; ProDOS Storage types +; +kSeedling = $10 +kSapling = $20 +kTree = $30 +kExtended = $50 +kDirectory = $D0 +kSubdirHeader = $E0 +kVolume = $F0 +kStorageTypeMask = $F0 + +; +; Filetypes +; +kFiletypeText = $04 +kFiletypeBinary = $06 +kFiletypeDirectory = $0F +kFiletypeBASIC1 = $F1 +kFiletypeBAS = $FC +kFiletypeSYS = $FF + +;------------------------------------------------------------------------------ +; end of CFFA1_API.s +;------------------------------------------------------------------------------ diff --git a/firmware/build_rom.sh b/firmware/build_rom.sh new file mode 100755 index 0000000..245b823 --- /dev/null +++ b/firmware/build_rom.sh @@ -0,0 +1,51 @@ +#!/bin/sh + +# cl65 -O -l -vm -m hello1.map -t replica1 hello1.c + +echo "Building Vicious ROM image: vicious" +cc65 -O -t replica1 main.c +cc65 -O -t replica1 vicilibc.c +cc65 -O -t replica1 sidfile.c +cc65 -O -t replica1 cffa1.c + +ca65 main.s +ca65 viciliba.s +ca65 vicilibc.s +ca65 sidfile.s +ca65 cffa1.s +ca65 cffa1a.s + +ld65 -o vicious -m vicious.map -C ld_rom.cfg main.o viciliba.o vicilibc.o sidfile.o cffa1.o cffa1a.o replica1.lib + +#bintomon hello >hellomon + +#rm -f vicious.map +rm -f main.s +rm -f vicilibc.s +rm -f cffa1.s + +cp vicious /Applications/OpenEmulator.app/Contents/Resources/roms/Briel/vicious.bin +echo "Copied Vicious ROM image to OpenEmulator" + +echo "Building romburner program: romburner" + +cc65 -O -t replica1 romburner.c +ca65 romburner.s +ca65 romburnerio.s +ld65 -o romburner -m romburner.map -C ld_romburner.cfg romburnerio.o romburner.o replica1.lib + +rm -f romburner.map +rm -f romburner.s + +echo "Creating self burning installation app: VICIOUSPROMMER" +rm -f VICIOUSPROMMER +touch VICIOUSPROMMER +cat romburner > VICIOUSPROMMER +cat vicious >> VICIOUSPROMMER + + +rm -f *.o + + + + diff --git a/firmware/cffa1.c b/firmware/cffa1.c new file mode 100644 index 0000000..9dae150 --- /dev/null +++ b/firmware/cffa1.c @@ -0,0 +1,90 @@ + +#include +#include "cffa1.h" +#include "sidfile.h" + +#define FILE_LIST_LEN 15 + +#define PRODOS_FILESIZE_OFFSET 0x15 + +#define CFFA1_OPENDIR 0x10 +#define CFFA1_READDIR 0x12 +#define CFFA1_READFILE 0x22 + +#define CFFA1_DESTINATION 0x0 +#define CFFA1_FILENAME 0x2 +#define CFFA1_FILESIZE 0x9 +#define CFFA1_ENTRYPTR 0xB + +#define CFFA1_E_FILENOTFOUND 0x46 + +byte fileNames[FILE_LIST_LEN][1+16+2]; + + +word loadFile(word destination) { + byte i; + byte l; + byte ret; + byte keybInput = 0; + byte cont=1; + callCFFA1API(CFFA1_OPENDIR); + putchar('\r'); + while(cont) { + i=0; + while(i='A' && keybInput<=(i+0x40)) { + i = keybInput-0x41; + prints("\rLOADING "); + prints(&fileNames[i][1]); + prints("\r\r"); + + l=0; + while(1){ + if(fileNames[i][1+l]==0) { + fileNames[i][0] = l; + //printb(l); + break; + } + l++; + } + + POKEW(CFFA1_DESTINATION, destination); + POKEW(CFFA1_FILENAME, (word)&fileNames[i][0]); + //prints("\rFILENAME ADDR: "); + //printw((word)&fileNames[i][0]); + ret = callCFFA1API(CFFA1_READFILE); + if(ret!=0) { + prints("\rLOAD ERROR: "); + printb(ret); + } + return PEEKW((word)&fileNames[i][17]); + break; + } + } + return 0; +} + diff --git a/firmware/cffa1.h b/firmware/cffa1.h new file mode 100644 index 0000000..db3778f --- /dev/null +++ b/firmware/cffa1.h @@ -0,0 +1,14 @@ + +#ifndef CFFA1_H_ +#define CFFA1_H_ + +#include "vicilib.h" + +extern void gotoCFFA1(); + +extern byte __fastcall__ callCFFA1API(byte function); + +word loadFile(word destination); + + +#endif diff --git a/firmware/cffa1a.s b/firmware/cffa1a.s new file mode 100644 index 0000000..0ea2834 --- /dev/null +++ b/firmware/cffa1a.s @@ -0,0 +1,16 @@ + + .export _gotoCFFA1 + .export _callCFFA1API + + + .include "CFFA1_API.s" + + +_gotoCFFA1: + jsr $9006 + rts + +_callCFFA1API: + tax + jsr CFFA1_API + rts diff --git a/firmware/ld_rom.cfg b/firmware/ld_rom.cfg new file mode 100644 index 0000000..af87e06 --- /dev/null +++ b/firmware/ld_rom.cfg @@ -0,0 +1,37 @@ +MEMORY { + ZP: start = $0020, size = $00C0, define = yes; + HEADER: start = $0000, size = $0004, file = ""; + RAM: start = $7000, size = $1000, type = rw, define = yes; + ROM: start = $B000, size = $F00, type = ro, define = yes, file = %O, fill = yes; +} +SEGMENTS { + EXEHDR: load = HEADER, type = ro; + VECTORS: load = ROM, type = ro; + STARTUP: load = ROM, type = ro, define = yes; + LOWCODE: load = ROM, type = ro, optional = yes; + INIT: load = ROM, type = ro, define = yes, optional = yes; + CODE: load = ROM, type = ro; + RODATA: load = ROM, type = ro; + DATA: load = ROM, run = RAM, type = rw, define = yes; + BSS: load = RAM, type = bss, define = yes; + HEAP: load = RAM, type = bss, optional = yes; + ZEROPAGE: load = ZP, type = zp; +} +FEATURES { + CONDES: segment = INIT, + type = constructor, + label = __CONSTRUCTOR_TABLE__, + count = __CONSTRUCTOR_COUNT__; + CONDES: segment = RODATA, + type = destructor, + label = __DESTRUCTOR_TABLE__, + count = __DESTRUCTOR_COUNT__; + CONDES: type = interruptor, + segment = RODATA, + label = __INTERRUPTOR_TABLE__, + count = __INTERRUPTOR_COUNT__; +} +SYMBOLS { + __STACKSIZE__ = $400; +} + diff --git a/firmware/ld_romburner.cfg b/firmware/ld_romburner.cfg new file mode 100644 index 0000000..0805398 --- /dev/null +++ b/firmware/ld_romburner.cfg @@ -0,0 +1,36 @@ +MEMORY { + ZP: start = $0020, size = $00C0, define = yes; + HEADER: start = $0000, size = $0004, file = ""; + RAM: start = $1000, size = $300, file = %O, fill = yes; +} +SEGMENTS { + EXEHDR: load = HEADER, type = ro; + STARTUP: load = RAM, type = ro, define = yes; + LOWCODE: load = RAM, type = ro, optional = yes; + INIT: load = RAM, type = ro, define = yes, optional = yes; + CODE: load = RAM, type = ro; + RODATA: load = RAM, type = ro; + DATA: load = RAM, type = rw; + BSS: load = RAM, type = bss, define = yes; + HEAP: load = RAM, type = bss, optional = yes; + ZEROPAGE: load = ZP, type = zp; + #END: load = RAM, type = ro, define = yes; +} +FEATURES { + CONDES: segment = INIT, + type = constructor, + label = __CONSTRUCTOR_TABLE__, + count = __CONSTRUCTOR_COUNT__; + CONDES: segment = RODATA, + type = destructor, + label = __DESTRUCTOR_TABLE__, + count = __DESTRUCTOR_COUNT__; + CONDES: type = interruptor, + segment = RODATA, + label = __INTERRUPTOR_TABLE__, + count = __INTERRUPTOR_COUNT__; +} +SYMBOLS { + __STACKSIZE__ = $400; +} + diff --git a/firmware/main.c b/firmware/main.c new file mode 100644 index 0000000..26cbd68 --- /dev/null +++ b/firmware/main.c @@ -0,0 +1,46 @@ +#include +#include "vicilib.h" +#include "sidfile.h" +#include "cffa1.h" + +//extern word Destination; + +int main (void) +{ + byte keybInput = 0; + initVicilib(); + while(keybInput!='Q') { + prints("\rVICIOUS> "); + keybInput = readkey() - 0x80; + switch (keybInput) { + case 'P': + playSidFile(0x1000, 0x2000); + break; + case 'S': + prints("\rSTOP PLAYING\r"); + stopPlaying(); + break; + case 'C': + stopPlaying(); + gotoCFFA1(); + break; + case 'L': + stopPlaying(); + playSidFile(0x1000, loadFile(0x1000)); + break; + case 'Q': + prints("\rQUIT\r"); + break; + default: + prints("\r\r VICIOUS MENU (0.9)\r ------------------"); + prints("\r P - PLAY SID TUNE"); + prints("\r S - STOP PLAYING"); + prints("\r C - CFFA1"); + prints("\r L - LOAD & PLAY SID TUNE"); + prints("\r Q - QUIT\r"); + break; + } + + } + return 1; +} diff --git a/firmware/romburner.c b/firmware/romburner.c new file mode 100644 index 0000000..7b2f40d --- /dev/null +++ b/firmware/romburner.c @@ -0,0 +1,62 @@ + +#include + +typedef unsigned char byte; +typedef unsigned word; + +#define ROM_IMAGE_SIZE 3840 +#define PROM 0xB000 +#define ROM_IMAGE_START_ADDRESS 0x1300 // defined in linker config + +#define POKE(addr,val) (*(unsigned char*) (addr) = (val)) +#define POKEW(addr,val) (*(unsigned*) (addr) = (val)) +#define PEEK(addr) (*(unsigned char*) (addr)) +#define PEEKW(addr) (*(unsigned*) (addr)) + + +extern void __fastcall__ putchar(byte c); +extern void __fastcall__ printb(byte c); + + +//extern word _END_LOAD__[]; +//extern word _END_SIZE__[]; + + +void prints(unsigned char* string) { + byte i = 0; + byte c; + while((c = string[i++])!='\0') { + putchar(c); + } +} + +void printw(word w) { + byte lsb = (byte) w; + printb((byte)(w>>8)); + printb(lsb); +} + + + + +int main(void) { + word i=0; + + //word imageStartAddress = (word)_END_LOAD__ + (word)_END_SIZE__; + word imageStartAddress = ROM_IMAGE_START_ADDRESS; + + prints("\r\rVICIOUS SOUND CARD FIRMWARE ROM BURNER\r"); + + + prints("START ADDRESS: "); + printw(imageStartAddress); + prints("\r"); + + while(i96) c-=32; + to[i] = c; + i++; + } + to[len-1] = '\0'; +} + +word _readBigEndianW(word address) { + word ret = PEEK(address)<<8; + return(ret + PEEK(address+1)); +} + +byte readSidHeader(word address, SidHeader1* header1) { + header1->location = address; + _memcpy((byte*)header1->magicId, address, 4); + if(header1->magicId[0]!='P' || + header1->magicId[1]!='S' || + header1->magicId[2]!='I' || + header1->magicId[3]!='D') return 0; + header1->version = _readBigEndianW(address+4); + header1->dataOffset = _readBigEndianW(address+6); + header1->loadAddress = _readBigEndianW(address+8); + header1->initAddress = _readBigEndianW(address+10); + header1->playAddress = _readBigEndianW(address+12); + header1->songs = _readBigEndianW(address+14); + header1->startSong = _readBigEndianW(address+16); + _memcpy((byte*)header1->speed, address+18, 4); + _strcpy((byte*)header1->title, address+0x16, 32); + _strcpy((byte*)header1->author, address+0x36, 32); + _strcpy((byte*)header1->release, address+0x56, 32); + + // TODO: read v2 of the header + return 1; +} + + +word moveSIDCodeToPlace(SidHeader1* header1, word dataLen) { + word loadAddress; + word dataAddressFrom = header1->location + header1->dataOffset; + if(header1->loadAddress!=0) { + loadAddress = header1->loadAddress; + } + else { + loadAddress = PEEKW(dataAddressFrom); + dataAddressFrom += 2; + } + _memcpy((byte*)loadAddress, dataAddressFrom, dataLen); + return loadAddress; +} + + +void playSidFile(word address, word fileSize) { + SidHeader1 header1; + + word loadAddress; + + prints("\r\rVICIOUS SID PLAYER\r"); + + if(readSidHeader(address, &header1)) { + //prints("\rSID TYPE: "); printsl(header1.magicId, 4); + //prints(" V."); printw(header1.version); + prints("\rTITLE: "); prints(header1.title); + prints("\rAUTHOR: "); prints(header1.author); + prints("\rRELEASE: "); prints(header1.release); + prints("\rSONGS: $"); printw(header1.songs); + prints(" DEFAULT: $"); printw(header1.startSong); + //prints("\rDATAOFFSET: "); printw(header1.dataOffset); + //prints("\rLOAD ADDRESS: "); printw(header1.loadAddress); + prints("\rUSING FILE SIZE: $"); printw(fileSize); + prints("\rINIT ADDRESS: $"); printw(header1.initAddress); + prints("\rPLAY ADDRESS: $"); printw(header1.playAddress); + + loadAddress = moveSIDCodeToPlace(&header1, fileSize); + + prints("\rLOAD ADDRESS: $"); printw(loadAddress); + prints("\r"); + + fixSIDTune(loadAddress, fileSize); + + initSIDTune(header1.initAddress, header1.playAddress); + + + POKEW(CIA_TIMER_A_VALUE, 0x4e20); // is this about 50Hz + POKE(CIA_IRQ_CTRL, BIT_CIA_IRQ_CTRL_TIMER_A_UNDERFLOW+BIT_CIA_IRQ_CTRL_FILL_BIT); + + prints("START PLAYING...\r"); + // start CIA timer A + POKE(CIA_TIMER_A_CTRL, BIT_CIA_TIMER_LOAD_VALUE+BIT_CIA_TIMER_START); + } + else { // Header not found + prints("\rSID TUNE NOT FOUND IN ADDRESS $"); + printw(address); + prints("\r"); + } + + //while(1) ; + +} diff --git a/firmware/sidfile.h b/firmware/sidfile.h new file mode 100644 index 0000000..2fb8e3b --- /dev/null +++ b/firmware/sidfile.h @@ -0,0 +1,37 @@ + +#ifndef SIDFILE_H_ +#define SIDFILE_H_ + +#include "vicilib.h" + +#define SID_HEADER1_LEN 0x76 + +#define SID 0xbf00 + +typedef struct SidHeader1 { + word location; + byte magicId[4]; + word version; + word dataOffset; + word loadAddress; + word initAddress; + word playAddress; + word songs; + word startSong; + byte speed[4]; + byte title[32]; + byte author[32]; + byte release[32]; +} SidHeader1; + + +void playSidFile(word address, word fileSize); + +/* FALSE if header not found */ +byte readSidHeader(word address, SidHeader1* header); + +word moveSIDCodeToPlace(SidHeader1* header1, word dataLen); + +void _strcpy(byte* to, word from, word len); + +#endif diff --git a/firmware/sidfile.s b/firmware/sidfile.s new file mode 100644 index 0000000..6b9b0fd --- /dev/null +++ b/firmware/sidfile.s @@ -0,0 +1,564 @@ +; +; File generated by cc65 v 2.13.3 +; + .fopt compiler,"cc65 v 2.13.3" + .setcpu "6502" + .smart on + .autoimport on + .case on + .debuginfo off + .importzp sp, sreg, regsave, regbank, tmp1, ptr1, ptr2 + .macpack longbranch + .import _initSIDTune + .import _prints + .import _printw + .import _fixSIDTune + .export _playSidFile + .export _readSidHeader + .export _moveSIDCodeToPlace + .export __strcpy + .export __memcpy + .export __readBigEndianW + +.segment "RODATA" + +L0001: + .byte $0D,$0D,$56,$49,$43,$49,$4F,$55,$53,$20,$53,$49,$44,$20,$50,$4C + .byte $41,$59,$45,$52,$0D,$00,$0D,$54,$49,$54,$4C,$45,$3A,$20,$20,$20 + .byte $00,$0D,$41,$55,$54,$48,$4F,$52,$3A,$20,$20,$00,$0D,$52,$45,$4C + .byte $45,$41,$53,$45,$3A,$20,$00,$0D,$53,$4F,$4E,$47,$53,$3A,$20,$24 + .byte $00,$20,$44,$45,$46,$41,$55,$4C,$54,$3A,$20,$24,$00,$0D,$55,$53 + .byte $49,$4E,$47,$20,$46,$49,$4C,$45,$20,$53,$49,$5A,$45,$3A,$20,$24 + .byte $00,$0D,$49,$4E,$49,$54,$20,$41,$44,$44,$52,$45,$53,$53,$3A,$20 + .byte $20,$20,$20,$24,$00,$0D,$50,$4C,$41,$59,$20,$41,$44,$44,$52,$45 + .byte $53,$53,$3A,$20,$20,$20,$20,$24,$00,$0D,$4C,$4F,$41,$44,$20,$41 + .byte $44,$44,$52,$45,$53,$53,$3A,$20,$20,$20,$20,$24,$00,$0D,$00,$53 + .byte $54,$41,$52,$54,$20,$50,$4C,$41,$59,$49,$4E,$47,$2E,$2E,$2E,$0D + .byte $00,$0D,$53,$49,$44,$20,$54,$55,$4E,$45,$20,$4E,$4F,$54,$20,$46 + .byte $4F,$55,$4E,$44,$20,$49,$4E,$20,$41,$44,$44,$52,$45,$53,$53,$20 + .byte $24,$00,$0D,$00 + +; --------------------------------------------------------------- +; void __near__ playSidFile (unsigned int, unsigned int) +; --------------------------------------------------------------- + +.segment "CODE" + +.proc _playSidFile: near + +.segment "CODE" + + ldy #$7A + jsr subysp + lda #<(L0001) + ldx #>(L0001) + jsr pushax + jsr _prints + ldy #$7F + jsr pushwysp + lda #$04 + jsr leaa0sp + jsr pushax + jsr _readSidHeader + tax + jeq L0078 + lda #<(L0001+22) + ldx #>(L0001+22) + jsr pushax + jsr _prints + lda #$1A + jsr leaa0sp + jsr pushax + jsr _prints + lda #<(L0001+33) + ldx #>(L0001+33) + jsr pushax + jsr _prints + lda #$3A + jsr leaa0sp + jsr pushax + jsr _prints + lda #<(L0001+44) + ldx #>(L0001+44) + jsr pushax + jsr _prints + lda #$5A + jsr leaa0sp + jsr pushax + jsr _prints + lda #<(L0001+55) + ldx #>(L0001+55) + jsr pushax + jsr _prints + ldy #$15 + jsr pushwysp + jsr _printw + lda #<(L0001+65) + ldx #>(L0001+65) + jsr pushax + jsr _prints + ldy #$17 + jsr pushwysp + jsr _printw + lda #<(L0001+77) + ldx #>(L0001+77) + jsr pushax + jsr _prints + ldy #$7D + jsr pushwysp + jsr _printw + lda #<(L0001+97) + ldx #>(L0001+97) + jsr pushax + jsr _prints + ldy #$11 + jsr pushwysp + jsr _printw + lda #<(L0001+117) + ldx #>(L0001+117) + jsr pushax + jsr _prints + ldy #$13 + jsr pushwysp + jsr _printw + lda #$02 + jsr leaa0sp + jsr pushax + ldy #$7F + jsr pushwysp + jsr _moveSIDCodeToPlace + jsr stax0sp + lda #<(L0001+137) + ldx #>(L0001+137) + jsr pushax + jsr _prints + jsr pushw0sp + jsr _printw + lda #<(L0001+157) + ldx #>(L0001+157) + jsr pushax + jsr _prints + jsr pushw0sp + ldy #$7F + jsr pushwysp + jsr _fixSIDTune + ldy #$11 + jsr pushwysp + ldy #$13 + jsr ldaxysp + jsr _initSIDTune + ldx #$4E + lda #$20 + sta $BF84 + stx $BF84+1 + lda #$81 + sta $BF8D + lda #<(L0001+159) + ldx #>(L0001+159) + jsr pushax + jsr _prints + lda #$11 + sta $BF8E + jmp L00BD +L0078: lda #<(L0001+177) + ldx #>(L0001+177) + jsr pushax + jsr _prints + ldy #$7F + jsr pushwysp + jsr _printw + lda #<(L0001+210) + ldx #>(L0001+210) + jsr pushax + jsr _prints +L00BD: ldy #$7E + jmp addysp + +.endproc + +; --------------------------------------------------------------- +; unsigned char __near__ readSidHeader (unsigned int, __near__ struct SidHeader1*) +; --------------------------------------------------------------- + +.segment "CODE" + +.proc _readSidHeader: near + +.segment "CODE" + + jsr ldax0sp + sta ptr1 + stx ptr1+1 + ldy #$03 + jsr ldaxysp + ldy #$00 + sta (ptr1),y + iny + txa + sta (ptr1),y + jsr ldax0sp + jsr incax2 + jsr pushax + ldy #$07 + jsr pushwysp + lda #$04 + jsr pusha0 + jsr __memcpy + jsr ldax0sp + sta ptr1 + stx ptr1+1 + ldy #$02 + lda (ptr1),y + cmp #$50 + bne L0035 + jsr ldax0sp + sta ptr1 + stx ptr1+1 + ldy #$03 + lda (ptr1),y + cmp #$53 + bne L0035 + jsr ldax0sp + sta ptr1 + stx ptr1+1 + ldy #$04 + lda (ptr1),y + cmp #$49 + bne L0035 + jsr ldax0sp + sta ptr1 + stx ptr1+1 + ldy #$05 + lda (ptr1),y + cmp #$44 + beq L0034 +L0035: ldx #$00 + txa + jmp incsp4 +L0034: jsr pushw0sp + ldy #$05 + jsr ldaxysp + jsr incax4 + jsr pushax + jsr __readBigEndianW + ldy #$06 + jsr staxspidx + jsr pushw0sp + ldy #$05 + jsr ldaxysp + jsr incax6 + jsr pushax + jsr __readBigEndianW + ldy #$08 + jsr staxspidx + jsr pushw0sp + ldy #$05 + jsr ldaxysp + jsr incax8 + jsr pushax + jsr __readBigEndianW + ldy #$0A + jsr staxspidx + jsr pushw0sp + ldy #$05 + jsr ldaxysp + ldy #$0A + jsr incaxy + jsr pushax + jsr __readBigEndianW + ldy #$0C + jsr staxspidx + jsr pushw0sp + ldy #$05 + jsr ldaxysp + ldy #$0C + jsr incaxy + jsr pushax + jsr __readBigEndianW + ldy #$0E + jsr staxspidx + jsr pushw0sp + ldy #$05 + jsr ldaxysp + ldy #$0E + jsr incaxy + jsr pushax + jsr __readBigEndianW + ldy #$10 + jsr staxspidx + jsr pushw0sp + ldy #$05 + jsr ldaxysp + ldy #$10 + jsr incaxy + jsr pushax + jsr __readBigEndianW + ldy #$12 + jsr staxspidx + jsr ldax0sp + ldy #$14 + jsr incaxy + jsr pushax + ldy #$05 + jsr ldaxysp + ldy #$12 + jsr incaxy + jsr pushax + lda #$04 + jsr pusha0 + jsr __memcpy + jsr ldax0sp + ldy #$18 + jsr incaxy + jsr pushax + ldy #$05 + jsr ldaxysp + ldy #$16 + jsr incaxy + jsr pushax + lda #$20 + jsr pusha0 + jsr __strcpy + jsr ldax0sp + ldy #$38 + jsr incaxy + jsr pushax + ldy #$05 + jsr ldaxysp + ldy #$36 + jsr incaxy + jsr pushax + lda #$20 + jsr pusha0 + jsr __strcpy + jsr ldax0sp + ldy #$58 + jsr incaxy + jsr pushax + ldy #$05 + jsr ldaxysp + ldy #$56 + jsr incaxy + jsr pushax + lda #$20 + jsr pusha0 + jsr __strcpy + ldx #$00 + lda #$01 + jmp incsp4 + +.endproc + +; --------------------------------------------------------------- +; unsigned int __near__ moveSIDCodeToPlace (__near__ struct SidHeader1*, unsigned int) +; --------------------------------------------------------------- + +.segment "CODE" + +.proc _moveSIDCodeToPlace: near + +.segment "CODE" + + jsr decsp2 + ldy #$05 + jsr ldaxysp + jsr ldaxi + sta sreg + stx sreg+1 + ldy #$05 + jsr ldaxysp + ldy #$09 + jsr ldaxidx + clc + adc sreg + pha + txa + adc sreg+1 + tax + pla + jsr pushax + ldy #$07 + jsr ldaxysp + ldy #$0B + jsr ldaxidx + cpx #$00 + bne L00C4 + cmp #$00 + beq L0064 +L00C4: ldy #$07 + jsr ldaxysp + ldy #$0B + jsr ldaxidx + ldy #$02 + jsr staxysp + jmp L0069 +L0064: jsr ldax0sp + jsr ldaxi + ldy #$02 + jsr staxysp + ldx #$00 + lda #$02 + jsr addeq0sp +L0069: ldy #$05 + jsr pushwysp + ldy #$05 + jsr pushwysp + ldy #$0B + jsr pushwysp + jsr __memcpy + ldy #$03 + jsr ldaxysp + jmp incsp8 + +.endproc + +; --------------------------------------------------------------- +; void __near__ _strcpy (__near__ unsigned char*, unsigned int, unsigned int) +; --------------------------------------------------------------- + +.segment "CODE" + +.proc __strcpy: near + +.segment "CODE" + + jsr push0 +L0014: jsr ldax0sp + ldy #$02 + cmp (sp),y + txa + iny + sbc (sp),y + bcs L0015 + jsr ldax0sp + clc + ldy #$04 + adc (sp),y + sta ptr1 + txa + iny + adc (sp),y + sta ptr1+1 + ldy #$00 + lda (ptr1),y + jsr pusha + ldy #$00 + lda (sp),y + cmp #$61 + bcc L001A + lda (sp),y + sec + sbc #$20 + sta (sp),y +L001A: ldy #$02 + jsr ldaxysp + clc + ldy #$07 + adc (sp),y + sta ptr1 + txa + iny + adc (sp),y + sta ptr1+1 + ldy #$00 + lda (sp),y + sta (ptr1),y + ldy #$02 + jsr ldaxysp + jsr incax1 + ldy #$01 + jsr staxysp + jsr incsp1 + jmp L0014 +L0015: ldy #$09 + jsr pushwysp + ldy #$05 + jsr ldaxysp + jsr decax1 + jsr tosaddax + sta ptr1 + stx ptr1+1 + lda #$00 + tay + sta (ptr1),y + jmp incsp8 + +.endproc + +; --------------------------------------------------------------- +; void __near__ _memcpy (__near__ unsigned char*, unsigned int, unsigned int) +; --------------------------------------------------------------- + +.segment "CODE" + +.proc __memcpy: near + +.segment "CODE" + + jsr push0 +L0009: jsr ldax0sp + ldy #$02 + cmp (sp),y + txa + iny + sbc (sp),y + jcs incsp8 + jsr ldax0sp + clc + ldy #$06 + adc (sp),y + sta sreg + txa + iny + adc (sp),y + sta sreg+1 + jsr ldax0sp + clc + ldy #$04 + adc (sp),y + sta ptr1 + txa + iny + adc (sp),y + sta ptr1+1 + ldy #$00 + lda (ptr1),y + sta (sreg),y + jsr ldax0sp + jsr incax1 + jsr stax0sp + jmp L0009 + +.endproc + +; --------------------------------------------------------------- +; unsigned int __near__ _readBigEndianW (unsigned int) +; --------------------------------------------------------------- + +.segment "CODE" + +.proc __readBigEndianW: near + +.segment "CODE" + + jsr ldax0sp + sta ptr1 + stx ptr1+1 + ldy #$00 + lda (ptr1),y + tax + tya + jsr pushax + jsr pushw0sp + ldy #$05 + jsr ldaxysp + jsr incax1 + sta ptr1 + stx ptr1+1 + ldx #$00 + lda (ptr1,x) + jsr tosadda0 + jmp incsp4 + +.endproc + diff --git a/firmware/vicilib.h b/firmware/vicilib.h new file mode 100644 index 0000000..9536f45 --- /dev/null +++ b/firmware/vicilib.h @@ -0,0 +1,75 @@ + +#ifndef VICILIB_H_ +#define VICILIB_H_ + +typedef unsigned char byte; +typedef unsigned word; + +#define POKE(addr,val) (*(unsigned char*) (addr) = (val)) +#define POKEW(addr,val) (*(unsigned*) (addr) = (val)) +#define PEEK(addr) (*(unsigned char*) (addr)) +#define PEEKW(addr) (*(unsigned*) (addr)) + +// SID is visible in address BF00 +#define SID 0xbf00 + + +// CIA is visible in address BF80 +#define CIA_PORT_A 0xbf80 +#define CIA_PORT_B 0xbf81 +#define CIA_PORT_A_DATA_DIR 0xbf82 +#define CIA_PORT_B_DATA_DIR 0xbf83 +#define CIA_TIMER_A_VALUE 0xbf84 // word +#define CIA_TIMER_B_VALUE 0xbf86 // word +#define CIA_TOD_DS 0xbf88 +#define CIA_TOD_S 0xbf89 +#define CIA_TOD_MIN 0xbf8a +#define CIA_TOD_HOUR 0xbf8b +#define CIA_SERIAL_SHIFT_REG 0xbf8c +#define CIA_IRQ_CTRL 0xbf8d +#define CIA_TIMER_A_CTRL 0xbf8e +#define CIA_TIMER_B_CTRL 0xbf8f + + +// Only relevant bits defined here + +#define BIT_CIA_TIMER_START 0x1 +#define BIT_CIA_TIMER_UNDERFLOW_STOP 0x2 +#define BIT_CIA_TIMER_LOAD_VALUE 0x10 +#define BIT_CIA_TIMER_B_COUNT_UNDERFLOWS_OF_TIMER_A 0x20 + +#define BIT_CIA_IRQ_CTRL_TIMER_A_UNDERFLOW 0x1 +#define BIT_CIA_IRQ_CTRL_TIMER_B_UNDERFLOW 0x2 +#define BIT_CIA_IRQ_CTRL_FILL_BIT 0x80 // set bits are set to this value + +// These are implemented in viciliba.s + +extern void initVicilib(); + +extern void __fastcall__ putchar(byte c); + +extern void __fastcall__ printb(byte c); +extern void irq; + +extern void __fastcall__ initSIDTune(word initAddress, word playAddress); + + +//extern unsigned char __fastcall__ copyshort(unsigned char len, void *src, void *dst); + + +// Implemented in vicilibc.c + +void prints(byte* string); + +void printsl(byte* string, byte len); + +void printw(word w); + +//void setIRQHandlerAddress(word address); + +void fixSIDTune(word address, word len); + +void stopPlaying(); + +#endif + diff --git a/firmware/viciliba.s b/firmware/viciliba.s new file mode 100644 index 0000000..0e852e3 --- /dev/null +++ b/firmware/viciliba.s @@ -0,0 +1,91 @@ + +; https://groups.google.com/forum/#!topic/comp.sys.apple2/usZKHLQi-Ak + + ;.smart on + .autoimport on + .import __STARTUP_LOAD__ + .export _putchar + .export _printb + ;.export _irq + .export _initSIDTune + .export _initVicilib + + .segment "VECTORS" + + jmp __STARTUP_LOAD__ ; B000 : jump to Vicious menu + ;jmp + + + + .code +_putchar: + jsr $ffef ; wozmon ECHO + rts + +_printb: + jsr $ffdc ; wozmon PRBYTE + rts + + +_initVicilib: + jsr zerobss + jsr copydata ; copies data segment from ROM to RAM + rts + + +; void __fastcall__ initSIDTune(word initAddress, word playAddress); +_initSIDTune: + ;sta playAddr+1 + ;stx playAddr+2 + sta $7f01 + stx $7f02 + jsr popax + ;sta initAddr+1 + ;stx initAddr+2 + sta $7f04 + stx $7f05 + lda #$4c ;jmp + sta $7f00 + sta $7f03 + lda #$0 ; song select here? + tax + tay +initAddr: + ;jsr $0000 ; call sid tune init address, modified code + jsr $7f03 + + ; setup jump from irq address 0000 to _irq handler here + lda #$4c ; jmp + sta $0000 + lda #<_irq + sta $0001 + lda #>_irq + sta $0002 + rts + + + +; SID Tune play irq which calls tune's play routine +_irq: + pha + txa + pha + tya + pha +playAddr: + ;jsr $0000 ; call sid tune play address, modified code + jsr $7f00 + + lda $bf8d ; clear CIA irq condition + + pla + tay + pla + tax + pla + rti + + + + + diff --git a/firmware/vicilibc.c b/firmware/vicilibc.c new file mode 100644 index 0000000..15e8b13 --- /dev/null +++ b/firmware/vicilibc.c @@ -0,0 +1,84 @@ + +#include "vicilib.h" + +#define IRQ_VECTOR 0x0000 // This is where Apple-1 IRQ vector points to +//#define JMP 0x4C + +const byte storeOpCodes[] = { + 0x8d, + 0x9d, + 0x99, + 0x81, + 0x91, + 0x8e, + 0x8c +}; + +void prints(unsigned char* string) { + byte i = 0; + byte c; + while((c = string[i++])!='\0') { + putchar(c); + } +} + +void printsl(byte* string, byte len) { + byte i=0; + while(i>8)); + printb(lsb); +} + +/* +void setIRQHandlerAddress(word address) { + POKE(IRQ_VECTOR, JMP); + POKEW(IRQ_VECTOR+1, address); +} +*/ + +byte _isStoreOpCode(byte b) { + byte i=0; + while(i<7) + if(storeOpCodes[i++]==b) return 1; + return 0; +} + +void fixSIDTune(word address, word len) { + word i=0, count=0; + prints("FIXING SID TUNE AT $"); + printw(address); + prints("...\r"); + while(i