initial commit to github

This commit is contained in:
Sampo Peltonen 2014-04-21 14:56:28 +03:00
parent 6f4deed143
commit 2451ed1065
16 changed files with 1593 additions and 0 deletions

259
firmware/CFFA1_API.s Normal file
View File

@ -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
;------------------------------------------------------------------------------

51
firmware/build_rom.sh Executable file
View File

@ -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

90
firmware/cffa1.c Normal file
View File

@ -0,0 +1,90 @@
#include <apple1.h>
#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<FILE_LIST_LEN) {
ret = callCFFA1API(CFFA1_READDIR);
if(ret!=0) {
cont=0;
if(ret!=CFFA1_E_FILENOTFOUND)
printb(ret);
prints("\r\rQUIT");
break;
}
_strcpy(&fileNames[i][1], PEEKW(CFFA1_ENTRYPTR)+1, 16);
prints("\r");
putchar(i+0x41);
prints(") ");
prints(&fileNames[i][1]);
//prints(" ");
//printw(PEEKW(PEEKW(CFFA1_ENTRYPTR)+PRODOS_FILESIZE_OFFSET));
POKEW((word)&fileNames[i++][17], PEEKW(PEEKW(CFFA1_ENTRYPTR)+PRODOS_FILESIZE_OFFSET));
}
if(i==FILE_LIST_LEN) prints("\r\rCONTINUE");
prints(" OR SELECT? ");
keybInput = readkey() - 0x80;
prints("\r");
if(keybInput=='Q') break;
if(keybInput>='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;
}

14
firmware/cffa1.h Normal file
View File

@ -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

16
firmware/cffa1a.s Normal file
View File

@ -0,0 +1,16 @@
.export _gotoCFFA1
.export _callCFFA1API
.include "CFFA1_API.s"
_gotoCFFA1:
jsr $9006
rts
_callCFFA1API:
tax
jsr CFFA1_API
rts

37
firmware/ld_rom.cfg Normal file
View File

@ -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;
}

36
firmware/ld_romburner.cfg Normal file
View File

@ -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;
}

46
firmware/main.c Normal file
View File

@ -0,0 +1,46 @@
#include <apple1.h>
#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;
}

62
firmware/romburner.c Normal file
View File

@ -0,0 +1,62 @@
#include <apple1.h>
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(i<ROM_IMAGE_SIZE) {
POKE(PROM+i, PEEK(imageStartAddress+i));
putchar('*');
i++;
}
prints("\rBURN FINISHED\r");
return 1;
}

18
firmware/romburnerio.s Normal file
View File

@ -0,0 +1,18 @@
.export _putchar
.export _printb
.code
_putchar:
jsr $ffef ; wozmon ECHO
rts
_printb:
jsr $ffdc ; wozmon PRBYTE
rts
;.segment "END"
;.byte "ROM IMAGE START"

113
firmware/sidfile.c Normal file
View File

@ -0,0 +1,113 @@
#include "sidfile.h"
void _memcpy(byte* to, word from, word len) {
word i=0;
while(i<len) {
to[i] = PEEK(from+i);
i++;
}
}
void _strcpy(byte* to, word from, word len) {
word i=0;
while(i<len) {
byte c = PEEK(from+i);
if(c>96) 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) ;
}

37
firmware/sidfile.h Normal file
View File

@ -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

564
firmware/sidfile.s Normal file
View File

@ -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

75
firmware/vicilib.h Normal file
View File

@ -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

91
firmware/viciliba.s Normal file
View File

@ -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

84
firmware/vicilibc.c Normal file
View File

@ -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<len) {
putchar(string[i++]);
}
}
void printw(word w) {
byte lsb = (byte) w;
printb((byte)(w>>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<len) {
if(PEEK(address+i+2)==0xd4 &&
_isStoreOpCode(PEEK(address+i)) &&
(PEEK(address+i+1)<0x1d))
{
POKE(address+i+2, 0xbf);
//prints(" FIXED SID ADDRESS AT ");
//printw(address+i+1);
//prints("\r");
++count;
}
++i;
}
prints("DONE: FIXED $");
printw(count);
prints(" SID ADDRESSES.\r");
}
void stopPlaying() {
int i=0;
POKE(CIA_TIMER_A_CTRL, 0);
while(i<=24) {
POKE(SID+i, 0);
i++;
}
}