Compare commits

..

No commits in common. "main" and "tdraudio-pcm" have entirely different histories.

32 changed files with 500 additions and 1552 deletions

View file

@ -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: Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

View file

@ -41,25 +41,6 @@ Other inspirations were, among others, in no particular order:
- the Magic-1 by Bill Buzbee - the Magic-1 by Bill Buzbee
- the OPC by revaldinho - the OPC by revaldinho
## October 2025 Update
This update introduces a data cache for the Tridora-CPU. It is similar to the instruction cache
as it caches the 16 bytes coming from the DRAM memory controller. It is a write-back cache, i.e.
when a word inside the cached area is written, it updates the cache instead of invalidating it.
This is important because there are many idioms in the stack machine assembly language where you
store a local variable and then read it again (e.g. updating a loop variable).
Since for most programs, the user stack and parts of the heap are inside the DRAM area, the data cache
has a more noticable impact. In the benchmark program that was already used for the last update,
the data cache results in a 50% improvement for the empty loop test. This is in comparison to the version
without data cache but with the instruction cache, both running code out of DRAM.
It is also noticable for compile times: With the data cache, compiling and assembling the
"hello,world" program takes 16 seconds instead of 20. With a little tweak of the SD-Card controller
that slightly increased the data transfer rate, the build time goes down to 15 seconds.
Also, an audio controller was added that allows interrupt-driven sample playback via an AMP2 PMOD.
## April 2025 Update ## April 2025 Update
The clock has been reduced to 77 MHz from 83 MHz. Apparently the design was at the limit and The clock has been reduced to 77 MHz from 83 MHz. Apparently the design was at the limit and
timing problems were cropping up seemingly at random. Reducing the clock speed made some timing problems were cropping up seemingly at random. Reducing the clock speed made some
@ -81,7 +62,7 @@ on the emulator image.
- the [Hackaday project](https://hackaday.io/project/198324-tridora-cpu) (mostly copy-paste from this README) - the [Hackaday project](https://hackaday.io/project/198324-tridora-cpu) (mostly copy-paste from this README)
- the [YouTube channel](https://www.youtube.com/@tridoracpu/videos) with some demo videos - the [YouTube channel](https://www.youtube.com/@tridoracpu/videos) with some demo videos
- the [emulator](https://git.insignificance.de/slederer/-/packages/generic/tridoraemu/0.0.5/files/12) (source and windows binary) - the [emulator](https://git.insignificance.de/slederer/-/packages/generic/tridoraemu/0.0.5/files/12) (source and windows binary)
- the [FPGA bitstream](https://git.insignificance.de/slederer/-/packages/generic/tdr-bitstream/0.0.4/files/16) for the Arty-A7-35T board - the [FPGA bitstream](https://git.insignificance.de/slederer/-/packages/generic/tdr-bitstream/0.0.2/files/14) for the Arty-A7-35T board
- an [SD-card image](https://git.insignificance.de/slederer/-/packages/generic/tdr-cardimage/0.0.4/files/13) - an [SD-card image](https://git.insignificance.de/slederer/-/packages/generic/tdr-cardimage/0.0.4/files/13)
Contact the author here: tridoracpu [at] insignificance.de Contact the author here: tridoracpu [at] insignificance.de

View file

@ -22,12 +22,11 @@ The _BSEL_ and _BPLC_ instructions are designed to assist with accessing bytes w
The byte ordering is big-endian. The byte ordering is big-endian.
## Accessing the I/O Area ## 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. 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.
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 individual I/O controllers each have a memory area of 128 bytes, so there is a maximum number of 16 I/O controllers. 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 | |I/O slot| Address | Controller |
|--------|---------|------------| |--------|---------|------------|
@ -35,4 +34,3 @@ Currently, only I/O slots 0-4 are being used.
| 1 | $880 | SPI-SD | | 1 | $880 | SPI-SD |
| 2 | $900 | VGA | | 2 | $900 | VGA |
| 3 | $980 | IRQC | | 3 | $980 | IRQC |
| 4 | $A00 | TDRAUDIO |

View file

@ -235,85 +235,6 @@ In Wirth Pascal, labels must be numbers. Other Pascal dialects also allow normal
Tridora-Pascal only allows identifiers as labels. Tridora-Pascal only allows identifiers as labels.
## Units
Units are the method to create libraries in Tridora-Pascal, that is, codes module that can
be reused in other programs.
Tridora-Pascal follows the unit syntax that has been established in UCSD-Pascal and is also
used in Turbo Pascal.
Units are imported with the *USES* keyword, right after the *PROGRAM* statement.
Multiple units can be imported by separating the unit names with commas.
There are some differences: In Tridora-Pascal, the unit file does not contain the interface
section, only the implementation section. The interface section is instead placed into a
separate file with the extension *.inc*, without any *UNIT* or *INTERFACE* keywords.
This file will be included by the compiler and should contain
procedure or function declarations (as *EXTERNAL*). It can also contain *TYPE*,
*CONST* and *VAR* statements.
All Pascal symbols of the unit are imported into the main program. There
is no separate namespace for units.
### Using an Existing Unit
An existing unit is imported with the *USES* statement that must be placed
immediately after the *PROGRAM* statement.
The compiler will look for an include file with the unit name and an *.inc* extension.
It will also
tell the assembler to include an assembly language file for each
unit. The filename must be the unit name plus an *.s* extension.
Since there is no linker in Tridora-Pascal, all imported units will be
assembled together with the main program.
The compiler looks for unit *.inc* and *.s* files in the current volume or
in the *SYSTEM* volume.
### Compiling a Unit
A unit implementation file should start with a *UNIT* statement instead of a *PROGRAM*
statement.
It should be compiled, not assembled.
When building a program that uses units, the assembler will include an assembly language
file for each unit.
It is possible to write units in assembly language. This is done by
directly providing the *.s* file and creating an *.inc* file with
the *EXTERNAL* declarations matching the assembly language
file.
#### Example
```pascal
(* UnitExamples.pas *)
program UnitExample;
uses hello;
begin
sayHello('unit');
end.
```
#### Example Unit Implementation File
```pascal
(* hello.pas *)
unit hello;
implementation
procedure sayHello(s:string);
begin
writeln('hello, ', s);
end;
end.
```
#### Example Unit Include File
```pascal
(* hello.inc *)
procedure sayHello(s:string); external;
```
## Compiler Directives ## Compiler Directives
Tridora-Pascal understands a small number of compiler directives which are introduced as usual with a comment and a dollar-sign. Both comment styles can be used. Tridora-Pascal understands a small number of compiler directives which are introduced as usual with a comment and a dollar-sign. Both comment styles can be used.

View file

@ -10,12 +10,12 @@ For the first channel the register addresses are:
|Address|Description| |Address|Description|
|-------|-----------| |-------|-----------|
| $A00 | Control Register | | $A00 | Control Register |
| $A04 | Clock Divider Register | | $A01 | Clock Divider Register |
| $A08 | Amplitude Register | | $A02 | Amplitude Register |
The register addresses for the second channel start at $A10, The register addresses for the second channel start at $A04,
the third channel at $A20 the third channel at $A08
and the fourth channel at $A30. and the fourth channel at $A0C.
## Reading the control register ## Reading the control register

View file

@ -37,7 +37,7 @@ It uses a fixed serial configuration of 115200 bps, 8 data bits, 1 stop bit, no
## Notes ## Notes
A 64 byte FIFO is used when receiving data. A 16 byte FIFO is used when receiving data.
When reading data, each byte needs to be acknowledged by writing the _C_ flag to the UART register. When reading data, each byte needs to be acknowledged by writing the _C_ flag to the UART register.

View file

@ -4,16 +4,13 @@ Registers
|Name|Address|Description| |Name|Address|Description|
|----|-------|-----------| |----|-------|-----------|
|_FB_RA_ | $900 | Read Address | |_FB_RA_ | $900 | Read Address |
|_FB_WA_ | $904 | Write Address | |_FB_WA_ | $901 | Write Address |
| _FB_IO_ | $908 | I/O Register | | _FB_IO_ | $902 | I/O Register |
| _FB_PS_ | $90C | Palette Select | | _FB_PS_ | $903 | Palette Select |
| _FB_PD_ | $910 | Palette Data | | _FB_PD_ | $904 | Palette Data |
| _FB_CTL_ | $914 | Control Register | | _FB_CTL_ | $905 | 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 |
## Pixel Data ## Pixel Data
Pixel data is organized in 32-bit-words. With four bits per pixel, one word 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 The _m_ field indicates the current graphics mode. At the time of writing, it is
always 1 which denotes a 640x400x4 mode. always 1 which denotes a 640x400x4 mode.
The _vb_ bit is 1 when the video signal generator is in its vertical blank phase. 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 |

View file

@ -1,5 +0,0 @@
const FIREWIDTH = 319; FIREHEIGHT = 79; (* keep in sync with fastfire.s! *)
type FireBuf = array [0..FIREHEIGHT, 0..FIREWIDTH] of integer;
procedure FastFireUpdate(var f:FireBuf); external;
procedure FastFireDraw(var f:FireBuf;screenx, screeny:integer); external;

View file

@ -1,326 +0,0 @@
; width and height of the fire cell matrix
; Be sure to sync this with fastfire.inc!
.EQU FIREWIDTH 319
.EQU FIREHEIGHT 79
;
; The cell matrix actually has one column
; and one row more than FIREWIDTH and
; FIREHEIGHT to handle the negative
; X offsets when calculating new
; cell values.
; Likewise, there is one more row.
; So rows are processed from 0 to FIREHEIGHT - 2
; and columms from 1 to FIREWIDTH - 1.
; cells considered for calculating new
; value for cell O (reference cells):
; .....O......
; ....123.....
; .....4......
; args: pointer to fire cell buffer
.EQU FF_ROW_COUNT 0
.EQU FF_COL_COUNT 4
.EQU FF_ROW_OFFS 8
.EQU FF_OFFS1 12
.EQU FF_OFFS2 16
.EQU FF_OFFS3 20
.EQU FF_OFFS4 24
.EQU FF_CELL_PTR 28
.EQU FF_FS 32
FASTFIREUPDATE:
FPADJ -FF_FS
STORE FF_CELL_PTR
LOADC FIREHEIGHT-1
STORE FF_ROW_COUNT
; calculate offsets for reference cells
LOADC FIREWIDTH+1
SHL 2
DUP
STORE FF_ROW_OFFS ; offset to next row: WIDTH*4
DEC 4 ; offset to cell 1: row offset - 4
DUP
STORE FF_OFFS1
INC 4
DUP
STORE FF_OFFS2 ; offset to cell 2: + 4
INC 4
STORE FF_OFFS3 ; offset to cell 3: + 4
LOAD FF_ROW_OFFS
SHL 1 ; offset to cell 4: row offset * 2
STORE FF_OFFS4
; start at column 1
LOAD FF_CELL_PTR
INC 4
STORE FF_CELL_PTR
FF_ROW:
LOADC FIREWIDTH-1
STORE FF_COL_COUNT
FF_COL:
LOAD FF_CELL_PTR
LOAD FF_OFFS1
ADD
LOADI
LOAD FF_CELL_PTR
LOAD FF_OFFS2
ADD
LOADI
LOAD FF_CELL_PTR
LOAD FF_OFFS3
ADD
LOADI
LOAD FF_CELL_PTR
LOAD FF_OFFS4
ADD
LOADI
ADD
ADD
ADD
SHR
SHR
; if new cell value > 0, subtract 1 to cool down
DUP
CBRANCH.Z FF_SKIP
DEC 1
FF_SKIP:
LOAD FF_CELL_PTR ; load cell ptr
SWAP ; swap with new value
STOREI 4 ; store with postincrement
STORE FF_CELL_PTR ; save new ptr value
LOAD FF_COL_COUNT ; decrement column count
DEC 1
DUP
STORE FF_COL_COUNT
CBRANCH.NZ FF_COL ; loop if col count <> 0
; at the end of a row, go to next row
; by adding 8 to the cell pointer,
; skipping the first cell of the next row
LOAD FF_CELL_PTR
INC 8
STORE FF_CELL_PTR
LOAD FF_ROW_COUNT ; decrement row count
DEC 1
DUP
STORE FF_ROW_COUNT
CBRANCH.NZ FF_ROW ; loop if row count <> 0
FF_EXIT:
FPADJ FF_FS
RET
; 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 WORDS_PER_LINE 80
; fire width in vmem words (strict left-to-right evaluation)
.EQU FFD_ROW_WORDS 1 + FIREWIDTH / 8
; draw all fire cells
; args: pointer to fire cell buffer, screen x, screen y
.EQU FFD_CELL_PTR 0
.EQU FFD_X 4
.EQU FFD_Y 8
.EQU FFD_ROW_COUNT 12
.EQU FFD_ROW_WORDCOUNT 16
.EQU FFD_VMEM_PTR 20
.EQU FFD_FS 24
FASTFIREDRAW:
FPADJ -FFD_FS
STORE FFD_Y
STORE FFD_X
STORE FFD_CELL_PTR
; calculate video memory addr
; addr = y * 80 + X / 8
LOAD FFD_Y
SHL 2 ; y * 16
SHL 2
DUP
SHL 2 ; + y * 64
ADD ; = y * 80
LOAD FFD_X
SHR
SHR
SHR
ADD ; + x / 8
DUP
STORE FFD_VMEM_PTR
LOADC FB_WA ; set vmem write address
SWAP
STOREI
DROP
LOADC FIREHEIGHT + 1
STORE FFD_ROW_COUNT
FFD_ROW:
LOADC FFD_ROW_WORDS
STORE FFD_ROW_WORDCOUNT
LOADC FB_WA ; set vmem write address
LOAD FFD_VMEM_PTR
STOREI
DROP
FFD_WORD:
LOAD FFD_CELL_PTR ; load cell ptr
LOADC 0 ; vmem word, start with 0
; leftmost pixel (0)
OVER ; [ cptr, vmemw, cptr ]
LOADI ; load cell value [ cptr, vmemw, cellval ]
SHR ; scale it down (from 7 bits to 4)
SHR
SHR ; [ cptr, vmemw, cellval shr 3 ]
OR ; [ cptr, vmemw ]
SWAP ; [ vmemw, cptr ]
INC 4 ; increment cell ptr on stack [ vmemw, cptr + 4 ]
SWAP ; [ cptr + 4, vmemw ]
SHL 2 ; move bits to left for next pixel
SHL 2
; pixel 1
OVER
LOADI ; load cell value
SHR ; scale it down (from 7 bits to 4)
SHR
SHR
OR
SWAP
INC 4 ; increment cell ptr on stack
SWAP
SHL 2 ; move bits to left for next pixel
SHL 2
; pixel 2
OVER
LOADI ; load cell value
SHR ; scale it down (from 7 bits to 4)
SHR
SHR
OR
SWAP
INC 4 ; increment cell ptr on stack
SWAP
SHL 2 ; move bits to left for next pixel
SHL 2
; pixel 3
OVER
LOADI ; load cell value
SHR ; scale it down (from 7 bits to 4)
SHR
SHR
OR
SWAP
INC 4 ; increment cell ptr on stack
SWAP
SHL 2 ; move bits to left for next pixel
SHL 2
; pixel 4
OVER
LOADI ; load cell value
SHR ; scale it down (from 7 bits to 4)
SHR
SHR
OR
SWAP
INC 4 ; increment cell ptr on stack
SWAP
SHL 2 ; move bits to left for next pixel
SHL 2
; pixel 5
OVER
LOADI ; load cell value
SHR ; scale it down (from 7 bits to 4)
SHR
SHR
OR
SWAP
INC 4 ; increment cell ptr on stack
SWAP
SHL 2 ; move bits to left for next pixel
SHL 2
; pixel 6
OVER
LOADI ; load cell value
SHR ; scale it down (from 7 bits to 4)
SHR
SHR
OR
SWAP
INC 4 ; increment cell ptr on stack
SWAP
SHL 2 ; move bits to left for next pixel
SHL 2
; pixel 7
OVER
LOADI ; load cell value
SHR ; scale it down (from 7 bits to 4)
SHR
SHR
OR
SWAP
INC 4 ; increment cell ptr on stack
SWAP
; store word to vmem
; vmem write addr will autoincrement
LOADC FB_IO
SWAP
STOREI
DROP
STORE FFD_CELL_PTR
; prepare for next word
LOAD FFD_ROW_WORDCOUNT
DEC 1
DUP
STORE FFD_ROW_WORDCOUNT
CBRANCH.NZ FFD_WORD
; prepare for next row
LOAD FFD_VMEM_PTR
LOADC WORDS_PER_LINE
ADD
STORE FFD_VMEM_PTR
LOAD FFD_ROW_COUNT
DEC 1
DUP
STORE FFD_ROW_COUNT
CBRANCH.NZ FFD_ROW
FFD_EXIT:
FPADJ FFD_FS
RET

View file

@ -1,76 +0,0 @@
{$H1}
{$S2}
program fire;
const MAXX = 30;
MAXY = 50;
var firebuf: array [0..MAXY, 0..MAXX] of integer;
firepalette: array [0..15] of integer =
( $FFA, $FF8, $FF4, $FF0, $FE0, $FD0, $FA0, $F90,
$F00, $E00, $D00, $A00, $800, $600, $300, $000);
x,y:integer;
procedure createPalette;
var i:integer;
begin
for i := 15 downto 0 do
setpalette(15 - i, firepalette[i]);
end;
procedure fireItUp;
var x,y:integer;
begin
y := MAXY - 1;
for x := 1 to MAXX - 1 do
firebuf[y, x] := random and 127;
end;
procedure updateFire;
var i,x,y:integer;
begin
for y := 0 to MAXY - 2 do
for x := 1 to MAXX - 1 do
begin
i :=
((firebuf[y + 1, x - 1]
+ firebuf[y + 1, x]
+ firebuf[y + 1, x + 1]
+ firebuf[y + 2, x])
) shr 2;
if i > 0 then
i := i - 1;
firebuf[y, x] := i;
end;
end;
procedure drawFire;
var x, y, col:integer;
begin
for y := 0 to MAXY - 1 do
begin
x := 0;
for col in firebuf[y] do
begin
putpixel(300 + x, 150 + y, col shr 3);
x := x + 1;
end;
end;
end;
begin
randomize;
initgraphics;
createPalette;
while not conavail do
begin
fireItUp;
updateFire;
drawFire;
end;
for y := 0 to MAXY do
begin
x := firebuf[y, 10];
drawline(0, y, x, y, 1);
end;
end.

View file

@ -1,84 +0,0 @@
{$H1}
{$S1}
program fire2;
uses fastfire;
const MAXX = FIREWIDTH;
MAXY = FIREHEIGHT;
var firecells: FireBuf;
firepalette: array [0..15] of integer =
{ ( $FFA, $FF8, $FF4, $FF0, $FE0, $FD0, $FA0, $F90,
$F00, $E00, $D00, $A00, $800, $600, $300, $000); }
( $FFA, $FFA, $FFA, $FFA, $FF0, $FF0, $FF0, $FF0,
$FF0, $FD0, $FA0, $C00, $A00, $700, $400, $000);
x,y:integer;
procedure createPalette;
var i:integer;
begin
for i := 15 downto 0 do
setpalette(15 - i, firepalette[i]);
end;
procedure fireItUp;
var x,y:integer;
begin
y := MAXY - 1;
for x := 1 to MAXX - 1 do
firecells[y, x] := random and 127;
end;
procedure updateFire;
var i,x,y:integer;
begin
for y := 0 to MAXY - 2 do
for x := 1 to MAXX - 1 do
begin
i :=
((firecells[y + 1, x - 1]
+ firecells[y + 1, x]
+ firecells[y + 1, x + 1]
+ firecells[y + 2, x])
) shr 2;
if i > 0 then
i := i - 1;
firecells[y, x] := i;
end;
end;
procedure drawFire;
var x, y, col:integer;
begin
for y := 0 to MAXY - 1 do
begin
x := 0;
for col in firecells[y] do
begin
putpixel(100 + x, 150 + y, col shr 3);
x := x + 1;
end;
end;
end;
begin
randomize;
initgraphics;
createPalette;
while not conavail do
begin
fireItUp;
FastFireUpdate(firecells);
{ updateFire; }
FastFireDraw(firecells, 160, 100);
{ drawFire; }
end;
for y := 0 to MAXY do
begin
x := firecells[y, 10];
drawline(0, y, x, y, 1);
end;
end.

View file

@ -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.

View file

@ -1,21 +1,32 @@
{$H2560} {$H1536}
program pcmtest2; program pcmtest;
uses pcmaudio; uses pcmaudio;
var filename:string; var filename:string;
buf:SndBufPtr; buf:SndBufPtr;
f:file;
size:integer;
i:integer;
c:char;
sampleRate:integer; sampleRate:integer;
err:integer; err:integer;
done:boolean;
c:char;
function readAudioFile(fname:string):SndBufPtr;
var i,size:integer;
c:char;
buf:SndBufPtr;
f:file;
begin begin
open(f, fname, ModeReadOnly); if ParamCount > 0 then
filename := ParamStr(1)
else
begin
write('Filename> ');
readln(filename);
end;
err := 1;
if ParamCount > 1 then
val(ParamStr(2),sampleRate, err);
if err <> 0 then
sampleRate := 16000;
open(f, filename, ModeReadOnly);
size := FileSize(f); size := FileSize(f);
new(buf, size); new(buf, size);
@ -30,26 +41,6 @@ begin
close(f); close(f);
readAudioFile := buf;
end;
begin
if ParamCount > 0 then
filename := ParamStr(1)
else
begin
write('Filename> ');
readln(filename);
end;
err := 1;
if ParamCount > 1 then
val(ParamStr(2), sampleRate, err);
if err > 0 then
sampleRate := 22050;
buf := readAudioFile(filename);
PlaySample(buf, sampleRate); PlaySample(buf, sampleRate);
dispose(buf); dispose(buf);

View file

@ -50,7 +50,7 @@ begin
buf := readAudioFile(filename); buf := readAudioFile(filename);
SampleQStart(buf, false, sampleRate); SampleQStart(buf, sampleRate);
write('Press ESC to stop> '); write('Press ESC to stop> ');
done := false; done := false;

View file

@ -3,16 +3,31 @@
.EQU WORDS_PER_LINE 80 .EQU WORDS_PER_LINE 80
.EQU FB_RA $900 .EQU FB_RA $900
.EQU FB_WA $904 .EQU FB_WA $901
.EQU FB_IO $908 .EQU FB_IO $902
.EQU FB_PS $90C .EQU FB_PS $903
.EQU FB_PD $910
.EQU FB_CTL $914 ; calculate mask for a word of pixels
.EQU FB_SHIFTER $918 ; args: word of pixels with four bits per pixel
.EQU FB_SHIFTCOUNT $91C ; returns: value that masks out all pixels that are set
.EQU FB_SHIFTERM $920 CALC_MASK:
.EQU FB_SHIFTERSP $924 LOADC $F ; pixel mask
.EQU FB_MASKGEN $928 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 ; calculate vmem address from coordinates
; args: x,y ; args: x,y
@ -52,19 +67,13 @@ CALC_VMEM_ADDR:
.EQU PS_SHIFT_C 20 .EQU PS_SHIFT_C 20
.EQU PS_SPILL 24 .EQU PS_SPILL 24
.EQU PS_STRIPE_C 28 .EQU PS_STRIPE_C 28
.EQU PS_BPSAVE 32 .EQU PS_FS 32
.EQU PS_FS 36
PUTSPRITE: PUTSPRITE:
FPADJ -PS_FS FPADJ -PS_FS
STORE PS_SPRITE_DATA STORE PS_SPRITE_DATA
STORE PS_Y STORE PS_Y
STORE PS_X STORE PS_X
LOADREG BP
STORE PS_BPSAVE
LOADC 0
STOREREG BP
; calculate vmem address ; calculate vmem address
LOAD PS_X LOAD PS_X
LOAD PS_Y LOAD PS_Y
@ -72,6 +81,11 @@ PUTSPRITE:
CALL CALL
STORE PS_VMEM_ADDR STORE PS_VMEM_ADDR
LOAD PS_X ; shift count = x mod 8
LOADC 7
AND
STORE PS_SHIFT_C
LOADC SPRITE_HEIGHT LOADC SPRITE_HEIGHT
STORE PS_SPRITE_LINES STORE PS_SPRITE_LINES
@ -79,10 +93,12 @@ PUTSPRITE:
PS_LOOP1: PS_LOOP1:
; set read and write address ; set read and write address
; in the vga controller ; in the vga controller
LOADC FB_RA ; read address register
LOAD PS_VMEM_ADDR LOAD PS_VMEM_ADDR
DUP STOREI 1 ; use autoincrement to get to the next register
STORE.B FB_RA LOAD PS_VMEM_ADDR
STORE.B FB_WA STOREI
DROP
LOAD PS_SPRITE_DATA ; address of sprite data LOAD PS_SPRITE_DATA ; address of sprite data
DUP DUP
@ -90,20 +106,61 @@ PS_LOOP1:
STORE PS_SPRITE_DATA ; and store it again STORE PS_SPRITE_DATA ; and store it again
LOADI ; load word from orig. address LOADI ; load word from orig. address
; ------- one word of sprite pixels on stack
STORE.B FB_SHIFTER LOADC 0
LOAD PS_X STORE PS_SPILL
STORE.B FB_SHIFTCOUNT
LOAD.B FB_SHIFTERM ; get shifted mask ; loop to shift pixel data to right
NOT LOAD PS_SHIFT_C ; load shift count
LOAD.B FB_IO ; and background pixel data PS_LOOP2:
AND ; remove foreground pixels DUP ; test it for zero
CBRANCH.Z PS_LOOP2_X
LOAD.B FB_SHIFTER ; get shifted pixels SWAP ; swap count with pixels
OR ; combine with background
STORE.B FB_IO ; store into 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_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 ; set counter for remaining stripes
LOADC SPRITE_STRIPES - 1 LOADC SPRITE_STRIPES - 1
@ -113,8 +170,8 @@ PS_LOOP1:
; process spilled bits and next vertical stripe of sprite data ; process spilled bits and next vertical stripe of sprite data
; ;
PS_NEXT_STRIPE: PS_NEXT_STRIPE:
;use spill bits from first column ; put spill bits on stack for later
LOAD.B FB_SHIFTERSP LOAD PS_SPILL
LOAD PS_SPRITE_DATA ; address of sprite data LOAD PS_SPRITE_DATA ; address of sprite data
DUP DUP
@ -122,21 +179,65 @@ PS_NEXT_STRIPE:
STORE PS_SPRITE_DATA ; and store it again STORE PS_SPRITE_DATA ; and store it again
LOADI ; load word from orig. address LOADI ; load word from orig. address
STORE.B FB_SHIFTER ; store into shifter ; reset spill bits
LOAD PS_X LOADC 0
STORE.B FB_SHIFTCOUNT ; shift stuff STORE PS_SPILL
LOAD.B FB_SHIFTER ; get shifted pixels
OR ; combine with spill bits (see above) ; 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 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 SWAP ; swap count with pixels
NOT
LOAD.B FB_IO ; get vmem data
AND ; remove foreground pixels from bg
OR ; combine with shifted pixels ; save the pixel that is shifted out
STORE.B FB_IO ; write to vmem 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 LOAD PS_STRIPE_C ; decrement stripe count
DEC 1 DEC 1
@ -145,18 +246,21 @@ PS_NEXT_STRIPE:
CBRANCH.NZ PS_NEXT_STRIPE ; if non-zero, next stripe CBRANCH.NZ PS_NEXT_STRIPE ; if non-zero, next stripe
; write spilled bits of the last stripe into next vmem word ; 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 DUP
STORE.B FB_MASKGEN LOADCP CALC_MASK ; calculate sprite mask for spill bits
LOAD.B FB_MASKGEN ; get sprite mask for spill bits CALL
NOT
LOAD.B FB_IO ; load next vmem word LOADCP FB_IO
LOADI ; load next vmem word
AND ; apply sprite mask AND ; apply sprite mask
OR ; OR in spill bits 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 LOAD PS_SPRITE_LINES ; decrement lines count
DEC 1 DEC 1
@ -172,9 +276,6 @@ PS_NEXT_STRIPE:
PS_L_XT: PS_L_XT:
DROP DROP
LOAD PS_BPSAVE
STOREREG BP
FPADJ PS_FS FPADJ PS_FS
RET RET
@ -221,7 +322,7 @@ UD_S_L1:
; store vmem offset into write addr reg ; store vmem offset into write addr reg
LOADCP FB_WA LOADCP FB_WA
LOAD UD_S_OFFSET LOAD UD_S_OFFSET
STOREI 4 ; ugly but fast: reuse addr STOREI 1 ; ugly but fast: reuse addr
; with postincrement to ; with postincrement to
; get to FB_IO for STOREI below ; get to FB_IO for STOREI below

View file

@ -1,251 +0,0 @@
{$H2560}
{$S8}
program xmas252;
uses pcmaudio, fastfire, tiles;
const MAXX = FIREWIDTH;
MAXY = FIREHEIGHT;
(* type PixelData = array[0..31999] of integer; *)
type Picture = record
magic:integer;
mode:integer;
palette: array[0..15] of integer;
pixels: PixelData;
end;
var firecells: FireBuf;
firepalette: array [0..15] of integer =
{ ( $FFA, $FF8, $FF4, $FF0, $FE0, $FD0, $FA0, $F90,
$F00, $E00, $D00, $A00, $800, $600, $300, $000); }
{ ( $FFA, $FFA, $FFA, $FFA, $FF0, $FF0, $FF0, $FF0, }
( $00F, $00F, $00F, $00F, $00F, $00F, $00F, $00F,
$FF0, $FD0, $FA0, $C00, $A00, $700, $400, $000);
x,y:integer;
infile:file;
pic:^Picture;
tilesheet:^Picture;
animationTick:integer;
animationHold:integer;
animationState:integer;
filename: string;
audiodata: SndBufPtr;
procedure createPalette;
var i:integer;
begin
for i := 15 downto 0 do
setpalette(15 - i, firepalette[i]);
end;
procedure fireItUp;
var x,y:integer;
begin
y := MAXY - 1;
for x := 1 to MAXX - 1 do
firecells[y, x] := random and 127;
end;
procedure updateFire;
var i,x,y:integer;
begin
for y := 0 to MAXY - 2 do
for x := 1 to MAXX - 1 do
begin
i :=
((firecells[y + 1, x - 1]
+ firecells[y + 1, x]
+ firecells[y + 1, x + 1]
+ firecells[y + 2, x])
) shr 2;
if i > 0 then
i := i - 1;
firecells[y, x] := i;
end;
end;
procedure drawFire(startX,startY:integer);
var x, y, col, col2:integer;
begin
for y := 0 to MAXY - 1 do
begin
x := 0;
for col in firecells[y] do
begin
{ scale and clamp color value }
col2 := col shr 3;
if col2 > FIREMAXCOLOR then col2 := FIREMAXCOLOR;
putpixel(startX + x, startY + y, col2);
x := x + 1;
end;
end;
end;
procedure readBackgroundPic(filename:string);
var i:integer;
begin
open(infile, filename, ModeReadonly);
read(infile, pic^);
close(infile);
for i := 0 to 15 do
SetPalette(i, pic^.palette[i]);
PutScreen(pic^.pixels);
end;
procedure animate;
var tileSrcX,tilesrcY:integer;
begin
animationTick := animationTick + 1;
if animationHold = 0 then
animationHold := 40;
if animationTick < animationHold then
exit;
animationTick := 0;
case animationState of
0: begin
tileSrcX := 0;
tileSrcY := 0;
animationHold := 40;
end;
1: begin
tileSrcX := 19;
tileSrcY := 0;
animationHold := 20;
if random and 7 > 4 then
animationState := -1;
end;
2: begin
tileSrcX := 38;
tileSrcY := 0;
animationHold := 2;
end;
3: begin;
tileSrcX := 57;
tileSrcY := 0;
animationHold := 2;
end;
4: begin
tileSrcX := 0;
tileSrcY := 13;
animationHold := 15;
end;
5: begin
tileSrcX := 57;
tileSrcY := 0;
animationHold := 2;
end;
6: begin
tileSrcX := 38;
tileSrcY := 0;
animationHold := 2;
end;
7: begin
tileSrcX := 0;
tileSrcY := 0;
animationHold := 2;
animationState := -1;
end;
end;
CopyTilesScr(tilesheet^.pixels,
tileSrcX, tileSrcY,
34,34,
19,13);
animationState := animationState + 1;
end;
procedure readTilesheet;
var filename:string;
i:integer;
begin
filename := 'tilesheet.pict';
open(infile, filename, ModeReadonly);
read(infile, tilesheet^);
close(infile);
end;
function newAudioData(fname:string):SndBufPtr;
var i,size:integer;
c:char;
buf:SndBufPtr;
f:file;
begin
open(f, fname, ModeReadOnly);
size := FileSize(f);
new(buf, size);
buf^ := '';
write('Reading ', size, ' bytes...');
for i := 1 to size do
begin
read(f,c);
AppendChar(buf^,c);
end;
writeln;
close(f);
newAudioData := buf;
end;
begin
if ParamCount > 0 then
filename := ParamStr(1)
else
filename := 'xmas25bg.pict';
Randomize;
audiodata := newAudioData('fireplace-loop.tdrau');
InitGraphics;
new(pic);
readBackgroundPic(filename);
new(tilesheet);
readTilesheet;
SampleQStart(audiodata, true, 22050);
while not ConAvail do
begin
fireItUp;
FastFireUpdate(firecells);
{ updateFire; }
FastFireDraw(firecells, 216, 165);
{ drawFire(216, 165); }
animate;
end;
SampleQStop;
for y := 0 to MAXY do
begin
x := firecells[y, 10];
drawline(0, y, x, y, 1);
end;
InitGraphics;
dispose(tilesheet);
dispose(pic);
dispose(audiodata);
end.

View file

@ -701,37 +701,113 @@ CMPWORDS_XT2:
; --------- Graphics Library --------------- ; --------- Graphics Library ---------------
; vga controller registers ; vga controller registers
.EQU FB_RA $900 .EQU FB_RA $900
.EQU FB_WA $904 .EQU FB_WA $901
.EQU FB_IO $908 .EQU FB_IO $902
.EQU FB_PS $90C .EQU FB_PS $903
.EQU FB_PD $910 .EQU FB_PD $904
.EQU FB_CTL $914 .EQU FB_CTL $905
.EQU FB_SHIFTER $918 ; set a pixel in fb memory
.EQU FB_SHIFTCOUNT $91C ; parameters: x,y - coordinates
.EQU FB_SHIFTERM $920 PUTPIXEL_1BPP:
.EQU FB_SHIFTERSP $924 ; calculate vmem address:
.EQU FB_MASKGEN $928 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 ADD ; add results together for vmem addr
; args: x, y, color
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:
PUTPIXEL_4BPP: PUTPIXEL_4BPP:
.EQU PUTPIXEL_X 0 .EQU PUTPIXEL_X 0
.EQU PUTPIXEL_Y 4 .EQU PUTPIXEL_Y 4
.EQU PUTPIXEL_COLOR 8 .EQU PUTPIXEL_COLOR 8
.EQU PUTPIXEL_BPSAV 12 .EQU PUTPIXEL_PIXPOS 12
.EQU PUTPIXEL_FS 16 .EQU PUTPIXEL_FS 16
FPADJ -PUTPIXEL_FS FPADJ -PUTPIXEL_FS
STORE PUTPIXEL_COLOR STORE PUTPIXEL_COLOR
STORE PUTPIXEL_Y STORE PUTPIXEL_Y
STORE PUTPIXEL_X STORE PUTPIXEL_X
LOADREG BP
STORE PUTPIXEL_BPSAV
LOADC 0
STOREREG BP
; calculate vmem address: (x / 8) + (y * 80) ; calculate vmem address: (x / 8) + (y * 80)
LOAD PUTPIXEL_X LOAD PUTPIXEL_X
@ -746,60 +822,86 @@ PUTPIXEL_4BPP:
SHL 2 ; * 16 SHL 2 ; * 16
DUP DUP
SHL 2; * 64 SHL 2; * 64
ADD ; y*16 + y*64 ADD ; x*16 + x*64
ADD ; add results together for vmem addr ADD ; add results together for vmem addr
DUP LOADCP FB_WA
STORE.B FB_WA ; set as write and read addresses OVER
STORE.B FB_RA 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 LOAD PUTPIXEL_COLOR
CBRANCH.Z PUTPX_CLR ; color 0 is special case LOAD PUTPIXEL_PIXPOS
SHR ; rcount = pixpos / 2
; create pixel data from color value in ROTLOOP_:
; leftmost pixel data bits (31-28) DUP ; exit loop if rcount is 0
LOADC 0 CBRANCH.Z ROTLOOP_END
LOAD PUTPIXEL_COLOR SWAP ; pixel value is now on top of stack
BPLC 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
SHL 2 EVEN_PIXPOS:
STORE.B FB_SHIFTER ; store pixel into shifter 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 ; read old vmem value
STORE.B FB_SHIFTCOUNT ; writing triggers shifting LOADCP FB_IO
LOADI
; mask bits
AND
; or in shifted pixel value
OR
LOAD.B FB_SHIFTERM ; get shift result as mask ; write new value
NOT ; invert to get background mask LOADCP FB_IO
LOAD.B FB_IO ; get background pixel data SWAP
AND ; remove bits for new pixel from bg STOREI
DROP
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
FPADJ PUTPIXEL_FS FPADJ PUTPIXEL_FS
RET RET
PUTPX_CLR: .CPOOL
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
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 ; draw a line between two points
; parameters: x0, y0, x1, y1, color ; parameters: x0, y0, x1, y1, color

View file

@ -2,6 +2,6 @@ type SndBuf = string[32768];
type SndBufPtr = ^SndBuf; type SndBufPtr = ^SndBuf;
procedure PlaySample(buf:SndBufPtr;sampleRate:integer); external; procedure PlaySample(buf:SndBufPtr;sampleRate:integer); external;
procedure SampleQStart(buf:SndBufPtr;loop:boolean;sampleRate:integer); external; procedure SampleQStart(buf:SndBufPtr;sampleRate:integer); external;
procedure SampleQStop; external; procedure SampleQStop; external;
function SampleQSize:integer; external; function SampleQSize:integer; external;

View file

@ -1,25 +1,25 @@
.EQU AUDIO_BASE $A00 .EQU AUDIO_BASE $A00
.EQU IRQC_REG $980 .EQU IRQC_REG $980
.EQU IRQC_EN $80 .EQU IRQC_EN $80
.EQU CPU_FREQ 77000000
; args: sample rate ; args: sample rate
START_PCMAUDIO: START_PCMAUDIO:
; calculate clock divider ; calculate clock divider
LOADCP CPU_FREQ LOADCP 77000000
SWAP SWAP
LOADCP _DIV LOADCP _DIV
CALL CALL
LOADC AUDIO_BASE + 4 LOADC AUDIO_BASE + 1
SWAP ; put clock divider on ToS SWAP ; put clock divider on ToS
STOREI 4 ; LOADCP 4812 ; clock divider for 16KHz sample rate
; LOADCP 2406 ; clock divider for 32KHz sample rate
STOREI 1
LOADCP 32768 ; set amplitude to biased 0 LOADCP 32768 ; set amplitude to biased 0
STOREI STOREI
DROP DROP
LOADC AUDIO_BASE LOADC AUDIO_BASE
LOADC 1 ; enable channel LOADC 17 ; enable channel, enable interrupt
STOREI STOREI
DROP DROP
RET RET
@ -95,20 +95,24 @@ PLAY1_L0:
AND AND
CBRANCH.NZ PLAY1_L0 ; loop if fifo is full CBRANCH.NZ PLAY1_L0 ; loop if fifo is full
LOADC AUDIO_BASE+8 ; store amplitude value LOADC AUDIO_BASE+2 ; store amplitude value
SWAP SWAP
STOREI STOREI
DROP DROP
RET RET
; set sample queue count and pointer from string header ; start interrupt-driven sample playback
; args: pointer to string/SndBufPtr ; args: pointer to pascal string, sample rate
_STR2SMPLQPTR: SAMPLEQSTART:
LOADCP START_PCMAUDIO
CALL
LOADCP SMPLQ_COUNT LOADCP SMPLQ_COUNT
OVER OVER
LOADI ; get string size from header LOADI ; get string size from header
SHR ; divide by 4 to get word count SHR ; divide by 4 to get word count
SHR SHR
STOREI STOREI
DROP DROP
@ -117,38 +121,6 @@ _STR2SMPLQPTR:
INC 8 ; skip rest of header INC 8 ; skip rest of header
STOREI ; store sample data pointer STOREI ; store sample data pointer
DROP DROP
RET
; start interrupt-driven sample playback
; args: pointer to pascal string, loop flag, sample rate
SAMPLEQSTART:
LOADCP START_PCMAUDIO ; sample rate is on ToS as arg to subroutine
CALL
SWAP ; swap loop flag and buf ptr
LOADCP _STR2SMPLQPTR
CALL
; loop flag is now on ToS
CBRANCH.Z SQ_S_1
; if nonzero, set loop ptr
LOADCP SMPLQ_PTR
LOADI
DEC 8 ; subtract offset for string header again
BRANCH SQ_S_0
SQ_S_1:
LOADC 0
SQ_S_0:
LOADCP SMPLQ_NEXT
SWAP
STOREI
DROP
LOADC AUDIO_BASE
LOADC 17 ; enable channel, enable interrupt
STOREI
DROP
LOADCP SMPLQ_ISR ; set interrupt handler LOADCP SMPLQ_ISR ; set interrupt handler
STOREREG IV STOREREG IV
@ -182,7 +154,6 @@ SAMPLEQSIZE:
SMPLQ_PTR: .WORD 0 SMPLQ_PTR: .WORD 0
SMPLQ_COUNT: .WORD 0 SMPLQ_COUNT: .WORD 0
SMPLQ_NEXT: .WORD 0
SMPLQ_ISR: SMPLQ_ISR:
LOADC IRQC_REG LOADC IRQC_REG
@ -199,7 +170,7 @@ SMPLQ_I_L:
DROP DROP
BRANCH SMPLQ_I_XT ; if null, end interrupt routine BRANCH SMPLQ_I_XT ; if null, end interrupt routine
SMPLQ_I_B: SMPLQ_I_B:
LOADI ; load next word which contains two samples LOADI ; load next word
DUP DUP
BROT ; get high half-word BROT ; get high half-word
@ -207,7 +178,7 @@ SMPLQ_I_B:
LOADCP $FFFF LOADCP $FFFF
AND AND
LOADC AUDIO_BASE+8 LOADC AUDIO_BASE+2
SWAP SWAP
STOREI ; write sample, keep addr STOREI ; write sample, keep addr
@ -234,42 +205,23 @@ SMPLQ_I_B:
STOREI STOREI
DROP DROP
; put up to 16 samples into the sample queue
LOADCP SMPLQ_COUNT
LOADI ; load word counter again
LOADC 7 ; check if count modulo 7 = 0
AND
CBRANCH.NZ SMPLQ_I_L ; if not, next two samples
; check if fifo is full ; check if fifo is full
; does not work reliably when running in DRAM, LOADC AUDIO_BASE
; maybe because at least one sample has already played LOADI
; since start of ISR? LOADC 8 ; fifo_full
; LOADC AUDIO_BASE AND
; LOADI CBRANCH.Z SMPLQ_I_L ; next sample if not full
; LOADC 8 ; fifo_full
; AND
; CBRANCH.Z SMPLQ_I_L ; next sample if not full
BRANCH SMPLQ_I_XT LOADC AUDIO_BASE
LOADC 17 ; re-enable channel interrupt
; end of sample buffer, check for next STOREI
SMPLQ_I_END:
DROP DROP
DROP
LOADCP SMPLQ_NEXT ; skip to end
LOADI ; if NEXT ptr is zero
DUP
CBRANCH.Z SMPLQ_I_END1
LOADCP _STR2SMPLQPTR
CALL
BRANCH SMPLQ_I_XT BRANCH SMPLQ_I_XT
; end playback, set ptr and counter to zero ; end playback, set ptr and counter to zero
SMPLQ_I_END1: SMPLQ_I_END:
DROP
DROP DROP
LOADCP SMPLQ_PTR LOADCP SMPLQ_PTR
LOADC 0 LOADC 0
@ -281,21 +233,12 @@ SMPLQ_I_END1:
DROP DROP
; set amplitude out to zero (biased) ; set amplitude out to zero (biased)
LOADC AUDIO_BASE+8 LOADC AUDIO_BASE+2
LOADCP 32768 LOADCP 32768
STOREI STOREI
DROP DROP
; exit without enabling interrupts for this channel
BRANCH SMPLQ_I_XT2
SMPLQ_I_XT: SMPLQ_I_XT:
LOADC AUDIO_BASE
LOADC 17 ; re-enable channel interrupt
STOREI
DROP
SMPLQ_I_XT2:
LOADC IRQC_REG ; re-enable interrupts LOADC IRQC_REG ; re-enable interrupts
LOADC IRQC_EN LOADC IRQC_EN
STOREI STOREI

View file

@ -13,7 +13,7 @@ LSYMGEN=./lsymgen
.pas: .pas:
fpc -Mobjfpc -gl $< fpc -Mobjfpc -gl $<
all: libs pcomp sasm sdis lsymgen shortgen nativecomp nativeprogs all: pcomp sasm sdis lsymgen shortgen nativeprogs
libs: pcomp sasm lsymgen shortgen libs: pcomp sasm lsymgen shortgen
$(SASM) ../lib/coreloader.s $(SASM) ../lib/coreloader.s
@ -22,9 +22,9 @@ libs: pcomp sasm lsymgen shortgen
$(SASM) ../lib/stdlibwrap.s ../lib/stdlib.lib $(SASM) ../lib/stdlibwrap.s ../lib/stdlib.lib
$(LSYMGEN) ../lib/stdlibwrap.sym ../lib/stdlib.lsym $(LSYMGEN) ../lib/stdlibwrap.sym ../lib/stdlib.lsym
test: libs sasm.s pcomp.s lsymgen.s shortgen.s test: sasm.s pcomp.s lsymgen.s shortgen.s
testprgs: libs sasm.prog pcomp.prog lsymgen.prog shortgen.prog testprgs: sasm.prog pcomp.prog lsymgen.prog shortgen.prog
nativecomp: libs pcomp.prog sasm.prog lsymgen.prog shortgen.prog nativecomp: libs pcomp.prog sasm.prog lsymgen.prog shortgen.prog
@ -41,5 +41,4 @@ examples: nativecomp ../tests/readtest.prog ../tests/readchartest.prog ../tests/
-$(MAKE) -C ../rogue -f Makefile.tridoracpu -$(MAKE) -C ../rogue -f Makefile.tridoracpu
clean: clean:
rm -f pcomp sasm sdis libgen lsymgen shortgen*.o *.s *.prog \ rm -f pcomp sasm sdis libgen lsymgen *.o *.s *.prog
../lib/stdlib.s ../lib/stdlib.lib ../lib/stdlib.lsym

View file

@ -324,9 +324,7 @@ begin
rewindStringList(usedUnits); rewindStringList(usedUnits);
while nextStringListItem(usedUnits, unitName) do while nextStringListItem(usedUnits, unitName) do
emitInclude(unitName + UnitSuffix2); emitInclude(unitName + UnitSuffix2);
(* _END label needs to be word-aligned because
it is used as the start of the heap *)
emitIns('.ALIGN');
emitLabelRaw('_END'); emitLabelRaw('_END');
end; end;

View file

@ -2056,9 +2056,6 @@ begin
operandValue := 0; operandValue := 0;
emitBlock(count, operandValue); emitBlock(count, operandValue);
end end
else
if lastToken.tokenText = '.ALIGN' then
alignOutput(wordSize)
else else
errorExit2('Unrecognized directive', lastToken.tokenText); errorExit2('Unrecognized directive', lastToken.tokenText);
end; end;

View file

@ -226,7 +226,6 @@ begin
if not invalid then if not invalid then
begin begin
open(xferFile, filename, ModeOverwrite); open(xferFile, filename, ModeOverwrite);
blockNo := 0;
done := false; done := false;
repeat repeat
serReadBlock(ok); serReadBlock(ok);

View file

@ -8,8 +8,8 @@ set_property -dict {PACKAGE_PIN E3 IOSTANDARD LVCMOS33} [get_ports clk]
create_clock -period 10.000 -name sys_clk_pin -waveform {0.000 5.000} -add [get_ports clk] create_clock -period 10.000 -name sys_clk_pin -waveform {0.000 5.000} -add [get_ports clk]
## Switches ## Switches
#set_property -dict {PACKAGE_PIN A8 IOSTANDARD LVCMOS33} [get_ports sw0] set_property -dict {PACKAGE_PIN A8 IOSTANDARD LVCMOS33} [get_ports sw0]
#set_property -dict {PACKAGE_PIN C11 IOSTANDARD LVCMOS33} [get_ports sw1] set_property -dict {PACKAGE_PIN C11 IOSTANDARD LVCMOS33} [get_ports sw1]
#set_property -dict { PACKAGE_PIN C10 IOSTANDARD LVCMOS33 } [get_ports { sw[2] }]; #IO_L13N_T2_MRCC_16 Sch=sw[2] #set_property -dict { PACKAGE_PIN C10 IOSTANDARD LVCMOS33 } [get_ports { sw[2] }]; #IO_L13N_T2_MRCC_16 Sch=sw[2]
#set_property -dict { PACKAGE_PIN A10 IOSTANDARD LVCMOS33 } [get_ports { sw[3] }]; #IO_L14P_T2_SRCC_16 Sch=sw[3] #set_property -dict { PACKAGE_PIN A10 IOSTANDARD LVCMOS33 } [get_ports { sw[3] }]; #IO_L14P_T2_SRCC_16 Sch=sw[3]
@ -34,7 +34,7 @@ set_property -dict {PACKAGE_PIN T9 IOSTANDARD LVCMOS33} [get_ports led2]
set_property -dict {PACKAGE_PIN T10 IOSTANDARD LVCMOS33} [get_ports led3] set_property -dict {PACKAGE_PIN T10 IOSTANDARD LVCMOS33} [get_ports led3]
## Buttons ## Buttons
#set_property -dict {PACKAGE_PIN D9 IOSTANDARD LVCMOS33} [get_ports btn0] set_property -dict {PACKAGE_PIN D9 IOSTANDARD LVCMOS33} [get_ports btn0]
#set_property -dict { PACKAGE_PIN C9 IOSTANDARD LVCMOS33 } [get_ports { btn1 }]; #IO_L11P_T1_SRCC_16 Sch=btn[1] #set_property -dict { PACKAGE_PIN C9 IOSTANDARD LVCMOS33 } [get_ports { btn1 }]; #IO_L11P_T1_SRCC_16 Sch=btn[1]
#set_property -dict { PACKAGE_PIN B9 IOSTANDARD LVCMOS33 } [get_ports { btn[2] }]; #IO_L11N_T1_SRCC_16 Sch=btn[2] #set_property -dict { PACKAGE_PIN B9 IOSTANDARD LVCMOS33 } [get_ports { btn[2] }]; #IO_L11N_T1_SRCC_16 Sch=btn[2]
#set_property -dict { PACKAGE_PIN B8 IOSTANDARD LVCMOS33 } [get_ports { btn[3] }]; #IO_L12P_T1_MRCC_16 Sch=btn[3] #set_property -dict { PACKAGE_PIN B8 IOSTANDARD LVCMOS33 } [get_ports { btn[3] }]; #IO_L12P_T1_MRCC_16 Sch=btn[3]

View file

@ -1,6 +1,6 @@
`timescale 1ns / 1ps `timescale 1ns / 1ps
module irqctrl #(IRQ_LINES = 3, IRQ_DELAY_WIDTH = 8) ( module irqctrl #(IRQ_LINES = 3, IRQ_DELAY_WIDTH = 4) (
input wire clk, input wire clk,
input wire [IRQ_LINES-1:0] irq_in, input wire [IRQ_LINES-1:0] irq_in,
input wire cs, input wire cs,

View file

@ -16,9 +16,9 @@ module stackcpu #(parameter ADDR_WIDTH = 32, WIDTH = 32,
output wire write_enable, output wire write_enable,
input wire mem_wait, input wire mem_wait,
output wire debug1, output wire led1,
output wire debug2, output wire led2,
output wire debug3 output wire led3
); );
localparam EVAL_STACK_INDEX_WIDTH = 6; localparam EVAL_STACK_INDEX_WIDTH = 6;
@ -90,6 +90,7 @@ module stackcpu #(parameter ADDR_WIDTH = 32, WIDTH = 32,
wire mem_write; wire mem_write;
wire x_is_zero; wire x_is_zero;
// wire [WIDTH-1:0] y_plus_operand = Y + operand;
wire x_equals_y = X == Y; wire x_equals_y = X == Y;
wire y_lessthan_x = $signed(Y) < $signed(X); wire y_lessthan_x = $signed(Y) < $signed(X);
@ -104,10 +105,16 @@ module stackcpu #(parameter ADDR_WIDTH = 32, WIDTH = 32,
assign write_enable = mem_write_enable; assign write_enable = mem_write_enable;
// debug output ------------------------------------------------------------------------------------ // debug output ------------------------------------------------------------------------------------
assign debug1 = reset; assign led1 = reset;
assign debug2 = ins_loadc; assign led2 = ins_loadc;
assign debug3 = ins_branch; assign led3 = ins_branch;
// assign debug_out1 = { mem_read_enable, mem_write_enable, x_is_zero,
// ins_branch, ins_aluop, y_lessthan_x, x_equals_y, {7{1'b0}}, seq_state};
// assign debug_out2 = data_in;
// assign debug_out3 = nX;
// assign debug_out4 = nPC;
// assign debug_out5 = ins;
// assign debug_out6 = IV;
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------
// instruction decoding // instruction decoding
@ -399,10 +406,7 @@ module stackcpu #(parameter ADDR_WIDTH = 32, WIDTH = 32,
// process irq // process irq
always @(posedge clk) always @(posedge clk)
begin begin
// in MEM state, clear irq_pending, when nPC has been set to IV if(seq_state == MEM && irq_pending && !(ins_xfer & xfer_r2p)) // in FETCH state, clear irq_pending.
// RET instruction is a special case because we need to use
// the new PC that is in mem_data
if(seq_state == MEM && irq_pending && !(ins_xfer && xfer_r2p))
irq_pending <= 0; irq_pending <= 0;
else else
irq_pending <= irq_pending || irq; // else set irq_pending when irq is high irq_pending <= irq_pending || irq; // else set irq_pending when irq is high

View file

@ -7,7 +7,7 @@ module wavegen #(DATA_WIDTH=32, CLOCK_DIV_WIDTH=22,
input wire reset, input wire reset,
input wire [1:0] reg_sel, input wire [1:0] reg_sel,
output wire [DATA_WIDTH-1:0] rd_data, output wire [DATA_WIDTH-1:0] rd_data,
input wire [AMP_WIDTH-1:0] wr_data, input wire [DATA_WIDTH-1:0] wr_data,
input wire rd_en, input wire rd_en,
input wire wr_en, input wire wr_en,
@ -20,9 +20,6 @@ module wavegen #(DATA_WIDTH=32, CLOCK_DIV_WIDTH=22,
localparam TDRAU_REG_CLK = 1; /* clock divider register */ localparam TDRAU_REG_CLK = 1; /* clock divider register */
localparam TDRAU_REG_AMP = 2; /* amplitude (volume) register */ localparam TDRAU_REG_AMP = 2; /* amplitude (volume) register */
/* avoid warning about unconnected port */
(* keep="soft" *) wire _unused = rd_en;
reg channel_enable; reg channel_enable;
reg [CLOCK_DIV_WIDTH-1:0] clock_div; reg [CLOCK_DIV_WIDTH-1:0] clock_div;
reg [CLOCK_DIV_WIDTH-1:0] div_count; reg [CLOCK_DIV_WIDTH-1:0] div_count;
@ -32,12 +29,12 @@ module wavegen #(DATA_WIDTH=32, CLOCK_DIV_WIDTH=22,
wire fifo_wr_en; wire fifo_wr_en;
wire fifo_rd_en, fifo_full, fifo_empty; wire fifo_rd_en, fifo_full, fifo_empty;
wire [AMP_WIDTH-1:0] fifo_rd_data; wire [DATA_WIDTH-1:0] fifo_rd_data;
fifo #(.ADDR_WIDTH(4), .DATA_WIDTH(16)) sample_buf( fifo #(.ADDR_WIDTH(4), .DATA_WIDTH(16)) sample_buf(
clk, reset, clk, reset,
fifo_wr_en, fifo_rd_en, fifo_wr_en, fifo_rd_en,
wr_data[AMP_WIDTH-1:0], fifo_rd_data, wr_data, fifo_rd_data,
fifo_full, fifo_full,
fifo_empty fifo_empty
); );
@ -169,14 +166,9 @@ module tdraudio #(DATA_WIDTH=32) (
localparam AMP_BIAS = 32768; localparam AMP_BIAS = 32768;
localparam DAC_WIDTH = 18; localparam DAC_WIDTH = 18;
/* avoid warning about unconnected port */
(* keep="soft" *) wire [DATA_WIDTH-1:AMP_WIDTH] _unused = wr_data[DATA_WIDTH-1:AMP_WIDTH];
wire [4:0] chan_sel = io_addr[6:2]; wire [4:0] chan_sel = io_addr[6:2];
wire [1:0] reg_sel = io_addr[1:0]; wire [1:0] reg_sel = io_addr[1:0];
wire [AMP_WIDTH-1:0] amp_wr_data = wr_data[AMP_WIDTH-1:0];
wire [AMP_WIDTH-1:0] chan0_amp; wire [AMP_WIDTH-1:0] chan0_amp;
wire [DATA_WIDTH-1:0] chan0_rd_data; wire [DATA_WIDTH-1:0] chan0_rd_data;
wire chan0_running; wire chan0_running;
@ -218,25 +210,25 @@ module tdraudio #(DATA_WIDTH=32) (
{DATA_WIDTH{1'b1}}; {DATA_WIDTH{1'b1}};
wavegen chan0(clk, reset, reg_sel, wavegen chan0(clk, reset, reg_sel,
chan0_rd_data, amp_wr_data, chan0_rd_data, wr_data,
chan0_rd_en, chan0_wr_en, chan0_rd_en, chan0_wr_en,
chan0_amp, chan0_amp,
chan0_running, chan0_irq); chan0_running, chan0_irq);
wavegen chan1(clk, reset, reg_sel, wavegen chan1(clk, reset, reg_sel,
chan1_rd_data, amp_wr_data, chan1_rd_data, wr_data,
chan1_rd_en, chan1_wr_en, chan1_rd_en, chan1_wr_en,
chan1_amp, chan1_amp,
chan1_running, chan1_irq); chan1_running, chan1_irq);
wavegen chan2(clk, reset, reg_sel, wavegen chan2(clk, reset, reg_sel,
chan2_rd_data, amp_wr_data, chan2_rd_data, wr_data,
chan2_rd_en, chan2_wr_en, chan2_rd_en, chan2_wr_en,
chan2_amp, chan2_amp,
chan2_running, chan2_irq); chan2_irq, chan2_running);
wavegen chan3(clk, reset, reg_sel, wavegen chan3(clk, reset, reg_sel,
chan3_rd_data, amp_wr_data, chan3_rd_data, wr_data,
chan3_rd_en, chan3_wr_en, chan3_rd_en, chan3_wr_en,
chan3_amp, chan3_amp,
chan3_running, chan3_irq); chan3_running, chan3_irq);

View file

@ -15,6 +15,9 @@
module top( module top(
input wire clk, input wire clk,
input wire rst, input wire rst,
input wire btn0,
input wire sw0,
input wire sw1,
output wire led0, output wire led0,
output wire led1, output wire led1,
output wire led2, output wire led2,
@ -137,7 +140,7 @@ module top(
assign fb_wr_data = mem_write_data; assign fb_wr_data = mem_write_data;
vgafb vgafb0(`clock, pixclk, rst, 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, fb_rd_en, fb_wr_en,
VGA_HS_O, VGA_VS_O, VGA_R, VGA_G, VGA_B); VGA_HS_O, VGA_VS_O, VGA_R, VGA_G, VGA_B);
`endif `endif
@ -226,15 +229,6 @@ module top(
assign uart_rd_data = { {WIDTH-10{1'b1}}, uart_rx_avail, uart_tx_busy, uart_rx_data }; assign uart_rd_data = { {WIDTH-10{1'b1}}, uart_rx_avail, uart_tx_busy, uart_rx_data };
wire audio_irq; wire audio_irq;
buart #(.CLKFREQ(`clkfreq)) uart0(`clock, rst,
uart_baud,
uart_txd_in, uart_rxd_out,
uart_rx_clear, uart_tx_en,
uart_rx_avail, uart_tx_busy,
uart_tx_data, uart_rx_data);
// audio controller
`ifdef ENABLE_TDRAUDIO `ifdef ENABLE_TDRAUDIO
wire [WIDTH-1:0] tdraudio_wr_data; wire [WIDTH-1:0] tdraudio_wr_data;
wire [WIDTH-1:0] tdraudio_rd_data; wire [WIDTH-1:0] tdraudio_rd_data;
@ -247,7 +241,7 @@ module top(
assign tdraudio_wr_data = mem_write_data; assign tdraudio_wr_data = mem_write_data;
tdraudio tdraudio0(`clock, ~rst, tdraudio tdraudio0(`clock, ~rst,
mem_addr[8:2], mem_addr[6:0],
tdraudio_rd_data, tdraudio_rd_data,
tdraudio_wr_data, tdraudio_wr_data,
tdraudio_rd_en, tdraudio_rd_en,
@ -278,9 +272,13 @@ module top(
(io_slot == 4) ? tdraudio_rd_data: (io_slot == 4) ? tdraudio_rd_data:
`endif `endif
-1; -1;
irqctrl irqctrl0(`clock, irq_in, irqc_cs, mem_write_enable,
irqc_seten, irqc_rd_data0, buart #(.CLKFREQ(`clkfreq)) uart0(`clock, rst,
irq); uart_baud,
uart_txd_in, uart_rxd_out,
uart_rx_clear, uart_tx_en,
uart_rx_avail, uart_tx_busy,
uart_tx_data, uart_rx_data);
// CPU ----------------------------------------------------------------- // CPU -----------------------------------------------------------------
stackcpu cpu0(.clk(`clock), .rst(rst), .irq(irq), stackcpu cpu0(.clk(`clock), .rst(rst), .irq(irq),
@ -289,8 +287,12 @@ module top(
.read_ins(dram_read_ins), .read_ins(dram_read_ins),
.data_out(mem_write_data), .write_enable(mem_write_enable), .data_out(mem_write_data), .write_enable(mem_write_enable),
.mem_wait(mem_wait), .mem_wait(mem_wait),
.debug1(led1), .debug2(led2), .debug3(led3)); .led1(led1), .led2(led2), .led3(led3));
// Interrupt Controller
irqctrl irqctrl0(`clock, irq_in, irqc_cs, mem_write_enable,
irqc_seten, irqc_rd_data0,
irq);
// count clock ticks // count clock ticks
// generate interrupt every 20nth of a second // generate interrupt every 20nth of a second

View file

@ -1,9 +1,6 @@
`timescale 1ns / 1ps `timescale 1ns / 1ps
`default_nettype none `default_nettype none
// enable shifter/masker registers
`define ENABLE_FB_ACCEL
// Project F: Display Timings // Project F: Display Timings
// (C)2019 Will Green, Open Source Hardware released under the MIT License // (C)2019 Will Green, Open Source Hardware released under the MIT License
// Learn more at https://projectf.io // 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_PAL_SLOT = 3; localparam REG_PAL_DATA = 4;
localparam REG_CTL = 5; 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 COLOR_WIDTH = 12;
localparam PALETTE_WIDTH = 4; localparam PALETTE_WIDTH = 4;
@ -156,32 +145,12 @@ module vgafb #(VMEM_ADDR_WIDTH = 15, VMEM_DATA_WIDTH = 32) (
wire pix_rd; wire pix_rd;
wire [VMEM_DATA_WIDTH-1:0] status; 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_rd_en = rd_en;
assign vmem_wr_en = (reg_sel == REG_VMEM) && wr_en; assign vmem_wr_en = (reg_sel == REG_VMEM) && wr_en;
assign rd_data = (reg_sel == REG_VMEM) ? vmem_rd_data : assign rd_data = (reg_sel == REG_VMEM) ? vmem_rd_data :
(reg_sel == REG_RD_ADDR) ? cpu_rd_addr : (reg_sel == REG_RD_ADDR) ? cpu_rd_addr :
(reg_sel == REG_WR_ADDR) ? cpu_wr_addr : (reg_sel == REG_WR_ADDR) ? cpu_wr_addr :
(reg_sel == REG_CTL) ? status : (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; 32'hFFFFFFFF;
wire [VMEM_ADDR_WIDTH-1:0] cpu_addr = vmem_wr_en ? cpu_wr_addr : cpu_rd_addr; 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 if(rd_en && reg_sel == REG_VMEM) cpu_rd_addr <= cpu_rd_addr + 1; // auto-increment read addr on read
end 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) always @(posedge pix_clk)
begin begin
if(scanline || shift_count == MAX_SHIFT_COUNT) // before start of a line if(scanline || shift_count == MAX_SHIFT_COUNT) // before start of a line

View file

@ -378,20 +378,33 @@
<Report Name="ROUTE_DESIGN.REPORT_METHODOLOGY" Enabled="1"/> <Report Name="ROUTE_DESIGN.REPORT_METHODOLOGY" Enabled="1"/>
<RQSFiles/> <RQSFiles/>
</Run> </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"> <Run Id="impl_1" Type="Ft2:EntireDesign" Part="xc7a35ticsg324-1L" ConstrsSet="constrs_1" Description="Similar to Performance_ExplorePostRoutePhysOpt, but enables logic optimization step (opt_design) with the ExploreWithRemap directive." 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"> <Strategy Version="1" Minor="2">
<StratHandle Name="Vivado Implementation Defaults" Flow="Vivado Implementation 2024"> <StratHandle Name="Performance_ExploreWithRemap" Flow="Vivado Implementation 2024">
<Desc>Default settings for Implementation.</Desc> <Desc>Similar to Performance_ExplorePostRoutePhysOpt, but enables logic optimization step (opt_design) with the ExploreWithRemap directive.</Desc>
</StratHandle> </StratHandle>
<Step Id="init_design"/> <Step Id="init_design"/>
<Step Id="opt_design"/> <Step Id="opt_design">
<Option Id="Directive">6</Option>
</Step>
<Step Id="power_opt_design"/> <Step Id="power_opt_design"/>
<Step Id="place_design"/> <Step Id="place_design">
<Option Id="Directive">0</Option>
</Step>
<Step Id="post_place_power_opt_design"/> <Step Id="post_place_power_opt_design"/>
<Step Id="phys_opt_design"/> <Step Id="phys_opt_design">
<Step Id="route_design"/> <Option Id="Directive">0</Option>
<Step Id="post_route_phys_opt_design"/> </Step>
<Step Id="write_bitstream"/> <Step Id="route_design">
<Option Id="Directive">1</Option>
<Option Id="MoreOptsStr"><![CDATA[-tns_cleanup]]></Option>
</Step>
<Step Id="post_route_phys_opt_design" EnableStepBool="1">
<Option Id="Directive">0</Option>
</Step>
<Step Id="write_bitstream">
<Option Id="BinFile">1</Option>
</Step>
</Strategy> </Strategy>
<GeneratedRun Dir="$PRUNDIR" File="gen_run.xml"/> <GeneratedRun Dir="$PRUNDIR" File="gen_run.xml"/>
<ReportStrategy Name="Vivado Implementation Default Reports" Flow="Vivado Implementation 2024"/> <ReportStrategy Name="Vivado Implementation Default Reports" Flow="Vivado Implementation 2024"/>

View file

@ -16,7 +16,6 @@
# limitations under the License. # limitations under the License.
import sys import sys
import os
import serial import serial
import time import time
import random import random
@ -42,6 +41,30 @@ def get_default_device():
return '/dev/ttyUSB1' return '/dev/ttyUSB1'
def serwrite_slow(databytes, ser):
total = len(data)
count = 1
for d in data:
sys.stdout.write("writing {0:02x} {1:04d}/{2:04d}\r".format(ord(d), count, total))
ser.write(bytes(d,"utf8"))
count += 1
time.sleep(0.020)
print()
def serwrite(datafile, ser):
with open(datafile) as f:
data = f.read()
total = len(data)
count = 1
for d in data:
sys.stdout.write("writing {0:02x} {1:04d}/{2:04d}\r".format(ord(d), count, total))
ser.write(bytes(d,"utf8"))
count += 1
time.sleep(0.020)
print()
def checksum(databytes): def checksum(databytes):
i = 0 i = 0
cksum = 0 cksum = 0
@ -62,29 +85,10 @@ def sendchar(char, ser):
ser.write(char.to_bytes(1, 'big')) ser.write(char.to_bytes(1, 'big'))
def sendcommand(ser, cmd=b'L', verbose=False): def sendcommand(ser, cmd=b'L'):
verbose = True
ser.write(cmd) ser.write(cmd)
resp = ser.read_until() resp = ser.read_until()
if verbose: print(cmd,"sent, response:", str(resp))
print(cmd,"sent, response:", str(resp))
return resp
# send command and wait for echo
def commandwait(ser, cmd):
resp = sendcommand(ser, cmd, verbose=False)
if len(resp) == 0:
print("timeout sending '{}' command".format(cmd))
return None
if resp.startswith(b"> "):
resp = resp[2:]
if resp != bytearray(cmd + b"\r\n"):
print("invalid response to '{}' command".format(cmd))
return None
return resp return resp
@ -149,8 +153,6 @@ def serload_bin(datafile, ser):
data += bytearray(pad) data += bytearray(pad)
print("{} total blocks".format((len(data) + blocksize - 1) // blocksize))
if not send_size_header(ser, filesize): if not send_size_header(ser, filesize):
print("Error sending size header.") print("Error sending size header.")
return return
@ -277,8 +279,18 @@ def serdownload(fname, ser):
def mput(filenames, ser): def mput(filenames, ser):
for f in filenames: for f in filenames:
resp = set_filename(f, ser) f_encoded = f.encode('utf8')
if resp is None: print("Setting filename", f)
resp = sendcommand(ser, b'S')
if len(resp) == 0:
print("timeout sending 'S' command")
return
if resp != b'S\r\n' and resp != b'> S\r\n':
print("unrecognized response to 'S' command, aborting")
return
resp = sendcommand(ser, f_encoded + b'\r')
if not f_encoded in resp:
print("unrecognized response to filename, aborting")
return return
serload_bin(f, ser) serload_bin(f, ser)
@ -287,94 +299,12 @@ def mput(filenames, ser):
time.sleep(2) time.sleep(2)
def set_filename(f, ser):
f_encoded = f.encode('utf8')
print("Setting filename", f)
resp = commandwait(ser, b'S')
if resp is None:
return None
resp = sendcommand(ser, f_encoded + b'\r')
if not f_encoded in resp:
print("unrecognized response to filename, aborting")
return None
return resp
def getnamedfile(filename, ser):
resp = set_filename(filename, ser)
if resp is None:
return None
serdownload(filename, ser)
def putnamedfile(filename, ser):
resp = set_filename(filename, ser)
if resp is None:
return None
serload_bin(filename, ser)
print("Remote status:")
showdata(ser)
def showdata(ser):
promptseen = False
while not promptseen:
c = ser.read(1)
if c == b'>':
promptseen = True
else:
print(c.decode('utf8'), end='')
rest = ser.read(1) # read trailing space of prompt
def localdir():
result = os.walk(".")
for dirpath, dirnames, filenames in os.walk("."):
for f in filenames:
print(f)
break
def interactive(ser):
done = False
while not done:
args = input("> ").strip().split()
if len(args) > 0:
cmd = args[0]
args.pop(0)
if cmd == 'dir':
if commandwait(ser, b'Y') is None:
return
showdata(ser)
elif cmd == 'get':
if len(args) > 1:
print("exactly one argument required (filename)")
else:
getnamedfile(args[0], ser)
elif cmd == 'put':
if len(args) > 1:
print("exactly one argument required (filename)")
else:
putnamedfile(args[0], ser)
elif cmd == 'ldir':
if len(args) > 0:
print("superfluous argument")
else:
localdir()
elif cmd == 'exit' or cmd == 'x':
done = True
else:
print("Unknown command. Valid commands are: dir get ldir put exit")
if __name__ == "__main__": if __name__ == "__main__":
argparser = argparse.ArgumentParser( argparser = argparse.ArgumentParser(
description='transfer files from/to the Tridora-CPU') description='transfer files from/to the Tridora-CPU')
argparser.add_argument('-d', '--device', help='serial device', default=get_default_device()) argparser.add_argument('-d', '--device', help='serial device', default=get_default_device())
argparser.add_argument('command', choices=['get', 'put', 'mput', 'interactive']) argparser.add_argument('command', choices=['get', 'put', 'mput'])
argparser.add_argument('filename', nargs='*') argparser.add_argument('filename', nargs='+')
args = argparser.parse_args() args = argparser.parse_args()
cmd = args.command cmd = args.command
@ -389,10 +319,8 @@ if __name__ == "__main__":
serload_bin(filenames[0], ser) serload_bin(filenames[0], ser)
elif cmd == 'mput': elif cmd == 'mput':
mput(filenames, ser) mput(filenames, ser)
elif cmd == 'interactive':
interactive(ser)
else: else:
print("should not get here") print("should not get here")
#if cmd is not None: if cmd is not None:
# ser.close() ser.close()

View file

@ -614,7 +614,6 @@ def create_image_with_stuff(imgfile):
slotnr = putfile("../examples/benchmarks.pas", None , f, part, partstart, slotnr) slotnr = putfile("../examples/benchmarks.pas", None , f, part, partstart, slotnr)
slotnr = putfile("../examples/animate.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.inc", None , f, part, partstart, slotnr)
slotnr = putfile("../examples/sprites.s", 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) slotnr = putfile("../examples/background.pict", None , f, part, partstart, slotnr)