EMERSON ARCADIA 2001 CODING GUIDE --------------------------------- This document was written on 12/3/03, and last updated on 19/12/22, by James Jacobs of Amigan Software. It is divided into five sections: a) disassembly: the process of generating assemblable source. b) comprehension: annotating the source and understanding the program. c) patching: modifying the program to work around emulator incompatibilities, and/or repairing bad dumps. d) programming notes. e) compatibility notes. Information herein is believed to be generally accurate, though some is tentative. If you have anything to contribute, please email amigansoftware@gmail.com . DISASSEMBLY--------------------------------------------------------------- ANNOTATE, DASMX and VACS are now considered obsolete, as AMI/WINARCADIA can replicate their functionality. COMPREHENSION------------------------------------------------------------- There are a variety of approaches to disassembly which can be used; some are more efficient than others. ROMs are comprised of code ("ROM code") and data ("ROM constants" or "ROM data"). The most usual arrangement is to intersperse routines with data relevant to those routines (data following its relevant code), but there are other possibilities; eg. all data at the end of the program (although this is less efficient as absolute operations would need to be used instead of relative operations). The first thing you must do is to ensure you have correctly delimited all code (subroutines and other code sections) and data sections; ie. one is not mistakenly marked as the other. Indirect jumps (* operator), ZBSR, ZBRR, and >4K ROMs can all cause the disassembler to thread incorrectly (ie. code areas will be shown as data). However, the reverse is not true, so anything shown as code is generally indeed code, not data. You should resolve all indirect jumps and indirect relative accesses (via comments only, of course) (search for "*$"). You will also be able to determine which variables are used as pointers. Elektor TV Games Computer: direct STRR instructions often indicate self- modifying code. The debugger is highly useful for eg. examining the flow of control, or the changing values of variables/registers. If, for example, you look at the area $1800..$1807 (sprite #0 data), you will be able to determine what image is assigned to sprite #0. To ascertain the purpose of a variable, you can set a data watchpoint on it and observe how and why it changes as the game is played. Viewing the ROM with HOWDIF can reveal text strings and level data. These are not visible with a standard hex editor as they are generally encoded in the Arcadia format, which is not ASCII. Only HOWDIF understands this format. Generally, programs that use the ZBRR and/or ZBSR instructions will begin with BCTA,un $40 or similar; the rest of $3..$3F will be a vector jump table. Most games do not like to read hardware registers unnecessarily. Normally they will copy the results of such a read to a variable in RAM and refer to that. So you can find and mark all such variables fairly easily by looking near accesses to the relevant hardware. Strings such as "GAME OVER" (often "GAME OVER" [sic] which can be centred exactly) are especially useful to find. Once you know the ROM addresses of these strings, you can not only reformat the listing, but also can search for references to that area of the ROM by the program. Eg. if you know that the string "GAME OVER" is stored at $800..$808, then if you can find references to that area (eg. a routine that copies $800..$808 to some part of the screen), you have likely found the end-of-game routine. Here is the general memory map of the Arcadia. Note the intermingling of the different memory areas. It is imporant to clearly distinguish between hardware registers (ie. memory-mapped I/O areas accessed by the 2637 UVI) and user RAM. Also note the dual use of the $1A00..$1ACF depending on resolution; this area is visible on the lower half of the screen in high- resolution mode, but not visible in low-resolution mode. This memory map is not explictly appended by ANNOTATE but is used internally by it to annotate addresses individually. Note that both the general memory map (above) and the specific memory map (below) are the latest, most correct versions. The versions used by the existing hand-disassemblies and source code are obsolete and slightly inaccurate, since they were made using the version current at the time. ;General Memory Map (Emerson Arcadia 2001 family)------------------------- $0000..$0FFF ROM 4096 bytes $1000..$10FF G: mirror of $1800..$18FF 256 bytes H: 256 bytes of CPU RAM 256 bytes ("1st" RAM bank) I: ROM 256 bytes $1100..$11FF G,H: mirror of $1900..$19FF 256 bytes I: ROM 256 bytes $1200..$12FF G: mirror of $1A00..$1AFF 256 bytes H: 256 bytes of CPU RAM 256 bytes ("2nd" RAM bank) I: ROM 256 bytes $1300..$13FF G,H: mirror of $1B00..$1BFF 256 bytes I: ROM 256 bytes $1400..$17FF G,H: mirror of $1000..$13FF 1024 bytes I: ROM 1024 bytes $1800..$18FF 256 bytes of CPU+UVI RAM 256 bytes ("3rd" RAM bank) $1800..$18CF upper screen 208 bytes $18D0..$18EF user RAM 32 bytes $18F0..$18F7 hardware registers 8 bytes $18F8..$18FB user RAM 4 bytes $18FC..$18FF hardware registers 4 bytes $1900..$19FF keyboard and UVI internal 256 bytes $1900..$1908 hardware registers 9 bytes $1909..$190F unmapped 7 bytes $1910..$191F mirror of $1900..$190F? 16 bytes $1920..$192F mirror of $1900..$190F? 16 bytes $1930..$193F mirror of $1900..$190F? 16 bytes $1940..$194F mirror of $1900..$190F? 16 bytes $1950..$195F mirror of $1900..$190F? 16 bytes $1960..$196F mirror of $1900..$190F? 16 bytes $1970..$197F mirror of $1900..$190F? 16 bytes $1980..$19BF hardware registers 64 bytes (sprite and UDC imagery) $19C0..$19F7 unmapped 56 bytes $19F8..$19FF hardware registers 8 bytes $1A00..$1AFF 256 bytes of CPU+UVI RAM 256 bytes ("4th" RAM bank) $1B00..$1BFF mirror of $1900..$19FF 256 bytes $1C00..$1FFF G,H: mirror of $1800..$1BFF 1024 bytes I: ROM $2000..$2FFF ROM 4096 bytes $3000..$3FFF G,H: mirror of $1000..$1FFF 4096 bytes I: ROM 4096 bytes $4000..$4FFF G,H: mirror of $0000..$0FFF? 4096 bytes I: ROM 4096 bytes $5000..$5FFF G,H: mirror of $1000..$1FFF 4096 bytes I: ROM 4096 bytes $6000..$6FFF G,H: mirror of $0000..$0FFF? 4096 bytes I: ROM 4096 bytes $7000..$7FFF G,H: mirror of $1000..$1FFF 4096 bytes I: ROM 4096 bytes "G": Emerson Arcadia 2001. 3rd and 4th RAM banks are present. "H": Tele-Fever. 1st..4th RAM banks are present. "I": Palladium. 3rd and 4th RAM banks are present. Here is the detailed memory map of the Arcadia. This is added automatically to the disassembly by ANNOTATE; otherwise you can add it manually. ;Hardware Equates/Memory Map (Emerson Arcadia 2001 family)---------------- ; $0000..$0FFF: (R/-) ROM ; $1000..$10FF: (*/*) G: mirror of $1800..$18FF ; (R/W) H: 256 bytes of CPU RAM ; (R/-) I: ROM ; $1100..$11FF: (*/*) G,H: mirror of $1900..$19FF ; (R/-) I: ROM ; $1200..$12FF: (*/*) G: mirror of $1A00..$1AFF ; (R/W) H: 256 bytes of CPU RAM ; (R/-) I: ROM ; $1300..$13FF: (*/*) G,H: mirror of $1900..$19FF ; (R/-) I: ROM ; $1400..$17FF: (*/*) G,H: mirror of $1000..$13FF ; (R/-) I: ROM ; $1800..$18CF: (R/W) upper screen ; $18D0..$18EF: (R/W) 32 bytes of CPU+UVI RAM SPRITE0Y equ $18F0 ;(R/W) SPRITE0X equ $18F1 ;(R/W) SPRITE1Y equ $18F2 ;(R/W) SPRITE1X equ $18F3 ;(R/W) SPRITE2Y equ $18F4 ;(R/W) SPRITE2X equ $18F5 ;(R/W) SPRITE3Y equ $18F6 ;(R/W) SPRITE3X equ $18F7 ;(R/W) ; $18F8..$18FB: (R/W) 4 bytes of CPU+UVI RAM VSCROLL equ $18FC ;(R/W) CRTC vertical position register PITCH equ $18FD ;(R/W) also other uses ; bit 7: 0 = normal mode ; 1 = "board" mode ; bits 6..0: pitch VOLUME equ $18FE ;(R/W) also other uses ; bits 7..5: horizontal scrolling (0..7) ; bit 4: noise on/off ; bit 3: tones on/off ; bits 2..0: volume (0..7) CHARLINE equ $18FF ;(R/-) current character line P1LEFTKEYS equ $1900 ;(R/-) ; bits 7..4: unused ; bit 3: p1 (left) '1' button ; bit 2: p1 (left) '4' button ; bit 1: p1 (left) '7' button ; bit 0: p1 (left) 'C' button (Clear) P1MIDDLEKEYS equ $1901 ;(R/-) ; bits 7..4: unused ; bit 3: p1 (left) '2' button ; bit 2: p1 (left) '5' button ; bit 1: p1 (left) '8' button ; bit 0: p1 (left) '0' button P1RIGHTKEYS equ $1902 ;(R/-) ; bits 7..4: unused ; bit 3: p1 (left) '3' button ; bit 2: p1 (left) '6' button ; bit 1: p1 (left) '9' button ; bit 0: p1 (left) 'E' button (Enter) P1PALLADIUM equ $1903 ;(R/-) ; bits 7..4: unused ; bit 3: p1 (left) Palladium button 'x4' ; bit 2: p1 (left) Palladium button 'x3' ; bit 1: p1 (left) Palladium button 'x2' ; bit 0: p1 (left) Palladium button 'x1' P2LEFTKEYS equ $1904 ;(R/-) ; bits 7..4: unused ; bit 3: p2 (right) '1' button ; bit 2: p2 (right) '4' button ; bit 1: p2 (right) '7' button ; bit 0: p2 (right) 'C' button (Clear) P2MIDDLEKEYS equ $1905 ;(R/-) ; bits 7..4: unused ; bit 3: p2 (right) '2' button ; bit 2: p2 (right) '5' button ; bit 1: p2 (right) '8' button ; bit 0: p2 (right) '0' button P2RIGHTKEYS equ $1906 ;(R/-) ; bits 7..4: unused ; bit 3: p2 (right) '3' button ; bit 2: p2 (right) '6' button ; bit 1: p2 (right) '9' button ; bit 0: p2 (right) 'E' button (Enter) P2PALLADIUM equ $1907 ;(R/-) ; bits 7..4: unused ; bit 3: p2 (right) Palladium button 'x4' ; bit 2: p2 (right) Palladium button 'x3' ; bit 1: p2 (right) Palladium button 'x2' ; bit 0: p2 (right) Palladium button 'x1' CONSOLE equ $1908 ;(R/-) ; bits 7..3: unused ; bit 2: B button ; bit 1: A button ; bit 0: START button ; $1909..$190F: (-/-) unmapped ; $1910..$191F: (*/*) mirror of $1900..$190F ; $1920..$192F: (*/*) mirror of $1900..$190F ; $1930..$193F: (*/*) mirror of $1900..$190F ; $1940..$194F: (*/*) mirror of $1900..$190F ; $1950..$195F: (*/*) mirror of $1900..$190F ; $1960..$196F: (*/*) mirror of $1900..$190F ; $1970..$197F: (*/*) mirror of $1900..$190F ; $1980..$1987: (R/W) sprite #0 imagery ; $1988..$198F: (R/W) sprite #1 imagery ; $1990..$1997: (R/W) sprite #2 imagery ; $1998..$199F: (R/W) sprite #3 imagery UDC0 equ $19A0 ; $19A0..$19A7: (R/W) user-defined character #0 imagery UDC1 equ $19A8 ; $19A8..$19AF: (R/W) user-defined character #1 imagery UDC2 equ $19B0 ; $19B0..$19B7: (R/W) user-defined character #2 imagery UDC3 equ $19B8 ; $19B8..$19BF: (R/W) user-defined character #3 imagery ; $19C0..$19F7: (-/-) unmapped GFXMODE equ $19F8 ;(-/W) graphics mode, etc. ; bit 7: 0 = normal mode ; 1 = block graphics mode ; bit 6: 0 = 13 character rows ; 1 = 26 character rows ; bits 5..3: in block mode, 1st frgrnd colour ; (inverted GRB) ; bits 2..0: in block mode, outer bkgrnd colour ; (inverted GRB) BGCOLOUR equ $19F9 ;(-/W) also other uses ; bit 7: 0 = low-res mode ; 1 = high-res mode ; bit 6: paddle interpolation ; bits 5..3: colours of tile set 0 ; bits 2..0: (inner) bkgrnd colour (inverted GRB) SPRITES23CTRL equ $19FA ;(-/W) ; bit 7: 0 = sprite #2 double-height ; 1 = sprite #2 normal ; bit 6: 0 = sprite #3 double-height ; 1 = sprite #3 normal ; bits 5..3: colours of sprite #2 ; bits 2..0: colours of sprite #3 SPRITES01CTRL equ $19FB ;(-/W) ; bit 7: 0 = sprite #0 double-height ; 1 = sprite #0 normal ; bit 6: 0 = sprite #1 double-height ; 1 = sprite #1 normal ; bits 5..3: colours of sprite #0 ; bits 2..0: colours of sprite #1 BGCOLLIDE equ $19FC ;(R/-) ; bits 7..4: unused ; bits 3: sprite #3 collision with bkgrnd ; bits 2: sprite #2 collision with bkgrnd ; bits 1: sprite #1 collision with bkgrnd ; bits 0: sprite #0 collision with bkgrnd SPRITECOLLIDE equ $19FD ;(R/-) ; bits 7..6: unused ; bit 5: sprites #2/#3 collision ; bit 4: sprites #1/#3 collision ; bit 3: sprites #1/#2 collision ; bit 2: sprites #0/#3 collision ; bit 1: sprites #0/#2 collision ; bit 0: sprites #0/#1 collision P2PADDLE equ $19FE ;(R/-) P1PADDLE equ $19FF ;(R/-) ; $1A00..$1ACF: (R/W) lower screen ; $1AD0..$1AFF: (R/W) 48 bytes of CPU+UVI RAM ; $1B00..$1BFF: (*/*) mirror of $1900..$19FF ; $1C00..$1FFF: (*/*) G,H: mirror of $1800..$1BFF ; (R/-) I: ROM ; $2000..$2FFF: (R/-) ROM ; $3000..$3FFF: (*/*) G,H: mirror of $1000..$1FFF ; (R/-) I: ROM ; $4000..$4FFF: (*/*) G,H: mirror of $0000..$0FFF? ; (R/-) I: ROM ; $5000..$5FFF: (*/*) G,H: mirror of $1000..$1FFF ; (R/-) I: ROM ; $6000..$6FFF: (*/*) G,H: mirror of $0000..$0FFF? ; (R/-) I: ROM ; $7000..$7FFF: (*/*) G,H: mirror of $1000..$1FFF ; (R/-) I: ROM R/W: read/write R/-: read-only (write attempts are ignored) -/-: unmapped (mostly return $FF when read, write attempts are ignored) -/W: write-only (always returns $FF when read) */*: mirror (resolve address to ascertain R/W attributes) Also, some addresses are read-once (as noted in the text). Look for accesses to hardware registers (eg. $1908, $18FC) and replace them with the appropriate named constant (eg. CONSOLE, VSCROLL). This is done automatically by ANNOTATE. Any input (console buttons, paddle buttons, paddle directions) or output (graphics, sound) must obviously go through the hardware registers. The purpose of any given subroutine is often apparent by the hardware registers it deals with. For each area of ROM data, you should be able to answer these questions: . what type of data is it? Common types include: . sprite bitmap definitions . UDC (user-defined character) bitmap definitions . music data (pitch, volume and/or duration) . screen layouts (character data) (eg. text strings) . pointers to code or data (eg. jump tables) . offsets to code or data . initialization values of RAM or hardware registers . BCD numbers . general data tables . what code reads this data? . where it is written (copied) to (RAM, hardware registers, CPU registers) by that code? Pause the game and draw any user-defined characters and/or sprites onto graph paper. Convert these images into their binary equivalents and then search through the listing for these values. Eg.: ..##.... 00110000 $30 .####... 01111000 $78 .####... 01111000 $78 ..##.#.. 00110100 $34 .#..#.## 01001011 $4B #....#.# 10000101 $85 ......#. 00000010 $02 ......#. 00000010 $02 In this case you would might search for "04BH", looking for an occurrence as part of a DB directive which is preceded by the value "034H" and succeeded by the value "85H". Eg. db 030H, 078H, 078H, 034H db 04BH, 085H, 002H, 002H This can then be reformatted as: db $30 ;..##.... db $78 ;.####... db $78 ;.####... db $34 ;..##.#.. db $4B ;.#..#.## db $85 ;#....#.# db $02 ;......#. db $02 ;......#. for clarity. You can use the "View ROM as imagery" command of AMIARCADIA/WINARCADIA to make this easier. Learn to recognize and document the start and ends of loops. Loops are used extensively in Arcadia software, and are a gimme. Most loops tend to be decrementing; the loop index is set at its maximum value prior to the start of the loop, and is decremented by one and compared against zero at the end of each iteration of the loop (generally with the BDRR opcode). This is the most common type of loop to on the 2650, but other types are possible (eg. incrementing loops). Loops are used for a variety of purposes, including printing strings to the screen and assigning/changing sprite/UDC images. It is important to uncover the structure of the program; ie. start and end points of subroutines and other code sections. You can do this by looking at occurrences of BSTA, RETC, etc. Although it is possible to have subroutines with multiple entry/exit points etc., such occurrences are generally rare. Generally most code is either straight-line code sections or subroutines. Selectively and temporarily disabling subroutines can help to reveal the purpose of the relevant subroutine. Similarly, interfering with the values of variables can reveal the purpose of the relevant variable. It is important to know which sprites are used for which game object, eg. player's ship might be sprite #0, player's bullet might be sprite #1, sprites #2 and #3 might be aliens. Then you can look for reads and writes of the sprite coordinate registers: this will reveal player input/movement routines, enemy movement routines, collision routines, etc. Use knowledge you already have to build further on that knowledge. For example, if you have found player X and Y variables, you can look at the code which changes those variables, and you will reveal delta-X and delta-Y variables. Using a variety of techniques and strategies will uncover the most complete information possible. Make use of all the tools (emulator, debugger, etc.) and resources possible. It is important to know the purposes of variables. The main way is to look at where the variables are read and written, and for what purpose and under what conditions. Most games tend to always have certain variables, although their location in RAM often differs of course. Eg. a monotonic incrementor, or frame counter, which is incremented once per frame, is needed to correctly handle paddle input and sprite multiplexing. Usually there is also a lives counter, current score, high score, game mode (eg. which "option" or "game" is selected), demo/game flag, number of players, level number, direction faced, etc. Also many hardware registers are often copied or 'shadowed' into user RAM, eg. paddle registers, sprite coordinates, etc. By the same token, most games tend to always have certain routines; eg. boot, title screen, new game, update score, update high score, collision detection, player input, enemy movement, player movement, bullet movement, new level, life lost, game over, decrement time, create enemies, etc. Being able to quickly ascertain these enables you to concentrate on other areas. In disassembly, it is usually best to disassemble the shortest and most obvious parts of the program first, so that your knowledge of the program will be more complete by the time you attempt the longer, more complex parts. As you disassemble, rename variables (with EQU directives) and labels (by straightforward text replacement) which you have ascertained the purpose of. It is also useful to comment accesses to, for example, the upper screen display ($1800..$18CF), as such. Endline comments showing instructions in pseudocode can also be added for additional clarity. For each subroutine or code section, you should be able to answer these questions: . what is the purpose of this subroutine? . what are the input arguments, if any? . what are the return codes, if any?. . what are the scratch (garbage) registers/RAM, if any? . what registers/RAM are preserved, if any? . which CPU registers, hardware registers, RAM and ROM are read and/or written? . what are the entry and exit points? . which code calls this subroutine? . which code is called by this subroutine? . what is the subroutine nesting depth of the routine (ie. how many levels of the Stack Pointer does it use)? Knowing which code calls which other code can help you establish a subroutine hierarchy and it also means you could build a flowchart. Become thoroughly familiar with the program from a user's perspective. Then you won't have to make guesses about what the program is trying to accomplish. Whilst annotating, you should assemble your annotated source code frequently and run HOWDIF to compare the freshly assembled binary against the original binary; they should match perfectly. If they do not, immediately fix the problem(s) before you continue your annotation. If hundreds or thousands of bytes are shown not to match, this usually indicates that bytes have been inserted or removed. Pointer variables are often the most difficult parts of a program to comprehend, depending on how they are used. Look for store and load opcodes which use indirection (* operator). You will hardly ever have to completely understand all the intricacies of a given program before being able to implement the patches (if any) which are required. However, the more knowledge about the program the better; understanding any given part of the code is never to be regretted. The amount of annotation required before patching can be undertaken varies considerably depending on many factors. In many cases a quick patch can be implemented without comprehending the rest of the program. In other cases considerable annotation will be required before you comprehend the program enough to implement the patch. Feel free to hypothesize and make educated guesses. However, any tentative information should be clearly marked as such (eg. appended with '?'). Assumptions should be verified before relying on such information. It is better that a section be uncommented than that it contain incorrect or misleading comments. Comments should be written at the level of intent; comments which paraphase assembly code into pseudo-C can also be useful for many programmers, but can be generated automatically by ANNOTATE. What ANNOTATE does not do, however, is to look at multiple statements together. You can write more complex pseudo-C comments that describe a whole group of assembly statements; this helps significantly with understanding the program. A few examples: ;if (FLAGS & %10000000 == 0) goto NEWGAME; else goto CONSOLELOOP; ;11 loda,r0 FLAGS ;r0 = FLAGS; ;3 andi,r0 $80 ;r0 &= %10000000; ;2 bcta,eq NEWGAME ;if (==) goto NEWGAME; ;3 bcta,un CONSOLELOOP ;goto CONSOLELOOP; ;3 ;if ((ENEMIES & %11110000) != 0 || (*($1AEE) & %11110000) != %00010000) ;goto LEVEL2LOOP; ;18 loda,r0 ENEMIES ;r0 = ENEMIES; ;3 andi,r0 $f0 ;r0 &= %11110000; ;2 bcfa,eq LEVEL2LOOP ;if (!=) goto LEVEL2LOOP; ;3 loda,r0 $1aee ;r0 = *($1AEE); ;3 andi,r0 $f0 ;r0 &= %11110000; ;2 comi,r0 $10 ;compare r0 against $10 ;2 bcfa,eq LEVEL2LOOP ;if (!=) goto LEVEL2LOOP; ;3 ;if (r3 == 2) then r3--; else if (r3 == 6) then r3++; ;16 comi,r3 $02 ;compare r3 against 2 ;2 bctr,eq L0744 ;if (==) goto L0744; ;2 comi,r3 $06 ;compare r3 against 6 ;2 bctr,eq L0748 ;if (==) goto L0748; ;2 bctr,un L074A ;goto L074A; ;2 L0744: subi,r3 $01 ;r3--; ;2 bctr,un L074A ;goto L074A; ;2 L0748: addi,r3 $01 ;r3++; ;2 ;if (*($18DC) != 0 || *($18DF) != 0) goto AROUTINE_4; ;10 loda,r0 $18DC ;r0 = *($18DC); // user RAM ;3 bcfr,eq AROUTINE_4 ;if != goto AROUTINE_4; ;2 loda,r0 $18DF ;r0 = *($18DF); // user RAM ;3 bcfr,eq AROUTINE_4 ;if != goto AROUTINE_4; ;2 ;while (CHARLINE != *($16B + r2)); ;8 PLAYGAME: loda,r0 CHARLINE ;r0 = CHARLINE; // hardware registers ;3 coma,r0 $016B,r2 ;compare r0 against *($016B + r2); ;3 bcfr,eq PLAYGAME_2 ;if != goto PLAYGAME_2; ;2 There are various tables of information which it is helpful to create as part of the annotation: For each variable, you should be able to answer these questions: . what is the purpose of this variable? . is this a 2-byte variable (a pointer) or a 1-byte variable (anything else)? . what format is this variable stored in? . what is the valid range of this variable's possible values? . is this a copy of any hardware register or ROM data? . when and where is this variable initialized? . which code reads and/or writes this variable? . for what span of time is this variable valid? . is this memory ever reused for another unrelated variable at another point in the program? It can be helpful to make a table showing the screen layout used by the game. For example, this one is from Capture: --0- --1- --2- --3- --4- --5- --6- --7- --8- --9- -10- -11- -12- -13- -14- -15- 0: 1800 1801 1802 1803 1804 1805 S C O R E 180B 180C 180D 180E 180F 1: 1810 1811 p1 1813 p1 1815 1816 1817 1818 1819 181A p2 181C p2 181E 181F 2: 1820 1821 : 1823 1824 1825 1826 1827 1828 1829 182A 182B 182C : 182E 182F 3: 1830 1831 : 1833 1834 1835 T I M E 183A 183B 183C : 183E 183F 4: 1840 1841 1842 1843 # # # # # # # # 184C 184D 184E 184F 5: 1850 1851 1852 1853 # # # # # # # # 185C 185D 185E 185F 6: 0 0 1862 1863 # # # # # # # # 186C 186D 0 0 7: 0 0 1872 1873 # # # p2 p1 # # # 187C 187D 0 0 8: 0 0 1882 1883 # # # p1 p2 # # # 188C 188D 0 0 9: 0 0 1892 1893 # # # # # # # # 189C 189D 0 0 10: 18A0 18A1 18A2 18A3 # # # # # # # # 18AC 18AD 18AE 18AF 11: 18B0 18B1 18B2 18B3 # # # # # # # # 18BC 18BD 18BE 18BF 12: 18C0 # 18C2 F O R F E I T M O V E 18CF PATCHING------------------------------------------------------------------ Patching is required when the system behaviour expected by the software is different to the actual behaviour of the emulator. Note that the patches formerly available for improving compatibility with the MESS V1 and V2 emulators have been withdrawn, due to the availability of accurate emulators (namely AmiArcadia and WinArcadia). It should no longer be really necessary to patch anything for compatibility purposes. Timing problems manifest themselves in several ways, the most common being: . games running too fast or too slow . sprite movement problems Often it is necessary to adjust code dealing with vertical blanking, to correct timing problems. Patching generally modifies the code in areas where there is a difference in behaviour between the emulator and the genuine console. It is imperative to know the behaviour on the authentic console. Otherwise you do not know whether any given aspect of the program is correct. Being able to play the game yourself on the authentic console is ideal, but if this is not possible, you can hopefully ask questions of someone who can via email or suchlike; a video clip of the game being played is also invaluable. Having the manual of the game is also helpful. Screenshots are an important resource but it is of course important to ensure they are photographs of games on a genuine console run through a television set, rather than screengrabs from an emulator. Any other information about the game can also be useful. A fundamental principle of patching is not to modify any more of the program than necessary. Often a replacement section is larger than the corresponding original section. In this case the best option is a subroutine call to your replacement section. Most games have at least a few bytes of junk data at the end of the ROM. If you can be sure of the size of this area, it is possible to locate your replacement sections there. In some cases there is insufficient junk data to provide enough space for your replacement sections: in this case you must extend the ROM. Typically you would extend it by 2K for consistency. Resist the temptation to make changes at random without understanding the issues involved. Although this approach may work occasionally, if you have not fixed the problem within five minutes you are better to spend your time understanding the program more thoroughly. By the same token, it is better generally to fix the cause of a bug than merely the symptoms: however, this is not a hard and fast rule. It is of course vital to test the program as you make each change to ensure correct behaviour. The main problem in patching, as for debugging generally, is isolating the area of code which is causing the problem. Once you understand what that code is doing, and what it should instead be doing, it is usually trivial to implement a patch. Patching bad dumps is similar in ways to patching for compatibility, but more difficult. Generally each bad byte is surrounded by good bytes. If more than a handful of bad bytes exist in the file it may not be feasible to fix them. The two problems are (a) to identify whether a given byte is good or bad; and (b) to determine what the correct value of the byte should be. Look for nonsensical instructions and so forth. If you have a thorough understanding of the relevant section of the program it is usually possible to determine what the value of the byte should be. Again, you must know exactly what the intended behaviour of the program is. Unfortunately, at times, depending on where the bad byte is, you may not be able to confidently know what the correct value should be, even if you understand the rest of the program. Thus one might never know for certain whether a bad dump which has been manually fixed in the way described is 100% equal to a perfect dump of that program. By disassembling the bad dump, you can ascertain how the exhibited behaviour is achieved. Once you know this you can then often tell what the intended behaviour should be. The following valid 2650 instructions are intended for I/O to external devices and should never occur in any conceivable EA2001 program. Thus they can be taken as evidence of a bad dump, or that you are in fact looking at data (or junk) rather than code. HALT RETE REDD REDC REDE WRTC WRTD WRTE PROGRAMMING NOTES--------------------------------------------------------- Overview -------- The architecture of the system is quite straightforward. A Signetics 2650A CPU runs the programs and a Signetics 2637N UVI handles input and output. There are a pair of 2114 RAM chips. (There are also a few "glue" chips but these are irrelevant to programming (or emulating) the system.) The ROM (game cartridge) is mapped to memory as already described. Execution starts from address $0000. (The CPU branches to address $0003 if an interrupt is generated at any time.) Addresses $1800..$1AFF are mapped to the UVI, I/O hardware and/or RAM. The game is not copied to RAM; it is instead run directly from ROM. The chips may be in any state after a reset, and thus their contents should be cleared to known values at the start of the program to ensure proper operation. The display is drawn by the UVI 60 times per second (for NTSC). The display is character-based (each character measuring 8*8 pixels), with programmable vertical and horizontal offsets. A choice of two resolutions (128*104 pixels or 16*13 characters, and 128*208 pixels or 16*26 characters) is available. The display is character-mapped, and thus does not need to be generated on the fly. There are 8 colours in the palette: black, white, red, green, blue, yellow, cyan and purple. There are 56 characters stored in ROM, and 4 which are stored in RAM (and thus user-definable). Lowercase letters and most punctuation marks are not available. There is also a set of 64 "block graphics" characters which can be effectively "swapped in". Each character is displayed in a foreground and background colour. In mode 0 ("normal mode"), there are 4 foreground colours and 1 background colour available. In mode 1 ("board mode"), there are 2 foreground colours and 2 background colours available. There are also four user-definable single-colour 8*8 sprites which can be freely positioned independently of the character display. The four sprite image definitions can also be used as user-defined characters, just as with the four "true" user-defined characters. There are three buttons on the master console which can be detected by software. There are also twelve or more distinct buttons on each of the two controllers. Each controller also has a paddle, which can be digital or analogue. There are two sound channels: a square wave channel (with 128 different frequencies possible), and a white noise channel (with 128 different modulations possible). There are 8 volume levels available. Games are synchronized with the raster beam, as is usual for most systems. Many of the UVI registers can only be read or written at certain points in the frame. The vertical retrace status is available in the Sense pin of the CPU. (The Flag pin of the CPU can be used to invert the colours of all or part of the display.) All other UVI data is mapped to memory locations; specialized I/O commands are not used. The current character row being drawn by the UVI is available as a UVI register (mapped to a memory location). The current raster line being drawn by the UVI is not available but can of course be deduced by cycle counting. Horizontal retrace status is not available (but is deducable). An NTSC Emerson Arcadia 2001 has these integrated circuits: U1: 74LS00N aka 7400 - NAND gate U2: 74LS04N aka 7404 - NOT gate U3,U5: 74LS86N aka 7486 - exclusive OR gate U4: 14066B aka 4066 - quad analog switch/quad multiplexer? U6: 14069UB aka 4069 - hex inverter? U7: 2622N - Signetics 2622 NTSC USG U8: 74LS258N aka 74258 - quad 2-input multiplexer? (keyboard inputs) U9: 2637 - Signetics 2637 PVI U10: 2650A - Signetics 2650A CPU U11,U12: 2114 - 1 kilonybble of 250 nS SRAM U13: 7805 - +5V regulator U14: 74LS145N aka 74145 - plastic binary-to-decimal decoder? (keyboard inputs) $1800..$1AFF is 768 bytes. The other 256 bytes are completely wasted (unmapped)? Arcadia BIN Format ------------------ Arcadia-family BINs are stored and loaded as follows: Size On disk In memory ORG ---- --------------------------- ------------------------------------ ----- 4K $0000..$0FFF (1st 4K chunk) -> $0000..$0FFF (1st half of page 0) 0 8K $1000..$1FFF (2nd 4K chunk) -> $2000..$2FFF (1st half of page 1) 0 12K $2000..$2FFF (3rd 4K chunk) -> $4000..$4FFF (1st half of page 2) 0 16K $3000..$3FFF (4th 4K chunk) -> $6000..$6FFF (1st half of page 3) 0 20K $4000..$4FFF (5th 4K chunk) -> $3000..$3FFF (2nd half of page 1) $1000 24K $5000..$5FFF (6th 4K chunk) -> $5000..$5FFF (2nd half of page 2) $1000 28K $6000..$6FFF (7th 4K chunk) -> $7000..$7FFF (2nd half of page 3) $1000 30K $7000..$77FF ( 2K chunk) -> $1000..$17FF (3rd quarter of page 0) $1000 31K $7800..$7BFF ( 1K chunk) -> $1C00..$1FFF (8th eighth of page 0) $1C00 Note that, since no cartridges larger than 12K are known, this is somewhat arbitrary beyond 12K and definitely arbitrary beyond 16K. Also, note that although type "I" (Palladium) supports up to 31K ROMs (presumably), types "G" (Emerson) and probably "H" (Tele-Fever) support only up to 8K ROMs. Also, note that cartridge RAM is not possible on any of these machines. Common Misconceptions --------------------- There are many inaccuracies which have become widely believed. These will now be corrected: * There are 8 colours in the palette, not 9. * There is a clock of 3,579,545Hz at the 2621 (pin 12 clock). At pin 11 (PCK) there is a 3,579,545Hz clock which is connected to the 2637 UVI. At pin 10 (CK4) the 2650 is connected with a frequency of 894,886.25Hz, because the output divides the input frequency by 4. The frequency measured at the input pin of the 2650 is still the same. Therefore, the effective speed is approx. 0.89MHz (approx. 3.58MHz � 4), the same as the Interton VC 4000 has. * RAM is 1K (type "G" has access to only half of this), not 28K. "The Arcadia's makers didn't hook up the highest address line on their pair of 2114 RAM chips; so what should be 1K of RAM is only 0.5K of RAM." - Ward Shrake. * The are two sound channels (one tone and one noise), not one. * The "White MPT-03": there is no such system. This misconception arises from stickers affixed to some MPT-03 games, saying: "For use White Intelligent Game MPT-03". The word "White" is erroneous; the intended meaning is "For use with Intelligent Game MPT-03". CPU --- If your game is larger than 4K, you will need to be aware of the following rules. Note that this section assumes use of VACS 1.24h. The CPU divides the 32K address space into 4 pages, each of 8K, as follows: PAGE0 equ $0000 ;$0000..$1FFF: 1st page PAGE1 equ $2000 ;$2000..$3FFF: 2nd page PAGE2 equ $4000 ;$4000..$5FFF: 3rd page PAGE3 equ $6000 ;$6000..$7FFF: 4th page Also, the way that BINs are loaded into memory divides the usable 31K address space into 9 chunks (see "Arcadia BIN Format"). Execution of code cannot flow across page boundaries (wraparound will occur instead). Direct loads and stores can be done only on the current page. But by using indirection you can load/store to any address. Eg. $2000: lodi,r0 $51 $2002: stra,r0 $1800 $2005: retc,un will not work ($3800 will be written to instead of $1800). But: $2000: lodi,r0 $51 $2002: stra,r0 *$2007 $2005: retc,un $2007: dw $1800 will work correctly (although it is slower). Also, sometimes you can take advantage of mirroring to perform your work on the current page and let the machine mirror it to the other pages. For example, the $3800 in the example above is a mirror of $1800 on types "G" and "H" and therefore would in fact work on those machines. In source code, you need to put an ORG directive at each chunk boundary. The ORG number necessary is (address % $2000); ie. the non-page component of the address. See the table in the "Arcadia BIN Format" section table for the ORG address to use for each chunk. Otherwise, VACS will have trouble with, for example, loads and stores (LODA/STRA/LODR/ STRR), as described in the "VACS/DASMx Bugs" section. When performing absolute branches (eg. BCTA/BSTA), you should add the page component of the address to the label. Eg. if a routine called FOO is at $5100, then it is on the 3rd page (page 2, as page numbering starts from zero) (ie. in the $4000..$5FFF range in memory). You must jump to it with BCTA,un PAGE2+FOO (the same as BCTA,un $4000+FOO), even if you are (ie. the IAR is) already on the same page. You can look at the 8K.ASM and 31K.ASM files in the Examples Pack to see working examples of large EA2001 programs (for types "G/H" and "I", respectively). The effective address is decoded by a real 2650 after the decision is made about whether a branch will be taken, not before. This is important because "entering the indirect addressing sequence adds two cycles (6 clock periods) to the execution time of an instruction." The 2650 manual seems to imply elsewhere that the effective address is calculated before the instruction itself is executed. However, this has now been proven to be wrong. The time penalty for indirection is applied only if the branch is actually taken. Paddles ------- "The potentiometers [ie. P1PADDLE and P2PADDLE] read from $00 to $FE, and going to $FF when VRESET is low. I would assume the other Arcadia titles check to see if the potentiometer is [much] higher than $80 rather than simply looking for a value of $FF which doesn't happen on the real machine." There are two paddles available. At any given moment they will either both contain X-coordinates or they will both contain Y-coordinates. Bit 6 of BGCOLOUR controls the paddle interpolation for both paddles. If this bit is 0, the Y-coordinates are available for reading. If it is 1, the X- coordinates are available. It seems that read the coordinates and then you set bit 6 of BGCOLOUR for which axis you want to read *next* frame. If you are only interested in one axis there is no need to flip the bit every frame, of course. You can only read the coordinates during vertical blank; reading them while the screen is being drawn will always give $FF. Also, the Emerson and Schmid (at least) have ports to connect up to two additional hand controllers. The MPT-03 (at least) does not. No known games seem to support these. The hardware in question (unpluggable hand controllers) does not seem to have ever been officially produced, though it would presumably be trivial to develop such hardware, or to modify a "non-unpluggable" hand controller (by cutting the cable and adding a plug) for the purpose. The memory addresses at which these hand controllers would appear is not known (most likely somewhere in the $1909..$190F and/ or $19C0..$19F7 regions). Support for these hand controllers could be added to the emulators if the addresses were known. The ranges used by each game for each direction are somewhat different. The values returned by the paddles are in fact different on each individual machine, and probably somewhat different for each individual hand controller. A value of $66 or $6A for a centred paddle appears most common. left/up centred right/down ALIENINV.BIN $00..$35 $36..$7D $7E..$FE CIRCUS .BIN $00..$3A $3B..$93 $94..$FE HOBO .BIN $00..$37 $38..$98 $99..$FE SSQUADRO.BIN $00..$2F $30..$A8 $A9..$FE Only rough values are used in the table below: X Y Paddle is being pushed ------------------------------------ $00 $00 up and left $70 $00 up +---------+---------+---------+ $FE $00 up and right | 0,0 | $70,0 | $FE,0 | $00 $70 left +---------+---------+---------+ $70 $70 centred | 0,$70 | $70,$70 | $FE,$70 | $FE $70 right +---------+---------+---------+ $00 $FE down and left | 0,$FE | $70,$FE | $FE,$FE | $70 $FE down +---------+---------+---------+ $FE $FE down and right Note that these are read-once registers: they can only be read (once) during vblank, and can never be written. Graphics -------- 3-bit colour codes (eg. as used for background and sprite colours) are as follows: %000 White %001 Yellow %010 Cyan (light blue) %011 Green %100 Purple (magenta) %101 Red %110 Blue (dark blue) %111 Black Orange, brown, grey, pink, etc. are not available but can of course be simulated by alternating two colours in successive frames, and/or by dithering, eg.: black + white = grey red + white = pink red + yellow = orange black + yellow = brown There are two colour modes, selected by the high bit (bit 7) of PITCH ($18FD). Mode 0 allows four character colours over one background colour. Mode 1 ("board mode") allows two character colours over two background colours. Character (tile) colours (bit 3 of BGCOLOUR) are as follows, for mode 0: $00..$3F $40..$7F $80..$BF $C0..$FF %x,x,xx0,xxx White Cyan Purple Blue %x,x,xx1,xxx Yellow Green Red Black In mode 0: bits 5..4 of BGCOLOUR are unused. bit 3 of BGCOLOUR is character colour set: 0: characters $00..$3F: white characters $40..$7F: cyan characters $80..$BF: purple characters $C0..$FF: blue 1: characters $00..$3F: yellow characters $40..$7F: green characters $80..$BF: red characters $C0..$FF: black bits 0..2 of BGCOLOUR are background colour. bits 5..0 of GFXMODE are unused. In mode 1: bits 5..3 of BGCOLOUR are 2nd character colour. bits 0..2 of BGCOLOUR are outer background colour. bits 5..3 of GFXMODE are 1st character colour. bits 0..2 of GFXMODE are inner background colour. chars $00..$3F: 1st char colour on inner bkgrnd colour. chars $40..$7F: 2nd char colour on inner bkgrnd colour. chars $80..$BF: 1st char colour on outer bkgrnd colour. chars $C0..$FF: 2nd char colour on outer bkgrnd colour. Block graphics mode can be set by a $C0 at the start of a line, and terminated by a $40 at the start of a line, or controlled by the high bit (bit 7) of GFXMODE ($19F8). The format is: 22211100 22211100 22211100 22211100 55544433 55544433 55544433 55544433 where each digit is the bit position corresponding with that pixel. Bits 7..6 are the colour, as normal. Examples: %xx000001 %xx000010 %xx000011 %xx000100 %xx000101 %xx000110 ......## ...###.. ...##### ###..... ###...## ######.. ......## ...###.. ...##### ###..... ###...## ######.. ......## ...###.. ...##### ###..... ###...## ######.. ......## ...###.. ...##### ###..... ###...## ######.. ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ ........ Character vs. block mode, and mode 0 vs. mode 1, are separate issues, and thus the modes can be freely combined. If you want low-res mode, you should set: GFXMODE as %x,0,xxx,xxx and BGCOLOUR as %0,x,xxx,xxx If you want hi-res mode, you should set: GFXMODE as %x,1,xxx,xxx and BGCOLOUR as %1,x,xxx,xxx Although it is possible to set them independently, it is generally pointless. The exception would be if you wanted low-resolution mode but did want the $1A00..$1ACF region drawn, and were using VSCROLL to vertically scroll the screen to reveal that area. VSCROLL ($18FC) is "the complement of the number of lines from the trailing edge of vertical drive to start character display". In other words, you use this register to vertically scroll the entire display. All 8 bits of the register are used for this. Higher values indicate positions towards the top of the screen, lower values indicate positions towards the bottom of the screen (as for sprites). $FF pushes the display to the top of the screen. $ED seems to be the default. $DF is the lowest which will show the entire display under MESS V2. Presumably, you can read this register, but it is really intended to be written. CHARLINE ($18FF) is a read-only hardware register. Bits 7..4 are apparently unused? (these bits are always set?) Bits 3..0 indicate the current character line (row) being rendered. 0..12 = which line 13 ($D) = end of DMA (ie. start of vertical blank) 14 ($E) = unused? 15 ($F) = beginning of DMA (ie. end of vertical blank) Startup Code ------------ Valid programs have certain startup requirements. Omitting these will not cause problems on most emulators but will cause problems on the genuine console. The machine at startup cannot be assumed to be in a known state; you should explicitly initialize it. A soft reset does not reinitialize the contents and status of the UVI, RAM, or even the CPU! The fourth byte (ie. byte 3) must always be $17, which is a RETC,UN instruction. You should use bytes 0..2 to jump past that byte. You must then clear then set the PSU and PSL, then wait, then clear memory. Your code can begin immediately following this code, at byte $20 (32). Of course, some programs use a zero page jump table, so will have this routine stored elsewhere. At program startup (IAR=$0), MESS V2 memory is initialized as follows: $1800..$18FE: $FF $18FF..$1908: $F0 $1909..$19FD: $FF $19FE..$19FF: $00 $1A00..$1BFF: $FF All other bytes are $00, except for used ROM. ;Standard Bootstrap-----------------------------------------------37665,33 org $0 ;0 eorz r0 ;r0 = 0; ;1 bctr,un BOOT ;2 retc,un ;1 BOOT: lpsu ;1 lpsl ;1 ppsu $20 ;PSU = %00100000; // inhibit interrupts ;2 ppsl $02 ;PSL = %00000010; // set unsigned compare ;2 stra,r0 VSCROLL ;VSCROLL = r0 [0]; ;3 BOOT_1: tpsu $80 ;2 bcfr,eq BOOT_1 ;2 BOOT_2: tpsu $80 ;2 bctr,eq BOOT_2 ;2 strz r1 ;r1 = r0 [0]; ;1 BOOT_3: stra,r0 $17FF,r1 ;3 stra,r0 $18FF,r1 ;3 stra,r0 $19FF,r1 ;3 bdrr,r1 BOOT_3 ;2 ;End Standard Bootstrap--------------------------------------------------- Decimal Arithmetic ------------------ Four-digit decimal numbers can be implemented as follows. Two decimal digits can be packed into each byte, one digit per nybble. The numbers are relative to $66, eg: addi,r0 $65 means -1 addi,r0 $66 means 0 addi,r0 $67 means +1 To add 1 to a four-digit decimal number, use the following code: ppsl $08 ;PSL |= %00001000; // set WC (With Carry) bit cpsl $01 ;PSL &= %11111110; // clear C (Carry/Borrow) bit loda,r0 LOWBYTE addi,r0 $67 dar,r0 stra,r0 LOWBYTE loda,r0 HIGHBYTE addi,r0 $66 ;add 0, but any carry is applied dar,r0 stra,r0 HIGHBYTE cpsl $08 ;PSL &= %11110111; // clear WC (With Carry) bit High byte, bits 7..4: thousands digit (%0000..%1001) High byte, bits 3..0: hundreds digit (%0000..%1001) Low byte, bits 7..4: tens digit (%0000..%1001) Low byte, bits 3..0: ones digit (%0000..%1001) Timing ------ See http://amigan.yatho.com/i-coding . The Arcadia uses the same 2621 or 2622 USG chips as the Interton and has the same timings. Adapted from Atari Graphics and Game Design (for NTSC): "The electron beam retraces its 208-line path sixty times a second. A television set actually scans 262 scan lines, but the average set can only display slightly more than 200 lines. The area above and below your rectangular playfield contains some of these extra lines. The electron beam has time to send 227 colour clocks (pixels) on one scan line. Again some of the information is plotted off screen. A progressive NTSC television image is composed of 262 lines. Some of these are offscreen due to normal vertical overscan. It takes the TV about 63.4158810686833 us (microseconds) to draw a single line. This includes horizontal blank (the time it takes to shut the electron beam off and reposition it back on the left edge one scan line below). The length of vertical blank is equivalent to 20 scan lines or 1268.31762137367 us. With the Arcadia's 2650 CPU running at 894,886.25 Hz, 1268.31762137367 us is equivalent to exactly 1135 machine cycles (1268.31762137367 us * 894,886.25 Hz = 1135)." Adapted from the Stella Programmer's Guide (for NTSC): "For the purposes of Arcadia programming, a single television frame consists of 262 horizontal lines, and each line is divided by 227 clock counts (3,579,545 Hz). The actual TV picture is drawn line by line from the top down 60.1867202475031 times a second, and actually consists of only a portion of the entire frame. A typical frame will consists of 3 vertical sync lines (to signal the TV set to start a new frame), 17 vertical blank lines, 208 TV picture lines, and 34 overscan lines (=262 lines in total). This pattern will work on all types of TV sets. Each scan line starts with 39 clock counts of horizontal blank (not seen on the TV screen) followed by 188 clock counts to fully scan one line of TV picture. When the electron beam reaches the end of a scan line, it returns to the left side of the screen, waits for the 39 horizontal blank clock counts, and proceeds to draw the next line below. All horizontal and vertical timing is taken care of by hardware. Since one 2650 CPU machine cycle occurs every 4 clock counts (pixels), the programmer has only 56.75 machine cycles per line (227 / 4 = 56.75)." BOWLING waits for approx. 284 CPU cycles for a scanline? CIRCUS exhibits different behaviour depending on whether the vertical blanking interval takes <= 621 slow CPU cycles or not (<= approx. 32 rastlines). "Green mode" is when the CPU is slow and the UVI is fast (vblank done in not many CPU cycles) (NTSC), and "black mode" is when the CPU is fast and the UVI is slow (vblank takes a lot of CPU cycles) (PAL). NTSC vertical blank takes 18.91667 slow cycles per rastline * 20 rastlines = 378.3' slow cycles per vblank. PAL vertical blank takes 18.91667 slow cycles per rastline * 43 rastlines = 813.41681 slow cycles per vblank. This is based on information found in the 2637 UVI manual: While drawing the top area (ie. VOFFSET area): DMA is $FF. UVI and RAM are accessible. Sense is probably pulled low now. Sprite coordinates and vertical offset are read now. UDCs may be changed now. While drawing main area: DMA is $F0..$FC. UVI is inaccessible. RAM may be locked out. Sense is pulled low now if it wasn't already. After drawing main area, now it is drawing the bottom area (ie. rest of VOFFSET area): DMA is $FD. UVI is accessible. Sense is maybe pulled high now. RAM may be locked out. Now it is doing 3 more (undisplayed) rasters. During this time the electron beam is actually moving diagonally up the screen in preparation for drawing the next frame (ie. this is the true vertical retrace interval): UVI and RAM are accessible. DMA is $FF. Sense is pulled high now if it wasn't already. UDCs may be changed now. To wait for the START of the vertical blank period, the usual method is: WAITVBLSTART: ;start of loop tpsu $80 ;test bits %10000000 of PSU; ;3,2 bcfr,eq WAITVBLSTART ;if != goto WAITVBLSTART; ;3,2 ;end of loop To wait for the END of the vertical blank period, the usual method is: WAITVBLEND: ;start of loop tpsu $80 ;test bits %10000000 of PSU; ;3,2 bctr,eq WAITVBLEND ;if == goto WAITVBLEND; ;3,2 ;end of loop So, BCFR is used when waiting for the START of vertical blank (ie. the END of the display frame), and BCTR is used when waiting for the END of vertical blank (ie. the START of the display frame). To wait for a specified CHARLINE, the usual method is: WAITLINE: ;start of loop loda,r0 CHARLINE andi,r0 $0F comi,r0 $03 ;change the operand according to desired line bcfr,eq WAITLINE ;end of loop The example above waits until line 3 (ie. CHARLINE == $F3). Change the operand of the COMI instruction as appropriate for other lines. Tones ----- Here are the optimal PITCH register values for harmonic melodies, in hexadecimal and decimal notations: PITCH reg Actual Freq Ideal Freq ---------- $7F (127) 61.52 Hz ---------- ---------- $7E (126) 62.00 Hz ---------- ---------- $7D (125) 62.49 Hz ---------- xlow B : $7C (124) 62.99 Hz 61.73 Hz ---------- $7B (123) 63.50 Hz ---------- ---------- $7A (122) 64.02 Hz ---------- ---------- $79 (121) 64.54 Hz ---------- ---------- $78 (120) 65.07 Hz ---------- vlow C : $77 (119) 65.62 Hz 65.40 Hz ---------- $76 (118) 66.17 Hz ---------- ---------- $75 (117) 66.73 Hz ---------- ---------- $74 (116) 67.30 Hz ---------- ---------- $73 (115) 67.88 Hz ---------- ---------- $72 (114) 68.47 Hz ---------- ---------- $71 (113) 69.07 Hz ---------- vlow C#: $70 (112) 69.68 Hz 69.29 Hz ---------- $6F (111) 70.30 Hz ---------- ---------- $6E (110) 70.94 Hz ---------- ---------- $6D (109) 71.58 Hz ---------- ---------- $6C (108) 72.24 Hz ---------- vlow D : $6B (107) 72.91 Hz 73.41 Hz ---------- $6A (106) 73.59 Hz ---------- ---------- $69 (105) 74.28 Hz ---------- ---------- $68 (104) 74.99 Hz ---------- ---------- $67 (103) 75.71 Hz ---------- ---------- $66 (102) 76.45 Hz ---------- ---------- $65 (101) 77.20 Hz ---------- vlow D#: $64 (100) 77.96 Hz 77.78 Hz ---------- $63 ( 99) 78.74 Hz ---------- ---------- $62 ( 98) 79.54 Hz ---------- ---------- $61 ( 97) 80.35 Hz ---------- ---------- $60 ( 96) 81.18 Hz ---------- vlow E : $5F ( 95) 82.02 Hz 82.40 Hz ---------- $5E ( 94) 82.88 Hz ---------- ---------- $5D ( 93) 83.77 Hz ---------- ---------- $5C ( 92) 84.67 Hz ---------- ---------- $5B ( 91) 85.59 Hz ---------- ---------- $5A ( 90) 86.53 Hz ---------- vlow F : $59 ( 89) 87.49 Hz 87.30 Hz ---------- $58 ( 88) 88.47 Hz ---------- ---------- $57 ( 87) 89.48 Hz ---------- ---------- $56 ( 86) 90.51 Hz ---------- ---------- $55 ( 85) 91.56 Hz ---------- vlow F#: $54 ( 84) 92.64 Hz 92.49 Hz ---------- $53 ( 83) 93.74 Hz ---------- ---------- $52 ( 82) 94.87 Hz ---------- ---------- $51 ( 81) 96.02 Hz ---------- ---------- $50 ( 80) 97.21 Hz ---------- vlow G : $4F ( 79) 98.42 Hz 98.00 Hz ---------- $4E ( 78) 99.67 Hz ---------- ---------- $4D ( 77) 100.95 Hz ---------- ---------- $4C ( 76) 102.26 Hz ---------- vlow G#: $4B ( 75) 103.61 Hz 103.82 Hz ---------- $4A ( 74) 104.99 Hz ---------- ---------- $49 ( 73) 106.41 Hz ---------- ---------- $48 ( 72) 107.86 Hz ---------- ---------- $47 ( 71) 109.36 Hz ---------- vlow A : $46 ( 70) 110.90 Hz 110.00 Hz ---------- $45 ( 69) 112.49 Hz ---------- ---------- $44 ( 68) 114.12 Hz ---------- ---------- $43 ( 67) 115.79 Hz ---------- vlow A#: $42 ( 66) 117.52 Hz 116.54 Hz ---------- $41 ( 65) 119.30 Hz ---------- ---------- $40 ( 64) 121.14 Hz ---------- vlow B : $3F ( 63) 123.03 Hz 123.47 Hz ---------- $3E ( 62) 124.98 Hz ---------- ---------- $3D ( 61) 127.00 Hz ---------- ---------- $3C ( 60) 129.08 Hz ---------- low C : $3B ( 59) 131.23 Hz 130.81 Hz ---------- $3A ( 58) 133.46 Hz ---------- ---------- $39 ( 57) 135.76 Hz ---------- low C#: $38 ( 56) 138.14 Hz 138.59 Hz ---------- $37 ( 55) 140.61 Hz ---------- ---------- $36 ( 54) 143.16 Hz ---------- low D : $35 ( 53) 145.81 Hz 146.83 Hz ---------- $34 ( 52) 148.57 Hz ---------- ---------- $33 ( 51) 151.42 Hz ---------- ---------- $32 ( 50) 154.39 Hz ---------- low D#: $31 ( 49) 157.48 Hz 155.56 Hz ---------- $30 ( 48) 160.69 Hz ---------- low E : $2F ( 47) 164.04 Hz 164.81 Hz ---------- $2E ( 46) 167.53 Hz ---------- ---------- $2D ( 45) 171.17 Hz ---------- low F : $2C ( 44) 174.98 Hz 174.61 Hz ---------- $2B ( 43) 178.95 Hz ---------- low F#: $2A ( 42) 183.12 Hz 184.99 Hz ---------- $29 ( 41) 187.48 Hz ---------- ---------- $28 ( 40) 192.05 Hz ---------- low G : $27 ( 39) 196.85 Hz 196.00 Hz ---------- $26 ( 38) 201.90 Hz ---------- low G#: $25 ( 37) 207.21 Hz 207.65 Hz ---------- $24 ( 36) 212.81 Hz ---------- low A : $23 ( 35) 218.72 Hz 220.00 Hz ---------- $22 ( 34) 224.97 Hz ---------- low A#: $21 ( 33) 231.59 Hz 233.08 Hz ---------- $20 ( 32) 238.61 Hz ---------- low B : $1F ( 31) 246.06 Hz 246.94 Hz ---------- $1E ( 30) 254.00 Hz ---------- middle C : $1D ( 29) 262.47 Hz 261.63 Hz ---------- $1C ( 28) 271.52 Hz ---------- middle C#: $1B ( 27) 281.21 Hz 277.18 Hz middle D : $1A ( 26) 291.63 Hz 293.66 Hz ---------- $19 ( 25) 302.84 Hz ---------- middle D#: $18 ( 24) 314.96 Hz 311.13 Hz middle E : $17 ( 23) 328.08 Hz 329.63 Hz ---------- $16 ( 22) 342.35 Hz ---------- middle F : $15 ( 21) 357.91 Hz 349.23 Hz middle F#: $14 ( 20) 374.95 Hz 369.99 Hz middle G : $13 ( 19) 393.70 Hz 392.00 Hz middle G#: $12 ( 18) 414.42 Hz 415.30 Hz middle A : $11 ( 17) 437.44 Hz 440.00 Hz middle A#: $10 ( 16) 463.18 Hz 466.16 Hz middle B : $0F ( 15) 492.12 Hz 493.88 Hz high C : $0E ( 14) 524.93 Hz 523.25 Hz high C#: $0D ( 13) 562.43 Hz 554.37 Hz high D : ----------------------- 587.33 Hz high D#: $0C ( 12) 605.69 Hz 622.25 Hz high E : $0B ( 11) 656.17 Hz 659.26 Hz high F : $0A ( 10) 715.82 Hz 698.46 Hz high F#: ----------------------- 739.99 Hz high G : $09 ( 9) 787.40 Hz 783.99 Hz high G#: ----------------------- 830.61 Hz high A : $08 ( 8) 874.89 Hz 880.00 Hz high A#: ----------------------- 932.30 Hz high B : $07 ( 7) 984.25 Hz 987.80 Hz vhigh C : ----------------------- 1046.50 Hz vhigh C#: $06 ( 6) 1124.86 Hz 1108.70 Hz vhigh D : ----------------------- 1174.70 Hz vhigh D#: ----------------------- 1244.50 Hz vhigh E : $05 ( 5) 1312.33 Hz 1318.50 Hz vhigh F : ----------------------- 1396.90 Hz vhigh F#: ----------------------- 1480.00 Hz vhigh G : $04 ( 4) 1574.80 Hz 1568.00 Hz vhigh G#: ----------------------- 1661.20 Hz middle G : $03 ( 3) 1968.50 Hz ---------- high C : $02 ( 2) 2624.67 Hz ---------- high G : $01 ( 1) 3937.00 Hz ---------- silence: $00 ( 0) ---------- ---------- This should assist in illustrating why tones are off-key. Strange results for the higher-pitched notes (3..1) are due to aliasing effects, which might be absent on the genuine console. The algorithm for tone generation is: frequency = 7874 � ((PITCH & %01111111) + 1); and the following algorithm also holds true: 1 � frequency = 2 * ((PITCH & %01111111) + 1) * horizontal line period; so for $01 (high G), at 3937 Hz: 1�3937 = 2 * (1 + 1) * horizontal line period 0.000254 = 4 * horizontal line period 0.0000635 = horizontal line period and for $02 (high C), at ~2624 Hz: 1�2624 = 2 * (2 + 1) * horizontal line period 0.000381 = 6 * horizontal line period 0.0000635 = horizontal line period Thus, it can be demonstrated that the horizontal line period is 63.5 �s (microseconds). The table below is similar to the table above, but is laid out as a piano- style keyboard. '!' means that it is not possible to play that note. ! ! ! ! ! 7 6 5 4 4 3 3 2 2 2 1 1 1 1 1 0 0 ! ! ! 0 ! ! ! ! ! ! ! ! ! 0 4 4 B 2 8 1 A 5 1 B 8 4 2 0 D C ! ! ! 6 ! ! ! ! ! ! !! ! ! 77 6 55 4 4 33 3 22 2 2 11 1 11 1 1 00 ! 00 0 0 0! ! 0! 0 ! !! ! ! !! ! ! C7 B F9 F 6 FB 5 FC 7 3 FD A 75 3 1 FE ! BA 9 8 7! ! 5! 4 ! !! [---xlow---][---vlow---][----low---][--middle--][---high---][---vhigh---] C#D#EF#G#A#BC#D#EF#G#A#BC#D#EF#G#A#BC#D#EF#G#A#BC#D#EF#G#A#BC#D#EF#G#A#BC Character Set ------------- The following table is an aid for quickly composing text messages and so forth. $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$ 0000000000000000111111111111111122222222222222223333333333333333 Hex Code (MSN) 0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF Hex Code (LSN) ---------------------------------------------------------------- /\abcdefghijklm0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ.,+$nopqrstu Character a = solid block b..e = sides f..i = corners j..m = triangles n..q = sprites #0..#3 r..u = UDCs #0..#3 Noise ----- The noise generator works much like the tone generator. As with the tone generator, it only ever generates square waves (ie. the speaker is only ever at maximum plus or maximum minus (here designated as +127 and -128 for convenience), not any intermediate values). However, whereas the tone generator flips from +127 to -127 and vice versa at regular, predictable intervals, the noise generator only flips, on average, on half of those occasions; the rest of the time, the output value is held steady. Or, to put it a different way, the difference is that whenever the tone generator would flip the amplitude (eg. change from -127 to +127 or vice versa), the amplitude will be randomly chosen to be -127 or +127. Eg. if you set a noise of $40, this is equivalent to 121.1385Hz. So, approximately every 121th of a second, the following pseudocode would be executed: if ((rand() % 2) == 0) value = 127; else value = -127; So, whereas a tone waveform might look like this: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| | a noise waveform of the same frequency might look like this: ___ _ _ ___ _____ _ _ _ ___ _ ___ _ | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |_| |_| |_| |___| |_| |_| |_| |___| |___| |_| |_| |_| |_| The higher the pitch (frequency), the more random the noise will sound. Ie. lower values are more regular and rhythmic. Sample values are (with acknowledgements to Tom Pittman): $7F = static $10 = rocket exhaust $04 = machine gun $02 = engine racing $00 = silence Note that the preceding discussion of the noise hardware also applies to systems such as the Interton VC 4000 and expanded Elektor TVGC. Hardware Registers ------------------ $18F0: SPRITE0Y $18F1: SPRITE0X $18F2: SPRITE1Y $18F3: SPRITE1X $18F4: SPRITE2Y $18F5: SPRITE2X $18F6: SPRITE3Y $18F7: SPRITE3X (all read/write) The horizontal and vertical coordinates of the four sprites will be accessed by the UVI at DMA 15. $18FC: VSCROLL (read/write) The vertical offset will be accessed by the UVI at DMA 15. $18FF: CHARLINE: Row status - last DMA number (4 bits) (read-only) The status can be accessed anytime in the field when the microprocessor is not paused. CHARLINE seems to go like this in low-resolution mode: $FF top margin (vertical offset as determined by VSCROLL) $F0 raster 0.. 15 $F1 raster 16.. 31 $F2 raster 32.. 47 $F3 raster 48.. 63 $F4 raster 64.. 79 $F5 raster 80.. 95 $F6 raster 96..111 $F7 raster 112..127 $F8 raster 128..143 $F9 raster 144..159 $FA raster 160..175 $FB raster 176..191 $FC raster 192..207 $FD raster 208..261 (Sense pin turned on after raster 256) $FF 'raster' 262..264 (vertical retrace) CHARLINE seems to go like this in high-resolution mode: $FF top margin (vertical offset as determined by VSCROLL) $F0 raster 0.. 7 $F1 raster 8.. 15 $F2 raster 16.. 23 $F3 raster 24.. 31 $F4 raster 32.. 39 $F5 raster 40.. 47 $F6 raster 48.. 55 $F7 raster 56.. 63 $F8 raster 64.. 71 $F9 raster 72.. 79 $FA raster 80.. 87 $FB raster 88.. 95 $FC raster 96..103 $F0 raster 104..111 $F1 raster 112..119 $F2 raster 120..127 $F3 raster 128..135 $F4 raster 136..143 $F5 raster 144..151 $F6 raster 152..159 $F7 raster 160..167 $F8 raster 168..175 $F9 raster 176..183 $FA raster 184..191 $FB raster 192..199 $FC raster 200..207 $FD raster 208..255 $FD raster 256..261 (Sense pin turned on after raster 256) $FF 'raster' 262..264 (vertical retrace) Raster lines as shown above are relative to 0 being the first line of the character (tile) display. $19FA: SPRITES23CTRL (8 bits) (write-only) Bit 7 : sprite #2 height (0=tall, 1=short) Bit 6 : sprite #3 height (0=tall, 1=short) Bits 5..3: sprite #2 colours Bits 2..0: sprite #3 colours $19FB: SPRITES01CTRL (8 bits) (write-only) Bit 7 : sprite #0 height (0=tall, 1=short) Bit 6 : sprite #1 height (0=tall, 1=short) Bits 5..3: sprite #0 colours Bits 2..0: sprite #1 colours $19FC: BGCOLLIDE: Sprite collision with characters (4 bits) (read-once) Bits 7..4: always %1111 Bit 3: sprite #3 collision with any character (%0=hit, %1=miss) Bit 2: sprite #2 collision with any character (%0=hit, %1=miss) Bit 1: sprite #1 collision with any character (%0=hit, %1=miss) Bit 0: sprite #0 collision with any character (%0=hit, %1=miss) It should be read during the vertical reset period. The bits are reset (to 0) on collision and set (to 1) by reading or the trailing edge of VRST. Each bit is set to 1 when the AND of the appropriate sprite and background videos is high. Each bit is reset to 0 when accessed or at the end of the vertical blanking period. Waiting until CHARLINE is $FD or $FF does not seem to work reliably on the real machine. It seems necessary to wait for the Sense bit (like eg. Combat does). $19FD: SPRITECOLLIDE: Inter-sprite collision (6 bits) (read-once) Bits 7..6: always %11 Bit 5: sprites #2/#3 collision (%0=hit, %1=miss) Bit 4: sprites #1/#3 collision (%0=hit, %1=miss) Bit 3: sprites #1/#2 collision (%0=hit, %1=miss) Bit 2: sprites #0/#3 collision (%0=hit, %1=miss) Bit 1: sprites #0/#2 collision (%0=hit, %1=miss) Bit 0: sprites #0/#1 collision (%0=hit, %1=miss) It should be read during the vertical reset period. The bits are reset (to 0) on collision and set (to 1) by reading or the trailing edge of VRST. Each bit is set to 0 when the AND of the appropriate two sprite videos is high. Each bit is reset to 1 when accessed or at the end of the vertical blanking period. $19FE: P2PADDLE: Right paddle status (8 bits) (read-once) During VRST: Range $00..$FE When mux=0: $00=up, about $70=middle, $f1=down When mux=1: $07=left, about $70=middle, $f1=right ____ During VRST: Returns $FF $19FF: P1PADDLE: Left paddle status (8 bits) (read-once) During VRST: Range $00..$FE When mux=0: $00=up, about $70=middle, $f1=down When mux=1: $07=left, about $70=middle, $f1=right ____ During VRST: Returns $FF Character Display ----------------- The horizontal resolution is 128 pixels; 16 tiles per row. The vertical resolution is either 104 (13 tiles per column) or 208 (26 tiles per column) pixels tall, Each tile is 8x8 in size. Graphics are character- based (ie. tile-based), not bitmapped. The screen is organized as a 16x13 or 16x26 grid of tiles, one byte per tile. There are also four freely positionable monochrome sprites. You can offset the display horizontally on a per-row (8 rasters) basis and vertically on a per-frame basis. The "upper screen" is stored from $1800..$18CF; the top row is $1800.. $1810, the 2nd row is $1810..$181F, etc., to the 13th row from $18C0.. $18CF. In low-resolution mode this is all that is used. However, in high- resolution mode, the 13 rows of the "lower screen", stored from $1A00.. $1ACF, are also shown. These 208 "lower screen" bytes are available for use as extra RAM in low-resolution mode. There are 64 characters in the EA2001 character set: 0 ( $00): built-in empty (' ') 1- 2 ($01..$02): built-in diagonal lines ('/', '\') 3 ( $03): built-in solid 4- 7 ($04..$07): built-in sides 8-11 ($08..$0B): built-in corners 12-15 ($0C..$0F): built-in right-angled triangles 16-25 ($10..$19): built-in numbers 0-9 26-51 ($1A..$33): built-in uppercase letters A-Z 52-55 ($34..$37): built-in punctuation ('.', ',', '+', '$') 56-59 ($38..$3B): sprites #0..#3 60-63 ($3C..$3F): user defined characters #0..#3 $00 ( ): ........ $01 (/) .......# $02 (\): #....... $03 (#): ######## ........ ......#. .#...... ######## ........ .....#.. ..#..... ######## ........ ....#... ...#.... ######## ........ ...#.... ....#... ######## ........ ..#..... .....#.. ######## ........ .#...... ......#. ######## ........ #....... .......# ######## $04 (b): ######## $05 (c): ......## $06 (_): ........ $07 (e): ##...... ######## ......## ........ ##...... ........ ......## ........ ##...... ........ ......## ........ ##...... ........ ......## ........ ##...... ........ ......## ........ ##...... ........ ......## ######## ##...... ........ ......## ######## ##...... $08 (f): ######## $09 (g): ######## $0A (h): ##...... $0B (i): ......## ######## ######## ##...... ......## ......## ##...... ##...... ......## ......## ##...... ##...... ......## ......## ##...... ##...... ......## ......## ##...... ##...... ......## ......## ##...... ######## ######## ......## ##...... ######## ######## $0C (j): .......# $0D (k): #....... $0E (l): ######## $0F (m): ######## ......## ##...... #######. .####### .....### ###..... ######.. ..###### ....#### ####.... #####... ...##### ...##### #####... ####.... ....#### ..###### ######.. ###..... .....### .####### #######. ##...... ......## ######## ######## #....... .......# These occupy the low 6 bits (bits 5..0) of the byte. The upper 2 bits (bits 7..6) are used for the foreground colour, as described elsewhere. Block mode is no different, except that a different set of image definitions is used, and these occupy all characters 0-63; therefore sprite and UDC imagery cannot be used in block mode. You will note that there is no provision made for lowercase letters. The image definitions of the four sprites (stored at $1980..$199F) are mirrored as characters $38..$3B (and, of course, $78..$7B, $B8..$BB and $F8..$FB). The sprite colours, as stored in SPRnnCTRL, have no influence on the colours of these; the colours used will be determined in the same way as for any other tile. There are also four dedicated UDCs (the image definitions of which are stored at $19A0..$19BF) which are not usable as sprites. $0123456789ABCDEF ---------------- $00: /\abcdefghijklm $00 : space $10: 0123456789ABCDEF $01-$0F (/-m): graphics characters $20: GHIJKLMNOPQRSTUV $38-$3B (n-q): sprites 0-3 $30: WXYZ.,+$nopqrstu $3C-$3F (r-u): user defined characters 0-3 Mode 0 ------ BGCOLOUR bits 2..0 (background colour): GRB %000 = 0 = white %001 = 1 = yellow %010 = 2 = cyan %011 = 3 = green %100 = 4 = purple %101 = 5 = red %110 = 6 = blue %111 = 7 = black The background colour may be freely chosen and does not affect the tile colours setting. When BGCOLOUR is %x,x,xx1,xxx, foreground colours are: $00..$3F = yellow (colour 1) $40..$7F = green (colour 3) $80..$BF = red (colour 5) $C0..$FF = black (colour 7) When BGCOLOUR is %x,x,xx0,xxx, foreground colours are: $00..$3F = white (colour 0) $40..$7F = cyan (colour 2) $80..$BF = purple (colour 4) $C0..$FF = blue (colour 6) You will note that these are equivalent to the two CGA palettes on the IBM-PC. However the background colour and sprite colours can be freely chosen from the full palette of 8 colours, and the Flag pin can also be used to invert the colours. Flag pin -------- Most consoles implement the Flag line (bit 7 of the PSU): while this is set, all colours are inverted. This even applies to sprites. The Tempest MPT-03, for example, definitely does implement this. However, some consoles, such as the actual Emerson Arcadia 2001, do not implement this and therefore the colours will not be inverted on such machines. Sense pin --------- This pin (bit 6 of the PSU) is read-only. It is set (to 1) as described elsewhere, to signify that the vertical retrace interval has begun, and cleared (to 0) to signify that the interval has ended. Mode 1 ------ In this mode the screen is filled with the inner background colour, but is surrounded by a border filled with the outer background colour (although of course if the inner and outer background colours are the same the border will not be apparent). The two background and two foreground colours can all be chosen and used freely. Bit 7 of each character specify whether to use the inner (0) or outer (1) background colour for the background (off bits in the image definition). Bit 6 of each character specify whether to use the 1st or 2nd foreground colour for the foreground (on bits in the image definition). RAM --- Only 84 bytes are assigned for use exclusively as user RAM. There is a 32- byte block from $18D0..$18EF, a 4-byte block from $18F8..$18FB, and a 48- byte block from $1AD0..$1AFF. However, as the screen memory can be read and written to it is also possible to use that for storage; eg. in low- resolution the lower screen memory at $1A00..$1ACF will not be displayed and there is no reason in that case not to use it as 208 bytes of extra RAM, increasing the total available to 292 bytes. Sprites ------- There are 4 sprites, each measuring 8x8 pixels. Their colours and position are independent of the underlying character display. Similar to the way the character display can be vertically "squashed" or "stretched" with bit 7 of BGCOLOUR, each sprite can be vertically "squashed" or "stretched" independently of the character display and the other sprites (using bit 7 or 6 of SPRITESnnCTRL). The foreground colour (1 bits in the image definition) of a sprite is chosen with bits 5..3 or 2..0 of SPRITESnnCTRL; all of the 8 colours are available. The background of a sprite (0 bits in the image definition) is never drawn. The coordinates of each sprite are relative to the bottom-left corner of the screen. That is to say, lower Y-coordinates are near the bottom of the screen, and higher Y-coordinates are near the top of the screen. Lower X- coordinates are near the left of the screen, and higher X-coordinates are near the right of the screen. CPU --- This section applies to all 2650-based machines. Never writing to any flags: BCFA/R BCTA/R BDRA/R BIRA/R BRNA/R BXA HALT NOP STRA/R WRTC WRTD WRTE ZBRR Only writing to CC: ANDA/I/R/Z COMA/I/R/Z DAR EORA/I/R/Z IORA/I/R/Z LODA/I/R/Z REDC REDD REDE SPSL SPSU TMI TPSL TPSU STRZ Only writing to SP: BSNA/R BSFA/R BSTA/R BSXA RETC ZBSR Writing to SP and II: RETE Writing to C, CC, IDC and OVF: ADDA/I/R/Z RRL RRR SUBA/I/R/Z Writing to all PSU bits (SP, II, F): CPSU LPSU PPSU Writing to all PSL bits (CC, IDC, RS, WC, OVF, COM, C): CPSL LPSL PPSL Reading from OVF and/or II: None Reading from IDC and C: DAR Reading from COM: COMA/I/R/Z Reading from C and WC: ADDA/I/R/Z RRL RRR SUBA/I/R/Z Reading from SP: BSNA/R BSFA/R BSTA/R BSXA RETC RETE ZBSR Reading from CC: most branches (eg. BCTR,eq). Reading from RS: most instructions (including eg. indexed branches). EORZ r0 is always better than LODI,r0 $00. BCTA,un $003F (or less), or BCTA,un $7FC0 (or more), are never better than ZBRR. And likewise for BSTA vs. ZBSR. COMZ r0 is always better than CPSL $C0. Both yield CC=eq. These are all the same in effect, size and speed: ANDI,Rn $FF EORI,Rn $00 IORI,Rn $00 All set CC according to r0. These are both the same in effect, size and speed: LODZ r0 (indeterminate!) IORZ r0 Both set CC according to r0. These are both the same in effect, size and speed: LODI,rn $FF IORI,rn $FF Both set rn to $FF and set CC to lt. These are both the same in effect, size and speed: LODI,rn $0 ANDI,rn $0 Both set rn to $00 and set CC to eq. It appears that code in the second 4K area cannot directly access addresses in the first 4K area, only indirectly. This only applies for statements with a 13-bit address field, such as LODA/STRA, not for flow control statements. LODA/STRA statements are encoded as follows: For LODA, the opcode field (one byte) is $0C..$0F: %000011,rr where rr indicate the destination or index register. In other words, $0C + register (0..3). For STRA, the opcode field (one byte) is $CC..$CF: %110011,rr where rr indicate the destination or index register. In other words, $CC + register (0..3). The operand field (two bytes) is as follows: bit 15: indirect (*) addressing flag bits 14..13: index control: %00 = no indexing rr is destination register %01 = incremental indexing (++) rr is index register r0 is destination register %01 = decremental indexing (--) rr is index register r0 is destination register %11 = normal indexing rr is index register r0 is destination register bits 12..0: absolute address ($0000..$1FFF) (8K range) $2000 is automatically added to the address for code beyond the 4K barrier. So you should use the algorithm + $2000. Here are some common values: opcode operand loda,r0 $0C %0,00, = loda,r1 $0D %0,00, = loda,r2 $0E %0,00, = loda,r3 $0F %0,00, = loda,r0 * $0C %1,00, = $8000 + loda,r1 * $0D %1,00, = $8000 + loda,r2 * $0E %1,00, = $8000 + loda,r3 * $0F %1,00, = $8000 + loda,r0 ,r1 $0D %0,11, = $6000 + loda,r0 ,r2 $0E %0,11, = $6000 + loda,r0 ,r3 $0F %0,11, = $6000 + loda,r0 *,r1 $0D %1,11, = $E000 + loda,r0 *,r2 $0E %1,11, = $E000 + loda,r0 *,r3 $0F %1,11, = $E000 + stra,r0 $CC %0,00, = stra,r1 $CD %0,00, = stra,r2 $CE %0,00, = stra,r3 $CF %0,00, = stra,r0 * $CC %1,00, = $8000 + stra,r1 * $CD %1,00, = $8000 + stra,r2 * $CE %1,00, = $8000 + stra,r3 * $CF %1,00, = $8000 + stra,r0 ,r1 $CD %0,11, = $6000 + stra,r0 ,r2 $CE %0,11, = $6000 + stra,r0 ,r3 $CF %0,11, = $6000 + stra,r0 *,r1 $CD %1,11, = $E000 + stra,r0 *,r2 $CE %1,11, = $E000 + stra,r0 *,r3 $CF %1,11, = $E000 + Relative Branches ----------------- The CPU calculates these from the *next* instruction, ie. the IAR is incremented by 2 so it points to the start of the next instruction, then the branch is calculated. Indirect branches are exactly the same as direct ones except that you must add $80 to the operand value. relative relative to next to this operand instruction instruction value -64 = $-40 -62 = $-3E $40 -63 = $-3F -61 = $-3D $41 -62 = $-3E -60 = $-3C $42 -61 = $-3D -59 = $-3B $43 -60 = $-3C -58 = $-3A $44 -59 = $-3B -57 = $-39 $45 -58 = $-3A -56 = $-38 $46 -57 = $-39 -55 = $-37 $47 -56 = $-38 -54 = $-36 $48 -55 = $-37 -53 = $-35 $49 -54 = $-36 -52 = $-34 $4A -53 = $-35 -51 = $-33 $4B -52 = $-34 -50 = $-32 $4C -51 = $-33 -49 = $-31 $4D -50 = $-32 -48 = $-30 $4E -49 = $-31 -47 = $-2F $4F -48 = $-30 -46 = $-2E $50 -47 = $-2F -45 = $-2D $51 -46 = $-2E -44 = $-2C $52 -45 = $-2D -43 = $-2B $53 -44 = $-2C -42 = $-2A $54 -43 = $-2B -41 = $-29 $55 -42 = $-2A -40 = $-28 $56 -41 = $-29 -39 = $-27 $57 -40 = $-28 -38 = $-26 $58 -39 = $-27 -37 = $-25 $59 -38 = $-26 -36 = $-24 $5A -37 = $-25 -35 = $-23 $5B -36 = $-24 -34 = $-22 $5C -35 = $-23 -33 = $-21 $5D -34 = $-22 -32 = $-20 $5E -33 = $-21 -31 = $-1F $5F -32 = $-20 -30 = $-1E $60 -31 = $-1F -29 = $-1D $61 -30 = $-1E -28 = $-1C $62 -29 = $-1D -27 = $-1B $63 -28 = $-1C -26 = $-1A $64 -27 = $-1B -25 = $-19 $65 -26 = $-1A -24 = $-18 $66 -25 = $-19 -23 = $-17 $67 -24 = $-18 -22 = $-16 $68 -23 = $-17 -21 = $-15 $69 -22 = $-16 -20 = $-14 $6A -21 = $-15 -19 = $-13 $6B -20 = $-14 -18 = $-12 $6C -19 = $-13 -17 = $-11 $6D -18 = $-12 -16 = $-10 $6E -17 = $-11 -15 = $-F $6F -16 = $-10 -14 = $-E $70 -15 = $-F -13 = $-D $71 -14 = $-E -12 = $-C $72 -13 = $-D -11 = $-B $73 -12 = $-C -10 = $-A $74 -11 = $-B -9 = $-9 $75 -10 = $-A -8 = $-8 $76 -9 = $-9 -7 = $-7 $77 -8 = $-8 -6 = $-6 $78 -7 = $-7 -5 = $-5 $79 -6 = $-6 -4 = $-4 $7A -5 = $-5 -3 = $-3 $7B -4 = $-4 -2 = $-2 $7C -3 = $-3 -1 = $-1 $7D -2 = $-2 0 = $0 $7E -1 = $-1 +1 = $+1 $7F 0 = $0 +2 = $+2 $00 +1 = $+1 +3 = $+3 $01 +2 = $+2 +4 = $+4 $02 +3 = $+3 +5 = $+5 $03 +4 = $+4 +6 = $+6 $04 +5 = $+5 +7 = $+7 $05 +6 = $+6 +8 = $+8 $06 +7 = $+7 +9 = $+9 $07 +8 = $+8 +10 = $+A $08 +9 = $+9 +11 = $+B $09 +10 = $+A +12 = $+C $0A +11 = $+B +13 = $+D $0B +12 = $+C +14 = $+E $0C +13 = $+D +15 = $+F $0D +14 = $+E +16 = $+10 $0E +15 = $+F +17 = $+11 $0F +16 = $+10 +18 = $+12 $10 +17 = $+11 +19 = $+13 $11 +18 = $+12 +20 = $+14 $12 +19 = $+13 +21 = $+15 $13 +20 = $+14 +22 = $+16 $14 +21 = $+15 +23 = $+17 $15 +22 = $+16 +24 = $+18 $16 +23 = $+17 +25 = $+19 $17 +24 = $+18 +26 = $+1A $18 +25 = $+19 +27 = $+1B $19 +26 = $+1A +28 = $+1C $1A +27 = $+1B +29 = $+1D $1B +28 = $+1C +30 = $+1E $1C +29 = $+1D +31 = $+1F $1D +30 = $+1E +32 = $+20 $1E +31 = $+1F +33 = $+21 $1F +32 = $+20 +34 = $+22 $20 +33 = $+21 +35 = $+23 $21 +34 = $+22 +36 = $+24 $22 +35 = $+23 +37 = $+25 $23 +36 = $+24 +38 = $+26 $24 +37 = $+25 +39 = $+27 $25 +38 = $+26 +40 = $+28 $26 +39 = $+27 +41 = $+29 $27 +40 = $+28 +42 = $+2A $28 +41 = $+29 +43 = $+2B $29 +42 = $+2A +44 = $+2C $2A +43 = $+2B +45 = $+2D $2B +44 = $+2C +46 = $+2E $2C +45 = $+2D +47 = $+2F $2D +46 = $+2E +48 = $+30 $2E +47 = $+2F +49 = $+31 $2F +48 = $+30 +50 = $+32 $30 +49 = $+31 +51 = $+33 $31 +50 = $+32 +52 = $+34 $32 +51 = $+33 +53 = $+35 $33 +52 = $+34 +54 = $+36 $34 +53 = $+35 +55 = $+37 $35 +54 = $+36 +56 = $+38 $36 +55 = $+37 +57 = $+39 $37 +56 = $+38 +58 = $+3A $38 +57 = $+39 +59 = $+3B $39 +58 = $+3A +60 = $+3C $3A +59 = $+3B +61 = $+3D $3B +60 = $+3C +62 = $+3E $3C +61 = $+3D +63 = $+3F $3D +62 = $+3E +64 = $+40 $3E +63 = $+3F +65 = $+41 $3F Eg. if you found the bytes $C8 and $50 at addresses $0100 and $0101: $C8 is the opcode STRR,R0 (you can find this out from eg. WinArcadia's "Help|Opcodes..." subwindow). We look up the operand value $50 on the above table (right-hand column). Now, looking at the central column, we see that it means -46 (which is $-2E in hex), relative from this instruction. So, the instruction is STRR,R0 $0100+$-2E, or, in other words, STRR,R0 $00D2. If we had instead found the bytes $C8 and $D0 at addresses $0100 and $0101, the instruction would instead be STRR,R0 *$00D2 (since $D0-$80=$50). Compiler 2001 ------------- Here is a dissassembly of sync_example.bin, with corresponding source lines shown. Note that the raw output of Compiler 2001 is encrypted and requires decrypting into ordinary 2650 code. Also, its output is buggy, as indicated below. cls: EORZ r0 ;$00 LODI,r1 $D0 ;$01..$02 cls_1: STRA,r0 $17FF,r1 ;$03..$05 BDRR,r1 CLS_1 ;$06..$07 EORZ r0 ;$08 // useless! LODI,r1 $D0 ;$09..$0A cls_2: STRA,r0 $19FF,r1 ;$0B..$0D BDRR,r1 CLS_2 ;$0E..$0F set sound off (actual version): LODA,r3 PITCH ;$10..$12 ANDI,r3 $F7 ;$13..$14 set sound off (corrected version): LODA,r3 VOLUME ;$10..$12 ANDI,r3 $F7 ;$13..$14 STRA,r3 VOLUME set noise off (actual version): LODA,r3 PITCH ;$15..$17 ANDI,r3 $EF ;$18..$19 set noise off (corrected version): LODA,r3 VOLUME ;$15..$17 ANDI,r3 $10 ;$18..$19 STRA,r3 VOLUME set text mode (actual version): LODA,r0 GFXMODE ;$1A..$1C ANDI,r0 $7F ;$1D..$1E STRA,r0 $1918 ;$1F..$21 set text mode (corrected version): LODA,r0 GFXMODE ;$1A..$1C ANDI,r0 $7F ;$1D..$1E STRA,r0 GFXMODE ;$1F..$21 set hi resolution: LODA,r3 BGCOLOUR ;$22..$24 IORI,r3 $80 ;$25..$26 STRA,r3 BGCOLOUR ;$27..$29 set vertical scroll n: LODI,r3 n ;$2A..$2B STRA,r3 VSCROLL ;$2C..$2E set horizontal scroll n: LODA,r3 VOLUME ;$2F..$31 ANDI,r3 $0F ;$32..$33 IORI,r3 n ;$34..$35 STRA,r3 VOLUME ;$36..$38 set extended colour mode off (actual version): LODA,r3 PITCH ;$39..$3B ANDI,r3 $7F ;$3C..$3D set extended colour mode off (corrected version): LODA,r3 PITCH ;$39..$3B ANDI,r3 $7F ;$3C..$3D STRA,r3 PITCH repeat: set screen colour green set screen colour blue forever LODA,r3 BGCOLOUR ;$3E..$40 ANDI,r3 $F8 ;$41..$42 IORI,r3 green [3] ;$43..$44 STRA,r3 BGCOLOUR ;$45..$47 LODA,r3 BGCOLOUR ;$48..$4A ANDI,r3 $F8 ;$4B..$4C IORI,r3 blue [6] ;$4D..$4E STRA,r3 BGCOLOUR ;$4F..$51 BCTA,un repeat ;$52..$54 Built-in constants: WHITE=0 YELLOW=1 CYAN=2 GREEN=3 PURPLE=4 RED=5 BLUE=6 BLACK=7 Label styles seen: