renamed 'src' to 'tools'

This commit is contained in:
FeralChild64 committed 2020-02-05 17:31:33 +01:00
1 parent 2305856d63
commit d5f6f1924e
12 files changed
+6 -6

No files matched your search

-1150
View File
File diff suppressed because it is too large. Load diff
-262
View File
@@ -1,262 +0,0 @@
/*
tcpclient.c
Copyright 2019, Paul Gardner-Stephen
Copyright 2012, Isaakidis Marios, Daniel Aguado
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <fcntl.h>
#include <getopt.h>
//file found = 1
//file not found = 0
//write and overwrite =1
//apend =0
//tcpclient IP port
//argc1, argv0: tcpclient.exe
//argc2, argv1: IP address TCP Server
//argv3, argv2: TCP Server port
int set_nonblock(int fd)
{
int retVal=0;
do {
if (fd==-1) break;
int flags;
if ((flags = fcntl(fd, F_GETFL, NULL)) == -1)
{
perror("fcntl");
// LOG_ERROR("set_nonblock: fcntl(%d,F_GETFL,NULL)",fd);
retVal=-1;
break;
}
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK | O_NDELAY) == -1)
{
perror("fcntl");
// LOG_ERROR("set_nonblock: fcntl(%d,F_SETFL,n|O_NONBLOCK)",fd);
return -1;
}
} while (0);
return retVal;
}
#define MAX_KEYS 1024
int key_times[MAX_KEYS];
char key_sequences[MAX_KEYS][1024];
int key_count=0;
int main (int argc, char *argv[]) {
int collect_cycles=5000000;
// KERNAL part is actually just under 7KB
int rom_bottom=0xa000;
int rom_top=0xffff;
char *filename=NULL;
int opt;
while ((opt = getopt(argc, argv, "f:b:t:c:k:v:")) != -1) {
switch (opt) {
case 'b':
rom_bottom=strtoll(optarg,NULL,16);
break;
case 't':
rom_top=strtoll(optarg,NULL,16);
break;
case 'f':
filename=optarg;
break;
case 'c':
collect_cycles=atoi(optarg);
break;
case 'v':
system(optarg);
sleep(3);
break;
case 'k':
// Scheduled key events
if (key_count>=MAX_KEYS) {
fprintf(stderr,"Too many -k arguments. Increase MAX_KEYS?\n");
exit(-1);
}
if (sscanf(optarg,"%d:%[^~]",&key_times[key_count],key_sequences[key_count])<2) {
fprintf(stderr,"Malformed argument to -k. Must be <cycle number>:<VICE compatible key sequence>\n"
" RETURN should be written \\x0d\n"
"\n");
exit(-1);
}
key_count++;
break;
default: /* '?' */
fprintf(stderr, "Usage: %s [-v <vice command line>] [-b <ROM bottom hex>] [-t <ROM top hex>] [-f <file to load and run>] [-c <cycles to collect data>] [[-k <time>:<key sequence> ] ...]\n",
argv[0]);
exit(-1);
}
}
int sockfd, rc, serverPort, nBytes;
struct sockaddr_in localAddr, servAddr;
struct hostent *h;
/* Check parameters from command line */
serverPort = 6510;
if(serverPort<=0 || serverPort>65535)//check number of TCP server port
{
fprintf(stderr, "The port number given is wrong.\n");
return -1;
}
h = gethostbyname("localhost");
if(h==NULL)//check assigment of TCP server host
{
perror("Unknown host ");
return -1;
}
/* Create TCP socket */
servAddr.sin_family = h->h_addrtype;
memcpy((char *) &servAddr.sin_addr.s_addr, h->h_addr_list[0], h->h_length);
servAddr.sin_port = htons(serverPort);
sockfd = socket(AF_INET, SOCK_STREAM, 0);
if(sockfd < 0)//check TCP socket is created correctly
{
perror("Cannot open socket ");
return -1;
}
/* Bind any port number */
localAddr.sin_family = AF_INET;
localAddr.sin_addr.s_addr = htonl(INADDR_ANY);
localAddr.sin_port = htons(0);
rc = bind(sockfd, (struct sockaddr *) &localAddr, sizeof(localAddr));
if(rc < 0)//check TCP socket is bind correctly
{
perror("Cannot bind on TCP port ");
close(sockfd);
return -1;
}
/* Connect to TCP server */
rc = connect(sockfd, (struct sockaddr *) &servAddr, sizeof(servAddr));
if(rc < 0)//check TCP socket is connected correctly
{
perror("Cannot connect ");
close(sockfd);
return -1;
}
printf("Starting TCP client...\n");
set_nonblock(sockfd);
write(sockfd,"reset 0\r",strlen("reset 0\n"));
int current_key=0;
int last_pc=0, last_sp=0xff;
int max_cycles=0;
unsigned char buffer[65536];
while(1) {
nBytes = read(sockfd, buffer, sizeof(buffer));
if (nBytes >= 1) {
write(sockfd,"step\rstep\rstep\rstep\r",5*1);
// fprintf(stdout,"%d: %s\n",nBytes,buffer);
int pc,sp,cycles;
int nf=sscanf((char *)buffer,".C:%x%*[^:]:%*x X:%*x Y:%*x SP:%x %*[^ ] %d",&pc,&sp,&cycles);
if (nf==3) {
// fprintf(stdout,"%x %x %d\n",pc,sp,cycles);
// Detect entry into ROM via code
if (pc<0xc000||pc>=0xe000)
if (pc>=rom_bottom&&pc<=rom_top) {
if (last_pc<rom_bottom||last_pc>rom_top) {
// PC has just entered the ROM
fprintf(stdout,"$%04x called from $%04x @ cycle %d\n",pc,last_pc,cycles);
}
}
// Detect entry into ROM via interrupt
if (pc>=rom_bottom&&pc<=rom_top)
if (sp==(last_sp-3)) {
// PC has just entered the ROM
fprintf(stdout,"$%04x called from via interrupt @ cycle %d\n",pc,cycles);
}
last_pc=pc; last_sp=sp;
}
// LOAD only after BASIC has started
if (last_pc==0xe5cd&&filename) {
/*
To load and a program in VICE via the monitor interface:
bload "filename" 0 07ff
> 07ff 0 0
keybuf run\x0d
*/
char cmd[1024];
snprintf(cmd,1024,"bload \"%s\" 0 07ff\r",filename);
write(sockfd,cmd,strlen(cmd));
usleep(1000000);
write(sockfd,"> 07ff 0 0\r",11);
usleep(100000);
write(sockfd,"keybuf run\\x0d\r",15);
filename=NULL;
}
if (current_key<key_count&&cycles>key_times[current_key]) {
char cmd[1024];
snprintf(cmd,1024,"keybuf %s\r",key_sequences[current_key]);
write(sockfd,cmd,strlen(cmd));
fprintf(stderr,">>> Keyboard injection: %s\n\r",cmd);
current_key++;
}
if (cycles>(max_cycles+10000)) {
max_cycles=cycles;
// fprintf(stderr,"%d cycles\n",cycles);
}
// Terminate emulator when done
if (cycles>=collect_cycles) {
write(sockfd,"\rquit\r",6);
usleep(1000000);
break;
}
} else usleep(1);
}
close(sockfd);
return 0;
}
-58
View File
@@ -1,58 +0,0 @@
//
// Common parts of utilities written in C++
// for providing uniform user experience
//
#include <iostream>
#include <string>
#if defined(WIN32) || defined(_WIN32)
#define DIR_SEPARATOR "\\"
#else
#define DIR_SEPARATOR "/"
#endif
const std::string BANNER_LINE = "//-------------------------------------------------------------------------------------------";
void ERROR()
{
exit(-1);
}
void ERROR(const std::string &message)
{
std::cout << "\n" << "ERROR: " << message << "\n\n";
exit(-1);
}
void printBannerLineTop()
{
std::cout << "\n\n\n" << BANNER_LINE << "\n";
}
void printBannerLineBottom()
{
std::cout << BANNER_LINE << "\n\n";
}
class DualStream
{
public:
DualStream(std::ostream& str1, std::ostream& str2) : str1(str1), str2(str2) {}
template<class T> DualStream &operator<<(const T& x)
{
str1 << x;
str2 << x;
return *this;
}
private:
std::ostream& str1;
std::ostream& str2;
};
-492
View File
@@ -1,492 +0,0 @@
#include <stdio.h>
#include <strings.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <math.h>
// https://www.c64-wiki.com/wiki/BASIC_token
/*
The URLs here provide examples of other BASIC dialects that include the specified commands
or keywords.
All except VERIFY are known with certainty to be part of other BASIC dialects, and thus
cannot be subject to copyright infringement when implemented on a C64.
The VERIFY Command however appears in other programming languages, and as a single word,
is the obvious complement to LOAD and SAVE when considering the following storage functions:
1. Load a file from storage into memory.
2. Save a file from memory to storage.
3. Verify that a file has been correctly written from memory to storage.
Note that verify is the key verb in the behaviour of this function. Therefore it is the
obvious choice for the name for such a function, taking into account the convention of
LOAD and SAVE. Indeed, that this is so is highlighted by the fact that it was used in the
C64 ROM. Therefore there is no exercise of creativity here, and a reasonable person would
likely select this name independently. Also, single words cannot be copyright.
Further, the list is a list of facts, and thus not copyrightable.
We sourced the list from a public source, https://www.c64-wiki.com/wiki/BASIC, which has
been public on the internet for a long time, as have many other similar sources.
That website offers the page under the GFDL license, and we can therefore take the list
under those license terms if we wish, should the need arise.
Note that in providing these justifications above, we do not in any way acknowledge that
without these conditions that the BASIC keyword list could be copyright, but provide this
information as a further layer of defence against any claims that it could somehow
infringe on the copyrights of the C64/C65/C128 etc ROMs.
*/
char *keyword_list[]={
"END", // https://www.landsnail.com/a2ref.htm
"FOR", // https://www.landsnail.com/a2ref.htm
"NEXT", // https://www.landsnail.com/a2ref.htm
"DATA", // https://www.landsnail.com/a2ref.htm
"INPUT#", // https://www.atariarchives.org/creativeatari/Using_Disks_With_Atari_Basic.php
"INPUT", // https://www.landsnail.com/a2ref.htm
"DIM", // https://www.landsnail.com/a2ref.htm
"READ", // https://www.landsnail.com/a2ref.htm
"LET", // https://en.wikipedia.org/wiki/Atari_BASIC
"GOTO", // https://www.landsnail.com/a2ref.htm
"RUN", // https://www.landsnail.com/a2ref.htm
"IF", // https://www.landsnail.com/a2ref.htm
"RESTORE", // https://www.landsnail.com/a2ref.htm
"GOSUB", // https://www.landsnail.com/a2ref.htm
"RETURN", // https://www.landsnail.com/a2ref.htm
"REM", // https://www.landsnail.com/a2ref.htm
"STOP", // https://www.landsnail.com/a2ref.htm
"ON", // https://www.landsnail.com/a2ref.htm
"WAIT", // http://www.picaxe.com/BASIC-Commands/Time-Delays/wait/
"LOAD", // https://www.landsnail.com/a2ref.htm
"SAVE", // https://www.landsnail.com/a2ref.htm
"VERIFY", // https://golang.org/cmd/go/
"DEF", // https://www.landsnail.com/a2ref.htm
"POKE", // https://www.landsnail.com/a2ref.htm
"PRINT#", // https://www.atariarchives.org/creativeatari/Using_Disks_With_Atari_Basic.php
"PRINT", // https://www.landsnail.com/a2ref.htm
"CONT", // https://www.landsnail.com/a2ref.htm
"LIST", // https://www.landsnail.com/a2ref.htm
"CLR", // Apple I Replica Creation: Back to the Garage, p125
"CMD", // https://en.wikipedia.org/wiki/List_of_DOS_commands
"SYS", // https://www.lifewire.com/dos-commands-4070427
"OPEN", // https://www.atariarchives.org/creativeatari/Using_Disks_With_Atari_Basic.php
"CLOSE", // https://www.atariarchives.org/creativeatari/Using_Disks_With_Atari_Basic.php
"GET", // https://www.landsnail.com/a2ref.htm
"NEW", // https://www.landsnail.com/a2ref.htm
"TAB(", // http://www.antonis.de/qbebooks/gwbasman/tab.html
"TO", // https://www.landsnail.com/a2ref.htm
"FN", // https://www.landsnail.com/a2ref.htm
"SPC(", // http://www.antonis.de/qbebooks/gwbasman/spc.html
"THEN", // https://www.landsnail.com/a2ref.htm
"NOT", // https://www.landsnail.com/a2ref.htm
"STEP", // https://www.landsnail.com/a2ref.htm
"+", // https://www.landsnail.com/a2ref.htm
"-", // https://www.landsnail.com/a2ref.htm
"*", // https://www.landsnail.com/a2ref.htm
"/", // https://www.landsnail.com/a2ref.htm
"^", // https://www.landsnail.com/a2ref.htm
"AND", // https://www.landsnail.com/a2ref.htm
"OR", // https://www.landsnail.com/a2ref.htm
">", // https://www.landsnail.com/a2ref.htm
"=", // https://www.landsnail.com/a2ref.htm
"<", // https://www.landsnail.com/a2ref.htm
"SGN", // https://www.landsnail.com/a2ref.htm
"INT", // https://www.landsnail.com/a2ref.htm
"ABS", // https://www.landsnail.com/a2ref.htm
"USR", // https://www.landsnail.com/a2ref.htm
"FRE", // http://www.antonis.de/qbebooks/gwbasman/fre.html
"POS", // http://www.antonis.de/qbebooks/gwbasman/pos.html
"SQR", // https://www.landsnail.com/a2ref.htm
"RND", // https://www.landsnail.com/a2ref.htm
"LOG", // https://www.landsnail.com/a2ref.htm
"EXP", // https://www.landsnail.com/a2ref.htm
"COS", // https://www.landsnail.com/a2ref.htm
"SIN", // https://www.landsnail.com/a2ref.htm
"TAN", // https://www.landsnail.com/a2ref.htm
"ATN", // https://www.landsnail.com/a2ref.htm
"PEEK", // https://www.landsnail.com/a2ref.htm
"LEN", // https://www.landsnail.com/a2ref.htm
"STR$", // https://www.landsnail.com/a2ref.htm
"VAL", // https://www.landsnail.com/a2ref.htm
"ASC", // https://www.landsnail.com/a2ref.htm
"CHR$", // https://www.landsnail.com/a2ref.htm
"LEFT$", // https://www.landsnail.com/a2ref.htm
"RIGHT$", // https://www.landsnail.com/a2ref.htm
"MID$", // https://www.landsnail.com/a2ref.htm
"GO", // https://en.wikipedia.org/wiki/Goto
NULL
};
/*
These error messages are generally so well known, that it seems silly to have to even point out that they are
so widely used and known, if not completely genericised.
The most of them are inherited from the MICROSOFT BASIC on which the C64's BASIC is based, so for those, there
cannot even be any claim to copyright over them in any exclusive way.
However, copyright of such short phrases that express ideas in short form cannot be copyrighted:
https://fairuse.stanford.edu/2003/09/09/copyright_protection_for_short/
*/
char *error_list[]={
"TOO MANY FILES", // https://docs.microsoft.com/en-us/dotnet/visual-basic/language-reference/error-messages/too-many-files
"FILE OPEN", // https://forums.autodesk.com/t5/3ds-max-forum/quot-file-open-error-quot/td-p/4036150
"FILE NOT OPEN", // https://www.synthetos.com/topics/file-not-open-error/
"FILE NOT FOUND", // https://answers.microsoft.com/en-us/windows/forum/windows_10-files/file-not-found-check-the-file-name-and-try-again/0734e837-b9f7-485e-93c5-5592f804b4c8
"DEVICE NOT PRESENT", // https://answers.microsoft.com/en-us/windows/forum/all/device-not-present/d9f96498-cf92-4ba0-9076-a206cf058131
"NOT INPUT FILE", // https://stackoverflow.com/questions/13055889/sed-with-literal-string-not-input-file/37682812 http://iama.stupid.cow.org/OldComputerMagazines/Commodore%20Power-Play%20100%25/Commodore_Power-Play_1985_Issue_16_V4_N04_Aug_Sep.pdf
"NOT OUTPUT FILE", // http://www.un4seen.com/forum/?topic=12547.0
"MISSING FILENAME", // https://arduino.stackexchange.com/questions/22839/missing-filename-compilation-error
"ILLEGAL DEVICE NUMBER", // https://comp.unix.solaris.narkive.com/dkqcQFg3/svm-mirror-not-working-illegal-major-device-number
"NEXT WITHOUT FOR", // https://stackoverflow.com/questions/18232928/compile-error-next-without-for-vba
"SYNTAX", // https://en.wikipedia.org/wiki/Syntax_error
"RETURN WITHOUT GOSUB", // https://stackoverflow.com/questions/11467746/return-without-gosub-when-using-subforms-in-access
"OUT OF DATA", // http://forum.qbasicnews.com/index.php?topic=8619.0
"ILLEGAL QUANTITY", // https://community.dynamics.com/ax/f/33/t/238113
"OVERFLOW", // https://www.webopedia.com/TERM/O/overflow_error.html
"OUT OF MEMORY", // https://support.microsoft.com/en-au/help/126962/out-of-memory-error-message-appears-when-you-have-a-large-number-of-pr
"UNDEF\'D STATEMENT", // http://ivanx.com/appleii/magicgoto/
"BAD SUBSCRIPT", // https://www.computing.net/answers/windows-8/how-does-bad-subscript-error-appear-in-gwbasic/2301.html
"REDIM\'D ARRAY", // https://fjkraan.home.xs4all.nl/comp/apple2faq/app2asoftfaq.html
"DIVISION BY ZERO", // https://fjkraan.home.xs4all.nl/comp/apple2faq/app2asoftfaq.html
"ILLEGAL DIRECT", // https://hwiegman.home.xs4all.nl/gw-man/Appendix%20A.html
"TYPE MISMATCH", // https://fjkraan.home.xs4all.nl/comp/apple2faq/app2asoftfaq.html
"STRING TOO LONG", // https://fjkraan.home.xs4all.nl/comp/apple2faq/app2asoftfaq.html
"FILE DATA", // https://www.minitool.com/data-recovery/data-error-crc.html
"FORMULA TOO COMPLEX", // https://fjkraan.home.xs4all.nl/comp/apple2faq/app2asoftfaq.html
"CAN\'T CONTINUE", // https://hwiegman.home.xs4all.nl/gw-man/Appendix%20A.html
"UNDEF\'D FUNCTION", // https://www.saatchiart.com/print/Painting-Undef-d-Function-3495/16167/3829193/view
"VERIFY", // https://forums.openvpn.net/viewtopic.php?t=22040
"LOAD", // https://www.discogs.com/Gimmik-Load-Error/release/482462
// Non-error messages
"READY.\r", // #29 // https://www.ibm.com/support/knowledgecenter/zosbasics/com.ibm.zos.zconcepts/zconc_whatistsonative.htm https://github.com/stefanhaustein/expressionparser
"BREAK", // #30
" IN ", // #31
"BRK AT $", // #32
"ERROR", // #33 // Simply the word error that is attached to the other parts of messages https://fjkraan.home.xs4all.nl/comp/apple2faq/app2asoftfaq.html
" BASIC BYTES FREE", // #34 // https://github.com/stefanhaustein/expressionparser
"MEMORY CORRUPT", // #35
"NOT IMPLEMENTED", // #36
NULL};
// XXX: create separate list for Kernal
// "LOADING", // #30 // https://community.tibco.com/wiki/how-show-loading-prompt-when-reloading-external-data
// "VERIFYING", // https://discussions.apple.com/thread/8087203
// "SAVING",
// Token $FF is end of message, so we can have only 255 unique words
#define MAX_WORDS 255
char *words[MAX_WORDS];
int word_count=0;
#define MAX_LEN 1024
unsigned char message_tokens[MAX_LEN];
int token_count=0;
unsigned char packed_words[MAX_LEN];
int packed_len=0;
unsigned char packed_keywords[MAX_LEN];
int packedkey_len=0;
struct char_freq {
unsigned char c;
int count;
};
int total_chars=0;
int repeats=0;
int cmpfreq(const void *a,const void *b)
{
const struct char_freq *aa=a,*bb=b;
if (aa->count<bb->count) return 1;
if (aa->count>bb->count) return -1;
return 0;
}
#define MAX_CHARS 256
struct char_freq char_freqs[MAX_CHARS];
int char_max=0;
int find_word(char *w)
{
int i;
for(i=0;i<word_count;i++) {
if (!strcmp(w,words[i])) {
// fprintf(stderr,"Word '%s' is repeated.\n",w);
return i;
}
}
words[word_count++]=strdup(w);
return word_count-1;
}
void process_word(char *w)
{
int num=find_word(w);
message_tokens[token_count++]=num;
return;
}
void end_of_message(void)
{
message_tokens[token_count++]=0xFF;
}
int not_vowel(char c)
{
switch(c) {
case 'A': case 'E': case 'I': case 'O': case 'U':
return 0;
}
return 1;
}
int calc_stats(const char *s)
{
for(int j=0;s[j];j++)
if (s[j]!=' ') {
int char_num=0;
for(char_num=0;char_num<char_max;char_num++)
if (s[j]==char_freqs[char_num].c) break;
if (s[j]&0x80) fprintf(stderr,"Word '%s' contains funny char 0x%02x\n",s,s[j]);
if (char_num>MAX_CHARS) {
fprintf(stderr,"Too many unique characters in message list. Max is %d\n",MAX_CHARS);
exit(-1);
}
if (char_num>=char_max) char_max=char_num+1;
char_freqs[char_num].c=s[j];
char_freqs[char_num].count++;
total_chars++;
if (j&&s[j]==s[j-1]) repeats++;
}
return 0;
}
int pack_word(const char *w,unsigned char *out,int *len)
{
fprintf(stderr,"Packing word '%s' at offset 0x%03x\n",w,*len);
int nybl_waiting=0;
int nybl_val=0;
for(int j=0;w[j];j++) {
int char_num;
for(char_num=0;char_num<char_max;char_num++)
if (w[j]==char_freqs[char_num].c) break;
fprintf(stderr,"'%c' = char #%d ",w[j],char_num);
if (char_num<14) {
if (nybl_waiting) {
// We have a nybl already waiting, so join them up
int byte=(nybl_val<<4)+(char_num+1);
out[(*len)++]=byte;
nybl_waiting=0;
fprintf(stderr," M[$%02x]",byte);
} else {
// Queue this nybl to pack in a byte
nybl_waiting=1;
nybl_val=char_num+1;
fprintf(stderr," Q[$%x]",char_num+1);
}
} else {
// It's a less frequent letter that we have to encode
// using a whole byte, and flush out any waiting nybl with a
// $F token to indicate long code follows.
if (nybl_waiting) {
int byte=(nybl_val<<4)+0xF;
out[(*len)++]=byte;
nybl_waiting=0;
fprintf(stderr," F[$%02x]",byte);
if (char_num&0x80) {
fprintf(stderr,"ERROR: Extended characters must be < 0x80\n");
exit(-1);
}
out[(*len)++]=w[j];
fprintf(stderr," XS[$%02x]",w[j]);
} else {
if (char_num < (14+13)) {
out[(*len)++]=0xF1+(char_num-14);
fprintf(stderr," L[$%02x]",0xF1+(char_num-14));
} else {
if (w[j+1]) {
out[(*len)++]=0xFF;
out[(*len)++]=w[j];
fprintf(stderr," X[$FF,$%02x]",char_num);
}
else {
out[(*len)++]=0xFE;
out[(*len)++]=w[j];
fprintf(stderr," X0[$FE,$%02x]\n",char_num);
return 0;
}
}
}
}
fprintf(stderr,"\n");
}
// Flush any pending nybl out, or if none, write $00
if (nybl_waiting) {
int byte=(nybl_val<<4)+0x0;
out[(*len)++]=byte;
nybl_waiting=0;
fprintf(stderr," EF[$%02x]\n",byte);
} else {
int byte=0x00;
out[(*len)++]=byte;
nybl_waiting=0;
fprintf(stderr," E[$%02x]\n",byte);
}
return 0;
}
int main(void)
{
int raw_size=0;
int keyraw_size=0;
// Get letter frequencies
for(int i=0;i<26;i++) char_freqs[i].count=0;
for(int i=0;error_list[i];i++) calc_stats(error_list[i]);
for(int i=0;keyword_list[i];i++) calc_stats(keyword_list[i]);
qsort(char_freqs,char_max,sizeof(struct char_freq),cmpfreq);
fprintf(stderr,"Letter frequencies of %d chars (%d unique, %d repeats):\n",total_chars,char_max,repeats);
for(int i=0;i<char_max;i++) fprintf(stderr,"%02x '%c' : %d (%.2f bits)\n",
char_freqs[i].c,
char_freqs[i].c,
char_freqs[i].count,log(total_chars*1.0/char_freqs[i].count)/log(2));
// Get 14 most frequent out to use as 4-bit tokens 1 - 14.
// Token 0 = end of word.
// Token 15 indicates opposite nybl identifies which of the other 15 possible values is used
// for less-frequent letters. (Low nybl $0 is reserved to make it easy to find end of each
// compressed word, at the cost of one possible symbol).
// Thus common letters take 4 bits, and uncommon ones take 8, i.e., they can never be longer,
// but can be upto 1/2 the length.
for(int i=0;error_list[i];i++) raw_size+=strlen(error_list[i]);
for(int i=0;keyword_list[i];i++) keyraw_size+=strlen(keyword_list[i]);
for(int i=0;error_list[i];i++)
{
char word[1024];
int offset=0;
for(int j=0;error_list[i][j];j++) {
if (error_list[i][j]==' ') {
// Found word boundary
int w=0;
for(int k=offset;k<j;k++) word[w++]=error_list[i][k];
word[w++]=0;
offset=j+1;
// fprintf(stderr,"Found word '%s'\n",word);
process_word(word);
}
}
{
// Get last word on line
int w=0;
for(int k=offset;k<error_list[i][k];k++) word[w++]=error_list[i][k];
word[w++]=0;
// fprintf(stderr,"Found word '%s'\n",word);
process_word(word);
end_of_message();
}
}
// Now encode the words
for(int i=0;i<word_count;i++) {
fprintf(stderr,"$%03X : ",i);
int old_offset=packed_len;
pack_word(words[i],packed_words,&packed_len);
if ((packed_len-old_offset)>=strlen(words[i])) {
if ((packed_len-old_offset)>strlen(words[i]))
fprintf(stderr," Packed word '%s' longer than original: ",words[i]);
else
fprintf(stderr," Packed word '%s' no smaller than original: ",words[i]);
for(int j=old_offset;j<packed_len;j++)
fprintf(stderr," $%02X",packed_words[j]);
fprintf(stderr,"\n");
}
}
for(int i=0;keyword_list[i];i++) {
fprintf(stderr,"$%03X : ",i);
int old_offset=packedkey_len;
pack_word(keyword_list[i],packed_keywords,&packedkey_len);
if ((packedkey_len-old_offset)>=strlen(keyword_list[i])) {
if ((packedkey_len-old_offset)>strlen(keyword_list[i]))
fprintf(stderr," Packed word '%s' longer than original: ",keyword_list[i]);
else
fprintf(stderr," Packed word '%s' no smaller than original: ",keyword_list[i]);
for(int j=old_offset;j<packedkey_len;j++)
fprintf(stderr," $%02X",packed_keywords[j]);
fprintf(stderr,"\n");
}
}
int word_bytes=0;
for(int i=0;i<word_count;i++) word_bytes+=strlen(words[i]);
if (packed_len>255) {
fprintf(stderr,"ERROR: packed_len>255\n");
exit(-1);
}
if (token_count>255) {
fprintf(stderr,"ERROR: token_count>255\n");
exit(-1);
}
printf("packed_message_chars:\n");
for(int i=0;i<char_max;i++) {
printf("\t.byte $%02X // '%c' ",char_freqs[i].c,char_freqs[i].c != 0xD ? char_freqs[i].c : 127);
if (i<14) printf(" = $%x (nybl)\n",i+1);
else if (i<(14+13)) printf(" = $F%x / $xF $%02X\n",i-14+1,char_freqs[i].c);
else printf(" = $FE/F + $%02X\n",char_freqs[i].c);
}
printf("packed_message_tokens:\n");
for(int i=0;i<token_count;i++)
{
if ((token_count-i)>=8) {
printf("\t.byte $%02x,$%02x,$%02x,$%02x,$%02x,$%02x,$%02x,$%02x\n",
message_tokens[i+0],message_tokens[i+1],message_tokens[i+2],message_tokens[i+3],
message_tokens[i+4],message_tokens[i+5],message_tokens[i+6],message_tokens[i+7]);
i+=7;
} else
printf("\t.byte $%02x\n",message_tokens[i]);
}
printf("packed_message_words:\n");
for(int i=0;i<packed_len;i++)
{
if ((packed_len-i)>=8) {
printf("\t.byte $%02x,$%02x,$%02x,$%02x,$%02x,$%02x,$%02x,$%02x\n",
packed_words[i+0],packed_words[i+1],packed_words[i+2],packed_words[i+3],
packed_words[i+4],packed_words[i+5],packed_words[i+6],packed_words[i+7]);
i+=7;
} else
printf("\t.byte $%02x\n",packed_words[i]);
}
printf("packed_keywords:\n");
for(int i=0;i<packedkey_len;i++)
{
if ((packedkey_len-i)>=8) {
printf("\t.byte $%02x,$%02x,$%02x,$%02x,$%02x,$%02x,$%02x,$%02x\n",
packed_keywords[i+0],packed_keywords[i+1],packed_keywords[i+2],packed_keywords[i+3],
packed_keywords[i+4],packed_keywords[i+5],packed_keywords[i+6],packed_keywords[i+7]);
i+=7;
} else
printf("\t.byte $%02x\n",packed_keywords[i]);
}
printf("\t.label packed_keyword_table_len = $%X\n",packedkey_len);
fprintf(stderr,"Error list takes %d bytes raw.\n",raw_size);
fprintf(stderr,"%d token bytes used to encode all messages.\n",token_count);
fprintf(stderr,"Plus %d bytes for the words\n",packed_len);
fprintf(stderr,"Space saving = %d bytes\n",raw_size-packed_len-token_count);
fprintf(stderr,"\nKeyword list takes %d bytes raw.\n",keyraw_size);
fprintf(stderr,"Keyword list takes %d bytes packed.\n",packedkey_len);
}
-254
View File
@@ -1,254 +0,0 @@
//
// Utility to generate various floating points constants
// in a Commodore-specific variable format
//
#include "common.h"
#include <unistd.h>
#include <cmath>
#include <cstdio>
#include <fstream>
#include <iomanip>
#include <vector>
//
// Basic types definition
//
typedef struct ConstEntry
{
ConstEntry(const std::string &constName, double constValue) :
constName(constName),
constValue(constValue),
outString(".error \"not generated\"")
{
};
const std::string constName;
const double constValue;
std::string outString;
} ConstEntry;
//
// Constants to output
//
std::vector<ConstEntry> GLOBAL_constants =
{
// Constants from:
// - https://www.c64-wiki.com/wiki/BASIC-ROM
// Computes Mapping the Commodore 64, pages 103, 105, 113, 114, 116
ConstEntry( "HALF", 0.5 ),
ConstEntry( "NEG_HALF", -0.5 ),
ConstEntry( "ONE", 1.0 ),
ConstEntry( "TEN", 10.0 ),
ConstEntry( "NEG_32768", -32768.0 ),
ConstEntry( "PI", M_PI ),
ConstEntry( "SQR_2", std::sqrt(2.0) ),
ConstEntry( "INV_SQR_2", 1.0 / std::sqrt(2.0) ),
ConstEntry( "LOG_2", std::log(2.0) ),
ConstEntry( "INV_LOG_2", 1.0 / std::log(2.0) ),
};
//
// Command line settings
//
std::string CMD_outFile = "out.s";
//
// Common helper functions
//
void printUsage()
{
std::cout << "\n" <<
"usage: generate_constants [-o <out file>]" << "\n\n";
}
void printBanner()
{
printBannerLineTop();
std::cout << "// Generating floating point constants" << "\n";
printBannerLineBottom();
}
//
// Top-level functions
//
void parseCommandLine(int argc, char **argv)
{
int opt;
// Retrieve command line options
while ((opt = getopt(argc, argv, "o:")) != -1)
{
switch(opt)
{
case 'o': CMD_outFile = optarg; break;
default: printUsage(); ERROR();
}
}
}
//
// Constants generation
//
std::string toAssemblerString(const std::string &constName, double constValue)
{
uint8_t outFloat[5] = { 0 };
// Retrieve mantissa nad exponent
int exponent = -0x80;
double mantissa = frexp(std::abs(constValue), &exponent);
// Round the mantissa to output format precission
const double coeff = 256.0 * 256.0 * 256.0 * 256.0;
double intPart;
if (modf(mantissa * coeff, &intPart) >= 0.5)
{
mantissa = (intPart + 1.0) / coeff;
}
else
{
mantissa = intPart / coeff;
}
if (mantissa >= 1.0)
{
mantissa = 0.5;
exponent++;
}
// Add a bias to exponent
exponent += 0x80;
// Check if output format can contain such a number
if (exponent > 0xFF)
{
ERROR(std::string("const '") + constName + "' abs value too large");
}
else if (exponent <= 0)
{
ERROR(std::string("const '") + constName + "' abs value too small");
}
// Set output constant exponent
outFloat[0] = exponent;
// Set output constant mantissa
for (uint8_t idxByte = 1; idxByte <= 4; idxByte++)
{
for (uint8_t idxBit = 0; idxBit <= 7; idxBit++)
{
outFloat[idxByte] = outFloat[idxByte] << 1;
if (mantissa >= 0.5)
{
outFloat[idxByte]++;
mantissa -= 0.5;
}
mantissa *= 2;
}
}
if (mantissa > 0.0) ERROR(std::string("const '") + constName + "' export error");
if (outFloat[1] < 0x80) ERROR(std::string("const '") + constName + "' not normalized");
// Set output constant sign
if (constValue >= 0.0)
{
outFloat[1] = outFloat[1] & 0x7F;
}
// Return the output constant as string for assembler
char buf[256] = { 0 };
snprintf(buf, sizeof(buf), "$%02X, $%02X, $%02X, $%02X, $%02X // %22.10f",
outFloat[0],
outFloat[1],
outFloat[2],
outFloat[3],
outFloat[4],
constValue);
std::string outDef = std::string("\n.macro put_const_") + constName + "()\n{\n\t.byte " + buf + "\n}\n";
return outDef;
}
void writeConstants()
{
// Convert constants to assembler strings
for (auto &entry : GLOBAL_constants)
{
entry.outString = toAssemblerString(entry.constName, entry.constValue);
}
// Remove old file
unlink(CMD_outFile.c_str());
// Open output file for writing
std::ofstream outFile(CMD_outFile, std::fstream::out | std::fstream::trunc);
if (!outFile.good()) ERROR(std::string("can't open oputput file '") + CMD_outFile + "'");
// Write header
outFile << "//\n// Generated file - do not edit\n//\n\n";
// Write constants
for (auto &entry : GLOBAL_constants)
{
outFile << entry.outString;
}
if (!outFile.good())
{
outFile.close();
unlink(CMD_outFile.c_str());
ERROR(std::string("can't write oputput file '") + CMD_outFile + "'");
}
// Close the file
outFile.close();
std::cout << std::string("Constants written to: ") + CMD_outFile + "\n\n";
}
//
// Main function
//
int main(int argc, char **argv)
{
parseCommandLine(argc, argv);
printBanner();
writeConstants();
return 0;
}
-54
View File
@@ -1,54 +0,0 @@
unsigned char frame0000[]={// border,bg,chars,colors
11,6,
32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
32,227,227,227,227,227,227,223,32,160,160,223,32,160,160,223,32,160,160,223,32,160,160,223,32,32,32,32,13,32,5,32,7,32,1,32,32,32,32,32,
32,227,227,227,227,227,227,227,223,95,205,205,223,95,223,32,32,95,223,32,32,95,223,95,223,32,32,2,32,1,32,19,32,9,32,3,32,32,32,32,
32,227,227,227,227,227,227,227,227,223,95,223,95,223,95,205,32,32,95,205,95,223,95,205,95,223,32,32,45,45,45,45,45,45,45,32,32,32,32,32,
32,227,227,227,227,227,227,227,227,227,223,95,223,95,32,95,160,160,32,95,160,160,32,95,223,95,32,32,32,32,49,46,48,32,32,32,32,32,32,32,
32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
32,18,5,1,4,25,46,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
32,160,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,
14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,
14,14,14,14,14,14,14,14,15,1,1,1,15,1,1,1,15,1,1,1,15,1,1,1,14,1,1,1,1,1,1,1,1,1,1,14,14,14,14,14,
14,13,13,13,13,13,13,13,13,13,13,13,13,13,13,1,1,13,13,1,1,13,13,13,13,1,1,13,13,13,13,13,13,13,13,13,14,14,14,14,
14,7,7,7,7,7,7,7,7,7,15,15,15,15,15,15,1,1,15,15,15,15,15,15,15,15,14,1,14,14,14,14,14,14,14,1,1,14,14,14,
14,10,10,10,10,10,10,10,10,10,10,14,14,14,15,14,14,14,15,14,14,14,15,14,14,14,15,14,1,1,15,15,15,1,1,1,14,14,14,14,
14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,1,1,14,14,14,14,
14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,1,1,14,14,14,14,
14,1,1,1,1,1,1,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,1,14,14,14,14,
14,1,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,
14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,
14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,
14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,
14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,
14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,
14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,
14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,1,14,14,14,14,14,14,14,14,14,14,14,14,14,
14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,
14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,
14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,
14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,
14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,
14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,
14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,
14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,
14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14
};
// META: 40 25 C64 upper
-61
View File
@@ -1,61 +0,0 @@
#include <stdio.h>
#include "basic005.c"
unsigned char bytes[65536];
int len=0;
unsigned char colour_codes[16]={
144,5,28,159,156,30,31,158,
129,149,150,151,152,153,154,155
};
int main(int argc,char **argv)
{
int reverse=0;
int last_colour=-1;
for(int y=0;y<24;y++) {
unsigned char *line=&frame0000[43+y*40];
unsigned char *colour=&frame0000[43+1000+y*40];
int w=39;
while(w&&line[w-1]==' ') w--;
for(int x=0;x<w;x++) {
// Update colour if required
if (colour[x]!=last_colour) {
bytes[len++]=colour_codes[colour[x]];
last_colour=colour[x];
}
if ((line[x]&0x80)^reverse) {
// Toggle reverse video flag
if (reverse) bytes[len++]=0x92; else bytes[len++]=0x12;
reverse^=0x80;
}
// Write char
if (line[x]<0x20) bytes[len++]=line[x]+0x40;
else if (line[x]>=0x40&&line[x]<0x60) bytes[len++]=line[x]+0x20;
else if (line[x]>=0xe0)
bytes[len++]=(line[x]&0x7f)+0x40;
else if (line[x]>=0x80)
bytes[len++]=line[x];
else
bytes[len++]=line[x];
}
// Add end of line marker if required
if (w<40) bytes[len++]=13;
}
// Skip blank lines at the end
while(bytes[len-1]==0x0d) len--;
bytes[len++]=0;
printf("; Used %d bytes\n",len);
printf("startup_banner:");
for(int i=0;i<len;i++) {
if (!(i&0xf)) printf("\n .byte ");
printf("$%02X",bytes[i]);
if ((i&0xf)!=0xf) printf(",");
}
printf("\n");
}
-310
View File
@@ -1,310 +0,0 @@
/*
Pre-process the various assembly source files to create a single
file for assembly, that has everything where it needs to live.
*/
#include <stdio.h>
#include <dirent.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#define DO_ERROR(X) { retVal=-1; fprintf(stderr,"%s:%d:%s():%s\n",__FILE__,__LINE__,__func__,X); break; }
#define MAX_FILES (128*1024)
typedef struct source_file {
char *file;
char *label;
int address;
// For getting routine sizes from first run of KickAss
int low_addr;
int high_addr;
int size;
// For knowing when we have output the various routines
int written;
} source_file;
source_file source_files[MAX_FILES];
int source_count=0;
static int compare_source(const void* a, const void* b)
{
source_file* aa = (source_file*)a;
source_file* bb = (source_file*)b;
if (aa->address<bb->address) return -1;
if (aa->address>bb->address) return 1;
return strcmp(aa->file,bb->file);
}
int dump_file(FILE *out,const char *name)
{
int retVal=0;
FILE *f=NULL;
do {
f=fopen(name,"r");
if (!f) DO_ERROR("Failed to read file for input");
char line[8192];
while(!feof(f)) {
line[0]=0; fgets(line,8192,f);
fprintf(out,"%s",line);
}
} while(0);
if (f) fclose(f);
return retVal;
}
void usage(void)
{
fprintf(stderr,"usage: preprocess [-d <directory>] [-l <start/low address>] [-h <end/high address>]\n");
}
int main(int argc,char **argv)
{
int retVal=0;
DIR *d=NULL;
char *directory=".";
struct dirent *de=NULL;
int start_address=0xe4d3;
int end_address=0xffff+1;
do {
int opt;
while ((opt=getopt(argc,argv,"d:l:h:")) != -1) {
switch(opt) {
case 'd': directory=optarg; break;
case 'l': start_address=strtol(optarg,NULL,16); break;
case 'h': end_address=strtol(optarg,NULL,16)+1; break;
default:
usage();
exit(-1);
}
}
// Get list of files
d=opendir(directory);
if (!d) DO_ERROR("opendir() failed");
while((de=readdir(d))!=NULL) {
if (strlen(de->d_name)>2) {
if (de->d_name[0]!='.'&&de->d_name[0]!='#') // ignore temp files etc
if (!strcmp(&de->d_name[strlen(de->d_name)-2],".s")) {
if (strcmp(de->d_name,"combined.s")) {
if (source_count>=MAX_FILES) DO_ERROR("Too many source files. Increase MAX_FILES?");
source_files[source_count].file=strdup(de->d_name);
source_files[source_count].label=strdup(de->d_name);
for (char *charptr = source_files[source_count].label; *charptr != 0; charptr++) {
if (*charptr == '.') *charptr = '_'; // KickAss does not like full stops in labels
}
source_files[source_count].address=-1;
source_count++;
}
}
}
}
if (retVal) break;
closedir(d); d=NULL;
fprintf(stderr,"Found %d source files\n",source_count);
// Now get routine addresses of everything
for(int i=0;i<source_count;i++) {
u_int32_t addr;
char funcname[1024];
if (sscanf(source_files[i].file,"%x.%[^.]",&addr,funcname)==2) {
// Function must appear at specific address
source_files[i].address=addr;
// printf("function '%s' should be placed at $%04X\n",funcname,addr);
}
}
fprintf(stderr,"Got fixed routine addresses\n");
// Sort files into order.
// The sorted order is the order the files will appear in the combined source file.
// This means that address prefixes on names will be in the right order, but function
// names will not.
qsort(source_files,source_count,sizeof(source_file),compare_source);
fprintf(stderr,"Sorted.\n");
// for(int i=0;i<source_count;i++) {
// fprintf(stderr,"$%04X : %s\n",
// source_files[i].address!=0x7ffffff?source_files[i].address:0,
// source_files[i].file);
// }
// First pass is to work out the size of each routine, so that we can pack them optimally
// around the routines that are located at fixed addresses.
unlink("temp.sym");
unlink("temp.s");
FILE *out=fopen("temp.s","w");
if (!out) DO_ERROR("Could not write to temp.s");
// Start at $100, so that no local data gets accesses using ZP addressing modes during this pass,
// which would otherwise upset things later
fprintf(out,"\t* = $100\n");
for(int i=0;i<source_count;i++) {
fprintf(out,"// Source file '%s'\n",source_files[i].file);
if (source_files[i].file[0]!=',') fprintf(out,"__routine_start_%s:\n",source_files[i].label);
char path[8192];
snprintf(path,8192,"%s/%s",directory,source_files[i].file);
if (dump_file(out,path)) DO_ERROR("Could not read source file");
if (source_files[i].file[0]!=',') fprintf(out,"__routine_end_%s:\n",source_files[i].label);
}
fclose(out);
char cmd[8192];
snprintf(cmd,8192,"java -jar assembler/KickAss.jar temp.s -symbolfile -o /dev/null");
fprintf(stderr,"Running first pass to get size of routines...\n");
system(cmd);
// Parse temp.sym contents to get size of every routine
fprintf(stderr,"Reading sizes of routines...\n");
out=fopen("temp.sym","r");
if (!out) DO_ERROR("Could not open temp.sym for reading. Did KickAss fail due to an error in one of the assembly files?");
while(!feof(out)) {
int addr;
char label[8192];
char line[8192];
line[0]=0; fgets(line,8192,out);
if (sscanf(line,".label __routine_start_%[^=]=$%x",label,&addr)==2) {
for(int i=0;i<source_count;i++)
if (!strcmp(label,source_files[i].label))
{ source_files[i].low_addr=addr;
source_files[i].size=source_files[i].high_addr-source_files[i].low_addr;
break; }
}
if (sscanf(line,".label __routine_end_%[^=]=$%x",label,&addr)==2) {
for(int i=0;i<source_count;i++)
if (!strcmp(label,source_files[i].label))
{ source_files[i].high_addr=addr;
source_files[i].size=source_files[i].high_addr-source_files[i].low_addr;
break; }
}
}
fclose(out);
fprintf(stderr,"Got routine sizes:\n");
for(int i=0;i<source_count;i++)
fprintf(stderr,"%s : %d bytes\n",source_files[i].file,source_files[i].size);
// Now write the routines out in a deterministic order that respects the requirement
// of some routines to be at fixed addresses.
// Start at beginning of actual kernal data
int address=start_address;
char filename[8192];
snprintf(filename,8192,"%s/combined.s",directory);
unlink(filename);
out=fopen(filename,"w");
if (!out) DO_ERROR("Could not write to combined.s");
fprintf(out,"\t.segment Main [start=$%04x, min=$%04x, max=$%04x, outBin=\"OUT.BIN\", fill]\n" ,
start_address, start_address, end_address - 1);
fprintf(out,"\t* = $%04x\n",address);
// First write files of length 0, so that all the zeropage location definitions are
// known to KickAss - otherwise it will assume 16-bit address is needed, which will backfire
int written_count = 0;
for(int i=0;i<source_count;i++) {
if (source_files[i].size == 0) {
fprintf(out,"\n// Source file %s\n",source_files[i].file);
char filename[8192];
snprintf(filename,8192,"%s/%s",directory,source_files[i].file);
if (dump_file(out,filename)) DO_ERROR("Could not dump 0-size routine");
source_files[i].written=1;
}
else source_files[i].written=0;
}
// XXX - We should use dynamic programming optimisation to pack the routines
// optimally in the space, so that when we get near to full, we don't run out
// of space due to fragmentation.
for(;written_count<source_count;written_count++) {
// First, find the next allocated address
int next_allocated_address=0xffff;
int next_fixed_routine=-1;
for(int i=0;i<source_count;i++)
if (!source_files[i].written) {
if (source_files[i].address>=address)
if (source_files[i].address<next_allocated_address) {
next_allocated_address=source_files[i].address;
next_fixed_routine=i;
}
}
int space=next_allocated_address-address;
int biggest=-1;
int biggest_size=-1;
for(int i=0;i<source_count;i++)
if (!source_files[i].written) {
if (source_files[i].address==-1) {
if (source_files[i].size>biggest_size) {
if (source_files[i].size<=space) {
biggest_size=source_files[i].size;
biggest=i;
}
}
}
}
// Check if done
if (next_fixed_routine==-1&&biggest==-1) break;
fprintf(stderr,"At address $%04x, next allocated address is $%04X. Current space is %d bytes\n",
address,next_allocated_address,space);
if (biggest>-1)
fprintf(stderr,"Biggest floating routine that fits is '%s' (%d bytes)\n",
source_files[biggest].file,biggest_size);
if (biggest==-1) {
// Nothing else fits here. So write next_fixed_routine
// fprintf(stderr,"Writing file %s\n",source_files[next_fixed_routine].file);
fprintf(out,"\n// Source file %s @ $%04X\n",source_files[next_fixed_routine].file,address);
fprintf(out,"\t* = $%04x\n", next_allocated_address);
char filename[8192];
snprintf(filename,8192,"%s/%s",directory,source_files[next_fixed_routine].file);
if (dump_file(out,filename)) DO_ERROR("Could not dump fixed address routine");
source_files[next_fixed_routine].written=1;
address=next_allocated_address+source_files[next_fixed_routine].size;
} else {
fprintf(out,"\n// Source file %s @ $%04x\n",source_files[biggest].file,address);
// fprintf(stderr,"Writing file %s\n",source_files[biggest].file);
fprintf(out,"\t* = $%04x\n", address);
char filename[8192];
snprintf(filename,8192,"%s/%s",directory,source_files[biggest].file);
if (dump_file(out,filename)) DO_ERROR("Could not dump floating routine");
source_files[biggest].written=1;
address+=source_files[biggest].size;
}
}
fclose(out);
if (retVal) break;
for(int i=0;i<source_count;i++)
if (!source_files[i].written) {
fprintf(stderr,"ERROR: Did not write source file '%s'\n",source_files[i].file);
DO_ERROR("Did not write all source files. Did they not fit?");
}
if (retVal) break;
fprintf(stderr,"Assembling combined source...\n");
snprintf(cmd,8192,"java -jar assembler/KickAss.jar %s/combined.s -symbolfile -vicesymbols -binfile -o %s/OUT.BIN",directory,directory);
system(cmd);
} while(0);
if (d) closedir(d);
return retVal;
}
-720
View File
@@ -1,720 +0,0 @@
/*
* Copyright 2002-2010 Guillaume Cottenceau.
* Copyright 2015-2018 Paul Gardner-Stephen.
*
* This software may be freely redistributed under the terms
* of the X11 license.
*
*/
/* ============================================================= */
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <strings.h>
#include <stdarg.h>
#define PNG_DEBUG 3
#include <png.h>
/* ============================================================= */
char *vhdl_prefix=
"library IEEE;\n"
"use IEEE.STD_LOGIC_1164.ALL;\n"
"use ieee.numeric_std.all;\n"
"use work.debugtools.all;\n"
"\n"
"--\n"
"entity charrom is\n"
"port (Clk : in std_logic;\n"
" address : in integer range 0 to 4095;\n"
" -- chip select, active low \n"
" cs : in std_logic;\n"
" data_o : out std_logic_vector(7 downto 0);\n"
"\n"
" writeclk : in std_logic;\n"
" -- Yes, we do have a write enable, because we allow modification of ROMs\n"
" -- in the running machine, unless purposely disabled. This gives us\n"
" -- something like the WOM that the Amiga had.\n"
" writecs : in std_logic;\n"
" we : in std_logic;\n"
" writeaddress : in unsigned(11 downto 0);\n"
" data_i : in std_logic_vector(7 downto 0)\n"
" );\n"
"end charrom;\n"
"\n"
"architecture Behavioral of charrom is\n"
"\n"
"-- 4K x 8bit pre-initialised RAM for character ROM\n"
"\n"
"type ram_t is array (0 to 4095) of std_logic_vector(7 downto 0);\n"
"signal ram : ram_t := (\n"
"\n";
char *vhdl_suffix=
");\n"
"\n"
"begin\n"
"\n"
"--process for read and write operation.\n"
"PROCESS(Clk,ram,writeclk)\n"
"BEGIN\n"
"\n"
" if(rising_edge(writeClk)) then \n"
" if writecs='1' then\n"
" if(we='1') then\n"
" ram(to_integer(writeaddress)) <= data_i;\n"
" end if;\n"
" end if;\n"
" data_o <= ram(address);\n"
" end if;\n"
"END PROCESS;\n"
"\n"
"end Behavioral;\n";
/* ============================================================= */
int x, y;
int width, height;
png_byte color_type;
png_byte bit_depth;
png_structp png_ptr;
png_infop info_ptr;
int number_of_passes;
png_bytep * row_pointers;
FILE *infile;
FILE *outfile;
/* ============================================================= */
void abort_(const char * s, ...)
{
va_list args;
va_start(args, s);
vfprintf(stderr, s, args);
fprintf(stderr, "\n");
va_end(args);
abort();
}
/* ============================================================= */
void read_png_file(char* file_name)
{
unsigned char header[8]; // 8 is the maximum size that can be checked
/* open file and test for it being a png */
infile = fopen(file_name, "rb");
if (infile == NULL)
abort_("[read_png_file] File %s could not be opened for reading", file_name);
fread(header, 1, 8, infile);
if (png_sig_cmp(header, 0, 8))
abort_("[read_png_file] File %s is not recognized as a PNG file", file_name);
/* initialize stuff */
png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
if (!png_ptr)
abort_("[read_png_file] png_create_read_struct failed");
info_ptr = png_create_info_struct(png_ptr);
if (!info_ptr)
abort_("[read_png_file] png_create_info_struct failed");
if (setjmp(png_jmpbuf(png_ptr)))
abort_("[read_png_file] Error during init_io");
png_init_io(png_ptr, infile);
png_set_sig_bytes(png_ptr, 8);
// Convert palette to RGB values
png_set_expand(png_ptr);
png_read_info(png_ptr, info_ptr);
width = png_get_image_width(png_ptr, info_ptr);
height = png_get_image_height(png_ptr, info_ptr);
color_type = png_get_color_type(png_ptr, info_ptr);
bit_depth = png_get_bit_depth(png_ptr, info_ptr);
printf("Input-file is: width=%d, height=%d.\n", width, height);
number_of_passes = png_set_interlace_handling(png_ptr);
png_read_update_info(png_ptr, info_ptr);
/* read file */
if (setjmp(png_jmpbuf(png_ptr)))
abort_("[read_png_file] Error during read_image");
row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * height);
for (y=0; y<height; y++)
row_pointers[y] = (png_byte*) malloc(png_get_rowbytes(png_ptr,info_ptr));
png_read_image(png_ptr, row_pointers);
if (infile != NULL) {
fclose(infile);
infile = NULL;
}
printf("Input-file is read and now closed\n");
}
/* ============================================================= */
struct rgb {
int r;
int g;
int b;
};
struct rgb palette[2560];
int palette_first=16;
int palette_index=16; // only use upper half of palette
int palette_lookup(int r,int g, int b)
{
int i;
// Do we know this colour already?
for(i=palette_first;i<palette_index;i++) {
if (r==palette[i].r&&g==palette[i].g&&b==palette[i].b) {
return i;
}
}
// new colour
if (palette_index>255) {
fprintf(stderr,"Too many colours in image: Must be < 256, now up to %d\n",
palette_index);
}
if (palette_index>2559) exit(-1);
// allocate it
palette[palette_index].r=r;
palette[palette_index].g=g;
palette[palette_index].b=b;
return palette_index++;
}
unsigned char nyblswap(unsigned char in)
{
return ((in&0xf)<<4)+((in&0xf0)>>4);
}
void process_file(int mode, char *outputfilename)
{
int multiplier=-1;
if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_RGB)
multiplier=3;
if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_RGBA)
multiplier=4;
if (multiplier==-1) {
fprintf(stderr,"Could not convert file to RGB or RGBA\n");
}
outfile=fopen(outputfilename,"w");
if (outfile == NULL) {
// could not open output file, so close all and exit
if (infile != NULL) {
fclose(infile);
infile = NULL;
}
abort_("[process_file] File %s could not be opened for writing", outputfilename);
}
/* ============================ */
if (mode==0) {
printf("mode=0 (logo)\n");
// Logo mode
// Pre-load in C64 palette, so that those colours can be re-used if required
palette[0]=(struct rgb){.r=0,.g=0,.b=0};
palette[1]=(struct rgb){.r=0xff,.g=0xff,.b=0xff};
palette[2]=(struct rgb){.r=0xab,.g=0x31,.b=0x26};
palette[3]=(struct rgb){.r=0x66,.g=0xda,.b=0xff};
palette[4]=(struct rgb){.r=0xbb,.g=0x3f,.b=0xb8};
palette[5]=(struct rgb){.r=0x55,.g=0xce,.b=0x58};
palette[6]=(struct rgb){.r=0x1d,.g=0x0e,.b=0x97};
palette[7]=(struct rgb){.r=0xea,.g=0xf5,.b=0x7c};
palette[8]=(struct rgb){.r=0xb9,.g=0x74,.b=0x18};
palette[9]=(struct rgb){.r=0x78,.g=0x73,.b=0x00};
palette[10]=(struct rgb){.r=0xdd,.g=0x93,.b=0x87};
palette[11]=(struct rgb){.r=0x5b,.g=0x5b,.b=0x5b};
palette[12]=(struct rgb){.r=0x8b,.g=0x8b,.b=0x8b};
palette[13]=(struct rgb){.r=0xb0,.g=0xf4,.b=0xac};
palette[14]=(struct rgb){.r=0xaa,.g=0x9d,.b=0xef};
palette[15]=(struct rgb){.r=0xb8,.g=0xb8,.b=0xb8};
for (y=0; y<height; y++) {
png_byte* row = row_pointers[y];
for (x=0; x<width; x++) {
png_byte* ptr = &(row[x*multiplier]);
int r,g,b;
if (ptr)
{ r=ptr[0]; g=ptr[1]; b=ptr[2]; // a=ptr[3];
} else { r=0; g=0; b=0; }
int c=palette_lookup(r,g,b);
if (c>255) printf("Too many colours at (%d,%d)\n",x,y);
/* work out where in logo file it must be written.
image is made of 8x8 blocks. So every 8 pixels across increases address
by 64, and every 8 pixels down increases pixel count by (64*8), and every
single pixel down increases address by 8.
*/
int address=0;
address+=0x300; // space for palettes
address+=(x&7)+(y&7)*8;
address+=(x>>3)*64;
// if (size==SIZE_LOGO)
address+=(y>>3)*64*(width/8);
// else
// address+=(y>>3)*64*40;
fseek(outfile,address,SEEK_SET);
int n=fwrite(&c,1,1,outfile);
if (n!=1) {
fprintf(stderr,"Could not write pixel (%d,%d) @ $%x\n",x,y,address);
if (outfile != NULL) {
fclose(outfile);
outfile = NULL;
}
exit(-1);
}
}
}
fprintf(stderr,"Writing out palette of %d values\n",palette_index-palette_first);
for(int i=0;i<256;i++){
int address;
unsigned char c;
int v;
address=i+0x000;
v=palette[i].r;
c=(v>>4)|((v&0xf)<<4);
fseek(outfile,address,SEEK_SET);
fwrite(&c,1,1,outfile);
address=i+0x100;
v=palette[i].g;
c=(v>>4)|((v&0xf)<<4);
fseek(outfile,address,SEEK_SET);
fwrite(&c,1,1,outfile);
address=i+0x200;
v=palette[i].b;
c=(v>>4)|((v&0xf)<<4);
fseek(outfile,address,SEEK_SET);
fwrite(&c,1,1,outfile);
}
if (outfile != NULL) {
fclose(outfile);
outfile = NULL;
}
}
/* ============================ */
if (mode==1) {
unsigned char first_half[1024];
printf("mode=1 (charrom)\n");
// charrom mode
int vhdl_mode=1;
if (!strstr(outputfilename,".vhdl")) vhdl_mode=0;
int bytes=0;
if (vhdl_mode) fprintf(outfile,"%s",vhdl_prefix);
if (width!=8) {
fprintf(stderr,"Fonts must be 8 pixels wide\n");
}
int spots[8][8];
int charsets;
// 4KB = 2x 256 char = 2KB charsets
for(charsets = 0 ; charsets<2 ; charsets++) {
if (bytes>=4096) break;
fprintf(stderr,"yheight=%d\n",height);
for (y=0; y<height; y++) {
png_byte* row = row_pointers[y];
int byte=0;
int yy=y&7;
for (x=0; x<width; x++) {
png_byte* ptr = &(row[x*multiplier]);
int r=ptr[0],g=ptr[1],b=ptr[2]; //, a=ptr[3];
if (x<8) {
if (r>0x7f||g>0x7f||b>0x7f) {
byte|=(1<<(7-x));
spots[yy][x]=1;
} else spots[yy][x]=0;
}
}
fflush(stdout);
char comma = ',';
if (bytes<1024) {
first_half[bytes]=byte;
}
bytes++;
if (bytes>=4096) {
comma=' ';
}
if (vhdl_mode) fprintf(outfile,"x\"%02x\"%c",byte,comma);
else fputc(byte,outfile);
if (vhdl_mode) {
if ((y&7)==7) {
fprintf(outfile,"\n");
int yy;
for(yy=0;yy<8;yy++) {
fprintf(outfile,"-- [");
for(x=0;x<8;x++) {
if (spots[yy][x]) fprintf(outfile,"*"); else fprintf(outfile," ");
}
fprintf(outfile,"]\n");
}
}
}
}
// Fill in any missing bytes
if (bytes<2048) {
printf("Padding output file to 2048 after first charset\n");
if (vhdl_mode) {
fprintf(outfile,",\n");
for(;bytes<2048;bytes+=8) {
int reverse=bytes&0x400;
if (reverse) reverse=0xff;
fprintf(outfile,"x\"%02X\",x\"%02X\",x\"%02X\",x\"%02X\",x\"%02X\",x\"%02X\",x\"%02X\",x\"%02X\"%c -- 0x%03x (set %d, char 0x%02x)\n",
first_half[(bytes+0)&0x3ff]^reverse,
first_half[(bytes+1)&0x3ff]^reverse,
first_half[(bytes+2)&0x3ff]^reverse,
first_half[(bytes+3)&0x3ff]^reverse,
first_half[(bytes+4)&0x3ff]^reverse,
first_half[(bytes+5)&0x3ff]^reverse,
first_half[(bytes+6)&0x3ff]^reverse,
first_half[(bytes+7)&0x3ff]^reverse,
',',
bytes,bytes/2048,(bytes/8)&0xff);
fprintf(outfile,"\n");
int yy;
for(yy=0;yy<8;yy++) {
fprintf(outfile,"-- [");
for(x=0;x<8;x++) {
if ((first_half[(bytes+yy)&0x3ff]^reverse)&(1<<(7-x))) fprintf(outfile,"*"); else fprintf(outfile," ");
}
fprintf(outfile,"]\n");
}
}
} else {
// In raw mode, don't pad, or write charset twice
break;
}
}
}
// Fill in any missing bytes
if (bytes<4096) {
printf("Padding output file to 4096\n");
if (vhdl_mode) {
fprintf(outfile,",\n");
for(;bytes<4096;bytes+=8) {
int reverse=bytes&0x400;
if (reverse) reverse=0xff;
fprintf(outfile,"x\"%02X\",x\"%02X\",x\"%02X\",x\"%02X\",x\"%02X\",x\"%02X\",x\"%02X\",x\"%02X\"%c -- 0x%03x (set %d, char %d)\n",
first_half[(bytes+0)&0x3ff]^reverse,
first_half[(bytes+1)&0x3ff]^reverse,
first_half[(bytes+2)&0x3ff]^reverse,
first_half[(bytes+3)&0x3ff]^reverse,
first_half[(bytes+4)&0x3ff]^reverse,
first_half[(bytes+5)&0x3ff]^reverse,
first_half[(bytes+6)&0x3ff]^reverse,
first_half[(bytes+7)&0x3ff]^reverse,
bytes<(4096-8)?',':' ',
bytes,bytes/2048,(bytes/8)&0xff);
}
} else {
// In raw mode, don't pad
}
}
if (vhdl_mode) fprintf(outfile,"%s",vhdl_suffix);
if (outfile != NULL) {
fclose(outfile);
outfile = NULL;
}
}
/* ============================ */
if (mode==2) {
printf("mode=2 (hi-res prep)\n");
// hi-res image preparation mode
// int bytes=0;
if (width%8||height%8) {
fprintf(stderr,"Image must be multiple of 8 pixels wide and high\n");
}
int problems=0;
int total=0;
int threes=0;
int fours=0;
int ones=0;
int tiles[8000][8][8];
int tile_count=0;
int this_tile[8][8];
for (y=0; y<height; y+=8) {
for (x=0; x<width; x+=8) {
int yy,xx;
int i;
int colour_count=0;
int colours[64];
printf("[%d,%d]\n",x,y);
total++;
for(yy=y;yy<y+8;yy++) {
png_byte* row = row_pointers[yy];
for(xx=x;xx<x+8;xx++) {
png_byte* ptr = &(row[xx*multiplier]);
int r,g,b;
if (ptr&&(yy<height)&&(xx<width)) {
r=ptr[0]; g=ptr[1]; b=ptr[2];
} else {
r=0; g=0; b=0;
}
int c=r+256*g+65536*b;
this_tile[yy-y][xx-x]=c;
for(i=0;i<colour_count;i++) if (c==colours[i]) break;
if (i==colour_count) {
colours[colour_count++]=c;
}
}
}
for(i=0;i<tile_count;i++) {
int dud=0;
int xx,yy;
for(xx=0;xx<8;xx++)
for(yy=0;yy<8;yy++) {
if (this_tile[yy][xx]!=tiles[i][yy][xx]) dud=1;
}
if (!dud) break;
}
if (i==tile_count) {
int xx,yy;
for(xx=0;xx<8;xx++)
for(yy=0;yy<8;yy++) {
tiles[tile_count][yy][xx]=this_tile[yy][xx];
}
printf(".[%d]",tile_count); fflush(stdout);
tile_count++;
if (tile_count>=8000) {
fprintf(stderr,"Too many tiles\n");
if (outfile != NULL) {
fclose(outfile);
outfile = NULL;
}
exit(-1);
}
}
if (colour_count==1) ones++;
if (colour_count==3) threes++;
if (colour_count==4) fours++;
if (colour_count>2) {
printf("%d colours in card\n",colour_count);
problems++;
}
}
}
printf("%d problem tiles out of %d total tiles\n",problems,total);
printf("%d with 3, %d with 4, %d with only one colour\n",threes,fours,ones);
printf("%d unique tiles\n",tile_count);
}
if (mode==3) {
// Output set of 16-colour sprites
int colour_count=0;
int colours[16];
fprintf(stderr,"Scanning colour palette...\n");
for (y=0; y<height; y++) {
for (x=0; x<width; x++) {
int i;
png_byte* row = row_pointers[y];
png_byte* ptr = &(row[x*multiplier]);
int r=ptr[0], g=ptr[1],b=ptr[2]; // , a=ptr[3];
int c=r+256*g+65536*b;
for(i=0;i<colour_count;i++) if (c==colours[i]) break;
if (i==colour_count) {
if (colour_count>=16) {
fprintf(stderr,"Too many colours. Image must be 16-colours or less.\n");
exit(-1);
}
colours[colour_count++]=c;
}
}
}
fprintf(stderr,"%d unique colours found.\n",colour_count);
// Avoid colour 0 if we can, to keep it for transparency
int colour_offset=0;
// if (colour_count<16) colour_offset=1;
int bytes_per_sprite=8*(height>>1);
while (bytes_per_sprite%64) bytes_per_sprite++;
fprintf(stderr,"Sprites are %d ($%02X) pixels high, and will occupy %d bytes (%d VIC-II sprite slots) each.\n",
height/2,height/2,bytes_per_sprite,bytes_per_sprite/64);
// Output palette
unsigned char red[16];
unsigned char green[16];
unsigned char blue[16];
int i;
for(i=0;i<16;i++)
{
int idx=i;
if (colour_offset) idx+=colour_offset;
if (idx>=16) idx-=16;
red[idx]=nyblswap(colours[i]&0xff);
green[idx]=nyblswap((colours[i]>>8)&0xff);
blue[idx]=nyblswap((colours[i]>>16)&0xff);
}
// Write magic string, height of sprites in pixels,
// and the number of sprites, and how many slots per
// sprite
fwrite("M65SPRITE16",12,1,outfile);
fputc(height/2,outfile);
fputc(width/32,outfile);
fputc(bytes_per_sprite&0xff,outfile);
fputc(bytes_per_sprite>>8,outfile);
fwrite(red,16,1,outfile);
fwrite(green,16,1,outfile);
fwrite(blue,16,1,outfile);
// Output pixels
for (x=0; x<width; x+=32) {
int bytes_written=0;
int xx;
fprintf(stderr,"Writing sprite %d\n",x/32);
for (y=0; y<height; y+=2) {
for(xx=x;xx<(x+32);xx+=4) {
unsigned char byte;
unsigned char p1,p2;
int i;
png_byte* row = row_pointers[y];
png_byte* ptr = &(row[(xx+0)*multiplier]);
int r=ptr[0], g=ptr[1],b=ptr[2]; // , a=ptr[3];
int c=r+256*g+65536*b;
for(i=0;i<colour_count;i++) if (c==colours[i]) break;
p1=i;
ptr = &(row[(xx+2)*multiplier]);
r=ptr[0]; g=ptr[1]; b=ptr[2]; // , a=ptr[3];
c=r+256*g+65536*b;
for(i=0;i<colour_count;i++) if (c==colours[i]) break;
p2=i;
byte=(p1<<4)+p2;
fputc(byte,outfile);
bytes_written++;
}
}
while(bytes_written<bytes_per_sprite) {
fputc(0x00,outfile);
bytes_written++;
}
}
}
}
/* ============================================================= */
int main(int argc, char **argv)
{
if (argc != 4) {
fprintf(stderr,"Usage: program_name <logo|charrom|hires|sprite16> <file_in> <file_out>\n");
exit(-1);
}
int mode=-1;
if (!strcasecmp("logo",argv[1])) mode=0;
if (!strcasecmp("charrom",argv[1])) mode=1;
if (!strcasecmp("hires",argv[1])) mode=2;
if (!strcasecmp("sprite16",argv[1])) mode=3;
if (mode==-1) {
fprintf(stderr,"Usage: program_name <logo|charrom|hires|sprite16> <file_in> <file_out>\n");
exit(-1);
}
printf("argv[0]=%s\n", argv[0]);
printf("argv[1]=%s\n", argv[1]);
printf("argv[2]=%s\n", argv[2]);
printf("argv[3]=%s\n", argv[3]);
printf("Reading %s\n",argv[2]);
read_png_file(argv[2]);
printf("Processing with mode=%d and output=%s\n", mode, argv[3]);
process_file(mode,argv[3]);
printf("done\n");
if (infile != NULL) {
fclose(infile);
infile = NULL;
}
if (outfile != NULL) {
fclose(outfile);
outfile = NULL;
}
return 0;
}
/* ============================================================= */
-615
View File
@@ -1,615 +0,0 @@
//
// Utility to prepare a release with automatically
// assigned version numbers
//
#include "common.h"
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <algorithm>
#include <cstring>
#include <fstream>
#include <ios>
#include <list>
#include <vector>
typedef struct
{
std::string romTypeName;
size_t fileSize;
size_t signatureOffset1; // offset to 'OR' string
size_t signatureOffset2; // offset to 'OR' string
size_t idBasicOffset; // offset to BASIC identification string
size_t idKernalOffset; // offset to KERNAL identification byte
} ROMTypeDescriptionEntry;
const std::vector<ROMTypeDescriptionEntry> ROM_DEFINITIONS =
{
{
"BASIC",
8192, // file size
0x1F52, // signature offset 1
0, // signature offset 2
0x0004, // BASIC ID offset
0 // KERNAL ID offset
},
{
"KERNAL",
8192, // file size
0x04B9, // signature offset 1
0, // signature offset 2
0, // BASIC ID offset
0x1F80 // KERNAL ID offset
},
{
"MEGA65",
128 * 1024, // file size
0xBF52, // signature offset 1
0xE4B9, // signature offset 2
0xA004, // BASIC ID offset
0xFF80 // KERNAL ID offset
},
};
const uint32_t MAX_FILE_SIZE = 128 * 1024;
const std::vector<uint8_t> STR_MEGABAS = { 0x4D, 0x45, 0x47, 0x41, 0x42, 0x41, 0x53, 0x32 };
const std::vector<uint8_t> STR_OR = { 0x4F, 0x52 };
const std::vector<uint8_t> STR_SNAPSHOT = { 0x28, 0x44, 0x45, 0x56, 0x45, 0x4C, 0x20, 0x53,
0x4E, 0x41, 0x50, 0x53, 0x48, 0x4F, 0x54, 0x29,
0x00 };
const std::string STR_DEV = "DEV.";
//
// Command and enviroment line settings
//
std::string CMD_inDir = "./build";
std::string CMD_outDir = "./bin";
std::list<std::string> CMD_fileList;
std::string ENV_developerId;
std::string ENV_dateString;
//
// Common helper functions
//
void printUsage()
{
std::cout << "\n" <<
"usage: release [-i <input (build) directory>] [-o <output (release) directory>]" << "\n" <<
" <file list>" << "\n\n";
}
//
// Class definitions
//
class ROMFile
{
public:
ROMFile(const std::string &baseFileName);
void embedRevisionStr(const std::string &newRevisionStr);
void save();
const ROMTypeDescriptionEntry *descPtr;
bool sameContent;
bool sameDeveloper;
bool sameDate; // only set if sameDeveloper
uint8_t oldDailyRelId; // only set if sameDeveloper and sameDate
private:
void readSrcFile();
void readDstFile();
void recognizeSrcFile();
void dropUnmatchingDst();
void analyzeContent();
std::string baseFileName;
std::vector<uint8_t> srcFileContent;
std::vector<uint8_t> dstFileContent;
};
//
// Global variables
//
size_t GLOBAL_maxFileNameLen = 0;
std::list<ROMFile> GLOBAL_ROMFiles;
//
// Top-level functions
//
void parseCommandLine(int argc, char **argv)
{
int opt;
// Retrieve command line options
while ((opt = getopt(argc, argv, "i:o:")) != -1)
{
switch(opt)
{
case 'i': CMD_inDir = optarg; break;
case 'o': CMD_outDir = optarg; break;
default: printUsage(); ERROR();
}
}
// Retrieve file/directory list
for (int idx = optind; idx < argc; idx++)
{
CMD_fileList.push_back(argv[idx]);
}
if (CMD_fileList.empty()) { printUsage(); ERROR("empty file list"); }
}
void getDeveloperId()
{
const char* id = getenv("OPEN_ROMS_DEVELOPER_ID");
// Check whether the developer ID is suitable
if (id == nullptr)
{
ERROR("environment variable OPEN_ROMS_DEVELOPER_ID not set");
}
ENV_developerId = id;
if (ENV_developerId.length() < 2 || ENV_developerId.length() > 3)
{
ERROR("OPEN_ROMS_DEVELOPER_ID has to be 2 or 3 characters long");
}
for (const char &c : ENV_developerId)
{
if (!(c >= 'A' && c <= 'Z'))
{
ERROR("OPEN_ROMS_DEVELOPER_ID can only contain uppercase letters");
}
}
}
void getDate()
{
time_t t = time(NULL);
struct tm tm = *localtime(&t);
const std::string year = std::to_string(tm.tm_year % 100);
const std::string month = std::to_string(tm.tm_mon + 1);
const std::string day = std::to_string(tm.tm_mday);
const std::string zero = "0";
ENV_dateString = (year.size() == 2 ? year : zero + year) +
(month.size() == 2 ? month : zero + month) +
(day.size() == 2 ? day : zero + day);
if (ENV_dateString.size() != 6) ERROR("could not retrieve date");
}
void printBanner()
{
printBannerLineTop();
std::cout << "// Preparing ROM set release" << "\n";
printBannerLineBottom();
}
void readFiles()
{
for (auto &fileName : CMD_fileList) GLOBAL_ROMFiles.push_back(ROMFile(fileName));
}
void decideIfUpdate()
{
// Check if we have any file at all
if (GLOBAL_ROMFiles.empty())
{
ERROR("no files to update");
}
// Check if we have at least one file with changed content
bool sameContent = true;
for (auto &file : GLOBAL_ROMFiles)
{
if (!file.sameContent)
{
sameContent = false;
break;
}
}
if (sameContent)
{
ERROR("same content in all the files");
}
}
void saveFiles()
{
// Determine a daily revision sequence ID
uint8_t relId = 0;
for (auto &file : GLOBAL_ROMFiles)
{
if (file.sameDeveloper && file.sameDate)
{
relId = std::max(file.oldDailyRelId, relId);
}
}
relId++;
if (relId > 9)
{
ERROR("too many releases this day");
}
// Create a new revision string
const std::string newRevStr = STR_DEV + ENV_dateString + "." + ENV_developerId + "." + std::to_string(relId);
for (auto &file : GLOBAL_ROMFiles)
{
file.embedRevisionStr(newRevStr);
}
std::cout << "Files for release '" << newRevStr << "':\n\n";
// Save the files
for (auto &file : GLOBAL_ROMFiles)
{
file.save();
}
}
//
// Main function
//
int main(int argc, char **argv)
{
parseCommandLine(argc, argv);
getDeveloperId();
getDate();
printBanner();
readFiles();
decideIfUpdate();
saveFiles();
std::cout << "\n";
return 0;
}
//
// Class 'ROMFile'
//
ROMFile::ROMFile(const std::string &baseFileName) :
descPtr(nullptr),
sameContent(false),
sameDeveloper(false),
sameDate(false),
oldDailyRelId(0),
baseFileName(baseFileName)
{
GLOBAL_maxFileNameLen = std::max(GLOBAL_maxFileNameLen, baseFileName.length());
readSrcFile();
readDstFile();
recognizeSrcFile();
dropUnmatchingDst();
analyzeContent();
}
void ROMFile::readSrcFile()
{
// Read the source file
const std::string srcFileNamePath = CMD_inDir + DIR_SEPARATOR + baseFileName;
std::ifstream srcFile;
srcFile.open(srcFileNamePath, std::ios::in | std::ios::binary | std::ios::ate);
if (!srcFile.good()) ERROR(std::string("unable to open ROM file '") + srcFileNamePath + "'");
auto srcSize = srcFile.tellg();
if (srcSize > MAX_FILE_SIZE) ERROR(std::string("incorrect size of ROM file '") + srcFileNamePath + "'");
srcFileContent.resize(srcSize);
srcFile.seekg (0, srcFile.beg);
srcFile.read((char *) srcFileContent.data(), srcFileContent.size());
if (!srcFile.good()) ERROR(std::string("unable to read ROM file '") + srcFileNamePath + "'");
srcFile.close();
}
void ROMFile::readDstFile()
{
// Read the destination file (if exists and it's size matches)
const std::string dstFileNamePath = CMD_outDir + DIR_SEPARATOR + baseFileName;
std::ifstream dstFile;
dstFile.open(dstFileNamePath, std::ios::in | std::ios::binary | std::ios::ate);
bool continueReading = dstFile.good();
if (continueReading && dstFile.tellg() != unsigned(srcFileContent.size())) continueReading = false;
if (continueReading)
{
dstFileContent.resize(srcFileContent.size());
dstFile.seekg (0, dstFile.beg);
dstFile.read((char *) dstFileContent.data(), dstFileContent.size());
}
if (!dstFile.good()) dstFileContent.clear();
dstFile.close();
}
void ROMFile::recognizeSrcFile()
{
// Recognize source file type
auto checkSignature = [this](size_t signatureOffset) -> bool
{
if (signatureOffset == 0)
{
return true;
}
if (memcmp(&srcFileContent[signatureOffset], STR_OR.data(), STR_OR.size()) != 0)
{
return false;
}
auto snapshotStrOffset = signatureOffset + STR_OR.size() + 1;
if (memcmp(&srcFileContent[snapshotStrOffset], STR_SNAPSHOT.data(), STR_SNAPSHOT.size()) != 0)
{
return false;
}
auto zeroByteoffset = snapshotStrOffset + 0x10;
if (srcFileContent[zeroByteoffset] != 0)
{
return false;
}
return true;
};
for (auto &SPEC : ROM_DEFINITIONS)
{
if (srcFileContent.size() != SPEC.fileSize) continue;
if ((SPEC.idBasicOffset != 0) &&
memcmp(&srcFileContent[SPEC.idBasicOffset], STR_MEGABAS.data(), STR_MEGABAS.size()) != 0)
{
continue;
}
if ((SPEC.idKernalOffset != 0) && srcFileContent[SPEC.idKernalOffset] != 0xF0)
{
continue;
}
if (!checkSignature(SPEC.signatureOffset1) || !checkSignature(SPEC.signatureOffset2))
{
continue;
}
descPtr = &SPEC;
break;
}
if (descPtr == nullptr)
{
ERROR(std::string("'") + CMD_inDir + DIR_SEPARATOR + baseFileName +
"' is not a supported unversioned Open ROMs image");
}
}
void ROMFile::dropUnmatchingDst()
{
// Drop unmatching destination file data
if (!dstFileContent.empty() && (descPtr->idBasicOffset != 0) &&
memcmp(&dstFileContent[descPtr->idBasicOffset], STR_MEGABAS.data(), STR_MEGABAS.size()) != 0)
{
dstFileContent.clear();
}
if (!dstFileContent.empty() && (descPtr->idKernalOffset != 0) &&
dstFileContent[descPtr->idKernalOffset] != 0xF0)
{
dstFileContent.clear();
}
if (!dstFileContent.empty() && (descPtr->signatureOffset1 != 0) &&
memcmp(&dstFileContent[descPtr->signatureOffset1], STR_OR.data(), STR_OR.size()) != 0)
{
dstFileContent.clear();
}
if (!dstFileContent.empty() && (descPtr->signatureOffset2 != 0) &&
memcmp(&dstFileContent[descPtr->signatureOffset2], STR_OR.data(), STR_OR.size()) != 0)
{
dstFileContent.clear();
}
}
void ROMFile::analyzeContent()
{
auto revOffset1 = (descPtr->signatureOffset1 != 0) ? (descPtr->signatureOffset1 + 3) : 0;
auto revOffset2 = (descPtr->signatureOffset2 != 0) ? (descPtr->signatureOffset2 + 3) : 0;
if (revOffset1 == 0)
{
revOffset1 = revOffset2;
revOffset2 = 0;
}
if (revOffset1 == 0)
{
return;
}
if (dstFileContent.empty())
{
return;
}
// If two ID strings are present, they should be the same
if (0 != revOffset1 && 0 != revOffset2 &&
memcmp(&dstFileContent[revOffset1], &dstFileContent[revOffset2], 0x10) != 0)
{
return;
}
// Check if meaningful file content is really different
sameContent = true;
for (uint32_t idx = 0; idx < descPtr->fileSize; idx++)
{
if (revOffset1 != 0 && idx - revOffset1 < 0x10)
{
continue;
}
if (revOffset2 != 0 && idx - revOffset2 < 0x10)
{
continue;
}
if (srcFileContent[idx] != dstFileContent[idx])
{
sameContent = false;
break;
}
}
// Determine expected length of developer ID
uint8_t devIdLen = ENV_developerId.length();
// Determine if old version is a devel version compatible with this tool
if (memcmp(&dstFileContent[revOffset1], STR_DEV.data(), STR_DEV.size()) != 0)
{
return;
}
uint16_t idxDate = revOffset1 + STR_DEV.size();
uint16_t idxLimit = idxDate + ENV_dateString.length();
uint16_t idx = idxDate;
for (; idx < idxLimit; idx++)
{
if (dstFileContent[idx] < '0' || dstFileContent[idx] > '9')
{
return;
}
}
if (dstFileContent[idx] != '.' ||
dstFileContent[idx + devIdLen + 1] != '.' ||
dstFileContent[idx + devIdLen + 2] < '0' ||
dstFileContent[idx + devIdLen + 2] > '9' ||
dstFileContent[idx + devIdLen + 3] != 0)
{
return;
}
// Retrieve release data
if (memcmp(&dstFileContent[idx + 1], ENV_developerId.data(), ENV_developerId.size()) != 0)
{
return; // different developer
}
sameDeveloper = true;
if (memcmp(&dstFileContent[idxDate], ENV_dateString.data(), ENV_dateString.size()) != 0)
{
return; // different date
}
sameDate = true;
oldDailyRelId = dstFileContent[idx + devIdLen + 2] - '0';
}
void ROMFile::embedRevisionStr(const std::string &newRevisionStr)
{
std::string revStr = newRevisionStr;
while (revStr.length() < 16) revStr = revStr + '\0';
if (descPtr->signatureOffset1 != 0)
{
memcpy(&srcFileContent[descPtr->signatureOffset1 + 3],
revStr.data(), revStr.size());
}
if (descPtr->signatureOffset2 != 0)
{
memcpy(&srcFileContent[descPtr->signatureOffset2 + 3],
revStr.data(), revStr.size());
}
}
void ROMFile::save()
{
const std::string outFileNamePath = CMD_outDir + DIR_SEPARATOR + baseFileName;
// Print out file name and metadata
std::string spacing;
spacing.resize(GLOBAL_maxFileNameLen + 4 - baseFileName.length(), ' ');
std::cout << " " << outFileNamePath << spacing << descPtr->romTypeName;
if (sameContent)
{
spacing.resize(10 - descPtr->romTypeName.length());
std::cout << spacing << "(unchanged)";
}
std::cout << "\n";
std::ofstream outFile;
outFile.open(outFileNamePath, std::ios::out | std::ios::binary | std::ios::trunc);
if (!outFile.good()) ERROR(std::string("unable to open output file '") + outFileNamePath + "'");
outFile.write((char *) srcFileContent.data(), srcFileContent.size());
if (!outFile.good()) ERROR(std::string("unable to write file '") + outFileNamePath + "'");
outFile.close();
}
-279
View File
@@ -1,279 +0,0 @@
/*
Simple program to find matching strings between two binaries
and report on the statistics of their lengths, and show the
longer ones. Used to defend against copyright infringement
claims.
*/
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#define MAX_SIZE 65536
int matches[MAX_SIZE];
int phase_mask[MAX_SIZE+MAX_SIZE];
int main(int argc,char **argv)
{
int verbose=0;
if (argc<3) {
fprintf(stderr,"usage: similarity <file1> <file2> [verbose]\n");
exit(-1);
}
if (argc==4) {
if (!strcmp(argv[3],"verbose")) verbose=1;
else {
fprintf(stderr,"Unrecognised directive.\n");
exit(-1);
}
}
for(int i=0;i<MAX_SIZE;i++) matches[i]=0;
bzero(phase_mask,sizeof(phase_mask));
unsigned char *f1,*f2;
int s1,s2;
struct stat st;
int fd;
fd = open(argv[1], O_RDONLY, 0);
if (fd==-1) {
fprintf(stderr,"Could not read '%s'\n",argv[1]);
exit(-1);
}
stat(argv[1], &st);
s1=st.st_size;
f1 = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE | MAP_POPULATE, fd, 0);
if (f1== MAP_FAILED) {
fprintf(stderr,"Could not mmap '%s'\n",argv[1]);
exit(-1);
}
fd = open(argv[2], O_RDONLY, 0);
if (fd==-1) {
fprintf(stderr,"Could not read '%s'\n",argv[2]);
exit(-1);
}
stat(argv[2], &st);
s2=st.st_size;
f2 = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE | MAP_POPULATE, fd, 0);
if (f2 == MAP_FAILED) {
fprintf(stderr,"Could not mmap '%s'\n",argv[2]);
exit(-1);
}
fprintf(stderr,"Searching files for similarities...\n");
for(int i=0;i<s1;i++) {
for(int j=0;j<s2;j++) {
int k,l;
int phase=s2+i-j;
if (phase<0) {
fprintf(stderr,"Phase < 0 : %04x %04x -> %d\n",i,j,phase);
exit(-1);
}
if (phase>=(MAX_SIZE+MAX_SIZE)) {
fprintf(stderr,"Phase too large : %04x %04x -> %d\n",i,j,phase);
exit(-1);
}
if (phase_mask[phase]) {
phase_mask[phase]--;
continue;
}
for(k=0;((i+k)<s1)&&((j+k)<s2);k++)
if (f1[i+k]!=f2[j+k]) break;
// When we find a match, we should then mask off this region,
// so that we don't find all the sub-sets of the matches.
// e.g. matching BASIC at $100 will result in a match of ASIC at $101
// and SIC at $102 etc. These are all the same match, so should be
// suppressed.
phase_mask[phase]=k;
// Ignore matches that are all the same byte
for(l=1;l<k;l++) if (f1[i+l]!=f1[i]) break;
if (l<k) {
// Ignore matches of no more than 3 bytes,
// since that is the max length of a 6502 instruction
int single_instruction=0;
if (k==5) {
if (f1[i]==0xa9&&f1[i+2]==0x8d) break; // LDA #$xx / STA $nnnn
}
if (k==4) {
if (f1[i]==0xa9&&f1[i+2]==0x85) break; // LDA #$xx / STA $nn
}
if (k==3) {
// Reject some very common instruction fragments
// Branch followed by any opcode
if (f1[i]==0xd0) break;
if (f1[i]==0xf0) break;
if (f1[i]==0xb0) break;
if (f1[i]==0x90) break;
if (f1[i]==0x10) break;
// Any single byte followed by a branch
if (f1[i+1]==0xd0) break;
if (f1[i+1]==0xf0) break;
if (f1[i+1]==0xb0) break;
if (f1[i+1]==0x90) break;
if (f1[i+1]==0x10) break;
// Any 2-byte opcode followed by a branch
if (f1[i+2]==0xd0) break;
if (f1[i+2]==0xf0) break;
if (f1[i+2]==0xb0) break;
if (f1[i+2]==0x90) break;
if (f1[i+2]==0x10) break;
// Common 2-byte instructions followed by
// any single byte are very common and not
// copyrightable.
if (f1[i]==0x85) break;
if (f1[i]==0xa2) break; // LDX #$xx
if (f1[i]==0xa9) break; // LDA #$xx
if (f1[i]==0xa5) break; // LDA $xx
if (f1[i]==0xa0) break; // LDY #$xx
if (f1[i]==0x69) break; // ORA #$xx ?
if (f1[i]==0xc9) break; // CMP #$xx
if (f1[i]==0x91) break; // STA ($nn),Y
// Similarly for random byte before such instructions
if (f1[i+1]==0x85) break;
if (f1[i+1]==0xa9) break; // LDA #$xx
if (f1[i+1]==0xa2) break; // LDX $xx
if (f1[i+1]==0xa5) break; // LDA $xx
if (f1[i+1]==0xa0) break; // LDY #$xx
if (f1[i+1]==0x69) break; // ADC #$xx
if (f1[i+1]==0xc9) break; // CMP #$xx
// Filter out all 3 byte instructions
switch(f1[i]) {
case 0x0C: // TSB $nnnn
case 0x0D: // ORA $nnnn
case 0x0E: // ASL $nnnn
case 0x19: // ORA $nnnn,Y
case 0x1C: // TRB $nnnn
case 0x1D: // ORA $nnnn,X
case 0x1E: // ASL $nnnn,X
case 0x20: // JSR $nnnn
case 0x22: // JSR ($nnnn)
case 0x23: // JSR ($nnnn,X)
case 0x2C: // BIT $nnnn
case 0x2D: // AND $nnnn
case 0x2E: // ROL $nnnn
case 0x39: // AND $nnnn,Y
case 0x3C: // BIT $nnnn,X
case 0x3D: // AND $nnnn,X
case 0x3E: // ROL $nnnn,X
case 0x4C: // JMP $nnnn
case 0x4D: // EOR $nnnn
case 0x4E: // LSR $nnnn
case 0x59: // EOR $nnnn,Y
case 0x5D: // EOR $nnnn,X
case 0x5E: // LSR $nnnn,X
case 0x6C: // JMP ($nnnn)
case 0x6D: // ADC $nnnn
case 0x6E: // ROR $nnnn
case 0x79: // ADC $nnnn,Y
case 0x7C: // JMP ($nnnn,X)
case 0x7D: // ADC $nnnn,X
case 0x7E: // ROR $nnnn,X
case 0x8B: // STY $nnnn,X
case 0x8C: // STY $nnnn
case 0x8D: // STA $nnnn
case 0x8E: // STX $nnnn
case 0x99: // STA $nnnn,Y
case 0x9B: // STX $nnnn,Y
case 0x9C: // STZ $nnnn
case 0x9D: // STA $nnnn,X
case 0x9E: // STZ $nnnn,X
case 0xAB: // LDZ $nnnn
case 0xAC: // LDY $nnnn
case 0xAD: // LDA $nnnn
case 0xAE: // LDX $nnnn
case 0xB9: // LDA $nnnn,Y
case 0xBB: // LDZ $nnnn,X
case 0xBC: // LDY $nnnn,X
case 0xBD: // LDA $nnnn,X
case 0xBE: // LDX $nnnn,Y
case 0xCB: // ASW $nnnn
case 0xCC: // CPY $nnnn
case 0xCD: // CMP $nnnn
case 0xCE: // DEC $nnnn
case 0xD9: // CMP $nnnn,Y
case 0xDC: // CPZ $nnnn
case 0xDD: // CMP $nnnn,X
case 0xDE: // DEC $nnnn,X
case 0xEB: // ROW $nnnn
case 0xEC: // CPX $nnnn
case 0xED: // SBC $nnnn
case 0xEE: // INC $nnnn
case 0xF4: // PHW #$nnnn
case 0xF9: // SBC $nnnn,Y
case 0xFC: // PHW $nnnn
case 0xFD: // SBC $nnnn,X
case 0xFE: // INC $nnnn,X
single_instruction=1;
break;
}
}
if (single_instruction) break;
if (k>2) {
char similarity_name[MAX_SIZE];
snprintf(similarity_name,MAX_SIZE,"strings/");
int offset=strlen(similarity_name);
for(int b=0;b<k;b++) {
snprintf(&similarity_name[offset],MAX_SIZE-offset,"%02X",f1[i+b]);
offset+=2;
}
FILE *f=fopen(similarity_name,"r");
if (f) {
// Get explanation why this match is irrelevant
char line[1024]; line[0]=0;
fgets(line,1024,f);
while (line[0]&&line[strlen(line)-1]=='\r') line[strlen(line)-1]=0;
while (line[0]&&line[strlen(line)-1]=='\n') line[strlen(line)-1]=0;
if (verbose)
fprintf(stderr,"Ignoring $%04X = $%04X + %d (%s)\n",
i,j,k,line);
break;
fclose(f);
}
// Otherwise, the match is unexplained.
matches[k]++;
// Display particularly long matches
printf("$%04X = $%04X :",i,j);
for(int b=0;b<k;b++) {
printf(" %02X",f1[i+b]);
}
printf("\n");
}
}
}
}
for(int i=0;i<MAX_SIZE;i++) {
if (matches[i])
printf("%6d unexplained matches of %d bytes\n",matches[i],i);
}
return 0;
}