2014-05-03 18:12:00 +02:00
|
|
|
'' 2-bit paletted tile-based video driver
|
|
|
|
'' Based on the NTSC Spectrum-like TV Video Driver
|
|
|
|
'' ──────────────────────────────────────────────────────────────────────────────────
|
|
|
|
'' Version story:
|
|
|
|
''
|
|
|
|
'' 2007-11-25 2.0 Tiled Version (Spork Frog)
|
|
|
|
'' 2007-12-05 1.0 First version (José Luis Cebrián)
|
|
|
|
'' 2009-11-12 Halved tile horizontal resolution (Héctor Peraza)
|
|
|
|
'' to eliminate redundant pixel pairs
|
|
|
|
'' (BoulderDash uses 8x16 tiles).
|
|
|
|
'' This also halves tile memory requirements.
|
|
|
|
'' Fixed vertical sync pulse generation.
|
|
|
|
'' 2009-11-13 Changed pixel clock settings (Héctor Peraza)
|
|
|
|
'' in order to display 20 tiles per line,
|
|
|
|
'' as in the classic C64 BoulderDash.
|
|
|
|
'' 2009-11-14 Reverse pixel order before waitvid (Héctor Peraza)
|
|
|
|
'' to avoid having to define mirrored tiles.
|
|
|
|
'' 2009-11-15 Added an optional status line. (Héctor Peraza)
|
|
|
|
'' 2009-11-17 Video config parameters can be passed (Héctor Peraza)
|
|
|
|
'' on driver startup, to allow for different
|
|
|
|
'' configurations and/or platforms.
|
|
|
|
'' 2009-11-18 Implemented PAL mode. Color seems to (Héctor Peraza)
|
|
|
|
'' be a bit off in PAL mode.
|
|
|
|
'' 2009-12-05 Added the possibilty to halve tile (Héctor Peraza)
|
|
|
|
'' height (we need that for the title screen,
|
|
|
|
'' since it has text on the bottom part
|
|
|
|
'' and characters are half-size)
|
|
|
|
''
|
|
|
|
'' ──────────────────────────────────────────────────────────────────────────────────
|
|
|
|
'' This code is in the public domain. Feel free to use it in any way you like.
|
|
|
|
''
|
|
|
|
'' The screen for this driver is composed of two major parts:
|
|
|
|
''
|
|
|
|
'' 1. Tile map
|
|
|
|
'' Each of these entries is 1 byte long, representing the tile number.
|
|
|
|
'' There are 880 words total for a 40 by 22 tile screen. The displayed
|
|
|
|
'' area is 20 x 12 in NTSC mode, 20 x 14 in PAL mode.
|
|
|
|
''
|
|
|
|
'' 2. Tiles
|
|
|
|
'' Each tile is made up of 16 words with 2 bit color encoding on
|
|
|
|
'' each tile. You can define as few or as many tiles as you want,
|
|
|
|
'' but be sure not to use any tiles that you don't define.
|
|
|
|
''
|
|
|
|
'' There is also an optional status line at the top of the screen that does not
|
|
|
|
'' scroll with the rest. It is also composed of tiles, but these are only 8 pixels
|
|
|
|
'' high. Two empty raster lines separate the status from the main screen. The
|
|
|
|
'' foreground color can be set per character basis.
|
|
|
|
''
|
|
|
|
'' 80Mhz is *required* in order to output pixels fast enough.
|
|
|
|
|
|
|
|
CON
|
|
|
|
|
|
|
|
' Border offset, to center the image
|
|
|
|
|
|
|
|
HorizBorderOffset = 9 ' positive values move the screen to the right
|
|
|
|
VertBorderOffset = 10 ' positive values move the screen down
|
|
|
|
|
|
|
|
' Counter Module Configuration
|
|
|
|
|
|
|
|
' • CTRMode - Operating mode (0001 for Video Mode)
|
|
|
|
' • PLLDiv - Divisor for the VCO frequency (111: use VCO value as-is)
|
|
|
|
|
|
|
|
'┌─────────── CTRMode
|
|
|
|
'│ ┌───── PLLDiv
|
|
|
|
CTRA_TVGEN = %00001_111
|
|
|
|
|
|
|
|
' NTSC Color frequency in Hz
|
|
|
|
'
|
|
|
|
' This is the 'base' clock rate for all our NTSC timings. At start, the
|
|
|
|
' driver will program the FRQA register to output at this rate. Our base
|
|
|
|
' clock value is 1/16 of the NTSC clock rate, or approximately 0.01746 µs.
|
|
|
|
|
|
|
|
NTSC_ClockFreq = 3_579_545
|
|
|
|
|
|
|
|
' NTSC Timings table
|
|
|
|
' Time Clocks Output
|
|
|
|
' Total horizontal timing: 63.5 µs 3638
|
|
|
|
' Horizontal blanking period: 10.9 µs 624
|
|
|
|
' Front porch: 1.5 µs 86 * Black ($02)
|
|
|
|
' Synchronizing pulse: 4.7 µs 269 * Blank ($00)
|
|
|
|
' Back porch: 4.7 µs 269
|
|
|
|
' Breeze away: 0.6 µs 34 * Black ($02)
|
|
|
|
' Colour burst: 2.5 µs 144 * Y Hue ($8A)
|
|
|
|
' Wait to data: 1.6 µs 92 * Black ($02)
|
|
|
|
' Visible line 52.6 µs 3008
|
|
|
|
' Left border 2.5 µs 146 * Black ($00)
|
|
|
|
' Pixel data 47.5 µs 2720
|
|
|
|
' Character (x20) 2.4 µs 136 * Data
|
|
|
|
' Right border 2.6 µs 146 * Black ($00)
|
|
|
|
' Half visible line ¹ 20.8 µs 1195
|
|
|
|
'
|
|
|
|
' Lines marked with * are the actual parts of a visible line as sent to the TV.
|
|
|
|
'
|
|
|
|
' ¹ The vertical sync pulse is a series of half lines with inverted horizontal sync
|
|
|
|
' pulses (the so-called equalization pulses). These half lines should have a length
|
|
|
|
' of 3638/2 = 1819 clocks (that is, a 624-clocks HSync followed by about 1195 clocks
|
|
|
|
' of visible data).
|
|
|
|
|
|
|
|
VSCL_NTSC_FrontPorch = 85
|
|
|
|
VSCL_NTSC_SynchronizingPulse = 270
|
|
|
|
VSCL_NTSC_BackPorch = 34 + 144 + 92
|
|
|
|
VSCL_NTSC_BreezeAway = 34
|
|
|
|
VSCL_NTSC_ColourBurst = 144
|
|
|
|
VSCL_NTSC_WaitToData = 92
|
|
|
|
VSCL_NTSC_VisibleLine = 3008
|
|
|
|
VSCL_NTSC_HalfLine = 1192 ' NTSC Half line
|
|
|
|
VSCL_NTSC_LeftBorder = 146 + HorizBorderOffset
|
|
|
|
VSCL_NTSC_RightBorder = 146 - HorizBorderOffset
|
|
|
|
VSCL_NTSC_Character = (17 << 12) + 136 ' Eight double-width pixels
|
|
|
|
|
|
|
|
NTSC_VerticalLines = 262 - 9
|
|
|
|
NTSC_TopLine = (NTSC_VerticalLines + 192) / 2 - VertBorderOffset
|
|
|
|
NTSC_BottomLine = (NTSC_VerticalLines - 192) / 2 - VertBorderOffset
|
|
|
|
|
|
|
|
' PAL Color frequency in Hz
|
|
|
|
'
|
|
|
|
' This is the 'base' clock rate for all the PAL timings. At start, the
|
|
|
|
' driver will program the FRQA register to output at this rate. Our base
|
|
|
|
' clock value is 1/16 of the PAL clock rate, or approximately 0.014097 µs
|
|
|
|
|
|
|
|
PAL_ClockFreq = 4_433_618
|
|
|
|
|
|
|
|
' PAL Timings table
|
|
|
|
' Time Clocks Output
|
|
|
|
' Total horizontal timing: 64.0 µs 4540
|
|
|
|
' Horizontal blanking period: 12.0 µs 851
|
|
|
|
' Front porch: 1.6 µs 113 * Black ($02)
|
|
|
|
' Synchronizing pulse: 4.7 µs 333 * Blank ($00)
|
|
|
|
' Back porch: 5.7 µs 404
|
|
|
|
' Breeze away: 0.9 µs 64 * Black ($02)
|
|
|
|
' Colour burst: 2.3 µs 160 * Y Hue ($8A)
|
|
|
|
' Wait to data: 2.5 µs 180 * Black ($02)
|
|
|
|
' Visible line 52.0 µs 3689
|
|
|
|
' Left border 3.4 µs 244 * Black ($00)
|
|
|
|
' Pixel data 45.1 µs 3200
|
|
|
|
' Character (x20) 2.3 µs 160 * Data
|
|
|
|
' Right border 3.4 µs 245 * Black ($00)
|
|
|
|
' Half visible line ¹ 20.0 µs 1419
|
|
|
|
'
|
|
|
|
' Lines marked with * are the actual parts of a visible line as sent to the TV.
|
|
|
|
'
|
|
|
|
' ¹ The vertical sync pulse is a series of half lines with inverted horizontal sync
|
|
|
|
' pulses (the so-called equalization pulses). These half lines should have a length
|
|
|
|
' of 4540/2 = 2270 clocks (that is, a 851-clocks HSync followed by about 1419 clocks
|
|
|
|
' of visible data).
|
|
|
|
|
|
|
|
VSCL_PAL_FrontPorch = 114
|
|
|
|
VSCL_PAL_SynchronizingPulse = 332
|
|
|
|
VSCL_PAL_BackPorch = 64 + 160 + 180
|
|
|
|
VSCL_PAL_BreezeAway = 64
|
|
|
|
VSCL_PAL_ColourBurst = 160
|
|
|
|
VSCL_PAL_WaitToData = 180
|
|
|
|
VSCL_PAL_VisibleLine = 3689
|
|
|
|
VSCL_PAL_HalfLine = 1419 ' PAL Half line
|
|
|
|
VSCL_PAL_LeftBorder = 244 + HorizBorderOffset
|
|
|
|
VSCL_PAL_RightBorder = 245 - HorizBorderOffset
|
|
|
|
VSCL_PAL_Character = (20 << 12) + 160 ' Eight double-width pixels
|
|
|
|
|
|
|
|
PAL_VerticalLines = 312 - 9
|
|
|
|
PAL_TopLine = (PAL_VerticalLines + 224) / 2 - VertBorderOffset
|
|
|
|
PAL_BottomLine = (PAL_VerticalLines - 224) / 2 - VertBorderOffset
|
|
|
|
|
|
|
|
PUB Start(vParam)
|
|
|
|
'' Starts the TV driver and begins the output of NTSC video.
|
|
|
|
'' Uses a Cog.
|
|
|
|
''
|
|
|
|
'' Parameters:
|
|
|
|
'' vParam → Array with video parameters:
|
|
|
|
'' [0] → VCFG mode
|
|
|
|
'' [1] → Pingroup for VCFG pins
|
|
|
|
'' [2] → Pinmask for VCFG pins
|
|
|
|
'' [3] → Pinmask for output pins
|
|
|
|
'' [4] → Color standard: 0 = NTSC, 1 = PAL
|
|
|
|
'' [5] → Tile map address
|
|
|
|
'' [6] → Address of a vsync flag byte in HUB memory
|
|
|
|
|
|
|
|
cognew(@Entry, vParam)
|
|
|
|
|
|
|
|
DAT
|
|
|
|
|
|
|
|
org $000
|
|
|
|
|
|
|
|
Entry jmp #StartDriver
|
|
|
|
|
|
|
|
' ─────────────────────────────────────────
|
|
|
|
' Data section
|
|
|
|
' ─────────────────────────────────────────
|
|
|
|
|
|
|
|
' Colors used in waitvid
|
|
|
|
|
|
|
|
COLOR_SYNC long $00 ' Sync level is below black
|
|
|
|
COLOR_BLACK long $02
|
|
|
|
COLOR_YHUE_NTSC long $8A ' NTSC color burst
|
|
|
|
COLOR_YHUE_PAL_1 long $7A ' PAL swinging color burst
|
|
|
|
COLOR_YHUE_PAL_2 long $AA ' " " " "
|
|
|
|
COLOR_BORDER long $02 ' Black border
|
|
|
|
|
|
|
|
' The following constants are too big to use in-place, so we need to
|
|
|
|
' reserve some registers to put them here
|
|
|
|
|
|
|
|
_ScreenSize long 40 * 28 ' Tile map size
|
|
|
|
|
|
|
|
_NTSC_ClockFreq long NTSC_ClockFreq
|
|
|
|
_VSCL_NTSC_Character long VSCL_NTSC_Character
|
|
|
|
_VSCL_NTSC_VisibleLine long VSCL_NTSC_VisibleLine
|
|
|
|
_VSCL_NTSC_HalfLine long VSCL_NTSC_HalfLine
|
|
|
|
|
|
|
|
_PAL_ClockFreq long PAL_ClockFreq
|
|
|
|
_VSCL_PAL_Character long VSCL_PAL_Character
|
|
|
|
_VSCL_PAL_VisibleLine long VSCL_PAL_VisibleLine
|
|
|
|
_VSCL_PAL_HalfLine long VSCL_PAL_HalfLine
|
|
|
|
|
|
|
|
' Other constants
|
|
|
|
|
|
|
|
TrueWord long $FFFF
|
|
|
|
|
|
|
|
' ─────────────────────────────────────────
|
|
|
|
' Code section
|
|
|
|
' ─────────────────────────────────────────
|
|
|
|
|
|
|
|
StartDriver
|
|
|
|
|
|
|
|
' Configure the Cog generators
|
|
|
|
|
|
|
|
mov R0, PAR
|
|
|
|
rdlong _VCFG_Mode, R0
|
|
|
|
add R0, #4
|
|
|
|
rdlong _VCFG_PinGroup, R0
|
|
|
|
add R0, #4
|
|
|
|
rdlong _VCFG_PinMask, R0
|
|
|
|
add R0, #4
|
|
|
|
rdlong _PortMask, R0
|
|
|
|
add R0, #4
|
|
|
|
rdlong VideoMode, R0
|
|
|
|
add R0, #4
|
|
|
|
rdlong ScreenPtr, R0
|
|
|
|
add R0, #4
|
|
|
|
rdlong VSyncPtr, R0
|
|
|
|
|
|
|
|
test VideoMode, #1 wz
|
|
|
|
if_z mov _ClockFreq, _NTSC_ClockFreq
|
|
|
|
if_nz mov _ClockFreq, _PAL_ClockFreq
|
|
|
|
if_z mov _VSCL_Character, _VSCL_NTSC_Character
|
|
|
|
if_nz mov _VSCL_Character, _VSCL_PAL_Character
|
|
|
|
if_z mov _VSCL_VisibleLine, _VSCL_NTSC_VisibleLine
|
|
|
|
if_nz mov _VSCL_VisibleLine, _VSCL_PAL_VisibleLine
|
|
|
|
if_z mov _VSCL_HalfLine, _VSCL_NTSC_HalfLine
|
|
|
|
if_nz mov _VSCL_HalfLine, _VSCL_PAL_HalfLine
|
|
|
|
if_z mov _VSCL_LeftBorder, #VSCL_NTSC_LeftBorder
|
|
|
|
if_nz mov _VSCL_LeftBorder, #VSCL_PAL_LeftBorder
|
|
|
|
if_z mov _VSCL_RightBorder, #VSCL_NTSC_RightBorder
|
|
|
|
if_nz mov _VSCL_RightBorder, #VSCL_PAL_RightBorder
|
|
|
|
if_z mov _VSCL_FrontPorch, #VSCL_NTSC_FrontPorch
|
|
|
|
if_nz mov _VSCL_FrontPorch, #VSCL_PAL_FrontPorch
|
|
|
|
if_z mov _VSCL_SynchronizingPulse, #VSCL_NTSC_SynchronizingPulse
|
|
|
|
if_nz mov _VSCL_SynchronizingPulse, #VSCL_PAL_SynchronizingPulse
|
|
|
|
if_z mov _VSCL_BreezeAway, #VSCL_NTSC_BreezeAway
|
|
|
|
if_nz mov _VSCL_BreezeAway, #VSCL_PAL_BreezeAway
|
|
|
|
if_z mov _VSCL_ColourBurst, #VSCL_NTSC_ColourBurst
|
|
|
|
if_nz mov _VSCL_ColourBurst, #VSCL_PAL_ColourBurst
|
|
|
|
if_z mov _VSCL_BackPorch, #VSCL_NTSC_BackPorch
|
|
|
|
if_nz mov _VSCL_BackPorch, #VSCL_PAL_BackPorch
|
|
|
|
if_z mov _VSCL_WaitToData, #VSCL_NTSC_WaitToData
|
|
|
|
if_nz mov _VSCL_WaitToData, #VSCL_PAL_WaitToData
|
|
|
|
if_z mov _VerticalLines, #NTSC_VerticalLines
|
|
|
|
if_nz mov _VerticalLines, #PAL_VerticalLines
|
|
|
|
if_z mov _TopLine, #NTSC_TopLine
|
|
|
|
if_nz mov _TopLine, #PAL_TopLine
|
|
|
|
if_z mov _BottomLine, #NTSC_BottomLine
|
|
|
|
if_nz mov _BottomLine, #PAL_BottomLine
|
|
|
|
if_z mov _Burst1, COLOR_YHUE_NTSC
|
|
|
|
if_nz mov _Burst1, COLOR_YHUE_PAL_1
|
|
|
|
if_z mov _Burst2, COLOR_YHUE_NTSC
|
|
|
|
if_nz mov _Burst2, COLOR_YHUE_PAL_2
|
|
|
|
|
|
|
|
movs VCFG, _VCFG_PinMask ' VCFG'S = pinmask (pin31: 0000_0111 : pin24)
|
|
|
|
movd VCFG, _VCFG_PinGroup ' VCFG'D = pingroup (grp. 3 i.e. pins 24-31)
|
|
|
|
movi VCFG, _VCFG_Mode ' Baseband video on bottom nibble, 2-bit color, enable chroma on baseband
|
|
|
|
or DIRA, _PortMask ' Setup the port mask for DAC access (set DAC pins to output)
|
|
|
|
|
|
|
|
movi CTRA, #CTRA_TVGEN ' Setup the Counter Module Generator A
|
|
|
|
mov R1, _ClockFreq ' R1 := Video Clock Frequency in Hz
|
|
|
|
rdlong R2, #0 ' R2 := Current CPU Clock Frequency in Hz
|
|
|
|
call #Divide ' R3 := R1÷R2 (fractional part)
|
|
|
|
mov FRQA, R3 ' Setup the Counter Module Generator frequency
|
|
|
|
|
|
|
|
mov phaseflip, #0
|
|
|
|
|
|
|
|
' Frame loop
|
|
|
|
|
|
|
|
:Frame
|
|
|
|
mov LineCounter, _VerticalLines ' LineCounter := Number of vertical lines
|
|
|
|
mov CharacterRows, #0
|
|
|
|
|
|
|
|
' Copy the screen parameters to local variables in Cog memory
|
|
|
|
|
|
|
|
mov AttribPtr, ScreenPtr
|
|
|
|
mov TilePtr, AttribPtr
|
|
|
|
add TilePtr, _ScreenSize
|
|
|
|
rdlong COLORS, TilePtr
|
|
|
|
add TilePtr, #4
|
|
|
|
rdlong OffsetX, TilePtr
|
|
|
|
mov OffsetY, OffsetX
|
|
|
|
shr OffsetX, #16
|
|
|
|
and OffsetY, TrueWord
|
|
|
|
add TilePtr, #4
|
|
|
|
rdlong Status, TilePtr
|
|
|
|
add TilePtr, #4
|
|
|
|
mov StatusPtr, TilePtr
|
|
|
|
add TilePtr, #20*2
|
|
|
|
|
|
|
|
' Y Offset Calculations
|
|
|
|
|
|
|
|
mov R0, OffsetY
|
|
|
|
test Status, #2 wz
|
|
|
|
if_z and OffsetY, #%1111
|
|
|
|
if_nz and OffsetY, #%111
|
|
|
|
shl OffsetY, #1
|
|
|
|
mov CharacterRows, OffsetY
|
|
|
|
if_z shr R0, #4
|
|
|
|
if_nz shr R0, #3
|
|
|
|
add R0, #1
|
|
|
|
:AddLoop add AttribPtr, #40
|
|
|
|
djnz R0, #:AddLoop
|
|
|
|
sub AttribPtr, #40
|
|
|
|
|
|
|
|
' X Offset Calculations
|
|
|
|
|
|
|
|
mov R0, OffsetX
|
|
|
|
and OffsetX, #%111
|
|
|
|
shl OffsetX, #1
|
|
|
|
mov R5, #16
|
|
|
|
sub R5, OffsetX
|
|
|
|
shr R0, #3
|
|
|
|
add AttribPtr, R0
|
|
|
|
|
|
|
|
' Status Line row count
|
|
|
|
|
|
|
|
mov R6, #0
|
|
|
|
|
|
|
|
' Visible line loop
|
|
|
|
|
|
|
|
:ScanLine call #HSync
|
|
|
|
|
|
|
|
' Check if the current line is in the top or bottom border
|
|
|
|
|
|
|
|
cmp LineCounter, _BottomLine wc
|
|
|
|
if_c jmp #:EmptyLine
|
|
|
|
cmp LineCounter, _TopLine wc
|
|
|
|
if_nc jmp #:CheckStat
|
|
|
|
jmp #:DataLine
|
|
|
|
|
|
|
|
:CheckStat test Status, #1 wz
|
|
|
|
if_z jmp #:EmptyLine
|
|
|
|
mov R0, _TopLine
|
|
|
|
add R0, #10
|
|
|
|
cmp LineCounter, R0 wc
|
|
|
|
if_nc jmp #:EmptyLine
|
|
|
|
cmp R6, #16 wz
|
|
|
|
if_z jmp #:EmptyLine
|
|
|
|
|
|
|
|
' Draw the status line
|
|
|
|
|
|
|
|
mov VSCL, _VSCL_LeftBorder
|
|
|
|
waitvid COLOR_BORDER, #0
|
|
|
|
|
|
|
|
' Character rendering loop
|
|
|
|
|
|
|
|
mov VSCL, _VSCL_Character
|
|
|
|
mov R0, #20 ' R0 := Character counter (per line)
|
|
|
|
mov R1, StatusPtr ' R1 := Pointer to status line
|
|
|
|
mov R4, R1
|
|
|
|
add R4, R0 ' R4 := Pointer to foreground color
|
|
|
|
:StatusChar rdbyte R3, R1 ' R3 := Tile description
|
|
|
|
add R1, #1 ' Advance the attribute pointer for the next character
|
|
|
|
shl R3, #4 ' Multiply by bitmap size to form an address
|
|
|
|
add R3, R6 ' Add the row offset for each tile
|
|
|
|
add R3, TilePtr ' R3 := Address of the bitmap word to read
|
|
|
|
rdword PIXELS, R3 ' Set pixels to the proper data
|
|
|
|
rev PIXELS, #16 ' Reverse bit order to output pixels MSB first
|
|
|
|
rdbyte R3, R4 ' R3 := foreground color
|
|
|
|
add R4, #1 ' Advance the color pointer for the next character
|
|
|
|
mov R2, COLORS
|
|
|
|
shl R2, #8
|
|
|
|
or R2, R3 ' Only foreground color is set, background remains the same from palette
|
|
|
|
ror R2, #8
|
|
|
|
test VideoMode, #1 wz
|
|
|
|
if_nz xor R2, phaseflip ' Flip color phase if PAL mode
|
|
|
|
waitvid R2, PIXELS ' Output our video
|
|
|
|
djnz R0, #:StatusChar ' and loop
|
|
|
|
|
|
|
|
mov VSCL, _VSCL_RightBorder
|
|
|
|
waitvid COLOR_BORDER, #0 ' Output the right border
|
|
|
|
|
|
|
|
add R6, #2
|
|
|
|
djnz LineCounter, #:ScanLine ' Next line (note that here LineCounter is always > 0)
|
|
|
|
|
|
|
|
' jmp #:EmptyLine
|
|
|
|
|
|
|
|
' Draw a data line
|
|
|
|
|
|
|
|
:DataLine mov VSCL, _VSCL_LeftBorder
|
|
|
|
waitvid COLOR_BORDER, #0 ' Output the left border
|
|
|
|
|
|
|
|
' Character rendering loop
|
|
|
|
|
|
|
|
mov VSCL, _VSCL_Character
|
|
|
|
mov R0, #20 ' R0 := Character counter (per line)
|
|
|
|
mov R1, AttribPtr ' R1 := Pointer to attribute (tile) area
|
|
|
|
:Character rdbyte R2, R1 ' R2 := Tile description long
|
|
|
|
add R1, #1 ' Advance the attribute pointer for the next character
|
|
|
|
test Status, #2 wz
|
|
|
|
if_z shl R2, #5 ' Multiply by bitmap size to form an address
|
|
|
|
if_nz shl R2, #4
|
|
|
|
add R2, CharacterRows ' Add the row offset for each tile
|
|
|
|
add R2, TilePtr ' R2 := Address of the bitmap word to read
|
|
|
|
rdword PIXELS, R2 ' Set pixels to the proper data
|
|
|
|
cmp OffsetX, #0 wz
|
|
|
|
if_z jmp #:Output
|
|
|
|
shl PIXELS, OffsetX
|
|
|
|
rdbyte R2, R1
|
|
|
|
test Status, #2 wz
|
|
|
|
if_z shl R2, #5 ' Multiply by bitmap size to form an address
|
|
|
|
if_nz shl R2, #4
|
|
|
|
add R2, CharacterRows ' Add the row offset for each tile
|
|
|
|
add R2, TilePtr ' R2 := Address of the bitmap word to read
|
|
|
|
rdword R3, R2
|
|
|
|
shr R3, R5
|
|
|
|
or PIXELS, R3
|
|
|
|
:Output rev PIXELS, #16 ' Reverse bit order to output pixels MSB first
|
|
|
|
mov R2, COLORS
|
|
|
|
test VideoMode, #1 wz
|
|
|
|
if_nz xor R2, phaseflip ' Flip color phase if PAL mode
|
|
|
|
waitvid R2, PIXELS ' Output our video
|
|
|
|
djnz R0, #:Character ' and loop
|
|
|
|
|
|
|
|
mov VSCL, _VSCL_RightBorder
|
|
|
|
waitvid COLOR_BORDER, #0 ' Output the right border
|
|
|
|
|
|
|
|
' Calculate the address of the next attribute line
|
|
|
|
|
|
|
|
add CharacterRows, #2
|
|
|
|
test Status, #2 wz
|
|
|
|
if_z mov R2, #32
|
|
|
|
if_nz mov R2, #16
|
|
|
|
cmp CharacterRows, R2 wz
|
|
|
|
if_z mov CharacterRows, #0 ' Advance to the next line of attributes if the sub-character counter
|
|
|
|
if_z add AttribPtr, #40 ' reaches the end of character, and reset it to 8
|
|
|
|
djnz LineCounter, #:ScanLine ' Next line (note that here LineCounter is always > 0)
|
|
|
|
|
|
|
|
' Empty (border only) lines
|
|
|
|
|
|
|
|
:EmptyLine mov VSCL, _VSCL_VisibleLine ' Output an entire visible line of BORDER color
|
|
|
|
waitvid COLOR_BORDER, #0
|
|
|
|
djnz LineCounter, #:ScanLine ' Next line: note that this may be the last one
|
|
|
|
|
|
|
|
' VSync
|
|
|
|
|
|
|
|
mov R0, #$FF
|
|
|
|
wrbyte R0, VSyncPtr
|
|
|
|
|
|
|
|
test VideoMode, #1 wz
|
|
|
|
if_nz xor phaseflip, phasemask
|
|
|
|
|
|
|
|
call #VSyncHigh ' VSync procedure: 6 half-lines of HSync-only values (3 lines)
|
|
|
|
call #VSyncLow ' 6 half-lines inverted from the previous ones (3 lines)
|
|
|
|
call #VSyncHigh ' 6 half-lines more of HSync-only values (3 lines)
|
|
|
|
|
|
|
|
mov R0, #0
|
|
|
|
wrbyte R0, VSyncPtr
|
|
|
|
|
|
|
|
jmp #:Frame ' Next frame
|
|
|
|
|
|
|
|
' ─────────────────────────────────────────
|
|
|
|
' Synchronization subroutines
|
|
|
|
' ─────────────────────────────────────────
|
|
|
|
|
|
|
|
HSync mov VSCL, _VSCL_FrontPorch
|
|
|
|
waitvid COLOR_BLACK, #0
|
|
|
|
mov VSCL, _VSCL_SynchronizingPulse
|
|
|
|
waitvid COLOR_SYNC, #0
|
|
|
|
mov VSCL, _VSCL_BreezeAway
|
|
|
|
waitvid COLOR_BLACK, #0
|
|
|
|
mov VSCL, _VSCL_ColourBurst
|
|
|
|
|
|
|
|
test VideoMode, #1 wz
|
|
|
|
if_nz xor phaseflip, phasemask wz ' Swing color burst if PAL
|
|
|
|
if_z mov R0, _Burst1
|
|
|
|
if_nz mov R0, _Burst2
|
|
|
|
waitvid R0, #0
|
|
|
|
|
|
|
|
mov VSCL, _VSCL_WaitToData
|
|
|
|
waitvid COLOR_BLACK, #0
|
|
|
|
HSync_Ret ret
|
|
|
|
|
|
|
|
VSyncHigh mov R0, #6
|
|
|
|
:Loop mov VSCL, _VSCL_FrontPorch
|
|
|
|
waitvid COLOR_BLACK, #0
|
|
|
|
mov R1, _VSCL_SynchronizingPulse
|
|
|
|
shr R1, #1 ' Half-length pulses
|
|
|
|
mov VSCL, R1
|
|
|
|
waitvid COLOR_SYNC, #0
|
|
|
|
mov VSCL, R1
|
|
|
|
waitvid COLOR_BLACK, #0
|
|
|
|
mov VSCL, _VSCL_BackPorch ' BackPorch = BreezeAway + ColourBurst + WaitToData
|
|
|
|
waitvid COLOR_BLACK, #0
|
|
|
|
mov VSCL, _VSCL_HalfLine
|
|
|
|
waitvid COLOR_BLACK, #0
|
|
|
|
djnz R0, #:Loop
|
|
|
|
VSyncHigh_Ret ret
|
|
|
|
|
|
|
|
VSyncLow mov R0, #6
|
|
|
|
:Loop mov VSCL, _VSCL_FrontPorch
|
|
|
|
waitvid COLOR_BLACK, #0
|
|
|
|
mov VSCL, _VSCL_HalfLine
|
|
|
|
waitvid COLOR_SYNC, #0
|
|
|
|
mov VSCL, _VSCL_BackPorch ' BackPorch = BreezeAway + ColourBurst + WaitToData
|
|
|
|
waitvid COLOR_SYNC, #0
|
|
|
|
mov VSCL, _VSCL_FrontPorch
|
|
|
|
waitvid COLOR_SYNC, #0
|
|
|
|
mov R1, _VSCL_SynchronizingPulse
|
|
|
|
sub R1, _VSCL_FrontPorch
|
|
|
|
mov VSCL, R1
|
|
|
|
' mov VSCL, _VSCL_SynchronizingPulse
|
|
|
|
waitvid COLOR_BLACK, #0
|
|
|
|
djnz R0, #:Loop
|
|
|
|
VSyncLow_Ret ret
|
|
|
|
|
|
|
|
|
|
|
|
' ─────────────────────────────────────────
|
|
|
|
' Utility subroutines
|
|
|
|
' ─────────────────────────────────────────
|
|
|
|
|
|
|
|
' Divide R1 by R2 and return the result in R3 with 32 bits of decimal precision
|
|
|
|
' Input: R1 → Dividend
|
|
|
|
' R2 → Divisor (it is required that R1 < R2)
|
|
|
|
' Output: R3 → (R1/R2) << 32
|
|
|
|
|
|
|
|
Divide mov R0, #33
|
|
|
|
:Loop cmpsub R1, R2 wc
|
|
|
|
rcl R3, #1
|
|
|
|
shl R1, #1
|
|
|
|
djnz R0, #:Loop
|
|
|
|
Divide_Ret ret
|
|
|
|
|
|
|
|
|
|
|
|
phaseflip long $00000000
|
|
|
|
phasemask long $F0F0F0F0 ' invert hue portion of color
|
|
|
|
|
|
|
|
' ─────────────────────────────────────────
|
|
|
|
' Uninitialized data
|
|
|
|
' ─────────────────────────────────────────
|
|
|
|
|
|
|
|
R0 res 1
|
|
|
|
R1 res 1
|
|
|
|
R2 res 1
|
|
|
|
R3 res 1
|
|
|
|
R4 res 1
|
|
|
|
R5 res 1
|
|
|
|
R6 res 1
|
|
|
|
|
|
|
|
COLORS res 1
|
|
|
|
PIXELS res 1
|
|
|
|
|
|
|
|
_VCFG_PinMask res 1
|
|
|
|
_VCFG_PinGroup res 1
|
|
|
|
_VCFG_Mode res 1
|
|
|
|
_PortMask res 1
|
|
|
|
|
|
|
|
_ClockFreq res 1
|
|
|
|
_VSCL_Character res 1
|
|
|
|
_VSCL_VisibleLine res 1
|
|
|
|
_VSCL_HalfLine res 1
|
|
|
|
_VSCL_LeftBorder res 1
|
|
|
|
_VSCL_RightBorder res 1
|
|
|
|
_VSCL_FrontPorch res 1
|
|
|
|
_VSCL_SynchronizingPulse res 1
|
|
|
|
_VSCL_BreezeAway res 1
|
|
|
|
_VSCL_ColourBurst res 1
|
|
|
|
_VSCL_BackPorch res 1
|
|
|
|
_VSCL_WaitToData res 1
|
|
|
|
_VerticalLines res 1
|
|
|
|
_TopLine res 1
|
|
|
|
_BottomLine res 1
|
|
|
|
_Burst1 res 1
|
|
|
|
_Burst2 res 1
|
|
|
|
|
|
|
|
LineCounter res 1
|
|
|
|
CharacterRows res 1
|
|
|
|
TilePtr res 1
|
|
|
|
AttribPtr res 1
|
|
|
|
ScreenPtr res 1
|
|
|
|
StatusPtr res 1
|
|
|
|
VSyncPtr res 1
|
|
|
|
|
|
|
|
PAddr res 1
|
|
|
|
PCtr res 1
|
|
|
|
|
|
|
|
OffsetX res 1
|
|
|
|
OffsetY res 1
|
|
|
|
|
|
|
|
Status res 1
|
|
|
|
VideoMode res 1
|
|
|
|
|
|
|
|
fit
|
|
|
|
|