Read Joystick

From Retrosoftware

(Difference between revisions)
Jump to: navigation, search
(New page: = BASIC Tokeniser/Detokeniser = Some C++ (but trivial to convert into C) routines that convert to/from a memory dump of any Acorn machine running the BASIC II ROM and an ASCII file of the...)
(BASIC Tokeniser/Detokeniser)
Line 1: Line 1:
-
= BASIC Tokeniser/Detokeniser =
+
= Read the Joystick =
-
Some C++ (but trivial to convert into C) routines that convert to/from a memory dump of any Acorn machine running the BASIC II ROM and an ASCII file of the BASIC program. Originally taken from my ElectrEm emulator.
+
Uses OSBYTE &80:
-
Routines of interest are ''bool ImportBASIC(char *Filename, Uint8 *Mem)'' and ''bool ExportBASIC(char *Filename, Uint8 *Memory)''. ''SetupBASICTables()'' must be called once before either routine is used.
+
X = 0 Read Fire Buttons in X
-
== The Code ==
+
X = 1 Read X Axis in Y;X (Returns 0- 65535 Right<->Left)
-
<pre>/*
+
X = 2 Read Y Axis in Y;X (returns 0- 65535 Down<->Up)
-
BASIC.cpp
 
-
=========
 
-
This is the code to take an Electron memory dump and then save
+
== The Code ==
-
the current BASIC file to an ASCII file, or alternatively to
+
-
open an ASCII file, tokenise it and put it into memory
+
-
 
+
-
*/
+
-
 
+
-
#include "BASIC.h"
+
-
#include <stdio.h>
+
-
 
+
-
/*
+
-
 
+
-
KeyWord table - used by both the exporter and the importer to convert
+
-
between the plain text version of a BASIC program and the format used
+
-
internally by the BASIC ROM
+
-
 
+
-
Information taken from pages 41-43 of the BASIC ROM User Guide,
+
-
BASIC 2 ROM assumed throughout
+
-
 
+
-
*/
+
-
 
+
-
struct KeyWord
+
-
{
+
-
char *Name;
+
-
Uint8 Flags;
+
-
unsigned int StrLen;
+
-
struct KeyWord *Next;
+
-
};
+
-
 
+
-
struct KeyWord KeyWordTable[0x80] =
+
-
{
+
-
/* 0x80 */
+
-
{"AND", 0x00}, {"DIV", 0x00}, {"EOR", 0x00}, {"MOD", 0x00},
+
-
{"OR", 0x00}, {"ERROR", 0x04}, {"LINE", 0x00}, {"OFF", 0x00},
+
-
 
+
-
/* 0x88 */
+
-
{"STEP", 0x00}, {"SPC", 0x00}, {"TAB(", 0x00}, {"ELSE", 0x14},
+
-
{"THEN", 0x14}, {"", 0x00}, {"OPENIN",0x00}, {"PTR", 0x43},
+
-
 
+
-
/* 0x90 */
+
-
{"PAGE", 0x43}, {"TIME", 0x43}, {"LOMEM", 0x43}, {"HIMEM", 0x43},
+
-
{"ABS", 0x00}, {"ACS", 0x00}, {"ADVAL", 0x00}, {"ASC", 0x00},
+
-
 
+
-
/* 0x98 */
+
-
{"ASN", 0x00}, {"ATN", 0x00}, {"BGET", 0x01}, {"COS", 0x00},
+
-
{"COUNT", 0x01}, {"DEG", 0x00}, {"ERL", 0x01}, {"ERR", 0x01},
+
-
 
+
-
/* 0xa0 */
+
-
{"EVAL", 0x00}, {"EXP", 0x00}, {"EXT", 0x01}, {"FALSE", 0x01},
+
-
{"FN", 0x08}, {"GET", 0x00}, {"INKEY", 0x00}, {"INSTR(", 0x00},
+
-
 
+
-
/* 0xa8 */
+
-
{"INT", 0x00}, {"LEN", 0x00}, {"LN", 0x00}, {"LOG", 0x00},
+
-
{"NOT", 0x00}, {"OPENUP",0x00}, {"OPENOUT",0x00}, {"PI", 0x01},
+
-
 
+
-
/* 0xb0 */
+
-
{"POINT(", 0x00}, {"POS", 0x01}, {"RAD", 0x00}, {"RND", 0x01},
+
-
{"SGN", 0x00}, {"SIN", 0x00}, {"SQR", 0x00}, {"TAN", 0x00},
+
-
 
+
-
/* 0xb8 */
+
-
{"TO", 0x00}, {"TRUE", 0x01}, {"USR", 0x00}, {"VAL", 0x00},
+
-
{"VPOS", 0x01}, {"CHR$", 0x00}, {"GET$", 0x00}, {"INKEY$", 0x00},
+
-
 
+
-
/* 0xc0 */
+
-
{"LEFT$(", 0x00}, {"MID$(", 0x00}, {"RIGHT$(",0x00}, {"STR$", 0x00},
+
-
{"STRING$(",0x00}, {"EOF", 0x01}, {"AUTO", 0x10}, {"DELETE", 0x10},
+
-
 
+
-
/* 0xc8 */
+
-
{"LOAD", 0x02}, {"LIST", 0x10}, {"NEW", 0x01}, {"OLD", 0x01},
+
-
{"RENUMBER",0x10}, {"SAVE", 0x02}, {"", 0x00}, {"PTR", 0x00},
+
-
 
+
-
/* 0xd0 */
+
-
{"PAGE", 0x00}, {"TIME", 0x01}, {"LOMEM", 0x00}, {"HIMEM", 0x00},
+
-
{"SOUND", 0x02}, {"BPUT", 0x03}, {"CALL", 0x02}, {"CHAIN", 0x02},
+
-
 
+
-
/* 0xd8 */
+
-
{"CLEAR", 0x01}, {"CLOSE", 0x03}, {"CLG", 0x01}, {"CLS", 0x01},
+
-
{"DATA", 0x20}, {"DEF", 0x00}, {"DIM", 0x02}, {"DRAW", 0x02},
+
-
 
+
-
/* 0xe0 */
+
-
{"END", 0x01}, {"ENDPROC",0x01}, {"ENVELOPE",0x02}, {"FOR", 0x02},
+
-
{"GOSUB", 0x12}, {"GOTO", 0x12}, {"GCOL", 0x02}, {"IF", 0x02},
+
-
 
+
-
/* 0xe8 */
+
-
{"INPUT", 0x02}, {"LET", 0x04}, {"LOCAL", 0x02}, {"MODE", 0x02},
+
-
{"MOVE", 0x02}, {"NEXT", 0x02}, {"ON", 0x02}, {"VDU", 0x02},
+
-
 
+
-
/* 0xf0 */
+
-
{"PLOT", 0x02}, {"PRINT", 0x02}, {"PROC", 0x0a}, {"READ", 0x02},
+
-
{"REM", 0x20}, {"REPEAT",0x00}, {"REPORT",0x01}, {"RESTORE",0x12},
+
-
 
+
-
/* 0xf8 */
+
-
{"RETURN", 0x01}, {"RUN", 0x01}, {"STOP", 0x01}, {"COLOUR", 0x02},
+
-
{"TRACE", 0x12}, {"UNTIL", 0x02}, {"WIDTH", 0x02}, {"OSCLI", 0x02}
+
-
};
+
-
 
+
-
KeyWord *QuickTable[26*26];
+
-
 
+
-
/*
+
-
 
+
-
Setup function, to establish contents of QuickTable, store strlens, etc
+
-
 
+
-
*/
+
-
 
+
-
#define HashCode(str) (str[0] < 'A' || str[0] > 'Z' || str[1] < 'A' || str[1] > 'Z') ? 0 : ((str[0] - 'A')*26 + (str[1] - 'A'))
+
-
 
+
-
void SetupBASICTables()
+
-
{
+
-
/* set QuickTable to empty */
+
-
int c = 26*26;
+
-
while(c--)
+
-
QuickTable[c] = NULL;
+
-
 
+
-
/* go through tokens, store strlens & populate QuickTable */
+
-
for(c = 0; c < 0x80; c++)
+
-
{
+
-
if(KeyWordTable[c].StrLen = strlen(KeyWordTable[c].Name))
+
-
{
+
-
/* reject any symbols that have already appeared 0x40 places earlier in the table */
+
-
if(c < 0x40 || strcmp(KeyWordTable[c].Name, KeyWordTable[c - 0x40].Name))
+
-
{
+
-
int Code = HashCode(KeyWordTable[c].Name);
+
-
KeyWord **InsertPointer = &QuickTable[Code];
+
-
while(*InsertPointer)
+
-
InsertPointer = &(*InsertPointer)->Next;
+
-
 
+
-
*InsertPointer = &KeyWordTable[c];
+
-
}
+
-
}
+
-
}
+
-
 
+
-
/*
+
-
 
+
-
Go through QuickTable, sorting each branch by string length
+
-
 
+
-
I'm an idiot, so I've used insertion sort!
+
-
 
+
-
*/
+
-
c = 26*26;
+
-
while(c--)
+
-
if(QuickTable[c] && QuickTable[c]->Next)
+
-
{
+
-
/* sort first by string length */
+
-
KeyWord **Check = &QuickTable[c];
+
-
unsigned int CurLength = (*Check)->StrLen;
+
-
Check = &(*Check)->Next;
+
-
while(*Check)
+
-
{
+
-
/* check if out of order */
+
-
if((*Check)->StrLen > CurLength)
+
-
{
+
-
/* unlink */
+
-
KeyWord *Takeout = *Check;
+
-
*Check = (*Check)->Next;
+
-
 
+
-
/* start at top of list, find correct insertion point */
+
-
KeyWord **InsertPoint = &QuickTable[c];
+
-
while((*InsertPoint)->StrLen >= Takeout->StrLen)
+
-
InsertPoint = &(*InsertPoint)->Next;
+
-
 
+
-
/* ...and insert */
+
-
Takeout->Next = *InsertPoint;
+
-
*InsertPoint = Takeout;
+
-
}
+
-
else
+
-
{
+
-
CurLength = (*Check)->StrLen;
+
-
Check = &(*Check)->Next;
+
-
}
+
-
}
+
-
}
+
-
}
+
-
 
+
-
/*
+
-
 
+
-
Little function to return an error string
+
-
 
+
-
*/
+
-
char *ErrorTable[] =
+
-
{
+
-
"",
+
-
"BASIC is not currently active",
+
-
"Unable to open file for input",
+
-
"Program too large",
+
-
"Unable to open file for output",
+
-
"Malformed BASIC program or not running BASIC",
+
-
"BASIC program appears to run past the end of RAM"
+
-
};
+
-
char DynamicErrorText[256];
+
-
 
+
-
int ErrorNum;
+
-
 
+
-
char *GetBASICError()
+
-
{
+
-
return ErrorNum >= 0 ? ErrorTable[ErrorNum] : DynamicErrorText;
+
-
}
+
-
 
+
-
/*
+
-
 
+
-
Functions to export BASIC code, i.e. decode from tokenised form to plain text
+
-
 
+
-
*/
+
-
 
+
-
bool ExtractLine(FILE *output, Uint8 *Memory, Uint16 Addr, Uint8 LineL)
+
-
{
+
-
int LineLength = (int)LineL;
+
-
 
+
-
while(LineLength >= 0)
+
-
{
+
-
Uint8 ThisByte = Memory[Addr]; Addr++; LineLength--;
+
-
if(ThisByte >= 0x80)
+
-
{
+
-
if(ThisByte == 0x8d) // then we're about to see a tokenised line number
+
-
{
+
-
Uint16 LineNumber;
+
-
 
+
-
// decode weirdo tokenised line number format
+
-
LineNumber = Memory[Addr+1]&0x3f;
+
-
LineNumber |= (Memory[Addr+2]&0x3f) << 8;
+
-
LineNumber |= (Memory[Addr]&0x0c) << 12;
+
-
LineNumber |= (Memory[Addr]&0x30) << 2;
+
-
LineNumber ^= 0x4040;
+
-
 
+
-
Addr += 3;
+
-
LineLength -= 3;
+
-
 
+
-
fprintf(output, "%d", LineNumber);
+
-
}
+
-
else //ordinary keyword
+
-
{
+
-
fputs(KeyWordTable[ThisByte - 0x80].Name, output);
+
-
+
-
if(KeyWordTable[ThisByte - 0x80].Flags & 0x20)
+
-
{
+
-
//copy to end of line without interpreting tokens}
+
-
while(LineLength >= 0)
+
-
{
+
-
fputc(Memory[Addr], output); Addr++; LineLength--;
+
-
}
+
-
return true;
+
-
}
+
-
}
+
-
}
+
-
else
+
-
{
+
-
switch(ThisByte)
+
-
{
+
-
default: fputc(ThisByte, output); break;
+
-
case '"':
+
-
/* copy string literal... */
+
-
fputc('"', output);
+
-
while(Memory[Addr] != '"' && LineLength >= 0)
+
-
{
+
-
fputc(Memory[Addr], output);
+
-
Addr++; LineLength--;
+
-
}
+
-
if(Memory[Addr] == '"')
+
-
{
+
-
fputc(Memory[Addr], output);
+
-
Addr++; LineLength--;
+
-
}
+
-
break;
+
-
}
+
-
}
+
-
}
+
-
 
+
-
return (LineLength == -1) ? true : false;
+
-
}
+
-
 
+
-
bool ExportBASIC(char *Filename, Uint8 *Memory)
+
-
{
+
-
ErrorNum = 0;
+
-
FILE *output = fopen(Filename, "wt");
+
-
 
+
-
if(!output)
+
-
{
+
-
ErrorNum = 4;
+
-
return false;
+
-
}
+
-
 
+
-
/* get the value of PAGE‚ start reading BASIC code from there */
+
-
Uint16 Addr = Memory[0x18] << 8;
+
-
 
+
-
if(Addr >= 32768 - 4)
+
-
ErrorNum = 6;
+
-
 
+
-
while(!ErrorNum)
+
-
{
+
-
/* character here should be \r */
+
-
if(Memory[Addr] != 0x0d)
+
-
{
+
-
ErrorNum = 5;
+
-
break;
+
-
}
+
-
Addr++;
+
-
 
+
-
/* get line number, check if we've hit the end of BASIC */
+
-
Uint16 LineNumber;
+
-
LineNumber = (Memory[Addr] << 8) | Memory[Addr+1];
+
-
Addr += 2;
+
-
if(LineNumber & 0x8000) // if we've hit the end of the program, exit
+
-
break;
+
-
 
+
-
Uint8 LineLength = Memory[Addr]; Addr++;
+
-
 
+
-
if(Addr+LineLength >= 32768 - 4)
+
-
{
+
-
ErrorNum = 6;
+
-
break;
+
-
}
+
-
 
+
-
/* print line number */
+
-
fprintf(output, "%5d", LineNumber);
+
-
 
+
-
/* detokenise, etc */
+
-
if(!ExtractLine(output, Memory, Addr, LineLength - 4))
+
-
break;
+
-
 
+
-
/* add a newline */
+
-
fputc('\n', output);
+
-
 
+
-
/* should process line here, but chicken out */
+
-
Addr += LineLength - 4;
+
-
}
+
-
 
+
-
fclose(output);
+
-
 
+
-
return ErrorNum ? false : true;
+
-
}
+
-
 
+
-
/*
+
-
 
+
-
Functions to import BASIC code, i.e. tokenise from plain text
+
-
 
+
-
*/
+
-
 
+
-
#define AlphaNumeric(v)\
+
-
(\
+
-
(v >= 'a' && v <= 'z') ||\
+
-
(v >= 'A' && v <= 'Z') ||\
+
-
(v >= '0' && v <= '9')\
+
-
)
+
-
 
+
-
char IncomingBuffer[9];
+
-
Uint8 Token, NextChar;
+
-
unsigned int IncomingPointer;
+
-
FILE *inputfile;
+
-
bool EndOfFile, NumberStart;
+
-
unsigned int NumberValue, NumberLength;
+
-
int CurLine;
+
-
 
+
-
Uint8 *Memory;
+
-
Uint16 Addr;
+
-
 
+
-
inline bool WriteByte(Uint8 value)
+
-
{
+
-
if(Addr == 32768) {ErrorNum = 3; return false;}
+
-
Memory[Addr++] = value;
+
-
return true;
+
-
}
+
-
 
+
-
int my_fgetc(FILE *in)
+
-
{
+
-
int r;
+
-
do
+
-
{
+
-
r = fgetc(in);
+
-
}
+
-
while(r == '\r');
+
-
if(r == '\n') CurLine++;
+
-
return r;
+
-
}
+
-
 
+
-
void GetCharacter()
+
-
{
+
-
if(IncomingPointer == 8)
+
-
{
+
-
/* shift, load into position [8] */
+
-
int c = 0;
+
-
while(c < 8)
+
-
{
+
-
IncomingBuffer[c] = IncomingBuffer[c+1];
+
-
c++;
+
-
}
+
-
 
+
-
IncomingBuffer[8] = my_fgetc(inputfile);
+
-
}
+
-
else
+
-
{
+
-
IncomingBuffer[IncomingPointer] = my_fgetc(inputfile);
+
-
IncomingPointer++;
+
-
}
+
-
 
+
-
if(feof(inputfile)) //if we've hit feof then the last char isn't anything
+
-
{
+
-
IncomingPointer--;
+
-
if(!IncomingPointer)
+
-
{
+
-
EndOfFile = true;
+
-
return;
+
-
}
+
-
}
+
-
 
+
-
/* check for tokens, set flags accordingly. Be a bit dense about this for now! */
+
-
Token = IncomingBuffer[0];
+
-
int Code = HashCode(IncomingBuffer);
+
-
KeyWord *CheckPtr = QuickTable[Code];
+
-
 
+
-
while(CheckPtr)
+
-
{
+
-
if(IncomingPointer >= CheckPtr->StrLen && !strncmp(IncomingBuffer, CheckPtr->Name, CheckPtr->StrLen))
+
-
{
+
-
Token = (CheckPtr - KeyWordTable) + 0x80;
+
-
NextChar = IncomingBuffer[CheckPtr->StrLen];
+
-
break;
+
-
}
+
-
 
+
-
CheckPtr = CheckPtr->Next;
+
-
}
+
-
 
+
-
/* check if this is a number start */
+
-
NumberStart = false;
+
-
if(Token >= '0' && Token <= '9')
+
-
{
+
-
NumberStart = true;
+
-
char *end;
+
-
NumberValue = strtol(IncomingBuffer, &end, 10);
+
-
NumberLength = end - IncomingBuffer;
+
-
}
+
-
}
+
-
 
+
-
void EatCharacters(int n)
+
-
{
+
-
/* shift left n places, decrease IncomingPointer */
+
-
int c = 0;
+
-
while(c < 9-n)
+
-
{
+
-
IncomingBuffer[c] = IncomingBuffer[c+n];
+
-
c++;
+
-
}
+
-
IncomingPointer -= n;
+
-
IncomingBuffer[IncomingPointer] = '\0';
+
-
while(n--) /* this is a quick fix: it causes lots of unnecessary token searches... */
+
-
GetCharacter();
+
-
}
+
-
 
+
-
bool CopyStringLiteral()
+
-
{
+
-
// eat preceeding quote
+
-
WriteByte(IncomingBuffer[0]);
+
-
EatCharacters(1);
+
-
 
+
-
// don't tokenise anything until another quote is hit, keep eye out for things that may have gone wrong
+
-
while(!ErrorNum && !EndOfFile && IncomingBuffer[0] != '"' && IncomingBuffer[0] != '\n')
+
-
{
+
-
WriteByte(IncomingBuffer[0]);
+
-
EatCharacters(1);
+
-
}
+
-
 
+
-
if(IncomingBuffer[0] != '"') // stopped going for some reason other than a close quote
+
-
{
+
-
ErrorNum = -1;
+
-
sprintf(DynamicErrorText, "Malformed string literal on line %d", CurLine);
+
-
return false;
+
-
}
+
-
 
+
-
// eat proceeding quote
+
-
WriteByte(IncomingBuffer[0]);
+
-
EatCharacters(1);
+
-
 
+
-
return true;
+
-
}
+
-
 
+
-
bool DoLineNumberTokeniser()
+
-
{
+
-
while(!ErrorNum && !EndOfFile)
+
-
{
+
-
if(NumberStart)
+
-
{
+
-
// tokenise line number
+
-
Uint16 LineNumber = NumberValue ^ 0x4040;
+
-
 
+
-
WriteByte(0x8d);
+
-
 
+
-
WriteByte(((LineNumber&0xc0) >> 2) | ((LineNumber&0xc000) >> 12) | 0x40);
+
-
WriteByte((LineNumber&0x3f) | 0x40);
+
-
WriteByte(((LineNumber >> 8)&0x3f) | 0x40);
+
-
 
+
-
EatCharacters(NumberLength);
+
-
}
+
-
else
+
-
switch(Token)
+
-
{
+
-
// whitespace and commas do not cause this mode to exit
+
-
case ' ':
+
-
case ',':
+
-
WriteByte(Token);
+
-
EatCharacters(1);
+
-
break;
+
-
 
+
-
// hex numbers get through unscathed too
+
-
case '&':
+
-
WriteByte(Token);
+
-
EatCharacters(1);
+
-
 
+
-
while(
+
-
!ErrorNum &&
+
-
!EndOfFile &&
+
-
(
+
-
(IncomingBuffer[0] >= '0' && IncomingBuffer[0] <= '9') ||
+
-
(IncomingBuffer[0] >= 'A' && IncomingBuffer[0] <= 'F')
+
-
)
+
-
)
+
-
{
+
-
WriteByte(IncomingBuffer[0]);
+
-
EatCharacters(1);
+
-
}
+
-
break;
+
-
 
+
-
/* grab strings without tokenising numbers */
+
-
case '"':
+
-
if(!CopyStringLiteral())
+
-
return false;
+
-
break;
+
-
 
+
-
/* default action is to turn off line number tokenising and get back to normal */
+
-
default: return true;
+
-
}
+
-
}
+
-
return true;
+
-
}
+
-
 
+
-
bool EncodeLine()
+
-
{
+
-
bool StartOfStatement = true;
+
-
 
+
-
/* continue until we hit a '\n' or file ends */
+
-
while(!EndOfFile && Token != '\n' && !ErrorNum)
+
-
{
+
-
/* even if this looks like a keyword, it really isn't if the conditional flag is set & the next char is alphanumeric*/
+
-
if(
+
-
Token >= 0x80 &&
+
-
(KeyWordTable[Token - 0x80].Flags&1) &&
+
-
AlphaNumeric(NextChar)
+
-
)
+
-
Token = IncomingBuffer[0];
+
-
 
+
-
if(Token < 0x80) //if not a keyword token
+
-
{
+
-
switch(Token)
+
-
{
+
-
default: //default is dump character to memory
+
-
WriteByte(Token);
+
-
 
+
-
if(Token == ':') // a colon always switches the tokeniser back to "start of statement" mode
+
-
StartOfStatement = true;
+
-
 
+
-
// grab entire variables rather than allowing bits to be tokenised
+
-
if
+
-
(
+
-
(Token >= 'a' && Token <= 'z') ||
+
-
(Token >= 'A' && Token <= 'Z')
+
-
)
+
-
{
+
-
StartOfStatement = false;
+
-
EatCharacters(1);
+
-
while(AlphaNumeric(IncomingBuffer[0]))
+
-
{
+
-
WriteByte(IncomingBuffer[0]);
+
-
EatCharacters(1);
+
-
}
+
-
}
+
-
else
+
-
EatCharacters(1);
+
-
 
+
-
break;
+
-
case '*':
+
-
WriteByte(Token);
+
-
EatCharacters(1);
+
-
 
+
-
if(StartOfStatement)
+
-
{
+
-
/* * at start of statement means don't tokenise rest of statement, other than string literals */
+
-
while(!EndOfFile && !ErrorNum && IncomingBuffer[0] != ':' && IncomingBuffer[0] != '\n')
+
-
{
+
-
switch(IncomingBuffer[0])
+
-
{
+
-
default:
+
-
WriteByte(IncomingBuffer[0]);
+
-
EatCharacters(1);
+
-
break;
+
-
case '"':
+
-
if(!CopyStringLiteral())
+
-
return false;
+
-
break;
+
-
}
+
-
}
+
-
}
+
-
break;
+
-
case '"':
+
-
if(!CopyStringLiteral())
+
-
return false;
+
-
break;
+
-
}
+
-
}
+
-
else
+
-
{
+
-
Uint8 Flags = KeyWordTable[Token - 0x80].Flags; //make copy of flags, as we're about to throwaway Token
+
-
 
+
-
WriteByte(Token); //write token
+
-
EatCharacters(KeyWordTable[Token - 0x80].StrLen);
+
-
 
+
-
/*
+
-
+
-
Effect token flags
+
-
+
-
*/
+
-
if(Flags & 0x08)
+
-
{
+
-
/* FN or PROC, so duplicate next set of alphanumerics without thought */
+
-
while(!ErrorNum && !EndOfFile && AlphaNumeric(IncomingBuffer[0]))
+
-
{
+
-
WriteByte(IncomingBuffer[0]);
+
-
EatCharacters(1);
+
-
}
+
-
}
+
-
 
+
-
if(Flags & 0x10)
+
-
{
+
-
/* tokenise line numbers for a bit */
+
-
if(!DoLineNumberTokeniser())
+
-
return false;
+
-
}
+
-
 
+
-
if(Flags & 0x20)
+
-
{
+
-
/* REM or DATA, so copy rest of line without tokenisation */
+
-
while(!ErrorNum && !EndOfFile && IncomingBuffer[0] != '\n')
+
-
{
+
-
WriteByte(IncomingBuffer[0]);
+
-
EatCharacters(1);
+
-
}
+
-
}
+
-
 
+
-
if(
+
-
(Flags & 0x40) && StartOfStatement
+
-
)
+
-
{
+
-
/* pseudo-variable flag */
+
-
Memory[Addr-1] += 0x40; //adjust just-written token
+
-
}
+
-
 
+
-
/* check if we now go into middle of statement */
+
-
if(Flags & 0x02)
+
-
StartOfStatement = false;
+
-
 
+
-
/* check if we now go into start of statement */
+
-
if(Flags & 0x04)
+
-
StartOfStatement = true;
+
-
}
+
-
}
+
-
 
+
-
EatCharacters(1); //either eat a '\n' or have no effect at all
+
-
 
+
-
return true;
+
-
}
+
-
 
+
-
bool ImportBASIC(char *Filename, Uint8 *Mem)
+
-
{
+
-
/* store memory target to global var */
+
-
Memory = Mem;
+
-
ErrorNum = 0;
+
-
 
+
-
/* get the value of PAGE‚ insert BASIC code starting from there */
+
-
Addr = Memory[0x18] << 8;
+
-
 
+
-
/* validity check: does PAGE currently point to a 0x0d? */
+
-
if(Memory[Addr] != 0x0d)
+
-
{
+
-
ErrorNum = 1;
+
-
return false;
+
-
}
+
-
/* validity check: does TOP - 2 point to a 0x0d, 0xff? */
+
<pre>
-
Uint16 TOPAddr = Memory[0x12] | (Memory[0x13] << 8);
+
JOY_RIGHT = 1
-
if(
+
JOY_LEFT = 2
-
(Memory[TOPAddr-2] != 0x0d) ||
+
JOY_DOWN = 4
-
(Memory[TOPAddr-1] != 0xff)
+
JOY_UP = 8
-
)
+
JOY_FIRE = &10
-
{
+
-
ErrorNum = 1;
+
-
return false;
+
-
}
+
-
/* open file, reset variables */
+
JoyBits = &70
-
inputfile = fopen(Filename, "rt");
+
-
IncomingPointer = 0;
+
-
CurLine = 1;
+
-
EndOfFile = false;
+
-
if(!inputfile)
 
-
{
 
-
ErrorNum = 2;
 
-
return false;
 
-
}
 
-
/* fill input buffer */
+
._READ_JOYSTICK:
-
int c = 8;
+
LDA #&00
-
while(c--)
+
STA JoyBits
-
GetCharacter();
+
-
while(!EndOfFile && !ErrorNum)
+
.left_right:
-
{
+
LDX #1
-
/* get line number */
+
LDA #&80
-
/* skip white space and empty lines */
+
JSR _OSBYTE
-
while(Token == ' ' || Token == '\t' || Token == '\r' || Token == '\n')
+
LDA #0
-
EatCharacters(1);
+
CPY #&25
-
+
BCS not_right
-
/* end of file? */
+
ORA #JOY_RIGHT
-
if(EndOfFile) break;
+
.not_right:
 +
CPY #&DB
 +
BCC not_left
 +
ORA #JOY_LEFT
 +
.not_left:
 +
STA JoyBits
-
/* now we should see a line number */
+
.up_down:
-
if(!NumberStart || (NumberValue >= 32768))
+
LDX #&02
-
{
+
LDA #&80
-
ErrorNum = -1;
+
JSR _OSBYTE
-
sprintf(DynamicErrorText, "Malformed line number at line %d", CurLine);
+
LDA JoyBits
-
break;
+
CPY #&25
-
}
+
BCS not_down
 +
ORA #JOY_DOWN
 +
.not_down:
 +
CPY #&DB
 +
BCC not_up
 +
ORA #JOY_UP
 +
.not_up:
 +
STA JoyBits
-
/* inject into memory */
+
LDX #&00
-
WriteByte(0x0d);
+
LDA #&80
-
WriteByte(NumberValue >> 8);
+
JSR _OSBYTE
-
WriteByte(NumberValue&0xff);
+
TXA
-
EatCharacters(NumberLength);
+
LSR A
 +
BCC not_fire
-
/* read rest of line, record length */
+
LDA JoyBits
-
Uint16 LengthAddr = Addr; WriteByte(0);
+
ORA #JOY_FIRE
-
if(!EncodeLine())
+
STA JoyBits
-
break;
+
-
Memory[LengthAddr] = (Uint8)(Addr - LengthAddr + 3);
+
-
}
+
-
/* write "end of program" */
+
.not_fire:
-
WriteByte(0x0d);
+
RTS
-
WriteByte(0xff);
+
-
+
-
/* write TOP */
+
-
Memory[0x12] = Addr&0xff;
+
-
Memory[0x13] = Addr >> 8;
+
-
fclose(inputfile);
+
</pre>
-
return ErrorNum ? false : true;
+
-
}</pre>
+

Revision as of 19:28, 13 November 2011

Read the Joystick

Uses OSBYTE &80:

X = 0 Read Fire Buttons in X

X = 1 Read X Axis in Y;X (Returns 0- 65535 Right<->Left)

X = 2 Read Y Axis in Y;X (returns 0- 65535 Down<->Up)


The Code

JOY_RIGHT	= 1
JOY_LEFT	        = 2
JOY_DOWN	= 4
JOY_UP		= 8
JOY_FIRE	        = &10

JoyBits 	        = &70


._READ_JOYSTICK:
	LDA 	#&00
	STA 	JoyBits

.left_right:
	LDX	#1
	LDA 	#&80
	JSR 	_OSBYTE
	LDA	#0
	CPY 	#&25
	BCS 	not_right
	ORA 	#JOY_RIGHT
.not_right:
	CPY 	#&DB
	BCC 	not_left
	ORA 	#JOY_LEFT
.not_left:
	STA 	JoyBits

.up_down:
	LDX 	#&02
	LDA 	#&80
	JSR 	_OSBYTE
	LDA 	JoyBits
        CPY 	#&25
	BCS 	not_down
  	ORA 	#JOY_DOWN
.not_down:
	CPY 	#&DB
	BCC 	not_up
	ORA 	#JOY_UP
.not_up:
   	STA 	JoyBits

        LDX 	#&00
	LDA 	#&80
	JSR 	_OSBYTE
	TXA
	LSR 	A
	BCC 	not_fire

	LDA 	JoyBits
	ORA 	#JOY_FIRE
	STA 	JoyBits

.not_fire:
	RTS