egeberg@solan.unit.no (Christian Egeberg) (06/06/91)
Ok, ok, I'm here.
God, I didn't know what three exams in a week could
do to someone...
This, and my following two posts will contain
Chipper V1.12 Documentation
Chipper V1.12 Source (Turbo Pascal 5.5)
Blinky V1.01 Source
Chipper has not changed since the first upload (V1.12).
It is not particularly ISO-Pascal compatible, and I have
not added S-Chip 1.0 or 1.1 mnemonics (yet).
Even though I thought I was through with Chip, I suspect
I will give in, and port it to Kernighan-Richie style C.
New mnemonics will be included, and propably conditional
assembly. The program will be tested on some Sun workstation,
SCO 386 Unix, and Microsoft C for MS-DOS.
Programming will start around 20/6, but I don't know if
I'll have internet access this summer, so it might not
be posted until early September.
Somwhere in my original Blinky posting, there was a bug.
The ghosts could pass through the maze at one particular
place. This has been corrected, and the keyboard layout
is now similar to zyzygy (1,2,6,9, I think).
I have no assembled version of Blinky handy, so if someone
would post an ASC version, I would be grateful, I still have
one exam to go...
----------------------------------------------------------------------
Chipper V1.12 is a simple assembler for the HP48SX Chip-8 language.
Written by Christian Egeberg (egeberg@solan.unit.no) 2/11 .. 7/11-'90,
using Turbo Pascal V5.5, and Turbo Debugger V2.0 in 386 virtual mode.
In order to use Chipper V1.12 you need the following:
* An IBM compatible PC, preferably with 512k or more.
* A Hewlett Packard 48SX Calculator.
* A PC to HP48SX serial cable.
* A Kermit compatible PC based communication program.
* The CHIP-48 interpreter for HP48SX.
CHIP-48 is a video game language interpreter written by Andreas Gustafsson
(gson@niksula.hut.fi). It utilizes the original CHIP-8 instruction set
commonly used by RCA CDP1802 based home computers in the late 1970's.
CHIP-48 should be available by anonymous ftp from vega.hut.fi, directory
/pub/misc/hp48sx/asap.
CHIP-48 programs have access to 4k bytes of memory, addressed from #000 to
#FFF. The programs start at address #200, because of the memory requirements
of the original CHIP-8 interpreter. Instructions are 16 bits long and start
at even memory locations.
CHIP-48 has 16 general registers, named V0, V1, V2, ... , VE, VF. These are
8 bits wide. The VF register works as carry flag and collision indicator,
and is modified by certain instructions. A 16 bit I register also exists.
The lower 12 bits of this register are typically used as a memory pointer.
A delay timer and a sound timer is provided as well. These are 8 bits wide
and decrement around 60 times per second, until a value of 0 is reached.
The HP48SX beeper will buzz until the sound timer reaches 0.
CHIP-48 screen resolution is 64 pixels horisontal and 32 pixels vertical.
Screen origin is the upper left corner. A sprite is 8 pixels wide and from 1
to 15 pixels high. That is also from 1 to 15 bytes large. Upper row in the
first byte, leftmost pixel in the most significant bit. Sprites are XOR-ed
onto the background. If this causes any pixel to be erased, VF is set to
#01, else VF will be #00.
CHIP-48 programs may access 16 keys numbered from #0 to #F. The HP48SX
keyboard mapping is shown below:
( 7 ) -> #1 ( 8 ) -> #2 ( 9 ) -> #3 ( / ) -> #C
( 4 ) -> #4 ( 5 ) -> #5 ( 6 ) -> #6 ( * ) -> #D
( 1 ) -> #7 ( 2 ) -> #8 ( 3 ) -> #9 ( - ) -> #E
( 0 ) -> #A ( . ) -> #0 ( _ ) -> #B ( + ) -> #F
The following table contains valid CHIP-48 instruction codes and their
syntax in Chipper V1.12. NNN indicates a 12 bit address. KK is an 8 bit
constant. X and Y denote 4 bit register numbers. Hexadecimal characters
represent themselves. WordExpr means an expression resulting in a 16 bit
constant. AddrExpr is an expression resulting in a 12 bit address. ByteExpr
results in an 8 bit constant, NibbleExpr makes a 4 bit constant, and Expr is
a general expression. Char is an ASCII character. String is a sequence of
ASCII characters. Text in curly brackets is optional. Instruction codes are
written most significant byte first, least significant byte last.
#0NNN SYS AddrExpr ; Call 1802 code at NNN (not HP48SX)
#00E0 CLS ; Clear display
#00EE RET ; Return from subroutine (16 levels)
#1NNN JP AddrExpr ; Jump to NNN
#2NNN CALL AddrExpr ; Call subroutine at NNN (16 levels)
#3XKK SE VX, ByteExpr ; Skip next instruction if VX = KK
#4XKK SNE VX, ByteExpr ; Skip next instruction if VX <> KK
#5XY0 SE VX, VY ; Skip next instruction if VX = VY
#6XKK LD VX, ByteExpr ; VX := KK
#7XKK ADD VX, ByteExpr ; VX := VX + KK
#8XY0 LD VX, VY ; VX := VY, VF updates
#8XY1 OR VX, VY ; VX := VX OR VY, VF updates
#8XY2 AND VX, VY ; VX := VX AND VY, VF updates
#8XY3 XOR VX, VY ; VX := VX XOR VY, VF updates
#8XY4 ADD VX, VY ; VX := VX + VY, VF := carry
#8XY5 SUB VX, VY ; VX := VX - VY, VF := NOT borrow
#8XY6 SHR VX {, VY} ; VX := VX SHR 1, VF := carry
#8XY7 SUBN VX, VY ; VX := VY - VX, VF := NOT borrow
#8XYE SHL VX {, VY} ; VX := VX SHL 1, VF := carry
#9XY0 SNE VX, VY ; Skip next instruction if VX <> VY
#ANNN LD I, AddrExpr ; I := NNN
#BNNN JP V0, AddrExpr ; Jump to NNN + V0
#CXKK RND VX , ByteExpr ; VX := random AND KK
#DXYN DRW VX, VY, NibbleExpr ; Draw N byte sprite from [I] at VX, VY
; ... VF := collision
#EX9E SKP VX ; Skip next instruction if key VX down
#EXA1 SKNP VX ; Skip next instruction if key VX up
#FX07 LD VX, DT ; VX := delaytimer
#FX0A LD VX, K ; VX := key, wait for keypress
#FX15 LD DT, VX ; Delaytimer := VX
#FX18 LD ST, VX ; Soundtimer := VX
#FX1E ADD I, VX ; I := I + VX
#FX29 LD F, VX ; Point I to 5 byte sprite char for VX
#FX33 LD B, VX ; Store BCD of VX in [I], [I+1], [I+2]
#FX55 LD [I], VX ; Store V0 .. VX in [I] .. [I+X]
#FX65 LD VX, [I] ; Read V0 .. VX from [I] .. [I+X]
Additional Chipper V1.12 directives are:
SYMBOL = Expr ; Assign value to symbol
SYMBOL EQU Expr ; Assign value to symbol
DB ByteExpr {, ...} ; Define byte(s) at current address
DW WordExpr {, ...} ; Define word(s) at current address
DA String ; Define string at current address
DS ByteExpr ; Define ByteExpr uninitialized
; ... bytes at current address
ORG AddrExpr ; Set current address to AddrExpr
END ; This directive is ignored
INCLUDE SourceFileName ; Includes one more sourcefile
Chipper V1.12 accepts one label, or symbol, per line of source. This should
start with an alphabetic character, and not contain non alphanumeric
characters, otherwise the expression parser may get a bit confused. All
symbols will be converted to upper case, and may be prefixed by an
underscore character and / or suffixed by a colon. These will be stripped
off before the symbol is used. Each symbol contains a 32 bit signed integer
value, set to current address, unless defined by the = or EQU directives.
A symbol name or string containing lower case characters or non alphanumeric
characters (not in symbol names, I have told you that), should be contained
within apostrophes. Two apostrophes following eachother will produce one
resultant apostrophe. Some string examples:
'11/6-'68' ; Is an unterminated string starting with 11/6-68
11/6-''68 ; Evaluates to 11/6-'68
Christian Egeberg ; Evaluates to CHRISTIAN EGEBERG
'Christian Egeberg' ; Evaluates to Christian Egeberg
This, is a test ; Evaluates to THIS
; ... and IS A TEST
This',' is a test ; Evaluates to THIS, IS A TEST
'This, is a test' ; Evaluates to This, is a test
'''' ; Evaluates to '''
'' ; Evaluates to '
A symbol primitive may be one of the following:
SymbolName ; for instance LOOP
DecimalValue ; for instance 1106
#HexadecimalValue ; for instance #452
$BinaryValue ; for instance $10001010010
@OctalValue ; for instance @2122
"Character ; for instance "'c'
? ; This is always assigned to current address
An expression may consist of symbol primitives and the following operators.
Horisontal lines denote different priorities. Operators sharing priority
level are evaluated left to right:
( ; Start parentheses expression
) ; End of parentheses expression
-----------------------------------
+ ; Unary plus sign
- ; Unary minus sign
~ ; Bitwise NOT operator
-----------------------------------
! ; Power of operator
< ; Shift left number of bits
> ; Shift right number of bits
-----------------------------------
* ; Multiply
/ ; Divide
-----------------------------------
+ ; Add
- ; Subtract
-----------------------------------
& ; Bitwise AND operator
| ; Bitwise OR operator
^ ; Bitwise XOR operator
-----------------------------------
\ ; Low priority divide
% ; Modulus operator
Some expression examples:
(? + 15 \ 16) * 16 ; Is a paragraph (16 bytes) alignment
"'c' + @2 % #20 ; Resolves to 5
-3 * -( -7 + ~3) ; Resolves to -33
-3 * -( -7 + ~3) & #FF ; Resolves to 223
( 2 + 1 )! 2 ^ $1101 > 2 ; Resolves to 10
(2+1)!2^$1101>2 ; Resolves to 10
TABLESTART + 4 * ITEMSIZE ; Resolves
Remarks are prefixed by semicolons, as in the above examples. Note that
Chipper V1.12 performs a word alignment after every line of source code.
This means that for instance two single parameter DB directives in rapid
succession will have an uninitialized separator byte between them. Avoid
this by defining any multiple of two bytes per DB directive.
A note concerning the CHIP-48 instruction set. The LD VX, [I] and LD [I], VX
instructions will change the value of the I register if VX is different from
V0. Actually, I think it is set to the address of the last byte / register
read or written. This may lead to rather obscure bugs. It took me a day's
worth of debugging to figure out why Blinky died, moving upwards on the
second screen, after updating a 16 bit score counter in memory... I had
overwritten the first byte of the Blinky facing up sprite definition, and
thus caused a collision detect.
Chipper V1.12 fatal error messages:
No source file found ; Incorrect source file name
Unable to open file ; Disk problem, no write access
Outside legal address range ; Current address outside #200 .. #FFF
Chipper V1.12 warning messages:
Incorrect number of parameters ; Too few or too many parameters
No directive found ; Two symbols defined on same line
No symbol associated ; = or EQU without a symbol
Attempt to redefine existing symbol ; Symbol already exists, discarded
Badly defined parameter ; Undefined symbol or bad syntax
; ... in expression
Parameter out of range ; Value too large or too small
Register not found ; Register operand expected
Illegal register ; Different register required
Internal data structure mismatch ; C. Egeberg is a lousy programmer
Chipper V1.12 should be invoked with:
CHIPPER sourcefilename destinationfilename listfilename
or just:
CHIPPER
which will prompt for filenames. Default file extensions are .CHP, nothing
and .LST. Destination and listfiles will by default be named after source.
The destination file is a binary download mode HP48SX string. Kermit it to
the calculator, put the string on the stack, and run CHIP-48. The listfile
will contain all errors and warnings, hexdump of all generated instructions,
and a complete symboltable. The format is rather simple.
This document contains some information more or less copied directly off
the CHIP-48 documentation by Andreas Gustafsson, who has done a great job,
hacking for the HP48SX. The Chipper V1.12 syntax was inspired by the
SYZYGY game listing posted to comp.sys.handhelds by Roy Trevino. SYZYGY
is the best CHIP-48 game so far...
CHIP-48 is (C) Copyright 1990 Andreas Gustafsson.
Chipper is (C) Copyright 1990 Christian Egeberg.
Noncommercial distribution allowed, provided that copyright messages
are preserved, and any modified versions are clearly marked as such.
CHIP-48 and, because of that, programs written in Chipper make use of
undocumented low-level features of the HP48SX calculator. They may or
may not cause loss of data, excessive battery drainage, and / or
damage to the calculator hardware. The authors take no responsibility
whatsoever for any damage caused by the use of these programs.
Chipper does all its I/O on the PC through the Turbo Pascal FExpand(),
Assign(), ReSet(), ReWrite(), Read(), Write(), Eof() and Close() run
time library functions, but the author takes no responsibility for
loss of data, damage to any PC hardware, nor strange incidents caused
by the use of this program.
This software is provided "as is" and without any express or implied
warranties, including, but not limited to, the implied warranties of
merchantability and fitness for a particular purpose.egeberg@solan.unit.no (Christian Egeberg) (06/06/91)
This is the Chipper V1.12 Source
---------------------------------------------------------------------
{ Chip-48 Assembler V1.12 by Christian Egeberg 2/11-'90 .. 7/11-'90 }
PROGRAM Chipper;
{$R-,S+,I+,F-,O-,A+,V+,B-,N-,E-,D-,L-,M 16384,16384,655360}
USES Dos, Crt;
CONST
CopyRight= 'Chip-48 Assembler V1.12 by Christian Egeberg 7/11-''90';
ErrorExitCode= 1;
StartAddress= $200;
StopAddress= $fff;
WordMask= $ffff;
AddrMask= $fff;
ByteMask= $ff;
NibbleMask= $f;
LineLength= 160;
ParamLength= 80;
SymbolLength= 16;
MaxParams= 64;
NullChar= Chr( 0);
BellChar= Chr( 7);
SpaceChar= Chr( 32);
SeparatorChar= ',';
RemarkChar= ';';
SymbolChar= '_';
LabelChar= ':';
EqualChar= '=';
TextChar= '''';
AddressChar= '?';
HexChar= '#';
BinChar= '$';
OctChar= '@';
AscChar= '"';
StartChar= '(';
StopChar= ')';
PlusChar= '+';
MinusChar= '-';
NotChar= '~';
PowerChar= '!';
ShlChar= '<';
ShrChar= '>';
MulChar= '*';
FracChar= '/';
AndChar= '&';
OrChar= '|';
XorChar= '^';
DivChar= '\';
ModChar= '%';
NameDefault= '';
InExtDefault= '.CHP';
OutExtDefault= '.';
ListExtDefault= '.LST';
RunErrorMessage= 'Fatal error: ';
RunWarningMessage= 'Warning: ';
WarningNumMessage= 'Total number of warnings: ';
NoSourceError= 'No source file found';
FileAccessError= 'Unable to open file';
BoundsError= 'Outside legal address range';
ParamCountWarning= 'Incorrect number of parameters';
DualSymbolWarning= 'No directive found';
NoSymbolWarning= 'No symbol associated';
CopySymbolWarning= 'Attempt to redefine existing symbol';
UndefinedWarning= 'Badly defined parameter';
RangeWarning= 'Parameter out of range';
NoRegisterWarning= 'Register not found';
BadRegisterWarning= 'Illegal register';
InternalWarning= 'Internal data structure mismatch';
TYPE
Token= ( EqualToken, AddToken, AndToken, CallToken, ClsToken, DaToken,
DbToken, DrwToken, DsToken, DwToken, EndToken, EquToken, IncludeToken,
JpToken, LdToken, OrToken, OrgToken, RetToken, RndToken, SeToken,
ShlToken, ShrToken, SknpToken, SkpToken, SneToken, SubToken, SubnToken,
SysToken, XorToken, LastToken);
Register= ( BReg, DtReg, FReg, IReg, KReg, V0Reg, V1Reg, V2Reg, V3Reg,
V4Reg, V5Reg, V6Reg, V7Reg, V8Reg, V9Reg, VaReg, VbReg, VcReg, VdReg,
VeReg, VfReg, StReg, IiReg, LastReg);
CharSet= SET OF Char;
LineString= STRING[ LineLength];
ParamString= STRING[ ParamLength];
SymbolString= STRING[ SymbolLength];
ParamPointer= ^ParamRecord;
ParamRecord=
RECORD
Param: ParamString;
Next: ParamPointer;
END;
SymbolPointer= ^SymbolRecord;
SymbolRecord=
RECORD
Symbol: SymbolString;
Address: LongInt;
Left, Right: SymbolPointer;
END;
InstPointer= ^InstRecord;
InstRecord=
RECORD
Line: Word;
Name: PathStr;
Address: LongInt;
Inst: Token;
Count: Byte;
Params: ParamPointer;
Next, Prev: InstPointer;
END;
CONST
TokenText: ARRAY[ Token] OF SymbolString=
( EqualChar, 'ADD', 'AND', 'CALL', 'CLS', 'DA', 'DB', 'DRW', 'DS', 'DW',
'END', 'EQU', 'INCLUDE', 'JP', 'LD', 'OR', 'ORG', 'RET', 'RND', 'SE',
'SHL', 'SHR', 'SKNP', 'SKP', 'SNE', 'SUB', 'SUBN', 'SYS', 'XOR', '');
RegisterText: ARRAY[ Register] OF SymbolString=
( 'B', 'DT', 'F', 'I', 'K', 'V0', 'V1', 'V2', 'V3', 'V4', 'V5', 'V6',
'V7', 'V8', 'V9', 'VA', 'VB', 'VC', 'VD', 'VE', 'VF', 'ST', '[I]', '');
Operators: CharSet=
[ StartChar, StopChar, PlusChar, MinusChar, NotChar, PowerChar, ShlChar,
ShrChar, MulChar, FracChar, AndChar, OrChar, XorChar, DivChar, ModChar];
DigitText: ParamString= '0123456789ABCDEF';
Instructions: InstPointer= NIL;
Directives: SymbolPointer= NIL;
Registers: SymbolPointer= NIL;
Symbols: SymbolPointer= NIL;
LastSymbol: SymbolPointer= NIL;
Current: LongInt= StartAddress;
Finish: LongInt= StartAddress;
LineText: LineString= '';
LineName: PathStr= NameDefault;
LineNum: Word= 1;
InstPoint: InstPointer= NIL;
ListOpen: Boolean= False;
WarningCount: Word= 0;
VAR
Memory: ARRAY[ StartAddress .. StopAddress] OF Byte;
OutFile: FILE OF Byte;
StdIn, StdOut, ListFile: Text;
InFileName, OutFileName, ListFileName: PathStr;
FileDir: DirStr;
FileName: NameStr;
FileExt: ExtStr;
PROCEDURE RunError( Message: STRING);
VAR
Number: SymbolString;
Param: ParamPointer;
BEGIN
WriteLn( StdOut, BellChar, RunErrorMessage, Message);
IF ListOpen THEN
WriteLn( ListFile, RunErrorMessage, Message);
IF LineName <> NameDefault THEN
BEGIN
Str( LineNum, Number);
WriteLn( StdOut, 'Current file ', LineName, ' line ', Number);
WriteLn( StdOut, LineText);
IF ListOpen THEN
BEGIN
WriteLn( ListFile, 'Current file ', LineName, ' line ', Number);
WriteLn( ListFile, LineText);
END;
END;
IF InstPoint <> NIL THEN
BEGIN
Str( InstPoint^.Line, Number);
WriteLn( StdOut, 'Associated file ', InstPoint^.Name, ' line ',
Number);
Write( StdOut, TokenText[ InstPoint^.Inst]);
IF ListOpen THEN
BEGIN
WriteLn( ListFile, 'Associated file ', InstPoint^.Name, ' line ',
Number);
Write( ListFile, TokenText[ InstPoint^.Inst]);
END;
Param:= InstPoint^.Params;
WHILE Param <> NIL DO
BEGIN
Write( StdOut, ', ', Param^.Param);
IF ListOpen THEN
Write( ListFile, ', ', Param^.Param);
Param:= Param^.Next;
END;
WriteLn( StdOut);
IF ListOpen THEN
WriteLn( ListFile);
END;
WriteLn( StdOut);
IF ListOpen THEN
WriteLn( ListFile);
Halt( ErrorExitCode);
{ Turbo Pascal closes all files on exit }
END;
PROCEDURE RunWarning( Message: STRING);
VAR
Number: SymbolString;
Param: ParamPointer;
BEGIN
WriteLn( StdOut, RunWarningMessage, Message);
WriteLn( ListFile, RunWarningMessage, Message);
IF LineName <> NameDefault THEN
BEGIN
Str( LineNum, Number);
WriteLn( StdOut, 'Current file ', LineName, ' line ', Number);
WriteLn( StdOut, LineText);
WriteLn( ListFile, 'Current file ', LineName, ' line ', Number);
WriteLn( ListFile, LineText);
END;
IF InstPoint <> NIL THEN
BEGIN
Str( InstPoint^.Line, Number);
WriteLn( StdOut, 'Associated file ', InstPoint^.Name, ' line ',
Number);
Write( StdOut, TokenText[ InstPoint^.Inst]);
WriteLn( ListFile, 'Associated file ', InstPoint^.Name, ' line ',
Number);
Write( ListFile, TokenText[ InstPoint^.Inst]);
Param:= InstPoint^.Params;
WHILE Param <> NIL DO
BEGIN
Write( StdOut, ', ', Param^.Param);
Write( ListFile, ', ', Param^.Param);
Param:= Param^.Next;
END;
WriteLn( StdOut);
WriteLn( ListFile);
END;
WriteLn( StdOut);
WriteLn( ListFile);
Inc( WarningCount);
END;
FUNCTION HexString( Value: LongInt; Count: Byte): SymbolString;
VAR
FoundWord: SymbolString;
Digit: Byte;
BEGIN
FoundWord:= '';
WHILE Value > 0 DO
BEGIN
Digit:= Value AND NibbleMask;
FoundWord:= DigitText[ Succ( Digit)] + FoundWord;
Value:= Value DIV 16;
END;
WHILE Length( FoundWord) < Count DO
FoundWord:= '0' + FoundWord;
HexString:= FoundWord;
END;
PROCEDURE ListInstruction( Address, Count: Word; Inst: InstPointer;
Number: Byte);
VAR
Param: ParamPointer;
This: Word;
BEGIN
Write( ListFile, Inst^.Line:5, ' ', TokenText[ Inst^.Inst]);
Param:= Inst^.Params;
FOR This:= 1 TO Number DO
IF Param <> NIL THEN
BEGIN
Write( ListFile, ', ', Param^.Param);
Param:= Param^.Next;
END;
WriteLn( ListFile);
Write( ListFile, HexString( Address, 3), ' ');
FOR This:= 0 TO Pred( Count) DO
Write( ListFile, HexString( Memory[ Address + This], 2));
WriteLn( ListFile);
WriteLn( ListFile);
END;
PROCEDURE ListSymbols( Head: SymbolPointer);
BEGIN
IF Head^.Left <> NIL THEN
ListSymbols( Head^.Left);
WriteLn( ListFile, HexString( Head^.Address, 3), ' ', Head^.Symbol);
IF Head^.Right <> NIL THEN
ListSymbols( Head^.Right);
END;
PROCEDURE ListWarnings;
BEGIN
WriteLn( StdOut, WarningNumMessage, WarningCount);
WriteLn( ListFile);
WriteLn( ListFile, WarningNumMessage, WarningCount);
END;
FUNCTION ExpandFileName( Path: PathStr; DefName: NameStr; DefExt: ExtStr):
PathStr;
VAR
Dir: DirStr;
Name: NameStr;
Ext: ExtStr;
BEGIN
FSplit( FExpand( Path), Dir, Name, Ext);
IF Name = '' THEN
Name:= DefName;
IF Ext = '' THEN
Ext:= DefExt;
FileDir:= Dir;
FileName:= Name;
FileExt:= Ext;
ExpandFileName:= Dir + Name + Ext;
END;
FUNCTION OpenFiles: Boolean;
VAR
InFile: Text;
BEGIN
OpenFiles:= False;
IF ParamCount >= 1 THEN
BEGIN
InFileName:= ExpandFileName( ParamStr( 1), NameDefault, InExtDefault);
IF FileName <> NameDefault THEN
BEGIN
OutFileName:= ParamStr( 2);
ListFileName:= ParamStr( 3);
END
ELSE
RunError( NoSourceError);
END
ELSE
BEGIN
Write( StdOut, 'SourceFileName? ');
ReadLn( StdIn, InFileName);
InFileName:= ExpandFileName( InFileName, NameDefault, InExtDefault);
IF FileName <> NameDefault THEN
BEGIN
Write( StdOut, 'TargetFileName? ');
ReadLn( StdIn, OutFileName);
Write( StdOut, 'ListFileName? ');
ReadLn( StdIn, ListFileName);
WriteLn( StdOut);
END
ELSE
RunError( NoSourceError);
END;
OutFileName:= ExpandFileName( OutFileName, FileName, OutExtDefault);
ListFileName:= ExpandFileName( ListFileName, FileName, ListExtDefault);
WriteLn( StdOut, 'SourceFile: ', InFileName);
WriteLn( StdOut, 'TargetFile: ', OutFileName);
Assign( OutFile, OutFileName);
{$I-}
ReWrite( OutFile);
{$I+}
IF IOResult <> 0 THEN
RunError( FileAccessError);
WriteLn( StdOut, 'ListFile: ', ListFileName);
Assign( ListFile, ListFileName);
{$I-}
ReWrite( ListFile);
{$I+}
IF IOResult <> 0 THEN
RunError( FileAccessError);
WriteLn( StdOut);
ListOpen:= True;
WriteLn( ListFile, CopyRight);
WriteLn( ListFile);
WriteLn( ListFile, 'SourceFile: ', InFileName);
WriteLn( ListFile, 'TargetFile: ', OutFileName);
WriteLn( ListFile, 'ListFile: ', ListFileName);
WriteLn( ListFile);
OpenFiles:= True;
END;
FUNCTION StripSymbol( Symbol: SymbolString): SymbolString;
BEGIN
IF Symbol[ 1] = SymbolChar THEN
Symbol:= Copy( Symbol, 2, Pred( Length( Symbol)));
IF Symbol[ Length( Symbol)] = LabelChar THEN
Dec( Symbol[ 0]);
StripSymbol:= Symbol;
END;
FUNCTION DefineSymbol( Symbol: SymbolString; Value: LongInt;
VAR Head: SymbolPointer): Boolean;
VAR
Symb, Last: SymbolPointer;
BEGIN
DefineSymbol:= True;
Symbol:= StripSymbol( Symbol);
Last:= NIL;
Symb:= Head;
WHILE Symb <> NIL DO
BEGIN
Last:= Symb;
IF Symbol = Last^.Symbol THEN
DefineSymbol:= False;
IF Symbol <= Symb^.Symbol THEN
Symb:= Symb^.Left
ELSE
Symb:= Symb^.Right;
END;
New( Symb);
Symb^.Symbol:= Symbol;
Symb^.Address:= Value;
Symb^.Left:= NIL;
Symb^.Right:= NIL;
IF Last = NIL THEN
Head:= Symb
ELSE
IF Symbol <= Last^.Symbol THEN
Last^.Left:= Symb
ELSE
Last^.Right:= Symb;
LastSymbol:= Symb;
END;
FUNCTION ResolveSymbol( Symbol: SymbolString; VAR Value: LongInt;
Head: SymbolPointer): Boolean;
BEGIN
ResolveSymbol:= False;
Value:= 0;
Symbol:= StripSymbol( Symbol);
WHILE Head <> NIL DO
BEGIN
IF Symbol = Head^.Symbol THEN
BEGIN
Value:= Head^.Address;
ResolveSymbol:= True;
Head:= NIL;
END
ELSE
IF Symbol < Head^.Symbol THEN
Head:= Head^.Left
ELSE
Head:= Head^.Right;
END;
END;
FUNCTION ResolveNumber( Symbol: SymbolString; Base: Byte;
VAR Value: LongInt): Boolean;
VAR
Digit, Count: Byte;
BEGIN
ResolveNumber:= True;
IF Symbol = '' THEN
ResolveNumber:= False;
Value:= 0;
Count:= 1;
WHILE Count <= Length( Symbol) DO
BEGIN
Digit:= Pos( Symbol[ Count], DigitText);
IF NOT ( Digit IN [ 1 .. Base]) THEN
BEGIN
ResolveNumber:= False;
Count:= Length( Symbol);
END
ELSE
Value:= Base * Value + Pred( Digit);
Inc( Count);
END;
END;
FUNCTION ResolveValue( Symbol: SymbolString; VAR Value: LongInt;
Head: SymbolPointer): Boolean;
BEGIN
ResolveValue:= False;
Value:= 0;
IF Symbol = AddressChar THEN
BEGIN
Value:= Current;
ResolveValue:= True;
END
ELSE
CASE Symbol[ 1] OF
'0' .. '9':
ResolveValue:= ResolveNumber( Symbol, 10, Value);
HexChar:
ResolveValue:= ResolveNumber( Copy( Symbol, 2,
Pred( Length( Symbol))), 16, Value);
BinChar:
ResolveValue:= ResolveNumber( Copy( Symbol, 2,
Pred( Length( Symbol))), 2, Value);
OctChar:
ResolveValue:= ResolveNumber( Copy( Symbol, 2,
Pred( Length( Symbol))), 8, Value);
AscChar:
BEGIN
IF Length( Symbol) >= 2 THEN
Value:= Ord( Symbol[ 2]);
IF Length( Symbol) = 2 THEN
ResolveValue:= True;
END;
ELSE
ResolveValue:= ResolveSymbol( Symbol, Value, Head);
END;
END;
FUNCTION SplitParam( VAR Param: ParamString): SymbolString;
VAR
FoundWord: ParamString;
Character: Char;
Start, Count: Byte;
Reading: Boolean;
BEGIN
FoundWord:= '';
Reading:= True;
Start:= 0;
Count:= 1;
WHILE Count <= Length( Param) DO
BEGIN
Character:= Param[ Count];
IF ( Character > SpaceChar) THEN
BEGIN
Start:= Count;
Count:= Length( Param);
END;
Inc( Count);
END;
IF Start > 0 THEN
BEGIN
Count:= Start;
Character:= Param[ Count];
IF Character IN Operators THEN
BEGIN
FoundWord:= Character;
Param:= Copy( Param, Succ( Count), Length( Param) - Count);
Reading:= False;
END
ELSE
WHILE Count <= Length( Param) DO
BEGIN
Character:= Param[ Count];
IF ( Character <= SpaceChar) OR ( Character IN Operators) THEN
BEGIN
Param:= Copy( Param, Count, Length( Param) - Pred( Count));
Count:= Length( Param);
Reading:= False;
END
ELSE
FoundWord:= FoundWord + Character;
Inc( Count);
END;
END;
IF Reading THEN
Param:= '';
SplitParam:= FoundWord;
END;
FUNCTION ResolveOperator( Symbol: SymbolString; VAR Token: Char;
Legal: CharSet): Boolean;
BEGIN
Token:= SpaceChar;
IF Length( Symbol) >= 1 THEN
Token:= Symbol[ 1];
ResolveOperator:= Token IN Legal;
END;
FUNCTION ResolveSingle( Token: Char; VAR Value: LongInt): Boolean;
BEGIN
ResolveSingle:= True;
CASE Token OF
PlusChar:
{ Nothing to be done };
MinusChar:
Value:= - Value;
NotChar:
Value:= NOT Value;
ELSE
ResolveSingle:= False;
END;
END;
FUNCTION ResolveDouble( Token: Char; VAR Value: LongInt;
Operand: LongInt): Boolean;
VAR
Count, This: LongInt;
BEGIN
ResolveDouble:= True;
CASE Token OF
PowerChar:
BEGIN
This:= 1;
FOR Count:= 1 TO Operand DO
This:= This * Value;
Value:= This;
END;
ShlChar:
Value:= Value SHL Operand;
ShrChar:
Value:= Value SHR Operand;
MulChar:
Value:= Value * Operand;
FracChar, DivChar:
Value:= Value DIV Operand;
PlusChar:
Value:= Value + Operand;
MinusChar:
Value:= Value - Operand;
AndChar:
Value:= Value AND Operand;
OrChar:
Value:= Value OR Operand;
XorChar:
Value:= Value XOR Operand;
ModChar:
Value:= Value MOD Operand;
ELSE
ResolveDouble:= False;
END;
END;
FUNCTION ResolveDivMod( VAR Symbol: SymbolString; VAR Param: ParamString;
VAR Value: LongInt; Head: SymbolPointer): Boolean;
FORWARD;
FUNCTION ResolveParent( VAR Symbol: SymbolString; VAR Param: ParamString;
VAR Value: LongInt; Head: SymbolPointer): Boolean;
VAR
Token: Char;
Status: Boolean;
BEGIN
IF ResolveOperator( Symbol, Token, [ StartChar]) THEN
BEGIN
Symbol:= SplitParam( Param);
Status:= ResolveDivMod( Symbol, Param, Value, Head);
IF NOT ResolveOperator( Symbol, Token, [ StopChar]) THEN
Status:= False;
Symbol:= SplitParam( Param);
END
ELSE
BEGIN
Status:= ResolveValue( Symbol, Value, Head);
Symbol:= SplitParam( Param);
END;
ResolveParent:= Status;
END;
FUNCTION ResolveUnary( VAR Symbol: SymbolString; VAR Param: ParamString;
VAR Value: LongInt; Head: SymbolPointer): Boolean;
VAR
Token: Char;
Status: Boolean;
BEGIN
IF ResolveOperator( Symbol, Token, [ PlusChar, MinusChar, NotChar]) THEN
BEGIN
Symbol:= SplitParam( Param);
Status:= ResolveParent( Symbol, Param, Value, Head);
Status:= Status AND ResolveSingle( Token, Value);
END
ELSE
Status:= ResolveParent( Symbol, Param, Value, Head);
ResolveUnary:= Status;
END;
FUNCTION ResolvePower( VAR Symbol: SymbolString; VAR Param: ParamString;
VAR Value: LongInt; Head: SymbolPointer): Boolean;
VAR
Operand: LongInt;
Token: Char;
Status: Boolean;
BEGIN
Operand:= 0;
Status:= ResolveUnary( Symbol, Param, Value, Head);
WHILE ResolveOperator( Symbol, Token, [ PowerChar, ShlChar, ShrChar]) DO
BEGIN
Symbol:= SplitParam( Param);
Status:= Status AND ResolveUnary( Symbol, Param, Operand, Head);
Status:= Status AND ResolveDouble( Token, Value, Operand);
END;
ResolvePower:= Status;
END;
FUNCTION ResolveMulDiv( VAR Symbol: SymbolString; VAR Param: ParamString;
VAR Value: LongInt; Head: SymbolPointer): Boolean;
VAR
Operand: LongInt;
Token: Char;
Status: Boolean;
BEGIN
Operand:= 0;
Status:= ResolvePower( Symbol, Param, Value, Head);
WHILE ResolveOperator( Symbol, Token, [ MulChar, FracChar]) DO
BEGIN
Symbol:= SplitParam( Param);
Status:= Status AND ResolvePower( Symbol, Param, Operand, Head);
Status:= Status AND ResolveDouble( Token, Value, Operand);
END;
ResolveMulDiv:= Status;
END;
FUNCTION ResolvePlusMinus( VAR Symbol: SymbolString; VAR Param: ParamString;
VAR Value: LongInt; Head: SymbolPointer): Boolean;
VAR
Operand: LongInt;
Token: Char;
Status: Boolean;
BEGIN
Operand:= 0;
Status:= ResolveMulDiv( Symbol, Param, Value, Head);
WHILE ResolveOperator( Symbol, Token, [ PlusChar, MinusChar]) DO
BEGIN
Symbol:= SplitParam( Param);
Status:= Status AND ResolveMulDiv( Symbol, Param, Operand, Head);
Status:= Status AND ResolveDouble( Token, Value, Operand);
END;
ResolvePlusMinus:= Status;
END;
FUNCTION ResolveBitWise( VAR Symbol: SymbolString; VAR Param: ParamString;
VAR Value: LongInt; Head: SymbolPointer): Boolean;
VAR
Operand: LongInt;
Token: Char;
Status: Boolean;
BEGIN
Operand:= 0;
Status:= ResolvePlusMinus( Symbol, Param, Value, Head);
WHILE ResolveOperator( Symbol, Token, [ AndChar, OrChar, XorChar]) DO
BEGIN
Symbol:= SplitParam( Param);
Status:= Status AND ResolvePlusMinus( Symbol, Param, Operand, Head);
Status:= Status AND ResolveDouble( Token, Value, Operand);
END;
ResolveBitWise:= Status;
END;
FUNCTION ResolveDivMod( VAR Symbol: SymbolString; VAR Param: ParamString;
VAR Value: LongInt; Head: SymbolPointer): Boolean;
VAR
Operand: LongInt;
Token: Char;
Status: Boolean;
BEGIN
Operand:= 0;
Status:= ResolveBitWise( Symbol, Param, Value, Head);
WHILE ResolveOperator( Symbol, Token, [ DivChar, ModChar]) DO
BEGIN
Symbol:= SplitParam( Param);
Status:= Status AND ResolveBitWise( Symbol, Param, Operand, Head);
Status:= Status AND ResolveDouble( Token, Value, Operand);
END;
ResolveDivMod:= Status;
END;
FUNCTION ResolveExpression( Param: ParamString; VAR Value: LongInt;
Head: SymbolPointer): Boolean;
VAR
Symbol: SymbolString;
BEGIN
ResolveExpression:= False;
Value:= 0;
Symbol:= SplitParam( Param);
IF Symbol <> '' THEN
ResolveExpression:= ResolveDivMod( Symbol, Param, Value, Head);
IF Symbol <> '' THEN
ResolveExpression:= False;
END;
PROCEDURE StoreDirectives( Min, Max: Byte; VAR DirHead: SymbolPointer);
VAR
This: Byte;
BEGIN
This:= Min + (( Max - Min) DIV 2);
IF NOT DefineSymbol( TokenText[ Token( This)], This, DirHead) THEN
RunWarning( CopySymbolWarning);
IF This <> Min THEN
StoreDirectives( Min, Pred( This), DirHead);
IF This <> Max THEN
StoreDirectives( Succ( This), Max, DirHead);
END;
PROCEDURE StoreRegisters( Min, Max: Byte; VAR RegHead: SymbolPointer);
VAR
This: Byte;
BEGIN
This:= Min + (( Max - Min) DIV 2);
IF NOT DefineSymbol( RegisterText[ Register( This)], This, RegHead) THEN
RunWarning( CopySymbolWarning);
IF This <> Min THEN
StoreRegisters( Min, Pred( This), RegHead);
IF This <> Max THEN
StoreRegisters( Succ( This), Max, RegHead);
END;
FUNCTION SplitLine( VAR Line: LineString; AbortFlag: Boolean): ParamString;
VAR
FoundWord: ParamString;
Character: Char;
Level, Start, Count: Byte;
TextFlag, Reading: Boolean;
BEGIN
FoundWord:= '';
Level:= 0;
TextFlag:= False;
Reading:= True;
Start:= 0;
Count:= 1;
WHILE Count <= Length( Line) DO
BEGIN
Character:= Line[ Count];
IF Character = RemarkChar THEN
Count:= Length( Line)
ELSE
IF ( Character > SpaceChar) AND ( Character <> SeparatorChar) THEN
BEGIN
Start:= Count;
Count:= Length( Line);
END;
Inc( Count);
END;
IF Start > 0 THEN
BEGIN
Count:= Start;
Dec( Count);
WHILE Count < Length( Line) DO
BEGIN
Inc( Count);
Character:= Line[ Count];
IF Character = TextChar THEN
BEGIN
IF Line[ Pred( Count)] = TextChar THEN
FoundWord:= FoundWord + TextChar;
TextFlag:= NOT TextFlag;
END
ELSE
IF TextFlag THEN
FoundWord:= FoundWord + Character
ELSE
CASE Character OF
NullChar .. SpaceChar:
IF AbortFlag AND ( Level = 0) THEN
BEGIN
Line:= Copy( Line, Count,
Length( Line) - Pred( Count));
Reading:= False;
Count:= Length( Line);
END
ELSE
IF FoundWord[ Length( FoundWord)] <> SpaceChar THEN
FoundWord:= FoundWord + SpaceChar;
SeparatorChar:
IF Level = 0 THEN
BEGIN
Line:= Copy( Line, Count,
Length( Line) - Pred( Count));
Reading:= False;
Count:= Length( Line);
END
ELSE
FoundWord:= FoundWord + Character;
RemarkChar:
BEGIN
Line:= '';
Reading:= False;
Count:= Length( Line);
END;
StartChar:
BEGIN
Inc( Level);
FoundWord:= FoundWord + Character;
END;
StopChar:
BEGIN
Dec( Level);
FoundWord:= FoundWord + Character;
END;
ELSE
FoundWord:= FoundWord + UpCase( Character);
END;
END;
END;
IF Reading THEN
Line:= '';
WHILE FoundWord[ Length( FoundWord)] = SpaceChar DO
Dec( FoundWord[ 0]);
SplitLine:= FoundWord;
END;
PROCEDURE ReleaseSymbols( VAR SymbHead: SymbolPointer);
BEGIN
IF SymbHead^.Left <> NIL THEN
ReleaseSymbols( SymbHead^.Left);
IF SymbHead^.Right <> NIL THEN
ReleaseSymbols( SymbHead^.Right);
Dispose( SymbHead);
SymbHead:= NIL;
END;
PROCEDURE ReleaseParams( VAR ParamHead: ParamPointer);
VAR
Param: ParamPointer;
BEGIN
WHILE ParamHead <> NIL DO
BEGIN
Param:= ParamHead^.Next;
Dispose( ParamHead);
ParamHead:= Param;
END;
END;
PROCEDURE ReleaseInsts( VAR InstHead: InstPointer);
VAR
Inst: InstPointer;
BEGIN
WHILE InstHead <> NIL DO
BEGIN
ReleaseParams( InstHead^.Params);
Inst:= InstHead^.Prev;
Dispose( InstHead);
InstHead:= Inst;
END;
END;
PROCEDURE AlignWordBounds;
BEGIN
Current:= 2 * ( Succ( Current) DIV 2);
IF Current > Finish THEN
Finish:= Current;
IF ( Current < StartAddress) OR ( Current > StopAddress) THEN
RunError( BoundsError);
END;
FUNCTION ParamCheck( Count, Min, Max: Byte): Boolean;
BEGIN
ParamCheck:= False;
IF ( Count < Min) OR ( Count > Max) THEN
RunWarning( ParamCountWarning);
IF Count >= Min THEN
ParamCheck:= True;
END;
FUNCTION RangeCheck( Value, Min, Max: LongInt; Message: STRING): Boolean;
BEGIN
RangeCheck:= False;
IF ( Value < Min) OR ( Value > Max) THEN
BEGIN
RunWarning( Message);
RangeCheck:= True;
END;
END;
PROCEDURE DecodeFile( Name: PathStr; VAR Head: InstPointer;
Dir: SymbolPointer; VAR Symb: SymbolPointer);
FORWARD;
FUNCTION DecodeParameters( Line: LineString; VAR Head: ParamPointer): Byte;
VAR
FoundWord: ParamString;
Param, Last: ParamPointer;
Count: Byte;
BEGIN
Head:= NIL;
Count:= 0;
Last:= NIL;
REPEAT
FoundWord:= SplitLine( Line, False);
IF FoundWord <> '' THEN
BEGIN
New( Param);
Param^.Param:= FoundWord;
Param^.Next:= NIL;
IF Last = NIL THEN
Head:= Param
ELSE
Last^.Next:= Param;
Last:= Param;
Inc( Count);
END;
UNTIL Line = '';
DecodeParameters:= Count;
END;
PROCEDURE DecodeDirective( Direct: Token; Line: LineString; Valid: Boolean;
VAR Head: InstPointer; Dir: SymbolPointer; VAR Symb: SymbolPointer);
VAR
Inst: InstPointer;
ParamHead: ParamPointer;
Value: LongInt;
Count: Byte;
BEGIN
Count:= DecodeParameters( Line, ParamHead);
CASE Direct OF
EquToken, EqualToken:
IF NOT Valid THEN
RunWarning( NoSymbolWarning)
ELSE
IF ParamCheck( Count, 1, 1) THEN
BEGIN
IF NOT ResolveExpression( ParamHead^.Param, Value, Symb) THEN
RunWarning( UndefinedWarning);
IF LastSymbol <> NIL THEN
LastSymbol^.Address:= Value;
END;
DsToken:
IF ParamCheck( Count, 1, 1) THEN
BEGIN
IF NOT ResolveExpression( ParamHead^.Param, Value, Symb) THEN
RunWarning( UndefinedWarning);
Inc( Current, Value);
END;
OrgToken:
IF ParamCheck( Count, 1, 1) THEN
BEGIN
IF ResolveExpression( ParamHead^.Param, Value, Symb) THEN
Current:= Value
ELSE
RunWarning( UndefinedWarning);
END;
IncludeToken:
BEGIN
IF ParamCheck( Count, 1, 1) THEN
DecodeFile( ParamHead^.Param, Head, Dir, Symb);
ReleaseParams( ParamHead);
END;
EndToken:
BEGIN
IF ParamCheck( Count, 0, 0) THEN
{ The END directive is ignored };
ReleaseParams( ParamHead);
END;
ELSE
New( Inst);
Inst^.Line:= LineNum;
Inst^.Name:= LineName;
Inst^.Address:= Current;
Inst^.Inst:= Direct;
Inst^.Count:= Count;
Inst^.Params:= ParamHead;
Inst^.Next:= NIL;
Inst^.Prev:= Head;
IF Head <> NIL THEN
Head^.Next:= Inst;
Head:= Inst;
CASE Direct OF
DbToken:
IF ParamCheck( Count, 1, MaxParams) THEN
Inc( Current, Count);
DwToken:
IF ParamCheck( Count, 1, MaxParams) THEN
Inc( Current, 2 * Count);
DaToken:
IF ParamCheck( Count, 1, 1) THEN
Inc( Current, Length( ParamHead^.Param));
ELSE
Inc( Current, 2);
END;
END;
END;
PROCEDURE DecodeLine( Line: LineString; VAR Head: InstPointer;
Dir: SymbolPointer; VAR Symb: SymbolPointer);
VAR
Direct: ParamString;
Value: LongInt;
Valid: Boolean;
BEGIN
Valid:= False;
REPEAT
Direct:= SplitLine( Line, True);
IF Direct <> '' THEN
BEGIN
IF NOT ResolveSymbol( Direct, Value, Dir) THEN
BEGIN
IF Valid THEN
RunWarning( DualSymbolWarning);
IF NOT DefineSymbol( Direct, Current, Symb) THEN
RunWarning( CopySymbolWarning);
Valid:= True;
END
ELSE
BEGIN
DecodeDirective( Token( Value), Line, Valid, Head, Dir, Symb);
AlignWordBounds;
Line:= '';
END;
END;
UNTIL Line = '';
END;
PROCEDURE DecodeFile( Name: PathStr; VAR Head: InstPointer;
Dir: SymbolPointer; VAR Symb: SymbolPointer);
VAR
InFile: Text;
Line: LineString;
StoreName: PathStr;
StoreNum: Word;
BEGIN
StoreName:= LineName;
StoreNum:= LineNum;
LineName:= ExpandFileName( Name, NameDefault, InExtDefault);
LineNum:= 1;
WriteLn( StdOut, 'Reading: ', LineName);
WriteLn( StdOut);
WriteLn( ListFile, 'Reading: ', LineName);
WriteLn( ListFile);
Assign( InFile, LineName);
{$I-}
ReSet( InFile);
{$I+}
IF IOResult <> 0 THEN
RunError( FileAccessError);
WHILE NOT EOF( InFile) DO
BEGIN
ReadLn( InFile, Line);
LineText:= Line;
DecodeLine( Line, Head, Dir, Symb);
Inc( LineNum);
END;
Close( InFile);
LineName:= StoreName;
LineNum:= StoreNum;
IF LineName <> NameDefault THEN
BEGIN
WriteLn( StdOut, 'Reading: ', LineName);
WriteLn( StdOut);
WriteLn( ListFile, 'Reading: ', LineName);
WriteLn( ListFile);
END
ELSE
BEGIN
WriteLn( StdOut, 'Done reading');
WriteLn( StdOut);
WriteLn( ListFile, 'Done reading');
WriteLn( ListFile);
END;
END;
PROCEDURE EncodeNoneToken( Instruct: InstPointer; OpCode: Word;
Reg, Symb: SymbolPointer);
BEGIN
WITH Instruct^ DO
BEGIN
IF ParamCheck( Count, 0, 0) THEN
{ Generate instruction anyway };
Memory[ Address]:= OpCode DIV 256;
Memory[ Succ( Address)]:= OpCode MOD 256;
ListInstruction( Address, 2, Instruct, 0);
END;
END;
PROCEDURE EncodeAddrToken( Instruct: InstPointer; OpCode: Word;
Reg, Symb: SymbolPointer);
VAR
Addr: LongInt;
BEGIN
Addr:= 0;
WITH Instruct^ DO
BEGIN
IF ParamCheck( Count, 1, 1) THEN
IF NOT ResolveExpression( Params^.Param, Addr, Symb) THEN
RunWarning( UndefinedWarning);
IF RangeCheck( Addr, 0, AddrMask, RangeWarning) THEN
Addr:= Addr AND AddrMask;
Addr:= OpCode OR Addr;
Memory[ Address]:= Addr DIV 256;
Memory[ Succ( Address)]:= Addr MOD 256;
ListInstruction( Address, 2, Instruct, 1);
END;
END;
PROCEDURE EncodeRegToken( Instruct: InstPointer; OpCode: Word;
Reg, Symb: SymbolPointer);
VAR
RegX: LongInt;
BEGIN
RegX:= Ord( V0Reg);
WITH Instruct^ DO
BEGIN
IF ParamCheck( Count, 1, 1) THEN
IF ResolveSymbol( Params^.Param, RegX, Reg) THEN
RegX:= RegX - Ord( V0Reg)
ELSE
RunWarning( NoRegisterWarning);
IF RangeCheck( RegX, 0, NibbleMask, BadRegisterWarning) THEN
RegX:= 0;
RegX:= OpCode OR $100 * RegX;
Memory[ Address]:= RegX DIV 256;
Memory[ Succ( Address)]:= RegX MOD 256;
ListInstruction( Address, 2, Instruct, 1);
END;
END;
PROCEDURE EncodeRegValToken( Instruct: InstPointer; OpCode: Word;
Reg, Symb: SymbolPointer);
VAR
RegX, Value: LongInt;
BEGIN
RegX:= Ord( V0Reg);
Value:= 0;
WITH Instruct^ DO
BEGIN
IF Count >= 1 THEN
IF ResolveSymbol( Params^.Param, RegX, Reg) THEN
RegX:= RegX - Ord( V0Reg)
ELSE
RunWarning( NoRegisterWarning);
IF RangeCheck( RegX, 0, NibbleMask, BadRegisterWarning) THEN
RegX:= 0;
IF ParamCheck( Count, 2, 2) THEN
IF NOT ResolveExpression( Params^.Next^.Param, Value, Symb) THEN
RunWarning( UndefinedWarning);
IF RangeCheck( Value, 0, ByteMask, RangeWarning) THEN
Value:= Value AND ByteMask;
Value:= OpCode OR $100 * RegX OR Value;
Memory[ Address]:= Value DIV 256;
Memory[ Succ( Address)]:= Value MOD 256;
ListInstruction( Address, 2, Instruct, 2);
END;
END;
PROCEDURE EncodeRegRegToken( Instruct: InstPointer; OpCode: Word; Min: Byte;
Reg, Symb: SymbolPointer);
VAR
RegX, RegY: LongInt;
BEGIN
RegX:= Ord( V0Reg);
RegY:= Ord( V0Reg);
WITH Instruct^ DO
BEGIN
IF ParamCheck( Count, Min, 2) THEN
IF ResolveSymbol( Params^.Param, RegX, Reg) THEN
RegX:= RegX - Ord( V0Reg)
ELSE
RunWarning( NoRegisterWarning);
IF RangeCheck( RegX, 0, NibbleMask, BadRegisterWarning) THEN
RegX:= 0;
IF Count >= 2 THEN
IF ResolveSymbol( Params^.Next^.Param, RegY, Reg) THEN
RegY:= RegY - Ord( V0Reg)
ELSE
RunWarning( NoRegisterWarning);
IF RangeCheck( RegY, 0, NibbleMask, BadRegisterWarning) THEN
RegY:= 0;
RegX:= OpCode OR $100 * RegX OR $10 * RegY;
Memory[ Address]:= RegX DIV 256;
Memory[ Succ( Address)]:= RegX MOD 256;
ListInstruction( Address, 2, Instruct, 2);
END;
END;
PROCEDURE EncodeRegRegOrValToken( Instruct: InstPointer;
OpCode1, OpCode2: Word; Reg, Symb: SymbolPointer);
VAR
RegX, RegY, Value: LongInt;
BEGIN
RegX:= Ord( V0Reg);
RegY:= Ord( V0Reg);
Value:= 0;
WITH Instruct^ DO
BEGIN
IF Count >= 1 THEN
IF ResolveSymbol( Params^.Param, RegX, Reg) THEN
RegX:= RegX - Ord( V0Reg)
ELSE
RunWarning( NoRegisterWarning);
IF RangeCheck( RegX, 0, NibbleMask, BadRegisterWarning) THEN
RegX:= 0;
IF ParamCheck( Count, 2, 2) THEN
IF ResolveSymbol( Params^.Next^.Param, RegY, Reg) THEN
BEGIN
RegY:= RegY - Ord( V0Reg);
IF RangeCheck( RegY, 0, NibbleMask, BadRegisterWarning) THEN
RegY:= 0;
Value:= OpCode1 OR $100 * RegX OR $10 * RegY;
Memory[ Address]:= Value DIV 256;
Memory[ Succ( Address)]:= Value MOD 256;
ListInstruction( Address, 2, Instruct, 2);
END
ELSE
BEGIN
IF NOT ResolveExpression( Params^.Next^.Param, Value, Symb) THEN
RunWarning( UndefinedWarning);
IF RangeCheck( Value, 0, ByteMask, RangeWarning) THEN
Value:= Value AND ByteMask;
Value:= OpCode2 OR $100 * RegX OR Value;
Memory[ Address]:= Value DIV 256;
Memory[ Succ( Address)]:= Value MOD 256;
ListInstruction( Address, 2, Instruct, 2);
END;
END;
END;
PROCEDURE EncodeRegRegValToken( Instruct: InstPointer; OpCode: Word;
Reg, Symb: SymbolPointer);
VAR
RegX, RegY, Value: LongInt;
BEGIN
RegX:= Ord( V0Reg);
RegY:= Ord( V0Reg);
Value:= 0;
WITH Instruct^ DO
BEGIN
IF Count >= 1 THEN
IF ResolveSymbol( Params^.Param, RegX, Reg) THEN
RegX:= RegX - Ord( V0Reg)
ELSE
RunWarning( NoRegisterWarning);
IF RangeCheck( RegX, 0, NibbleMask, BadRegisterWarning) THEN
RegX:= 0;
IF Count >= 2 THEN
IF ResolveSymbol( Params^.Next^.Param, RegY, Reg) THEN
RegY:= RegY - Ord( V0Reg)
ELSE
RunWarning( NoRegisterWarning);
IF RangeCheck( RegY, 0, NibbleMask, BadRegisterWarning) THEN
RegY:= 0;
IF ParamCheck( Count, 3, 3) THEN
IF NOT ResolveExpression( Params^.Next^.Next^.Param, Value,
Symb) THEN
RunWarning( UndefinedWarning);
IF RangeCheck( Value, 0, NibbleMask, RangeWarning) THEN
Value:= Value AND NibbleMask;
Value:= OpCode OR $100 * RegX OR $10 * RegY OR Value;
Memory[ Address]:= Value DIV 256;
Memory[ Succ( Address)]:= Value MOD 256;
ListInstruction( Address, 2, Instruct, 3);
END;
END;
PROCEDURE EncodeAddToken( Instruct: InstPointer; Reg, Symb: SymbolPointer);
VAR
RegX, RegY, Value: LongInt;
BEGIN
RegX:= Ord( V0Reg);
RegY:= Ord( V0Reg);
Value:= 0;
WITH Instruct^ DO
BEGIN
IF Count >= 1 THEN
IF NOT ResolveSymbol( Params^.Param, RegX, Reg) THEN
RunWarning( NoRegisterWarning);
IF RegX = Ord( IReg) THEN
BEGIN
IF ParamCheck( Count, 2, 2) THEN
IF ResolveSymbol( Params^.Next^.Param, RegX, Reg) THEN
RegX:= RegX - Ord( V0Reg)
ELSE
RunWarning( NoRegisterWarning);
IF RangeCheck( RegX, 0, NibbleMask, BadRegisterWarning) THEN
RegX:= 0;
Value:= $f01e OR $100 * RegX;
Memory[ Address]:= Value DIV 256;
Memory[ Succ( Address)]:= Value MOD 256;
ListInstruction( Address, 2, Instruct, 2);
END
ELSE
BEGIN
RegX:= RegX - Ord( V0Reg);
IF RangeCheck( RegX, 0, NibbleMask, BadRegisterWarning) THEN
RegX:= 0;
IF ParamCheck( Count, 2, 2) THEN
IF ResolveSymbol( Params^.Next^.Param, RegY, Reg) THEN
BEGIN
RegY:= RegY - Ord( V0Reg);
IF RangeCheck( RegY, 0, NibbleMask, BadRegisterWarning) THEN
RegY:= 0;
Value:= $8004 OR $100 * RegX OR $10 * RegY;
Memory[ Address]:= Value DIV 256;
Memory[ Succ( Address)]:= Value MOD 256;
ListInstruction( Address, 2, Instruct, 2);
END
ELSE
BEGIN
IF NOT ResolveExpression( Params^.Next^.Param, Value,
Symb) THEN
RunWarning( UndefinedWarning);
IF RangeCheck( Value, 0, ByteMask, RangeWarning) THEN
Value:= Value AND ByteMask;
Value:= $7000 OR $100 * RegX OR Value;
Memory[ Address]:= Value DIV 256;
Memory[ Succ( Address)]:= Value MOD 256;
ListInstruction( Address, 2, Instruct, 2);
END;
END;
END;
END;
PROCEDURE EncodeJpToken( Instruct: InstPointer; Reg, Symb: SymbolPointer);
VAR
RegX, Addr: LongInt;
BEGIN
RegX:= Ord( V0Reg);
Addr:= Ord( V0Reg);
WITH Instruct^ DO
IF ParamCheck( Count, 1, 2) THEN
IF ResolveSymbol( Params^.Param, RegX, Reg) THEN
BEGIN
IF RegX <> Ord( V0Reg) THEN
RunWarning( BadRegisterWarning);
IF Count = 1 THEN
RunWarning( ParamCountWarning)
ELSE
IF NOT ResolveExpression( Params^.Next^.Param, Addr, Symb) THEN
RunWarning( UndefinedWarning);
IF RangeCheck( Addr, 0, AddrMask, RangeWarning) THEN
Addr:= Addr AND AddrMask;
Addr:= $b000 OR Addr;
Memory[ Address]:= Addr DIV 256;
Memory[ Succ( Address)]:= Addr MOD 256;
ListInstruction( Address, 2, Instruct, 2);
END
ELSE
BEGIN
IF NOT ResolveExpression( Params^.Param, Addr, Symb) THEN
RunWarning( UndefinedWarning);
IF Count = 2 THEN
RunWarning( ParamCountWarning);
IF RangeCheck( Addr, 0, AddrMask, RangeWarning) THEN
Addr:= Addr AND AddrMask;
Addr:= $1000 OR Addr;
Memory[ Address]:= Addr DIV 256;
Memory[ Succ( Address)]:= Addr MOD 256;
ListInstruction( Address, 2, Instruct, 1);
END;
END;
PROCEDURE EncodeLdToken( Instruct: InstPointer; Reg, Symb: SymbolPointer);
VAR
RegX, RegY, Value: LongInt;
RegFlag: Boolean;
BEGIN
RegX:= Ord( V0Reg);
RegY:= Ord( V0Reg);
Value:= 0;
RegFlag:= True;
WITH Instruct^ DO
BEGIN
IF Count >= 1 THEN
IF NOT ResolveSymbol( Params^.Param, RegX, Reg) THEN
RunWarning( NoRegisterWarning);
CASE RegX OF
Ord( BReg), Ord( DtReg), Ord( FReg), Ord( StReg), Ord( IiReg):
BEGIN
RegY:= RegX;
RegX:= Ord( V0Reg);
IF ParamCheck( Count, 2, 2) THEN
IF ResolveSymbol( Params^.Next^.Param, RegX, Reg) THEN
RegX:= RegX - Ord( V0Reg)
ELSE
RunWarning( NoRegisterWarning);
IF RangeCheck( RegX, 0, NibbleMask, BadRegisterWarning) THEN
RegX:= 0;
CASE RegY OF
Ord( BReg):
Value:= $f033 OR $100 * RegX;
Ord( DtReg):
Value:= $f015 OR $100 * RegX;
Ord( Freg):
Value:= $f029 OR $100 * RegX;
Ord( StReg):
Value:= $f018 OR $100 * RegX;
Ord( IiReg):
Value:= $f055 OR $100 * RegX;
ELSE
RunWarning( InternalWarning);
END;
Memory[ Address]:= Value DIV 256;
Memory[ Succ( Address)]:= Value MOD 256;
ListInstruction( Address, 2, Instruct, 2);
END;
Ord( IReg):
BEGIN
IF ParamCheck( Count, 2, 2) THEN
IF NOT ResolveExpression( Params^.Next^.Param, Value,
Symb) THEN
RunWarning( UndefinedWarning);
IF RangeCheck( Value, 0, AddrMask, RangeWarning) THEN
Value:= Value AND AddrMask;
Value:= $a000 OR Value;
Memory[ Address]:= Value DIV 256;
Memory[ Succ( Address)]:= Value MOD 256;
ListInstruction( Address, 2, Instruct, 2);
END;
ELSE
RegX:= RegX - Ord( V0Reg);
IF RangeCheck( RegX, 0, NibbleMask, BadRegisterWarning) THEN
RegX:= 0;
IF ParamCheck( Count, 2, 2) THEN
RegFlag:= ResolveSymbol( Params^.Next^.Param, RegY, Reg);
IF RegFlag THEN
CASE RegY OF
Ord( DtReg):
BEGIN
Value:= $f007 OR $100 * RegX;
Memory[ Address]:= Value DIV 256;
Memory[ Succ( Address)]:= Value MOD 256;
ListInstruction( Address, 2, Instruct, 2);
END;
Ord( KReg):
BEGIN
Value:= $f00a OR $100 * RegX;
Memory[ Address]:= Value DIV 256;
Memory[ Succ( Address)]:= Value MOD 256;
ListInstruction( Address, 2, Instruct, 2);
END;
Ord( IiReg):
BEGIN
Value:= $f065 OR $100 * RegX;
Memory[ Address]:= Value DIV 256;
Memory[ Succ( Address)]:= Value MOD 256;
ListInstruction( Address, 2, Instruct, 2);
END;
ELSE
RegY:= RegY - Ord( V0Reg);
IF RangeCheck( RegY, 0, NibbleMask,
BadRegisterWarning) THEN
RegY:= 0;
Value:= $8000 OR $100 * RegX OR $10 * RegY;
Memory[ Address]:= Value DIV 256;
Memory[ Succ( Address)]:= Value MOD 256;
ListInstruction( Address, 2, Instruct, 2);
END
ELSE
BEGIN
IF Count >= 2 THEN
IF NOT ResolveExpression( Params^.Next^.Param, Value,
Symb) THEN
RunWarning( UndefinedWarning);
IF RangeCheck( Value, 0, ByteMask, RangeWarning) THEN
Value:= Value AND ByteMask;
Value:= $6000 OR $100 * RegX OR Value;
Memory[ Address]:= Value DIV 256;
Memory[ Succ( Address)]:= Value MOD 256;
ListInstruction( Address, 2, Instruct, 2);
END;
END;
END;
END;
PROCEDURE EncodeDaToken( Instruct: InstPointer; Reg, Symb: SymbolPointer);
VAR
Param: ParamString;
This: Byte;
BEGIN
Param:= '';
WITH Instruct^ DO
BEGIN
IF Count >= 1 THEN
Param:= Params^.Param;
FOR This:= 1 TO Length( Param) DO
Memory[ Address + Pred( This)]:= Ord( Param[ This]);
ListInstruction( Address, Length( Param), Instruct, 1);
END;
END;
PROCEDURE EncodeDbToken( Instruct: InstPointer; Reg, Symb: SymbolPointer);
VAR
Param: ParamPointer;
Value: LongInt;
This: Byte;
BEGIN
This:= 0;
Value:= 0;
WITH Instruct^ DO
BEGIN
Param:= Params;
WHILE Param <> NIL DO
BEGIN
IF NOT ResolveExpression( Param^.Param, Value, Symb) THEN
RunWarning( UndefinedWarning);
IF RangeCheck( Value, 0, ByteMask, RangeWarning) THEN
Value:= Value AND ByteMask;
Memory[ Address + This]:= Value;
Inc( This);
Param:= Param^.Next;
END;
ListInstruction( Address, Count, Instruct, Count);
END;
END;
PROCEDURE EncodeDwToken( Instruct: InstPointer; Reg, Symb: SymbolPointer);
VAR
Param: ParamPointer;
Value: LongInt;
This: Byte;
BEGIN
This:= 0;
Value:= 0;
WITH Instruct^ DO
BEGIN
Param:= Params;
WHILE Param <> NIL DO
BEGIN
IF NOT ResolveExpression( Param^.Param, Value, Symb) THEN
RunWarning( UndefinedWarning);
IF RangeCheck( Value, 0, WordMask, RangeWarning) THEN
Value:= Value AND WordMask;
Memory[ Address + This]:= Value DIV 256;
Memory[ Address + Succ( This)]:= Value MOD 256;
Inc( This, 2);
Param:= Param^.Next;
END;
ListInstruction( Address, 2 * Count, Instruct, Count);
END;
END;
PROCEDURE EncodeInstruction( Inst: InstPointer; Reg, Symb: SymbolPointer);
BEGIN
CASE Inst^.Inst OF
AddToken:
EncodeAddToken( Inst, Reg, Symb);
AndToken:
EncodeRegRegToken( Inst, $8002, 2, Reg, Symb);
CallToken:
EncodeAddrToken( Inst, $2000, Reg, Symb);
ClsToken:
EncodeNoneToken( Inst, $00e0, Reg, Symb);
DaToken:
EncodeDaToken( Inst, Reg, Symb);
DbToken:
EncodeDbToken( Inst, Reg, Symb);
DrwToken:
EncodeRegRegValToken( Inst, $d000, Reg, Symb);
DwToken:
EncodeDwToken( Inst, Reg, Symb);
JpToken:
EncodeJpToken( Inst, Reg, Symb);
LdToken:
EncodeLdToken( Inst, Reg, Symb);
OrToken:
EncodeRegRegToken( Inst, $8001, 2, Reg, Symb);
RetToken:
EncodeNoneToken( Inst, $00ee, Reg, Symb);
RndToken:
EncodeRegValToken( Inst, $c000, Reg, Symb);
SeToken:
EncodeRegRegOrValToken( Inst, $5000, $3000, Reg, Symb);
ShlToken:
EncodeRegRegToken( Inst, $800e, 1, Reg, Symb);
ShrToken:
EncodeRegRegToken( Inst, $8006, 1, Reg, Symb);
SknpToken:
EncodeRegToken( Inst, $e0a1, Reg, Symb);
SkpToken:
EncodeRegToken( Inst, $e09e,Reg, Symb);
SneToken:
EncodeRegRegOrValToken( Inst, $9000, $4000, Reg, Symb);
SubToken:
EncodeRegRegToken( Inst, $8005, 2, Reg, Symb);
SubnToken:
EncodeRegRegToken( Inst, $8007, 2, Reg, Symb);
SysToken:
EncodeAddrToken( Inst, $2000, Reg, Symb);
XorToken:
EncodeRegRegToken( Inst, $8003, 2, Reg, Symb);
ELSE
RunWarning( InternalWarning);
END;
END;
PROCEDURE EncodeMemory( Inst: InstPointer; Reg, Symb: SymbolPointer);
VAR
Prev: InstPointer;
Count: Word;
BEGIN
FOR Count:= StartAddress TO StopAddress DO
Memory[ Count]:= 0;
Prev:= Inst;
WHILE Prev <> NIL DO
BEGIN
Inst:= Prev;
Prev:= Prev^.Prev;
END;
WHILE Inst <> NIL DO
BEGIN
InstPoint:= Inst;
Current:= Inst^.Address;
EncodeInstruction( Inst, Reg, Symb);
Inst:= Inst^.Next;
END;
InstPoint:= NIL;
ListSymbols( Symb);
ListWarnings;
END;
PROCEDURE WriteMemory( Start, Stop: Word);
VAR
HpHeading: ARRAY[ $0 .. $c] OF Byte;
Size, Count: LongInt;
BEGIN
Size:= 2 * ( Stop - Start) + 5;
HpHeading[ $0]:= Ord( 'H');
HpHeading[ $1]:= Ord( 'P');
HpHeading[ $2]:= Ord( 'H');
HpHeading[ $3]:= Ord( 'P');
HpHeading[ $4]:= Ord( '4');
HpHeading[ $5]:= Ord( '8');
HpHeading[ $6]:= Ord( '-');
HpHeading[ $7]:= Ord( 'A');
HpHeading[ $8]:= $2c;
HpHeading[ $9]:= $2a;
HpHeading[ $a]:= $0 OR $10 * ( Size MOD 16);
HpHeading[ $b]:= ( Size DIV 16) MOD 256;
HpHeading[ $c]:= ( Size DIV ( 16 * 256)) MOD 256;
FOR Count:= $0 TO $c DO
Write( OutFile, HpHeading[ Count]);
FOR Count:= Start TO Pred( Stop) DO
Write( OutFile, Memory[ Count]);
END;
BEGIN
Assign( StdIn, '');
Assign( StdOut, '');
ReSet( StdIn);
ReWrite( StdOut);
WriteLn( StdOut, CopyRight);
WriteLn( StdOut);
IF OpenFiles THEN
BEGIN
StoreDirectives( 0, Ord( Pred( LastToken)), Directives);
StoreRegisters( 0, Ord( Pred( LastReg)), Registers);
DecodeFile( InFileName, Instructions, Directives, Symbols);
EncodeMemory( Instructions, Registers, Symbols);
WriteMemory( StartAddress, Finish);
Close( OutFile);
Close( ListFile);
ReleaseSymbols( Symbols);
ReleaseSymbols( Registers);
ReleaseSymbols( Directives);
ReleaseInsts( Instructions);
END;
Close( StdIn);
Close( StdOut);
END.egeberg@solan.unit.no (Christian Egeberg) (06/06/91)
This is the Blinky V1.01 Chipper source
---------------------------------------------------------------------
; Chip-48 Blinky (PacMan) by Christian Egeberg 7/11-'90 .. 11/11-'90
; Register usage:
; V0: Temporary data, may change during any call
; V1: Temporary data, may change during any call
; V2: Temporary data, may change during most calls
; V3: Temporary data, may change during most calls
; V4: Temporary data, may change during some calls
; V5: Temporary data, may change during some calls
; V6: Pill and score counter
; V7: Life and sprite direction register
; V8: Blinky X screen coordinate
; V9: Blinky Y screen coordinate
; VA: Packlett X screen coordinate
; VB: Packlett Y screen coordinate
; VC: Heward X screen coordinate
; VD: Heward Y screen coordinate
; VE: Temporary constant and flag storage
; VF: Flag register
MASKNIBB = $1111
MASKBYTE = $11111111
DOWNKEY = #6
RIGHTKEY = #8
LEFTKEY = #7
UPKEY = #3
PRESSKEY = #F
PILLNUM = 231
SUPERNUM = 4
PILLTIME = 5
SUPERTIME = 255
CLSWAIT = 64
EYEWAIT = 3
PILLADD = 1
SUPERADD = 4
HWRDADD = 25
PKLTADD = 50
SCREENADD = 100
MASKLIFE = $01000000
MASKHUNT = $10000000
MASKCODE = $11
DOWNCODE = $11
RIGHTCODE = $10
LEFTCODE = $01
UPCODE = $00
BLNKCODE = DOWNCODE
BLNKX = 26
BLNKY = 12
PKLTCODE = LEFTCODE
PKLTX = 56
PKLTY = 0
HWRDCODE = RIGHTCODE
HWRDX = 2
HWRDY = 26
GATELEFT = 0
GATERIGHT = 58
SCXPOS = 17
SCYPOS = 16
HIXPOS = 17
HIYPOS = 10
EYEX1 = 0
EYEX2 = 48
EYEY1 = 0
EYEY2 = 22
CLS
JP START
COPYRIGHT: DA 'Chr. Egeberg 11/11-''90'
START: XOR V0, V0
XOR V1, V1
LD I, SCORE
LD [I], V1
REINIT: LD V6, 0
LD V7, 0
CALL COPYMAZE
RESTART: LD VE, MASKLIFE
AND V7, VE
LD VE, HWRDCODE < 4 | PKLTCODE < 2 | BLNKCODE
OR V7, VE
LD V8, BLNKX
LD V9, BLNKY
LD VA, PKLTX
LD VB, PKLTY
LD VC, HWRDX
LD VD, HWRDY
CLS
CALL DRAWMAZE
CALL DRAWBLNK
LD I, GHOST
DRW VA, VB, 4
DRW VC, VD, 4
GAMELOOP: CALL MOVEBLNK
SE VE, 0
JP ENCOUNTER
SPLITUP: CALL MOVEPKLT
CALL MOVEHWRD
SE V6, PILLADD * PILLNUM + SUPERADD * SUPERNUM
JP GAMELOOP
LD VE, V6
CALL ADDSCORE
LD VE, SCREENADD
CALL ADDSCORE
JP REINIT
ENCOUNTER: LD V0, DT
SNE V0, 0
JP GOTCHA
OOPSPKLT: LD V0, V8
SHR V0
SHR V0
SHR V0
LD V1, VA
SHR V1
SHR V1
SHR V1
SE V0, V1
JP OOPSHWRD
LD V0, V9
SHR V0
SHR V0
SHR V0
LD V1, VB
SHR V1
SHR V1
SHR V1
SE V0, V1
JP OOPSHWRD
LD I, GHOST
DRW VA, VB, 4
LD VA, PKLTX
LD VB, PKLTY
DRW VA, VB, 4
LD VE, ~( MASKCODE < 2) & MASKBYTE
AND V7, VE
LD VE, PKLTCODE < 2
OR V7, VE
LD VE, PKLTADD
CALL ADDSCORE
OOPSHWRD: LD V0, V8
SHR V0
SHR V0
SHR V0
LD V1, VC
SHR V1
SHR V1
SHR V1
SE V0, V1
JP SPLITUP
LD V0, V9
SHR V0
SHR V0
SHR V0
LD V1, VD
SHR V1
SHR V1
SHR V1
SE V0, V1
JP SPLITUP
LD I, GHOST
DRW VC, VD, 4
LD VC, HWRDX
LD VD, HWRDY
DRW VC, VD, 4
LD VE, ~( MASKCODE < 4) & MASKBYTE
AND V7, VE
LD VE, HWRDCODE < 4
OR V7, VE
LD VE, HWRDADD
CALL ADDSCORE
JP SPLITUP
GOTCHA: LD V0, CLSWAIT
CALL WAITKEY
LD VE, MASKLIFE
XOR V7, VE
LD V0, V7
AND V0, VE
SE V0, 0
JP RESTART
LD VE, V6
CALL ADDSCORE
CALL NEWHIGH
CLS
LD V6, HIXPOS
LD V7, HIYPOS
LD I, HIGHSCORE
CALL PRINTDEC
LD V6, SCXPOS
LD V7, SCYPOS
LD I, SCORE
CALL PRINTDEC
LD V4, EYEX1
LD V5, EYEX1 + 8
LD V6, EYEY1
LD V7, PRESSKEY
EYEX1LOOP: LD I, EYELEFT
DRW V4, V6, 9
LD I, EYERIGHT
DRW V5, V6, 9
LD V0, EYEWAIT
CALL WAITKEY
SE VE, 0
JP EYEPRESS
LD I, EYELEFT
DRW V4, V6, 9
LD I, EYERIGHT
DRW V5, V6, 9
ADD V4, 2
ADD V5, 2
SE V4, EYEX2
JP EYEX1LOOP
EYEY1LOOP: LD I, EYELEFT
DRW V4, V6, 9
LD I, EYERIGHT
DRW V5, V6, 9
LD V0, EYEWAIT
CALL WAITKEY
SE VE, 0
JP EYEPRESS
LD I, EYELEFT
DRW V4, V6, 9
LD I, EYERIGHT
DRW V5, V6, 9
ADD V6, 2
SE V6, EYEY2
JP EYEY1LOOP
EYEX2LOOP: LD I, EYELEFT
DRW V4, V6, 9
LD I, EYERIGHT
DRW V5, V6, 9
LD V0, EYEWAIT
CALL WAITKEY
SE VE, 0
JP EYEPRESS
LD I, EYELEFT
DRW V4, V6, 9
LD I, EYERIGHT
DRW V5, V6, 9
ADD V4, -2 & MASKBYTE
ADD V5, -2 & MASKBYTE
SE V4, EYEX1
JP EYEX2LOOP
EYEY2LOOP: LD I, EYELEFT
DRW V4, V6, 9
LD I, EYERIGHT
DRW V5, V6, 9
LD V0, EYEWAIT
CALL WAITKEY
SE VE, 0
JP EYEPRESS
LD I, EYELEFT
DRW V4, V6, 9
LD I, EYERIGHT
DRW V5, V6, 9
ADD V6, -2 & MASKBYTE
SE V6, EYEY1
JP EYEY2LOOP
JP EYEX1LOOP
EYEPRESS: LD I, EYERIGHT
DRW V5, V6, 9
LD I, EYEBLINK
DRW V5, V6, 9
JP START
; MOVEBLNK
; ->: Nothing
; <-: VE: Collision flag
; <>: V0, V1, V2, V3, V4, V5, V6, V7, V8, V9, VE, VF, I
MOVEBLNK: LD V3, V7
LD VE, MASKCODE
AND V3, VE
LD V4, V8
LD V5, V9
LD VE, DOWNKEY
SKNP VE
JP BLNKDOWN
LD VE, UPKEY
SKNP VE
JP BLNKUP
LD VE, RIGHTKEY
SKNP VE
JP BLNKRIGHT
LD VE, LEFTKEY
SKNP VE
JP BLNKLEFT
NOKEY: SNE V3, DOWNCODE
ADD V5, 2
SNE V3, UPCODE
ADD V5, -2 & MASKBYTE
SNE V3, RIGHTCODE
ADD V4, 2
SNE V3, LEFTCODE
ADD V4, -2 & MASKBYTE
LD V0, V4
LD V1, V5
CALL SPRITMAZE
LD V2, V0
LD VE, GRAPHEDGE
AND V0, VE
DONEKEY: SE V0, 0
JP STOPBLNK
LD VE, GRAPHSPEC
LD V0, V2
AND V2, VE
SNE V2, PL
JP EATPILL
SNE V2, SP
JP EATSUPER
SNE V2, GW
JP GATEWAY
DONEEAT: CALL DRAWBLNK
LD VE, ~MASKCODE & MASKBYTE
AND V7, VE
OR V7, V3
LD V8, V4
LD V9, V5
JP DRAWBLNK
BLNKDOWN: LD V0, V4
LD V1, V5
ADD V1, 2
CALL SPRITMAZE
LD V2, V0
LD VE, GRAPHEDGE
AND V0, VE
SE V0, 0
JP NOKEY
LD V3, DOWNCODE
ADD V5, 2
JP DONEKEY
BLNKUP: LD V0, V4
LD V1, V5
ADD V1, -2 & MASKBYTE
CALL SPRITMAZE
LD V2, V0
LD VE, GRAPHEDGE
AND V0, VE
SE V0, 0
JP NOKEY
LD V3, UPCODE
ADD V5, -2 & MASKBYTE
JP DONEKEY
BLNKRIGHT: LD V0, V4
LD V1, V5
ADD V0, 2
CALL SPRITMAZE
LD V2, V0
LD VE, GRAPHEDGE
AND V0, VE
SE V0, 0
JP NOKEY
LD V3, RIGHTCODE
ADD V4, 2
JP DONEKEY
BLNKLEFT: LD V0, V4
LD V1, V5
ADD V0, -2 & MASKBYTE
CALL SPRITMAZE
LD V2, V0
LD VE, GRAPHEDGE
AND V0, VE
SE V0, 0
JP NOKEY
LD V3, LEFTCODE
ADD V4, -2 & MASKBYTE
JP DONEKEY
STOPBLNK: CALL DRAWBLNK
DRW V8, V9, 4
LD VE, VF
RET
EATPILL: LD VE, ~MASKNIBB & MASKBYTE
AND V0, VE
OR V0, V3
LD [I], V0
LD I, PILL
DRW V4, V5, 4
ADD V6, PILLADD
LD V1, PILLTIME
LD V0, DT
SNE V0, 0
LD ST, V1
JP DONEEAT
EATSUPER: LD VE, ~MASKNIBB & MASKBYTE
AND V0, VE
OR V0, V3
LD [I], V0
LD I, SUPER
DRW V4, V5, 4
ADD V6, SUPERADD
LD V0, VA
LD V1, VB
CALL SPRITMAZE
LD VE, ~MASKNIBB & MASKBYTE
AND V0, VE
SE V0, 0
JP SKIPPKLT
LD VE, MASKCODE < 2
XOR V7, VE
SKIPPKLT: LD V0, VC
LD V1, VD
CALL SPRITMAZE
LD VE, ~MASKNIBB & MASKBYTE
AND V0, VE
SE V0, 0
JP SKIPHWRD
LD VE, MASKCODE < 4
XOR V7, VE
SKIPHWRD: LD V0, SUPERTIME
LD ST, V0
LD DT, V0
JP DONEEAT
GATEWAY: SNE V3, LEFTCODE
LD V4, GATERIGHT
SNE V3, RIGHTCODE
LD V4, GATELEFT
JP DONEEAT
; MOVEPKLT
; ->: Nothing
; <-: Nothing
; <>: V0, V1, V2, V7, VA, VB, VE, VF, I
MOVEPKLT: LD V2, V7
LD VE, MASKCODE < 2
AND V2, VE
LD V0, VA
LD V1, VB
CALL SPRITMAZE
LD I, GHOST
LD VE, ~MASKNIBB & MASKBYTE
AND V0, VE
SE V0, 0
JP RANDPKLT
TURNPKLT: DRW VA, VB, 4
SNE V2, DOWNCODE < 2
ADD VB, 2
SNE V2, UPCODE < 2
ADD VB, -2 & MASKBYTE
SNE V2, RIGHTCODE < 2
ADD VA, 2
SNE V2, LEFTCODE < 2
ADD VA, -2 & MASKBYTE
DRW VA, VB, 4
RET
RANDPKLT: RND V1, ~MASKNIBB & MASKBYTE
AND V0, V1
SE V0, 0
JP SETPKLT
PKLTERR: LD VE, MASKCODE < 2
XOR V7, VE
XOR V2, VE
JP TURNPKLT
SETPKLT: DRW VA, VB, 4
PKLTL: SHL V0
SNE VF, 0
JP PKLTD
LD V2, LEFTCODE < 2
ADD VA, -2 & MASKBYTE
JP PKLTSET
PKLTD: SHL V0
SNE VF, 0
JP PKLTR
LD V2, DOWNCODE < 2
ADD VB, 2
JP PKLTSET
PKLTR: SHL V0
SNE VF, 0
JP PKLTU
LD V2, RIGHTCODE < 2
ADD VA, 2
JP PKLTSET
PKLTU: SHL V0
SNE VF, 0
JP PKLTERR
LD V2, UPCODE < 2
ADD VB, -2 & MASKBYTE
PKLTSET: DRW VA, VB, 4
LD VE, ~( MASKCODE < 2) & MASKBYTE
AND V7, VE
OR V7, V2
RET
; MOVEHWRD
; ->: Nothing
; <-: Nothing
; <>: V0, V1, V2, V3, V7, VC, VD, VE, VF, I
MOVEHWRD: LD V2, V7
LD V3, V7
LD VE, MASKCODE < 4
AND V2, VE
LD V0, VC
LD V1, VD
CALL SPRITMAZE
LD I, GHOST
LD VE, ~MASKNIBB & MASKBYTE
AND V0, VE
SE V0, 0
JP LOOKHWRD
TURNHWRD: DRW VC, VD, 4
SNE V2, DOWNCODE < 4
ADD VD, 2
SNE V2, UPCODE < 4
ADD VD, -2 & MASKBYTE
SNE V2, RIGHTCODE < 4
ADD VC, 2
SNE V2, LEFTCODE < 4
ADD VC, -2 & MASKBYTE
DRW VC, VD, 4
RET
LOOKHWRD: LD VE, MASKHUNT
LD V1, DT
SE V1, 0
JP RANDHWRD
LD V1, V0
SHL V3
SNE VF, 0
JP HORISHWRD
VERTHWRD: LD V3, V9
SUB V3, VD
SNE VF, 0
JP HWRDLU
SE V3, 0
JP HWRDLD
XOR V7, VE
LD V3, V8
SUB V3, VC
SNE VF, 0
JP HWRDLL
SE V3, 0
JP HWRDLR
XOR V7, VE
JP RANDHWRD
HORISHWRD: LD V3, V8
SUB V3, VC
SNE VF, 0
JP HWRDLL
SE V3, 0
JP HWRDLR
XOR V7, VE
LD V3, V9
SUB V3, VD
SNE VF, 0
JP HWRDLU
SE V3, 0
JP HWRDLD
XOR V7, VE
JP RANDHWRD
HWRDLD: LD V3, MD
AND V1, V3
SNE V1, 0
JP RANDHWRD
DRW VC, VD, 4
ADD VD, 2
DRW VC, VD, 4
XOR V7, VE
LD VE, ~( MASKCODE < 4) & MASKBYTE
AND V7, VE
LD V2, DOWNCODE < 4
OR V7, V2
RET
HWRDLU: LD V3, MU
AND V1, V3
SNE V1, 0
JP RANDHWRD
DRW VC, VD, 4
ADD VD, -2 & MASKBYTE
DRW VC, VD, 4
XOR V7, VE
LD VE, ~( MASKCODE < 4) & MASKBYTE
AND V7, VE
LD V2, UPCODE < 4
OR V7, V2
RET
HWRDLR: LD V3, MR
AND V1, V3
SNE V1, 0
JP RANDHWRD
DRW VC, VD, 4
ADD VC, 2
DRW VC, VD, 4
XOR V7, VE
LD VE, ~( MASKCODE < 4) & MASKBYTE
AND V7, VE
LD V2, RIGHTCODE < 4
OR V7, V2
RET
HWRDLL: LD V3, ML
AND V1, V3
SNE V1, 0
JP RANDHWRD
DRW VC, VD, 4
ADD VC, -2 & MASKBYTE
DRW VC, VD, 4
XOR V7, VE
LD VE, ~( MASKCODE < 4) & MASKBYTE
AND V7, VE
LD V2, LEFTCODE < 4
OR V7, V2
RET
RANDHWRD: RND V1, ~MASKNIBB & MASKBYTE
AND V0, V1
SE V0, 0
JP SETHWRD
HWRDERR: XOR V7, VE
LD VE, MASKCODE < 4
XOR V7, VE
XOR V2, VE
JP TURNHWRD
SETHWRD: DRW VC, VD, 4
HWRDRL: SHL V0
SNE VF, 0
JP HWRDRD
LD V2, LEFTCODE < 4 | MASKHUNT
ADD VC, -2 & MASKBYTE
JP HWRDSET
HWRDRD: SHL V0
SNE VF, 0
JP HWRDRR
LD V2, DOWNCODE < 4
ADD VD, 2
JP HWRDSET
HWRDRR: SHL V0
SNE VF, 0
JP HWRDRU
LD V2, RIGHTCODE < 4 | MASKHUNT
ADD VC, 2
JP HWRDSET
HWRDRU: SHL V0
SNE VF, 0
JP HWRDERR
LD V2, UPCODE < 4
ADD VD, -2 & MASKBYTE
HWRDSET: DRW VC, VD, 4
LD VE, ~( MASKCODE < 4 | MASKHUNT) & MASKBYTE
AND V7, VE
OR V7, V2
RET
; DRAWBLNK
; -> V7: Sprite direction register
; -> V8: Blinky X screen coordinate
; -> V9: Blinky Y screen coordinate
; <- VE: Collision flag
; <- I: Blinky sprite pointer
; <> V0, VE, VF, I
DRAWBLNK: LD V0, V7
LD VE, 3
AND V0, VE
SHL V0
SHL V0
LD I, SPRITES
ADD I, V0
DRW V8, V9, 4
LD VE, VF
RET
; COPYMAZE
; -> Nothing
; <- Nothing
; <> V0, V1, V2, V3, VE, VF, I
COPYMAZE: LD VE, 0
COPYLOOP: LD I, MAZE
ADD I, VE
ADD I, VE
ADD I, VE
ADD I, VE
LD V3, [I]
LD I, BUFFER
ADD I, VE
ADD I, VE
ADD I, VE
ADD I, VE
LD [I], V3
ADD VE, 1
SE VE, MAZEEND - MAZE \ 4
JP COPYLOOP
RET
; DRAWMAZE
; -> Nothing
; <- Nothing
; <> V0, V1, V2, V3, VE, VF, I
DRAWMAZE: XOR V2, V2
XOR V3, V3
LD VE, 15
DRAWLOOP: LD V0, V2
LD V1, V3
CALL GRAPHMAZE
AND V0, VE
SHL V0
LD I, GRAPHS
ADD I, V0
DRW V2, V3, 2
ADD V2, 2
SE V2, 64
JP DRAWLOOP
XOR V2, V2
ADD V3, 2
SNE V3, 32
RET
JP DRAWLOOP
; SPRITMAZE, GRAPHMAZE
; -> V0: X coordinate
; -> V1: Y coordinate
; <- V0: Maze data byte
; <- I: Maze data pointer
; <> V0, V1, VF, I
SPRITMAZE: ADD V0, 2
ADD V1, 2
GRAPHMAZE: SHR V0
SHR V1
SHL V1
SHL V1
SHL V1
SHL V1
LD I, BUFFER
ADD I, V1
ADD I, V1
ADD I, V0
LD V0, [I]
RET
; PRINTDEC
; -> V6: Print X coordinate
; -> V7: Print Y coordinate
; -> I: 16 bit value pointer
; <- Nothing
; <> V0, V1, V2, V3, V4, V5, V6, V7, VE, VF, I
PRINTDEC: LD V1, [I]
LD VE, 1
XOR V4, V4
LD V2, V0
LD V3, V1
LOOPTENG: LD V5, 10000 % 256
SUB V3, V5
SNE VF, 0
SUB V2, VE
SNE VF, 0
JP SKIPTENG
LD V5, 10000 \ 256
SUB V2, V5
SNE VF, 0
JP SKIPTENG
LD V0, V2
LD V1, V3
ADD V4, VE
JP LOOPTENG
SKIPTENG: LD F, V4
DRW V6, V7, 5
ADD V6, 6
XOR V4, V4
LD V2, V0
LD V3, V1
LOOPTHOUS: LD V5, 1000 % 256
SUB V3, V5
SNE VF, 0
SUB V2, VE
SNE VF, 0
JP SKIPTHOUS
LD V5, 1000 \ 256
SUB V2, V5
SNE VF, 0
JP SKIPTHOUS
LD V0, V2
LD V1, V3
ADD V4, VE
JP LOOPTHOUS
SKIPTHOUS: LD F, V4
DRW V6, V7, 5
ADD V6, 6
XOR V4, V4
LD V2, V0
LD V3, V1
LOOPHUNDR: LD V5, 100
SUB V3, V5
SNE VF, 0
SUB V2, VE
SNE VF, 0
JP SKIPHUNDR
LD V0, V2
LD V1, V3
ADD V4, VE
JP LOOPHUNDR
SKIPHUNDR: LD F, V4
DRW V6, V7, 5
ADD V6, 6
XOR V4, V4
LD V2, V0
LD V3, V1
LOOPTEN: LD V5, 10
SUB V3, V5
SNE VF, 0
JP SKIPTEN
LD V1, V3
ADD V4, VE
JP LOOPTEN
SKIPTEN: LD F, V4
DRW V6, V7, 5
ADD V6, 6
LD F, V1
DRW V6, V7, 5
RET
; ADDSCORE
; -> VE: Score count to add
; <- Nothing
; <> V0, V1, VE, VF, I
ADDSCORE: LD I, SCORE
LD V1, [I]
ADD V1, VE
SE VF, 0
ADD V0, 1
LD I, SCORE
LD [I], V1
RET
; NEWHIGH
; -> Nothing
; <- Nothing
; <> V0, V1, V2, V3, VE, VF, I
NEWHIGH: LD I, SCORE
LD V3, [I]
LD VE, V0
SUB VE, V2
SNE VF, 0
RET
SE VE, 0
JP STOREHIGH
LD VE, V1
SUB VE, V3
SNE VF, 0
RET
STOREHIGH: LD I, HIGHSCORE
LD [I], V1
RET
; WAITKEY
; -> V0: Waitcount
; <- VE: Keypressed
; <> V0, V1, V2, V3, VE, VF
WAITKEY: XOR VE, VE
LD V2, PRESSKEY
LD V3, -1 & MASKBYTE
LD V1, 16
LOOPKEY: SKNP V2
JP HITKEY
ADD V1, V3
SE V1, 0
JP LOOPKEY
LD V1, 16
ADD V0, V3
SE V0, 0
JP LOOPKEY
RET
HITKEY: LD VE, 1
RET
SCORE DW 0
HIGHSCORE: DW 0
SPRITES = ?
UP: DB $00000000, $01010000, $01110000, $00100000
LEFT: DB $00000000, $01100000, $00110000, $01100000
RIGHT: DB $00000000, $00110000, $01100000, $00110000
DOWN: DB $00000000, $00100000, $01110000, $01010000
GHOST: DB $00000000, $00100000, $01110000, $01110000
PILL: DB $00000000, $00000000, $00100000, $00000000
SUPER: DB $00000000, $00000000, $00000000, $00000000
GRAPHS = ?
; $0000 Trail up
; $0001 Trail left
; $0010 Trail right
; $0011 Trail down
; $0100 Empty space
; $0101 Ordinary pill
; $0110 Super pill
; $0111 Gateway
; $1000 Horisontal egde
; $1001 Invisible horisontal edge
; $1010 Vertical edge
; $1011 Invisible vertical edge
; $1100 Upper left corner
; $1101 Upper right corner
; $1110 Lower left corner
; $1111 Lower right corner
GRAPHEDGE = $1000
GRAPHSPEC = $0111
ES = $0100
PL = $0101
SP = $0110
GW = $0111
LR = $1000
ILR = $1001
UD = $1010
IUD = $1011
UL = $1100
UR = $1101
DL = $1110
DR = $1111
MU = $00010000
MR = $00100000
MUR = $00110000
MD = $01000000
MDU = $01010000
MDR = $01100000
MDUR = $01110000
ML = $10000000
MUL = $10010000
MRL = $10100000
MURL = $10110000
MDL = $11000000
MDUL = $11010000
MDRL = $11100000
MDURL = $11110000
DB $00000000, $00000000
DB $00000000, $00000000
DB $00000000, $00000000
DB $00000000, $00000000
EMPTY: DB $00000000, $00000000
PILLGR: DB $10000000, $00000000
SUPERGR: DB $00000000, $00000000
GATEGR: DB $00000000, $00000000
HORIS: DB $11000000, $00000000
INVHORIS: DB $00000000, $00000000
VERT: DB $10000000, $10000000
INVVERT: DB $00000000, $00000000
UPLEFT: DB $11000000, $10000000
UPRIGHT: DB $10000000, $10000000
DOWNLEFT: DB $11000000, $00000000
DOWNRIGHT: DB $10000000, $00000000
MAZE = ?
; ##################################################################
; #------------------------------- ------------------------------- #
; #| | | O | #
; #| ? . . . . ? . . ? . . . . ? | | ? . . . . ? . . ? . . .OOO? | #
; #| | | OOO | #
; #| . ------- . --- . ------- . --- . ------- . --- . ------- . | #
; #| | | | | | | | | | #
; #| . | ? x . ? | | ? . . ? | ? . . ? | ? . . ? | | ? . x ? | . | #
; #| | | | | | | | | | #
; #| . | . --------------- . ----------- . --------------- . | . | #
; #| | | | #
; #| ? . ? . . . . ? | ? . ? . ? . . ? . ? . ? | ? . . . . ? . ? | #
; #| | | | #
; #| . ----------- . | . ----- . --- . ----- . | . ----------- . | #
; #| | | | O | | | | #
; #| . | ? . . ? | ? ? ? | ? .O?O. . ? . ? | ? ? ? | ? . . ? | . | #
; # | | O O | | #
; #+ ? . ? --- . --- . --- . ----------- . --- . --- . --- ? . ? + #
; # | | #
; #| . | ? . . ? . . ? . . ? ----- ----- ? . . ? . . ? . . ? | . | #
; #| | | | | | #
; #| . ------- . --------- ? . ? | | ? . ? --------- . ------- . | #
; #| | | | | | | | | | #
; #| ? . . ? | . ------------- . --- . ------------- . | ? . . ? | #
; #| | | | #
; #| . --- x | ? . . . . ? . . ? . . ? . . ? . . . . ? | x --- . | #
; #| | | | | | | | #
; #| . --- . ----------- . --- . --- . --- . ----------- . --- . | #
; #| O | | | | | #
; #| ?OOO. ? . . . . . . ? | | ? . . ? | | ? . . . . . . ? . . ? | #
; #| OOO | | | | | #
; #------------------------- ----------- ------------------------- #
; # #
; ##################################################################
DB UL, LR, LR, LR, LR, LR, LR, LR
DB LR, LR, LR, LR, LR, LR, LR, UR
DB UL, LR, LR, LR, LR, LR, LR, LR
DB LR, LR, LR, LR, LR, LR, LR, UR
DB UD, MDR | PL, PL, PL, PL, PL, MDRL | PL, PL
DB PL, MDRL | PL, PL, PL, PL, PL, MDL | PL, UD
DB UD, MDR | PL, PL, PL, PL, PL, MDRL | PL, PL
DB PL, MDRL | PL, PL, PL, PL, PL, MDL | PL, UD
DB UD, PL, UL, LR, LR, DR, PL, UL
DB UR, PL, LR, LR, LR, UR, PL, DL
DB DR, PL, UL, LR, LR, DR, PL, UL
DB UR, PL, LR, LR, LR, UR, PL, UD
DB UD, PL, UD, MDR | PL, SP, PL, MUL | PL, UD
DB UD, MUR | PL, PL, PL, MDL | PL, UD, MUR | PL, PL
DB PL, MUL | PL, UD, MDR | PL, PL, PL, MUL | PL, UD
DB UD, MUR | PL, PL, SP, MDL | PL, UD, PL, UD
DB UD, PL, DR, PL, LR, LR, LR, LR
DB LR, UL, LR, DR, PL, LR, LR, LR
DB LR, LR, DR, PL, LR, LR, UL, LR
DB LR, LR, LR, DR, PL, DR, PL, UD
DB UD, MDUR | PL, PL, MURL | PL, PL, PL, PL, PL
DB MDL | PL, UD, MDR | PL, PL, MURL | PL, PL, MDRL | PL, PL
DB PL, MDRL | PL, PL, MURL | PL, PL, MDL | PL, UD, MDR | PL
DB PL, PL, PL, PL, MURL | PL, PL, MDUL | PL, UD
DB UD, PL, UL, LR, LR, LR, LR, UR
DB PL, DR, PL, UL, LR, DR, PL, LR
DB DR, PL, LR, LR, UR, PL, DR, PL
DB UL, LR, LR, LR, LR, UR, PL, UD
DB DR, PL, DR, MDR | PL, PL, PL, MDL | PL, UD
DB MUR | PL, MDRL | PL, MUL | PL, UD, MDR | PL, PL, MURL, PL
DB PL, MURL | PL, PL, MDL | PL,
DB UD, MUR | PL, MDRL | PL, MUL | PL
DB UD, MDR | PL, PL, PL, MDL | PL, DR, PL, DR
DB GW, MDUR | ES, PL, MDUL | PL, LR, DR, PL, DL
DB DR, PL, LR, DR, PL, UL, LR, LR
DB LR, LR, UR, PL, LR, DR, PL, LR
DB DR, PL, LR, DR, MDUR | PL, PL, MDUL | ES, GW
DB UD, PL, UD, MUR | PL, PL, PL, MDURL | PL, PL
DB PL, MURL | PL, PL, PL, MDUL | PL, LR, LR, UR
DB UL, LR, DR, MDUR | PL, PL, PL, MURL | PL, PL
DB PL, MDURL | PL, PL, PL, MUL | PL, UD, PL, UD
DB UD, PL, LR, LR, LR, UR, PL, UL
DB LR, LR, LR, UR, MUR | PL, PL, MDL | PL, UD
DB UD, MDR | PL, PL, MUL | PL, UL, LR, LR, LR
DB UR, PL, UL, LR, LR, DR, PL, UD
DB UD, MDUR | PL, PL, PL, MDL | PL, UD, PL, LR
DB LR, LR, LR, LR, LR, DR, PL, LR
DB DR, PL, LR, LR, LR, LR, LR, LR
DB DR, PL, UD, MDR | PL, PL, PL, MDUL | PL, UD
DB UD, PL, UL, UR, SP, UD, MUR | PL, PL
DB PL, PL, PL, MDRL | PL, PL, PL, MDURL | PL, PL
DB PL, MDURL | PL, PL, PL, MDRL | PL, PL, PL, PL
DB PL, MUL | PL, UD, SP, UL, UR, PL, UD
DB UD, PL, LR, DR, PL, LR, LR, LR
DB LR, LR, DR, PL, UL, UR, PL, LR
DB DR, PL, UL, UR, PL, LR, LR, LR
DB LR, LR, DR, PL, LR, DR, PL, UD
DB UD, MUR | PL, PL, PL, MURL | PL, PL, PL, PL
DB PL, PL, PL, MUL | PL, UD, UD, MUR | PL, PL
DB PL, MUL | PL, UD, UD, MUR | PL, PL, PL, PL
DB PL, PL, PL, MURL | PL, PL, PL, MUL | PL, UD
DB LR, LR, LR, LR, LR, LR, LR, LR
DB LR, LR, LR, LR, DR, LR, LR, LR
DB LR, LR, DR, LR, LR, LR, LR, LR
DB LR, LR, LR, LR, LR, LR, LR, DR
MAZEEND = ?
EYES = ?
EYELEFT: DB $00111100, $01000010
DB $10011001, $10011001
DB $01000010, $00111100
DB $00000001, $00010000
DB $00001111
EYERIGHT: DB $01111000, $10000100
DB $00110010, $00110010
DB $10000100, $01111000
DB $00000000, $00010000
DB $11100000
EYEBLINK: DB $01111000, $11111100
DB $11111110, $11111110
DB $10000100, $01111000
DB $00000000, $00010000
DB $11100000
BUFFER = ?catto@wagner.ecn.purdue.edu (Erin S Catto) (06/07/91)
Having used Chipper to create Joust 2 ..., I am truly impressed with its operation and speed! Chippers error messages make debugging the source code a breeze and its speed makes debugging the CHIP program a snap. Anyone interested in CHIP programming should not start without Chipper. Great job Christian Egeberg! Zoom P.S. In Joust: the eggs falling through the platforms is not a bug, actually I meant it to be that way so you have to chase it.
darrylo@hpnmdla.sr.hp.com (Darryl Okahata) (06/07/91)
In comp.sys.handhelds, egeberg@solan.unit.no (Christian Egeberg) writes: > Even though I thought I was through with Chip, I suspect > I will give in, and port it to Kernighan-Richie style C. > New mnemonics will be included, and propably conditional > assembly. The program will be tested on some Sun workstation, > SCO 386 Unix, and Microsoft C for MS-DOS. Why don't you run Chipper through Dave Gillespie's excellent p2c program (a Unix-based Pascal-to-C translator)? It won't be perfect, but it'll be a starting point. Just as a test, I ran Chipper though p2c, and it took all of nine seconds. There's a fair amount of work that needs to be done before it can be functional (mainly dealing with FExpand(), FSplit(), and assign()), but it's much easier than hand-translating the code. Here's an example of the translated code. Before: ------------------------------------------------------------------------------- PROCEDURE EncodeDwToken( Instruct: InstPointer; Reg, Symb: SymbolPointer); VAR Param: ParamPointer; Value: LongInt; This: Byte; BEGIN This:= 0; Value:= 0; WITH Instruct^ DO BEGIN Param:= Params; WHILE Param <> NIL DO BEGIN IF NOT ResolveExpression( Param^.Param, Value, Symb) THEN RunWarning( UndefinedWarning); IF RangeCheck( Value, 0, WordMask, RangeWarning) THEN Value:= Value AND WordMask; Memory[ Address + This]:= Value DIV 256; Memory[ Address + Succ( This)]:= Value MOD 256; Inc( This, 2); Param:= Param^.Next; END; ListInstruction( Address, 2 * Count, Instruct, Count); END; END; ------------------------------------------------------------------------------- and after: ------------------------------------------------------------------------------- Static Void EncodeDwToken(Instruct, Reg, Symb) InstRecord *Instruct; SymbolRecord *Reg, *Symb; { ParamRecord *Param; int Value; uchar This; This = 0; Value = 0; Param = Instruct->Params; while (Param != NULL) { if (!ResolveExpression(Param->Param, &Value, Symb)) RunWarning(UndefinedWarning); if (RangeCheck(Value, 0, WordMask, RangeWarning)) Value &= WordMask; Memory[Instruct->Address + This - StartAddress] = Value / 256; Memory[Instruct->Address + This - StartAddress + 1] = Value & 255; This += 2; Param = Param->Next; } ListInstruction(Instruct->Address, Instruct->Count * 2, Instruct, Instruct->Count); } ------------------------------------------------------------------------------- It's very nice. -- Darryl Okahata Internet: darrylo%sr@relay.hp.com P.S. -- For those people who would like a version of p2c that runs under MSDOS: don't ask me about it. While it might be possible if p2c was compiled using a DOS extender, it would be a lot of work, take up enourmous amounts of memory (a 386 would be a MUST), and it would not work under Windows 3.0 (it would have to use extended -- not expanded -- memory). DISCLAIMER: this message is the author's personal opinion and does not constitute the support, opinion or policy of Hewlett-Packard or of the little green men that have been following him all day.