initial commit to github
This commit is contained in:
parent
6f4deed143
commit
2451ed1065
|
@ -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
|
||||||
|
;------------------------------------------------------------------------------
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
|
@ -0,0 +1,16 @@
|
||||||
|
|
||||||
|
.export _gotoCFFA1
|
||||||
|
.export _callCFFA1API
|
||||||
|
|
||||||
|
|
||||||
|
.include "CFFA1_API.s"
|
||||||
|
|
||||||
|
|
||||||
|
_gotoCFFA1:
|
||||||
|
jsr $9006
|
||||||
|
rts
|
||||||
|
|
||||||
|
_callCFFA1API:
|
||||||
|
tax
|
||||||
|
jsr CFFA1_API
|
||||||
|
rts
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
|
@ -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"
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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) ;
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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