Compare commits
No commits in common. "4d103f99ec041a5e50ec5fbf64b8dcdac144d7ec" and "d17c4c41fd2b27fd94b2c28986626bb8f5a8387c" have entirely different histories.
4d103f99ec
...
d17c4c41fd
13 changed files with 346 additions and 427 deletions
|
|
@ -4,7 +4,7 @@ All files, except where explicitly stated otherwise, are licensed according to t
|
|||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
Copyright 2024-2026 Sebastian Lederer
|
||||
Copyright 2024 Sebastian Lederer
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
|
|
|
|||
|
|
@ -22,12 +22,11 @@ The _BSEL_ and _BPLC_ instructions are designed to assist with accessing bytes w
|
|||
The byte ordering is big-endian.
|
||||
|
||||
## Accessing the I/O Area
|
||||
The I/O area uses the same word addressing in increments of four to access the registers of the I/O controllers. In practice, only the VGA framebuffer controller and the audio controller use multiple registers.
|
||||
For the other controllers, there is a single 32 bit register that is repeated all over the address space of the corresponding I/O slot.
|
||||
The I/O area organizes memory slightly different. Here, pointing out individual bytes is not very useful, so the I/O controllers use register addresses with increments of one. In practice, there is only the VGA framebuffer controller which uses multiple registers.
|
||||
|
||||
The individual I/O controllers each have a memory area of 128 bytes, so there is a maximum number of 16 I/O controllers.
|
||||
|
||||
Currently, only I/O slots 0-4 are being used.
|
||||
Currently, only I/O slots 0-3 are being used.
|
||||
|
||||
|I/O slot| Address | Controller |
|
||||
|--------|---------|------------|
|
||||
|
|
|
|||
|
|
@ -10,12 +10,12 @@ For the first channel the register addresses are:
|
|||
|Address|Description|
|
||||
|-------|-----------|
|
||||
| $A00 | Control Register |
|
||||
| $A04 | Clock Divider Register |
|
||||
| $A08 | Amplitude Register |
|
||||
| $A01 | Clock Divider Register |
|
||||
| $A02 | Amplitude Register |
|
||||
|
||||
The register addresses for the second channel start at $A10,
|
||||
the third channel at $A20
|
||||
and the fourth channel at $A30.
|
||||
The register addresses for the second channel start at $A04,
|
||||
the third channel at $A08
|
||||
and the fourth channel at $A0C.
|
||||
|
||||
## Reading the control register
|
||||
|
||||
|
|
|
|||
68
doc/vga.md
68
doc/vga.md
|
|
@ -4,16 +4,13 @@ Registers
|
|||
|Name|Address|Description|
|
||||
|----|-------|-----------|
|
||||
|_FB_RA_ | $900 | Read Address |
|
||||
|_FB_WA_ | $904 | Write Address |
|
||||
| _FB_IO_ | $908 | I/O Register |
|
||||
| _FB_PS_ | $90C | Palette Select |
|
||||
| _FB_PD_ | $910 | Palette Data |
|
||||
| _FB_CTL_ | $914 | Control Register |
|
||||
| _FB_SHIFTER | $918 | Shift Assist Register |
|
||||
| _FB_SHIFTCOUNT | $91C | Shift Count Register |
|
||||
| _FB_SHIFTERM | $920 | Shifted Mask Register |
|
||||
| _FB_SHIFTERSP | $924 | Shifter Spill Register |
|
||||
| _FB_MASKGEN | $928 | Mask Generator Register |
|
||||
|_FB_WA_ | $901 | Write Address |
|
||||
| _FB_IO_ | $902 | I/O Register |
|
||||
| _FB_PS_ | $903 | Palette Select |
|
||||
| _FB_PD_ | $904 | Palette Data |
|
||||
| _FB_CTL_ | $905 | Control Register |
|
||||
|
||||
|
||||
|
||||
## Pixel Data
|
||||
Pixel data is organized in 32-bit-words. With four bits per pixel, one word
|
||||
|
|
@ -84,54 +81,3 @@ The control register contains status information. It can only be read.
|
|||
The _m_ field indicates the current graphics mode. At the time of writing, it is
|
||||
always 1 which denotes a 640x400x4 mode.
|
||||
The _vb_ bit is 1 when the video signal generator is in its vertical blank phase.
|
||||
|
||||
## Shift Assist Register
|
||||
The *shift assist register* can be used to accelerate shifting pixel/bitmap data.
|
||||
Writing a word of pixel data to this register initialises the shifting process.
|
||||
|
||||
After writing to the shift count register (see below), reading the shift assist
|
||||
register retrieves the shifted pixel data.
|
||||
|
||||
Writing to the shift assist register will reset the shift count.
|
||||
|
||||
## Shift Count Register
|
||||
Writing a number from 0-7 to the *shift count register* triggers shifting the
|
||||
contents of the shift assist register. Pixel data is shifted by four bits
|
||||
to the right times the shift count. Bits 31-3 of the shift count are ignored, so you can
|
||||
directly write a horizontal screen coordinate to the register.
|
||||
|
||||
This register cannot be read.
|
||||
|
||||
## Shifter Mask Register
|
||||
The *shifter mask register* contains the shifted pixel data converted into
|
||||
a mask. See the *mask generator register* for an
|
||||
explanation of the mask.
|
||||
|
||||
## Shifter Spill Register
|
||||
The *shifter spill register* contains the pixel data that has
|
||||
been shifted out to the right. For example, if the shift count is two,
|
||||
the spill register contains the two rightmost pixels (bits 7-0) of
|
||||
the original pixel data, placed into the two topmost pixels (bits 31-24).
|
||||
|
||||
The rest of the register is set to zero.
|
||||
|
||||
## Mask Generator Register
|
||||
The *mask generator register* creates a mask from pixel data.
|
||||
For each four bits of a pixel, the corresponding four mask bits
|
||||
are all set to one if the pixel value is not zero.
|
||||
|
||||
This can be used to combine foreground and background pixel data
|
||||
with a pixel value of zero for a transparent background color.
|
||||
|
||||
Usually, the mask will be inverted with a *NOT* instruction
|
||||
to clear all pixels in the background that are set in the foreground
|
||||
with an *AND* instruction
|
||||
before *ORing* foreground and background together.
|
||||
|
||||
Example in hexadecimal, each digit is a pixel:
|
||||
| Pixel Data | Mask |
|
||||
|------------|------|
|
||||
| $00000000 | $00000000 |
|
||||
| $00000001 | $0000000F |
|
||||
| $0407000F | $0F0F000F |
|
||||
| $1234ABC0 | $FFFFFFF0 |
|
||||
|
|
|
|||
|
|
@ -123,11 +123,11 @@ FF_EXIT:
|
|||
|
||||
; framebuffer controller registers
|
||||
.EQU FB_RA $900
|
||||
.EQU FB_WA $904
|
||||
.EQU FB_IO $908
|
||||
.EQU FB_PS $90C
|
||||
.EQU FB_PD $910
|
||||
.EQU FB_CTL $914
|
||||
.EQU FB_WA $901
|
||||
.EQU FB_IO $902
|
||||
.EQU FB_PS $903
|
||||
.EQU FB_PD $904
|
||||
.EQU FB_CTL $905
|
||||
.EQU WORDS_PER_LINE 80
|
||||
|
||||
; fire width in vmem words (strict left-to-right evaluation)
|
||||
|
|
|
|||
|
|
@ -1,125 +0,0 @@
|
|||
program graphbench;
|
||||
uses sprites;
|
||||
|
||||
var starttime,endtime:DateTime;
|
||||
spriteData:SpritePixels;
|
||||
|
||||
procedure readSpriteData(filename:string);
|
||||
var f:file;
|
||||
begin
|
||||
open(f,filename,ModeReadOnly);
|
||||
seek(f,8); (* skip file header *)
|
||||
read(f,spriteData);
|
||||
close(f);
|
||||
end;
|
||||
|
||||
procedure startBench(name:string);
|
||||
begin
|
||||
write(name:20, ' ');
|
||||
starttime := GetTime;
|
||||
end;
|
||||
|
||||
procedure endBench;
|
||||
var secDelta, minDelta, hourDelta:integer;
|
||||
procedure write2Digits(i:integer);
|
||||
begin
|
||||
if i < 10 then
|
||||
write('0');
|
||||
write(i);
|
||||
end;
|
||||
begin
|
||||
endTime := GetTime;
|
||||
|
||||
hourDelta := endtime.hours - starttime.hours;
|
||||
minDelta := endtime.minutes - starttime.minutes;
|
||||
secDelta := endtime.seconds - starttime.seconds;
|
||||
|
||||
if secDelta < 0 then
|
||||
begin
|
||||
secDelta := 60 + secDelta;
|
||||
minDelta := minDelta - 1;
|
||||
end;
|
||||
|
||||
if minDelta < 0 then
|
||||
begin
|
||||
minDelta := 60 + minDelta;
|
||||
hourDelta := hourDelta - 1;
|
||||
end;
|
||||
|
||||
write2Digits(hourDelta);
|
||||
write(':'); write2Digits(minDelta);
|
||||
write(':'); write2Digits(secDelta);
|
||||
writeln;
|
||||
end;
|
||||
|
||||
function randint(lessthan:integer):integer;
|
||||
var r:integer;
|
||||
begin
|
||||
r := random and 511;
|
||||
if r >= lessthan then
|
||||
r := r - lessthan;
|
||||
randint := r;
|
||||
end;
|
||||
|
||||
procedure drawsprites(count:integer);
|
||||
var i,col,x,y:integer;
|
||||
begin
|
||||
col := 1;
|
||||
for i := 1 to count do
|
||||
begin
|
||||
x := randint(350);
|
||||
y := randint(350);
|
||||
PutSprite(x,y,spriteData);
|
||||
col := col + 1;
|
||||
if col > 15 then col := 1;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure drawlines(count:integer);
|
||||
var i,col,x1,y1,x2,y2:integer;
|
||||
begin
|
||||
col := 1;
|
||||
for i := 1 to count do
|
||||
begin
|
||||
x1 := randint(500);
|
||||
y1 := randint(400);
|
||||
x2 := randint(500);
|
||||
y2 := randint(400);
|
||||
DrawLine(x1,y1,x2,y2,col);
|
||||
col := col + 1;
|
||||
if col > 15 then col := 1;
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure drawpoints(count:integer);
|
||||
var i,col,x,y:integer;
|
||||
begin
|
||||
col := 1;
|
||||
for i := 1 to count do
|
||||
begin
|
||||
x := randint(500);
|
||||
y := randint(400);
|
||||
PutPixel(x,y,col);
|
||||
col := col + 1;
|
||||
if col > 15 then col := 1;
|
||||
end;
|
||||
end;
|
||||
|
||||
begin
|
||||
readSpriteData('rocket.sprt');
|
||||
|
||||
InitGraphics;
|
||||
startBench('points 200K');
|
||||
drawpoints(200000);
|
||||
endBench;
|
||||
|
||||
InitGraphics;
|
||||
startBench('lines 10K');
|
||||
drawlines(10000);
|
||||
endBench;
|
||||
|
||||
InitGraphics;
|
||||
startBench('sprites 50K');
|
||||
drawsprites(50000);
|
||||
endBench;
|
||||
end.
|
||||
|
|
@ -3,16 +3,31 @@
|
|||
|
||||
.EQU WORDS_PER_LINE 80
|
||||
.EQU FB_RA $900
|
||||
.EQU FB_WA $904
|
||||
.EQU FB_IO $908
|
||||
.EQU FB_PS $90C
|
||||
.EQU FB_PD $910
|
||||
.EQU FB_CTL $914
|
||||
.EQU FB_SHIFTER $918
|
||||
.EQU FB_SHIFTCOUNT $91C
|
||||
.EQU FB_SHIFTERM $920
|
||||
.EQU FB_SHIFTERSP $924
|
||||
.EQU FB_MASKGEN $928
|
||||
.EQU FB_WA $901
|
||||
.EQU FB_IO $902
|
||||
.EQU FB_PS $903
|
||||
|
||||
; calculate mask for a word of pixels
|
||||
; args: word of pixels with four bits per pixel
|
||||
; returns: value that masks out all pixels that are set
|
||||
CALC_MASK:
|
||||
LOADC $F ; pixel mask
|
||||
C_M_L0:
|
||||
SWAP ; swap mask and pixels value
|
||||
AND.S1.X2Y ; isolate one pixel, keep args
|
||||
CBRANCH.Z C_M_L1 ; if pixel is zero, dont set mask bits
|
||||
OVER ; copy current mask
|
||||
OR ; or into pixels value
|
||||
C_M_L1:
|
||||
SWAP ; swap back, ToS is now mask bits
|
||||
SHL 2 ; shift mask for next pixel to the left
|
||||
SHL 2
|
||||
|
||||
DUP
|
||||
CBRANCH.NZ C_M_L0 ; if mask is zero, we are done
|
||||
DROP ; remove mask bits
|
||||
NOT ; invert result
|
||||
RET
|
||||
|
||||
; calculate vmem address from coordinates
|
||||
; args: x,y
|
||||
|
|
@ -52,19 +67,13 @@ CALC_VMEM_ADDR:
|
|||
.EQU PS_SHIFT_C 20
|
||||
.EQU PS_SPILL 24
|
||||
.EQU PS_STRIPE_C 28
|
||||
.EQU PS_BPSAVE 32
|
||||
.EQU PS_FS 36
|
||||
.EQU PS_FS 32
|
||||
PUTSPRITE:
|
||||
FPADJ -PS_FS
|
||||
STORE PS_SPRITE_DATA
|
||||
STORE PS_Y
|
||||
STORE PS_X
|
||||
|
||||
LOADREG BP
|
||||
STORE PS_BPSAVE
|
||||
LOADC 0
|
||||
STOREREG BP
|
||||
|
||||
; calculate vmem address
|
||||
LOAD PS_X
|
||||
LOAD PS_Y
|
||||
|
|
@ -72,6 +81,11 @@ PUTSPRITE:
|
|||
CALL
|
||||
STORE PS_VMEM_ADDR
|
||||
|
||||
LOAD PS_X ; shift count = x mod 8
|
||||
LOADC 7
|
||||
AND
|
||||
STORE PS_SHIFT_C
|
||||
|
||||
LOADC SPRITE_HEIGHT
|
||||
STORE PS_SPRITE_LINES
|
||||
|
||||
|
|
@ -79,10 +93,12 @@ PUTSPRITE:
|
|||
PS_LOOP1:
|
||||
; set read and write address
|
||||
; in the vga controller
|
||||
LOADC FB_RA ; read address register
|
||||
LOAD PS_VMEM_ADDR
|
||||
DUP
|
||||
STORE.B FB_RA
|
||||
STORE.B FB_WA
|
||||
STOREI 1 ; use autoincrement to get to the next register
|
||||
LOAD PS_VMEM_ADDR
|
||||
STOREI
|
||||
DROP
|
||||
|
||||
LOAD PS_SPRITE_DATA ; address of sprite data
|
||||
DUP
|
||||
|
|
@ -90,20 +106,61 @@ PS_LOOP1:
|
|||
STORE PS_SPRITE_DATA ; and store it again
|
||||
LOADI ; load word from orig. address
|
||||
|
||||
; ------- one word of sprite pixels on stack
|
||||
|
||||
STORE.B FB_SHIFTER
|
||||
LOAD PS_X
|
||||
STORE.B FB_SHIFTCOUNT
|
||||
LOADC 0
|
||||
STORE PS_SPILL
|
||||
|
||||
LOAD.B FB_SHIFTERM ; get shifted mask
|
||||
NOT
|
||||
LOAD.B FB_IO ; and background pixel data
|
||||
AND ; remove foreground pixels
|
||||
; loop to shift pixel data to right
|
||||
LOAD PS_SHIFT_C ; load shift count
|
||||
PS_LOOP2:
|
||||
DUP ; test it for zero
|
||||
CBRANCH.Z PS_LOOP2_X
|
||||
|
||||
LOAD.B FB_SHIFTER ; get shifted pixels
|
||||
OR ; combine with background
|
||||
STORE.B FB_IO ; store into vmem
|
||||
SWAP ; swap count with pixels
|
||||
|
||||
; save the pixel that is shifted out
|
||||
LOADC $F ; mask the four bits
|
||||
AND.S0 ; keep original value on stack
|
||||
BROT ; and move them to MSB
|
||||
BROT
|
||||
BROT
|
||||
SHL 2
|
||||
SHL 2 ; shift by 28 in total
|
||||
|
||||
LOAD PS_SPILL ; load spill bits
|
||||
SHR ; shift by four to make space
|
||||
SHR
|
||||
SHR
|
||||
SHR
|
||||
OR ; or with orig value
|
||||
STORE PS_SPILL ; store new value
|
||||
|
||||
SHR ; shift pixels right
|
||||
SHR ; four bits per pixel
|
||||
SHR
|
||||
SHR
|
||||
|
||||
SWAP ; swap back, count now ToS
|
||||
DEC 1
|
||||
BRANCH PS_LOOP2
|
||||
PS_LOOP2_X:
|
||||
DROP ; remove shift count, shifted pixels now in ToS
|
||||
|
||||
DUP
|
||||
LOADCP CALC_MASK ; calculate sprite mask for this word
|
||||
CALL
|
||||
|
||||
LOADCP FB_IO ; address of the i/o register
|
||||
LOADI ; read word from video mem
|
||||
|
||||
AND ; and word with mask
|
||||
|
||||
OR ; OR sprite data with original pixels
|
||||
|
||||
LOADCP FB_IO
|
||||
SWAP
|
||||
STOREI ; store result into i/o reg
|
||||
DROP
|
||||
|
||||
; set counter for remaining stripes
|
||||
LOADC SPRITE_STRIPES - 1
|
||||
|
|
@ -113,8 +170,8 @@ PS_LOOP1:
|
|||
; process spilled bits and next vertical stripe of sprite data
|
||||
;
|
||||
PS_NEXT_STRIPE:
|
||||
;use spill bits from first column
|
||||
LOAD.B FB_SHIFTERSP
|
||||
; put spill bits on stack for later
|
||||
LOAD PS_SPILL
|
||||
|
||||
LOAD PS_SPRITE_DATA ; address of sprite data
|
||||
DUP
|
||||
|
|
@ -122,21 +179,65 @@ PS_NEXT_STRIPE:
|
|||
STORE PS_SPRITE_DATA ; and store it again
|
||||
LOADI ; load word from orig. address
|
||||
|
||||
STORE.B FB_SHIFTER ; store into shifter
|
||||
LOAD PS_X
|
||||
STORE.B FB_SHIFTCOUNT ; shift stuff
|
||||
LOAD.B FB_SHIFTER ; get shifted pixels
|
||||
OR ; combine with spill bits (see above)
|
||||
; reset spill bits
|
||||
LOADC 0
|
||||
STORE PS_SPILL
|
||||
|
||||
; last spill bits are on ToS now
|
||||
|
||||
; shift pixel data to right
|
||||
LOAD PS_SHIFT_C ; load shift count
|
||||
PS_LOOP3: ; test it for zero
|
||||
DUP
|
||||
STORE.B FB_MASKGEN ; store to mask reg to get new mask
|
||||
CBRANCH.Z PS_LOOP3_X
|
||||
|
||||
LOAD.B FB_MASKGEN ; get mask for spill bits + shifted pixels
|
||||
NOT
|
||||
LOAD.B FB_IO ; get vmem data
|
||||
AND ; remove foreground pixels from bg
|
||||
SWAP ; swap count with pixels
|
||||
|
||||
OR ; combine with shifted pixels
|
||||
STORE.B FB_IO ; write to vmem
|
||||
; save the pixel that is shifted out
|
||||
LOADC $F ; mask the four bits
|
||||
AND.S0 ; keep original value on stack
|
||||
BROT ; and move them to MSB
|
||||
BROT
|
||||
BROT
|
||||
SHL 2
|
||||
SHL 2 ; shift by 28 in total
|
||||
|
||||
LOAD PS_SPILL ; load spill bits
|
||||
SHR ; shift by four to make space
|
||||
SHR
|
||||
SHR
|
||||
SHR
|
||||
OR ; or with orig value
|
||||
STORE PS_SPILL ; store new value
|
||||
|
||||
SHR ; shift pixels right
|
||||
SHR ; four bits per pixel
|
||||
SHR
|
||||
SHR
|
||||
|
||||
SWAP ; swap back, count now ToS
|
||||
DEC 1
|
||||
BRANCH PS_LOOP3
|
||||
PS_LOOP3_X:
|
||||
DROP ; remove shift count, shifted pixels now in ToS
|
||||
|
||||
OR ; or together with spill bits
|
||||
|
||||
DUP
|
||||
LOADCP CALC_MASK ; calculate sprite mask
|
||||
CALL
|
||||
|
||||
LOADCP FB_IO ; load original pixels
|
||||
LOADI
|
||||
|
||||
AND ; and with mask
|
||||
|
||||
OR ; or together with original pixels
|
||||
|
||||
LOADCP FB_IO
|
||||
SWAP
|
||||
STOREI
|
||||
DROP
|
||||
|
||||
LOAD PS_STRIPE_C ; decrement stripe count
|
||||
DEC 1
|
||||
|
|
@ -145,19 +246,22 @@ PS_NEXT_STRIPE:
|
|||
CBRANCH.NZ PS_NEXT_STRIPE ; if non-zero, next stripe
|
||||
|
||||
; write spilled bits of the last stripe into next vmem word
|
||||
LOAD.B FB_SHIFTERSP ; get spill bits
|
||||
LOAD PS_SPILL ; get spill bits
|
||||
DUP
|
||||
STORE.B FB_MASKGEN
|
||||
LOAD.B FB_MASKGEN ; get sprite mask for spill bits
|
||||
NOT
|
||||
LOADCP CALC_MASK ; calculate sprite mask for spill bits
|
||||
CALL
|
||||
|
||||
LOAD.B FB_IO ; load next vmem word
|
||||
LOADCP FB_IO
|
||||
LOADI ; load next vmem word
|
||||
AND ; apply sprite mask
|
||||
|
||||
OR ; OR in spill bits
|
||||
|
||||
STORE.B FB_IO ; write to vmem
|
||||
|
||||
LOADCP FB_IO
|
||||
SWAP ; swap pixels and addr
|
||||
STOREI ; write back
|
||||
DROP
|
||||
|
||||
LOAD PS_SPRITE_LINES ; decrement lines count
|
||||
DEC 1
|
||||
DUP
|
||||
|
|
@ -171,10 +275,7 @@ PS_NEXT_STRIPE:
|
|||
BRANCH PS_LOOP1
|
||||
PS_L_XT:
|
||||
DROP
|
||||
|
||||
LOAD PS_BPSAVE
|
||||
STOREREG BP
|
||||
|
||||
|
||||
FPADJ PS_FS
|
||||
RET
|
||||
|
||||
|
|
@ -221,7 +322,7 @@ UD_S_L1:
|
|||
; store vmem offset into write addr reg
|
||||
LOADCP FB_WA
|
||||
LOAD UD_S_OFFSET
|
||||
STOREI 4 ; ugly but fast: reuse addr
|
||||
STOREI 1 ; ugly but fast: reuse addr
|
||||
; with postincrement to
|
||||
; get to FB_IO for STOREI below
|
||||
|
||||
|
|
|
|||
216
lib/corelib.s
216
lib/corelib.s
|
|
@ -701,37 +701,113 @@ CMPWORDS_XT2:
|
|||
; --------- Graphics Library ---------------
|
||||
; vga controller registers
|
||||
.EQU FB_RA $900
|
||||
.EQU FB_WA $904
|
||||
.EQU FB_IO $908
|
||||
.EQU FB_PS $90C
|
||||
.EQU FB_PD $910
|
||||
.EQU FB_CTL $914
|
||||
.EQU FB_SHIFTER $918
|
||||
.EQU FB_SHIFTCOUNT $91C
|
||||
.EQU FB_SHIFTERM $920
|
||||
.EQU FB_SHIFTERSP $924
|
||||
.EQU FB_MASKGEN $928
|
||||
.EQU FB_WA $901
|
||||
.EQU FB_IO $902
|
||||
.EQU FB_PS $903
|
||||
.EQU FB_PD $904
|
||||
.EQU FB_CTL $905
|
||||
; set a pixel in fb memory
|
||||
; parameters: x,y - coordinates
|
||||
PUTPIXEL_1BPP:
|
||||
; calculate vmem address:
|
||||
OVER ; duplicate x
|
||||
; divide x by 32
|
||||
SHR
|
||||
SHR
|
||||
SHR
|
||||
SHR
|
||||
SHR
|
||||
SWAP
|
||||
; multiply y by words per line
|
||||
SHL 2
|
||||
SHL 2
|
||||
SHL
|
||||
|
||||
; draw a single pixel
|
||||
; args: x, y, color
|
||||
ADD ; add results together for vmem addr
|
||||
|
||||
DUP
|
||||
LOADCP FB_WA
|
||||
SWAP
|
||||
STOREI ; store to framebuffer write addr register
|
||||
DROP
|
||||
LOADCP FB_RA ; and to framebuffer read addr register
|
||||
SWAP
|
||||
STOREI
|
||||
DROP
|
||||
|
||||
; x is now at top of stack
|
||||
; get bit value from x modulo 32
|
||||
LOADC 31
|
||||
AND
|
||||
SHL 2 ; (x & 31) * 4 = offset into table
|
||||
LOADCP INT_TO_PIX_TABLE
|
||||
ADD
|
||||
LOADI
|
||||
|
||||
LOADCP FB_IO
|
||||
; read old vmem value
|
||||
LOADCP FB_IO
|
||||
LOADI
|
||||
; or in new bit
|
||||
OR
|
||||
; write new value
|
||||
STOREI
|
||||
DROP
|
||||
|
||||
RET
|
||||
|
||||
INT_TO_PIX_TABLE:
|
||||
.WORD %10000000_00000000_00000000_00000000
|
||||
.WORD %01000000_00000000_00000000_00000000
|
||||
.WORD %00100000_00000000_00000000_00000000
|
||||
.WORD %00010000_00000000_00000000_00000000
|
||||
.WORD %00001000_00000000_00000000_00000000
|
||||
.WORD %00000100_00000000_00000000_00000000
|
||||
.WORD %00000010_00000000_00000000_00000000
|
||||
.WORD %00000001_00000000_00000000_00000000
|
||||
.WORD %00000000_10000000_00000000_00000000
|
||||
.WORD %00000000_01000000_00000000_00000000
|
||||
.WORD %00000000_00100000_00000000_00000000
|
||||
.WORD %00000000_00010000_00000000_00000000
|
||||
.WORD %00000000_00001000_00000000_00000000
|
||||
.WORD %00000000_00000100_00000000_00000000
|
||||
.WORD %00000000_00000010_00000000_00000000
|
||||
.WORD %00000000_00000001_00000000_00000000
|
||||
.WORD %00000000_00000000_10000000_00000000
|
||||
.WORD %00000000_00000000_01000000_00000000
|
||||
.WORD %00000000_00000000_00100000_00000000
|
||||
.WORD %00000000_00000000_00010000_00000000
|
||||
.WORD %00000000_00000000_00001000_00000000
|
||||
.WORD %00000000_00000000_00000100_00000000
|
||||
.WORD %00000000_00000000_00000010_00000000
|
||||
.WORD %00000000_00000000_00000001_00000000
|
||||
.WORD %00000000_00000000_00000000_10000000
|
||||
.WORD %00000000_00000000_00000000_01000000
|
||||
.WORD %00000000_00000000_00000000_00100000
|
||||
.WORD %00000000_00000000_00000000_00010000
|
||||
.WORD %00000000_00000000_00000000_00001000
|
||||
.WORD %00000000_00000000_00000000_00000100
|
||||
.WORD %00000000_00000000_00000000_00000010
|
||||
.WORD %00000000_00000000_00000000_00000001
|
||||
|
||||
PUTMPIXEL:
|
||||
LOADC 1
|
||||
; set a pixel in fb memory
|
||||
; parameters: x,y,color - coordinates, color value (0-15)
|
||||
PUTPIXEL:
|
||||
PUTPIXEL_4BPP:
|
||||
.EQU PUTPIXEL_X 0
|
||||
.EQU PUTPIXEL_Y 4
|
||||
.EQU PUTPIXEL_COLOR 8
|
||||
.EQU PUTPIXEL_BPSAV 12
|
||||
.EQU PUTPIXEL_PIXPOS 12
|
||||
.EQU PUTPIXEL_FS 16
|
||||
|
||||
FPADJ -PUTPIXEL_FS
|
||||
|
||||
STORE PUTPIXEL_COLOR
|
||||
STORE PUTPIXEL_Y
|
||||
STORE PUTPIXEL_X
|
||||
|
||||
LOADREG BP
|
||||
STORE PUTPIXEL_BPSAV
|
||||
LOADC 0
|
||||
STOREREG BP
|
||||
|
||||
; calculate vmem address: (x / 8) + (y * 80)
|
||||
LOAD PUTPIXEL_X
|
||||
|
|
@ -750,56 +826,82 @@ PUTPIXEL_4BPP:
|
|||
|
||||
ADD ; add results together for vmem addr
|
||||
|
||||
DUP
|
||||
STORE.B FB_WA ; set as write and read addresses
|
||||
STORE.B FB_RA
|
||||
LOADCP FB_WA
|
||||
OVER
|
||||
STOREI ; store to framebuffer write addr register
|
||||
DROP
|
||||
LOADCP FB_RA ; and to framebuffer read addr register
|
||||
SWAP ; swap addr and value for STOREI
|
||||
STOREI
|
||||
DROP
|
||||
|
||||
LOAD PUTPIXEL_X
|
||||
; |0000.0000|0000.0000|0000.0000|0000.1111|
|
||||
LOADC 7
|
||||
AND ; calculate pixel position in word
|
||||
LOADC 7
|
||||
SWAP
|
||||
SUB ; pixpos = 7 - (x & 7)
|
||||
STORE PUTPIXEL_PIXPOS
|
||||
|
||||
LOAD PUTPIXEL_COLOR
|
||||
CBRANCH.Z PUTPX_CLR ; color 0 is special case
|
||||
|
||||
; create pixel data from color value in
|
||||
; leftmost pixel data bits (31-28)
|
||||
LOADC 0
|
||||
LOAD PUTPIXEL_COLOR
|
||||
BPLC
|
||||
LOAD PUTPIXEL_PIXPOS
|
||||
SHR ; rcount = pixpos / 2
|
||||
ROTLOOP_:
|
||||
DUP ; exit loop if rcount is 0
|
||||
CBRANCH.Z ROTLOOP_END
|
||||
SWAP ; pixel value is now on top of stack
|
||||
BROT ; value = value << 8
|
||||
SWAP ; rcount is now on top of stack
|
||||
DEC 1 ; rcount = rcount - 1
|
||||
BRANCH ROTLOOP_
|
||||
ROTLOOP_END:
|
||||
DROP ; drop rcount
|
||||
; shifted pixel value is now at top of stack
|
||||
LOAD PUTPIXEL_PIXPOS
|
||||
LOADC 1
|
||||
AND
|
||||
CBRANCH.Z EVEN_PIXPOS
|
||||
SHL 2 ; if pixpos is odd, shift by 4 bits
|
||||
SHL 2
|
||||
SHL 2
|
||||
STORE.B FB_SHIFTER ; store pixel into shifter
|
||||
EVEN_PIXPOS:
|
||||
LOAD PUTPIXEL_X
|
||||
; get bit value from x modulo 8
|
||||
LOADC 7
|
||||
AND
|
||||
SHL 2 ; (x & 7) * 4 = offset into table
|
||||
LOADCP INT_TO_MASK_TABLE
|
||||
ADD
|
||||
LOADI
|
||||
|
||||
LOAD PUTPIXEL_X ; use x coord as shift count
|
||||
STORE.B FB_SHIFTCOUNT ; writing triggers shifting
|
||||
; read old vmem value
|
||||
LOADCP FB_IO
|
||||
LOADI
|
||||
; mask bits
|
||||
AND
|
||||
; or in shifted pixel value
|
||||
OR
|
||||
|
||||
LOAD.B FB_SHIFTERM ; get shift result as mask
|
||||
NOT ; invert to get background mask
|
||||
LOAD.B FB_IO ; get background pixel data
|
||||
AND ; remove bits for new pixel from bg
|
||||
|
||||
LOAD.B FB_SHIFTER ; load shifted pixel
|
||||
OR ; OR in new pixel bits
|
||||
STORE.B FB_IO ; write new pixel data word to vmem
|
||||
|
||||
PUTPX_XT:
|
||||
LOAD PUTPIXEL_BPSAV
|
||||
STOREREG BP
|
||||
; write new value
|
||||
LOADCP FB_IO
|
||||
SWAP
|
||||
STOREI
|
||||
DROP
|
||||
|
||||
FPADJ PUTPIXEL_FS
|
||||
RET
|
||||
|
||||
PUTPX_CLR:
|
||||
LOADCP $F0000000 ; mask for leftmost pixel
|
||||
STORE.B FB_SHIFTER ; shift accordingly
|
||||
LOAD PUTPIXEL_X
|
||||
STORE.B FB_SHIFTCOUNT
|
||||
|
||||
LOAD.B FB_SHIFTER ; get shifted value
|
||||
NOT ; invert for real mask
|
||||
LOAD.B FB_IO ; get background pixels
|
||||
AND ; clear pixel with mask
|
||||
STORE.B FB_IO ; no need to OR in new pixel, just store to vmem
|
||||
|
||||
BRANCH PUTPX_XT
|
||||
|
||||
.CPOOL
|
||||
|
||||
INT_TO_MASK_TABLE:
|
||||
.WORD %00001111_11111111_11111111_11111111
|
||||
.WORD %11110000_11111111_11111111_11111111
|
||||
.WORD %11111111_00001111_11111111_11111111
|
||||
.WORD %11111111_11110000_11111111_11111111
|
||||
.WORD %11111111_11111111_00001111_11111111
|
||||
.WORD %11111111_11111111_11110000_11111111
|
||||
.WORD %11111111_11111111_11111111_00001111
|
||||
.WORD %11111111_11111111_11111111_11110000
|
||||
|
||||
; draw a line between two points
|
||||
; parameters: x0, y0, x1, y1, color
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ START_PCMAUDIO:
|
|||
LOADCP _DIV
|
||||
CALL
|
||||
|
||||
LOADC AUDIO_BASE + 4
|
||||
LOADC AUDIO_BASE + 1
|
||||
SWAP ; put clock divider on ToS
|
||||
STOREI 4
|
||||
STOREI 1
|
||||
LOADCP 32768 ; set amplitude to biased 0
|
||||
STOREI
|
||||
DROP
|
||||
|
|
@ -95,7 +95,7 @@ PLAY1_L0:
|
|||
AND
|
||||
CBRANCH.NZ PLAY1_L0 ; loop if fifo is full
|
||||
|
||||
LOADC AUDIO_BASE+8 ; store amplitude value
|
||||
LOADC AUDIO_BASE+2 ; store amplitude value
|
||||
SWAP
|
||||
STOREI
|
||||
DROP
|
||||
|
|
@ -207,7 +207,7 @@ SMPLQ_I_B:
|
|||
LOADCP $FFFF
|
||||
AND
|
||||
|
||||
LOADC AUDIO_BASE+8
|
||||
LOADC AUDIO_BASE+2
|
||||
SWAP
|
||||
STOREI ; write sample, keep addr
|
||||
|
||||
|
|
@ -281,7 +281,7 @@ SMPLQ_I_END1:
|
|||
DROP
|
||||
|
||||
; set amplitude out to zero (biased)
|
||||
LOADC AUDIO_BASE+8
|
||||
LOADC AUDIO_BASE+2
|
||||
LOADCP 32768
|
||||
STOREI
|
||||
DROP
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ module top(
|
|||
assign fb_wr_data = mem_write_data;
|
||||
|
||||
vgafb vgafb0(`clock, pixclk, rst,
|
||||
mem_addr[5:2], fb_rd_data, fb_wr_data,
|
||||
mem_addr[3:0], fb_rd_data, fb_wr_data,
|
||||
fb_rd_en, fb_wr_en,
|
||||
VGA_HS_O, VGA_VS_O, VGA_R, VGA_G, VGA_B);
|
||||
`endif
|
||||
|
|
@ -247,7 +247,7 @@ module top(
|
|||
assign tdraudio_wr_data = mem_write_data;
|
||||
|
||||
tdraudio tdraudio0(`clock, ~rst,
|
||||
mem_addr[8:2],
|
||||
mem_addr[6:0],
|
||||
tdraudio_rd_data,
|
||||
tdraudio_wr_data,
|
||||
tdraudio_rd_en,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
`timescale 1ns / 1ps
|
||||
`default_nettype none
|
||||
|
||||
// enable shifter/masker registers
|
||||
`define ENABLE_FB_ACCEL
|
||||
|
||||
// Project F: Display Timings
|
||||
// (C)2019 Will Green, Open Source Hardware released under the MIT License
|
||||
// Learn more at https://projectf.io
|
||||
|
|
@ -129,14 +126,6 @@ module vgafb #(VMEM_ADDR_WIDTH = 15, VMEM_DATA_WIDTH = 32) (
|
|||
localparam REG_PAL_SLOT = 3; localparam REG_PAL_DATA = 4;
|
||||
localparam REG_CTL = 5;
|
||||
|
||||
`ifdef ENABLE_FB_ACCEL
|
||||
localparam REG_SHIFTER = 6;
|
||||
localparam REG_SHIFTCOUNT = 7;
|
||||
localparam REG_SHIFTERM = 8;
|
||||
localparam REG_SHIFTERSP = 09;
|
||||
localparam REG_MASKGEN = 10;
|
||||
`endif
|
||||
|
||||
localparam COLOR_WIDTH = 12;
|
||||
localparam PALETTE_WIDTH = 4;
|
||||
|
||||
|
|
@ -156,32 +145,12 @@ module vgafb #(VMEM_ADDR_WIDTH = 15, VMEM_DATA_WIDTH = 32) (
|
|||
wire pix_rd;
|
||||
wire [VMEM_DATA_WIDTH-1:0] status;
|
||||
|
||||
`ifdef ENABLE_FB_ACCEL
|
||||
reg [VMEM_DATA_WIDTH-1:0] acc_shifter_in;
|
||||
reg [(VMEM_DATA_WIDTH*2)-1:0] acc_shifter_out;
|
||||
reg [4:0] acc_shift_count;
|
||||
reg acc_start_shift;
|
||||
reg [VMEM_DATA_WIDTH-1:0] acc_mask_in;
|
||||
reg [VMEM_DATA_WIDTH-1:0] acc_mask_buf;
|
||||
reg [VMEM_DATA_WIDTH-1:0] acc_shiftmask_buf;
|
||||
wire [VMEM_DATA_WIDTH-1:0] acc_shifter_mask = acc_shiftmask_buf;
|
||||
wire [VMEM_DATA_WIDTH-1:0] acc_shifter_out_h = acc_shifter_out[(VMEM_DATA_WIDTH*2)-1:VMEM_DATA_WIDTH];
|
||||
wire [VMEM_DATA_WIDTH-1:0] acc_shifter_out_l = acc_shifter_out[VMEM_DATA_WIDTH-1:0];
|
||||
|
||||
`endif
|
||||
|
||||
assign vmem_rd_en = rd_en;
|
||||
assign vmem_wr_en = (reg_sel == REG_VMEM) && wr_en;
|
||||
assign rd_data = (reg_sel == REG_VMEM) ? vmem_rd_data :
|
||||
(reg_sel == REG_RD_ADDR) ? cpu_rd_addr :
|
||||
(reg_sel == REG_WR_ADDR) ? cpu_wr_addr :
|
||||
(reg_sel == REG_CTL) ? status :
|
||||
`ifdef ENABLE_FB_ACCEL
|
||||
(reg_sel == REG_SHIFTER) ? acc_shifter_out_h:
|
||||
(reg_sel == REG_SHIFTERM) ? acc_shiftmask_buf :
|
||||
(reg_sel == REG_SHIFTERSP) ? acc_shifter_out_l :
|
||||
(reg_sel == REG_MASKGEN) ? acc_mask_buf :
|
||||
`endif
|
||||
32'hFFFFFFFF;
|
||||
|
||||
wire [VMEM_ADDR_WIDTH-1:0] cpu_addr = vmem_wr_en ? cpu_wr_addr : cpu_rd_addr;
|
||||
|
|
@ -302,74 +271,6 @@ module vgafb #(VMEM_ADDR_WIDTH = 15, VMEM_DATA_WIDTH = 32) (
|
|||
if(rd_en && reg_sel == REG_VMEM) cpu_rd_addr <= cpu_rd_addr + 1; // auto-increment read addr on read
|
||||
end
|
||||
|
||||
`ifdef ENABLE_FB_ACCEL
|
||||
//
|
||||
// shifter/masker registers
|
||||
//
|
||||
always @(posedge cpu_clk)
|
||||
begin
|
||||
if(wr_en && reg_sel == REG_SHIFTER)
|
||||
acc_shifter_in <= wr_data;
|
||||
end
|
||||
|
||||
always @(posedge cpu_clk)
|
||||
begin
|
||||
if(wr_en && reg_sel == REG_SHIFTCOUNT)
|
||||
begin
|
||||
acc_shift_count <= { wr_data[2:0], 2'b0};
|
||||
acc_start_shift <= 1;
|
||||
end
|
||||
|
||||
if(acc_start_shift)
|
||||
acc_start_shift <= 0;
|
||||
end
|
||||
|
||||
always @(posedge cpu_clk)
|
||||
begin
|
||||
if (acc_start_shift)
|
||||
acc_shifter_out <= {acc_shifter_in, {VMEM_DATA_WIDTH{1'b0}}} >> acc_shift_count;
|
||||
end
|
||||
|
||||
// mask register
|
||||
always @(posedge cpu_clk)
|
||||
begin
|
||||
if (wr_en && reg_sel == REG_MASKGEN)
|
||||
acc_mask_in <= wr_data;
|
||||
end
|
||||
|
||||
// mask output is buffered to avoid timing problems
|
||||
always @(posedge cpu_clk)
|
||||
begin
|
||||
acc_mask_buf <= {
|
||||
{4{|{acc_mask_in[31:28]}}},
|
||||
{4{|{acc_mask_in[27:24]}}},
|
||||
{4{|{acc_mask_in[23:20]}}},
|
||||
{4{|{acc_mask_in[19:16]}}},
|
||||
{4{|{acc_mask_in[15:12]}}},
|
||||
{4{|{acc_mask_in[11:8]}}},
|
||||
{4{|{acc_mask_in[7:4]}}},
|
||||
{4{|{acc_mask_in[3:0]}}}
|
||||
};
|
||||
end
|
||||
|
||||
always @(posedge cpu_clk)
|
||||
begin
|
||||
acc_shiftmask_buf = {
|
||||
{4{|{acc_shifter_out_h[31:28]}}},
|
||||
{4{|{acc_shifter_out_h[27:24]}}},
|
||||
{4{|{acc_shifter_out_h[23:20]}}},
|
||||
{4{|{acc_shifter_out_h[19:16]}}},
|
||||
{4{|{acc_shifter_out_h[15:12]}}},
|
||||
{4{|{acc_shifter_out_h[11:8]}}},
|
||||
{4{|{acc_shifter_out_h[7:4]}}},
|
||||
{4{|{acc_shifter_out_h[3:0]}}}
|
||||
};
|
||||
end
|
||||
`endif
|
||||
|
||||
//
|
||||
// shifting pixels at pixel clock
|
||||
//
|
||||
always @(posedge pix_clk)
|
||||
begin
|
||||
if(scanline || shift_count == MAX_SHIFT_COUNT) // before start of a line
|
||||
|
|
|
|||
|
|
@ -358,9 +358,7 @@
|
|||
<Runs Version="1" Minor="22">
|
||||
<Run Id="synth_1" Type="Ft3:Synth" SrcSet="sources_1" Part="xc7a35ticsg324-1L" ConstrsSet="constrs_1" Description="Vivado Synthesis Defaults" AutoIncrementalCheckpoint="false" WriteIncrSynthDcp="false" State="current" Dir="$PRUNDIR/synth_1" IncludeInArchive="true" IsChild="false" AutoIncrementalDir="$PSRCDIR/utils_1/imports/synth_1" AutoRQSDir="$PSRCDIR/utils_1/imports/synth_1" ParallelReportGen="true">
|
||||
<Strategy Version="1" Minor="2">
|
||||
<StratHandle Name="Vivado Synthesis Defaults" Flow="Vivado Synthesis 2024">
|
||||
<Desc>Vivado Synthesis Defaults</Desc>
|
||||
</StratHandle>
|
||||
<StratHandle Name="Vivado Synthesis Defaults" Flow="Vivado Synthesis 2024"/>
|
||||
<Step Id="synth_design"/>
|
||||
</Strategy>
|
||||
<GeneratedRun Dir="$PRUNDIR" File="gen_run.xml"/>
|
||||
|
|
@ -380,9 +378,7 @@
|
|||
</Run>
|
||||
<Run Id="impl_1" Type="Ft2:EntireDesign" Part="xc7a35ticsg324-1L" ConstrsSet="constrs_1" Description="Default settings for Implementation." AutoIncrementalCheckpoint="false" WriteIncrSynthDcp="false" State="current" Dir="$PRUNDIR/impl_1" SynthRun="synth_1" IncludeInArchive="true" IsChild="false" GenFullBitstream="true" AutoIncrementalDir="$PSRCDIR/utils_1/imports/impl_1" LaunchOptions="-jobs 6 " AutoRQSDir="$PSRCDIR/utils_1/imports/impl_1" ParallelReportGen="true">
|
||||
<Strategy Version="1" Minor="2">
|
||||
<StratHandle Name="Vivado Implementation Defaults" Flow="Vivado Implementation 2024">
|
||||
<Desc>Default settings for Implementation.</Desc>
|
||||
</StratHandle>
|
||||
<StratHandle Name="Vivado Implementation Defaults" Flow="Vivado Implementation 2024"/>
|
||||
<Step Id="init_design"/>
|
||||
<Step Id="opt_design"/>
|
||||
<Step Id="power_opt_design"/>
|
||||
|
|
|
|||
|
|
@ -614,7 +614,6 @@ def create_image_with_stuff(imgfile):
|
|||
slotnr = putfile("../examples/benchmarks.pas", None , f, part, partstart, slotnr)
|
||||
|
||||
slotnr = putfile("../examples/animate.pas", None , f, part, partstart, slotnr)
|
||||
slotnr = putfile("../examples/graphbench.pas", None , f, part, partstart, slotnr)
|
||||
slotnr = putfile("../examples/sprites.inc", None , f, part, partstart, slotnr)
|
||||
slotnr = putfile("../examples/sprites.s", None , f, part, partstart, slotnr)
|
||||
slotnr = putfile("../examples/background.pict", None , f, part, partstart, slotnr)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue