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.