[comp.sys.handhelds] Chipper source & stuff

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.