120 lines
3.4 KiB
Plaintext
120 lines
3.4 KiB
Plaintext
/*
|
|
- horizontal sync pulse generation
|
|
this machine commands the timing for the other machines too
|
|
- vertical sync pulse generation
|
|
counts the horizontal sync pulses and generated a vertical sync pulse accordingly
|
|
- pixel machine
|
|
horizontal sync machine also sends an interrupt when the back porch is finished,
|
|
thus triggering the pixel machine to start shifting out the pixels
|
|
*/
|
|
|
|
/*
|
|
Pixel Machine
|
|
Wait for the "start-of-visible-line" interrupt signal,
|
|
and output 640 pixels
|
|
*/
|
|
.program vga_pixel
|
|
|
|
entrypoint_vga_pixel:
|
|
// pull configuration into Y register
|
|
// Y = number of visible pixels in the line
|
|
pull
|
|
mov y, osr
|
|
|
|
.wrap_target
|
|
mov x, y
|
|
wait irq 7
|
|
|
|
// loop lasts 4 SM clocks, which corresponds to 1 VGA pixel
|
|
loop:
|
|
out pins, 1 [2]
|
|
jmp x-- loop
|
|
.wrap
|
|
|
|
/*
|
|
Horizontal Sync Machine
|
|
A line is composed by 840 pixels:
|
|
- 64 sync pulse
|
|
- 120 back porch
|
|
- 640 visible area
|
|
- 16 front porch
|
|
*/
|
|
.program vga_hsync
|
|
entrypoint_vga_hsync:
|
|
// pull configuration into ISR and OSR registers
|
|
// ISR = sync pulse duration - 2 (64 pixel)
|
|
// OSR = visible line + front porch - 1 (640 + 16 pixel)
|
|
// Note: configuration contains desired value -1/-2,
|
|
// because of how end-of-loop check is performed
|
|
// and how time is spent in overhead instructions
|
|
pull
|
|
mov isr, osr
|
|
pull
|
|
|
|
// once configured, no more data is pulled from the TX FIFO,
|
|
// so program wraps around here
|
|
.wrap_target
|
|
set pins, 0 [1] // start pulse low
|
|
irq set 0 // trigger "start-of-line" interrupt signal
|
|
mov x, isr // load register X with sync pulse duration
|
|
// +4 SM clocks elapsed (first VGA pixel)
|
|
|
|
// loop for the duration of the low pulse (remaining 63 pixels)
|
|
hsync_pulse:
|
|
jmp x-- hsync_pulse [3]
|
|
|
|
set pins, 1 [1] // start pulse high (.5 pixel)
|
|
// hardcoded backporch duration: 120 pixel => 120 x 4 = 480 SM clocks
|
|
// but one pixel is already in the surronding logic, then only 476 clocks are cycled
|
|
// where 476 = (27 + 1) * (16 + 1)
|
|
set y, 27 // another .25 pixel
|
|
hsync_back_porch:
|
|
jmp y-- hsync_back_porch [16]
|
|
irq set 7 // trigger "start-of-visible-line" interrupt signal
|
|
// (.25 more pixel)
|
|
|
|
mov x, osr [3] // load register X with remaining line duration (visible line + front porch)
|
|
// also, just make this last 4 SM clocks (1 pixel)
|
|
hsync_idle:
|
|
jmp x-- hsync_idle [3]
|
|
.wrap
|
|
|
|
|
|
/*
|
|
Vertical Sync Machine
|
|
A frame is composed by 500 lines:
|
|
- 3 vertical sync pulse
|
|
- 16 back porch
|
|
- 480 visible lines
|
|
- 1 front porch
|
|
*/
|
|
.program vga_vsync
|
|
entrypoint_vga_vsync:
|
|
// Pull configuration into ISR and OSR registers
|
|
// ISR = sync pulse assert duration - 1 (3 lines)
|
|
// OSR = sync pulse idle duration - 1 (497 lines)
|
|
pull
|
|
mov isr, osr
|
|
pull
|
|
|
|
// once configured, no more data is pulled from the TX FIFO,
|
|
// so program wraps around here
|
|
.wrap_target
|
|
mov x, isr // load register X with low pulse duration
|
|
vsync_pulse:
|
|
wait irq 0 // wait for "start-of-line" interrupt from hsync machine
|
|
set pins, 0 // start pulse low
|
|
jmp x-- vsync_pulse
|
|
|
|
// trigger "start-of-frame" interrupt
|
|
// (actually, 3 lines of the back porch are already passed)
|
|
irq set 1
|
|
|
|
mov x, osr // load register X with high pulse duration
|
|
vsync_idle:
|
|
wait irq 0
|
|
set pins, 1
|
|
jmp x-- vsync_idle
|
|
|
|
.wrap
|