mirror of
https://github.com/sampopeltonen/Vicious.git
synced 2024-12-26 21:31:36 +00:00
initial commit to github
This commit is contained in:
parent
6f4deed143
commit
2451ed1065
259
firmware/CFFA1_API.s
Normal file
259
firmware/CFFA1_API.s
Normal 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
51
firmware/build_rom.sh
Executable 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
90
firmware/cffa1.c
Normal 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
14
firmware/cffa1.h
Normal 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
16
firmware/cffa1a.s
Normal 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
37
firmware/ld_rom.cfg
Normal 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
36
firmware/ld_romburner.cfg
Normal 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
46
firmware/main.c
Normal 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
62
firmware/romburner.c
Normal 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
18
firmware/romburnerio.s
Normal 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
113
firmware/sidfile.c
Normal 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
37
firmware/sidfile.h
Normal 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
564
firmware/sidfile.s
Normal 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
75
firmware/vicilib.h
Normal 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
91
firmware/viciliba.s
Normal 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
84
firmware/vicilibc.c
Normal 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++;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user