[dismiss]
The Wikibooks community has accepted video game strategy guides on this wiki! See Wikibooks:Strategy guides for the newly-created policy on strategy games. We're looking forward to your contributions.

# Signetics 2650 & 2636 programming/Printable version

From Wikibooks, open books for an open world
< Signetics 2650 & 2636 programming
Jump to navigation Jump to search

| This is the print version of Signetics 2650 & 2636 programming You won't see this message or any elements not part of the book's content when you print or preview this page.

# Introduction[edit | edit source]

Manufacturers

- Acetronic MPU 1000/2000
- Audiosonic PP-1292/1392 Advanced Programmable Video System
- Aureac Tele Computer
- Fountain Force 2
- Fountain 1292/1392 Advanced Programmable Video System
- Grandstand Advanced Programmable Video System
- Grundig Super Play Computer 4000
- Hanimex HMG 1292/1392 Advanced Programmable Video System
- Interton VC 4000
- ITMC Vidéo Ordinateur MPT-05
- Karvan Jeu Video TV
- Lansay 1392
- Palson CX-3000 Data Bass Sistem
- Prinztronic VC-6000
- Radofin 1292/1392 Advanced Programmable Video System
- Rowtron Television Computer System
- Societe Occitane Electronique OC-2000
- Teleng Television Computer System
- TRQ Video Computer H-21
- Videomaster Database
- Voltmace Database

Note: Flags indicate primary country of the company. Many consoles were available in multiple countries.

This book is a guide to programming a family of video game consoles based on the Signetics 2650 microprocessor and 2636 Programmable Video Interface. These consoles were manufactured and marketed in the late 1970s and early 1980s by numerous companies in Europe, Australia and New Zealand. They are largely software-compatible, though there are physical differences in the games cartridges. Some of the joysticks are self-centering while others are not. The audio effects and colour circuitry also vary slightly between clones.

This book will initially concentrate on the Videomaster / Voltmace Database, as that is the console I have to work with. When differences are documented from reliable sources, they will be discussed here where appropriate. I stress reliable as there seems to be a lot of badly sourced misinformation about these consoles on the web.

- Videomaster Database

- Interton VC 4000

- Acetronic MPU 1000

Readers should have some basic knowledge of assembler level programming, perhaps with processors of a similar vintage such as the 6502 or Z80. A little background in digital and analogue electronics would be helpful too. The glossary and bibliography will include basic information and links to further reading to fill in gaps in knowledge concerning these fundamentals.

This book starts with a description of the hardware of the console, and information about the microprocessor and programmable video interface. The intent is to keep these sections concise yet detailed so that they serve as a handy reference for programmers. This is followed by a series of tutorials showing how to program the various elements of the console so that eventually the reader will have enough knowledge to build their own games. Code for these tutorials will be found in the appendices. It is intended that this code will not only make it easy for the reader to gain hands-on experience, but will also provide useful chunks of code that can be built upon to create more useful programs. A second set of tutorials will delve further into code that combines these basic elements into something more interactive.

My objective in starting this book is to encourage the creation of new games for these consoles.

| Wikipedia has related information at 1292 Advanced Programmable Video System and Interton Video Computer 4000

- ITMC MPT-05

- Société Occitane d'électronique OC 2000

- Grundig Super Play Computer 4000

# Console Hardware[edit | edit source]

Externally these consoles are very similar to one another apart from their styling. They all have a slot for the game cartridge, four switches on the front, a pair of hand-controllers, and leads for power input and connection to a TV. Some have external power packs that supply low voltages to the console from the mains, while others perform this function internally.

The four switches on the console are power on/off, reset/load, start and select. (The load label that appears on some machines has led to some misinformation appearing on the web, as it has given the impression that the game is loaded from the cartridge to internal memory. This is incorrect as there is no internal memory on these machines. That button simply forces the microprocessor to start executing the program at memory address $0000 in the cartridge.)

Some of the consoles, such as the Database, have all the electronics on one printed circuit board, while others such as the Interton have divided the circuit into modules housed on smaller boards.

The game cartridge was configured in several different formats. Most had either a 2k or a 4k ROM, while some were 6k ROM and some had 1k RAM. The Acetronic Hobby Module was 2k ROM and 2k RAM.

### System architecture[edit | edit source]

Block diagram of the Voltmace Database video game console.

This block diagram shows the main components of the console and their most significant interconnections:

### Clock generation[edit | edit source]

All the necessary clocks for the system are derived from a single 8.867238 MHz crystal connected to the the video encoder. The encoder uses this for generating colour signals in its composite video output. It also divides it by 2½ to give a 3.55 MHz clock for the 2621 sync generator.

The sync generator passes this to the 2636 PVI as a pixel (or position) clock, and divides it by four to clock the microprocessor at 887 kHz. It also generates the vertical and horizontal reset signals for the PVI, as well as composite video timing signals for the video encoder.

### Processor[edit | edit source]

  Further information: 2650 processor

The Signetics 2650 is an eight bit microprocessor capable of addressing 32kB of memory. However, in this console only 13 of its address lines are used to access up to 8kB of memory.

The Reset signal causes it to start executing code at address $0000.

An Interrupt signal from the PVI, in conjunction with a vector supplied by the PVI, can cause execution to temporarily switch to $0003.

The Sense input is used to detect the state of the VRST signal (vertical blanking). The Flag output is used to determine which of the joystick potentiometers are connected to the PVI.

### Memory map[edit | edit source]

### 2636 PVI[edit | edit source]

  Further information: 2636 PVI

The 2636 Programmable Video Interface has 108 registers that control its audio and video output. Four other registers provide the processor with information about video object collisions and analogue joystick data. A further 37 registers are available as general purpose memory locations.

The PVI also generates chip select signals for various interface circuits and memory in the games cartridge. It also sends and interrupt request signal to the processor when it needs attention. See 2636 PVI for more details.

### Effects[edit | edit source]

The PVI can only generate a single square wave frequency. This is fed to some audio effects circuits that are controlled from the microprocessor via the 74LS378 latches. These circuits:

- turn the PVI audio on/off
- turn on white noise
- cause an explosion sound
- set one of four volume levels

One other bit in the effects register can invert the colour of the background grid and screen.

| To do: Make a 'see for further info' link when that chapter is done

### Player inputs[edit | edit source]

The Reset button on the console causes the processor to start executing the program at memory address $0000 in the game cartridge.

The Start, Select and keypad buttons are all controlled by the 74LS258 and 74LS156. They are arranged as a matrix, with the '156 selecting the column, and the '258 reading the data from the rows.

Each joystick controls two variable resistors. The four variable resistors are connected to the two analogue-to-digital converter inputs on the PVI via the CD4053 analogue multiplexer. The Flag output from the microprocessor controls which two resistors are to be measured.

Programmers should note that the joysticks are self-centring on some consoles and not on others. This can have a big impact on how they are used and programmed.

| To do: Make a 'see for further info' link when that chapter is done

# 2650 processor[edit | edit source]

Signetics logo
Signetics 2650A die

The 2650 was first released by Signetics in 1975.[1] These video game consoles use the 2650A, a redesigned version that was smaller, cheaper to manufacture and had improved operating margins. The 2650A was released in 1977.[2] A later version, the 2650B, had a few design changes and additional features, but we aren't concerned with those here. The 2650 was fabricated in NMOS, was TTL compatible, and its many control signals made it simple to interface to. It was described by Adam Osborne as being a very minicomputer-like device having been closely based on the IBM 1130. It has a number of distinguishing features:

- 32k byte address range, organised as four 8k byte pages.
- Variable-length instructions of 1, 2 or 3 bytes.
- Single bit I/O path (flag and sense pins).
- Multiple addressing modes including indirect and indexed.
- Vectored interrupt.
- Eight-level return address stack.
- Seven 8-bit registers.
- Two program status bytes.
- Powerful instruction set.

[3] [4]

## Registers[edit | edit source]

The 2650 has seven general purpose 8-bit registers. R0 is always available, whereas there are two banks of R1, R2 and R3 which are selected by the RS bit in the Program Status Word.

## Program Status Word[edit | edit source]

The Program Status Word (PSW) is a special purpose register that contains status and control bits. It is divided into two bytes, the Program Status Upper (PSU) and Program Status Lower (PSL). The PSW bits may be tested, loaded, stored, preset or cleared using the instructions tpsu, tpsl, lpsu, lpsl, spsu, spsl, ppsu, ppsl, cpsu or cpsl.

PSU
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
S | F | II | - | - | SP2 | SP1 | SP0

PSL
7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
CC1 | CC0 | IDC | RS | WC | OVF | COM | C

S | Sense input pin | Pin 1 of the 2650, connected to the Vertical Reset signal.
F | Flag output pin | Pin 40 of the 2650, determines if vertical or horizontal joystick potentiometers are read.
II | Interrupt Inhibit | Inhibits recognition of interrupt signals.
SP2,SP1,SP0 | Stack Pointer | Pointer to the internal Return Address Stack of 8 words.
CC1,CC0 | Condition code | Set by most operations except branches.
IDC | Inter Digit Carry | Stores the bit 3 to bit 4 carry in arithmetic and rotate instructions.
RS | Register bank Select | Determines which of the two banks of registers (r1,r2,r3) is being used.
WC | With/without Carry | Determines if the carry bit is used in arithmetic and rotate instructions.
OVF | Overflow | Set if a two's complement overflow occurs.
COM | Logical/arithmetic Compare | Determines how comparisons are made.
C | Carry | Stores any carry from the high order bit during arithmetic and rotate instructions.

## Reset[edit | edit source]

When a Reset pulse is applied to the 2650A, program execution is forced to begin at memory address 0. The only other thing we know for sure about the state of the processor after reset is that the Interrupt Inhibit bit in the Program Status Word is cleared. As we very much don’t want spontaneous interrupts to happen, one of the first things our program should do is set it to disable them until we ready for them. The “Signetics 2650 Initialization Application Memo” also suggests that we may set the Stack Pointer to zero, and that we should set the Register Select bit to a known state.[5]

The With Carry bit and the Compare bit may be set to whatever condition you will need them in first. In a simple program which has them in the same state throughout, this is a straightforward decision. In more complex programs it is generally best to set them to the most used condition initially, and then switch them back and forth whenever the opposite condition is needed. If the With Carry bit is on, then the Carry bit should be initialised appropriately before the first arithmetic instruction. Otherwise there is no need to initialise Carry or any of the other bits (Condition Code, Interdigit Carry, or Overflow). [5]

## Interrupts[edit | edit source]

Certain events occurring in the PVI cause it to signal the 2650 that it needs attention by setting the interrupt line low. If the Interrupt Inhibit bit in the Program Status Word is not set, (in other words interrupts are enabled), the processor will finish the current instruction and then set the Interrupt Inhibit bit. The PVI then outputs its interrupt vector, 3, to the data bus and the processor executes a ZBSR (branch to subroutine on page zero) to address $0003.

When the interrupt service subroutine has finished, it should be exited with either a RETC or RETE instruction. The latter will re-enable interrupts.

## Instruction set[edit | edit source]

All mnemonics in the 2650A instruction set are 3 or 4 characters long. The first two or three characters specify the operation, and if present, a final character indicates an addressing mode to use (see the next section, Addressing modes for more detail). Assembly code for the 2650 generally looks something like this:

    loop:   lodi,r0 $20          ; load r0 from the byte immediately following, $20
            stra,r0 $1F40	     ; store r0 in the absolute address $1F40
            eorz    r0           ; exclusive-or r0 with itself
            bctr,un loop         ; branch relative, unconditionally, to address specified by 'loop'

There are 34 basic instructions:

2650A basic instruction set
Transfer | LOD | Z,I,R,A | Load register
STR | Z,R,A | Store register
Arithmetic | ADD | Z,I,R,A | Add to register
SUB | Z,I,R,A | Subtract from register
DAR |  | Decimal adjust register
Logical | AND | Z,I,R,A | Bitwise AND to register
IOR | Z,I,R,A | Bitwise OR to register
EOR | Z,I,R,A | Bitwise EXCLUSIVE-OR to register
Test | COM | Z,I,R,A | Compare value to register
TM | I | Test masked bits of register
Rotate | RRR |  | Rotate register right by one bit
RRL |  | Rotate register left by one bit
Branch | BCT | R,A | Branch on condition true
BCF | R,A | Branch on condition false
BRN | R,A | Branch if register not-zero
BIR | R,A | Increment the register and branch if it is not zero
BDR | R,A | Decrement the register and branch if it is not zero
ZBR | R | Branch, relative to address $0000
BX | A | Branch, indexed
Call / Return | BST | R,A | Branch to subroutine on condition true
BSF | R,A | Branch to subroutine on condition false
BSN | R,A | Branch to subroutine if register not-zero
ZBS | R | Branch to subroutine, relative to address $0000
BSX | A | Branch to subroutine, indexed
RET | C,E | Conditionally return from subroutine. RETE also enables interrupts
Input / Output | WRT | C,D,E | Input/output hardware is not implemented on these consoles
RED | C,D,E
Miscellaneous | HALT |  | Enter WAIT state until processor is reset or interrupted
NOP |  | Do nothing except fetch and execute this instruction
Program status word | LPS | U,L | Load the PSW
SPS | U,L | Store the PSW
CPS | U,L | Clear specified bits in the the PSW
PPS | U,L | Preset specified bits in the the PSW
TPS | U,L | Test specified bits in the the PSW

| To do: Create new page that explains each instruction in more detail. Make hyperlinks from this table to relevant section in new page

## Addressing modes[edit | edit source]

The Signetics 2650 has four main addressing modes: Register, Immediate, Relative and Absolute. In addition Indirect and Indexing may be combined with some of these.

### Register addressing[edit | edit source]

All register-to-register instructions are 1 byte in length and use R0 as the first operand, while the other can be any of R0, R1, R2, R3.

            lodz    r3          ; load r0 from r3
            addz    r2          ; r0 = r0 + r2
            lodz    r0          ; not very useful!
            eorz    r0          ; exclusive-or r0 with itself
                                ; very useful! A one byte instruction for r0 = 0

### Immediate addressing[edit | edit source]

In immediate addressing the first operand, which can be any register, is specified next to the instruction; followed by the value of the other operand, which implicitly must be a constant. All immediate addressing instructions are two bytes in length.

            lodi,r2 $20        ; r2 = 32
            subi,r0 8	   ; r0 = r0 - 8
            iori,r1 %00100000  ; set bit5 of r1
            comi,r3 15         ; compare r0 to 15

### Absolute addressing[edit | edit source]

In absolute addressing the first operand, which can be any register(but see exception in Indexed addressing below), is specified next to the instruction. The second argument designates the absolute address of the memory location where the other operand is located.

### Relative addressing[edit | edit source]

In relative addressing the first operand, which can be any register, is specified next to the instruction. The second argument designates a memory location where the other operand is located. This second argument is a relative displacement from the current address and can be any value from -64 to +63. In practice we don't specify this value as a number. It generally takes the form of a label and the assembler does the calculation for us. If the label is too far away the assembler will generate an error message. Relative addressing is also used in branch instructions to jump to a memory location close by.

            lodr,r2 number     ; r2 = 42
            bctr,un next       ; branch to next
    number: db      42         ; define a data byte = 42
    next:   addi,r2 53

### Indexed addressing[edit | edit source]

When indexed addressing is used, the effective address is calculated by adding the contents of the specified index register to the address field. The addition uses the value in the index register as an 8-bit positive number.

            lodi,r2 5
            loda,r0 $1F00,r2    ; r0 = contents of address $1F05

Note that the first argument is always r0. Some assemblers may allow this to be omitted from the code, but for the sake of readability it should be included if possible.

Two other options allow for the index register to be auto-decremented or auto-incremented before it is added to the base address:

            lodi,r2 5
            loda,r0 $1F00,r2+    ; r0 = contents of address $1F06
            lodi,r3 $A6
            loda,r0 $1000,r3-    ; r0 = contents of address $10A5

Indexing may only be used with absolute addressing. It may not be used with branch instructions, but two special instructions exist for this purpose. They are BXA (branch indexed, absolute) and BSXA (branch to subroutine indexed, absolute). Auto-increment and auto-decrement cannot be used with them, and register R3 must be specified as the index register.

### Indirect addressing[edit | edit source]

Indirect addressing means that the argument address of an instruction is not specified by the instruction itself, but rather the argument address will be found in the two bytes pointed to by the instruction. The indirect addressing mode is indicated by an asterisk.

    pointer dw $0F00            ; in most cases this would be an address in RAM, i.e. a variable
            .....
            loda,r1 *pointer    ; r1 = contents of $0F00

Indirection may be used with relative addressing as well, but since code in these consoles is operating out of ROM, and RAM addresses are generally too far away for a relative address, there is no apparent use. One exception does occur when space for code is at a premium:

                  :bar   equ     $1F0E
    0000  066A    :      lodi,r2 $6A
    0002  CE1F0E  :      stra,r2 bar
    0005  0D1F0E  :foo   loda,r1 bar
    0008  0BFC    :      lodr,r3 *foo+1   ; load r3 indirectly from $0006, i.e. $1F0E

The last two instructions both load a register from bar, but the last instruction only uses two bytes rather than three.

### Indexed Indirect addressing[edit | edit source]

When indexing and indirect are used together, the value of the index register is added to the indirect address, not to the value in the address field of the instruction.

## Further reading[edit | edit source]

For more detailed information consult the Signetics 2650 User Manual, available as a full pdf or abridged for programmers as html.

## References[edit | edit source]

| Wikipedia has related information at Signetics 2650 and NMOS logic

1. ↑ Microcomputer Digest (Cupertino, CA: Microcomputer Associates Inc) 2 (1): 1-3. July 1975. http://bitsavers.trailing-edge.com/magazines/Microcomputer_Digest/Microcomputer_Digest_v02n01_Jul75.pdf. Retrieved 4 December 2021.
2. ↑ "Signetics 2650: An IBM on a Chip". October 16th, 2016. https://www.cpushack.com/2016/10/16/signetics-2650-an-ibm-on-a-chip/#comment-68442.
3. ↑ Osborne, Adam (1978). An introduction to microcomputers. Berkeley, Calif.: Osborne & Associates. p. 736. ISBN 0-931988-15-2. http://www.bitsavers.org/pdf/osborne/books/Osborne_An_Introduction_to_Microcomputers_Volume_2_Sep78.pdf. Retrieved 4 November 2021.
4. ↑ 2650 Series Microprocessor datasheet. Philips. https://frank.pocnet.net/sheets/084/2/2650.pdf. Retrieved 4 November 2021.
5. ↑ Jump up to: a b Signetics Applications Memo MP51 - 2650 initialization. Philips. https://frank.pocnet.net/other/sos/Philips_2650/Philips_(Signetics)_2650_MP51.pdf. Retrieved 4 November 2021.

# 2636 PVI[edit | edit source]

| This section of the book is primarily reference material for the PVI. More detailed discussions can be found in the Tutorials.

The Signetics 2636 Programmable Video Interface (PVI) is a 40-pin DIL chip that handles a number of functions within the console:

- Video generation
  - Screen colour
  - Background grid
  - Score digits
  - Four objects (sprites)
  - Object completion and collision detection
- Square wave audio
- A/D conversion for the analogue joysticks
- Chip select signals for memory and input/output devices
- 37 bytes of scratch memory

These functions are all implemented by reading and writing to various registers within the PVI.

Graphical features of the PVI
|  | Screen colour Score digits Objects Background grid Object duplicates

## PVI registers[edit | edit source]

Internally the PVI registers are located at addresses $F00 to $FFF. Within the console's memory map they are located at addresses $1F00 to $1FFF. Programmers only need to be concerned with these latter addresses, so those will be the ones we use throughout this book. For the same reason we will also ignore the areas where the memory map is duplicated.

Note that some control registers are write-only. If a program needs to know the contents of any of them, a duplicate copy must be saved elsewhere. Tests on some of these registers on one console indicate that if a read operation is attempted the contents will be corrupted.

PVI registers
1F00 | Object descriptor 1
1F0E | 2 bytes of scratch
1F10 | Object descriptor 2
1F1E | 2 bytes of scratch
1F20 | Object descriptor 3
1F2E | XXXXXXXXXXXXXXXXX
1F40 | Object descriptor 4
1F4E | 32 bytes of scratch
1F6E | XXXXXXXXXXXXXXXXX
1F80 | Background vertical bar definition
1FA8 | Background horizontal bar extensions
1FAD | 1 byte of scratch
1FAE | XXXXXXXXXXXXXXXXX
1FC0 | Control registers
1FD0 | Duplicate 1FC0
1FE0 | Duplicate 1FC0
1FF0 | Duplicate 1FC0

Object descriptor 1,2,3 or 4
0 | Shape of object, 10 lines of 8 bits
1
2
3
4
5
6
7
8
9
A | Horizontal coordinate of object
B | Horizontal coordinate of duplicate
C | Vertical coordinate of object
D | Vertical offset of duplicate

Control registers
Byte | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | Read/Write | Function
--- | --- | --- | --- | --- | --- | --- | --- | --- | --- | ---
1FC0 | Size object 4 | Size object 3 | Size object 2 | Size object 1 | Write only | Sizes of objects
1FC1 |  | R1 | G1 | B1 | R2 | G2 | B2 | Write only | Colours of objects
1FC2 |  | R3 | G3 | B3 | R4 | G4 | B4 | Write only
1FC3 |  | Format | Position | Write only | Score format and position
1FC4 |  |  |
1FC5 |
1FC6 |  |

Grid colour
R | G | B

Grid enable

Screen colour
R | G | B

Write only
Grid enable and colours
1FC7 | Sound | Write only | Tone period
1FC8 | Score digit 1 | Score digit 2 | Write only | Values of the four score digits
1FC9 | Score digit 3 | Score digit 4
1FCA |

Object/grid collision
1 | 2 | 3 | 4

Object display complete
1 | 2 | 3 | 4

Read only
Collision status. Object display completion. VRLE set at leading edge of VRST. All bits reset when read or at trailing edge of VRST.
1FCB |  | VRLE |

Inter-object collision
1/2 | 1/3 | 1/4 | 2/3 | 2/4 | 3/4

1FCC | A/D pot 1 | Read only | A/D value, valid during VRST only.
1FCD | A/D pot 2
1FCE |  |  |
1FCF |

## Objects[edit | edit source]

| Wikipedia has related information at Sprite (computer graphics)

The PVI has four, programmable, two-dimensional objects that can be positioned anywhere on screen in a single 3-bit colour in one of four different sizes. It is said that the PVI was the first device to have this capability. This style of graphic was later termed a sprite, but we will stick with 'object' in this book as it is the term used in the Signetics datasheet.

### Object shape[edit | edit source]

The shape of the object is set in a ten-byte array. Bits set to zero are transparent, while bits set to a one are displayed as the selected colour.

### Object colours[edit | edit source]

Object colours are set by registers $1FC1 and $1FC2. See Programming colours for details.

### Object size[edit | edit source]

Objects can be displayed in four different sizes as set by two-bits in register $1FCO.

Magnification | Size | MSB | LSB
--- | --- | --- | ---
x1 | 8 x 10 | 0 | 0
x2 | 16 x 20 | 0 | 1
x4 | 32 x 40 | 1 | 0
x8 | 64 x 80 | 1 | 1

### Object position[edit | edit source]

Positioning of objects is determined by reference to the origin of the tv scan at the top-left of the screen.

The horizontal coordinate (i.e. $1F0A) is set as the number of horizontal clocks to skip after the start of the raster line before the object is displayed. It maybe changed while the object is being displayed in which case the remainder of the object will be displaced. The vertical coordinate (i.e. $1F0C) of an object is set with an eight-bit unsigned value equal to the number of lines to skip before the object is displayed. This value must be set before the trailing edge of VRST.

### Object duplicates[edit | edit source]

One or more duplicate objects can be displayed further down the screen. The horizontal coordinate of the duplicate (i.e. $1F0B) is set in the same way as the original object. The vertical coordinate of the duplicate is set as the "number of lines to skip - 1" after displaying the last line of its predecessor.

The horizontal coordinate of the duplicate is read by the PVI on each line during HRST, so if it is changed during display of a duplicate, the rest of the duplicate will appear at a different position. The vertical coordinate of the duplicate must be programmed before the object completion status bit is set by the previous occurrence of that object.

### Object completion[edit | edit source]

When the last line of an object has been displayed an object complete bit is set in register $1FCA and the PVI generates an interrupt signal for the microprocessor.

### Object collision[edit | edit source]

If an object overlaps the background grid an object/grid collision bit is set in register $1FCA. If two objects overlap an inter-object collision bit is set in register $1FCB.

## Score[edit | edit source]

  See also: Tutorial — Score

The four score digits may be displayed as either two separate 2-digit numbers or as a single 4-digit number. They may be displayed at the top or bottom of the screen, or by reprogramming during the vertical scan between lines 40 and 199 they may appear at both top and bottom. The mode of display is determined by the format and position bits of register $1FC3.

$1FC3 | 7 | 6 | 5 | 4 | 3 | 2 | 1 Format | 0 Position
--- | --- | --- | --- | --- | --- | --- | --- | ---
0 |  |  |  |  |  |  | two 2-digit | top
1 |  |  |  |  |  |  | one 4-digit | bottom

Each score digit is 12 clocks wide and 20 lines high.

The top position corresponds to lines 0 to 19 of the background grid, or vertical coordinates 20 to 39. The bottom position corresponds to lines 180 to 199, or vertical coordinates 200 to 219.

The colour of the score is the inverse of the colour programmed for the background grid. See: Programming colours

Horizontal position of score digits
| 1 | 2 | 3 | 4 | Appearance
--- | --- | --- | --- | --- | ---
Format = 0 | 28 | 44 | 76 | 92 | 12 34
Format = 1 | 28 | 44 | 60 | 76 | 1234

The four score digits in $1FC8 and $1FC9 may be set to any hexadecimal value 0 to F. Values 0-9 display as the number, A-F display a blank.

## Background grid[edit | edit source]

The PVI has a programmable background grid comprised of an array of elements, 16 wide and 20 deep.

These elements are vertical bars, one pixel wide, arranged in pairs of horizontal rows; the first row of each pair is two raster lines deep and the second row is 18 raster lines deep. Each of these 320 vertical bars may be switched on or off by setting a bit in a 40-byte array located at memory addresses $1F80 - $1FA7.

The vertical bars may be extended horizontally, controlled by five registers $1FA8 - $1FAC. Each of the five extension registers control four consecutive rows of vertical bars.

Extension registers
Register | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
--- | --- | --- | --- | --- | --- | --- | --- | ---
| Extend group x1,2,4 | Extend row x8
$1FA8 | Rows 1-4 | 4B | 4A | 3 | 2B | 2A | 1
$1FA9 | Rows 5-8 | 8B | 8A | 7 | 6B | 6A | 5
$1FAA | Rows 9-12 | 12B | 12A | 11 | 10B | 10A | 9
$1FAB | Rows 13-16 | 16B | 16A | 15 | 14B | 14A | 13
$1FAC | Rows 17-20 | 20B | 20A | 19 | 18B | 18A | 17

Group extension
Extension | 7 | 6
--- | --- | ---
x1 | 0 | 0
x2 | 0 | 1
x1 | 1 | 0
x4 | 1 | 1

Bits 5-0 of the extension register enable all of the vertical bars in a horizontal row to be extended to 8 pixels. The 18-line vertical bars are divided into two parts (A and B) each of 9 lines making it possible to extend the top and bottom part of the bars by 1, 2 or 4 pixels.

Bits 7-6 of the extension register enable all of the vertical bars in a group of four rows to be extended by to either 1, 2 or 4 pixels.

The colour of the background grid is set by bits 2-0 in register $1FC6. The background grid is enabled by setting bit 3 in register $1FC6. If the background grid is not enabled, registers $1F80-$1FAC maybe used as scratch memory, a whopping 45 bytes, in addition to the 37 bytes of scratch memory

## Sound[edit | edit source]

The PVI can generate a single audio square wave output. Its frequency is set by register $1FC7. If this value is 0, the square wave is inhibited, otherwise its period is 2(n+1)TH, where n is the value set in $1FC7 and TH is the horizontal reset period. For a PAL system this simplifies to 128(n+1)μs.

If the sound register is changed while audio is being output, the change will not become effective until the next negative or positive transition of the audio signal.

| The 2636 datasheet is misleading. It says that the horizontal reset period is normally 63.5μs. That is correct for an NTSC system, but for PAL it is 64μs.[1] Both the horizontal reset period and a number of audio output frequencies have been verified on a Voltmace Database using a Hantek DSO5102P oscilloscope.

See PVI audio frequency chart for a table of frequencies attainable.

## Analogue to digital conversion[edit | edit source]

Two analogue to digital converters in the PVI are used to determine the position of the analogue joysticks. External circuitry driven from the processor's flag output is used to select input from either the horizontal or the vertical potentiometers.

The conversion process occurs during the active part of the video scan. The resulting digital values must be read from $1FCC and $1FCD during the vertical reset period to get a valid result.

## References[edit | edit source]

1. ↑ http://martin.hinner.info/vga/pal.html

# Getting started[edit | edit source]

## Tutorial — Getting started[edit | edit source]

The objective of this tutorial is to gently introduce the mechanics of assembling, running and debugging a simple piece of code using WinArcadia. We'll be using the debugger to step through the program instruction by instruction, both to learn about using the debugger and also to gain a better understanding of how the processor works.

If you haven't done so already, instructions for downloading and installing WinArcadia can be found at WinArcadia.

### Tutorial program[edit | edit source]

The code for this tutorial can be found at Tutorial code - getting started. Copy & paste this code into a text file and append the standard label definitions file Tutorial code. Save the file as intro.asm in the Projects folder.

Open WinArcadia. If the emulator screen is not displaying INTERTON VC 4000, use menu items: Options > Machine > Interton

In the emulators command line, enter bp 0. This sets a breakpoint to stop execution of the program as soon as it starts.

Again in the command line, enter asm intro. This assembles the program we just created and opens a WinArcadia Output window which, if the assembly was successful, should include the message 0 warnings, 0 errors. It will also have created an assembly listing and a binary file in the Projects folder. Open the list file, intro.lst . The first column shows us the address where a line of program is stored, and the second column shows one, two or three bytes of the machine code. The rest of the line is the program we wrote.

WinArcadia will have automatically started to execute the program, but has been stopped straight away when it hit the breakpoint we entered at $0000. In the command line enter s. This will Step through the next instruction and display the state of the processor in the output window. The only thing that the first instruction — bcta,un reset — will have done is set the Instruction Address Register IAR to $0004, the address equivalent to label reset.

Step again (enter s in the command line). The instruction lodi,r0 $20 has been executed and we can see that r0 now holds $20.

Step twice again. These two instructions set up the Program Status Word. Our goals here are to disable interrupts, set the return address stack pointer to zero. See Reset.

The next instruction, eorz r0, is a single byte instruction that has the effect of setting r0 to $00, and then we save that in the 'effects' register. This turns off the audio and clears the invert bit .

We then take a branch to the subroutine InitPVI. Notice how the stack pointer SP has been incremented to 1, and the first location in the Return Address Stack is $000F.

The first two instructions in the subroutine set r0=0 and r3=$CA in preparation for setting all the PVI registers to 0. R3 will be used as an index register.

Step the program so that you are viewing the instruction stra,r0 object1,r3-. Notice that the debugger is showing us that index register r3 has been auto-decremented to $C9 and that r0 is going to be stored in SCORE34, the last address in the PVI that we can write to. Open the Tools menu and turn on the UVI/PVI(s) monitor. You should see that SCORERT is 00. Step twice more and SCORELT will also be set to OO.

We could go on single stepping through the loop if we needed to debug something, but in our case we are going to skip to the end of the loop we are in by using the r l command. This runs until after the next BDRR/BDRA/BIRR/BIRA/BRNR/BRNA instruction which does not branch. Note that this does not work for BCTR/BCTA/BCFR/BCFR.

The next line sets R3 up as an index ready to load shape and position data for object 1 which is fetched from a data table one. As you step through this loop, you can see the X and Y coordinates of object 1 being set in the PVI monitor. There is not a great deal more to see now, so click the Pause button at the top of the emulator window and the program run to its end. The emulator screen should be showing a black screen, four white score digits and a small magenta box.

One feature of the debugger is the Guide ray which shows where the TV beam would be as each instruction is being executed. It is turned on and off from the command line instruction gr. The picture here shows the guide ray as our program overwrites the default Interton display. If you watch the beam as you single step through the program you can see just how few instructions can be performed on each horizontal line of the scan.

To run the program again, select File > Reset to game and the program should once again hit the breakpoint at $0000. Note however that the emulator is still showing our black screen and magenta box. It is probably fairer to start again by selecting File > Reinitialise machine. Note that the emulator must not be Paused for either of these to work.

Once back to the green Interton screen, type bl in the command line. This will list all breakpoints in the output window, and it should say None as they were cleared when the machine was reinitialised. So the breakpoint will need to be set and the program assembled again. You can now repeat all the above, or type h in the command line to see all the commands that can be used in the debugger and perhaps try some out.

One final and powerful feature of the debugger are conditional breakpoints. These enable a breakpoint to be set if a particular condition is met. As an example enter bp 001e r3 eq 1 in the command line and let the program run. It should stop in loopIS when the index register r3 reaches 1. There are many other conditional options including testing memory data or any register including PSU, PSL, SP, CC, RASn. For full details see Help > Manual

## See also[edit | edit source]

Other features of WinArcadia are explored in various tutorials elsewhere in this book:

- Tutorial - Score: PVI monitor, Memory editor

# Objects[edit | edit source]

# Tutorial — Objects[edit | edit source]

  See also: PVI - Objects

The most powerful feature of the PVI are its four reprogrammable objects. Several sources cite this Signetics chip (c.1977) as being the first commercially available hardware implementation of a memory mapped object on a video chip.

## Tutorial code[edit | edit source]

The code for this tutorial can be found at Tutorial code - objects. It generates the static screen shown here. There are four objects, 1,2,3 and 4 in different colours and sizes. There are also duplicates of objects 1, 2 and 4.

This is the section of code that programs these objects. It is taken from the list file created by the WinArcadia assembler:

                                                ;set the colour of the four objects:
    002D  0403    :        lodi,r0 %00000011
    002F  CC1FC1  :        stra,r0 colours12	    ; obj 1 white, 2 red
    0032  0429    :        lodi,r0 %00101001
    0034  CC1FC2  :        stra,r0 colours34	    ; obj 3 green, 4 yellow

                                                ; set the size of the four objects:
    0037  04E4    :        lodi,r0 %11100100
    0039  CC1FC0  :        stra,r0 objectsize

                                                ; load the shape and size of the four objects:
    003C  070E    :        lodi,r3 $0E
                  :loopISe:
    003E  0F4059  :        loda,r0 one,r3-
    0041  CF7F00  :        stra,r0 shape1,r3
    0044  0F6067  :        loda,r0 two,r3
    0047  CF7F10  :        stra,r0 shape2,r3
    004A  0F6075  :        loda,r0 three,r3
    004D  CF7F20  :        stra,r0 shape3,r3
    0050  0F6083  :        loda,r0 four,r3
    0053  CF7F40  :        stra,r0 shape4,r3
    0056  5B66    :        brnr,r3 loopISe

Lets look at each part in turn.

### Colour[edit | edit source]

BLACK | 111
BLUE | 110
GREEN | 101
CYAN | 100
RED | 011
MAGENTA | 010
YELLOW | 001
WHITE | 000

  See also: Tutorial — Programming colours

The pixels within one object are all the same colour. The colour of each object is set by three bits (giving a choice of eight colours) in the PVI registers at $1FC1 and $1FC2.

| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
--- | --- | --- | --- | --- | --- | --- | --- | ---
$1FC1 | X | X | R1 | G1 | B1 | R2 | G2 | B2
$1FC2 | X | X | R3 | G3 | B3 | R4 | G4 | B4

The program sets:
$1FC1 = 00000011 = 00 000 011 = white red
$1FC2 = 00101001 = 00 101 001 = green yellow

### Size[edit | edit source]

Magnification | Size | MSB | LSB
--- | --- | --- | ---
x1 | 8 x 10 | 0 | 0
x2 | 16 x 20 | 0 | 1
x4 | 32 x 40 | 1 | 0
x8 | 64 x 80 | 1 | 1

The size of each object can be set independently to one of four different sizes. Each object can have a height of either 10, 20, 40 or 80 lines, and the widths are scaled similarly to 8, 16, 32 or 64 horizontal clocks. The individual pixels are not square; they are roughly twice as wide as they are tall, an important point to remember when designing shapes.

| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
1FC0 | Size object 4 | Size object 3 | Size object 2 | Size object 1

The program sets:
$1FC0 = 11100100 = 11 10 01 00 = x8 x4 x2 x1

### Object descriptors[edit | edit source]

Object descriptor 1,2,3 or 4
0 | Shape of object, 10 lines of 8 bits
1
2
3
4
5
6
7
8
9
A | Horizontal coordinate of object
B | Horizontal coordinate of duplicate
C | Vertical coordinate of object
D | Vertical offset of duplicate

The shape and position of the four objects are each defined by 14 registers called an object descriptor, comprised, in order, as 10 bytes of shape data, a horizontal coordinate(HC), a horizontal coordinate for the duplicates(HCB), a vertical coordinate(VC) and a vertical offset for the duplicates(VCB).

The code that programs these objects copies blocks of data from tables one, two, three, four to the object descriptors $1F00, $1F10, $1F20 and $1F40. Register R3 is used as an index register to go through a loop 14 times, on each pass setting up one byte in each of the four object descriptors.

#### Shape[edit | edit source]

In the ten-bytes of the object descriptor that defines the shape, and bits set to zero are transparent, while bits set to a one are displayed as the programmed colour.

#### Position[edit | edit source]

The position of any primary object on the screen is set by its corresponding horizontal(HC) and vertical(VC) coordinates with the origin at the top left of the screen. The coordinates can be any eight bit value, but a horizontal coordinate greater than 227 pushes the object off the right hand side of the screen, and vertical coordinate greater than 252 will be off the bottom of the screen.

#### Duplicates[edit | edit source]

The power of these objects comes from the fact that they can be output again further down the screen. And again and again…. In fact it is possible to have up to 80 objects on the screen at one time. These are referred to as duplicates. A duplicated object will always be below its predecessor, and they cannot overlap. The horizontal coordinate of a duplicate is simply the distance from the left edge of the screen, in the same way that the original duplicate is specified. The vertical position of a duplicate is trickier; it is set as a vertical offset from its predecessor.

It is also possible to reprogram the shape, size and colour of an object between duplicates, but that requires some real-time programming which will be discussed in a later tutorial.

Coordinates of the objects and a picture of the screen for comparison

| object 1 | object 2 | object 3 | object 4
--- | --- | --- | --- | ---
HC | 10 | 40 | 60 | 100
HCB | 10 | 35 | 120 | 110
VC | 20 | 60 | 90 | 0
VCB | 20 | 10 | 250 | 255

Object 1 has a horizontal coordinate of 10, as do its duplicates. Its vertical coordinate is 20, and all of its duplicates are offset vertically by 20 from its predecessor.

Object 2’s duplicates are shifted 5 pixels left, and offset vertically by 10.

Object 3 is all on its own. Its duplicate has been pushed off the bottom of the screen by setting the vertical offset to 250.

Object 4’s duplicates are all touching. This is achieved by setting the offset to 255. This may seem a little odd, but if 255 is converted to 8 bit binary and then interpreted as a signed two’s complement number, we get -1. This is because VCB has to be set to “the number of lines -1” that you want to be skipped. If you want a gap of just one line, set VCB to 0 and so on.

### Exercises[edit | edit source]

1. Change the size of an object.
2. Change shape of an object.
3. Change the position of an object.
4. Change the position of an object's duplicates.

# Score[edit | edit source]

# Tutorial — Score[edit | edit source]

$1FC3 | 7 | 6 | 5 | 4 | 3 | 2 | 1 Format | 0 Position
--- | --- | --- | --- | --- | --- | --- | --- | ---
0 |  |  |  |  |  |  | two 2-digit | top
1 |  |  |  |  |  |  | one 4-digit | bottom

  See also: 2636 PVI

Four score digits can be programmed to appear at the top or bottom of the screen. They may be arranged as either one group of four digits, or two groups of two digits. The digits displayed are programmed as BCD (Binary Coded Decimal). If any nibble is set as greater than or equal to $A, the corresponding digit is left blank.

## Tutorial code[edit | edit source]

The code for this tutorial can be found at Tutorial code - score. Once it is assembled and run you should see a screen like the one shown here.

This is the part of the program that sets up the score:

    000F  0403    :        lodi,r0 $03
    0011  CC1FC3  :        stra,r0 scoreformat
    0014  0498    :        lodi,r0 $98
    0016  CC1FC8  :        stra,r0 score12
    0019  0476    :        lodi,r0 $76
    001B  CC1FC9  :        stra,r0 score34

Both bits in scoreformat are set to one, so the score digits appear as a single block of four characters at the bottom of the screen. One way of changing this would be to edit the assembly language file and assemble it. There are faster ways to experiment with this using some tools in WinArcadia.

## WinArcadia tools[edit | edit source]

  See also: WinArcadia and Getting started

One way we can make changes is to directly edit the values in the PVI in real-time. From the Tools menu, open the PVI monitor. The top panel lets you change the registers in the PVI. Change SCORELT from 98 to 2A and the displayed score will change from 9876 to 2 76. Notice how the use of the hex digit A displays as a blank. Now change SCORECTRL from 03 to 00 and see how the score moves to the top of the screen and displays as two, two-digit numbers.

It is also possible to change the machine code that the assembler has created. First though we should run our program again: File > Reset to game and that should return the screen to its original condition.

Refer to the code snippet above, and see how the value $03 that gets put into the scoreformat register appears in the machine code at address $0010. Open the Memory editor from the Tools menu and click on the box containing 03 at $0010. This opens a dialogue box where this byte can be changed. Set it to 0. Notice that the emulator screen has not changed. This is because we have only changed the program and we still have to execute it, which we do once again via File > Reset to game. Now the score digits should be displayed as 98 76 at the top of the screen.

# Programming colours[edit | edit source]

## Tutorial — Programming colours[edit | edit source]

This family of consoles have a very limited palette of colors to work with. The PVI has three digital (i.e. either on or off) colour signals that are connected to the red, green and blue inputs of the PAL encoder. This limits the the system to a palette of eight colours: black, red, green, blue, cyan, magenta, yellow and white.

This seems simple at first sight, but is complicated by two other signals that can affect the colour on the display:

- the OBJ/SCR output of the PVI which goes low whenever an object or a score digit is being displayed. Remember: SCR is score, not screen!
- bit 5 of the 74LS138 'effects' register.

A further complication is that some consoles such as the Interton VC4000 use these signals to alter the brightness of the colour, while others use it to invert the colour. The rest of this page is applicable to the colour inversion method, specifically as implemented on the Voltmace Database.

### Hardware description[edit | edit source]

Block diagram of the colour control circuit in the Voltmace Database video game console.

The three active-high colour inputs to the PAL encoder are connected directly to the active-low colour outputs of the PVI: R, G, B.

| Wikipedia has related information at Wired logic connection, Open collector, Pull-up resistor and Flip-flop (electronics)

The active-high INV input to the PAL encoder is derived from a wire-AND circuit comprising an open-collector transistor, the open-drain OBJ/SCR and a pull-up resistor. Either one of those devices can pull the invert input to a low logic level.

The INVERT signal is set by writing to bit 5 of the hex D-type flip-flop, 74LS378, at memory address $1E80. Writing a 1 to that bit causes the transistor to turn on, and pull the INV input to the PAL encoder to a logic 0 irrespective of the state of OBJ/SCR.

### Registers[edit | edit source]

All the colour information is controlled by four registers, three in the PVI and one logic chip. Note that these registers are all write-only. It is up to the programmer to keep track of what is in them.

#### $1E80 'Effects' register[edit | edit source]

7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
--- | --- | --- | --- | --- | --- | --- | ---
- | - | INVERT | - | - | - | - | -

The other bits in this register are used by the audio circuitry.

#### $1FC1 Colour objects 1 & 2[edit | edit source]

7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
--- | --- | --- | --- | --- | --- | --- | ---
X | X | R1 | G1 | B1 | R2 | G2 | B2

#### $1FC2 Colour objects 3 & 4[edit | edit source]

7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
--- | --- | --- | --- | --- | --- | --- | ---
X | X | R3 | G3 | B3 | R4 | G4 | B4

#### $1FC6 Background and screen — enable and colours[edit | edit source]

7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
--- | --- | --- | --- | --- | --- | --- | ---
X | R | G | B | Background Enable | R | G | B
X | Background colour | Screen colour

If Background Enable is set to zero, both the screen and background grid are output from the PVI as '111'.

If the background grid is not used for graphics, the registers that define it, $1F80 - $1FAC, may be used for variable storage. They are hidden on the display by setting background and screen to the same colour.

### Colours[edit | edit source]

If you find the hardware description hard to follow with the multiple inversions, you aren't alone. All you really need to do is use the table below when programming colours.

COLOUR | OBJECTS | BACKGROUND or SCREEN
INVERT = 0 | INVERT = 1
BLACK | 111 | 000 | 111
BLUE | 110 | 001 | 110
GREEN | 101 | 010 | 101
CYAN | 100 | 011 | 100
RED | 011 | 100 | 011
MAGENTA | 010 | 101 | 010
YELLOW | 001 | 110 | 001
WHITE | 000 | 111 | 000

The score colour cannot be programmed independently; it is always the inverse of the colour programmed for the background.

Neither the object colours or the score colour are affected by the state of bit 5 of the 74LS138 'effects' register since the OBJ/SCR will be low while they are being displayed.

The INVERT bit is used in some games such as Interton's Super Space as a screen-saver. While it is tempting to normally set the INVERT bit to 0, it might be better to have it normally set to 1. In this way the colour codes are the same for objects, score, background and screen, and will be in negative logic, i.e. a 0 turns the colour on.

### Code snippets[edit | edit source]

All the colours are set by these four registers:

    effects     equ $1E80
    colours12   equ $1FC1
    colours34   equ $1FC2
    backgnd     equ $1FC6

It is a good idea to clear the effects register early on in your program. This will not only turn off the colour inversion, but will also turn off the audio.

            eorz    r0
            stra,r0 effects

Alternatively, as discussed above, the audio can be turned off and the colour inversion turned on so that all colours can be programmed with the same codes.

            lodi,r0 $20
            stra,r0 effects

Remember that the objects colours are always active low, so the RGB sequence 101 would appear as green, etc:

            lodi,r0 %00010101       ; XX  /  011     /   110
            stra,r0 colours12       ;     / obj1 red / obj2 blue

The background and screen colours depend on the state of the invert bit. When the invert bit is 0, their colours are always active high, so the RGB sequence 010 would appear as green, etc:

            eorz    r0
            stra,r0 effects         ; invert bit = 0
            lodi,r0 %00001110       ;  X / 100            /   1     / 001
            stra,r0 backgnd         ;    / background red / enabled / screen blue

Conversely, when the invert bit is 1, their colours are active low, so the RGB sequence 101 would appear as green, etc:

            lodi,r0 $20
            stra,r0 effects         ; invert bit = 1
            lodi,r0 %00001110       ;  X / 011            /   1     / 110
            stra,r0 backgnd         ;    / background red / enabled / screen blue

### Tutorial program[edit | edit source]

Demonstration of colour programming on the Voltmace Database games console

The code for this tutorial can be found at Signetics 2650 & 2636 programming/Tutorial code - programming colours. When this program is run, you should see a static screen that looks like this:

The fours objects are programmed in different colours. When the first row of objects have been displayed, the INVERT bit in the effects register is set to one. This inverts the colour of the screen (yellow 110 becomes blue 001) and the background switches from black to white.

Notice that the objects and the score digits are the same above and below this transition. Also note that the score is the inverse of the colour programmed for the background.

#### Exercises[edit | edit source]

1. Change the colours of the four objects to red, cyan, yellow and blue.
2. Change the initial colour of the background grid to cyan.
3. Change the colour of the score to magenta.

# Background grid[edit | edit source]

# Tutorial — Background grid[edit | edit source]

  See also: 2636 PVI: Background grid

Programming the background grid is easy enough once it is understood, but its' design is somewhat convoluted and explaining it and a comprehending it is not easy. Refer back to the link above for the details, then follow along with this tutorial and play with the settings to test you understanding. Visualising what can and cannot be designed with it can be tricky though, so a worksheet is included that can be printed and doodled on.

It is really just an array of dots and vertical lines one pixel wide. The dot is two lines tall and immediately underneath it the vertical line is 18 lines tall. This pair of elements is repeated 16 times across the screen, and 10 times down, and all 320 individual parts can individually be turned on or off.

They can also be extended horizontally to 1, 2 4 or 8 pixels wide. By extending just the dots, a grid can be constructed. It is easy to see how something like a maze could be constructed from this by turning some of the parts off:

This pattern is generated by Tutorial code - background grid. Go ahead and assemble it with WinArcadia. Notice how there are lines sticking out the right side and the bottom. This is a consequence of the structure of the grid. To remove the lines on the right hand side, use the memory editor to change $1F81 from $FF to $FE, and repeat at every fourth byte. To remove the lines at the bottom, change $1FA6 and $1FA7 from $FF to $00. You should now have a nice 9 x 15 array of cells. There is no fix however for the small gaps down the right hand edge.

Now change $1F82 and $1F86 both to $9F, and $1F84 to $5F. You can probably see now how this could easily become a maze or some other similar structure.

So far this has been reasonably straightforward, but the horizontal extensions add significant complexity. The most important things to recall are that extensions apply to whole rows, and that 8x extensions can be applied to each individual row but x2 and x4 extensions are applied to groups of four rows. As a reminder, here is the table showing extension assignments:

Extension registers
Register | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
--- | --- | --- | --- | --- | --- | --- | --- | ---
| Extend group x1,2,4 | Extend row x8
$1FA8 | Rows 1-4 | 4B | 4A | 3 | 2B | 2A | 1
$1FA9 | Rows 5-8 | 8B | 8A | 7 | 6B | 6A | 5
$1FAA | Rows 9-12 | 12B | 12A | 11 | 10B | 10A | 9
$1FAB | Rows 13-16 | 16B | 16A | 15 | 14B | 14A | 13
$1FAC | Rows 17-20 | 20B | 20A | 19 | 18B | 18A | 17

Group extension
Extension | 7 | 6
--- | --- | ---
x1 | 0 | 0
x2 | 0 | 1
x1 | 1 | 0
x4 | 1 | 1

Currently all extension registers are set to $09, which only extends the odd numbered rows (the 2 line bars) to 8 pixels. Change extension register $1FA8 to $0B. This adds a x8 extension to all the active vertical bars in the top half of row 2.

Now change $1FA9 to $C9. This extends all the active bars in the second group to 4 pixels. Likewise, all active bars in the bottom group can extended to 2 pixels by setting register $1FAC to $49. You should now see this in the emulator screen:

## Quiz[edit | edit source]

There was a bug in the program when it was first run and the top part of the grid is corrupted. Can you tell which lines of code had to be added to the tutorial code to fix this?

Reveal the answer
What we are seeing here are the score digits 00 00 in black placed over the background grid. To remove them, the score digits needed to be programmed to $FF and $FF.
            lodi,r0 $FF
            stra,r0 score12
            stra,r0 score34

## Grid design[edit | edit source]

Background worksheet available for download in sizes up to 1,650 × 1,275 pixels suitable for printing.

This worksheet should help with the design and programming of background grids.

Remember these constraints:

- Extensions apply to all cells in a row.
- Extension to eight pixels may be applied individually to any of the 20 rows.
- Extensions to two or four pixels apply to all 6 rows in a group.

Don't forget that the 18-line vertical bars can be extended in two part, top and bottom.

# Sync to VRST[edit | edit source]

So far our programs have largely been static — the PVI has been set up once and left alone. In this tutorial you will learn how to synchronise to the vertical reset signal and change what is on the screen every frame.

## What is VRST?[edit | edit source]

  See also: Console Hardware and Analogue TV

VRST is a signal generated by the sync generator chip. It goes high for about 2.7ms in every 20ms frame of the video signal, and during this time the CRT's electron beam is turned off and is being moved back to point to the top of the screen.

This signal is connected to the Sense input of the microprocessor and its state is indicated by bit 7 of the PSU register. The leading edge of VRST also sets bit 6 (VRLE) of register $1FCB in the PVI, though this is not such a convenient place to check.

VRST is significant for two reasons. First, it gives our program a regular clock tick every 20ms that we can use to determine how fast things are moving on the screen. Second, the PVI requires that we set the vertical coordinate of all the primary objects before the trailing edge of VRST. Related to the latter, we also need to start handling other real-time events related to changing PVI registers during the scan, and that will be discussed in the next chapter.

## Tutorial code[edit | edit source]

The code for this tutorial can be found at Tutorial code - sync to VRST.

These two subroutines are responsible for detecting the state of the VRST signal:

                  :;=================================================================
                  :; subroutine - wait for VRST to clear
                  :Vsync0:
    0058  B480    :        tpsu    sense
    005A  187C    :        bctr,eq Vsync0          ; wait for Sense bit to clear
    005C  17      :        retc,un
                  :;=================================================================
                  :; subroutine - wait for VRST to set
                  :Vsync1:
    005D  B480    :        tpsu    sense           ; wait for Sense bit to be set
    005F  1A7C    :        bctr,lt Vsync1
    0061  17      :        retc,un
                  :;=================================================================

The instruction TPSU tests individual bits in the upper byte of the program status word. The constant sense has been equated to the value $80 by the statement sense equ $80, so we are testing bit 7 of PSU, the sense input of the processor, which in turn is fed by the VRST signal.

In subroutine Vsync0 the program keeps looping while sense is high, or in other words it waits until sense is low. Conversely, subroutine Vsync1 loops while sense is low, or in other words it waits until sense is high.

This is the main loop of the program:

                  :endless:
    002D  3F0058  :        bsta,un Vsync0          ; make sure VRST hasn't started
    0030  3F005D  :        bsta,un Vsync1          ; wait for VRST to start

    0033  0C1F0C  :        loda,r0 vc1		; increment vertical position of object 1
    0036  8401    :        addi,r0 1
    0038  CC1F0C  :        stra,r0 vc1

    003B  0C1F0A  :        loda,r0 hc1		; decrement horizontal position of object 1
    003E  A401    :        subi,r0 1
    0040  CC1F0A  :        stra,r0 hc1

    0043  1B68    :        bctr,un endless

The two subroutines Vsync0 and Vsync1 work together and are effectively waiting for the transition of the VRST signal from low to high. When that happens, the vertical coordinate of object 1 is incremented by one and its horizontal coordinate is decremented by one. The object therefore moves diagonally across the screen, one pixel per 20ms frame and therefore taking about 5 seconds per sweep.

Don't forget that the vertical coordinate must be set up before the end of the VRST period. This is because at that time the value in the register is transferred to a counter which is decremented on every horizontal line until it reaches zero, and then the first line of the object is displayed. So it is a good idea to set the vertical coordinate soon after the start of VRST. The WinArcadia debugger lets us see where this is occurring:

- Turn on the blanking display: Options > VDU > Blanking areas?
- Set a breakpoint at 0033: bp $0033
- Turn on the guide ray: gr
- Step through the program: s

You should see that both coordinates are set during the first few horizontal lines during the vertical blanking period. The X and Y coordinates are also shown in the WInArcadia output window.

It is also important that this is done only once per frame, which is why we sync to the transition of VRST. Without that first subroutine call, Vsync0, the display gets erratic. Try patching it out, either by reassembling the code with a comment at the start of the line, ;bsta,un Vsync0 , or by using the WinArcadia memory editor to replace the code with NOP's, 002D C0 C0 C0.

## Exercises[edit | edit source]

- Alter the amount the coordinate is changed by every frame.

# Sync to Object completion[edit | edit source]

## Tutorial — Sync to object completion[edit | edit source]

The PVI has four primary objects that can be reprogrammed to be displayed further down the screen. The screenshot shown here, created by this tutorial's program, solely uses object 1. The primary object is the white rectangle, the first duplicate the yellow triangle, and the second duplicate is the green triangle. The shape, colour, size and position of each occurrence have to be programmed on every frame. Timing of the programming has to be syncronized to VRST, which was covered in another tutorial, and also to the completion of the display of the video of the object.

## Detecting completion status[edit | edit source]

When the PVI outputs the last line of an object it generates an interrupt and sets the appropriate bit in register $1FCA which is named objectstatus in this program.

Byte | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0
--- | --- | --- | --- | --- | --- | --- | --- | ---
1FCA |

Object/grid collision
1 | 2 | 3 | 4

Object display complete
1 | 2 | 3 | 4

Handling interrupts will be discussed in a later section, and as there is nothing else for the processor to be doing, this program is going to sit in a loop polling the register waiting for the appropriate bit to be set. That is done by subroutine WaitObj:

                  :;=================================================================
                  :;subroutine - wait for object to finish
                  :;     enter with r1=mask for bit to be tested:
                  :;     obj1=$08, obj2=$04, obj3=$02, obj4=$01
                  :WaitObj:
    00B7  0C1FCA  :        loda,r0 objectstatus
    00BA  41      :        andz    r1
    00BB  187A    :        bctr,eq waitobj
    00BD  17      :        retc,un

The program calls this subroutine with r1 = $08 which is used as a mask to test a specific bit in the objectstatus register $1FCA. If the corresponding object complete bit is not set, the result of the andz instruction will be zero, so the program loops back and tries again until the bit is set, the andz operation yields a non-zero result and the subroutine is exited.

## Programming the primary object and its duplicates[edit | edit source]

As soon as VRST goes high the initial state of object 1 is set up. That is done by subroutine Object1A which sets its shape, size, colour (a small white rectangle) and its horizontal and vertical coordinates. It also sets the horizontal coordinate and vertical offset for the first duplicate.

The program then waits for the display of the primary object to complete, at which time subroutine Object1B sets a new shape, size and colour (a yellow triangle). It also sets the vertical offset for the second duplicate.

Finally the program waits for the display of the first duplicate to complete, at which time subroutine Object1C sets a new shape, size, colour (a green triangle) and horizontal coordinate for the second duplicate. It also sets the vertical offset for the third duplicate to a large value to be sure it is off the screen and not displayed.

The program then loops back and waits for the start of the VRST signal.

            bsta,un Vsync0          ; make sure VRST hasn't started
    endless:
            bsta,un Vsync1          ; wait for VRST to start
            bsta,un Object1A        ; set initial state of object 1
            bsta,un Vsync0          ; wait for VRST to end (SEE BELOW)
            lodi,r1 $08
            bsta,un WaitObj         ; wait for primary object 1 to complete
            bsta,un Object1B        ; set first duplicate of object 1
            lodi,r1 $08
            bsta,un WaitObj         ; wait for first duplicate to complete
            bsta,un Object1C        ; set second duplicate of object 1:
            bctr,un endless

The object complete bits in the objectstatus register are reset by the PVI either when the register is read, or on the trailing edge of VRST. The line of code that waits for VRST to end is there to make sure these bits are clear before we begin processing a new frame. If that line is patched out, you will see that the yellow object is no longer displayed.

What is happening is that we don't test for the second duplicate to complete at the end of the frame, so in the next frame the primary object is mistakenly detected as complete during the VRST period. If a test for object 1 to complete is inserted at the end of the loop you will see that the problem is fixed.

However, waiting for the trailing edge of VRST at the start of the loop is a cleaner solution:

- In more complex programs there are likely to be multiple duplicates setting status bits at the end of the frame.
- On the first pass of the loop, we can't be certain of the condition of the status bits.

## Creating shapes, WithCarry, and Rotate[edit | edit source]

Rather than using data tables to define the shape of the three objects in this example, they are created algorithmically.

Creating the rectangle, subroutine Object1A, is a straightforward case of writing $FF to all ten bytes of the shape array.

The triangles are created by writing FF to the bottom row, then 7F, 3F etc.

            lodi,r3 10
            lodi,r0 $FF
            ppsl    withcarry     ; include carry in rotate instructions
    loop1B:
            stra,r0 shape1,r3-    ; triangle shape
            cpsl    carrybit
            rrr,r0                ; shift right with 0 from the carry bit
            brnr,r3 loop1B

This snippet of code starts off by setting the withcarry bit in the program status word. Now, when a rotate instruction is executed, it includes the carry bit in the loop and operates as a nine-bit rotate. By clearing the carry bit before every rotate, it behaves more like a shift register, shifting the 1s out and a 0 in.

## Timing Visualization[edit | edit source]

Changing the screen colour to observe time spent waiting for object completion

Sometimes it is useful to get a visualization of how much time the program is spending simply waiting for completion of an object. This can be achieved quite simply by changing the screen colour while the subroutine WaitObj is executing:

    WaitObj:
    	lodi,r0 $19		; blue
    	stra,r0 backgnd
    wo2
    	loda,r0 ocomplete
    	andz	r1
    	bctr,eq	wo2
    	lodi,r0 $00		; black
    	stra,r0 backgnd
    	retc,un

In the screenshot here, the normally black screen is turned blue while during WaitObj. In this case we generally have lots of time to spare, but there are animations that make some of the blue lines all but disappear. There is still lots of programming to do, and this shows where in the frame there is time to do it.

# Interrupts[edit | edit source]

| Wikipedia has related information at Interrupt

### Interrupt mechanisms[edit | edit source]

Interrupts are a mechanism that allows an external event to signal to the microprocessor that it should stop what is doing and take care of the needs of that event. In these consoles it is only the PVI that does this. The events that cause an interrupt are:

- the leading edge of each vertical reset
- the completion of video generation of an object.

The PVI's interrupt is reset on the trailing edge of the vertical reset signal.

The PVI signals an interrupt request to the processor on its INTREQ pin. If the Interrupt Inhibit bit in the Processor's Program Status Word Upper is not set, the following events take place:

- processor finishes the instruction it was executing
- processor pushes the Instruction Address register on to the Return Address Stack
- processor sets the Interrupt Inhibit bit
- processor signals its acceptance of the request on the INTACK signal
- processor begins to execute a ZBSR (branch to subroutine relative to location zero)
- PVI responds by outputting $03 on the data bus, its in-built interrupt vector and the second byte of the ZBSR instruction
- processor completes the ZBSR instruction by starting execution at address $0003

Notice that the only information the processor has saved automatically is the Return Address. It is up to the programmer to save the Status register and any general purpose registers which the interrupt service routine might use (see below).

The processor can determine why the PVI requested an interrupt by examining the Object Complete bits of register $1FCA and the VRLE bit of register $1FCB. Note that all bits of these registers are cleared when read, and at the trailing edge of VRST.

The interrupt service routine is terminated by a return from subroutine instruction, usually a RETE which enables interrupts once again, or a RETC instruction can be used if it is not desired to enable interrupts.

### Saving and restoring processor status[edit | edit source]

At the end of any interrupt service the registers must be restored to the same condition they were in at the time of the interrupt. For registers r1, r2 and r3, this is usually achieved by switching to the second register bank during the interrupt service routine.

Register r0 and the Program Status Lower are more problematic. The problem arises because on the 2650A the Program Status Word can only be transferred to r0, and not directly to memory. This was fixed on the 2650B with the addition of the LDPL and STPL instructions.

This doesn't work:

            stra,r0 STORER0      ;save r0 in memory
            spsl                 ;r0 = PSL
            stra,r0 STOREPSL     ;save PSL in memory
            ......
            ..the rest of the interrupt service routine goes here
            ......
            loda,r0 STOREPSL
            lpsl                 ;restore PSL
            loda,r0 STORER0      ;restore r0
            rete,un

The problem here is that the last loda instruction affects the condition code bits in the restored PSL.

One clever solution is as follows:

    start_interrupt:
            stra,r0 PRESERVER0   ;PRESERVER0 = r0
            ppsl    $10          ;bank 1
            spsl
            stra,r0 PRESERVEPSL  ;save PSL with bank 1 already selected!

            ..........

    end_interrupt:
            loda,r0 PRESERVEPSL  ;
            strz    r4           ;put a copy of PSL in r4
            lpsl                 ;restores psl (but with bank 1 still selected)
            loda,r0 PRESERVER0   ;restore r0 (but probably changes the condition code)
            andi,r4 $C0          ;this restores the condition code!
            cpsl    $10          ;switch to bank 0
            rete,un              ;

If it isn’t clear how that AND operation works, it’s because the condition code is in bits 7 and 6 of the PSL. Their binary values (00=zero, 01=positive, 10=negative) correspond to the result of the AND $C0 operation:

- 00000000 is zero
- 01000000 is a positive number
- 10000000 is a negative number

### Tutorial code[edit | edit source]

Three different shapes are displayed, all using Object 1

This tutorial's program performs the same task as that in Sync to Object completion but rather than continuously checking the state of VRST or polling the PVI to see if video generation of object 1 has completed, it relies on interrupts from the PVI to make the processor stop what it is doing and take care of whichever event has occurred.

Once an interrupt occurs, and Register0 and the PSL have been saved, the interrupt service routine checks to see if the interrupt was caused by the vertical reset. It does this by testing the VRLE bit in the 'collisions' register, $1FCB. If it is, subroutine Object1A takes care of setting up the initial state of the object. The interrupt routine is then exited and the processor, in this case, sits idle waiting for the next interrupt. When that comes, it must be caused by the completion of object 1. At this point though, it is necessary to determine whether it was the primary object, its first duplicate or its second duplicate that caused the interrupt. The PVI cannot tell us thatNote 1 directly, so a variable Obj1dup has been used to do this. If the variable is 1, then subroutine Object1B is executed; this sets up the first duplicate and sets the variable to 2. On the next interrupt, Object1C is executed, and this sets up the second duplicate and the variable to 0.

This is a very simple program with just one object causing interrupts, and it has only been necessary to determine which duplicate caused the interrupt. When more objects are in use, the interrupt service routine must also determine which object is causing the interrupt. This is done by reading objectstatus at $1FCA. Bits 0-3 are set according to which object has completed.

Care has to be take when using this register since it gets reset when it is read, and it also contains the object/background collision flags. This means that all set bits either need to be acted upon straight away, or saved in a variable to be used later.

Note 1: In some case it might be possible to determine this indirectly by examining the shape or coordinates of the object, though that may not be a very efficient solution.

# BCD arithmetic[edit | edit source]

### Binary Coded Decimal[edit | edit source]

The natural way for a microprocessor to represent numbers and perform arithmetic is in binary format. Humans prefer decimal notation, so when a computer wants to communicate with us, it has to convert from one format to the other. Another option is to force the computer to operate on binary-coded decimals, where each decimal digit, 0-9, is represented by 4-bits. The other six numbers that can be represented by those four bits, A16 - F16 are meaningless, and this makes binary arithmetic more complicated than in a pure binary format. If for example we do the addition 8 + 4 in binary, the result is C16 which is meaningless in BCD. To get the correct answer we must add six, giving 1216. The '1' is carried to the next most significant digit.

The primary use of BCD in these video game consoles is for displaying the score, a number that is typically only incremented, so our discussion here will be confined to addition of BCD numbers. Should your game require subtraction, signed integer arithmetic or division, the Application Memo listed in the references below should help.

### Decimal adjust[edit | edit source]

The 2650 microprocessor has two features that aid BCD arithmetic. In addition to the Carry Flag (C) that registers a carry from bit 7, there is an Inter Digit Flag (IDC) that registers a carry from bit 3. There is also a special instruction, Decimal Adjust Register, DAR, that examines these flags after an arithmetic operation and makes appropriate adjustments to the high nibble, bits 4-7, and the low nibble, bits 0-3. In the case of the most significant nibble, it adds 1010 if C is 0, and for the least significant nibble it adds 1010 if IDC is 0.

### Incrementing the score[edit | edit source]

The program listed below adds an 8-bit number to a 16-bit number and will probably take care of most score-handling needs on these consoles. The MSB of the score is saved in 1F50 and the LSB of the score is in 1F51. Don't forget that the score registers, 1FC8 and 1FC9, are write-only, so the current score cannot be retrieved from them, it must be saved elsewhere.

The WinArcadia debugger can be used to step through this program to see how it works. The incremental value and the initial 16-bit value need to be set using the Memory Editor.

    0000  1F0004  :
    0003  17      :
    0004  7620    :

    0006  7708    :
    0008  7501    :
    000A  0C1F51  :
    000D  0D1F50  :
    0010  0F1F52  :
    0013  8466    :
    0015  8566    :
    0017  7501    :
    0019  83      :
    001A  94      :
    001B  8500    :
    001D  95      :
    001E  CC1F51  :
    0021  CD1F50  :
    0024  1B60    :
                  :
                  :
                  :
                  :
                  :
                  :
                  :
    bcta,un start       ; reset vector
            retc,un             ; interrupt vector
    start:  ppsu    intinhibit  ;inhibit interrupts

    loop:   ppsl    withcarry
            cpsl    carry
            loda,r0 lsbA        ; get the score
            loda,r1 msbA
            loda,r3 inc         ; get the increment
            addi,r0 H'66'
            addi,r1 H'66'
            cpsl    H'01'       ; clear carry
            addz    r3          ; perform BCD addition, scoreX = scoreX + r3
            dar,r0
            addi,r1 H'00'
            dar,r1
            stra,r0 lsbA        ; save result
            stra,r1 msbA
            bctr,un loop

    msbA         equ $1F50
    lsbA         equ $1F51
    inc          equ $1F52
    carry        equ $01
    withcarry    equ $08
    intinhibit   equ $20

### References[edit | edit source]

| Wikipedia has related information at Binary-coded decimal and Half-carry flag

- Signetics 2650 Microprocessor Applications Memo — Fixed Point Decimal Arithmetic Routines

# Indexed branching[edit | edit source]

### BSXA and BXA[edit | edit source]

These two instructions enable indexed branching. BXA is an unconditional indexed branch, while BSXA is an unconditional indexed branch to a subroutine. Register 3 must be specified as the index register.

The example below uses the BSXA instruction to execute one of four subroutines selected by the value in R3. Stepping through this with the WinArcadia debugger is a good way to understand how it works.

    mybyte  equ     $1F0E      ; define a variable
            lodi,r0 $20        ; initialise program status word, just to be sure!
            lpsu               ; inhibit interrupts, stack pointer=0
            lpsl               ; register bank 0, without carry, arithmetic compare
            stra,r0 mybyte
    loop:
            lodi,r3 0            ;go and subtract 1
            bsxa    mysubs,r3
            lodi,r3 9            ;go and multiply by 4
            bsxa    mysubs,r3
            lodi,r3 6            ;go and add 16
            bsxa    mysubs,r3
            lodi,r3 3            ;go and divide by 2
            bsxa    mysubs,r3
            bctr,un loop

    mysubs:
    x0:     bcta,un subtract1
    x3:     bcta,un divide2
    x6:     bcta,un add16
    x9:     bcta,un multiply4

    subtract1:
            loda,r0 mybyte
            subi,r0 1
            stra,r0 mybyte
            retc,un

    divide2:
            loda,r0 mybyte
            rrr,r0
            stra,r0 mybyte
            retc,un

    add16:  loda,r0 mybyte
            addi,r0 16
            stra,r0 mybyte
            retc,un

    multiply4:
            loda,r0 mybyte
            rrl,r0
            rrl,r0
            stra,r0 mybyte
            retc,un

Some points to note:

- The index register must be R3
- The index value goes in steps of three because the bcta instructions are three bytes long.
- If the subroutines were all close enough together, bctr instructions could be used and the index would go in steps of two.
- In some scenarios the table of branch instructions, mysubs, could be omitted altogether but the index values would be rather haphazard and might be difficult to maintain.

### Creating a state machine[edit | edit source]

| Wikipedia has related information at State machine

Indexed branching might typically be used where a 'case statement' would be used in a high-level language. One application of BSXA is to create a state machine to control parts of a program. It might be something global such as controlling which part of the program is running: splash screen, attract mode, select level screen, game play, or game over screen. It might also be used to control the state of certain elements within a program.

The code for this tutorial can be found at 'State machine'. In this program, the code draws two objects, one of them moving along a rectangular path, the other a triangular path. This could be done with code that tests the objects position and determines which way to move it on every frame, but this can quickly get messy. In a state machine model, the object moving in a rectangular pattern is in one four states: moving right, moving down, moving left or moving up. Each state does a check after every move to see if it has reached its endpoint, at which time it changes the state of the machine to the next operation. A variable is used to keep track of the state, and this acts as the index to the appropriate subroutine. In this code, a second state machine asynchronously controls the object moving on the triangular path.

#### Further reading[edit | edit source]

- Nystrom, Robert (November 2014). "State". Game Programming Patterns. Genever Benning. ISBN 0990582906. https://gameprogrammingpatterns.com/state.html. Retrieved 6 January 2022. State machines in game programming with examples in high-level language.
- Finite-State Machines: Theory and Implementation
- Artificial Intelligence 1: Finite State Machines
- Why developers never use state machines Some pitfalls of state-machines (note: they are more formally called finite state machines) and when not to use them.

### Exercise[edit | edit source]

Add a third state machine that controls a blue rectangle moving back-and-forth along a path like a greater-than symbol, >

# Tutorial code[edit | edit source]

## Tutorial code - Introduction[edit | edit source]

This section of the book contains all the code used in the tutorials.

The code below must be appended to every program before it can be assembled. It contains the equate direcitives that assign labels to all the hardware addresses and related constants. This is done both to avoid repetition and also to ensure consistent labelling of registers throughout this book.

#### Hardware definitions[edit | edit source]

This is a standard block of assembler directives that give names to constants and memory addresses of all the registers in the system. Notice that some addresses such as $1F00 have several labels. This allows the programmer to use the one most appropriate to the task being performed as an aid to making readable code. For example, if all the PVI registers are being initialised to 0, use pvi; if setting up object 1 shape and position use 'object1' etc.

    ; HARDWARE DEFINITIONS
    ;   updated 11 Jan 2021
    ;============================================================

    ; PROCESSOR CONSTANTS
    ; -------------------
    carrybit        equ $01
    compare         equ $02
    withcarry       equ $08
    registerselect  equ $10
    intinhibit      equ $20
    stackpointer    equ $07
    sense           equ $80
    flag            equ $40

    ; EFFECTS REGISTER
    ; ----------------
    effects         equ $1e80

    ; BUTTONS
    ; --------
    player1keys147c equ $1E88 ;player1 keypad, bits: 1,4,7,clear,x,x,x,x
    player1keys2580 equ $1E89 ;player1 keypad, bits: 2,5,8,0,x,x,x,x
    player1keys369e equ $1E8A ;player1 keypad, bits: 3,6,9,enter,x,x,x,x
    player2keys147c equ $1E8C ;player2 keypad, bits: 1,4,7,clear,x,x,x,x
    player2keys2580 equ $1E8D ;player2 keypad, bits: 2,5,8,0,x,x,x,x
    player2keys369e equ $1E8E ;player2 keypad, bits: 3,6,9,enter,x,x,x,x
    keymask123      equ $80     ;top row of keys
    keymask456      equ $40
    keymask789      equ $20
    keymaskc0e      equ $10     ;bottom row of keys
    console         equ $1E8B ;start and select buttons on console
    consolestart    equ $40
    consoleselect   equ $80

    ; PVI ADDRESSES AND CONSTANTS
    ; ---------------------------
    pvi          equ $1F00

    object1      equ $1F00
    shape1       equ $1F00
    hc1          equ $1F0A ; hc = Horizontal Coordinate
    hcd1         equ $1F0B ; hcd = Horizontal Coordinate Duplicate
    hcb1         equ $1F0B ; hcb =  ditto  (Signetics datasheet name)
    vc1          equ $1F0C ; vc = Vertical Coordinate
    voff1        equ $1F0D ; voff = Vertical Offset
    vcb1         equ $1F0D ; vcb =  ditto  (Signetics datasheet name)

    object2      equ $1F10
    shape2       equ $1F10
    hc2          equ $1F1A
    hcd2         equ $1F1B
    hcb2         equ $1FCB
    vc2          equ $1F1C
    voff2        equ $1F1D
    vcb2         equ $1F1D

    object3      equ $1F20
    shape3       equ $1F20
    hc3          equ $1F2A
    hcd3         equ $1F2B
    hcb3         equ $1F2B
    vc3          equ $1F2C
    voff3        equ $1F2D
    vcb3         equ $1F2D

    object4      equ $1F40
    shape4       equ $1F40
    hc4          equ $1F4A
    hcd4         equ $1F4B
    hcb4         equ $1F4B
    vc4          equ $1F4C
    voff4        equ $1F4D
    vcb4         equ $1F4D

    grid         equ $1F80     ; background grid
    vbars        equ $1F80     ; vertical bar definitions
    hbars        equ $1FA8     ; horizontal bar extensions

    objectsize   equ $1FC0

    colours12    equ $1FC1     ; colour objects 1,2
    colours34    equ $1FC2     ; colour objects 3,4
    coloursback  equ $1FC6     ; background grid colour / background grid enable / screen colour
    backgnd      equ $1FC6     ;     deprecated

    pitch        equ $1FC7

    scoreformat  equ $1FC3
    score12      equ $1FC8
    score34      equ $1FC9

    objectstatus equ $1FCA     ; object-background collision / object complete
    obj1complete equ $08
    obj2complete equ $04
    obj3complete equ $02
    obj4complete equ $01
    collisions   equ $1FCB     ; VRLE / inter-object collision
    vrle         equ $40
    adpot1       equ $1FCC
    adpot2       equ $1FCD

# Tutorial code - getting started[edit | edit source]

This is the code for the tutorial Getting started

| This code block must be merged with the standard 'Hardware definitions' code before it can be assembled

    reset_vector:
            bcta,un	reset
    interrupt_vector:
            retc,un	        ;just in case an interrupt occurs before we disable them
    reset:
            lodi,r0 $20	;initialise program status word, just to be sure!
            lpsu            ;inhibit interrupts, stack pointer=0
            lpsl            ;register bank 0, without carry, arithmetic compare

            eorz	r0
            stra,r0	effects   ;initialise the 74LS378

            bsta,un InitPVI   ;initialise video chip

    endless:
            bctr,un endless

    ;===================================================================
    ; subroutine - initialise PVI
    InitPVI:
            eorz	r0		;r0 = 0
            lodi,r3	$CA		;set 1F00-1FC9 to 00 (most of PVI)
    loop1:			        ;sets all colours to black, turns off sound, score 1 field at top.
            stra,r0	shape1,r3-
            brnr,r3	loop1

            lodi,r3 $0E
    loopIS:			;load sprite shape and coords
            loda,r0 one,r3-
            stra,r0 shape1,r3
            brnr,r3 loopIS
            lodi,r0 $17
            stra,r0 colours12
            retc,un

    one:
            db	$FF
            db	$81
            db	$81
            db	$81
            db	$81
            db	$81
            db	$81
            db	$81
            db	$81
            db	$FF
            db	100	;hc
            db	100	;hcb
            db	100	;vc
            db	200	;voff

# Tutorial code - objects[edit | edit source]

This is the code for the tutorial Objects

| This code block must be merged with the standard 'Hardware definitions' code before it can be assembled

    reset_vector:
            bcta,un	reset
    interrupt_vector:
            retc,un
    reset:
            lodi,r0 $20	;initialise program status word, just to be sure!
            lpsu            ;inhibit interrupts, stack pointer=0
            lpsl            ;register bank 0, without carry, arithmetic compare

            eorz	r0
            stra,r0	effects		;initialise the 74LS378

            bsta,un InitPVI		;initialise video chip

    endless:
            bctr,un endless

    ;===================================================================
    ; subroutine - initialise PVI
    InitPVI:
            eorz	r0		;r0 = 0
            lodi,r3	$CA		;set 1F00-1FC9 to 00 (most of PVI)
    loop1:				;sets all colours to black, turns off sound, score 1 field at top.
            stra,r0	shape1,r3-
            brnr,r3	loop1

            lodi,r0 $02
            stra,r0 scoreformat
            lodi,r0 $aa		;blank the score digits
            stra,r0 score12
            lodi,r0 $aa
            stra,r0 score34

            lodi,r0 $78		;screen black
            stra,r0 backgnd

            lodi,r0	%00000011
            stra,r0 colours12	; obj 1 white, 2 red
            lodi,r0	%00101001
            stra,r0 colours34	; obj 3 green, 4 yellow

            lodi,r0	%11100100
            stra,r0 objectsize

            lodi,r3 $0E
    loopISe:			;load object descriptors
            loda,r0 one,r3-
            stra,r0 shape1,r3
            loda,r0 two,r3
            stra,r0 shape2,r3
            loda,r0 three,r3
            stra,r0 shape3,r3
            loda,r0 four,r3
            stra,r0 shape4,r3
            brnr,r3 loopISe

            retc,un

    one:
            db	$08
            db	$18
            db	$08
            db	$08
            db	$08
            db	$08
            db	$08
            db	$08
            db	$1c
            db	$1c
            db	10	;hc
            db	10	;hcb
            db	20	;vc
            db	20	;vcb
    two:
            db	$1c
            db	$3e
            db	$22
            db	$02
            db	$0e
            db	$18
            db	$30
            db	$20
            db	$3e
            db	$3e
            db	40
            db	35
            db	60
            db	10
    three:
            db	$7c
            db	$7c
            db	$04
            db	$04
            db	$1c
            db	$1c
            db	$04
            db	$04
            db	$7c
            db	$7c
            db	60
            db	120
            db	90
            db	250
    four:
            db	$40
            db	$40
            db	$40
            db	$40
            db	$48
            db	$7e
            db	$7e
            db	$08
            db	$08
            db	$08
            db	100
            db	110
            db	0
            db	255	;

# Tutorial code - score[edit | edit source]

This is the code for the tutorial Score

| This code block must be merged with the standard 'Hardware definitions' code before it can be assembled

    reset_vector:
            bcta,un	reset
    interrupt_vector:
            retc,un	        ;just in case an interrupt occurs before we disable them
    reset:
            lodi,r0 $20	;initialise program status word, just to be sure!
            lpsu            ;inhibit interrupts, stack pointer=0
            lpsl            ;register bank 0, without carry, arithmetic compare

            eorz	r0
            stra,r0	effects   ;initialise the 74LS378

            bsta,un InitPVI   ;initialise video chip

            lodi,r0 $03
            stra,r0 scoreformat
            lodi,r0 $98
            stra,r0 score12
            lodi,r0 $76
            stra,r0 score34

    endless:
            bctr,un endless

    ;===================================================================
    ; subroutine - initialise PVI
    InitPVI:
            eorz	r0		;r0 = 0
            lodi,r3	$CA		;set 1F00-1FC9 to 00 (most of PVI)
    loop1:			        ;sets all colours to black, turns off sound, score 1 field at top.
            stra,r0	shape1,r3-
            brnr,r3	loop1

            retc,un

# Tutorial code - programming colours[edit | edit source]

This is the code for the tutorial Programming colours

| This code block must be merged with the standard 'Hardware definitions' code before it can be assembled

    ; Tutorial  Colours
    ;=============================================================================
            org     0
    reset_vector:                   ; the microprocessor starts here when the reset button is pressed
            bcta,un reset
            org     3
    interrupt_vector:               ; interrupts shouldn't happen, but we set this just in case
            retc,un
    reset:
                                    ;initialise program status word, just to be sure!
            ppsu    intinhibit      ;inhibit interrupts
            cpsu    stackpointer    ;stack pointer=%000
            cpsl    registerselect  ;register bank 0
            cpsl    withcarry       ;without carry
            cpsl    compare         ;arithmetic compare

            eorz    r0
            stra,r0 effects         ;initialise the 74LS378
            stra,r0 objectsize      ;all objects size 0
            bsta,un DefineObjects   ;define all objects
            bsta,un DefineGrid      ;define the grid
            lodi,r0 $67
            stra,r0 score12
            lodi,r0 $89
            stra,r0 score34
            lodi,r0 %00001110       ;  X / 000              /   1     / 110
            stra,r0 backgnd         ;    / black background / enabled / yellow screen
            bsta,un Vsync0          ; make sure VRST hasn't started
    endless:
            bsta,un Vsync1          ; wait for VRST to start
            eorz    r0
            stra,r0 effects         ; !invert = 0
            stra,r0 scoreformat     ; 2 + 2 score digits at top  (see Tutorial......)
            lodi,r0 %00010101       ; XX  /  010        /   101
            stra,r0 colours12       ;     / obj1 magenta / obj2 green
            lodi,r0 %00111000       ; XX  /  111        /   000
            stra,r0 colours34               ;     / obj 3 black / 4 white
            bsta,un Vsync0          ; wait for VRST to end
            lodi,r1 1
            bsta,un WaitObj         ; wait for object 4 to complete  (see Tutorial......)
            lodi,r0 $20
            stra,r0 effects         ; !invert = 1
            lodi,r0 3
            stra,r0 scoreformat     ; 4 score digits at bottom
            bctr,un endless

    ;===================================================================
    ; subroutine  -  define shapes and position of all objects
                            ; (see Tutorial......)
    DefineObjects:
            lodi,r3 $0A
            lodi,r0 $FF
    loopDS:
            stra,r0 shape1,r3-      ; create rectangular shapes
            stra,r0 shape2,r3
            stra,r0 shape3,r3
            stra,r0 shape4,r3
            brnr,r3 loopDS
            lodi,r0 40              ; set their positions
            stra,r0 hc1
            stra,r0 hcd1
            lodi,r0 60
            stra,r0 hc2
            stra,r0 hcd2
            lodi,r0 80
            stra,r0 hc3
            stra,r0 hcd3
            lodi,r0 100
            stra,r0 hc4
            stra,r0 hcd4
            lodi,r0 88
            stra,r0 vc1
            stra,r0 voff1
            stra,r0 vc2
            stra,r0 voff2
            stra,r0 vc3
            stra,r0 voff3
            stra,r0 vc4
            stra,r0 voff4
            retc,un
    ;=================================================================
    ; subroutine  -  define background grid
                            ; (see Tutorial......)
    DefineGrid:
            lodi,r0 $FF
            stra,r0 $1f80
            stra,r0 $1fa4
            lodi,r0 $FE
            stra,r0 $1f81
            stra,r0 $1fa5

            lodi,r3 $81
    loopDG:
        lodi,r0     $80
            stra,r0 $1f00,r3+
            lodi,r0 $01
            stra,r0 $1f00,r3+
            comi,r3 $a3
            bcfr,eq loopDG
            lodi,r0 $01
            stra,r0 $1fa8
            lodi,r0 $08
            stra,r0 $1fac
            lodi,r0 $00
            stra,r0 $1fa9
            stra,r0 $1faa
            stra,r0 $1fab
            stra,r0 $1fa6
            stra,r0 $1fa7
            retc,un
    ;=================================================================
    ; subroutine - wait for vertical reset to clear
                            ; (see Tutorial......)
    Vsync0:
            tpsu    sense
            bctr,eq Vsync0          ; wait for Sense bit to clear
            retc,un
    ;=================================================================
    ; subroutine - wait for vertical reset to set
    Vsync1:
            tpsu    sense           ; wait for Sense bit to be set
            bctr,lt Vsync1
            retc,un
    ;=================================================================
    ;subroutine - wait for object to finish
                            ; (see Tutorial......)
    ;  enter with r1=mask for bit to be tested:
    ;       obj1=$08, obj2=$04, obj3=$02, obj4=$01
    WaitObj:
            loda,r0 objectstatus
            andz    r1
            bctr,eq waitobj
            retc,un

# Tutorial code - background grid[edit | edit source]

This is the code for the tutorial Background grid

| This code block must be merged with the standard 'Hardware definitions' code before it can be assembled

    reset_vector:
            bcta,un	reset
    interrupt_vector:
            retc,un	        ;just in case an interrupt occurs before we disable them
    reset:
            lodi,r0 $20	;initialise program status word, just to be sure!
            lpsu            ;inhibit interrupts, stack pointer=0
            lpsl            ;register bank 0, without carry, arithmetic compare

            eorz	r0
            stra,r0	effects   ;initialise the 74LS378

            bsta,un InitPVI   ;initialise video chip

    endless:
            bctr,un endless

    ;===================================================================
    ; subroutine - initialise PVI
    InitPVI:
            eorz	r0		;r0 = 0
            lodi,r3	$CA		;set 1F00-1FC9 to 00 (most of PVI)
    loop1:			        ;sets all colours to black, turns off sound, score 1 field at top.
            stra,r0	shape1,r3-
            brnr,r3	loop1

            lodi,r0 $78
            stra,r0 coloursback

            lodi,r0 $FF
            stra,r0 score12
            stra,r0 score34

            lodi,r3 $2D
    loopIS:			;load sprite shape and coords
            loda,r0 one,r3-
            stra,r0 grid,r3
            brnr,r3 loopIS

            retc,un

    one:
            dw	$FFFF	; 1
            dw	$FFFF	; 2
            dw	$FFFF	; 3
            dw	$FFFF	; 4
            dw	$FFFF	; 5
            dw	$FFFF	; 6
            dw	$FFFF	; 7
            dw	$FFFF	; 8
            dw	$FFFF	; 9
            dw	$FFFF	; 10
            dw	$FFFF	; 11
            dw	$FFFF	; 12
            dw	$FFFF	; 13
            dw	$FFFF	; 14
            dw	$FFFF	; 15
            dw	$FFFF	; 16
            dw	$FFFF	; 17
            dw	$FFFF	; 18
            dw	$FFFF	; 19
            dw	$FFFF	; 20

                            ; EXTEND x1,2,4           EXTEND x8
                            ; -------------    -----------------------
            db	$09     ; Rows 1-4	   4B	4A   3	2B  2A	1
            db	$09     ; Rows 5-8	   8B	8A   7	6B  6A	5
            db	$09     ; Rows 9-12	   12B	12A  11	10B 10A	9
            db	$09     ; Rows 13-16	   16B	16A  15	14B 14A	13
            db	$09     ; Rows 17-20	   20B	20A  19	18B 18A	17

# Tutorial code - Sync to VRST[edit | edit source]

This is the code for the tutorial Sync to VRST

| This code block must be merged with the standard 'Hardware definitions' code before it can be assembled

    ; Tutorial SyncVRST
    ;=============================================================================
            org     0
    reset_vector:                   ; the microprocessor starts here when the reset button is pressed
            bcta,un reset
            org     3
    interrupt_vector:               ; interrupts shouldn't happen, but we set this just in case
            retc,un
    reset:
            lodi,r0 $20	        ; initialise program status word, just to be sure!
            lpsu                    ; inhibit interrupts, stack pointer=0
            lpsl                    ; register bank 0, without carry, arithmetic compare

            eorz    r0
            stra,r0 effects         ;initialise the 74LS378
            stra,r0 objectsize      ;all objects size 0
            bsta,un DefineObjects   ;define all objects

            lodi,r0 $AA
            stra,r0 score12
            stra,r0 score34

            eorz    r0
            stra,r0 effects         ; !invert = 0

            lodi,r0 0               ;  X / 000              /   0     / 000
            stra,r0 backgnd         ;    / black background / disabled / yellow screen

            lodi,r0 %00011101       ; XX  /  011        /   101
            stra,r0 colours12       ;     / obj1 red / obj2 green
            lodi,r0 %00110000       ; XX  /  110        /   000
            stra,r0 colours34       ;     / obj 3 blue / 4 white
    endless:
            bsta,un Vsync0          ; make sure VRST hasn't started
            bsta,un Vsync1          ; wait for VRST to start

            loda,r0 vc1		; increment vertical position of object 1
            addi,r0 1
            stra,r0 vc1

            loda,r0 hc1		; decrement horizontal position of object 1
            subi,r0 1
            stra,r0 hc1

            bctr,un endless

    ;===================================================================
    ; subroutine  -  define shapes and position of all objects

    DefineObjects:
            lodi,r3 $0E
            lodi,r0 $FF
    loopDS:
            stra,r0 shape1,r3-      ; create rectangular shapes
            stra,r0 shape2,r3
            stra,r0 shape3,r3
            stra,r0 shape4,r3
            brnr,r3 loopDS

            retc,un

    ;=================================================================
    ; subroutine - wait for VRST to clear
    Vsync0:
            tpsu    sense
            bctr,eq Vsync0          ; wait for Sense bit to clear
            retc,un
    ;=================================================================
    ; subroutine - wait for VRST to set
    Vsync1:
            tpsu    sense           ; wait for Sense bit to be set
            bctr,lt Vsync1
            retc,un
    ;=================================================================

# Tutorial code - Sync to object completion[edit | edit source]

This is the code for the tutorial Sync to Object completion

| This code block must be merged with the standard 'Hardware definitions' code before it can be assembled

    ; Tutorial - Sync to object completion
    ;=============================================================================
            org     0
    reset_vector:                   ; the microprocessor starts here when the reset button is pressed
            bcta,un reset
            org     3
    interrupt_vector:               ; interrupts shouldn't happen, but set this just in case
            retc,un
    reset:
            lodi,r0 $20	        ; initialise program status word
            lpsu                    ; inhibit interrupts, stack pointer=0
            lpsl                    ; register bank 0, without carry, arithmetic compare

            eorz    r0
            stra,r0 effects         ; initialise the 74LS378

            bsta,un DefineUnused    ; push all unused objects offscreen
            lodi,r0 $AA             ; blank the score digits
            stra,r0 score12
            stra,r0 score34
            lodi,r0 %00000000       ;  X / 000              /   0     / 000
            stra,r0 backgnd         ;    / black background / disabled / black screen

            bsta,un Vsync0          ; make sure VRST hasn't started
    endless:
            bsta,un Vsync1          ; wait for VRST to start

            bsta,un Object1A	; set initial state of object 1:
                                    ; shape, colour, size, HC,VC
                                    ; and HCB,VCB for first duplicate, B

            bsta,un Vsync0          ; wait for VRST to end

            lodi,r1 $08
            bsta,un WaitObj         ; wait for object 1 to complete

            bsta,un Object1B	; set first duplicate of object 1:
                                    ; shape, colour, size
    				                ; and VCB for second duplicate, C

            lodi,r1 $08
            bsta,un WaitObj         ; wait for object 1 to complete (first duplicate, B)

            bsta,un Object1C	; set second duplicate of object 1:
                                    ; shape, colour, size, HCB
    				                ; and VCB to push next duplicate offscreen

            bctr,un endless

    ;==================================================================
    ; subroutine  -  Primary object
    ; set initial state of object 1: shape, colour, size, HC,VC
    ; and HCB,VCB for first duplicate, B

    Object1A:
            lodi,r3 10
            lodi,r0 $FF
    loop1A:
            stra,r0 shape1,r3-         ; rectangle shape
            brnr,r3 loop1A

            stra,r3 objectsize      ;  size 0

            lodi,r0 $07             ;  white
            stra,r0 colours12

            lodi,r0 10
            stra,r0 vc1             ; vc = 10
    	rrl,r0
            stra,r0 vcb1            ; vcb = 20
    	rrl,r0
            stra,r0 hc1             ; hc = 40
    	rrl,r0
            stra,r0 hcb1            ; vcb = 80

            retc,un
    ;==================================================================
    ; subroutine  -  First duplicate
    ; set: shape, colour, size
    ; and VCB for second duplicate

    Object1B:
            lodi,r3 10
            lodi,r0 $FF
    	ppsl    withcarry       ; include carry in rotate instructions
    loop1B:
            stra,r0 shape1,r3-      ; triangle shape
            cpsl    carrybit
    	rrr,r0                      ; shift right with 0 from the carry bit
            brnr,r3 loop1B

            lodi,r3 1
            stra,r3 objectsize      ;  size 1

            lodi,r0 $08             ;  yellow
            stra,r0 colours12

            lodi,r0 80
            stra,r0 vcb1            ; vcb = 80

            retc,un
    ;==================================================================
    ; subroutine  -  Second duplicate
    ; set: shape, colour, size, HCB
    ; and VCB to push next duplicate off screen

    Object1C:
            lodi,r3 10
            lodi,r0 $FF
    	ppsl    withcarry       ; include carry in rotate instructions
    loop1C:
            stra,r0 shape1,r3-      ; triangle shape
            cpsl    carrybit
    	rrl,r0                      ; shift left with 0 from the carry bit
            brnr,r3 loop1C

            lodi,r3 2
            stra,r3 objectsize      ;  size 2

            lodi,r0 $28             ;  green
            stra,r0 colours12

            lodi,r0 250
            stra,r0 vcb1            ; make sure there are no more duplicates
            lodi,r0 60
            stra,r0 hcb1            ; hcb = 60

            retc,un

    ;===================================================================
    ; subroutine  -  define position of unused objects

    DefineUnused:
            lodi,r0 254
            stra,r0 vc2      ; push unused objects offscreen
            stra,r0 vc3
            stra,r0 vc4

            lodi,r0 $FF            ; set all objects black
            stra,r0 colours12
            stra,r0 colours34
            retc,un

    ;=================================================================
    ; subroutine - wait for vertical reset to clear
                            ; (see Tutorial......)
    Vsync0:
            tpsu    sense
            bctr,eq Vsync0          ; wait for Sense bit to clear
            retc,un
    ;=================================================================
    ; subroutine - wait for vertical reset to set
    Vsync1:
            tpsu    sense           ; wait for Sense bit to be set
            bctr,lt Vsync1
            retc,un
    ;=================================================================
    ;subroutine - wait for object to finish

    ;  enter with r1=mask for bit to be tested:
    ;       obj1=$08, obj2=$04, obj3=$02, obj4=$01
    WaitObj:
            loda,r0 objectstatus
            andz    r1
            bctr,eq waitobj
            retc,un

# Tutorial code - State machine[edit | edit source]

This is the code for the tutorial Indexed branching - creating a state machine

| This code block must be merged with the standard 'Hardware definitions' code before it can be assembled

    ; Tutorial State machine
    ;=============================================================================
    SquareState     equ    $1F0E    ;variable indicates state of the Square operation
    TriangleState   equ    $1F0F    ;variable indicates state of the Triangle operation
            org     0
    reset_vector:                   ; the microprocessor starts here when the reset button is pressed
            bcta,un reset
            org     3
    interrupt_vector:               ; interrupts shouldn't happen, but we set this just in case
            retc,un
    reset:
            lodi,r0 $20	        ; initialise program status word
            lpsu                    ; inhibit interrupts, stack pointer=0
            lodi,r0 $02
            lpsl                    ; register bank 0, without carry, logical compare

            eorz    r0
            stra,r0 effects         ;initialise the 74LS378
            stra,r0 objectsize      ;all objects size 0
            bsta,un DefineObjects   ;define all objects

            lodi,r0 $AA
            stra,r0 score12
            stra,r0 score34

            eorz    r0
            stra,r0 effects         ; !invert = 0

            lodi,r0 0               ;  X / 000              /   0     / 000
            stra,r0 backgnd         ;    / black background / disabled / yellow screen

            lodi,r0 %00011101       ; XX  /  011        /   101
            stra,r0 colours12       ;     / obj1 red / obj2 green

            lodi,r0 40              ;set initial position of two objects
            stra,r0 hc1
            stra,r0 hc2
            stra,r0 vc1
            stra,r0 vc2

            lodi,r0 0
            stra,r0 SquareState
            lodi,r0 0
            stra,r0 TriangleState

    endless:
            bsta,un Vsync0          ; make sure VRST hasn't started
            bsta,un Vsync1          ; wait for VRST to start
            loda,r3 SquareState     ; go and make one movement in the Square state-machine
            bsxa    squareLUT,r3
            loda,r3 TriangleState   ; go and make one movement in the Triangle state-machine
            bsxa    triangleLUT,r3
            bctr,un endless         ; repeat endlessly

    ;===========================================================================================
    ; Define the Square state-machine
    squareLUT:                       ; look up table connecting index to subroutine
            bcta,un squaretop            ; 0
            bcta,un squareright          ; 3
            bcta,un squarebottom         ; 6
            bcta,un squareleft           ; 9

    squaretop:
            loda,r0 hc1		        ; move object 1 right
            addi,r0 1
            stra,r0 hc1
            comi,r0 150
            retc,lt                 ; if reached endpoint
            lodi,r0 3
            stra,r0 SquareState     ;   change state to move down
            retc,un

    squareright:
            loda,r0 vc1		        ; move object 1 down
            addi,r0 1
            stra,r0 vc1
            comi,r0 120
            retc,lt                 ; if reached endpoint
            lodi,r0 6
            stra,r0 SquareState     ;   change state to move left
            retc,un

    squarebottom:
            loda,r0 hc1		        ; move object 1 left
            subi,r0 1
            stra,r0 hc1
            comi,r0 40
            retc,gt                 ; if reached endpoint
            lodi,r0 9
            stra,r0 SquareState     ;   change state to move up
            retc,un

    squareleft:
            loda,r0 vc1		        ; move object 1 up
            subi,r0 1
            stra,r0 vc1
            comi,r0 40
            retc,gt                 ; if reached endpoint
            lodi,r0 0
            stra,r0 SquareState     ;   change state to move right
            retc,un

    ;===========================================================================================
    ; Define the Triangle state-machine
    triangleLUT:
            bcta,un triangleleft         ; 0
            bcta,un trianglebottom       ; 3
            bcta,un trianglehypo         ; 6

    triangleleft:
            loda,r0 vc2		; move object 2 down
            addi,r0 1
            stra,r0 vc2
            comi,r0 140
            retc,lt                 ; if reached endpoint
            lodi,r0 3
            stra,r0 TriangleState   ;   change state
            retc,un

    trianglebottom:
            loda,r0 hc2		; move object 2 right
            addi,r0 1
            stra,r0 hc2
            comi,r0 140
            retc,lt                 ; if reached endpoint
            lodi,r0 6
            stra,r0 TriangleState   ;   change state
            retc,un

    trianglehypo:
            loda,r0 hc2		; move object 2 left
            subi,r0 1
            stra,r0 hc2
            loda,r0 vc2		; move object 2 up
            subi,r0 1
            stra,r0 vc2
            comi,r0 40
            retc,gt                 ; if reached endpoint
            lodi,r0 0
            stra,r0 TriangleState   ;   change state
            retc,un

    ;===================================================================
    ; subroutine  -  define shapes and position of all objects

    DefineObjects:
            lodi,r3 $0E
            lodi,r0 $FF
    loopDS:
            stra,r0 shape1,r3-      ; create rectangular shapes
            stra,r0 shape2,r3
            stra,r0 shape3,r3
            stra,r0 shape4,r3
            brnr,r3 loopDS

            retc,un

    ;=================================================================
    ; subroutine - wait for VRST to clear
    Vsync0:
            tpsu    sense
            bctr,eq Vsync0          ; wait for Sense bit to clear
            retc,un
    ;=================================================================
    ; subroutine - wait for VRST to set
    Vsync1:
            tpsu    sense           ; wait for Sense bit to be set
            bctr,lt Vsync1
            retc,un

# Tutorial code - Interrupts[edit | edit source]

This is the code for the tutorial Interrupts

| This code block must be merged with the standard 'Hardware definitions' code before it can be assembled

    ; Tutorial - Interrupts
    ;=============================================================================
    Reg0      equ   $1F0E           ;save r0 here during interrupts
    StatusL   equ   $1F0F           ;save the program status word lower here during interrupts
    Obj1dup   equ   $1F1E           ;tracks which duplicates have been output

            org     0
    reset_vector:                   ; the microprocessor starts here when the reset button is pressed
            bcta,un reset

            org     3
    interrupt_vector:
                                 ;Save state of processor when interrupt occured:
            stra,r0 Reg0         ;preserve r0
            ppsl    $10          ;bank 1,  logical compare
            spsl
            stra,r0 StatusL      ;save PSL with bank 1 already selected!

            loda,r0 collisions
            tmi,r0  vrle         ;case
            bcfr,eq IVdup1       ;    interrupt caused by VRLE
            bsta,un Object1A     ;        set initial state of object 1
            bcta,un IVend        ;        quit interrupt routine

    IVdup1:                      ;
            loda,r0 Obj1dup      ;
            comi,r0 1            ;    next duplicate is the first
            bcfr,eq IVdup2
            bsta,un Object1B     ;        set first duplicate of object 1
            bcta,un IVend        ;        quit interrupt routine

    IVdup2:                      ;
            loda,r0 Obj1dup      ;
            comi,r0 2            ;    next duplicate is the second
            bcfr,eq IVend
            bsta,un Object1C     ;        set second duplicate of object 1

    IVend:                       ;Restore processor to state it was in before interrupt:
            loda,r0 StatusL
            strz    r4           ;put a copy of PSL in r4
            lpsl                 ;restores psl (but with bank 1 still selected)
            loda,r0 Reg0         ;restore r0 (but probably changes the condition code)
            andi,r4 $C0          ;this restores the condition code!
            cpsl    $10          ;switch to bank 0
            rete,un              ;exit interrupt routine

    ;======================================================================================
    reset:
            lodi,r0 intinhibit      ; initialise program status word
            lpsu                    ;   inhibit interrupts, stack pointer=0
            lpsl                    ;   register bank 0, without carry, arithmetic compare
            eorz    r0
            stra,r0 effects         ; initialise the 74LS378
            bsta,un DefineUnused    ; push all unused objects offscreen
            lodi,r0 $AA             ; blank the score digits
            stra,r0 score12
            stra,r0 score34
            lodi,r0 %00000000       ;  X / 000              /   0     / 000
            stra,r0 backgnd         ;    / black background / disabled / black screen
            spsl    compare         ; logical compares
            cpsu    intinhibit      ; enable interrupts
    endless:
            nop                     ; this is the main program loop
            nop                     ; the processor is free to perform other tasks
            nop                     ; in between servicing PVI interrupts
            bctr,un endless

    ;==================================================================
    ; subroutine  -  Primary object
    ; set initial state of object 1: shape, colour, size, HC,VC
    ; and HCB,VCB for first duplicate, B

    Object1A:
            lodi,r3 10
            lodi,r0 $FF
    loop1A:
            stra,r0 shape1,r3-      ; rectangle shape
            brnr,r3 loop1A

            stra,r3 objectsize      ;  size 0

            lodi,r0 $07             ;  white
            stra,r0 colours12

            lodi,r0 10
            stra,r0 vc1             ; vc = 10
            rrl,r0
            stra,r0 vcb1            ; vcb = 20
            rrl,r0
            stra,r0 hc1             ; hc = 40
            rrl,r0
            stra,r0 hcb1            ; vcb = 80
            lodi,r0 1
            stra,r0 Obj1dup         ;next to draw is the first duplicate

            retc,un
    ;==================================================================
    ; subroutine  -  First duplicate
    ; set: shape, colour, size
    ; and VCB for second duplicate

    Object1B:
            lodi,r3 10
            lodi,r0 $FF
            ppsl    withcarry       ; include carry in rotate instructions
    loop1B:
            stra,r0 shape1,r3-      ; triangle shape
            cpsl    carrybit
            rrr,r0                  ; shift right with 0 from the carry bit
            brnr,r3 loop1B

            lodi,r3 1
            stra,r3 objectsize      ;  size 1

            lodi,r0 $08             ;  yellow
            stra,r0 colours12

            lodi,r0 80
            stra,r0 vcb1            ; vcb = 80
            lodi,r0 2
            stra,r0 Obj1dup         ;next to draw is the second duplicate

            retc,un
    ;==================================================================
    ; subroutine  -  Second duplicate
    ; set: shape, colour, size, HCB
    ; and VCB to push next duplicate off screen

    Object1C:
            lodi,r3 10
            lodi,r0 $FF
            ppsl    withcarry       ; include carry in rotate instructions
    loop1C:
            stra,r0 shape1,r3-      ; triangle shape
            cpsl    carrybit
            rrl,r0                  ; shift left with 0 from the carry bit
            brnr,r3 loop1C

            lodi,r3 2
            stra,r3 objectsize      ;  size 2

            lodi,r0 $28             ;  green
            stra,r0 colours12

            lodi,r0 250
            stra,r0 vcb1            ; make sure there are no more duplicates
            lodi,r0 60
            stra,r0 hcb1            ; hcb = 60
            lodi,r0 0
            stra,r0 Obj1dup         ;next to draw is the primary object
            retc,un

    ;===================================================================
    ; subroutine  -  define position of unused objects

    DefineUnused:
            lodi,r0 254
            stra,r0 vc2         ; push unused objects offscreen
            stra,r0 vc3
            stra,r0 vc4

            lodi,r0 $FF         ; set all objects black
            stra,r0 colours12
            stra,r0 colours34
            retc,un

# Bibliography[edit | edit source]

### Datasheets[edit | edit source]

- Phillips 2650 Series Datasheet. [1]

- Signetics 2650 User Manual. [2]

- Signetics 2650 Overview. [3]

- Signetics Programmable Video Interface 2636. [4]

### Commentary on 2650[edit | edit source]

- An introduction to microcomputers, vol 2, chap 11, The Signetics 2650A. Adam Osborne. [5]

- Signetics 2650: An IBM on a Chip, The CPUshack museum. [6]

### Other resources[edit | edit source]

- Emerson Arcadia 2001 Central, supporting all Signetics 2650-based machines, (Interton VC 4000, Elektor TV Games Computer, et al.) [7]

# Analogue TV[edit | edit source]

These consoles were manufactured in the days of analogue TV, when the picture was drawn on a cathode-ray tube by an electron beam scanning back and forth across the screen. The programmer doesn't need to know all the details of how these TVs worked, but should be familiar with the basic principle of operation, some of the terminology, and the speed at which the beam crosses the screen. The purpose of this section is to provide an overview and suggest some further reading.

### Raster scan[edit | edit source]

Electron-beam path

The electron beam draws one horizontal scan line at a time, starting at the top left of the screen. By the time it reaches the bottom right of the screen a whole picture has been displayed. A long-persistence phosphor coating on the screen retains the image long enough that the human eye does not perceive any flicker.

While the beam is moving left to right the screen is being lit up with the three primary colours, red, green and blue. At the end of each scanline it is moved back, right to left. This movement is faster than in the other direction, and no colours are displayed; this is referred to variously as the horizontal retrace, horizontal flyback or horizontal blanking. In a similar manner, at the end of each frame the position of the beam has to be moved back to the top left of the screen without displaying any colours; this is referred to variously as the vertical retrace, vertical flyback, vertical reset or vertical blanking. The PVI documentation refers to it simply as VRST

### PAL and SECAM[edit | edit source]

Colour television was broadcast in three different standards in various parts of the world, NTSC, PAL, or SECAM. As far as is known, the 2636 PVI was never used in any consoles for the North American NTSC system. Most were PAL and those made in France were presumably SECAM. Fortunately these two standards vary only in the way the colour is encoded in the broadcast signal, and both use the same number of scan lines with the same timings. This means that most of the electronics, and more importantly the firmware, will be the same. The only things that need to change are the video encoder and possibly the modulator. PAL and SECAM both fall under a standard known as either 625 lines or 576i. The number 576 comes about because 49 of the 625 lines are not visible during the vertical blanking period.

### Interlaced TV pictures[edit | edit source]

Animation of an interlaced TV.

The concept of interlaced video also needs to be addressed here as it explains the difference between the 625 lines of a standard tv signal and the much smaller number of lines we can specify with an eight-bit register. In an interlaced tv picture every other line is output first, then when the beam sweeps down across the screen again it displays the lines in between. This method was adopted to help reduce flicker. Each vertical pass is known as a field, and it takes two fields to make a frame. In this way a lot more detail can be displayed in the picture.

### Non-interlaced console picture[edit | edit source]

These video game consoles generate a non-interlaced signal. Each vertical pass of the screen is a complete frame. Each scanline takes 64μs and each frame has 312 scanlines. Each frame takes a total of 20ms, equivalent to 50Hz. Forty three scanlines are blanked during the vertical retrace, leaving 269 potentially visible.

### Further reading[edit | edit source]

Wikipedia has numerous articles related to this subject should the reader what to dig deeper into this subject.

- Analog television
- Cathode-ray tube
- Raster scan
- Interlaced video
- Frame rate
- 576i
- PAL
- SECAM

# Development systems[edit | edit source]

| Choice of system Examples in this book will use the WinArcadia emulator because it is freely available and requires no additional hardware other than a Windows based PC.

This chapter takes a brief look at the options available for developing new software for these consoles.

### WinArcadia[edit | edit source]

This screenshot was created with WinArcadia.

  For more details on using WinArcadia, see Getting started.

WinArcadia (for Windows) and AmiArcadia (for Amiga) are the most developed systems for programming these consoles. They are emulators for a range of 2650-based consoles, computers and arcade machines, with capability for disassembly, assembly and debugging. The supported languages are currently English, Dutch, French, German, Greek, Italian, Polish, Russian and Spanish. They are built and maintained by James Jacobs in Australia, and can be downloaded from Emerson Arcadia 2001 Central at amigan.yatho.com

### Pi2650[edit | edit source]

A Raspberry Pi 400 computer.

This is currently under development by Derek Andrews in Canada. It is an IDE that runs on a Raspberry Pi 400 connected to a console via an interface board. The IDE includes an editor and assembler. It dumps its binary output to a dual-port RAM on the interface board. It enables developers to test their code on a real console.

# WinArcadia[edit | edit source]

WinArcadia is a multi-emulator, assembler, disassembler and debugger for a large range of 2650-based video game consoles, arcade machines and computers.[1] It has many features and we will limit our discussion to the basics that will enable Interton/APVS programmers to get started using it. The built in Help menu is equally extensive.[2] Further examples of its use will be shown in the Tutorials, particularly with reference to the debugger.

## Download and install[edit | edit source]

WinArcadia can be downloaded from https://amigan.yatho.com/#software. It should be unpacked from its RAR format and the files and folders moved to a folder of your own naming. Here it shown in a desktop folder WinArcadia. From here the .exe file can be pinned to the taskbar or a shortcut can be placed on the desktop. You should also make a new folder in this directory called Projects. This sub-directory is where the assembler will look for files to assemble.

## Set up[edit | edit source]

The Options menu leads to a few items that need to be set up. There are many others to explore, but these should be sufficient to get started:

The Machine option leads to a list of all the machines that WinArcadia can emulate. For our purposes select Interton VC 4000.

The Left and Right controller options allow you to select between mouse/trackball, gamepad or keyboard. If the keyboard is chosen, the Input and Redefine keys will show which keys are mapped to certain functions and allows them to be changed if desired.

## Play[edit | edit source]

Interton's "Super Space" on the WinArcadia emulator

Dumped ROMs of most, if not all, known cartridges are available for download at Emerson Arcadia 2001 Central, amigan.yatho.com. Look in the Packs section and download the Games link. This has games for many machines, so look for the Interton folder. This has the binaries of all the cartridges, and they can be run on WinArcadia using the File, Open menu option.

For information about each game, and links to manuals where they are available, use the Help > Gaming Guide....

## Assemble[edit | edit source]

  Further information: Getting started

Using WinArcadia's assembler

To assemble a program, it should be saved in the Projects file with the .asm extension. Simply type asm filename in the command line at the bottom of the WinArcadia window. A new window will open and tell you if the assembly was successful of if there were errors. In the screenshot shown here, the tutorial Programming Colours has been successfully assembled and the program automatically run.

## Debug[edit | edit source]

  Further information: Getting started

The PVI monitor in WinArcadia lets the programmer examine the contents of the 2636's registers.
The CPU monitor in WinArcadia lets the programmer examine the state of the 2650 processor.

The WinArcadia debugger allows the programmer to track the execution of their program and examine the state of variables, processor status, and PVI registers. Some of the more common commands are listed here, but a full list and instructions can be found within WinArcadia (Help > Manual > Debugger).

- Pause / Unpause
- View CPU / PVI / RAM
- Set / Clear Breakpoints and Watchpoints, which may be conditional
- Step next instruction
- Trace execution
- Run to:
  - end of loop
  - end of subroutine
  - next interrupt
  - next rasterline
  - next frame
- Step over subroutine
- Examine memory addresses
- Change memory addresses
- Generate an interrupt

A breakpoint is triggered when the Instruction Address Register (program counter) reaches the breakpoint address. A watchpoint is triggered when the specified address is accessed.

Typical output from the WinArcadia debugger as it steps through the execution of three instructions.

The screenshot here shows the output of the debugger as it steps through three instructions as indicated by >s in white. For each instruction, the following information is shown:

- the instruction, clock cycle count, the number of clock cycles the instruction will take to run, the number of bytes in the instruction, the instruction address and the code byte(s).
- a description of the instruction
- the state of the processor upon completion:
  - the new clock cycle count, value of registers R0, R1, R2, R3 and PSU
  - X and Y coordinates currently being output to the screen (negative values of X mean the horizontal blanking period), registers R4, R5, R6 (the second bank of R1, R2, R3) and PSL
  - the eight values stored in the Return Address Stack, and the Instruction Address Register.
- information about the next instruction

In the last instruction shown here, an unconditional return from subroutine, notice how the third item in the Return Address Stack is transferred to the Instruction Address Register, and the Stack Pointer (SP in the PSU) is decremented.

## Settings[edit | edit source]

There is a multitude of settings in WinArcadia and some may cause the emulator to behave in unexpected ways. Here is a list of some of them and their effect.

Menu | Option | Effect
--- | --- | ---
Options > Left Controller | Gamepad / Keyboard | Determines which input device will be used
Options > Colours | Darken backgrounds | White become grey etc
Options > Colours | Pure colours | Some colours are much brighter than the true PVI colours
Options > Sound | Enabled? | No sound?

## References[edit | edit source]

1. ↑ "AmiArcadia/WinArcadia". https://amigan.yatho.com/wa.txt. Retrieved 8 December 2021.
2. ↑ Jacobs, James (5 Dec 2021), AmiArcadia and WinArcadia, WinArcadia 28.31, Amigan Software

# PVI audio frequency chart[edit | edit source]

## Frequency table[edit | edit source]

The chart below shows the audio frequency generated by the PVI when the value n is programmed into $1FC7.

| period | cycles per frame | frequency | A4=440Hz[1] | error
--- | --- | --- | --- | --- | ---
n | 128(n+1)μs |  | Hz | note/octave | ideal f | diff, Hz | diff, %
0 | OFF |  |  |  |  |  |
1 | 256 | 78.13 | 3906.25 | B7 | 3951.07 | 44.82 | 1.13
2 | 384 | 52.08 | 2604.17 | E7 | 2637.02 | 32.85 | 1.25
3 | 512 | 39.06 | 1953.13 | B6 | 1975.53 | 22.41 | 1.13
4 | 640 | 31.25 | 1562.50 | G6 | 1661.22 | 98.72 | 5.94
5 | 768 | 26.04 | 1302.08 | E6 | 1318.51 | 16.43 | 1.25
6 | 896 | 22.32 | 1116.07 | C♯6 | 1108.73 | -7.34 | -0.66
7 | 1024 | 19.53 | 976.56 | B5 | 987.77 | 11.21 | 1.13
8 | 1152 | 17.36 | 868.06 | A5 | 880.00 | 11.94 | 1.36
9 | 1280 | 15.63 | 781.25 | G5 | 783.99 | 2.74 | 0.35
10 | 1408 | 14.20 | 710.23 | F5 | 698.46 | -11.77 | -1.68
11 | 1536 | 13.02 | 651.04 | E5 | 659.25 | 8.21 | 1.25
12 | 1664 | 12.02 | 600.96 | D5 | 587.33 | -13.63 | -2.32
13 | 1792 | 11.16 | 558.04 | C♯5 | 554.37 | -3.67 | -0.66
14 | 1920 | 10.42 | 520.83 | C5 | 523.25 | 2.42 | 0.46
15 | 2048 | 9.77 | 488.28 | B4 | 493.88 | 5.60 | 1.13
16 | 2176 | 9.19 | 459.56 | A♯4 | 466.16 | 6.60 | 1.42
17 | 2304 | 8.68 | 434.03 | A4 | 440.00 | 5.97 | 1.36
18 | 2432 | 8.22 | 411.18 | G♯4 | 415.30 | 4.12 | 0.99
19 | 2560 | 7.81 | 390.63 | G4 | 392.00 | 1.37 | 0.35
20 | 2688 | 7.44 | 372.02 | F♯4 | 369.99 | -2.03 | -0.55
21 | 2816 | 7.10 | 355.11 | F4 | 349.23 | -5.88 | -1.68
22 | 2944 | 6.79 | 339.67 |  |  |  |
23 | 3072 | 6.51 | 325.52 | E4 | 329.63 | 4.11 | 1.25
24 | 3200 | 6.25 | 312.50 | D♯4 | 311.13 | -1.37 | -0.44
25 | 3328 | 6.01 | 300.48 |  |  |  |
26 | 3456 | 5.79 | 289.35 | D4 | 293.66 | 4.31 | 1.47
27 | 3584 | 5.58 | 279.02 | C♯4 | 277.18 | -1.84 | -0.66
28 | 3712 | 5.39 | 269.40 |  |  |  |
29 | 3840 | 5.21 | 260.42 | C4 | 261.63 | 1.21 | 0.46
30 | 3968 | 5.04 | 252.02 |  |  |  |
31 | 4096 | 4.88 | 244.14 | B3 | 246.94 | 2.80 | 1.13
32 | 4224 | 4.73 | 236.74 |  |  |  |
33 | 4352 | 4.60 | 229.78 | A♯3 | 233.08 | 3.30 | 1.42
34 | 4480 | 4.46 | 223.21 |  |  |  |
35 | 4608 | 4.34 | 217.01 | A3 | 220.00 | 2.99 | 1.36
36 | 4736 | 4.22 | 211.15 |  |  |  |
37 | 4864 | 4.11 | 205.59 | G♯3 | 207.65 | 2.06 | 0.99
38 | 4992 | 4.01 | 200.32 |  |  |  |
39 | 5120 | 3.91 | 195.31 | G3 | 196.00 | 0.69 | 0.35
40 | 5248 | 3.81 | 190.55 |  |  |  |
41 | 5376 | 3.72 | 186.01 | F♯3 | 185.00 | -1.01 | -0.55
42 | 5504 | 3.63 | 181.69 |  |  |  |
43 | 5632 | 3.55 | 177.56 |  |  |  |
44 | 5760 | 3.47 | 173.61 | F3 | 174.61 | 1.00 | 0.57
45 | 5888 | 3.40 | 169.84 |  |  |  |
46 | 6016 | 3.32 | 166.22 | E3 | 164.81 | -1.41 | -0.86
47 | 6144 | 3.26 | 162.76 |  |  |  |
48 | 6272 | 3.19 | 159.44 |  |  |  |
49 | 6400 | 3.13 | 156.25 | D♯3 | 155.56 | -0.69 | -0.44
50 | 6528 | 3.06 | 153.19 |  |  |  |
51 | 6656 | 3.00 | 150.24 |  |  |  |
52 | 6784 | 2.95 | 147.41 | D3 | 146.83 | -0.58 | -0.39
53 | 6912 | 2.89 | 144.68 |  |  |  |
54 | 7040 | 2.84 | 142.05 |  |  |  |
55 | 7168 | 2.79 | 139.51 | C♯3 | 138.59 | -0.92 | -0.66
56 | 7296 | 2.74 | 137.06 |  |  |  |
57 | 7424 | 2.69 | 134.70 |  |  |  |
58 | 7552 | 2.65 | 132.42 |  |  |  |
59 | 7680 | 2.60 | 130.21 | C3 | 130.81 | 0.60 | 0.46
60 | 7808 | 2.56 | 128.07 |  |  |  |
61 | 7936 | 2.52 | 126.01 |  |  |  |
62 | 8064 | 2.48 | 124.01 |  |  |  |
63 | 8192 | 2.44 | 122.07 | B2 | 123.47 | 1.40 | 1.13
64 | 8320 | 2.40 | 120.19 |  |  |  |
65 | 8448 | 2.37 | 118.37 |  |  |  |
66 | 8576 | 2.33 | 116.60 | A♯2 | 116.54 | -0.06 | -0.06
67 | 8704 | 2.30 | 114.89 |  |  |  |
68 | 8832 | 2.26 | 113.22 |  |  |  |
69 | 8960 | 2.23 | 111.61 |  |  |  |
70 | 9088 | 2.20 | 110.04 | A2 | 110.00 | -0.04 | -0.03
71 | 9216 | 2.17 | 108.51 |  |  |  |
72 | 9344 | 2.14 | 107.02 |  |  |  |
73 | 9472 | 2.11 | 105.57 |  |  |  |
74 | 9600 | 2.08 | 104.17 | G♯2 | 103.83 | -0.34 | -0.32
75 | 9728 | 2.06 | 102.80 |  |  |  |
76 | 9856 | 2.03 | 101.46 |  |  |  |
77 | 9984 | 2.00 | 100.16 |  |  |  |
78 | 10112 | 1.98 | 98.89 |  |  |  |
79 | 10240 | 1.95 | 97.66 | G2 | 98.00 | 0.34 | 0.35
80 | 10368 | 1.93 | 96.45 |  |  |  |
81 | 10496 | 1.91 | 95.27 |  |  |  |
82 | 10624 | 1.88 | 94.13 |  |  |  |
83 | 10752 | 1.86 | 93.01 | F♯2 | 92.50 | -0.51 | -0.55
84 | 10880 | 1.84 | 91.91 |  |  |  |
85 | 11008 | 1.82 | 90.84 |  |  |  |
86 | 11136 | 1.80 | 89.80 |  |  |  |
87 | 11264 | 1.78 | 88.78 |  |  |  |
88 | 11392 | 1.76 | 87.78 | F2 | 87.31 | -0.47 | -0.54
89 | 11520 | 1.74 | 86.81 |  |  |  |
90 | 11648 | 1.72 | 85.85 |  |  |  |
91 | 11776 | 1.70 | 84.92 |  |  |  |
92 | 11904 | 1.68 | 84.01 |  |  |  |
93 | 12032 | 1.66 | 83.11 |  |  |  |
94 | 12160 | 1.64 | 82.24 | E2 | 82.41 | 0.17 | 0.21
95 | 12288 | 1.63 | 81.38 |  |  |  |
96 | 12416 | 1.61 | 80.54 |  |  |  |
97 | 12544 | 1.59 | 79.72 |  |  |  |
98 | 12672 | 1.58 | 78.91 |  |  |  |
99 | 12800 | 1.56 | 78.13 |  |  |  |
100 | 12928 | 1.55 | 77.35 | D♯2 | 77.78 | 0.43 | 0.55
101 | 13056 | 1.53 | 76.59 |  |  |  |
102 | 13184 | 1.52 | 75.85 |  |  |  |
103 | 13312 | 1.50 | 75.12 |  |  |  |
104 | 13440 | 1.49 | 74.40 |  |  |  |
105 | 13568 | 1.47 | 73.70 | D2 | 73.42 | -0.28 | -0.39
106 | 13696 | 1.46 | 73.01 |  |  |  |
107 | 13824 | 1.45 | 72.34 |  |  |  |
108 | 13952 | 1.43 | 71.67 |  |  |  |
109 | 14080 | 1.42 | 71.02 |  |  |  |
110 | 14208 | 1.41 | 70.38 |  |  |  |
111 | 14336 | 1.40 | 69.75 |  |  |  |
112 | 14464 | 1.38 | 69.14 | C♯2 | 69.30 | 0.16 | 0.23
113 | 14592 | 1.37 | 68.53 |  |  |  |
114 | 14720 | 1.36 | 67.93 |  |  |  |
115 | 14848 | 1.35 | 67.35 |  |  |  |
116 | 14976 | 1.34 | 66.77 |  |  |  |
117 | 15104 | 1.32 | 66.21 |  |  |  |
118 | 15232 | 1.31 | 65.65 | C2 | 65.41 | -0.24 | -0.37
119 | 15360 | 1.30 | 65.10 |  |  |  |
120 | 15488 | 1.29 | 64.57 |  |  |  |
121 | 15616 | 1.28 | 64.04 |  |  |  |
122 | 15744 | 1.27 | 63.52 |  |  |  |
123 | 15872 | 1.26 | 63.00 |  |  |  |
124 | 16000 | 1.25 | 62.50 |  |  |  |
125 | 16128 | 1.24 | 62.00 |  |  |  |
126 | 16256 | 1.23 | 61.52 | B1 | 61.74 | 0.22 | 0.36
127 | 16384 | 1.22 | 61.04 |  |  |  |
128 | 16512 | 1.21 | 60.56 |  |  |  |
129 | 16640 | 1.20 | 60.10 |  |  |  |
130 | 16768 | 1.19 | 59.64 |  |  |  |
131 | 16896 | 1.18 | 59.19 |  |  |  |
132 | 17024 | 1.17 | 58.74 |  |  |  |
133 | 17152 | 1.17 | 58.30 | A♯1 | 58.27 | -0.03 | -0.06
134 | 17280 | 1.16 | 57.87 |  |  |  |
135 | 17408 | 1.15 | 57.44 |  |  |  |
136 | 17536 | 1.14 | 57.03 |  |  |  |
137 | 17664 | 1.13 | 56.61 |  |  |  |
138 | 17792 | 1.12 | 56.21 |  |  |  |
139 | 17920 | 1.12 | 55.80 |  |  |  |
140 | 18048 | 1.11 | 55.41 |  |  |  |
141 | 18176 | 1.10 | 55.02 | A1 | 55.00 | -0.02 | -0.03
142 | 18304 | 1.09 | 54.63 |  |  |  |
143 | 18432 | 1.09 | 54.25 |  |  |  |
144 | 18560 | 1.08 | 53.88 |  |  |  |
145 | 18688 | 1.07 | 53.51 |  |  |  |
146 | 18816 | 1.06 | 53.15 |  |  |  |
147 | 18944 | 1.06 | 52.79 |  |  |  |
148 | 19072 | 1.05 | 52.43 |  |  |  |
149 | 19200 | 1.04 | 52.08 | G♯1 | 51.91 | -0.17 | -0.33
150 | 19328 | 1.03 | 51.74 |  |  |  |
151 | 19456 | 1.03 | 51.40 |  |  |  |
152 | 19584 | 1.02 | 51.06 |  |  |  |
153 | 19712 | 1.01 | 50.73 |  |  |  |
154 | 19840 | 1.01 | 50.40 |  |  |  |
155 | 19968 | 1.00 | 50.08 |  |  |  |
156 | 20096 | 1.00 | 49.76 |  |  |  |
157 | 20224 | 0.99 | 49.45 |  |  |  |
158 | 20352 | 0.98 | 49.14 | G1 | 49.00 | -0.14 | -0.28
159 | 20480 | 0.98 | 48.83 |  |  |  |
160 | 20608 | 0.97 | 48.52 |  |  |  |
161 | 20736 | 0.96 | 48.23 |  |  |  |
162 | 20864 | 0.96 | 47.93 |  |  |  |
163 | 20992 | 0.95 | 47.64 |  |  |  |
164 | 21120 | 0.95 | 47.35 |  |  |  |
165 | 21248 | 0.94 | 47.06 |  |  |  |
166 | 21376 | 0.94 | 46.78 |  |  |  |
167 | 21504 | 0.93 | 46.50 |  |  |  |
168 | 21632 | 0.92 | 46.23 | F♯1 | 46.25 | 0.02 | 0.05
169 | 21760 | 0.92 | 45.96 |  |  |  |
170 | 21888 | 0.91 | 45.69 |  |  |  |
171 | 22016 | 0.91 | 45.42 |  |  |  |
172 | 22144 | 0.90 | 45.16 |  |  |  |
173 | 22272 | 0.90 | 44.90 |  |  |  |
174 | 22400 | 0.89 | 44.64 |  |  |  |
175 | 22528 | 0.89 | 44.39 |  |  |  |
176 | 22656 | 0.88 | 44.14 |  |  |  |
177 | 22784 | 0.88 | 43.89 |  |  |  |
178 | 22912 | 0.87 | 43.65 | F1 | 43.65 | 0.00 | 0.01
179 | 23040 | 0.87 | 43.40 |  |  |  |
180 | 23168 | 0.86 | 43.16 |  |  |  |
181 | 23296 | 0.86 | 42.93 |  |  |  |
182 | 23424 | 0.85 | 42.69 |  |  |  |
183 | 23552 | 0.85 | 42.46 |  |  |  |
184 | 23680 | 0.84 | 42.23 |  |  |  |
185 | 23808 | 0.84 | 42.00 |  |  |  |
186 | 23936 | 0.84 | 41.78 |  |  |  |
187 | 24064 | 0.83 | 41.56 |  |  |  |
188 | 24192 | 0.83 | 41.34 |  |  |  |
189 | 24320 | 0.82 | 41.12 | E1 | 41.20 | 0.08 | 0.20
190 | 24448 | 0.82 | 40.90 |  |  |  |
191 | 24576 | 0.81 | 40.69 |  |  |  |
192 | 24704 | 0.81 | 40.48 |  |  |  |
193 | 24832 | 0.81 | 40.27 |  |  |  |
194 | 24960 | 0.80 | 40.06 |  |  |  |
195 | 25088 | 0.80 | 39.86 |  |  |  |
196 | 25216 | 0.79 | 39.66 |  |  |  |
197 | 25344 | 0.79 | 39.46 |  |  |  |
198 | 25472 | 0.79 | 39.26 |  |  |  |
199 | 25600 | 0.78 | 39.06 |  |  |  |
200 | 25728 | 0.78 | 38.87 | D♯1 | 38.89 | 0.02 | 0.06
201 | 25856 | 0.77 | 38.68 |  |  |  |
202 | 25984 | 0.77 | 38.49 |  |  |  |
203 | 26112 | 0.77 | 38.30 |  |  |  |
204 | 26240 | 0.76 | 38.11 |  |  |  |
205 | 26368 | 0.76 | 37.92 |  |  |  |
206 | 26496 | 0.75 | 37.74 |  |  |  |
207 | 26624 | 0.75 | 37.56 |  |  |  |
208 | 26752 | 0.75 | 37.38 |  |  |  |
209 | 26880 | 0.74 | 37.20 |  |  |  |
210 | 27008 | 0.74 | 37.03 |  |  |  |
211 | 27136 | 0.74 | 36.85 |  |  |  |
212 | 27264 | 0.73 | 36.68 | D1 | 36.71 | 0.03 | 0.09
213 | 27392 | 0.73 | 36.51 |  |  |  |
214 | 27520 | 0.73 | 36.34 |  |  |  |
215 | 27648 | 0.72 | 36.17 |  |  |  |
216 | 27776 | 0.72 | 36.00 |  |  |  |
217 | 27904 | 0.72 | 35.84 |  |  |  |
218 | 28032 | 0.71 | 35.67 |  |  |  |
219 | 28160 | 0.71 | 35.51 |  |  |  |
220 | 28288 | 0.71 | 35.35 |  |  |  |
221 | 28416 | 0.70 | 35.19 |  |  |  |
222 | 28544 | 0.70 | 35.03 |  |  |  |
223 | 28672 | 0.70 | 34.88 |  |  |  |
224 | 28800 | 0.69 | 34.72 | C♯1 | 34.65 | -0.07 | -0.21
225 | 28928 | 0.69 | 34.57 |  |  |  |
226 | 29056 | 0.69 | 34.42 |  |  |  |
227 | 29184 | 0.69 | 34.27 |  |  |  |
228 | 29312 | 0.68 | 34.12 |  |  |  |
229 | 29440 | 0.68 | 33.97 |  |  |  |
230 | 29568 | 0.68 | 33.82 |  |  |  |
231 | 29696 | 0.67 | 33.67 |  |  |  |
232 | 29824 | 0.67 | 33.53 |  |  |  |
233 | 29952 | 0.67 | 33.39 |  |  |  |
234 | 30080 | 0.66 | 33.24 |  |  |  |
235 | 30208 | 0.66 | 33.10 |  |  |  |
236 | 30336 | 0.66 | 32.96 |  |  |  |
237 | 30464 | 0.66 | 32.83 |  |  |  |
238 | 30592 | 0.65 | 32.69 | C1 | 32.70 | 0.01 | 0.04
239 | 30720 | 0.65 | 32.55 |  |  |  |
240 | 30848 | 0.65 | 32.42 |  |  |  |
241 | 30976 | 0.65 | 32.28 |  |  |  |
242 | 31104 | 0.64 | 32.15 |  |  |  |
243 | 31232 | 0.64 | 32.02 |  |  |  |
244 | 31360 | 0.64 | 31.89 |  |  |  |
245 | 31488 | 0.64 | 31.76 |  |  |  |
246 | 31616 | 0.63 | 31.63 |  |  |  |
247 | 31744 | 0.63 | 31.50 |  |  |  |
248 | 31872 | 0.63 | 31.38 |  |  |  |
249 | 32000 | 0.63 | 31.25 |  |  |  |
250 | 32128 | 0.62 | 31.13 |  |  |  |
251 | 32256 | 0.62 | 31.00 |  |  |  |
252 | 32384 | 0.62 | 30.88 | B0 | 30.87 | -0.01 | -0.03
253 | 32512 | 0.62 | 30.76 |  |  |  |
254 | 32640 | 0.61 | 30.64 |  |  |  |
255 | 32768 | 0.61 | 30.52 |  |  |  |

## PVI integers and indexes chart[edit | edit source]

PVI integer (index number)
Note\Octave | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
--- | --- | --- | --- | --- | --- | --- | --- | ---
C |  | 238(1) | 118(13) | 59(25) | 29(37) | 14(49) |  |
C♯/D♭ |  | 224(2) | 112(14) | 55(26) | 27(38) | 13(50) | 6(62) |
D |  | 212(3) | 105(15) | 52(27) | 26(39) | 12(51) |  |
E♭/D♯ |  | 200(4) | 100(16) | 49(28) | 24(40) |  |  |
E |  | 189(5) | 94(17) | 46(29) | 23(41) | 11(53) | 5(65) | 2(77)
F |  | 178(6) | 88(18) | 44(30) | 21(42) | 10(54) |  |
F♯/G♭ |  | 168(7) | 83(19) | 41(31) | 20(43) |  |  |
G |  | 158(8) | 79(20) | 39(32) | 19(44) | 9(56) | 4(68) |
A♭/G♯ |  | 149(9) | 74(21) | 37(33) | 18(45) |  |  |
A |  | 141(10) | 70(22) | 35(34) | 17(46) | 8(58) |  |
B♭/A♯ |  | 133(11) | 66(23) | 33(35) | 16(47) |  |  |
B | 252(0) | 126(12) | 63(24) | 31(36) | 15(48) | 7(60) | 3(72) | 1(84)

## Coded index table[edit | edit source]

This table is just one way of implementing an index table to look up integer values to feed to the PVI sound register $1FC7. Each unit step in index value shift by one semitone. Stepping by 12 in the index value shifts by an octave.

The lookup table uses 85 bytes and covers the range B0 to B7 but could be shrunk by reducing the range, or by eliminating notes not used in the composition.

            db 252
            db 238
            db 224
            db 212
            db 200
            db 189
            db 178
            db 168
            db 158
            db 149
            db 141
            db 133
            db 126
            db 118
            db 112
            db 105
            db 100
            db 94
            db 88
            db 83
            db 79
            db 74
            db 70
            db 66
            db 63
            db 59
            db 55
            db 52
            db 49
            db 46
            db 44
            db 41
            db 39
            db 37
            db 35
            db 33
            db 31
            db 29
            db 27
            db 26
            db 24
            db 23
            db 21
            db 20
            db 19
            db 18
            db 17
            db 16
            db 15
            db 14
            db 13
            db 12
            db 0
            db 11
            db 10
            db 0
            db 9
            db 0
            db 8
            db 0
            db 7
            db 0
            db 6
            db 0
            db 0
            db 5
            db 0
            db 0
            db 4
            db 0
            db 0
            db 0
            db 3
            db 0
            db 0
            db 0
            db 0
            db 2
            db 0
            db 0
            db 0
            db 0
            db 0
            db 0
            db 1

## References[edit | edit source]

1. ↑ https://pages.mtu.edu/~suits/notefreqs.html

# Contributions[edit | edit source]

## Welcome[edit | edit source]

As a Wikibook, anyone can contribute to it. It will be a work-in-progress for a long time. Your contributions might be anything from fixing a typo to creating a new page, or using a page's Discussion tab to raise questions or point out things that need a better explanation. If you are new to Wikibooks you can find help at Help:Contents.

Wikibooks policies and guidelines
Project-wide principles | What is Wikibooks? · What is Wikijunior? · Making decisions · Copyrights · Administration · Global rights policy
Book standards | Naming · Neutral point of view · Annotated texts · Featured books
Behavioral policies | Be civil · No personal attacks · Using bots · Protecting pages · Blocking users*
Behavioral guidelines | Please do not bite the newcomers · Assume good faith* · Etiquette*
Content guidelines | Deletion · Media · Profanity · Strategy guides · User pages* · Ownership*
Editing guidelines | Be bold · Contributing* · Style manual*
*Pending approval. Discuss on talk pages or in proposals reading room.

## Local manual of style[edit | edit source]

There are few rules, but it is nice to have a consistent style throughout the book. There is an overiding Manual of style for Wikibooks, otherwise please try to follow the style used on similar pages or in these guidelines. But don't worry about breaking the rules as errors are easily fixed.

- Use British English.
- Use a single layer of subpages, Book_Title/Page_name — Do not nest them, Book_Title/Section/Page_name.
- Add new pages to the Contents page and to the printable version.
- Most pages should only be marked 100% complete once they have been independently reviewed and tested, and when most of the other pages are in place and everything is fully integrated with appropriate links.

## Wikicode snippets[edit | edit source]

Some common wikicode used throughout the book:

- Template for a new page:

    {{TOC right}}

    ...page contents go here

    {{Status|0%}}
    {{Bookcat}}

- Template for a new tutorial code page:

    ==Tutorial code - XXXXX==

    This is the code for the tutorial [[{{BOOKNAME}}/XXXXX|XXXXX]]

    {{warning|This code block must be merged with the standard [[../Tutorial code|'Hardware definitions']] code before it can be assembled}}

    <pre>
    YOUR 2650 CODE GOES HERE
    </pre>
    {{Status|0%}}
    {{Bookcat}}

- See Also hatnote:

    {{see also|{{BOOKNAME}}/XXXXX|l1= TEXT FOR LINK}}

For more general help with editing these links may help:

Wikibooks editor navigation
Content | Stacks (Departments · Adding books · Alphabetical classification) · Featured books (Criteria) · Cookbook (Help pages · Special pages) · Wikijunior (Department) · Computing · Engineering · Humanities · Languages · Mathematics · Science · Social sciences · Standard curricula · Wikibooks and MediaWiki help · PDF versions · Requested books
FAQs | Wikibooks overview · Readers · Contributing · Editing · Administration · Problems
Help | Glossary · Navigating · Pages (Creating, Namespaces, Discussing, Archiving, Moving, Merging, Deleting) · Tracking changes · Account management · Contributing (Why and why not contribute? Starting a book) · Editing · Categories · Files · Tables · Variables · Formulas · Quizzes · Local manuals of style (Wikijunior) · Collections · Importing · Editnotices · Database download · Development stages · Dialog (Assistant) · Export · Print versions · Help books (Using Wikibooks · Editing Wikitext · CSS)
Templates | Template help · Template index (Book information · General · Sources · Links · Navigation · Files · Media · Maintenance · Licensing issues notices · Deletion · Stubs · User messages · User notices · User pages · Discussion · Featured books · Policies and guidelines) · Template categories · Making Templates A101 · Templates Ready to Use
Reading room | General · Proposals · Projects · Featured books · General · Technical · Administrative · Deletion · Undeletion · Import · Permissions · Bulletin Board
Useful pages | Accuracy dispute · Authorship · Autoblock · Book donations · Categories · Dealing with vandalism · Dewikify · How to obtain public domain books · Language Learning Difficulty · Licensing guidelines · NPOV dispute · Reading Levels · Render as PNG · Subject pages · Textbook standards · User rights · Wikibooks for course instructors · Wikibooks for Wikimedians · Wikimedia
Other project pages | Welcome · Sandbox · Contact us · Disclaimers category · CCO Resources · Shortcuts · Community Portal · Maintenance · WikiProject · Wikibooks essays (Essays category) · Book sources · Wikibooks for teaching (Guidelines for class projects · List of class projects) · WikiNode · Edit filter · CC-BY-SA License · GFDL License
See also: Policies and guidelines (Category)

## Contributors[edit | edit source]

The following editors have made contributions to this Wikibook. New editors, please add yourself to the list.

- Derek Andrews - started the book

Retrieved from "https://en.wikibooks.org/w/index.php?title=Signetics_2650_%26_2636_programming/Printable_version&oldid=4038156"
Category:

- Book:Signetics 2650 & 2636 programming

Hidden category:

- Wikibooks pages with to-do lists