Repository: ABelliqueux/nolibgs_hello_worlds
Branch: main
Commit: 48790325a9e3
Files: 186
Total size: 40.3 MB
Directory structure:
gitextract_v4hb7egi/
├── .github/
│ └── workflows/
│ ├── Linux-build-archive.yml
│ ├── linux-build.yml
│ ├── macos-build.yml
│ └── windows-build.yml
├── .gitmodules
├── LICENSE
├── Makefile
├── README.md
├── TIM/
│ ├── README.md
│ ├── TIM16.tim
│ ├── TIM4.tim
│ ├── TIM8.tim
│ ├── bousai.tim
│ └── cubetex.tim
├── VAG/
│ ├── 0_come.vag
│ ├── 1_cuek.vag
│ ├── 2_erro.vag
│ ├── 3_hehe.vag
│ ├── 4_m4a1.vag
│ ├── 5_punc.vag
│ ├── 7_wron.vag
│ ├── 8_yooo.vag
│ ├── README.md
│ ├── hello.vag
│ ├── hello_poly.vag
│ └── poly.vag
├── common.mk
├── hello_2pads/
│ ├── Makefile
│ └── hello_2pads.c
├── hello_bs/
│ ├── Makefile
│ ├── README.md
│ ├── bs/
│ │ ├── bace.bs
│ │ ├── bace.rgb
│ │ ├── bace.tim
│ │ └── bace.yuv
│ ├── hello_bs.c
│ ├── isoconfig.xml
│ └── system.cnf
├── hello_cd/
│ ├── Makefile
│ ├── README.md
│ ├── hello_cd.c
│ ├── isoconfig.xml
│ └── system.cnf
├── hello_cdda/
│ ├── Makefile
│ ├── README.md
│ ├── hello_cdda.c
│ ├── isoconfig.xml
│ └── system.cnf
├── hello_cube/
│ ├── Makefile
│ ├── cube.c
│ └── hello_cube.c
├── hello_cubetex/
│ ├── Makefile
│ ├── cubetex.c
│ └── hello_cubetex.c
├── hello_cubetex_stp/
│ ├── Makefile
│ ├── README.md
│ ├── TIM/
│ │ ├── stpOn8bpp.tim
│ │ ├── stpOnBlack.tim
│ │ └── stpOnNonBlack.tim
│ ├── cubetex.c
│ └── hello_cubetex_stp.c
├── hello_font/
│ ├── Makefile
│ ├── README.md
│ ├── fnt.tim
│ └── hello_font.c
├── hello_fx/
│ ├── Makefile
│ ├── TIM/
│ │ ├── bg.tim
│ │ ├── cube.tim
│ │ └── sky.tim
│ ├── cubetex.c
│ └── hello_fx.c
├── hello_gte_opti/
│ ├── Makefile
│ └── hello_gte_opti.c
├── hello_light/
│ ├── Makefile
│ ├── cube.c
│ └── hello_light.c
├── hello_mod/
│ ├── HIT/
│ │ ├── STAR.HIT
│ │ └── STAR.mod
│ ├── Makefile
│ ├── README.md
│ ├── hello_mod.c
│ └── src/
│ ├── mod.c
│ └── mod.h
├── hello_multi_vag/
│ ├── Makefile
│ ├── README.md
│ └── hello_multi_vag.c
├── hello_multi_xa/
│ ├── Makefile
│ ├── README.md
│ ├── hello_multi_xa.c
│ ├── isoconfig.xml
│ ├── system.cnf
│ └── xa/
│ ├── 5_come.xa
│ ├── 5_erro.xa
│ ├── 5_sile_h.xa
│ ├── 6_cuek.xa
│ ├── 6_hehe.xa
│ ├── 6_sile_h.xa
│ ├── 6_wron.xa
│ ├── 7_m4a1.xa
│ ├── 7_punch.xa
│ ├── 7_sile_h.xa
│ ├── 8_yooo.xa
│ ├── channel5.xa
│ ├── channel6.xa
│ ├── channel7.xa
│ ├── channel8.xa
│ ├── interleave8.txt
│ └── vert8.xa
├── hello_ovl_exec/
│ ├── Makefile
│ ├── README.md
│ ├── common.h
│ ├── hello_ovl_exec.c
│ ├── hello_ovl_world/
│ │ ├── Makefile
│ │ ├── hello_ovl_world.c
│ │ └── hello_world.c
│ ├── hello_poly/
│ │ ├── Makefile
│ │ ├── hello_ovl_poly.c
│ │ └── hello_poly.c
│ ├── hello_tile/
│ │ ├── Makefile
│ │ ├── hello_ovl_tile.c
│ │ └── hello_tile.c
│ ├── isoconfig.xml
│ ├── overlay.ld
│ └── system.cnf
├── hello_pad/
│ ├── Makefile
│ └── hello_pad.c
├── hello_poly/
│ ├── Makefile
│ └── hello_poly.c
├── hello_poly_ft/
│ ├── Makefile
│ └── hello_poly_ft.c
├── hello_poly_fun/
│ ├── Makefile
│ └── hello_poly_fun.c
├── hello_poly_gt/
│ ├── Makefile
│ └── hello_poly_gt.c
├── hello_poly_gt_tw/
│ ├── Makefile
│ └── hello_poly_gt_tw.c
├── hello_poly_inline/
│ ├── Makefile
│ └── hello_poly_inline.c
├── hello_poly_stp/
│ ├── Makefile
│ ├── README.md
│ ├── TIM/
│ │ ├── stpOnAlpha.tim
│ │ ├── stpOnAlphaI.tim
│ │ ├── stpOnBlack.tim
│ │ ├── stpOnCol.tim
│ │ ├── stpOnColIndex.tim
│ │ └── stpOnNonBlack.tim
│ └── hello_poly_stp.c
├── hello_sio/
│ ├── Makefile
│ └── hello_sio.c
├── hello_sprt/
│ ├── Makefile
│ └── hello_sprt.c
├── hello_spu_readback/
│ ├── Makefile
│ ├── README.md
│ ├── hello_spu_readback.c
│ ├── isoconfig.xml
│ └── system.cnf
├── hello_str/
│ ├── Makefile
│ ├── README.md
│ ├── hello_str.c
│ ├── isoconfig.xml
│ ├── str/
│ │ └── copyings.str
│ └── system.cnf
├── hello_strplay/
│ ├── Makefile
│ ├── README.md
│ ├── hello_strplay.c
│ ├── isoconfig.xml
│ ├── str/
│ │ └── copyings.str
│ ├── strplay.c
│ └── system.cnf
├── hello_tile/
│ ├── Makefile
│ └── hello_tile.c
├── hello_vag/
│ ├── Makefile
│ ├── README.md
│ └── hello_vag.c
├── hello_world/
│ ├── Makefile
│ └── hello_world.c
├── hello_xa/
│ ├── Makefile
│ ├── README.md
│ ├── hello_xa.c
│ ├── isoconfig.xml
│ ├── system.cnf
│ └── xa/
│ ├── inter4.xa
│ ├── inter8.xa
│ ├── interleave4.txt
│ └── interleave8.txt
└── includes/
└── CPUMAC.H
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/Linux-build-archive.yml
================================================
name: Linux archive build
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Get repo archive
run: |
wget https://github.com/ABelliqueux/nolibgs_hello_worlds/releases/download/v0.11/nolibgs_hello_worlds.zip
7z x nolibgs_hello_worlds.zip -o${{github.workspace}}
- name: Install mipsel toolchain
run: |
sudo apt-get update
sudo apt-get install gcc-mipsel-linux-gnu g++-mipsel-linux-gnu binutils-mipsel-linux-gnu p7zip cmake build-essential libtinyxml2-10 libtinyxml2-dev
- name: Clone mkpsxiso
uses: GuillaumeFalourd/clone-github-repo-action@v1
with:
owner: 'Lameguy64'
repository: 'mkpsxiso'
- name: Build mkpsxiso
run: |
sudo chown -R runner:docker ${{github.workspace}}/mkpsxiso/
mkdir ${{github.workspace}}/mkpsxiso/build
cmake -DCMAKE_BUILD_TYPE=Release -S ${{github.workspace}}/mkpsxiso/ -B ${{github.workspace}}/mkpsxiso/build
cmake --build ${{github.workspace}}/mkpsxiso/build
echo "${{github.workspace}}/mkpsxiso/build" >> $GITHUB_PATH
- name: Get converted libs
run: |
wget http://psx.arthus.net/sdk/Psy-Q/psyq-4.7-converted-full.7z
7z x psyq-4.7-converted-full.7z -o${{github.workspace}}/nolibgs_hello_worlds/psyq
- name: Make all
run: |
cd ${{github.workspace}}/nolibgs_hello_worlds
make all
================================================
FILE: .github/workflows/linux-build.yml
================================================
name: Linux build
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Install mipsel toolchain
run: |
sudo apt-get update
sudo apt-get install gcc-mipsel-linux-gnu g++-mipsel-linux-gnu binutils-mipsel-linux-gnu p7zip cmake build-essential libtinyxml2-10 libtinyxml2-dev
- name: Fetch submodules
run: git submodule update --init --recursive
- name: Clone mkpsxiso
uses: GuillaumeFalourd/clone-github-repo-action@v1
with:
owner: 'Lameguy64'
repository: 'mkpsxiso'
- name: Build mkpsxiso
run: |
sudo chown -R runner:docker ${{github.workspace}}/mkpsxiso/
git -C ${{github.workspace}}/mkpsxiso/ submodule update --init --recursive
mkdir ${{github.workspace}}/mkpsxiso/build
cmake -DCMAKE_BUILD_TYPE=Release -S ${{github.workspace}}/mkpsxiso/ -B ${{github.workspace}}/mkpsxiso/build
cmake --build ${{github.workspace}}/mkpsxiso/build
echo "${{github.workspace}}/mkpsxiso/build" >> $GITHUB_PATH
- name: Get converted libs
run: |
wget http://psx.arthus.net/sdk/Psy-Q/psyq-4.7-converted-full.7z
7z x psyq-4.7-converted-full.7z -o./psyq
- name: Make all
run: make all
================================================
FILE: .github/workflows/macos-build.yml
================================================
name: macOS build
on:
push:
branches: [ main ]
jobs:
build:
runs-on: macOS-latest
steps:
- uses: actions/checkout@v2
- name: Fetch submodules
run: git submodule update --init --recursive
- name: Install tinyxml2
run : |
brew update --verbose
brew install tinyxml2
- name: Install mipsel binutils
run: |
wget https://raw.githubusercontent.com/grumpycoders/pcsx-redux/a899d09d81cf602ef48e51cda09a6c62638fa5c5/tools/macos-mips/mipsel-none-elf-binutils.rb -O ${{github.workspace}}/mipsel-none-elf-binutils.rb
brew install ${{github.workspace}}/mipsel-none-elf-binutils.rb --debug
- name: Upload mipsel-binutils
uses: actions/upload-artifact@v2
with:
name: mipsel-none-elf-binutils-macos
path: /usr/local/Cellar/mipsel-none-elf-binutils/
- name: Install mipsel gcc
run: |
wget https://raw.githubusercontent.com/grumpycoders/pcsx-redux/a899d09d81cf602ef48e51cda09a6c62638fa5c5/tools/macos-mips/mipsel-none-elf-gcc.rb -O ${{github.workspace}}/mipsel-none-elf-gcc.rb
brew install ${{github.workspace}}/mipsel-none-elf-gcc.rb --debug
- name: Upload mipsel-gcc
uses: actions/upload-artifact@v2
with:
name: mipsel-none-elf-gcc-macos
path: /usr/local/Cellar/mipsel-none-elf-gcc/
- name: Build mkpsxiso
run: |
wget https://github.com/Lameguy64/mkpsxiso/archive/refs/heads/master.zip -O ${{github.workspace}}/master.zip
7z x ${{github.workspace}}/master.zip -o${{github.workspace}}
awk 'n>=4 { print a[n%7] } { a[n%7]=$0; n=n+1 }' ${{github.workspace}}/mkpsxiso-master/CMakeLists.txt >> ${{github.workspace}}/mkpsxiso-master/CMakeLists.txt.tmp
mv -f ${{github.workspace}}/mkpsxiso-master/CMakeLists.txt.tmp ${{github.workspace}}/mkpsxiso-master/CMakeLists.txt
mkdir ${{github.workspace}}/mkpsxiso-master/build
cmake -DCMAKE_BUILD_TYPE=Release -S ${{github.workspace}}/mkpsxiso-master -B ${{github.workspace}}/mkpsxiso-master/build
cmake --build ${{github.workspace}}/mkpsxiso-master/build
echo "${{github.workspace}}/mkpsxiso-master/build" >> $GITHUB_PATH
- name: Upload mkpsxiso-macos
uses: actions/upload-artifact@v2
with:
name: mkpsxiso-macos
path: ${{github.workspace}}/mkpsxiso-master/build
- name: Get converted libs
run: |
wget http://psx.arthus.net/sdk/Psy-Q/psyq-4.7-converted-full.7z
7z x psyq-4.7-converted-full.7z -o./psyq
- name: Make all
run: make all
================================================
FILE: .github/workflows/windows-build.yml
================================================
name: Windows build
on:
push:
branches: [ main ]
jobs:
build:
runs-on: windows-latest
steps:
- uses: actions/checkout@v2
- name: Fetch submodules
run: git submodule update --init --recursive
- name: Get prebuilt stuff
run: |
C:\msys64\usr\bin\wget.exe http://static.grumpycoder.net/pixel/mips/g++-mipsel-none-elf-11.2.0.zip
C:\msys64\usr\bin\wget.exe http://lameguy64.github.io/mkpsxiso/mkpsxiso-1.20.zip
C:\msys64\usr\bin\wget.exe http://psx.arthus.net/sdk/Psy-Q/psyq-4.7-converted-full.7z
7z.exe x g++-mipsel-none-elf-11.2.0.zip -o"$GITHUB_WORKSPACE\mipsel"
7z.exe x mkpsxiso-1.20.zip -o"$GITHUB_WORKSPACE\mkpsxiso"
7z.exe x psyq-4.7-converted-full.7z -o"D:\a\nolibgs_hello_worlds\nolibgs_hello_worlds\psyq"
echo "$GITHUB_WORKSPACE/mipsel/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
echo "$GITHUB_WORKSPACE/mkpsxiso" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
echo $GITHUB_PATH
- name: Make all
run: make all
================================================
FILE: .gitmodules
================================================
[submodule "thirdparty/nugget"]
path = thirdparty/nugget
url = https://github.com/pcsx-redux/nugget.git
================================================
FILE: LICENSE
================================================
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
Copyright (C)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
Copyright (C)
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
.
================================================
FILE: Makefile
================================================
hello_2pads:
$(MAKE) -C hello_2pads
hello_bs:
$(MAKE) -C hello_bs
hello_cd:
$(MAKE) -C hello_cd
hello_cdda:
$(MAKE) -C hello_cdda
hello_cd_exec:
$(MAKE) -C hello_cd_exec
hello_cube:
$(MAKE) -C hello_cube
hello_cubetex:
$(MAKE) -C hello_cubetex
hello_cubetex_stp:
$(MAKE) -C hello_cubetex_stp
hello_font:
$(MAKE) -C hello_font
hello_fx:
$(MAKE) -C hello_fx
hello_gte_opti:
$(MAKE) -C hello_gte_opti
hello_light:
$(MAKE) -C hello_light
hello_mod:
$(MAKE) -C hello_mod
hello_multi_vag:
$(MAKE) -C hello_multi_vag
hello_multi_xa:
$(MAKE) -C hello_multi_xa
hello_ovl_exec:
$(MAKE) -C hello_ovl_exec
hello_pad:
$(MAKE) -C hello_pad
hello_poly:
$(MAKE) -C hello_poly
hello_poly_ft:
$(MAKE) -C hello_poly_ft
hello_poly_fun:
$(MAKE) -C hello_poly_fun
hello_poly_gt:
$(MAKE) -C hello_poly_gt
hello_poly_gt_tw:
$(MAKE) -C hello_poly_gt_tw
hello_poly_inline:
$(MAKE) -C hello_poly_inline
hello_poly_stp:
$(MAKE) -C hello_poly_stp
hello_sio:
$(MAKE) -C hello_sio
hello_sprt:
$(MAKE) -C hello_sprt
hello_spu_readback:
$(MAKE) -C hello_spu_readback
hello_str:
$(MAKE) -C hello_str
hello_strplay:
$(MAKE) -C hello_strplay
hello_tile:
$(MAKE) -C hello_tile
hello_vag:
$(MAKE) -C hello_vag
hello_world:
$(MAKE) -C hello_world
hello_xa:
$(MAKE) -C hello_xa
hello_xa_streaming:
$(MAKE) -C hello_xa_streaming
clean:
$(MAKE) -C hello_2pads clean
$(MAKE) -C hello_cube clean
$(MAKE) -C hello_cubetex clean
$(MAKE) -C hello_cubetex_stp clean
$(MAKE) -C hello_poly_fun clean
$(MAKE) -C hello_gte_opti clean
$(MAKE) -C hello_light clean
$(MAKE) -C hello_mod clean
$(MAKE) -C hello_multi_vag clean
$(MAKE) -C hello_multi_xa clean
$(MAKE) -C hello_ovl_exec cleansub
$(MAKE) -C hello_pad clean
$(MAKE) -C hello_poly clean
$(MAKE) -C hello_poly_ft clean
$(MAKE) -C hello_poly_stp clean
$(MAKE) -C hello_poly_gt clean
$(MAKE) -C hello_poly_gt_tw clean
$(MAKE) -C hello_poly_inline clean
$(MAKE) -C hello_sio clean
$(MAKE) -C hello_sprt clean
$(MAKE) -C hello_spu_readback cleansub
$(MAKE) -C hello_tile clean
$(MAKE) -C hello_vag clean
$(MAKE) -C hello_world clean
$(MAKE) -C hello_cdda cleansub
$(MAKE) -C hello_cd cleansub
$(MAKE) -C hello_xa cleansub
$(MAKE) -C hello_bs cleansub
$(MAKE) -C hello_str cleansub
$(MAKE) -C hello_strplay cleansub
all:
$(MAKE) -C hello_2pads
$(MAKE) -C hello_cube
$(MAKE) -C hello_cubetex
$(MAKE) -C hello_cubetex_stp
$(MAKE) -C hello_poly_fun
$(MAKE) -C hello_gte_opti
$(MAKE) -C hello_light
$(MAKE) -C hello_mod
$(MAKE) -C hello_multi_vag
$(MAKE) -C hello_multi_xa
$(MAKE) -C hello_ovl_exec
$(MAKE) -C hello_pad
$(MAKE) -C hello_poly
$(MAKE) -C hello_poly_ft
$(MAKE) -C hello_poly_gt
$(MAKE) -C hello_poly_gt_tw
$(MAKE) -C hello_poly_inline
$(MAKE) -C hello_sio
$(MAKE) -C hello_sprt
$(MAKE) -C hello_spu_readback
$(MAKE) -C hello_tile
$(MAKE) -C hello_vag
$(MAKE) -C hello_world
$(MAKE) -C hello_cd all
$(MAKE) -C hello_cdda all
$(MAKE) -C hello_xa all
$(MAKE) -C hello_bs all
$(MAKE) -C hello_str all
$(MAKE) -C hello_strplay all
# declare phony rules
.PHONY: hello_2pads hello_bs hello_cd hello_cdda hello_cd_exec hello_cube hello_cubetex hello_cubetex_stp hello_font hello_fx hello_gte_opti hello_light hello_mod hello_multi_vag hello_multi_xa hello_ovl_exec hello_pad hello_poly hello_poly_ft hello_poly_fun hello_poly_gt hello_poly_gt_tw hello_poly_inline hello_poly_stp hello_sio hello_sprt hello_spu_readback hello_str hello_strplay hello_tile hello_vag hello_world hello_xa hello_xa_streaming \
clean all
================================================
FILE: README.md
================================================
# Nolibgs Hello Worlds !
So you want to begin developping on the original PSX but don't know where to start ?
This repo is destined to host a bunch of simple examples, each describing how to do one thing.
The code here will be using **Nugget + PsyQ**, the "Official" Sony SDK but with a modern MIPS toolchain.
We will not be using libGS, the Extended Graphics Library for the graphic stuff...
Instead we'll try to devise methods to reproduce libgs functions. This will not necessarly be more efficient, but we'll learn
a lot more stuff !
## Installation
We'll keep things simple for now. If you want to read about more methods to get things up and running, see the wiki's [Installation methods](https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/Installation-methods) section.
### Windows
#### MIPS toolchain setup
You can setup a pre-built MIPS toolchain by copy-pasting the following into a command prompt:
```
powershell -c "& { iwr https://raw.githubusercontent.com/grumpycoders/pcsx-redux/main/mips.ps1 | iex }"
```
Then, open a new command prompt, and type the following:
```
mips install 11.2.0
```
#### Nugget + PsyQ setup
1. Download the PsyQ converted libraries here : [http://psx.arthus.net/sdk/Psy-Q/psyq-4.7-converted-full.7z](http://psx.arthus.net/sdk/Psy-Q/psyq-4.7-converted-full.7z)
2. Clone the 'nolibgs_hello_worlds' repo with
`git clone https://github.com/ABelliqueux/nolibgs_hello_worlds.git --recursive`
or download this [repository's release](https://github.com/ABelliqueux/nolibgs_hello_worlds/releases/download/v0.11/nolibgs_hello_worlds.zip) and extract **`nolibgs_hello_worlds.zip`**'s content to `C:\no_libgs_hello_worlds\` .
3. Extract the content of `psyq-4.7-converted-full.7z` in `C:\no_libgs_hello_worlds\psyq`. You should now have `C:\no_libgs_hello_worlds\psyq\include` and `C:\no_libgs_hello_worlds\psyq\lib` ;
```
no_libgs_hello_worlds
├── common.mk
├── hello_world
| ├── hello_world.c
| ├── Makefile
├── hello_...
└── psyq
├── lib
| └── *.a
└── include
└── *.h
```
4. Test everything is working by [launching a command prompt](https://www.lifewire.com/how-to-open-command-prompt-2618089), change to the `C:\no_libgs_hello_worlds\` directory with the following command: `cd C:\no_libgs_hello_worlds\`, then type `make` and hit enter.
By default, this should build the `hello_world` example, and you should now have a `hello_world.ps-exe` file in `C:\no_libgs_hello_worlds\hello_world`. This a PSX executable that can be run in an emulator like [pcsx-redux](https://github.com/grumpycoders/pcsx-redux/).
### Linux
#### Install your distribution's MIPS toolchain
In a terminal :
On Debian derivatives (Ubuntu, Mint...) :
```bash
sudo apt-get install make gcc-mipsel-linux-gnu g++-mipsel-linux-gnu binutils-mipsel-linux-gnu
```
On Arch derivatives (Manjaro), the mipsel environment can be installed from [AUR](https://wiki.archlinux.org/index.php/Aur) : [mipsel-linux-gnu-binutils](https://aur.archlinux.org/packages/mipsel-linux-gnu-binutils/) and [mipsel-linux-gnu-gcc](https://aur.archlinux.org/packages/mipsel-linux-gnu-gcc/) using your [AURhelper](https://wiki.archlinux.org/index.php/AUR_helpers) of choice:
```bash
yay -S make mipsel-linux-gnu-binutils mipsel-linux-gnu-gcc
```
#### Nugget + PsyQ setup
Let's do it all on the [CLI](https://en.wikipedia.org/wiki/Command-line_interface) !
1. Install the git client :
```bash
sudo apt-get install git
```
2. Clone this repository :
```bash
git clone https://github.com/ABelliqueux/nolibgs_hello_worlds.git --recursive
```
3. Change to the repo's directory and get the PsyQ converted libraries:
```bash
cd nolibgs_hello_worlds
wget http://psx.schnappy.xyz/sdk/Psy-Q/psyq-4.7-converted-full.7z
7z x psyq-4.7-converted-full.7z -o./psyq
```
4. Try your setup :
```bash
make
```
By default, this should build the `hello_world` example, and you should now have a `hello_world.ps-exe` file in `./hello_world/`. This a PSX executable that can be run in an emulator like [pcsx-redux](https://github.com/grumpycoders/pcsx-redux/).
### MacOS
A [brew](https://brew.sh/) installation script can be found [here.](https://github.com/grumpycoders/pcsx-redux#macos).
## Compilation
In a terminal, `cd` to your psxdev setup directory and type `make all` to build all examples in their respective directories.
Alternatively, you can use `make example_name` to only build that example, i.e : `make hello_poly`.
If you want to remove all the files generated by the compilation process, type `make clean`.
## Upcoming examples
* hello_poly_subdiv (polygon subdivision)
* hello_rsd (rsd format)
# Links and Doc
* [Getting started with PSX dev](https://psx.arthus.net/starting.html)
* [Ps1 dev ressource center](https://ps1.consoledev.net/)
* [PsyQ docs](https://psx.arthus.net/sdk/Psy-Q/DOCS/)
* [psxdev.net](http://psxdev.net/)
* [psxdev Discord](https://discord.com/invite/N2mmwp)
# Credits, thanks, hugs
Everything here was learnt from some more talented persons, mainly but not excluding others that hang around on the [psxdev discord](https://discord.com/channels/642647820683444236/642848627823345684)
Nicolas Noble, Lameguy64, NDR008, Jaby smoll seamonstah, danhans42, rama, sickle, paul, squaresoft74, and lot mores !
================================================
FILE: TIM/README.md
================================================
See here for more informations about the TIM fileformat and tools :
https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/TIM
================================================
FILE: VAG/README.md
================================================
See here for more informations about the VAG fileformat and tools :
https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/VAG
================================================
FILE: common.mk
================================================
# If you change this to exe, you'll have to rename the file ./thirdparty/nugget/ps-exe.ld too.
TYPE = ps-exe
THISDIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
SRCS += $(THISDIR)thirdparty/nugget/common/crt0/crt0.s
SRCS += $(THISDIR)thirdparty/nugget/common/syscalls/printf.s
CPPFLAGS += -I$(THISDIR)thirdparty/nugget/psyq/include -I$(THISDIR)psyq-4_7-converted/include -I$(THISDIR)psyq-4.7-converted-full/include -I$(THISDIR)psyq/include
LDFLAGS += -L$(THISDIR)thirdparty/nugget/psyq/lib -L$(THISDIR)psyq-4_7-converted/lib -L$(THISDIR)psyq-4.7-converted-full/lib -L$(THISDIR)psyq/lib
# add support for NDR008's VScode setup
CPPFLAGS += -I$(THISDIR)../third_party/psyq/include
LDFLAGS += -L$(THISDIR)../third_party/psyq/lib
LDFLAGS += -Wl,--start-group
LDFLAGS += -lapi
LDFLAGS += -lc
LDFLAGS += -lc2
LDFLAGS += -lcard
LDFLAGS += -lcomb
LDFLAGS += -lds
LDFLAGS += -letc
LDFLAGS += -lgpu
LDFLAGS += -lgs
LDFLAGS += -lgte
LDFLAGS += -lgun
LDFLAGS += -lhmd
LDFLAGS += -lmath
LDFLAGS += -lmcrd
LDFLAGS += -lmcx
LDFLAGS += -lpad
LDFLAGS += -lpress
LDFLAGS += -lsio
LDFLAGS += -lsnd
LDFLAGS += -lspu
LDFLAGS += -ltap
LDFLAGS += -lcd
LDFLAGS += -Wl,--end-group
include $(THISDIR)thirdparty/nugget/common.mk
define OBJCOPYME
$(PREFIX)-objcopy -I binary --set-section-alignment .data=4 --rename-section .data=.rodata,alloc,load,readonly,data,contents -O $(FORMAT) -B mips $< $@
endef
# convert TIM file to bin
%.o: %.tim
$(call OBJCOPYME)
# convert VAG files to bin
%.o: %.vag
$(call OBJCOPYME)
# convert HIT to bin
%.o: %.HIT
$(call OBJCOPYME)
================================================
FILE: hello_2pads/Makefile
================================================
TARGET = hello_2pads
SRCS = hello_2pads.c \
include ../common.mk
================================================
FILE: hello_2pads/hello_2pads.c
================================================
// hello_libpad example
//
// We're using libpad this time.
// You can use the classic controller, analog, wheel, gun buttons or mouse
//
// Schnappy - 12/2020
//
// Based on : ../psyq/addons/scea/CNTRL/PAD.C
#include
#include
#include
#include
#include
#include
#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL
#define SCREENXRES 320
#define SCREENYRES 240
#define CENTERX SCREENXRES/2
#define CENTERY SCREENYRES/2
#define MARGINX 32 // margins for text display
#define MARGINY 32
#define FONTSIZE 8 * 7 // Text Field Height
#define OTLEN 8 // Ordering Table Length
DISPENV disp[2]; // Double buffered DISPENV and DRAWENV
DRAWENV draw[2];
u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes
char primbuff[2][32768]; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes
char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0]
short db = 0; // index of which buffer is used, values 0, 1
// Pad stuff
// Structure for RAW hardware-based light gun position values
typedef struct
{
unsigned short v_count; // Y-axis (vertical scan counter)
unsigned short h_count; // H-axis (horizontal pixel clock value)
} Gun_Position;
// Structure for storing processed controller data
typedef struct
{
int xpos, ypos; // Stored position for sprite(s)
int xpos2, ypos2; // controlled by this controller.
unsigned char status; // These 8 values are obtained
unsigned char type; // directly from the controller
unsigned char button1; // buffer we installed with InitPAD.
unsigned char button2;
unsigned char analog0;
unsigned char analog1;
unsigned char analog2;
unsigned char analog3;
} Controller_Data;
// All-purpose controller data buffer
typedef struct
{
unsigned char pad[34]; // 8-bytes w/o Multi-Tap, 34-bytes w/Multi-Tap
} Controller_Buffer;
Controller_Buffer controllers[2]; // Buffers for reading controllers
Controller_Data theControllers[8]; // Processed controller data
void init(void)
{
ResetGraph(0);
SetDefDispEnv(&disp[0], 0, 0, SCREENXRES, SCREENYRES);
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[1], 0, 0, SCREENXRES, SCREENYRES);
if (VMODE)
{
SetVideoMode(MODE_PAL);
disp[0].screen.y += 8;
disp[1].screen.y += 8;
}
SetDispMask(1); // Display on screen
setRGB0(&draw[0], 50, 50, 50);
setRGB0(&draw[1], 50, 50, 50);
draw[0].isbg = 1;
draw[1].isbg = 1;
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
FntLoad(960, 0);
FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 );
}
void display(void)
{
DrawSync(0);
VSync(0);
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
DrawOTag(&ot[db][OTLEN - 1]);
db = !db;
nextpri = primbuff[db];
}
void get_digital_direction( Controller_Data *c, int buttondata ) // get analog stick values
{
int i;
i = ~(buttondata);
if( i & 0x80 )
c->xpos -= 1;
if( i & 0x20 )
c->xpos += 1;
if( i & 0x40 )
c->ypos += 1;
if( i & 0x10 )
c->ypos -= 1;
}
void read_controller( Controller_Data *c, unsigned char *buf, int port ) // get the raw values from controller
{
register int mouse_x, mouse_y, x;
register Gun_Position *g;
c->status = buf[0]; // Copy over raw controller data
c->type = buf[1];
c->button1 = buf[2];
c->button2 = buf[3];
c->analog0 = buf[4];
c->analog1 = buf[5];
c->analog2 = buf[6];
c->analog3 = buf[7];
if( buf[0] == 0xff ) // If controller returns BAD status then bail on it.
{
c->type = 0;
return;
}
// Look at the controller type code & process controller data as indicated
switch( c->type )
{
case 0x12: // Sony Mouse
mouse_x = buf[4];
mouse_y = buf[5];
if( mouse_x & 0x80 )
mouse_x |= 0xffffff80;
if( mouse_y & 0x80 )
mouse_y |= 0xffffff80;
c->xpos += mouse_x;
c->ypos += mouse_y;
break;
case 0x23: // Namco negCon
// Steering wheel
// Sankyo Pachinko controler
get_digital_direction( c, buf[2] );
break;
case 0x53: // Analog 2-stick
get_digital_direction( c, buf[2] );
break;
case 0x41: // Standard Sony PAD controller
get_digital_direction( c, buf[2] );
break;
default: // If don't know what it is, treat it like standard controller
get_digital_direction( c, buf[2] );
break;
}
}
int main(void)
{
TILE * PADL; // Tile primitives
TILE * TRIGGERL;
TILE * PADR;
TILE * TRIGGERR;
TILE * START, * SELECT;
init();
InitPAD(controllers[0].pad, 34, controllers[1].pad, 34);
StartPAD();
while (1)
{
read_controller( &theControllers[0], &controllers[0].pad[0], 0 ); // Read controllers
read_controller( &theControllers[1], &controllers[1].pad[0], 1 );
ClearOTagR(ot[db], OTLEN);
// D-cross
PADL = (TILE *)nextpri;
setTile(PADL);
setRGB0(PADL, 80, 180, 255);
setXY0(PADL, CENTERX - 80, CENTERY);
setWH(PADL, 24, 24);
addPrim(ot[db], PADL);
nextpri += sizeof(TILE);
// L1+L2
TRIGGERL = (TILE *)nextpri;
setTile(TRIGGERL);
setRGB0(TRIGGERL, 255, 0, 0);
setXY0(TRIGGERL, CENTERX - 80, CENTERY - 80);
setWH(TRIGGERL, 24, 24);
addPrim(ot[db], TRIGGERL);
nextpri += sizeof(TILE);
// /\, X, O, []
PADR = (TILE *)nextpri;
setTile(PADR);
setRGB0(PADR, 0, 255, 0);
setXY0(PADR, CENTERX + 50, CENTERY);
setWH(PADR, 24, 24);
addPrim(ot[db], PADR);
nextpri += sizeof(TILE);
// R1+R2
TRIGGERR = (TILE *)nextpri;
setTile(TRIGGERR);
setRGB0(TRIGGERR, 255, 0, 255);
setXY0(TRIGGERR, CENTERX + 50, CENTERY -80);
setWH(TRIGGERR, 24, 24);
addPrim(ot[db], TRIGGERR);
nextpri += sizeof(TILE);
// START + SELECT
START = (TILE *)nextpri;
setTile(START);
setRGB0(START, 240, 240, 240);
setXY0(START, CENTERX - 16, CENTERY - 36);
setWH(START, 24, 24);
addPrim(ot[db], START);
nextpri += sizeof(TILE);
// D-pad
switch(theControllers[0].button1){
case 0xDF: // Right
PADL->x0 = CENTERX - 64;
break;
case 0x7F: // Left
PADL->x0 = CENTERX - 96;
break;
case 0xEF: // Up
PADL->y0 = CENTERY - 16;
break;
case 0xBF: // Down
PADL->y0 = CENTERY + 16;
break;
// Start & Select
case 0xF7:
START->w = 32; START->h = 32;START->x0 -= 4;START->y0 -= 4; // START
break;
case 0xFE: // SELECT
START->r0 = 0;
break;
// Dualshock L3 + R3
case 0xFD: // L3
TRIGGERL->w += 10;
TRIGGERL->h += 10;
break;
case 0xFB: //R3
TRIGGERR->w += 10;
TRIGGERR->h += 10;
break;
}
// Buttons
switch(theControllers[0].button2){
case 0xDF: // ⭘
PADR->x0 = CENTERX + 66;
break;
case 0x7F: // ⬜
PADR->x0 = CENTERX + 34;
break;
case 0xEF: // △
PADR->y0 = CENTERY - 16;
break;
case 0xBF: // ╳
PADR->y0 = CENTERY + 16;
break;
// Shoulder buttons
case 0xFB: // L1
TRIGGERL->y0 = CENTERY - 64;
break;
case 0xFE: // L2
TRIGGERL->y0 = CENTERY - 96;
break;
case 0xF7: // R1
TRIGGERR->y0 = CENTERY - 96;
break;
case 0xFD: // R2
TRIGGERR->y0 = CENTERY - 64;
break;
// Mouse buttons
case 0xF4: // Mouse Left click
PADL->w += 10;
PADL->h += 10;
break;
case 0xF8: // Mouse Right click
PADL->w -= 10;
PADL->h -= 10;
break;
}
FntPrint("Hello 2 pads!\n\n");
FntPrint( "Pad 1 : %02x\nButtons:%02x %02x, Stick:%02x %02x %02x %02x\n",
theControllers[0].type, // Controller type : 00 == none, 41 == standard, 73 == analog/dualshock, 12 == mouse, 23 == steering wheel, 63 == gun, 53 == analog joystick
theControllers[0].button1, //
theControllers[0].button2,
theControllers[0].analog0,
theControllers[0].analog1,
theControllers[0].analog2,
theControllers[0].analog3 );
FntPrint( "Pad 2 : %02x\nButtons:%02x %02x, Stick:%02x %02x %02x %02x\n",
theControllers[1].type, // Controller type : 00 == none, 41 == standard, 73 == analog/dualshock, 12 == mouse, 23 == steering wheel, 63 == gun, 53 == analog joystick
theControllers[1].button1, //
theControllers[1].button2,
theControllers[1].analog0, // R3 horizontal
theControllers[1].analog1, // R3 vertical
theControllers[1].analog2, // L3 horizontal
theControllers[1].analog3 ); // L3 vertical
FntFlush(-1);
display();
}
return 0;
}
================================================
FILE: hello_bs/Makefile
================================================
.PHONY: all cleansub
all:
mkpsxiso -y ./isoconfig.xml
cleansub:
$(MAKE) clean
rm -f hello_bs.cue hello_bs.bin
TARGET = hello_bs
SRCS = hello_bs.c \
include ../common.mk
================================================
FILE: hello_bs/README.md
================================================
# Loading a BS still image
You need [mkpsxiso](https://github.com/Lameguy64/mkpsxiso) in your $PATH to generate a PSX disk image.
## Compile
```bash
make all
```
## Clean directory
```bash
make cleansub
```
## Converting a still image to BS
`MC32` can convert these formats to BS : TIM, RGB, YUV.
### Image > TIM with img2tim
Convert your image to a 24bpp TIM with [`img2tim`](https://github.com/Lameguy64/img2tim):
```bash
img2tim -bpp 24 -o output.tim input.png
```
Then use `MC32` as instructed below.
Result :
```bash
identify bace.tim
bace.tim TIM 320x240 320x240+0+0 8-bit sRGB 230420B 0.000u 0:00.000
```
### Image > RGB with imagemagick
You can convert your image to RGB with:
```bash
convert input.png RGB:output.rgb
```
Result :
```bash
identify -size 320x240 -depth 8 RGB:bace.rgb
RGB:bace.rgb=>bace.rgb RGB 320x240 320x240+0+0 8-bit sRGB 230400B 0.000u 0:00.003
```
### Image > YUV422 UYVY with imagemagick
You can convert your image to YUV with:
```bash
convert input.png UYVY:output.yuv
```
Result :
```bash
dentify -size 320x240 UYVY:bace.yuv
UYVY:bace.yuv=>bace.yuv UYVY 320x240 320x240+0+0 8-bit YCbCr 153600B 0.000u 0:00.005
```
### TIM/RGB/UYVY > BS conversion
Use the [`MC32` tool](http://psx.arthus.net/tools/pimp-psx.zip) conversion tool to import the image, specifying the right dimensions, and convert to `bs` with those settings :
**Note that a BS image must have a width and height that is a multiple of 16**
```
Input: RGB, Output: bs
MDEC version : 2
Custom: Size in sectors or (2048 * sector number) bytes, Variable frame size
```

## Sources & Refs
img2tim : https://github.com/Lameguy64/img2tim
MC32 : http://psx.arthus.net/tools/pimp-psx.zip
mdecnote : http://psx.arthus.net/sdk/Psy-Q/DOCS/TECHNOTE/mdecnote.pdf
PSX RGB and YUV format : http://psx.arthus.net/sdk/Psy-Q/DOCS/Devrefs/Dataconv.pdf , p.68
YUV pixel format : https://www.fourcc.org/pixel-format/yuv-uyvy/
RGB pixelformat : https://www.fourcc.org/pixel-format/rgb-bi_rgb/
RGB<>YUV conversion formulas : https://www.fourcc.org/fccyvrgb.php
================================================
FILE: hello_bs/hello_bs.c
================================================
// Load a BS file from CD, decompress and display it.
// Schnappy 07-2021
#include
#include
#include
#include
#include
// CD library
#include
// CODEC library
#include
#include
#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL
#define SCREENXRES 320 // Screen width
#define SCREENYRES 240 + (VMODE << 4) // Screen height : If VMODE is 0 = 240, if VMODE is 1 = 256
#define CENTERX SCREENXRES/2 // Center of screen on x
#define CENTERY SCREENYRES/2 // Center of screen on y
#define MARGINX 8 // margins for text display
#define MARGINY 16
#define FONTSIZE 8 * 7 // Text Field Height
DISPENV disp[2]; // Double buffered DISPENV and DRAWENV
DRAWENV draw[2];
short db = 0; // index of which buffer is used, values 0, 1
// CD specifics
#define CD_SECTOR_SIZE 2048
// Converting bytes to sectors SECTOR_SIZE is defined in words, aka int
#define BtoS(len) ( ( len + CD_SECTOR_SIZE - 1 ) / CD_SECTOR_SIZE )
// Name of file to load
static char * loadFile;
// libcd's CD file structure contains size, location and filename
CdlFILE filePos = {0};
//~ struct EXEC * exeStruct;
// Define start address of allocated memory
// Let's use an array so we don't have to worry about using a memory segment that's already in use.
static unsigned char ramAddr[0x40000]; // https://discord.com/channels/642647820683444236/663664210525290507/864936962199781387
// We could also set a memory address manually, but we have to make sure this won't get in the way of other routines.
// void * ramAddr = (void *)0x80030D40;
// Load data to this buffer
u_long * dataBuffer;
// Those are not strictly needed, but we'll use them to see the commands results.
// They could be replaced by a 0 in the various functions they're used with.
u_char CtrlResult[8];
// Value returned by CDread() - 1 is good, 0 is bad
int CDreadOK = 0;
// Value returned by CDsync() - Returns remaining sectors to load. 0 is good.
int CDreadResult = 0;
// BS decompression
// Store size of uncompressed data
long bsBufferSize;
// Allocated memory address
void * bsWorkBuffer;
// Define image draw area
RECT bsDrawArea = { 0, 0, 16, SCREENYRES};
// Used to store a 16x240 image strip
u_long bsStrip[16*240];
void init(void)
{
ResetGraph(0); // Initialize drawing engine with a complete reset (0)
SetDefDispEnv(&disp[0], 0, 0 , SCREENXRES, SCREENYRES); // Set display area for both &disp[0] and &disp[1]
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES); // &disp[0] is on top of &disp[1]
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES); // Set draw for both &draw[0] and &draw[1]
SetDefDrawEnv(&draw[1], 0, 0 , SCREENXRES, SCREENYRES); // &draw[0] is below &draw[1]
// Set video mode
if (VMODE){ SetVideoMode(MODE_PAL);}
SetDispMask(1); // Display on screen
setRGB0(&draw[0], 155, 0, 150); // set color for first draw area
setRGB0(&draw[1], 155, 0, 150); // set color for second draw area
draw[0].isbg = 0; // set mask for draw areas. 1 means repainting the area with the RGB color each frame
draw[1].isbg = 0;
PutDispEnv(&disp[db]); // set the disp and draw environnments
PutDrawEnv(&draw[db]);
FntLoad(960, 0); // Load font to vram at 960,0(+128)
FntOpen(MARGINX, MARGINY, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); // FntOpen(x, y, width, height, black_bg, max. nbr. chars
}
void display(void)
{
DrawSync(0); // Wait for all drawing to terminate
VSync(0); // Wait for the next vertical blank
PutDispEnv(&disp[db]); // set alternate disp and draw environnments
PutDrawEnv(&draw[db]);
db = !db; // flip db value (0 or 1)
}
int main(void)
{
// Init display
init();
// Init CD system
CdInit();
// Init heap
InitHeap((u_long *)ramAddr, sizeof(ramAddr));
// If the other method was chosen at l.39
// InitHeap((void *)0x80030D40, 0x40000);
// Set name of file to load
loadFile = "\\BACE.BS;1";
// Get file position from filename
CdSearchFile( &filePos, loadFile);
// Allocate memory
dataBuffer = malloc( BtoS(filePos.size) * CD_SECTOR_SIZE );
// Issue CdlSetloc CDROM command : Set the seek target position
// Beware of a misnomed 'sector' member in the CdlLOC struct that should really be named 'frame'.
// https://discord.com/channels/642647820683444236/663664210525290507/864912470996942910
CdControl(CdlSetloc, (u_char *)&filePos.pos, CtrlResult);
// Read data and load it to dataBuffer
CDreadOK = CdRead( (int)BtoS(filePos.size), (u_long *)dataBuffer, CdlModeSpeed );
// Wait for operation to complete
CDreadResult = CdReadSync(0, 0);
// Image Decompression
// Initialize image processing subsystem
DecDCTReset(0);
// Find the needed buffer size
bsBufferSize = DecDCTBufSize(dataBuffer);
// Allocate buffer size at &bsWorkBuffer
bsWorkBuffer = malloc( bsBufferSize );
// Decode Huffman (also called VLC: variable length coding ) compressed image
DecDCTvlc( dataBuffer, (u_long *) bsWorkBuffer );
// Send decoded data to MDEC for RLE decoding.
DecDCTin( (u_long*) bsWorkBuffer, 0);
// Fetch decoded image in 16x240 strips
for( bsDrawArea.x = 0; bsDrawArea.x < SCREENXRES; bsDrawArea.x += 16 ){
// Request decoded data from MDEC
// Request 16 * 240 pixel high lines.
// But size is in long words (4B), so divide by 2 to get words (2B) ?
DecDCTout( bsStrip, (16*SCREENYRES)/2);
// Wait for transfer to complete
DecDCToutSync(0);
// Load image data to fb
LoadImage( &bsDrawArea, bsStrip );
}
free( bsWorkBuffer );
while (1) // infinite loop
{
// Copy BS image to the other buffer
MoveImage2(&disp[db].disp, 0, disp[!db].disp.y );
DrawSync(0);
FntPrint("Hello BS! \n");
// Print filesize in bytes/sectors
FntPrint("Bs Size: %dB sectors: %d\n", filePos.size, BtoS(filePos.size));
// Print heap and buffer addresses
FntPrint("Heap: %x - Buf: %x\n", ramAddr, dataBuffer);
FntPrint("bsWork: %x\nbsBufSize: %dB\n", bsWorkBuffer, bsBufferSize);
FntFlush(-1); // Draw print stream
display(); // Execute display()
}
return 0;
}
================================================
FILE: hello_bs/isoconfig.xml
================================================
================================================
FILE: hello_bs/system.cnf
================================================
BOOT=cdrom:\SCES_313.37;1
TCB=4
EVENT=10
STACK=801FFFF0
================================================
FILE: hello_cd/Makefile
================================================
.PHONY: all cleansub
all:
mkpsxiso -y ./isoconfig.xml
cleansub:
$(MAKE) clean
rm -f hello_cd.cue hello_cd.bin
TARGET = hello_cd
SRCS = hello_cd.c \
include ../common.mk
================================================
FILE: hello_cd/README.md
================================================
## Loading a file from CD
You need [mkpsxiso](https://github.com/Lameguy64/mkpsxiso) in your $PATH to generate a PSX disk image.
### Compile
```bash
make all
```
### Clean directory
```bash
make cleansub
```
### Adding data to the CD
In `isoconfig.xml`, in the data track's `directory_tree` section, use this syntax :
```xml
```
See https://github.com/Lameguy64/mkpsxiso/blob/master/examples/example.xml for more details.
================================================
FILE: hello_cd/hello_cd.c
================================================
// Load files from CD and execute them
// Schnappy 07-2021
#include
#include
#include
#include
#include
#include
// CD library
#include
#include
#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL
#define SCREENXRES 320 // Screen width
#define SCREENYRES 240 + (VMODE << 4) // Screen height : If VMODE is 0 = 240, if VMODE is 1 = 256
#define CENTERX SCREENXRES/2 // Center of screen on x
#define CENTERY SCREENYRES/2 // Center of screen on y
#define MARGINX 0 // margins for text display
#define MARGINY 32
#define FONTSIZE 8 * 7 // Text Field Height
DISPENV disp[2]; // Double buffered DISPENV and DRAWENV
DRAWENV draw[2];
short db = 0; // index of which buffer is used, values 0, 1
// CD specifics
#define CD_SECTOR_SIZE 2048
// Converting bytes to sectors SECTOR_SIZE is defined in words, aka int
#define BtoS(len) ( ( len + CD_SECTOR_SIZE - 1 ) / CD_SECTOR_SIZE )
// Name of file to load
static char * loadFile;
// libcd's CD file structure contains size, location and filename
CdlFILE filePos = {0};
//~ struct EXEC * exeStruct;
// Define start address of allocated memory
// Let's use an array so we don't have to worry about using a memory segment that's already in use.
static unsigned char ramAddr[0x40000]; // https://discord.com/channels/642647820683444236/663664210525290507/864936962199781387
// We could also set a memory address manually, but we have to make sure this won't get in the way of other routines.
// void * ramAddr = (void *)0x80030D40;
// Load data to this buffer
u_long * dataBuffer;
// Those are not strictly needed, but we'll use them to see the commands results.
// They could be replaced by a 0 in the various functions they're used with.
u_char CtrlResult[8];
// Value returned by CDread() - 1 is good, 0 is bad
int CDreadOK = 0;
// Value returned by CDsync() - Returns remaining sectors to load. 0 is good.
int CDreadResult = 0;
void init(void)
{
ResetGraph(0); // Initialize drawing engine with a complete reset (0)
SetDefDispEnv(&disp[0], 0, 0 , SCREENXRES, SCREENYRES); // Set display area for both &disp[0] and &disp[1]
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES); // &disp[0] is on top of &disp[1]
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES); // Set draw for both &draw[0] and &draw[1]
SetDefDrawEnv(&draw[1], 0, 0 , SCREENXRES, SCREENYRES); // &draw[0] is below &draw[1]
// Set video mode
if (VMODE){ SetVideoMode(MODE_PAL);}
SetDispMask(1); // Display on screen
setRGB0(&draw[0], 255, 50, 50); // set color for first draw area
setRGB0(&draw[1], 255, 50, 50); // set color for second draw area
draw[0].isbg = 1; // set mask for draw areas. 1 means repainting the area with the RGB color each frame
draw[1].isbg = 1;
PutDispEnv(&disp[db]); // set the disp and draw environnments
PutDrawEnv(&draw[db]);
FntLoad(960, 0); // Load font to vram at 960,0(+128)
FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); // FntOpen(x, y, width, height, black_bg, max. nbr. chars
}
void display(void)
{
DrawSync(0); // Wait for all drawing to terminate
VSync(0); // Wait for the next vertical blank
PutDispEnv(&disp[db]); // set alternate disp and draw environnments
PutDrawEnv(&draw[db]);
db = !db; // flip db value (0 or 1)
}
int main(void)
{
// Init display
init();
// Init CD system
CdInit();
// Init heap
InitHeap((u_long *)ramAddr, sizeof(ramAddr));
// If the other method was chosen at l.39
// InitHeap((void *)0x80030D40, 0x40000);
// Set name of file to load
loadFile = "\\HELO.DAT;1";
// Get file position from filename
CdSearchFile( &filePos, loadFile);
// Allocate memory
dataBuffer = malloc( BtoS(filePos.size) * CD_SECTOR_SIZE );
// Issue CdlSetloc CDROM command : Set the seek target position
// Beware of a misnomed 'sector' member in the CdlLOC struct that should really be named 'frame'.
// https://discord.com/channels/642647820683444236/663664210525290507/864912470996942910
CdControl(CdlSetloc, (u_char *)&filePos.pos, CtrlResult);
// Read data and load it to dataBuffer
CDreadOK = CdRead( (int)BtoS(filePos.size), (u_long *)dataBuffer, CdlModeSpeed );
// Wait for operation to complete
CDreadResult = CdReadSync(0, 0);
while (1) // infinite loop
{
// Print the content of the loaded file - See HELO.DAT
FntPrint("%s%d\n", (char *)dataBuffer, VSync(-1));
// Print heap and buffer addresses
FntPrint("Heap: %x - Buf: %x\n", ramAddr, dataBuffer);
// Print returned values
FntPrint("CdCtrl: %d\nRead : %d %d\n", CtrlResult[0], CDreadOK, CDreadResult);
// Print filesize in bytes/sectors
FntPrint("Size: %dB sectors: %d", filePos.size, BtoS(filePos.size));
FntFlush(-1); // Draw print stream
display(); // Execute display()
}
return 0;
}
================================================
FILE: hello_cd/isoconfig.xml
================================================
================================================
FILE: hello_cd/system.cnf
================================================
BOOT=cdrom:\SCES_313.37;1
TCB=4
EVENT=10
STACK=801FFFF0
================================================
FILE: hello_cdda/Makefile
================================================
.PHONY: all cleansub
all:
mkpsxiso -y ./isoconfig.xml
cleansub:
$(MAKE) clean
rm -f hello_cdda.cue hello_cdda.bin
TARGET = hello_cdda
SRCS = hello_cdda.c \
include ../common.mk
================================================
FILE: hello_cdda/README.md
================================================
## Compiling
You need [mkpsxiso](https://github.com/Lameguy64/mkpsxiso) in your $PATH to generate a PSX disk image.
Typing
```bash
make
```
in a terminal will compile and generate the bin/cue files.
Typing
```bash
make cleansub
```
will clean the current directory
## Creating the disk image
```bash
mkpsxiso -y isoconfig.xml
```
## Using ffmpeg to generate a CDDA compliant Wav file
Needed Specification : `RIFF (little-endian) data, WAVE audio, Microsoft PCM, 16 bit, stereo 44100 Hz`
### Conversion
```bash
ffmpeg -i input.mp3 -acodec pcm_s16le -ac 2 -ar 44100 output.wav
```
### Merging two mono audio channels into one stereo channel
```bash
ffmpeg -i herb.wav.new -filter_complex "[0:a][0:a]amerge=inputs=2[a]" -map "[a]" herbi.wav
```
### Adding the audio track to the CD
Add a track section **after** your data track in `isoconfig.xml` :
```xml
```
## Music credits
Track 1 :
Beach Party by Kevin MacLeod
Link: https://incompetech.filmmusic.io/song/3429-beach-party
License: https://filmmusic.io/standard-license
Track 2:
Funk Game Loop by Kevin MacLeod
Link: https://incompetech.filmmusic.io/song/3787-funk-game-loop
License: https://filmmusic.io/standard-license
================================================
FILE: hello_cdda/hello_cdda.c
================================================
// CDDA track playback example
// Schnappy 07-2021
#include
#include
#include
#include
#include
#include
// CD library
#include
// SPU library
#include
#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL
#define SCREENXRES 320 // Screen width
#define SCREENYRES 240 + (VMODE << 4) // Screen height : If VMODE is 0 = 240, if VMODE is 1 = 256
#define CENTERX SCREENXRES/2 // Center of screen on x
#define CENTERY SCREENYRES/2 // Center of screen on y
#define MARGINX 0 // margins for text display
#define MARGINY 32
#define FONTSIZE 8 * 7 // Text Field Height
DISPENV disp[2]; // Double buffered DISPENV and DRAWENV
DRAWENV draw[2];
short db = 0; // index of which buffer is used, values 0, 1
// SPU attributes
SpuCommonAttr spuSettings;
// CD tracks
int playing = -1;
int tracks[] = {2, 0}; // Track to play , 1 is data, 2 is beach.wav, 3 is funk.wav. See isoconfig.xml
void init(void)
{
ResetGraph(0); // Initialize drawing engine with a complete reset (0)
SetDefDispEnv(&disp[0], 0, 0 , SCREENXRES, SCREENYRES); // Set display area for both &disp[0] and &disp[1]
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES); // &disp[0] is on top of &disp[1]
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES); // Set draw for both &draw[0] and &draw[1]
SetDefDrawEnv(&draw[1], 0, 0 , SCREENXRES, SCREENYRES); // &draw[0] is below &draw[1]
// Set video mode
if (VMODE){ SetVideoMode(MODE_PAL);}
SetDispMask(1); // Display on screen
setRGB0(&draw[0], 50, 50, 50); // set color for first draw area
setRGB0(&draw[1], 50, 50, 50); // set color for second draw area
draw[0].isbg = 1; // set mask for draw areas. 1 means repainting the area with the RGB color each frame
draw[1].isbg = 1;
PutDispEnv(&disp[db]); // set the disp and draw environnments
PutDrawEnv(&draw[db]);
FntLoad(960, 0); // Load font to vram at 960,0(+128)
FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); // FntOpen(x, y, width, height, black_bg, max. nbr. chars
}
void display(void)
{
DrawSync(0); // Wait for all drawing to terminate
VSync(0); // Wait for the next vertical blank
PutDispEnv(&disp[db]); // set alternate disp and draw environnments
PutDrawEnv(&draw[db]);
db = !db; // flip db value (0 or 1)
}
int main(void)
{
int count = 0;
int flip = 1;
CdlLOC loc[100];
int ntoc;
// Init display
init();
// Init extended CD system
CdInit();
// Init Spu
SpuInit();
// Set master & CD volume to max
spuSettings.mask = (SPU_COMMON_MVOLL | SPU_COMMON_MVOLR | SPU_COMMON_CDVOLL | SPU_COMMON_CDVOLR | SPU_COMMON_CDMIX);
// Master volume should be in range 0x0000 - 0x3fff
spuSettings.mvol.left = 0x3fff;
spuSettings.mvol.right = 0x3fff;
// Cd volume should be in range 0x0000 - 0x7fff
spuSettings.cd.volume.left = 0x7fff;
spuSettings.cd.volume.right = 0x7fff;
// Enable CD input ON
spuSettings.cd.mix = SPU_ON;
// Apply settings
SpuSetCommonAttr(&spuSettings);
// Set transfer mode
SpuSetTransferMode(SPU_TRANSFER_BY_DMA);
// CD Playback setup
// Play second audio track
// Get CD TOC
while ((ntoc = CdGetToc(loc)) == 0) { /* Read TOC */
FntPrint("No TOC found: please use CD-DA disc...\n");
}
// Prevent out of bound pos
for (int i = 1; i < ntoc; i++) {
CdIntToPos(CdPosToInt(&loc[i]) - 74, &loc[i]);
}
// Those array will hold the return values of the CD commands
u_char param[4], result[8];
// Set CD parameters ; Report Mode ON, CD-DA ON. See LibeOver47.pdf, p.188
param[0] = CdlModeRept|CdlModeDA;
CdControlB (CdlSetmode, param, 0); /* set mode */
VSync (3); /* wait three vsync times */
// Play second track in toc array
CdControlB (CdlPlay, (u_char *)&loc[3], 0); /* play */
while (1) // infinite loop
{
count ++;
// Get current track number ~ every second
// See LibeOver47.pdf, p.188
if (count%50 == 0){
CdReady(1, &result[0]);
// current track number can also be obtained with
// CdControlB (CdlGetlocP, 0, &result[0]);
}
// Switch track after ~ 20 seconds
if (count%(50*20) == 0){
// Flip can have a value of 1 or -1
flip *= -1;
uint8_t nextTrackIndex = result[1] + flip;
// Send CD command to switch track
CdControlB (CdlPlay, (u_char *)&loc[ nextTrackIndex ], 0);
}
FntPrint("Hello CDDA !\n"); // Send string to print stream
FntPrint("Playback status: %d", result[1]); // Send string to print stream
FntFlush(-1); // Draw printe stream
display(); // Execute display()
}
return 0;
}
================================================
FILE: hello_cdda/isoconfig.xml
================================================
================================================
FILE: hello_cdda/system.cnf
================================================
BOOT=cdrom:\SCES_313.37;1
TCB=4
EVENT=10
STACK=801FFFF0
================================================
FILE: hello_cube/Makefile
================================================
TARGET = hello_cube
SRCS = hello_cube.c \
include ../common.mk
================================================
FILE: hello_cube/cube.c
================================================
SVECTOR modelCube_mesh[] = {
{ -128,128,128 },
{ 128,128,128 },
{ 128,128,-128 },
{ -128,128,-128 },
{ -128,-128,128 },
{ 128,-128,128 },
{ 128,-128,-128 },
{ -128,-128,-128 }
};
SVECTOR modelCube_normal[] = {
2365,-2365,-2365, 0,
-2365,-2365,-2365, 0,
-2365,-2365,2365, 0,
2365,-2365,2365, 0,
2365,2365,-2365, 0,
-2365,2365,-2365, 0,
-2365,2365,2365, 0,
2365,2365,2365, 0
};
CVECTOR modelCube_color[] = {
255,237,0, 0,
255,235,0, 0,
255,236,0, 0,
255,2,0, 0,
254,3,0, 0,
255,8,0, 0,
229,0,255, 0,
229,0,255, 0,
229,0,255, 0,
5,16,250, 0,
0,12,255, 0,
0,12,255, 0,
4,251,25, 0,
0,255,26, 0,
0,255,26, 0,
0,248,255, 0,
0,248,255, 0,
0,248,255, 0,
255,237,0, 0,
255,237,0, 0,
255,235,0, 0,
255,2,0, 0,
255,6,2, 0,
254,3,0, 0,
229,0,255, 0,
232,21,232, 0,
229,0,255, 0,
5,16,250, 0,
2,13,253, 0,
0,12,255, 0,
4,251,25, 0,
0,255,26, 0,
0,255,26, 0,
0,248,255, 0,
0,248,255, 0,
0,248,255, 0
};
int modelCube_index[] = {
0,2,3,
7,5,4,
4,1,0,
5,2,1,
2,7,3,
0,7,4,
0,1,2,
7,6,5,
4,5,1,
5,6,2,
2,6,7,
0,3,7
};
TMESH modelCube = {
modelCube_mesh,
modelCube_normal,
0,
modelCube_color,
12
};
SVECTOR modelCube1_mesh[] = {
{ -128,128,128 },
{ 128,128,128 },
{ 128,128,-128 },
{ -128,128,-128 },
{ -128,-128,128 },
{ 128,-128,128 },
{ 128,-128,-128 },
{ -128,-128,-128 }
};
SVECTOR modelCube1_normal[] = {
2365,-2365,-2365, 0,
-2365,-2365,-2365, 0,
-2365,-2365,2365, 0,
2365,-2365,2365, 0,
2365,2365,-2365, 0,
-2365,2365,-2365, 0,
-2365,2365,2365, 0,
2365,2365,2365, 0
};
CVECTOR modelCube1_color[] = {
255,237,0, 0,
255,235,0, 0,
255,236,0, 0,
255,2,0, 0,
254,3,0, 0,
255,8,0, 0,
229,0,255, 0,
229,0,255, 0,
229,0,255, 0,
5,16,250, 0,
0,12,255, 0,
0,12,255, 0,
4,251,25, 0,
0,255,26, 0,
0,255,26, 0,
0,248,255, 0,
0,248,255, 0,
0,248,255, 0,
255,237,0, 0,
255,237,0, 0,
255,235,0, 0,
255,2,0, 0,
255,6,2, 0,
254,3,0, 0,
229,0,255, 0,
232,21,232, 0,
229,0,255, 0,
5,16,250, 0,
2,13,253, 0,
0,12,255, 0,
4,251,25, 0,
0,255,26, 0,
0,255,26, 0,
0,248,255, 0,
0,248,255, 0,
0,248,255, 0
};
int modelCube1_index[] = {
0,2,3,
7,5,4,
4,1,0,
5,2,1,
2,7,3,
0,7,4,
0,1,2,
7,6,5,
4,5,1,
5,6,2,
2,6,7,
0,3,7
};
TMESH modelCube1 = {
modelCube1_mesh,
modelCube1_normal,
0,
modelCube1_color,
12
};
================================================
FILE: hello_cube/hello_cube.c
================================================
/* primdrawG.c, by Schnappy, 12-2020
- Draw a gouraud shaded mesh exported as a TMESH by the blender <= 2.79b plugin io_export_psx_tmesh.py
based on primdraw.c by Lameguy64 (http://www.psxdev.net/forum/viewtopic.php?f=64&t=537)
2014 Meido-Tek Productions.
Controls:
Start - Toggle interactive/non-interactive mode.
Select - Reset object's position and angles.
L1/L2 - Move object closer/farther.
L2/R2 - Rotate object (XY).
Up/Down/Left/Right - Rotate object (XZ/YZ).
Triangle/Cross/Square/Circle - Move object up/down/left/right.
*/
#include
#include
#include
#include
#include
// Sample vector model
#include "cube.c"
#define VMODE 0
#define SCREENXRES 320
#define SCREENYRES 240
#define CENTERX SCREENXRES/2
#define CENTERY SCREENYRES/2
#define OTLEN 2048 // Maximum number of OT entries
#define PRIMBUFFLEN 32768 // Maximum number of POLY_GT3 primitives
// Display and draw environments, double buffered
DISPENV disp[2];
DRAWENV draw[2];
u_long ot[2][OTLEN]; // Ordering table (contains addresses to primitives)
char primbuff[2][PRIMBUFFLEN]; // Primitive list // That's our prim buffer
char * nextpri = primbuff[0]; // Primitive counter
short db = 0; // Current buffer counter
// Prototypes
void init(void);
void display(void);
//~ void LoadTexture(u_long * tim, TIM_IMAGE * tparam);
void init(){
// Reset the GPU before doing anything and the controller
PadInit(0);
ResetGraph(0);
// Initialize and setup the GTE
InitGeom();
SetGeomOffset(CENTERX, CENTERY); // x, y offset
SetGeomScreen(CENTERX); // Distance between eye and screen
// Set the display and draw environments
SetDefDispEnv(&disp[0], 0, 0 , SCREENXRES, SCREENYRES);
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[1], 0, 0, SCREENXRES, SCREENYRES);
if (VMODE)
{
SetVideoMode(MODE_PAL);
disp[0].screen.y += 8;
disp[1].screen.y += 8;
}
SetDispMask(1); // Display on screen
setRGB0(&draw[0], 0, 128, 255);
setRGB0(&draw[1], 0, 128, 255);
draw[0].isbg = 1;
draw[1].isbg = 1;
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
// Init font system
FntLoad(960, 0);
FntOpen(16, 16, 196, 64, 0, 256);
}
void display(void){
DrawSync(0);
VSync(0);
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
DrawOTag(&ot[db][OTLEN - 1]);
db = !db;
nextpri = primbuff[db];
}
int main() {
int i;
int PadStatus;
int TPressed=0;
int AutoRotate=1;
long t, p, OTz, Flag; // t == vertex count, p == depth cueing interpolation value, OTz == value to create Z-ordered OT, Flag == see LibOver47.pdf, p.143
POLY_G3 *poly = {0}; // pointer to a POLY_G4
SVECTOR Rotate={ 232, 232, 0, 0 }; // Rotation coordinates
VECTOR Trans={ 0, 0, CENTERX * 3, 0 }; // Translation coordinates
// Scaling coordinates
VECTOR Scale={ ONE/2, ONE/2, ONE/2, 0 }; // ONE == 4096
MATRIX Matrix={0}; // Matrix data for the GTE
init();
// Main loop
while (1) {
// Read pad status
PadStatus = PadRead(0);
if (AutoRotate == 0) {
if (PadStatus & PADL1) Trans.vz -= 4;
if (PadStatus & PADR1) Trans.vz += 4;
if (PadStatus & PADL2) Rotate.vz -= 8;
if (PadStatus & PADR2) Rotate.vz += 8;
if (PadStatus & PADLup) Rotate.vx -= 8;
if (PadStatus & PADLdown) Rotate.vx += 8;
if (PadStatus & PADLleft) Rotate.vy -= 8;
if (PadStatus & PADLright) Rotate.vy += 8;
if (PadStatus & PADRup) Trans.vy -= 2;
if (PadStatus & PADRdown) Trans.vy += 2;
if (PadStatus & PADRleft) Trans.vx -= 2;
if (PadStatus & PADRright) Trans.vx += 2;
if (PadStatus & PADselect) {
Rotate.vx = Rotate.vy = Rotate.vz = 0;
Scale.vx = Scale.vy = Scale.vz = ONE/2;
Trans.vx = Trans.vy = 0;
Trans.vz = CENTERX * 3;
}
}
if (PadStatus & PADstart) {
if (TPressed == 0) {
AutoRotate = (AutoRotate + 1) & 1;
Rotate.vx = Rotate.vy = Rotate.vz = 0;
Scale.vx = Scale.vy = Scale.vz = ONE/2;
Trans.vx = Trans.vy = 0;
Trans.vz = CENTERX * 3;
}
TPressed = 1;
} else {
TPressed = 0;
}
if (AutoRotate) {
Rotate.vy += 28; // Pan
Rotate.vx += 28; // Tilt
//~ Rotate.vz += 8; // Roll
}
// Clear the current OT
ClearOTagR(ot[db], OTLEN);
// Convert and set the matrixes
RotMatrix(&Rotate, &Matrix);
TransMatrix(&Matrix, &Trans);
ScaleMatrix(&Matrix, &Scale);
SetRotMatrix(&Matrix);
SetTransMatrix(&Matrix);
// Render the sample vector model
t=0;
// modelCube is a TMESH, len member == # vertices, but here it's # of triangle... So, for each tri * 3 vertices ...
for (i = 0; i < (modelCube.len*3); i += 3) {
poly = (POLY_G3 *)nextpri;
// Initialize the primitive and set its color values
SetPolyG3(poly);
setRGB0(poly, modelCube.c[i].r , modelCube.c[i].g , modelCube.c[i].b);
setRGB1(poly, modelCube.c[i+2].r, modelCube.c[i+2].g, modelCube.c[i+2].b);
setRGB2(poly, modelCube.c[i+1].r, modelCube.c[i+1].g, modelCube.c[i+1].b);
// Rotate, translate, and project the vectors and output the results into a primitive
OTz = RotTransPers(&modelCube_mesh[modelCube_index[t]] , (long*)&poly->x0, &p, &Flag);
OTz += RotTransPers(&modelCube_mesh[modelCube_index[t+2]], (long*)&poly->x1, &p, &Flag);
OTz += RotTransPers(&modelCube_mesh[modelCube_index[t+1]], (long*)&poly->x2, &p, &Flag);
// Sort the primitive into the OT
OTz /= 3;
if ((OTz > 0) && (OTz < OTLEN))
AddPrim(&ot[db][OTz-2], poly);
nextpri += sizeof(POLY_G3);
t+=3;
}
FntPrint("Hello gouraud shaded cube!\n");
FntFlush(-1);
display();
}
return 0;
}
================================================
FILE: hello_cubetex/Makefile
================================================
TARGET = hello_cubetex
SRCS = hello_cubetex.c \
../TIM/cubetex.tim \
include ../common.mk
================================================
FILE: hello_cubetex/cubetex.c
================================================
SVECTOR modelCube_mesh[] = {
{32,32,-32.0},
{32,-32,-32},
{-32,-32,-32},
{-32,32,-32},
{32,32,32},
{32,-32,32},
{-32,-32,32},
{-32,32,32}
};
SVECTOR modelCube_normal[] = {
0,-0,-1,0,
0,0,1,0,
1,0,-2,0,
-9,-1,-3,0,
-1,2,-1,0,
3,1,2,0,
0,0,-1,0,
0,-0,1,0,
1,-6,3,0,
-5,-1,9,0,
-1,2,-1,0,
2,1,2,0
};
SVECTOR modelCube_uv[] = {
84,84, 0, 0,
125,42, 0, 0,
84,42, 0, 0,
125,84, 0, 0,
84,125, 0, 0,
125,125, 0, 0,
1,84, 0, 0,
42,125, 0, 0,
42,84, 0, 0,
42,125, 0, 0,
84,84, 0, 0,
42,84, 0, 0,
42,1, 0, 0,
1,42, 0, 0,
42,42, 0, 0,
42,84, 0, 0,
1,42, 0, 0,
1,84, 0, 0,
84,84, 0, 0,
125,84, 0, 0,
125,42, 0, 0,
125,84, 0, 0,
84,84, 0, 0,
84,125, 0, 0,
1,84, 0, 0,
1,125, 0, 0,
42,125, 0, 0,
42,125, 0, 0,
84,125, 0, 0,
84,84, 0, 0,
42,1, 0, 0,
1,1, 0, 0,
1,42, 0, 0,
42,84, 0, 0,
42,42, 0, 0,
1,42, 0, 0
};
CVECTOR modelCube_color[] = {
255,255,255, 0,
255,255,255, 0,
255,0,251, 0,
255,255,255, 0,
255,5,7, 0,
255,255,255, 0,
255,255,255, 0,
255,255,255, 0,
4,18,255, 0,
255,5,7, 0,
255,255,255, 0,
255,255,255, 0,
254,255,23, 0,
122,255,107, 0,
255,255,255, 0,
255,255,255, 0,
255,255,255, 0,
254,255,94, 0,
255,255,255, 0,
35,255,11, 0,
255,255,255, 0,
255,255,255, 0,
255,255,255, 0,
255,5,7, 0,
255,255,255, 0,
255,5,7, 0,
255,255,255, 0,
255,5,7, 0,
255,255,255, 0,
255,255,255, 0,
254,255,23, 0,
255,255,255, 0,
122,255,107, 0,
255,255,255, 0,
54,65,255, 0,
255,255,255, 0
};
int modelCube_index[] = {
0,2,3,
7,5,4,
4,1,0,
5,2,1,
2,7,3,
0,7,4,
0,1,2,
7,6,5,
4,5,1,
5,6,2,
2,6,7,
0,3,7
};
TMESH modelCube = {
modelCube_mesh,
modelCube_normal,
modelCube_uv,
modelCube_color,
12
};
extern unsigned long _binary____TIM_cubetex_tim_start[];
extern unsigned long _binary____TIM_cubetex_tim_end[];
extern unsigned long _binary____TIM_cubetex_tim_length;
TIM_IMAGE tim_cube;
================================================
FILE: hello_cubetex/hello_cubetex.c
================================================
/* primdrawG.c, by Schnappy, 12-2020
- Draw a gouraud shaded textured mesh exported as a TMESH by the blender <= 2.79b plugin io_export_psx_tmesh.py
based on primdraw.c by Lameguy64 (http://www.psxdev.net/forum/viewtopic.php?f=64&t=537)
2014 Meido-Tek Productions.
Demonstrates:
- Using a primitive OT to draw triangles without libgs.
- Using the GTE to rotate, translate, and project 3D primitives.
Controls:
Start - Toggle interactive/non-interactive mode.
Select - Reset object's position and angles.
L1/L2 - Move object closer/farther.
L2/R2 - Rotate object (XY).
Up/Down/Left/Right - Rotate object (XZ/YZ).
Triangle/Cross/Square/Circle - Move object up/down/left/right.
*/
#include
#include
#include
#include
#include
// Sample vector model
#include "cubetex.c"
#define VMODE 0
#define SCREENXRES 320
#define SCREENYRES 240
#define CENTERX SCREENXRES/2
#define CENTERY SCREENYRES/2
#define OTLEN 2048 // Maximum number of OT entries
#define PRIMBUFFLEN 32768 // Maximum number of POLY_GT3 primitives
// Display and draw environments, double buffered
DISPENV disp[2];
DRAWENV draw[2];
u_long ot[2][OTLEN]; // Ordering table (contains addresses to primitives)
char primbuff[2][PRIMBUFFLEN] = {0}; // Primitive list // That's our prim buffer
//~ int primcnt=0; // Primitive counter
char * nextpri = primbuff[0]; // Primitive counter
short db = 0; // Current buffer counter
// Prototypes
void init(void);
void display(void);
void LoadTexture(u_long * tim, TIM_IMAGE * tparam);
void init(){
// Reset the GPU before doing anything and the controller
PadInit(0);
ResetGraph(0);
// Initialize and setup the GTE
InitGeom();
SetGeomOffset(CENTERX, CENTERY); // x, y offset
SetGeomScreen(CENTERX); // Distance between eye and screen
// Set the display and draw environments
SetDefDispEnv(&disp[0], 0, 0 , SCREENXRES, SCREENYRES);
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[1], 0, 0, SCREENXRES, SCREENYRES);
if (VMODE)
{
SetVideoMode(MODE_PAL);
disp[0].screen.y += 8;
disp[1].screen.y += 8;
}
SetDispMask(1);
setRGB0(&draw[0], 0, 0, 255);
setRGB0(&draw[1], 0, 0, 255);
draw[0].isbg = 1;
draw[1].isbg = 1;
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
// Init font system
FntLoad(960, 0);
FntOpen(16, 16, 196, 64, 0, 256);
}
void display(void){
DrawSync(0);
VSync(0);
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
DrawOTag(&ot[db][OTLEN - 1]);
db = !db;
nextpri = primbuff[db];
}
void LoadTexture(u_long * tim, TIM_IMAGE * tparam){ // This part is from Lameguy64's tutorial series : lameguy64.net/svn/pstutorials/chapter1/3-textures.html login/pw: annoyingmous
OpenTIM(tim); // Open the tim binary data, feed it the address of the data in memory
ReadTIM(tparam); // This read the header of the TIM data and sets the corresponding members of the TIM_IMAGE structure
LoadImage(tparam->prect, tparam->paddr); // Transfer the data from memory to VRAM at position prect.x, prect.y
DrawSync(0); // Wait for the drawing to end
if (tparam->mode & 0x8){ // check 4th bit // If 4th bit == 1, TIM has a CLUT
LoadImage(tparam->crect, tparam->caddr); // Load it to VRAM at position crect.x, crect.y
DrawSync(0); // Wait for drawing to end
}
}
int main() {
int i;
int PadStatus;
int TPressed=0;
int AutoRotate=1;
long t, p, OTz, Flag; // t == vertex count, p == depth cueing interpolation value, OTz == value to create Z-ordered OT, Flag == see LibOver47.pdf, p.143
POLY_GT3 *poly = {0}; // pointer to a POLY_G4
SVECTOR Rotate={ 0 }; // Rotation coordinates
VECTOR Trans={ 0, 0, CENTERX, 0 }; // Translation coordinates
// Scaling coordinates
VECTOR Scale={ ONE, ONE, ONE, 0 }; // ONE == 4096
MATRIX Matrix={0}; // Matrix data for the GTE
// Texture window
DR_MODE * dr_mode; // Pointer to dr_mode prim
RECT tws = {0, 0, 32, 32}; // Texture window coordinates : x, y, w, h
init();
LoadTexture(_binary____TIM_cubetex_tim_start, &tim_cube);
// Main loop
while (1) {
// Read pad status
PadStatus = PadRead(0);
if (AutoRotate == 0) {
if (PadStatus & PADL1) Trans.vz -= 4;
if (PadStatus & PADR1) Trans.vz += 4;
if (PadStatus & PADL2) Rotate.vz -= 8;
if (PadStatus & PADR2) Rotate.vz += 8;
if (PadStatus & PADLup) Rotate.vx -= 8;
if (PadStatus & PADLdown) Rotate.vx += 8;
if (PadStatus & PADLleft) Rotate.vy -= 8;
if (PadStatus & PADLright) Rotate.vy += 8;
if (PadStatus & PADRup) Trans.vy -= 2;
if (PadStatus & PADRdown) Trans.vy += 2;
if (PadStatus & PADRleft) Trans.vx -= 2;
if (PadStatus & PADRright) Trans.vx += 2;
if (PadStatus & PADselect) {
Rotate.vx = Rotate.vy = Rotate.vz = 0;
Scale.vx = Scale.vy = Scale.vz = ONE;
Trans.vx = Trans.vy = 0;
Trans.vz = CENTERX;
}
}
if (PadStatus & PADstart) {
if (TPressed == 0) {
AutoRotate = (AutoRotate + 1) & 1;
Rotate.vx = Rotate.vy = Rotate.vz = 0;
Scale.vx = Scale.vy = Scale.vz = ONE;
Trans.vx = Trans.vy = 0;
Trans.vz = CENTERX;
}
TPressed = 1;
} else {
TPressed = 0;
}
if (AutoRotate) {
Rotate.vy += 8; // Pan
Rotate.vx += 8; // Tilt
//~ Rotate.vz += 8; // Roll
}
// Clear the current OT
ClearOTagR(ot[db], OTLEN);
// Convert and set the matrixes
RotMatrix(&Rotate, &Matrix);
TransMatrix(&Matrix, &Trans);
ScaleMatrix(&Matrix, &Scale);
SetRotMatrix(&Matrix);
SetTransMatrix(&Matrix);
// Render the sample vector model
t=0;
// modelCube is a TMESH, len member == # vertices, but here it's # of triangle... So, for each tri * 3 vertices ...
for (i = 0; i < (modelCube.len*3); i += 3) {
poly = (POLY_GT3 *)nextpri;
// Initialize the primitive and set its color values
SetPolyGT3(poly);
((POLY_GT3 *)poly)->tpage = getTPage(tim_cube.mode&0x3, 0,
tim_cube.prect->x,
tim_cube.prect->y
);
setRGB0(poly, modelCube.c[i].r , modelCube.c[i].g , modelCube.c[i].b);
setRGB1(poly, modelCube.c[i+2].r, modelCube.c[i+2].g, modelCube.c[i+2].b);
setRGB2(poly, modelCube.c[i+1].r, modelCube.c[i+1].g, modelCube.c[i+1].b);
setUV3(poly, modelCube.u[i].vx, modelCube.u[i].vy,
modelCube.u[i+2].vx, modelCube.u[i+2].vy,
modelCube.u[i+1].vx, modelCube.u[i+1].vy);
// Rotate, translate, and project the vectors and output the results into a primitive
OTz = RotTransPers(&modelCube_mesh[modelCube_index[t]] , (long*)&poly->x0, &p, &Flag);
OTz += RotTransPers(&modelCube_mesh[modelCube_index[t+2]], (long*)&poly->x1, &p, &Flag);
OTz += RotTransPers(&modelCube_mesh[modelCube_index[t+1]], (long*)&poly->x2, &p, &Flag);
// Sort the primitive into the OT
OTz /= 3;
if ((OTz > 0) && (OTz < OTLEN))
AddPrim(&ot[db][OTz-2], poly);
nextpri += sizeof(POLY_GT3);
t+=3;
}
dr_mode = (DR_MODE *)nextpri;
setDrawMode(dr_mode,1,0, getTPage(tim_cube.mode&0x3, 0,
tim_cube.prect->x,
tim_cube.prect->y), &tws); //set texture window
AddPrim(&ot[db], dr_mode);
nextpri += sizeof(DR_MODE);
FntPrint("Hello textured cube!\n");
FntFlush(-1);
display();
}
return 0;
}
================================================
FILE: hello_cubetex_stp/Makefile
================================================
TARGET = hello_cubetex_stp
SRCS = hello_cubetex_stp.c \
TIM/stpOnBlack.tim \
TIM/stpOnNonBlack.tim \
TIM/stpOn8bpp.tim \
include ../common.mk
================================================
FILE: hello_cubetex_stp/README.md
================================================

# STP : Semi-Transparency usage
This example shows the effect of activating Semi-Transparency on a textured primitive.
Use the `SELECT` button to switch primitive semi-transparency on and off.
Use the `START` button to cycle throught the 4 transparency rates.
It also features a few C struct to facilitate access to the TIM file / pixel data.
See [https://github.com/ABelliqueux/nolibgs_hello_worlds/tree/main/hello_poly_stp](https://github.com/ABelliqueux/nolibgs_hello_worlds/tree/main/hello_poly_stp) for more information on how to convert your images to TIM while preserving transparency.
See [https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/TIM#transparency](https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/TIM#transparency) for mor details on transparency.
## Documentation
http://psx.arthus.net/sdk/Psy-Q/DOCS/LibOver47.pdf, p.107
http://psx.arthus.net/sdk/Psy-Q/DOCS/FileFormat47.pdf, p.181
http://psx.arthus.net/sdk/Psy-Q/DOCS/LibRef47.pdf, p.306, 345, 365
================================================
FILE: hello_cubetex_stp/cubetex.c
================================================
SVECTOR modelCube_mesh[] = {
{48,48,-48.0},
{48,-48,-48},
{-48,-48,-48},
{-48,48,-48},
{48,48,48},
{48,-48,48},
{-48,-48,48},
{-48,48,48}
};
SVECTOR modelCube_normal[] = {
0,-0,-1,0,
0,0,1,0,
1,0,-2,0,
-9,-1,-3,0,
-1,2,-1,0,
3,1,2,0,
0,0,-1,0,
0,-0,1,0,
1,-6,3,0,
-5,-1,9,0,
-1,2,-1,0,
2,1,2,0
};
SVECTOR modelCube_uv[] = {
84,84, 0, 0,
125,42, 0, 0,
84,42, 0, 0,
125,84, 0, 0,
84,125, 0, 0,
125,125, 0, 0,
1,84, 0, 0,
42,125, 0, 0,
42,84, 0, 0,
42,125, 0, 0,
84,84, 0, 0,
42,84, 0, 0,
42,1, 0, 0,
1,42, 0, 0,
42,42, 0, 0,
42,84, 0, 0,
1,42, 0, 0,
1,84, 0, 0,
84,84, 0, 0,
125,84, 0, 0,
125,42, 0, 0,
125,84, 0, 0,
84,84, 0, 0,
84,125, 0, 0,
1,84, 0, 0,
1,125, 0, 0,
42,125, 0, 0,
42,125, 0, 0,
84,125, 0, 0,
84,84, 0, 0,
42,1, 0, 0,
1,1, 0, 0,
1,42, 0, 0,
42,84, 0, 0,
42,42, 0, 0,
1,42, 0, 0
};
CVECTOR modelCube_color[] = {
255,255,255, 0,
255,255,255, 0,
255,0,251, 0,
255,255,255, 0,
255,5,7, 0,
255,255,255, 0,
255,255,255, 0,
255,255,255, 0,
4,18,255, 0,
255,5,7, 0,
255,255,255, 0,
255,255,255, 0,
254,255,23, 0,
122,255,107, 0,
255,255,255, 0,
255,255,255, 0,
255,255,255, 0,
254,255,94, 0,
255,255,255, 0,
35,255,11, 0,
255,255,255, 0,
255,255,255, 0,
255,255,255, 0,
255,5,7, 0,
255,255,255, 0,
255,5,7, 0,
255,255,255, 0,
255,5,7, 0,
255,255,255, 0,
255,255,255, 0,
254,255,23, 0,
255,255,255, 0,
122,255,107, 0,
255,255,255, 0,
54,65,255, 0,
255,255,255, 0
};
int modelCube_index[] = {
0,2,3,
7,5,4,
4,1,0,
5,2,1,
2,7,3,
0,7,4,
0,1,2,
7,6,5,
4,5,1,
5,6,2,
2,6,7,
0,3,7
};
TMESH modelCube = {
modelCube_mesh,
modelCube_normal,
modelCube_uv,
modelCube_color,
12
};
typedef struct RGB_PIX {
u_int R:5, G:5, B:5, STP:1;
} RGB_PIX;
// Some structures to handle TIM files
typedef struct PIXEL {
u_long bnum;
u_short DX, DY;
u_short W, H;
RGB_PIX data[];
} PIXEL;
typedef struct CLUT {
u_long bnum;
u_short DX, DY;
u_short W, H;
u_short clut[];
} CLUT;
typedef struct TIM_FILE_CLUT{
u_long ID;
u_long flag;
u_long clut;
PIXEL pixel[];
} TIM_FILE_CLUT;
typedef struct TIM_FILE{
u_long ID;
u_long flag;
PIXEL pixel[];
} TIM_FILE;
extern TIM_FILE _binary_TIM_stpOnBlack_tim_start;
extern TIM_FILE _binary_TIM_stpOnNonBlack_tim_start;
extern TIM_FILE _binary_TIM_stpOn8bpp_tim_start;
================================================
FILE: hello_cubetex_stp/hello_cubetex_stp.c
================================================
// Demo the different settings and rates for pixel and primitive semi-transparency on a cube
// Controls :
// SELECT : Switch semi-transparency on/off on primitives
// START : Cycle semi-transparency rates on/off on primitives
// Schnappy 07-2021
#include
#include
#include
#include
#include
//~ #include
// Sample vector model
#include "cubetex.c"
#define VMODE 0
// Number of primitives to draw
#define NUM_PRIM 3
#define SCREENXRES 320
#define SCREENYRES 240
#define CENTERX SCREENXRES/2
#define CENTERY SCREENYRES/2
#define MARGINX 16 // margins for text display
#define MARGINY 16
#define OTLEN 2048 // Maximum number of OT entries
#define PRIMBUFFLEN 32768 // Maximum number of POLY_GT3 primitives
// Display and draw environments, double buffered
DISPENV disp[2];
DRAWENV draw[2];
u_long ot[2][OTLEN]; // Ordering table (contains addresses to primitives)
char primbuff[2][PRIMBUFFLEN]; // Primitive list // That's our prim buffer
char * nextpri = primbuff[0]; // Primitive counter
short db = 0; // Current buffer counter
// Store TIM files in an array so we can iterate over them - see 'cubetex.c' for TIM_FILE struct and declaration
TIM_FILE * timFiles[3];
TIM_IMAGE timImages[3];
// Prototypes
void init(void);
void display(void);
void LoadTexture(TIM_FILE * tim, TIM_IMAGE * tparam);
void init(){
// Reset the GPU before doing anything and the controller
PadInit(0);
ResetGraph(0);
// Initialize and setup the GTE
InitGeom();
SetGeomOffset(CENTERX, CENTERY); // x, y offset
SetGeomScreen(CENTERX); // Distance between eye and screen
// Set the display and draw environments
SetDefDispEnv(&disp[0], 0, 0 , SCREENXRES, SCREENYRES);
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[1], 0, 0, SCREENXRES, SCREENYRES);
if (VMODE)
{
SetVideoMode(MODE_PAL);
disp[0].screen.y += 8;
disp[1].screen.y += 8;
}
SetDispMask(1);
setRGB0(&draw[0], 0, 0, 255);
setRGB0(&draw[1], 0, 0, 255);
draw[0].isbg = 1;
draw[1].isbg = 1;
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
// Init font system
FntLoad(960, 0);
FntOpen(MARGINX, MARGINY, SCREENXRES - MARGINX * 2, SCREENXRES - MARGINY * 2, 0, 512 );
}
void display(void){
DrawSync(0);
VSync(0);
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
DrawOTag(&ot[db][OTLEN - 1]);
db = !db;
nextpri = primbuff[db];
}
void LoadTexture(TIM_FILE * tim, TIM_IMAGE * tparam){ // This part is from Lameguy64's tutorial series : lameguy64.net/svn/pstutorials/chapter1/3-textures.html login/pw: annoyingmous
OpenTIM((u_long*)tim); // Open the tim binary data, feed it the address of the data in memory
ReadTIM(tparam); // This read the header of the TIM data and sets the corresponding members of the TIM_IMAGE structure
LoadImage(tparam->prect, tparam->paddr); // Transfer the data from memory to VRAM at position prect.x, prect.y
DrawSync(0); // Wait for the drawing to end
if (tparam->mode & 0x8){ // check 4th bit // If 4th bit == 1, TIM has a CLUT
LoadImage(tparam->crect, tparam->caddr); // Load it to VRAM at position crect.x, crect.y
DrawSync(0); // Wait for drawing to end
}
}
int main() {
// Populate array with pointers to TIM data
timFiles[0] = &_binary_TIM_stpOnBlack_tim_start;
timFiles[1] = &_binary_TIM_stpOnNonBlack_tim_start;
timFiles[2] = &_binary_TIM_stpOn8bpp_tim_start;
// Pad values
int pad, oldPad;
// Set semi-transparency on (1) and off (0)
int stpFlag = 0;
// Set primitive semi-transparency rate - See LibOver47.pdf, p.107
int stpRate = 0;
// If set, rotate cube
int rotateCube = 1;
// Array of pointers to a POLY_G4 we iterate over
POLY_GT3 * poly[NUM_PRIM];
// Rotation vector
SVECTOR rotVector={ 496, 0, 0, 0 };
// Translation vector
VECTOR transVector= { -SCREENXRES/NUM_PRIM, -SCREENYRES/NUM_PRIM, SCREENXRES, 0};
// Init Disp/Drawenv, Font, etc.
init();
// Load textures to VRAM
for (char tim = 0; tim < NUM_PRIM; tim++){
LoadTexture(timFiles[tim], &timImages[tim]);
}
// Main loop
while (1) {
// Work matrix
MATRIX Work= {0} ;
// Triangle counters array - one for each cube
long curTriangle[3] = {0,0,0};
// Clear the current OT
ClearOTagR(ot[db], OTLEN);
// Rotate cube
if(rotateCube) rotVector.vy += 10;
// Apply Transl, Rot, then matrix
RotMatrix(&rotVector, &Work);
TransMatrix(&Work, &transVector);
SetRotMatrix(&Work);
SetTransMatrix(&Work);
// Draw NUM_PRIM primitives
for (int prim = 0; prim < NUM_PRIM; prim++){
// Draw prims with an offset based on iteration number
// A bit messy but the cubes are drawn where we want them :)
transVector.vx = ( SCREENXRES/NUM_PRIM + ( prim * (SCREENXRES/NUM_PRIM + 48) )) - SCREENXRES + 56 ;
transVector.vy = SCREENYRES/NUM_PRIM - SCREENYRES + 86;
// Add vertical offset to second cube
if ( prim == 1) {
transVector.vy = SCREENYRES/NUM_PRIM + 48;
}
// Apply transl matrix
TransMatrix(&Work, &transVector);
SetTransMatrix(&Work);
// modelCube is a TMESH, len member == # vertices, but here it's # of triangle... So, for each tri * 3 vertices ...
for (int i = 0; i < (modelCube.len * 3); i += 3) {
// t == vertex count, p == depth cueing interpolation value, OTz == value to create Z-ordered OT, Flag == see LibOver47.pdf, p.143
long p, OTz, Flag;
// cast nextpri as POLY_GT3
poly[prim] = (POLY_GT3 *)nextpri;
// Initialize the primitive and set its color values
SetPolyGT3(poly[prim]);
// Get TPAGE
poly[prim]->tpage = getTPage( timImages[prim].mode&0x3, stpRate,
timImages[prim].prect->x,
timImages[prim].prect->y
);
// Set RGB colors for each vertex
setRGB0(poly[prim] , modelCube.c[i].r , modelCube.c[i].g , modelCube.c[i].b);
setRGB1(poly[prim], modelCube.c[i+2].r, modelCube.c[i+2].g, modelCube.c[i+2].b);
setRGB2(poly[prim], modelCube.c[i+1].r, modelCube.c[i+1].g, modelCube.c[i+1].b);
// If 8/4bpp, load CLUT to vram
if ( (timImages[prim].mode & 0x3) < 2 ) {
setClut( poly[prim],
timImages[prim].crect->x,
timImages[prim].crect->y
);
}
// Set stpFlag
SetSemiTrans(poly[prim], stpFlag);
// Set UV coordinates
setUV3(poly[prim], modelCube.u[i].vx, modelCube.u[i].vy,
modelCube.u[i+2].vx, modelCube.u[i+2].vy,
modelCube.u[i+1].vx, modelCube.u[i+1].vy);
// Rotate, translate, and project the vectors and output the results into a primitive
// curTriangle, +1, +2 point to the vertices index of the triangle we're drawing.
OTz = RotTransPers(&modelCube_mesh[ modelCube_index[ curTriangle[prim] ] ] , ( long * ) &poly[prim]->x0, &p, &Flag);
OTz += RotTransPers(&modelCube_mesh[ modelCube_index[ curTriangle[prim] + 2] ], ( long*) &poly[prim]->x1, &p, &Flag);
OTz += RotTransPers(&modelCube_mesh[ modelCube_index[ curTriangle[prim] + 1] ], ( long * ) &poly[prim]->x2, &p, &Flag);
// Average OTz value for 3 vertices
// OTz is 1/4 of screen to vertex length
OTz /= 3;
// If OTz is in range (not to close)
if ((OTz > 0) && (OTz < OTLEN))
// Add to ordering table, at index OTz-2
AddPrim(&ot[ db ][ OTz-2 ], poly[prim]);
// Increment next primitive address
nextpri += sizeof(POLY_GT3);
// Increment to next triangle
curTriangle[prim] += 3;
}
}
// Get pad input
pad = PadRead(0);
// If select button is used
if ( pad & PADselect && !(oldPad & PADselect) ){
// Flip STP flag
stpFlag = !stpFlag;
// Set flag to avoir misfire
oldPad = pad;
}
// Reset flag when button released
if ( !(pad & PADselect) && oldPad & PADselect) {
oldPad = pad;
}
// If start button is used
if ( pad & PADstart && !( oldPad & PADstart ) ){
// Switch STP rates
stpRate > 2 ? stpRate = 0 : stpRate++;
// Set flag to avoir misfire
oldPad = pad;
}
// Reset flag when button released
if (!(pad & PADstart) && oldPad & PADstart) {
oldPad = pad;
}
FntPrint("Hello semi-transparency !\nPrim STP (push Select) : %d\nSTP rate (push start): %d\n\n\n\n\n\n\n\n\n\n\n\n", stpFlag, stpRate);
FntPrint(" stp on black stp on col index\n\n\n\n\n\n\n\n\n\n\n\n");
FntPrint(" stp on non-black");
FntFlush(-1);
display();
}
return 0;
}
================================================
FILE: hello_font/Makefile
================================================
TARGET = hello_font
SRCS = hello_font.c \
fnt.tim \
include ../common.mk
================================================
FILE: hello_font/README.md
================================================
See the **wiki** for more details : [https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/FONT](https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/FONT)
================================================
FILE: hello_font/hello_font.c
================================================
// Change the debug font face and colors !
// Schnappy 2020
#include
#include
#include
#include
#include
#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL
#define SCREENXRES 320 // Screen width
#define SCREENYRES 240 // Screen height
#define CENTERX SCREENXRES/2 // Center of screen on x
#define CENTERY SCREENYRES/2 // Center of screen on y
#define MARGINX 96 // margins for text display
#define MARGINY 64
#define FONTSIZE 8 * 7 // Text Field Height
DISPENV disp[2]; // Double buffered DISPENV and DRAWENV
DRAWENV draw[2];
short db = 0; // index of which buffer is used, values 0, 1
#define FONTX 960
#define FONTY 0
// Two color vectors R,G,B
CVECTOR fntColor = { 255, 0, 0 };
CVECTOR fntColorBG = { 0, 0, 0 };
extern unsigned long _binary_fnt_tim_start[];
extern unsigned long _binary_fnt_tim_end[];
extern unsigned long _binary_fnt_tim_length;
// Loading an image to vram. See https://github.com/ABelliqueux/nolibgs_hello_worlds/blob/main/hello_sprt/hello_sprt.c#L42
TIM_IMAGE fontface;
void LoadTexture(u_long * tim, TIM_IMAGE * tparam){
OpenTIM(tim);
ReadTIM(tparam);
LoadImage(tparam->prect, tparam->paddr);
DrawSync(0);
if (tparam->mode & 0x8){ // check 4th bit
LoadImage(tparam->crect, tparam->caddr);
DrawSync(0);
}
}
void FntColor(CVECTOR fgcol, CVECTOR bgcol )
{
// The debug font clut is at tx, ty + 128
// tx = bg color
// tx + 1 = fg color
// We can override the color by drawing a rect at these coordinates
//
// Define 1 pixel at 960,128 (background color) and 1 pixel at 961, 128 (foreground color)
RECT fg = { FONTX+1, FONTY + 128, 1, 1 };
RECT bg = { FONTX, FONTY + 128, 1, 1 };
// Set colors
ClearImage(&fg, fgcol.r, fgcol.g, fgcol.b);
ClearImage(&bg, bgcol.r, bgcol.g, bgcol.b);
}
void init(void)
{
ResetGraph(0); // Initialize drawing engine with a complete reset (0)
SetDefDispEnv(&disp[0], 0, 0 , SCREENXRES, SCREENYRES); // Set display area for both &disp[0] and &disp[1]
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES); // &disp[0] is on top of &disp[1]
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES); // Set draw for both &draw[0] and &draw[1]
SetDefDrawEnv(&draw[1], 0, 0 , SCREENXRES, SCREENYRES); // &draw[0] is below &draw[1]
if (VMODE) // PAL
{
SetVideoMode(MODE_PAL);
disp[0].screen.y += 8; // add offset : 240 + 8 + 8 = 256
disp[1].screen.y += 8;
}
SetDispMask(1); // Display on screen
setRGB0(&draw[0], 50, 50, 50); // set color for first draw area
setRGB0(&draw[1], 50, 50, 50); // set color for second draw area
draw[0].isbg = 1; // set mask for draw areas. 1 means repainting the area with the RGB color each frame
draw[1].isbg = 1;
PutDispEnv(&disp[db]); // set the disp and draw environnments
PutDrawEnv(&draw[db]);
FntLoad(960, 0); // Load font to vram at 960,0(+128)
FntOpen(MARGINX, MARGINY, SCREENXRES, SCREENXRES, 0, 280 ); // FntOpen(x, y, width, height, black_bg, max. nbr. chars
}
void display(void)
{
DrawSync(0); // Wait for all drawing to terminate
VSync(0); // Wait for the next vertical blank
PutDispEnv(&disp[db]); // set alternate disp and draw environnments
PutDrawEnv(&draw[db]);
db = !db; // flip db value (0 or 1)
}
int main(void)
{
u_int t = 0;
u_short step = 10;
u_char * channel, * prevChannel;
channel = &fntColor.r;
prevChannel = &fntColor.b;
init(); // execute init()
LoadTexture(_binary_fnt_tim_start, &fontface);
FntColor(fntColor, fntColorBG);
while (1) // infinite loop
{
t++;
if (fntColor.r >= 254 - step ){channel = &fntColor.g; prevChannel = &fntColor.r;}
if (fntColor.g >= 254 - step ){channel = &fntColor.b; prevChannel = &fntColor.g;}
if (fntColor.b >= 254 - step ){channel = &fntColor.r; prevChannel = &fntColor.b;}
*channel += step;
if (*prevChannel){
*prevChannel -= step;
}
//~ fntColorBG.g = 255 - color ;
FntColor(fntColor, fntColorBG);
// 4lines, 16 glyphs on each line
// Glyphs are 5x7 pixels wide in 16bpp,
FntPrint("!\"#$%%&'()*+,-./\n");
FntPrint("0123456789:;<=^?\n");
FntPrint("@ABCDEFGHIJKLMNO\n");
FntPrint("PQRSTUVWXYZ[\\]>");
FntPrint("\n\nHELLO DEBUG FONT! %d", t);
FntFlush(-1); // Draw print stream
display(); // Execute display()
}
return 0;
}
================================================
FILE: hello_fx/Makefile
================================================
TARGET = hello_fx
SRCS = hello_fx.c \
TIM/cube.tim \
TIM/sky.tim \
TIM/bg.tim \
include ../common.mk
================================================
FILE: hello_fx/cubetex.c
================================================
SVECTOR modelCube_mesh[] = {
{48,48,-48.0},
{48,-48,-48},
{-48,-48,-48},
{-48,48,-48},
{48,48,48},
{48,-48,48},
{-48,-48,48},
{-48,48,48}
};
SVECTOR modelCube_normal[] = {
0,-0,-1,0,
0,0,1,0,
1,0,-2,0,
-9,-1,-3,0,
-1,2,-1,0,
3,1,2,0,
0,0,-1,0,
0,-0,1,0,
1,-6,3,0,
-5,-1,9,0,
-1,2,-1,0,
2,1,2,0
};
SVECTOR modelCube_uv[] = {
84,84, 0, 0,
125,42, 0, 0,
84,42, 0, 0,
125,84, 0, 0,
84,125, 0, 0,
125,125, 0, 0,
1,84, 0, 0,
42,125, 0, 0,
42,84, 0, 0,
42,125, 0, 0,
84,84, 0, 0,
42,84, 0, 0,
42,1, 0, 0,
1,42, 0, 0,
42,42, 0, 0,
42,84, 0, 0,
1,42, 0, 0,
1,84, 0, 0,
84,84, 0, 0,
125,84, 0, 0,
125,42, 0, 0,
125,84, 0, 0,
84,84, 0, 0,
84,125, 0, 0,
1,84, 0, 0,
1,125, 0, 0,
42,125, 0, 0,
42,125, 0, 0,
84,125, 0, 0,
84,84, 0, 0,
42,1, 0, 0,
1,1, 0, 0,
1,42, 0, 0,
42,84, 0, 0,
42,42, 0, 0,
1,42, 0, 0
};
CVECTOR modelCube_color[] = {
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0,
128,128,128, 0
};
int modelCube_index[] = {
0,2,3,
7,5,4,
4,1,0,
5,2,1,
2,7,3,
0,7,4,
0,1,2,
7,6,5,
4,5,1,
5,6,2,
2,6,7,
0,3,7
};
TMESH modelCube = {
modelCube_mesh,
modelCube_normal,
modelCube_uv,
modelCube_color,
12
};
typedef struct RGB_PIX {
u_int R:5, G:5, B:5, STP:1;
} RGB_PIX;
// Some structures to handle TIM files
typedef struct PIXEL {
u_long bnum;
u_short DX, DY;
u_short W, H;
RGB_PIX data[];
} PIXEL;
typedef struct CLUT {
u_long bnum;
u_short DX, DY;
u_short W, H;
u_short clut[];
} CLUT;
typedef struct TIM_FILE_CLUT{
u_long ID;
u_long flag;
u_long clut;
PIXEL pixel[];
} TIM_FILE_CLUT;
typedef struct TIM_FILE{
u_long ID;
u_long flag;
PIXEL pixel[];
} TIM_FILE;
================================================
FILE: hello_fx/hello_fx.c
================================================
// Controls :
// SELECT : Switch semi-transparency on/off on primitives
// START : Cycle semi-transparency rates on/off on primitives
// LEFT/RIGHT: Move forward cube
// X : Reset Cube position
// Schnappy 11-2021
#include
#include
#include
#include
#include
#include
#include
// Sample vector model
#include "cubetex.c"
#define VMODE 0
// Number of primitives to draw
#define NUM_PRIM 2
// Number of textures to load
#define NUM_TEX 3
#define SCREENXRES 320
#define SCREENYRES 240
#define CENTERX SCREENXRES/2
#define CENTERY SCREENYRES/2
#define MARGINX 16 // margins for text display
#define MARGINY 16
#define OTLEN 2048 // Maximum number of OT entries
#define PRIMBUFFLEN 32768 // Maximum number of POLY_GT3 primitives
// Display and draw environments, double buffered
DISPENV disp[2];
DRAWENV draw[2];
u_long ot[2][OTLEN]; // Ordering table (contains addresses to primitives)
char primbuff[2][PRIMBUFFLEN]; // Primitive list // That's our prim buffer
char * nextpri = primbuff[0]; // Primitive counter
short db = 0; // Current buffer counter
// Store TIM files in an array so we can iterate over them - see 'cubetex.c' for TIM_FILE struct and declaration
TIM_FILE * timFiles[3];
TIM_IMAGE timImages[3];
// Get included tim files address
extern TIM_FILE _binary_TIM_cube_tim_start;
extern TIM_FILE _binary_TIM_sky_tim_start;
extern TIM_FILE _binary_TIM_bg_tim_start;
// Light
CVECTOR BGc = {130, 200, 255, 0};
// Back color
VECTOR BKc = {128, 128, 128, 0};
// Light rotation angle
SVECTOR lgtang = {0, 0, 0};
// These will be used to store the light rotation matrix, cube rotation matrix, and composite light matrix.
MATRIX rotlgt, rotcube, light;
// Local Light Matrix : Direction and reach of each light source.
MATRIX lgtmat = {
// X Y Z
0, -ONE, 0, // Lightsource 1 : here, the light source is at the Bottom-Left of the screen, and points into the screen.
0, 0, 0, // Lightsource 2
0, 0, 0, // Lightsource 3
};
// Local Color Matrix
MATRIX cmat = {
// L1 L2 L3
4096, 0, 0, // R
4096, 0, 0, // G
4096, 0, 0 // B
};
// Prototypes
void init(void);
void display(void);
void LoadTexture(TIM_FILE * tim, TIM_IMAGE * tparam);
void init(){
// Reset the GPU before doing anything and the controller
PadInit(0);
ResetGraph(0);
// Initialize and setup the GTE
InitGeom();
SetGeomOffset(CENTERX, CENTERY); // x, y offset
SetGeomScreen(CENTERX); // Distance between eye and screen
// Set the display and draw environments
SetDefDispEnv(&disp[0], 0, 0 , SCREENXRES, SCREENYRES);
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[1], 0, 0, SCREENXRES, SCREENYRES);
if (VMODE)
{
SetVideoMode(MODE_PAL);
disp[0].screen.y += 8;
disp[1].screen.y += 8;
}
SetDispMask(1);
// Set far color
SetFarColor( BGc.r, BGc.g, BGc.b );
// Set Ambient color
SetBackColor( BKc.vx, BKc.vy, BKc.vz );
// Set Color matrix
SetColorMatrix(&cmat);
// Set Fog settings
SetFogNearFar( 128, 1024, CENTERX );
setRGB0(&draw[0], 0, 0, 0);
setRGB0(&draw[1], 0, 0, 0);
draw[0].isbg = 1;
draw[1].isbg = 1;
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
// Init font system
FntLoad(960, 0);
FntOpen(MARGINX, MARGINY, SCREENXRES - MARGINX * 2, SCREENXRES - MARGINY * 2, 0, 512 );
}
void display(void){
DrawSync(0);
VSync(0);
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
DrawOTag(&ot[db][OTLEN - 1]);
db = !db;
nextpri = primbuff[db];
}
void LoadTexture(TIM_FILE * tim, TIM_IMAGE * tparam){ // This part is from Lameguy64's tutorial series : lameguy64.net/svn/pstutorials/chapter1/3-textures.html login/pw: annoyingmous
OpenTIM((u_long*)tim); // Open the tim binary data, feed it the address of the data in memory
ReadTIM(tparam); // This read the header of the TIM data and sets the corresponding members of the TIM_IMAGE structure
LoadImage(tparam->prect, tparam->paddr); // Transfer the data from memory to VRAM at position prect.x, prect.y
DrawSync(0); // Wait for the drawing to end
if (tparam->mode & 0x8){ // check 4th bit // If 4th bit == 1, TIM has a CLUT
LoadImage(tparam->crect, tparam->caddr); // Load it to VRAM at position crect.x, crect.y
DrawSync(0); // Wait for drawing to end
}
}
int main() {
// Populate array with pointers to TIM data
timFiles[0] = &_binary_TIM_cube_tim_start;
timFiles[1] = &_binary_TIM_sky_tim_start;
timFiles[2] = &_binary_TIM_bg_tim_start;
// Pad values
int pad, oldPad;
// Set semi-transparency on (1) and off (0)
int stpFlag = 1;
// Set primitive semi-transparency rate - See LibOver47.pdf, p.107
int stpRate = 0;
// If set, rotate cube
int rotateCube = 1;
int offsetCube = 0;
// Array of pointers to a POLY_G4 we iterate over
POLY_GT3 * poly[NUM_PRIM];
// Rotation vector
SVECTOR rotVector={ 384, 0, 128, 0 };
// Translation vector
VECTOR transVector= { 0, 0, 256, 0};
// BG sprt
POLY_FT4 * bg;
// Normalized UV coordinates for the X axis
long normH = ((255 << 12) / SCREENXRES);
// Init Disp/Drawenv, Font, etc.
init();
// Load textures to VRAM
for (char tex = 0; tex < NUM_TEX; tex++){
LoadTexture(timFiles[tex], &timImages[tex]);
}
// Main loop
while (1) {
// Work matrix
MATRIX Work= {0} ;
// Triangle counters array - one for each cube
long curTriangle[3] = {0,0,0};
// Clear the current OT
ClearOTagR(ot[db], OTLEN);
// Draw BG
bg = (POLY_FT4 * )nextpri;
SetPolyFT4(bg);
bg->tpage = getTPage( timImages[2].mode&0x3, 0,
timImages[2].prect->x,
timImages[2].prect->y
);
if ( (timImages[2].mode & 0x3) < 2 ) {
setClut( bg,
timImages[2].crect->x,
timImages[2].crect->y
);
}
setRGB0(bg, 127,127,127);
setUV4(bg, 0, 0,
SCREENYRES, 0,
0, SCREENYRES,
SCREENYRES, SCREENYRES
);
setXY4(bg, 0 , 0,
SCREENXRES, 0,
0 , SCREENYRES,
SCREENXRES, SCREENYRES);
addPrim(ot[db][OTLEN-1], bg);
nextpri += sizeof(POLY_FT4);
// Rotate cube
if(rotateCube) rotVector.vy += 10;
// Find and apply light rotation matrix
// Find rotmat from light angles
RotMatrix_gte(&lgtang, &rotlgt);
// Find rotmat from cube angles
RotMatrix_gte(&rotVector, &rotcube);
// RotMatrix cube * RotMatrix light
MulMatrix0(&rotcube, &rotlgt, &rotlgt);
// Light Matrix * RotMatrix light
MulMatrix0(&lgtmat, &rotlgt, &light);
// Set new light matrix
SetLightMatrix(&light);
// Apply Transl, Rot, then matrix
RotMatrix(&rotVector, &Work);
TransMatrix(&Work, &transVector);
SetRotMatrix(&Work);
SetTransMatrix(&Work);
long p, OTz, Flag;
// Draw NUM_PRIM primitives
for (int i = 0; i < (modelCube.len * 3); i += 3) {
// Set projection matrices
transVector.vx = 0;
TransMatrix(&Work, &transVector);
SetRotMatrix(&Work);
SetTransMatrix(&Work);
// Cast nextpri as POLY_GT3
poly[0] = (POLY_GT3 *)nextpri;
poly[1] = (POLY_GT3 *)nextpri+sizeof(POLY_GT3);
// Initialize the primitives
SetPolyGT3(poly[0]);
SetPolyGT3(poly[1]);
// Reflection Cube
// This cube has its UVs mapped directly to VRAM coordinates
// We're using the framebuffers as a texture (0,0 and 0,256)
// Get 256x256 texture page that's at x0, y0
poly[1]->tpage = getTPage( 2, stpRate,
0,
!(db) << 8 // Here, we're using db's value that can be either 0 or 1 to determine the texture page Y coordinate.
);
// Set STP
SetSemiTrans(poly[1], stpFlag);
// Map coordinates from drawarea (320x240) to texture size (128x128) in fixed point math
// x = x * (256 / 320) => ( x * ( 128 * 4096 ) / 320 ) / 4096
// y = y * (240 / 240) => ( y * ( 240 * 4096 ) / 240 ) / 4096 => y * 2184 >> 12 -> y
setUV3( poly[1],
(poly[1]->x0 * normH) >> 12,
poly[1]->y0 - (!(db) << 4) , // We're using db's value again to add a 16 pixels offset to the Y's coordinates of the UVs
(poly[1]->x1 * normH) >> 12,
poly[1]->y1 - (!(db) << 4), // We have to do that because the buffer is 240 high, whereas our texture page is 256, hence 256 - 240 == 16
(poly[1]->x2 * normH) >> 12,
poly[1]->y2 - (!(db) << 4)
);
// Draw "container" cube
// This cube has a texture with transparent areas.
// STP bit is set on PNG's alpha channel : img2tim -usealpha -org 320 0 -o cube.tim cube.png
poly[0]->tpage = getTPage( timImages[0].mode&0x3, stpRate,
timImages[0].prect->x,
timImages[0].prect->y
);
// If 8/4bpp, load CLUT to vram
if ( (timImages[0].mode & 0x3) < 2 ) {
setClut( poly[0],
timImages[0].crect->x,
timImages[0].crect->y
);
}
// Set UV coordinates
setUV3(poly[0], modelCube.u[i].vx, modelCube.u[i].vy,
modelCube.u[i+2].vx, modelCube.u[i+2].vy,
modelCube.u[i+1].vx, modelCube.u[i+1].vy
);
// Rotate, translate, and project the vectors and output the results into a primitive
// curTriangle, +1, +2 point to the vertices index of the triangle we're drawing.
OTz = RotTransPers(&modelCube_mesh[ modelCube_index[ curTriangle[0] ] ] , ( long * ) &poly[1]->x0, &p, &Flag);
OTz += RotTransPers(&modelCube_mesh[ modelCube_index[ curTriangle[0] + 2] ], ( long*) &poly[1]->x1, &p, &Flag);
OTz += RotTransPers(&modelCube_mesh[ modelCube_index[ curTriangle[0] + 1] ], ( long * ) &poly[1]->x2, &p, &Flag);
// Here we're only messing with the matrices so that the foreground cube can be moved independantly from the backgound one.
// In real code, you don't want to do the same calculation twice !
transVector.vx = offsetCube;
TransMatrix(&Work, &transVector);
SetRotMatrix(&Work);
SetTransMatrix(&Work);
OTz = RotTransPers(&modelCube_mesh[ modelCube_index[ curTriangle[0] ] ] , ( long * ) &poly[0]->x0, &p, &Flag);
OTz += RotTransPers(&modelCube_mesh[ modelCube_index[ curTriangle[0] + 2] ], ( long*) &poly[0]->x1, &p, &Flag);
OTz += RotTransPers(&modelCube_mesh[ modelCube_index[ curTriangle[0] + 1] ], ( long * ) &poly[0]->x2, &p, &Flag);
// The right way to do it is re-using the results from the first RotTransPer() batch
// i.e commenting lines 273 to 280 and uncommenting lines 284 to 289
//~ poly[0]->x0 = poly[1]->x0;
//~ poly[0]->y0 = poly[1]->y0;
//~ poly[0]->x1 = poly[1]->x1;
//~ poly[0]->y1 = poly[1]->y1;
//~ poly[0]->x2 = poly[1]->x2;
//~ poly[0]->y2 = poly[1]->y2;
// Average OTz value for 3 vertices
// OTz is 1/4 of screen to vertex length
OTz /= 3;
// Work color vectors
// This is the hue of the transparent cube
CVECTOR prismCol = {0xff,0xff,0x0,0x0};
// This will store the result of the depth cueing.
CVECTOR outCol, outCol1, outCol2 = { 0,0,0,0 };
// Find local color from three normal vectors and perform depth cueing.
gte_NormalColorDpq3( &modelCube.n[i+0],
&modelCube.n[i+2],
&modelCube.n[i+3],
&prismCol, p, &outCol, &outCol1, &outCol2);
// Set vertex colors on transparent/background cube
setRGB0(poly[1], outCol.r, outCol.g , outCol.b);
setRGB1(poly[1], outCol1.r, outCol1.g, outCol1.b);
setRGB2(poly[1], outCol2.r, outCol2.g, outCol2.b);
// Non-transparent/foreground cube color
// Find local color from three normal vectors and perform depth cueing.
gte_NormalColorDpq( &modelCube.n[i+0], &modelCube.c[i+0], p, &outCol);
gte_NormalColorDpq( &modelCube.n[i+2], &modelCube.c[i+2], p, &outCol2);
gte_NormalColorDpq( &modelCube.n[i+1], &modelCube.c[i+1], p, &outCol1);
// Set vertex colors
setRGB0(poly[0], outCol.r, outCol.g , outCol.b);
setRGB1(poly[0], outCol1.r, outCol1.g, outCol1.b);
setRGB2(poly[0], outCol2.r, outCol2.g, outCol2.b);
// If OTz is in range (not too close)
if ((OTz > 0) && (OTz < OTLEN))
// Add to ordering table, at index OTz-2
AddPrim(&ot[ db ][ OTz-2 ], poly[0]);
AddPrim(&ot[ db ][ OTz-2 ], poly[1]);
// Increment next primitive address
nextpri += sizeof(POLY_GT3)*2;
// Increment to next triangle
curTriangle[0] += 3;
curTriangle[1] += 3;
}
// Get pad input
pad = PadRead(0);
// If select button is used
if ( pad & PADselect && !(oldPad & PADselect) ){
// Flip STP flag
stpFlag = !stpFlag;
// Set flag to avoir misfire
oldPad = pad;
}
// Reset flag when button released
if ( !(pad & PADselect) && oldPad & PADselect) {
oldPad = pad;
}
// If start button is used
if ( pad & PADstart && !( oldPad & PADstart ) ){
// Switch STP rates
stpRate > 2 ? stpRate = 0 : stpRate++;
// Set flag to avoir misfire
oldPad = pad;
}
// Reset flag when button released
if (!(pad & PADstart) && oldPad & PADstart) {
oldPad = pad;
}
if ( pad & PADRdown && !( oldPad & PADRdown ) ){
// Switch STP rates
offsetCube = 0;
// Set flag to avoir misfire
oldPad = pad;
}
// Reset flag when button released
if (!(pad & PADRdown) && oldPad & PADRdown) {
oldPad = pad;
}
if ( pad & PADLright && !( oldPad & PADLright ) ){
offsetCube += 6;
}
if ( pad & PADLleft && !( oldPad & PADLleft ) ){
offsetCube -= 6;
}
FntPrint("Hello fx !\n");
FntPrint("Select: STP on/off\nStart: Cycle STP rates\nLeft/Right: Move FG cube.\nX: Reset cube pos\n");
FntPrint("STP : %d\n", stpFlag);
FntPrint("STP rate : %d\n", stpRate);
FntFlush(-1);
display();
}
return 0;
}
================================================
FILE: hello_gte_opti/Makefile
================================================
TARGET = hello_gte_opti
SRCS = hello_gte_opti.c \
include ../common.mk
================================================
FILE: hello_gte_opti/hello_gte_opti.c
================================================
// Hello free cycles !
//
// Ref : /psyq/DOCS/Devrefs/Inlinref.pdf, p.18
// /psyq/psx/sample/scea/GTE
// https://psx-spx.consoledev.net/geometrytransformationenginegte/
// PSX / Z+
// screen /
//coordinate +-----X+
//system / |
// eye | Y+
//
// Credits, thanks : Nicolas Noble, Sickle, Lameguy64 @ psxdev discord : https://discord.com/invite/N2mmwp
// https://discord.com/channels/642647820683444236/663664210525290507/834831466100949002
// Schnappy 2021
#include
#include
#include
#include
#include
// OldWorld PsyQ has a inline_c.h file for inline GTE functions. We have to use the one at https://github.com/grumpycoders/pcsx-redux/blob/07f9b02d1dbb68f57a9f5b9773041813c55a4913/src/mips/psyq/include/inline_n.h
// because the real GTE commands are needed in nugget : https://psx-spx.consoledev.net/geometrytransformationenginegte/#gte-coordinate-calculation-commands
#include
// RAM -> CPU and CPU -> GTE macros :
#include "../includes/CPUMAC.H"
#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL
#define SCREENXRES 320 // Screen width
#define SCREENYRES 240 + (VMODE << 4) // Screen height : If VMODE is 0 = 240, if VMODE is 1 = 256
#define CENTERX ( SCREENXRES >> 1 ) // Center of screen on x
#define CENTERY ( SCREENYRES >> 1 ) // Center of screen on y
#define MARGINX 0 // margins for text display
#define MARGINY 32
#define FONTSIZE 8 * 7 // Text Field Height
#define OTLEN 10 // Ordering Table Length
DISPENV disp[2]; // Double buffered DISPENV and DRAWENV
DRAWENV draw[2];
u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes
char primbuff[2][32768] = {1}; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes
char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0]
short db = 0; // index of which buffer is used, values 0, 1
// DCache setup
#define dc_camdirp ((sshort*) getScratchAddr(0))
#define dc_ip ((uchar*) getScratchAddr(1))
#define dc_opzp ((slong*) getScratchAddr(2))
#define dc_wmatp ((MATRIX*) getScratchAddr(3))
#define dc_cmatp ((MATRIX*) getScratchAddr(9))
#define dc_sxytbl ((DVECTOR*) getScratchAddr(15))
void init(void)
{
ResetGraph(0);
// Initialize and setup the GTE
InitGeom();
//~ SetGeomOffset(CENTERX,CENTERY);
gte_SetGeomOffset(CENTERX,CENTERY);
gte_SetGeomScreen(CENTERX);
// Set display environment
SetDefDispEnv(&disp[0], 0, 0, SCREENXRES, SCREENYRES);
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES);
// Set draw environment
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[1], 0, 0, SCREENXRES, SCREENYRES);
if (VMODE){ SetVideoMode(MODE_PAL);}
SetDispMask(1);
// Set background color
setRGB0(&draw[0], 50, 50, 50);
setRGB0(&draw[1], 50, 50, 50);
draw[0].isbg = 1;
draw[1].isbg = 1;
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
FntLoad(960, 0);
FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 );
}
void display(void)
{
// Wait for drawing
DrawSync(0);
// Wait for vsync
VSync(1);
// Flip DISP and DRAW env
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
DrawOTag(&ot[db][OTLEN - 1]);
// Flip db index
db = !db;
// Get next primitive in buffer
nextpri = primbuff[db];
}
int main(void)
{
long p, flag, OTz;
SVECTOR rotVector = {0};
SVECTOR rotVector4 = {0}; // Initialize rotation vector {x, y, z} - ALWAYS !
VECTOR transVector = {0, 0, CENTERX, 0}; // Initialize translation vector {x, y, z}
SVECTOR vertPos[4] = {
{ 0, -32, 0, 0 }, // Vert 1
{ 32, 0, 0, 0 }, // Vert 2
{ -32, 0, 0, 0 },
{ 0, 32, 0, 0 }
}; // Vert 3
MATRIX workMatrix = {0};
POLY_F3 * poly = {0}; // pointer to a POLY_F4
POLY_F4 * poly4 = {0}; // pointer to a POLY_F4
init();
// Declare registers
register ulong ur0 asm("$16");
register ulong ur1 asm("$17");
register ulong ur2 asm("$18");
register ulong ur3 asm("$19");
register ulong ur4 asm("$20");
register ulong ur5 asm("$21");
while (1)
{
// Set Ordering table
ClearOTagR(ot[db], OTLEN);
// Cast next primitives in buffer as a POLY_F3 and a POLY_F4 (see display() )
poly = (POLY_F3 *)nextpri;
nextpri += sizeof(POLY_F3);
poly4 = (POLY_F4 *)nextpri;
// Set matrices - Move to left of screen
// Draw on the left part of the screen
transVector.vx = -CENTERX/2;
// Increment rotation angle on Y axis
rotVector.vy += 1;
// Find rotation matrix from vector, store in
RotMatrix_gte(&rotVector, &workMatrix);
// Ditto for translation
TransMatrix(&workMatrix, &transVector);
// Set the matrices we just found
gte_SetRotMatrix(&workMatrix);
gte_SetTransMatrix(&workMatrix);
// Draw a Tri and a Quad
// Copy Tri vertices from ram to cpu registers casting as ulong so that ur0 (len 32bits) contains vx and vy (2 * 8bits)
// Hence the use of vx, vz members
cpu_ldr(ur0,(ulong*)&vertPos[0].vx); // Put vx, vy value in ur0
cpu_ldr(ur1,(ulong*)&vertPos[0].vz); // Put vz, pad value in ur1
cpu_ldr(ur2,(ulong*)&vertPos[1].vx);
cpu_ldr(ur3,(ulong*)&vertPos[1].vz);
cpu_ldr(ur4,(ulong*)&vertPos[2].vx);
cpu_ldr(ur5,(ulong*)&vertPos[2].vz);
// Load the gte registers from the cpu registers (gte-cpu move 1 cycle) - mtc2 %0, $0;
cpu_gted0(ur0);
cpu_gted1(ur1);
cpu_gted2(ur2);
cpu_gted3(ur3);
cpu_gted4(ur4);
cpu_gted5(ur5);
// Tri RotTransPers3
// The two last cpu->gte copy will happen during the 2 nops in gte_rtpt()
gte_rtpt();
// Fill the cpu registers with the Quad vertices
cpu_ldr(ur0,(ulong*)&vertPos[0].vx);
cpu_ldr(ur1,(ulong*)&vertPos[0].vz);
cpu_ldr(ur2,(ulong*)&vertPos[1].vx);
cpu_ldr(ur3,(ulong*)&vertPos[1].vz);
cpu_ldr(ur4,(ulong*)&vertPos[2].vx);
cpu_ldr(ur5,(ulong*)&vertPos[2].vz);
// Get nclip value, and win two cycles
gte_nclip();
// Copy Tri 's screen coordinates from gte registers to d-cache.
gte_stsxy3c(&dc_sxytbl[0]);
// Set matrices - Move to right of screen
transVector.vx = CENTERX/2;
// Increment rot on X/Y axis
rotVector4.vy -= 1 ;
rotVector4.vx -= 1 ;
// Set matrices
RotMatrix_gte(&rotVector4, &workMatrix);
TransMatrix(&workMatrix, &transVector);
gte_SetRotMatrix(&workMatrix);
gte_SetTransMatrix(&workMatrix);
// Load the gte registers from the cpu registers (gte-cpu move 1 cycle) - mtc2 %0, $0;
cpu_gted0(ur0);
cpu_gted1(ur1);
cpu_gted2(ur2);
cpu_gted3(ur3);
cpu_gted4(ur4);
cpu_gted5(ur5);
// Quad RotTransPers3
// Getting 2 cycles back thanks to nops
gte_rtpt();
// gte_nclip() has 2 nops, lets use them to load the remaining vertex data from ram->cpu register
cpu_ldr(ur0,(ulong*)&vertPos[3].vx);
cpu_ldr(ur1,(ulong*)&vertPos[3].vz);
// Calculate nclip (outer product)
gte_nclip();
// Copy result to d-cache + 3
gte_stsxy3c(&dc_sxytbl[3]);
// Copy from cpu-gte
cpu_gted0(ur0);
cpu_gted1(ur1);
// Quad last vertex RotTransPers
// These two last cpu->gte load are free :p
gte_rtps();
gte_nclip();
// Copy last vertex value to d-cache
gte_stsxy(&dc_sxytbl[6]);
// Get p, flag, OTz
gte_stdp(&p);
gte_stflg(&flag);
gte_stszotz(&OTz);
// That's 10 cycles we won back ?
// Copy vertices data from d-cache to ram
// Tri
*(unsigned long long*)&poly->x0 = *(unsigned long long*)&dc_sxytbl[0];
*(ulong*)&poly->x2 = *(ulong*)&dc_sxytbl[2];
// Quad
*(unsigned long long*)&poly4->x0 = *(unsigned long long*)&dc_sxytbl[3];
*(unsigned long long*)&poly4->x2 = *(unsigned long long*)&dc_sxytbl[5];
// Initialize polygons
setPolyF3(poly);
setRGB0(poly, 255, 0, 255);
setPolyF4(poly4);
setRGB0(poly4, 0, 255, 255);
// Add to OT
addPrim(ot[db], poly);
addPrim(ot[db], poly4);
// Display text
FntPrint("Hello Free cycles !\n");
FntFlush(-1);
display();
}
return 0;
}
================================================
FILE: hello_light/Makefile
================================================
TARGET = hello_light
SRCS = hello_light.c \
include ../common.mk
================================================
FILE: hello_light/cube.c
================================================
SVECTOR modelCube_mesh[] = {
{ -128,128,128 },
{ 128,128,128 },
{ 128,128,-128 },
{ -128,128,-128 },
{ -128,-128,128 },
{ 128,-128,128 },
{ 128,-128,-128 },
{ -128,-128,-128 }
};
SVECTOR modelCube_normal[] = {
2365,-2365,-2365, 0,
-2365,-2365,-2365, 0,
-2365,-2365,2365, 0,
2365,-2365,2365, 0,
2365,2365,-2365, 0,
-2365,2365,-2365, 0,
-2365,2365,2365, 0,
2365,2365,2365, 0
};
CVECTOR modelCube_color[] = {
255,237,0, 0,
255,235,0, 0,
255,236,0, 0,
255,2,0, 0,
254,3,0, 0,
255,8,0, 0,
229,0,255, 0,
229,0,255, 0,
229,0,255, 0,
5,16,250, 0,
0,12,255, 0,
0,12,255, 0,
4,251,25, 0,
0,255,26, 0,
0,255,26, 0,
0,248,255, 0,
0,248,255, 0,
0,248,255, 0,
255,237,0, 0,
255,237,0, 0,
255,235,0, 0,
255,2,0, 0,
255,6,2, 0,
254,3,0, 0,
229,0,255, 0,
232,21,232, 0,
229,0,255, 0,
5,16,250, 0,
2,13,253, 0,
0,12,255, 0,
4,251,25, 0,
0,255,26, 0,
0,255,26, 0,
0,248,255, 0,
0,248,255, 0,
0,248,255, 0
};
int modelCube_index[] = {
0,2,3,
7,5,4,
4,1,0,
5,2,1,
2,7,3,
0,7,4,
0,1,2,
7,6,5,
4,5,1,
5,6,2,
2,6,7,
0,3,7
};
TMESH modelCube = {
modelCube_mesh,
modelCube_normal,
0,
modelCube_color,
12
};
SVECTOR modelCube1_mesh[] = {
{ -128,128,128 },
{ 128,128,128 },
{ 128,128,-128 },
{ -128,128,-128 },
{ -128,-128,128 },
{ 128,-128,128 },
{ 128,-128,-128 },
{ -128,-128,-128 }
};
SVECTOR modelCube1_normal[] = {
2365,-2365,-2365, 0,
-2365,-2365,-2365, 0,
-2365,-2365,2365, 0,
2365,-2365,2365, 0,
2365,2365,-2365, 0,
-2365,2365,-2365, 0,
-2365,2365,2365, 0,
2365,2365,2365, 0
};
CVECTOR modelCube1_color[] = {
255,237,0, 0,
255,235,0, 0,
255,236,0, 0,
255,2,0, 0,
254,3,0, 0,
255,8,0, 0,
229,0,255, 0,
229,0,255, 0,
229,0,255, 0,
5,16,250, 0,
0,12,255, 0,
0,12,255, 0,
4,251,25, 0,
0,255,26, 0,
0,255,26, 0,
0,248,255, 0,
0,248,255, 0,
0,248,255, 0,
255,237,0, 0,
255,237,0, 0,
255,235,0, 0,
255,2,0, 0,
255,6,2, 0,
254,3,0, 0,
229,0,255, 0,
232,21,232, 0,
229,0,255, 0,
5,16,250, 0,
2,13,253, 0,
0,12,255, 0,
4,251,25, 0,
0,255,26, 0,
0,255,26, 0,
0,248,255, 0,
0,248,255, 0,
0,248,255, 0
};
int modelCube1_index[] = {
0,2,3,
7,5,4,
4,1,0,
5,2,1,
2,7,3,
0,7,4,
0,1,2,
7,6,5,
4,5,1,
5,6,2,
2,6,7,
0,3,7
};
TMESH modelCube1 = {
modelCube1_mesh,
modelCube1_normal,
0,
modelCube1_color,
12
};
================================================
FILE: hello_light/hello_light.c
================================================
/* hello_light.c, by Schnappy, 06-2021
- Demonstrates setting and using light sources in 3D without libgs.
Controls:
Start - Toggle interactive/non-interactive mode.
Select - Reset object's position and angles.
L1/L2 - Move object closer/farther.
L2/R2 - Rotate object (XY).
Up/Down/Left/Right - Rotate object (XZ/YZ).
Triangle/Cross/Square/Circle - Move object up/down/left/right.
based on primdraw.c by Lameguy64 (http://www.psxdev.net/forum/viewtopic.php?f=64&t=537)
2014 Meido-Tek Productions.
*/
#include
#include
#include
#include
#include
// Sample vector model
#include "cube.c"
#define VMODE 0
#define SCREENXRES 320
#define SCREENYRES 240
#define CENTERX SCREENXRES/2
#define CENTERY SCREENYRES/2
#define OTLEN 2048 // Maximum number of OT entries
#define PRIMBUFFLEN 32768 // Maximum number of POLY_GT3 primitives
// Display and draw environments, double buffered
DISPENV disp[2];
DRAWENV draw[2];
u_long ot[2][OTLEN]; // Ordering table (contains addresses to primitives)
char primbuff[2][PRIMBUFFLEN]; // Primitive list // That's our prim buffer
char * nextpri = primbuff[0]; // Primitive counter
short db = 0; // Current buffer counter
long t, p, OTz, Flag; // t == vertex count, p == depth cueing interpolation value, OTz == value to create Z-ordered OT, Flag == see LibOver47.pdf, p.143
// Lighting
// See PsyQ's LibOver47.pdf, p.133 for more details on the purpose of each component and full calculations.
// Far color : This is the color used to fade to when the mesh is far from the cam (NearFog)
CVECTOR BGc = {150, 50, 75, 0};
// Back color
VECTOR BKc = {128, 128, 128, 0};
// Light rotation angle
SVECTOR lgtang = {0, 0, 0};
// These will be used to store the light rotation matrix, cube rotation matrix, and composite light matrix.
MATRIX rotlgt, rotcube, light;
// Local Light Matrix : Direction and reach of each light source.
// Each light points in the direction aligned with the axis, hence direction is in the same coordinate system as the PSX (see l.23-30 of this file)
// Negative/positive value denotes light direction on corresponding axis
// -4096 > Value < 4096 denotes reach/intensity of light source
MATRIX lgtmat = {
// X Y Z
-ONE, -ONE, ONE, // Lightsource 1 : here, the light source is at the Bottom-Left of the screen, and points into the screen.
0, 0, 0, // Lightsource 2
0, 0, 0, // Lightsource 3
};
// Local Color Matrix
// Set color of each light source (L)
// Value range : 0 > x < 4096
MATRIX cmat = {
// L1 L2 L3
4096, 0, 0, // R
4096, 0, 0, // G
4096, 0, 0 // B
};
// Prototypes
void init(void);
void display(void);
void init(){
// Reset the GPU before doing anything and the controller
PadInit(0);
ResetGraph(0);
// Initialize and setup the GTE
InitGeom();
SetGeomOffset(CENTERX, CENTERY); // x, y offset
SetGeomScreen(CENTERX); // Distance between eye and screen
// Set the display and draw environments
SetDefDispEnv(&disp[0], 0, 0 , SCREENXRES, SCREENYRES);
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[1], 0, 0, SCREENXRES, SCREENYRES);
if (VMODE)
{
SetVideoMode(MODE_PAL);
disp[0].screen.y += 8;
disp[1].screen.y += 8;
}
SetDispMask(1);
// Set light env
// Set far color
SetFarColor( BGc.r, BGc.g, BGc.b );
// Set Ambient color
SetBackColor( BKc.vx, BKc.vy, BKc.vz );
// Set Color matrix
SetColorMatrix(&cmat);
// Set Fog settings
SetFogNearFar( 1200, 2200, SCREENXRES );
setRGB0(&draw[0], 0, 0, 255);
setRGB0(&draw[1], 0, 0, 255);
draw[0].isbg = 1;
draw[1].isbg = 1;
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
// Init font system
FntLoad(960, 0);
FntOpen(16, 16, 196, 64, 0, 256);
}
void display(void){
DrawSync(0);
VSync(0);
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
DrawOTag(&ot[db][OTLEN - 1]);
db = !db;
nextpri = primbuff[db];
}
int main() {
int i;
int PadStatus;
int TPressed=0;
int AutoRotate=1;
// Rotating cube
POLY_G3 * poly;
SVECTOR Rotate={ ONE/6,ONE/6,ONE/6 }; // Rotation coordinates
VECTOR Trans={ -SCREENXRES/2, 0, CENTERX * 3, 0 }; // Translation coordinates
VECTOR Scale={ ONE/2, ONE/2, ONE/2, 0 }; // Scaling coordinates : ONE == 4096
MATRIX Matrix={0}; // Matrix data for the GTE
// Static cube
POLY_G3 * poly1; // pointer to a POLY_G4
SVECTOR Rotate1={ ONE/6, ONE/6, ONE/6, 0 }; // Rotation coordinates
VECTOR Trans1={ SCREENXRES/2, 0, CENTERX * 3, 0 }; // Translation coordinates
VECTOR Scale1={ ONE/2, ONE/2, ONE/2, 0 }; // Scaling coordinates : ONE == 4096
MATRIX Matrix1={0}; // Matrix data for the GTE
init();
// Main loop
while (1) {
// Read pad status
PadStatus = PadRead(0);
if (AutoRotate == 0) {
if (PadStatus & PADL1) Trans.vz -= 4;
if (PadStatus & PADR1) Trans.vz += 4;
if (PadStatus & PADL2) Rotate.vz -= 8;
if (PadStatus & PADR2) Rotate.vz += 8;
if (PadStatus & PADLup) Rotate.vx -= 8;
if (PadStatus & PADLdown) Rotate.vx += 8;
if (PadStatus & PADLleft) Rotate.vy -= 8;
if (PadStatus & PADLright) Rotate.vy += 8;
if (PadStatus & PADRup) Trans.vy -= 2;
if (PadStatus & PADRdown) Trans.vy += 2;
if (PadStatus & PADRleft) Trans.vx -= 2;
if (PadStatus & PADRright) Trans.vx += 2;
}
if (PadStatus & PADstart) {
if (TPressed == 0) {
AutoRotate = (AutoRotate + 1) & 1;
Rotate.vy = Rotate.vx = Rotate.vz = ONE/6;
Scale.vx = Scale.vy = Scale.vz = ONE/2;
Trans.vx = -SCREENXRES/2;
Trans.vy = 0;
Trans.vz = CENTERX * 3;
}
TPressed = 1;
} else {
TPressed = 0;
}
if (AutoRotate) {
Rotate.vy += 8; // Pan
Rotate.vx += 8; // Tilt
//~ Rotate.vz += 8; // Roll
}
// Clear the current OT
ClearOTagR(ot[db], OTLEN);
// Render the sample vector model
t=0;
// modelCube is a TMESH, len member == # vertices, but here it's # of triangle... So, for each tri * 3 vertices ...
for (i = 0; i < (modelCube.len*3); i += 3) {
poly = (POLY_G3 *)nextpri;
// Initialize the primitive and set its color values
SetPolyG3(poly);
// Rotate, translate, and project the vectors and output the results into a primitive
// Could be replaced with one call with RotTransPers3()
OTz = RotTransPers(&modelCube_mesh[modelCube_index[t]] , (long*)&poly->x0, &p, &Flag);
OTz += RotTransPers(&modelCube_mesh[modelCube_index[t+2]], (long*)&poly->x1, &p, &Flag);
OTz += RotTransPers(&modelCube_mesh[modelCube_index[t+1]], (long*)&poly->x2, &p, &Flag);
// Find light color
// Work color vectors
CVECTOR outCol, outCol1, outCol2 = { 0,0,0,0 };
// Find local color from three normal vectors and perform depth cueing.
// Could be replaced with one call with NormalColorDpq3()
NormalColorDpq(&modelCube.n[ modelCube_index[t+0] ], &modelCube.c[i+0], p, &outCol);
NormalColorDpq(&modelCube.n[ modelCube_index[t+2] ], &modelCube.c[i+2], p, &outCol1);
NormalColorDpq(&modelCube.n[ modelCube_index[t+1] ], &modelCube.c[i+1], p, &outCol2);
// Set vertex colors
setRGB0(poly, outCol.r, outCol.g , outCol.b);
setRGB1(poly, outCol1.r, outCol1.g, outCol1.b);
setRGB2(poly, outCol2.r, outCol2.g, outCol2.b);
// Sort the primitive into the OT
OTz /= 3;
if ((OTz > 0) && (OTz < OTLEN))
AddPrim(&ot[db][OTz-2], poly);
nextpri += sizeof(POLY_G3);
t+=3;
}
// Find and apply light rotation matrix
// Find rotmat from light angles
RotMatrix_gte(&lgtang, &rotlgt);
// Find rotmat from cube angles
RotMatrix_gte(&Rotate, &rotcube);
// RotMatrix cube * RotMatrix light
MulMatrix0(&rotcube, &rotlgt, &rotlgt);
// Light Matrix * RotMatrix light
MulMatrix0(&lgtmat, &rotlgt, &light);
// Set new light matrix
SetLightMatrix(&light);
// Convert and set the matrices
// Find Rotation matrix from object's angles
RotMatrix(&Rotate, &Matrix);
// Find Scale matrix from object's angles
ScaleMatrix(&Matrix, &Scale);
// Find Translation matrix from object's angles
TransMatrix(&Matrix, &Trans);
// Set GTE's rotation matrix
SetRotMatrix(&Matrix);
// Set GTE's Translation matrix
SetTransMatrix(&Matrix);
// Draw static cube
t=0;
for (i = 0; i < (modelCube1.len*3); i += 3) {
poly1 = (POLY_G3 *)nextpri;
SetPolyG3(poly1);
OTz = RotTransPers(&modelCube1_mesh[modelCube1_index[t]] , (long*)&poly1->x0, &p, &Flag);
OTz += RotTransPers(&modelCube1_mesh[modelCube1_index[t+2]], (long*)&poly1->x1, &p, &Flag);
OTz += RotTransPers(&modelCube1_mesh[modelCube1_index[t+1]], (long*)&poly1->x2, &p, &Flag);
CVECTOR outCol = { 0,0,0,0 };
CVECTOR outCol1 = { 0,0,0,0 };
CVECTOR outCol2 = { 0,0,0,0 };
NormalColorDpq(&modelCube1.n[ modelCube1_index[t+0] ], &modelCube1.c[i+0], p, &outCol);
NormalColorDpq(&modelCube1.n[ modelCube1_index[t+2] ], &modelCube1.c[i+2], p, &outCol1);
NormalColorDpq(&modelCube1.n[ modelCube1_index[t+1] ], &modelCube1.c[i+1], p, &outCol2);
setRGB0(poly1, outCol.r, outCol.g , outCol.b);
setRGB1(poly1, outCol1.r, outCol1.g, outCol1.b);
setRGB2(poly1, outCol2.r, outCol2.g, outCol2.b);
OTz /= 3;
if ((OTz > 0) && (OTz < OTLEN))
AddPrim(&ot[db][OTz-2], poly1);
nextpri += sizeof(POLY_G3);
t+=3;
}
// See l.216
RotMatrix_gte(&lgtang, &rotlgt);
RotMatrix_gte(&Rotate1, &rotcube);
MulMatrix0(&rotcube, &rotlgt, &rotlgt);
MulMatrix0(&lgtmat, &rotlgt, &light);
SetLightMatrix(&light);
// See l.227
RotMatrix(&Rotate1, &Matrix1);
ScaleMatrix(&Matrix1, &Scale1);
TransMatrix(&Matrix1, &Trans1);
SetRotMatrix(&Matrix1);
SetTransMatrix(&Matrix1);
FntPrint("Hello lightsources !\n");
FntFlush(-1);
display();
}
return 0;
}
================================================
FILE: hello_mod/Makefile
================================================
TARGET = hello_mod
SRCS = hello_mod.c \
src/mod.c \
../thirdparty/nugget/modplayer/modplayer.c \
HIT/STAR.HIT \
include ../common.mk
================================================
FILE: hello_mod/README.md
================================================
This is an example of how to use [@nicolasnoble](https://github.com/nicolasnoble/)'s [modplayer](https://github.com/grumpycoders/pcsx-redux/tree/main/src/mips/modplayer) port in conjunction with the psyq libs.
See the **wiki** for more details on MOD usage : [https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/MOD](https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/MOD).
## Credits & thanks
Many thanks to @nicolasnobe for his work and help on getting this example working.
MOD : stardust memories by Jester : https://modarchive.org/index.php?request=view_by_moduleid&query=59344
================================================
FILE: hello_mod/hello_mod.c
================================================
// Play a MOD file converted to HIT
// MOD Wiki page : https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/MOD
#include
#include
#include
#include
#include
#include
// Mod playback
#include "src/mod.h"
#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL
#define SCREENXRES 320 // Screen width
#define SCREENYRES 240 // Screen height : If VMODE is 0 = 240, if VMODE is 1 = 256
#define CENTERX SCREENXRES/2 // Center of screen on x
#define CENTERY SCREENYRES/2 // Center of screen on y
#define FONTX 960
#define FONTY 0
#define OTLEN 8
DISPENV disp[2]; // Double buffered DISPENV and DRAWENV
DRAWENV draw[2];
short db = 1; // index of which buffer is used, values 0, 1
// Font color
CVECTOR fntColor = { 128, 255, 0 };
CVECTOR fntColorBG = { 0, 0, 0 };
// MOD Playback
#define MAXVOLUME 65536
#define INCVOLUME 256
// State
enum PLAYBACK {
STOP = 0,
PLAY = 1,
PAUSE = 2,
};
// Music volume should range from 0 to 65536
u_int musicVolume = 2048;
enum PLAYBACK state = PLAY;
void init(void);
void FntColor(CVECTOR fgcol, CVECTOR bgcol );
void display(void);
void drawBG(void);
void checkPad(void);
void FntColor(CVECTOR fgcol, CVECTOR bgcol )
{
// The debug font clut is at tx, ty + 128
// tx = bg color
// tx + 1 = fg color
// We can override the color by drawing a rect at these coordinates
//
RECT fg = { FONTX+1, FONTY + 128, 1, 1 };
RECT bg = { FONTX, FONTY + 128, 1, 1 };
ClearImage(&fg, fgcol.r, fgcol.g, fgcol.b);
ClearImage(&bg, bgcol.r, bgcol.g, bgcol.b);
}
void init(void)
{
ResetCallback();
ResetGraph(0); // Initialize drawing engine with a complete reset (0)
InitGeom();
SetGeomOffset(CENTERX,CENTERY);
SetGeomScreen(CENTERX);
SetDefDispEnv(&disp[0], 0, 0 , SCREENXRES, SCREENYRES); // Set display area for both &disp[0] and &disp[1]
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES); // &disp[0] is on top of &disp[1]
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES); // Set draw for both &draw[0] and &draw[1]
SetDefDrawEnv(&draw[1], 0, 0 , SCREENXRES, SCREENYRES); // &draw[0] is below &draw[1]
// Set video mode
#if VMODE
SetVideoMode(MODE_PAL);
disp[0].disp.y = 8;
disp[1].disp.y = 8;
#endif
SetDispMask(1); // Display on screen
setRGB0(&draw[0], 40, 40, 40); // set color for first draw area
setRGB0(&draw[1], 40, 40, 40); // set color for second draw area
draw[0].isbg = 1; // set mask for draw areas. 1 means repainting the area with the RGB color each frame
draw[1].isbg = 1;
PutDispEnv(&disp[db]); // set the disp and draw environnments
PutDrawEnv(&draw[db]);
FntLoad(FONTX, FONTY); // Load font to vram at 960,0(+128)
FntOpen(16, 60, 288, 260, 0, 256 ); // FntOpen(x, y, width, height, black_bg, max. nbr. chars
FntColor(fntColor, fntColorBG);
}
void display(void)
{
DrawSync(0); // Wait for all drawing to terminate
VSync(0); // Wait for the next vertical blank
PutDispEnv(&disp[db]); // set alternate disp and draw environnments
PutDrawEnv(&draw[db]);
db = !db;
}
void checkPad(void)
{
u_short pad = 0;
static u_short oldPad;
pad = PadRead(0);
// Up
if ( pad & PADLup && !(oldPad & PADLup) )
{
MOD_PlaySoundEffect(11, 25, 15, 63);
oldPad = pad;
}
if ( !(pad & PADLup) && oldPad & PADLup )
{
oldPad = pad;
}
// Down
if ( pad & PADLdown && !(oldPad & PADLdown) )
{
MOD_PlaySoundEffect(12, 26, 15, 63);
oldPad = pad;
}
if ( !(pad & PADLdown) && oldPad & PADLdown )
{
oldPad = pad;
}
// Left
if ( pad & PADLleft && !(oldPad & PADLleft) )
{
MOD_PlaySoundEffect(13, 27, 15, 63);
oldPad = pad;
}
if ( !(pad & PADLleft) && oldPad & PADLleft )
{
oldPad = pad;
}
// Right
if ( pad & PADLright && !(oldPad & PADLright) )
{
// Channel 1 is transition anim, only take input when !transition
MOD_PlaySoundEffect(6, 21, 15, 63);
oldPad = pad;
}
if ( !(pad & PADLright) && oldPad & PADLright )
{
oldPad = pad;
}
// Cross button
if ( pad & PADRdown && !(oldPad & PADRdown) )
{
// Select sound
MOD_PlaySoundEffect(7, 22, 15, 63);
oldPad = pad;
}
if ( !(pad & PADRdown) && oldPad & PADRdown )
{
oldPad = pad;
}
// Square button
if ( pad & PADRleft && !(oldPad & PADRleft) )
{
// Select sound
MOD_PlaySoundEffect(8, 23, 15, 63);
oldPad = pad;
}
if ( !(pad & PADRleft) && oldPad & PADRleft )
{
oldPad = pad;
}
// Circle button
if ( pad & PADRright && !(oldPad & PADRright) )
{
// Select sound
MOD_PlaySoundEffect(9, 28, 15, 63);
oldPad = pad;
}
if ( !(pad & PADRright) && oldPad & PADRright )
{
oldPad = pad;
}
// Circle button
if ( pad & PADRup && !(oldPad & PADRup) )
{
// Select sound
MOD_PlaySoundEffect(9, 24, 15, 63);
oldPad = pad;
}
if ( !(pad & PADRup) && oldPad & PADRup )
{
oldPad = pad;
}
// Select button : Stop mod / Play back from the start
if ( pad & PADselect && !(oldPad & PADselect) )
{
if ( state == PLAY ) { stopMusic(); state = STOP; }
else if ( state == STOP ) { loadMod(); startMusic(); state = PLAY; }
oldPad = pad;
}
if ( !(pad & PADselect) && oldPad & PADselect )
{
oldPad = pad;
}
// Start button : Toggle playback pause
if ( pad & PADstart && !(oldPad & PADstart) )
{
if ( state == PLAY ) { pauseMusic(); state = PAUSE; }
else if ( state == PAUSE ) { resumeMusic(); state = PLAY; }
oldPad = pad;
}
if ( !(pad & PADstart) && oldPad & PADstart )
{
oldPad = pad;
}
// R1/R2 : change music volume
if ( pad & PADR1 && !(oldPad & PADR1) )
{
if(musicVolume < MAXVOLUME) {
musicVolume += INCVOLUME;
}
MOD_SetMusicVolume(musicVolume);
oldPad = pad;
}
if ( !(pad & PADR1) && oldPad & PADR1 )
{
oldPad = pad;
}
if ( pad & PADR2 && !(oldPad & PADR2) )
{
if(musicVolume > 0) {
musicVolume -= INCVOLUME;
}
MOD_SetMusicVolume(musicVolume);
oldPad = pad;
}
if ( !(pad & PADR2) && oldPad & PADR2 )
{
oldPad = pad;
}
}
int main() {
u_int t = 0;
init();
PadInit(0);
VSyncCallback(checkPad);
// Mod Playback
loadMod();
startMusic();
MOD_SetMusicVolume(musicVolume);
// Main loop
while (1)
{
// Timer
t++;
// Print stuff
FntPrint("Hello mod ! %d\nUse the pad's buttons to\nplay sound effects.\n", t);
FntPrint("State: %d, music volume : %d\n", state, musicVolume);
FntPrint("Start : play/pause music.\n");
FntPrint("select : stop/play music.\n");
FntPrint("R1/R2 : Change music volume.\n");
FntFlush(-1);
display();
}
return 0;
}
================================================
FILE: hello_mod/src/mod.c
================================================
#include "mod.h"
long musicEvent;
typedef struct SpuVoiceVolume {
short volL, volR;
} SpuVoiceVolume;
SpuVoiceVolume volumeState[24] = {0};
static void muteSPUvoices() {
for (unsigned i = 0; i < 24; i++) {
// Store current volume
SpuGetVoiceVolume(i, &(volumeState[i].volL), &(volumeState[i].volR) );
// Mute
SpuSetVoiceVolume(i, 0, 0);
}
}
static void restoreSPUvoices() {
for (unsigned i = 0; i < 24; i++) {
// Restore volume
SpuSetVoiceVolume(i, volumeState[i].volL, volumeState[i].volR );
}
}
// Playing a sound effect (aka mod note): https://discord.com/channels/642647820683444236/642848592754901033/898249196174458900
// Code by NicolasNoble : https://discord.com/channels/642647820683444236/663664210525290507/902624952715452436
void loadMod() {
printf("Loading MOD:\'%s\'\n", HITFILE);
MOD_Load((struct MODFileFormat*)HITFILE);
printf("%02d Channels, %02d Orders\n", MOD_Channels, MOD_SongLength);
}
static long processMusic() {
uint32_t old_hblanks = MOD_hblanks;
MOD_Poll();
uint32_t new_hblanks = MOD_hblanks;
if (old_hblanks != new_hblanks) SetRCnt(RCntCNT1, new_hblanks, RCntMdINTR);
return MOD_hblanks;
}
void startMusic() {
ResetRCnt(RCntCNT1);
SetRCnt(RCntCNT1, MOD_hblanks, RCntMdINTR);
StartRCnt(RCntCNT1);
musicEvent = OpenEvent(RCntCNT1, EvSpINT, EvMdINTR, processMusic);
EnableEvent(musicEvent);
restoreSPUvoices();
}
void pauseMusic() {
muteSPUvoices();
DisableEvent(musicEvent);
}
void resumeMusic() {
restoreSPUvoices();
EnableEvent(musicEvent);
}
void stopMusic() {
MOD_Silence();
StopRCnt(RCntCNT1);
DisableEvent(musicEvent);
CloseEvent(musicEvent);
}
================================================
FILE: hello_mod/src/mod.h
================================================
#pragma once
#include
#include
#include "../../thirdparty/nugget/common/hardware/hwregs.h"
#include "../../thirdparty/nugget/common/hardware/irq.h"
#include "../../thirdparty/nugget/common/syscalls/syscalls.h"
#define printf ramsyscall_printf
// Mod Playback
#include "../../thirdparty/nugget/modplayer/modplayer.h"
extern const uint8_t _binary_HIT_STAR_HIT_start[];
#define HITFILE _binary_HIT_STAR_HIT_start
extern long musicEvent;
void loadMod();
void startMusic();
void pauseMusic();
void resumeMusic();
void stopMusic();
================================================
FILE: hello_multi_vag/Makefile
================================================
TARGET = hello_multi_vag
SRCS = hello_multi_vag.c \
../VAG/hello.vag \
../VAG/poly.vag \
../VAG/0_come.vag \
../VAG/1_cuek.vag \
../VAG/2_erro.vag \
../VAG/3_hehe.vag \
../VAG/4_m4a1.vag \
../VAG/5_punc.vag \
../VAG/7_wron.vag \
../VAG/8_yooo.vag \
include ../common.mk
================================================
FILE: hello_multi_vag/README.md
================================================
See here for more informations about the VAG fileformat and tools :
https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/VAG
================================================
FILE: hello_multi_vag/hello_multi_vag.c
================================================
// Hello multi vag by Schnappy
// August 2021
//
//
// Load VAG samples to the SPU sound buffer and play them back with pad input
//
// See here for more details, file format infos, tools, conversion :
// https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/VAG
// Docs : see libformat47.pdf p.209
// libover47.pdf, p.271
// libref47.pdf, p.980
#include
#include
#include
#include
#include
// Sound system
#include
#include
#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL
#define SCREENXRES 320
#define SCREENYRES 240
#define CENTERX SCREENXRES/2
#define CENTERY SCREENYRES/2
#define MARGINX 0 // margins for text display
#define MARGINY 32
#define FONTSIZE 8 * 7 // Text Field Height
DISPENV disp[2]; // Double buffered DISPENV and DRAWENV
DRAWENV draw[2];
short db = 0; // index of which buffer is used, values 0, 1
// Number of VAG files to load
#define VAG_NBR 8
#define MALLOC_MAX VAG_NBR // Max number of time we can call SpuMalloc
// convert Little endian to Big endian
#define SWAP_ENDIAN32(x) (((x)>>24) | (((x)>>8) & 0xFF00) | (((x)<<8) & 0x00FF0000) | ((x)<<24))
// Memory management table ; allow MALLOC_MAX calls to SpuMalloc() - libref47.pdf p.1044
char spu_malloc_rec[SPU_MALLOC_RECSIZ * (MALLOC_MAX + 1)];
// Custom struct to handle VAG files
typedef struct VAGsound {
u_char * VAGfile; // Pointer to VAG data address
u_long spu_channel; // SPU voice to playback to
u_long spu_address; // SPU address for memory freeing spu mem
} VAGsound;
// VAG header struct (see fileformat47.pdf, p.209)
typedef struct VAGhdr { // All the values in this header must be big endian
char id[4]; // VAGp 4 bytes -> 1 char * 4
unsigned int version; // 4 bytes
unsigned int reserved; // 4 bytes
unsigned int dataSize; // (in bytes) 4 bytes
unsigned int samplingFrequency;// 4 bytes
char reserved2[12]; // 12 bytes -> 1 char * 12
char name[16]; // 16 bytes -> 1 char * 16
// Waveform data after that
} VAGhdr;
// SPU settings
SpuCommonAttr commonAttributes; // structure for changing common voice attributes
SpuVoiceAttr voiceAttributes ; // structure for changing individual voice attributes
// extern VAG files
extern u_char _binary____VAG_0_come_vag_start;
extern u_char _binary____VAG_1_cuek_vag_start;
extern u_char _binary____VAG_2_erro_vag_start;
extern u_char _binary____VAG_3_hehe_vag_start;
extern u_char _binary____VAG_4_m4a1_vag_start;
extern u_char _binary____VAG_5_punc_vag_start;
extern u_char _binary____VAG_7_wron_vag_start;
extern u_char _binary____VAG_8_yooo_vag_start;
// soundBank
VAGsound soundBank[VAG_NBR] = {
{ &_binary____VAG_0_come_vag_start,
SPU_00CH, 0 },
{ &_binary____VAG_1_cuek_vag_start,
SPU_01CH, 0 },
{ &_binary____VAG_2_erro_vag_start,
SPU_02CH, 0 },
{ &_binary____VAG_3_hehe_vag_start,
SPU_03CH, 0 },
{ &_binary____VAG_4_m4a1_vag_start,
SPU_04CH, 0 },
{ &_binary____VAG_5_punc_vag_start,
SPU_05CH, 0 },
{ &_binary____VAG_7_wron_vag_start,
SPU_06CH, 0 },
{ &_binary____VAG_8_yooo_vag_start,
SPU_07CH, 0 }
};
// Prototypes
void initGraph(void);
void display(void);
void initSnd(void);
u_long sendVAGtoSPU(unsigned int VAG_data_size, u_char *VAG_data);
void setVoiceAttr(unsigned int pitch, long channel, unsigned long soundAddr );
u_long setSPUtransfer(VAGsound * sound);
void playSFX(VAGsound * sound);
void initGraph(void)
{
ResetGraph(0);
SetDefDispEnv(&disp[0], 0, 0, SCREENXRES, SCREENYRES);
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[1], 0, 0, SCREENXRES, SCREENYRES);
if (VMODE)
{
SetVideoMode(MODE_PAL);
disp[0].screen.y += 8;
disp[1].screen.y += 8;
}
SetDispMask(1); // Display on screen
setRGB0(&draw[0], 50, 50, 50);
setRGB0(&draw[1], 50, 50, 50);
draw[0].isbg = 1;
draw[1].isbg = 1;
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
FntLoad(960, 0);
FntOpen(8, 60, 304, 200, 0, 500 );
}
void display(void)
{
DrawSync(0);
VSync(0);
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
db = !db;
}
// Audio initialisation & functions
void initSnd(void){
SpuInitMalloc(MALLOC_MAX, spu_malloc_rec); // Maximum number of blocks, mem. management table address.
commonAttributes.mask = (SPU_COMMON_MVOLL | SPU_COMMON_MVOLR); // Mask which attributes to set
commonAttributes.mvol.left = 0x3fff; // Master volume left
commonAttributes.mvol.right = 0x3fff; // see libref47.pdf, p.1058
SpuSetCommonAttr(&commonAttributes); // set attributes
SpuSetIRQ(SPU_OFF);
SpuSetKey(SpuOff, SPU_ALLCH);
}
u_long sendVAGtoSPU(unsigned int VAG_data_size, u_char *VAG_data){
u_long transferred;
SpuSetTransferMode(SpuTransByDMA); // DMA transfer; can do other processing during transfer
transferred = SpuWrite (VAG_data + sizeof(VAGhdr), VAG_data_size); // transfer VAG_data_size bytes from VAG_data address to sound buffer
SpuIsTransferCompleted (SPU_TRANSFER_WAIT); // Checks whether transfer is completed and waits for completion
return transferred;
}
void setVoiceAttr(unsigned int pitch, long channel, unsigned long soundAddr ){
voiceAttributes.mask= //~ Attributes (bit string, 1 bit per attribute)
(
SPU_VOICE_VOLL |
SPU_VOICE_VOLR |
SPU_VOICE_PITCH |
SPU_VOICE_WDSA |
SPU_VOICE_ADSR_AMODE |
SPU_VOICE_ADSR_SMODE |
SPU_VOICE_ADSR_RMODE |
SPU_VOICE_ADSR_AR |
SPU_VOICE_ADSR_DR |
SPU_VOICE_ADSR_SR |
SPU_VOICE_ADSR_RR |
SPU_VOICE_ADSR_SL
);
voiceAttributes.voice = channel; //~ Voice (low 24 bits are a bit string, 1 bit per voice )
voiceAttributes.volume.left = 0x0; //~ Volume
voiceAttributes.volume.right = 0x0; //~ Volume
voiceAttributes.pitch = pitch; //~ Interval (set pitch)
voiceAttributes.addr = soundAddr; //~ Waveform data start address
voiceAttributes.a_mode = SPU_VOICE_LINEARIncN; //~ Attack rate mode = Linear Increase - see libref47.pdf p.1091
voiceAttributes.s_mode = SPU_VOICE_LINEARIncN; //~ Sustain rate mode = Linear Increase
voiceAttributes.r_mode = SPU_VOICE_LINEARDecN; //~ Release rate mode = Linear Decrease
voiceAttributes.ar = 0x0; //~ Attack rate
voiceAttributes.dr = 0x0; //~ Decay rate
voiceAttributes.rr = 0x0; //~ Release rate
voiceAttributes.sr = 0x0; //~ Sustain rate
voiceAttributes.sl = 0xf; //~ Sustain level
SpuSetVoiceAttr(&voiceAttributes); // set attributes
}
u_long setSPUtransfer(VAGsound * sound){
// Return spu_address
u_long transferred, spu_address;
u_int pitch;
const VAGhdr * VAGheader = (VAGhdr *) sound->VAGfile;
pitch = (SWAP_ENDIAN32(VAGheader->samplingFrequency) << 12) / 44100L;
spu_address = SpuMalloc(SWAP_ENDIAN32(VAGheader->dataSize)); // Allocate an area of dataSize bytes in the sound buffer.
SpuSetTransferStartAddr(spu_address); // Sets a starting address in the sound buffer
transferred = sendVAGtoSPU(SWAP_ENDIAN32(VAGheader->dataSize), sound->VAGfile);
setVoiceAttr(pitch, sound->spu_channel, spu_address);
// Return 1 if ok, size transferred else.
//~ if (transferred == SWAP_ENDIAN32(VAGheader->dataSize)){
//~ return 1;
//~ }
//~ return transferred;
return spu_address;
}
void playSFX(VAGsound * sound){
// Set voice volume to max
voiceAttributes.mask= ( SPU_VOICE_VOLL | SPU_VOICE_VOLR );
voiceAttributes.voice = sound->spu_channel;
voiceAttributes.volume.left = 0x1000;
voiceAttributes.volume.right = 0x1000;
SpuSetVoiceAttr(&voiceAttributes);
// Play voice
SpuSetKey(SpuOn, sound->spu_channel);
}
int main(void)
{
// Store input values
int pad, oldpad;
u_long spu_address;
// Init all the things !
initGraph();
// Init Spu
SpuInit();
// Init sound settings
initSnd();
// Init Pad
PadInit(0);
// Transfer all VAGs to SPU RAM
// Beware that the SPU only has 512KB of RAM so a 'soundbank' should stay within that limit.
// If more is needed, a second soundbank could be loaded on demand.
for (u_short vag = 0; vag < VAG_NBR; vag++ ){
soundBank[vag].spu_address = setSPUtransfer(&soundBank[vag]);
}
while (1)
{
// Pad
// Use up, down, left, right, triangle, cross, circle, square to play various samples
// Read pad values
pad = PadRead(0);
if (pad & PADLleft && !(oldpad & PADLleft)){
playSFX(&soundBank[0]);
oldpad = pad;
}
if (pad & PADLright && !(oldpad & PADLright)){
playSFX(&soundBank[1]);
oldpad = pad;
}
if (pad & PADLup && !(oldpad & PADLup)){
playSFX(&soundBank[2]);
oldpad = pad;
}
if (pad & PADLdown && !(oldpad & PADLdown)){
playSFX(&soundBank[3]);
oldpad = pad;
}
if (pad & PADRleft && !(oldpad & PADRleft)){
playSFX(&soundBank[4]);
oldpad = pad;
}
if (pad & PADRright && !(oldpad & PADRright)){
playSFX(&soundBank[5]);
oldpad = pad;
}
if (pad & PADRup && !(oldpad & PADRup)){
playSFX(&soundBank[6]);
oldpad = pad;
}
if (pad & PADRdown && !(oldpad & PADRdown)){
playSFX(&soundBank[7]);
oldpad = pad;
}
// Reset oldpad
if (!(pad & PADRdown) ||
!(pad & PADRup) ||
!(pad & PADRleft) ||
!(pad & PADRright) ||
!(pad & PADLdown) ||
!(pad & PADLup) ||
!(pad & PADLleft) ||
!(pad & PADLright)
){ oldpad = pad; }
FntPrint("Hello multi vag ! %d\n", VSync(-1));
FntPrint("Use the pad to play various samples.\n");
FntFlush(-1);
display();
}
return 0;
}
================================================
FILE: hello_multi_xa/Makefile
================================================
.PHONY: all cleansub
all:
mkpsxiso -y ./isoconfig.xml
cleansub:
$(MAKE) clean
rm -f hello_multi_xa.cue hello_multi_xa.bin
TARGET = hello_multi_xa
SRCS = hello_multi_xa.c \
include ../common.mk
================================================
FILE: hello_multi_xa/README.md
================================================
## Multi XA samples playback
Use an interleaved XA file to store multiple sound effects and use pre-calculated offset to play them back.
Use up, down, left, right, triangle, cross, circle, square to play various samples.
If this seems complicated, first study the simple XA playback example : https://github.com/ABelliqueux/nolibgs_hello_worlds/tree/main/hello_xa
You need [mkpsxiso](https://github.com/Lameguy64/mkpsxiso) in your $PATH to generate a PSX disk image.
You also need [ffmpeg](https://ffmpeg.org/), [`psxavenc` and `xainterleave`](https://github.com/ABelliqueux/candyk-psx/tree/master/toolsrc/).
### Compile
This will compile and build an iso image :
```bash
make
```
### Clean directory
```bash
make cleansub
```
## Producing an interleaved XA sound bank
### Sound to WAV conversion with ffmpeg
```bash
ffmpeg -i input.mp3 -acodec pcm_s16le -ac 2 -ar 44100 output.wav
```
### WAV to XA conversion
See further down how you should adapt the `-F` and `-C` parameters.
```bash
psxavenc -f 37800 -t xa -b 4 -c 2 -F 1 -C 0 input.wav output.xa
```
### XA to multi XA track
You can use a concatenation tool to build your tracks like so :
```bash
cat track1.xa track2.xa track3.xa > channelX.xa
```
On windows, use :
```
copy track1.xa+track2.xa+track3.xa channelX.xa
```
### XA to interleaved XA
Use `xainterleave` as instructed here : https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/XA#interleaving-xa-files
## Multi XA specifics
## Silence file
You should use a silent XA file to intersperse between your samples, so that you have a margin of error to avoid pops and noises.I.e:
```bash
cat track1.xa silence.xa track3.xa silence.xa track4.xa > channelX.xa
```
### XA file size and vertical density
An interleaved XA (4 or 8 channels) file will have the size of its largest channel multiplied by the number of channels.
Therefore, you should try to obtain a vertical density on all channels for your sample, i.e :
That :
```
channel 0 sample1.xa
channel 1 sample2.xa
channel 2 sample3.xa
channel 3 sample4.xa
```
instead of :
```
channel 0 sample1.xa sample2.xa sample2.xa sample4.xa
channel 1
channel 2
channel 3
```
You should use your longest sample as a base duration, and create the other channels to obtain a similar duration, i.e :
```
channel 0 <------------- sample1.xa ---------------> <-- silence.xa --> <-sample2.xa ->
channel 1 <-- sample3.xa --> <-- silence.xa --> < -------- sample4.xa ----------->
```
### Calculating sample's start and end offsets
Using the size of your XA sample file, you can find it's size in sector like so :
`(size/2336)-1 * 8`
For example, file with size `84096B` is `(((84096/2336) == 36) - 1 == 35) * 8 == 280`.
First sample's start will then be `0`, and end `280`.
The following sample's start position will have an offset of `8`, hence `288`, etc.
#### Example
These are the calculations for the files on 4 channels
```
// channel 1
// name size start end
5_come.xa 18688 0 56
5_sile_h.xa 23360 64 136
5_erro.xa 44384 144 288
// channel 2
6_cuek.xa 32704 0 104
6_sile_h.xa 23360 112 184
6_hehe.xa 56064 196 380
6_sile_h.xa 23360 388 460
6_wron.xa 53728 468 644
// channel 3
7_m4a1.xa 84096 0 280
7_sile_h.xa 23360 288 360
7_punch.xa 16352 368 416
// channel 4
8_yooo.xa 114464 0 384
```
and the corresponding structure in code :
```c
XAbank soundBank = {
8, // index
0, // file offset
{
// channel 5
// id size file channel start end cursor
{ 0, 18688, 0, 5, 0, 56, -1 },
// Ommit the silence.xa file
{ 1, 44384, 0, 5 , 144, 288, -1 },
// channel 6
{ 2, 32704, 0, 6 , 0, 104, -1 },
// Ommit the silence.xa file
{ 3, 56064, 0, 6 , 196, 380, -1 },
// Ommit the silence.xa file
{ 4, 53728, 0, 6 , 468, 644, -1 },
// channel 7
{ 5, 84096, 0, 7 , 0, 260, -1 },
// Ommit the silence.xa file
{ 6, 16352, 0, 7 , 368, 440, -1 },
// channel 8
{ 7, 114464, 0, 8 , 0, 384, -1 }
}
};
```
## Tools, sources and documentation
See the wiki for general informations, tools and sources about the XA format : https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/XA
## Sound credits
All the sound effects come from https://www.myinstants.com and are :
```
comedy_pop_finger_in_mouth_001(1).mp3
cuek.swf.mp3
erro.mp3
m4a1_single-kibblesbob-8540445.mp3
punch.mp3
wrong-answer-sound-effect.mp3
yooooooooooooooooooooooooo_4.mp3
hehehehhehehehhehehheheehehe.mp3
```
================================================
FILE: hello_multi_xa/hello_multi_xa.c
================================================
// XA multi sample playback example
//
// Use an interleaved XA file to store multiple sound effects and use pre-calculated offset to play them back.
// Use up, down, left, right, triangle, cross, circle, square to play various samples.
// If this seems complicated, first study the simple XA playback example : https://github.com/ABelliqueux/nolibgs_hello_worlds/tree/main/hello_xa
//
// base on `psyq/addons/scee/CD/XAPLAYER`
// Refs : http://psx.arthus.net/code/XA/XATUT.pdf
// http://psx.arthus.net/code/XA/xatut.zip
// http://psx.arthus.net/code/XA/XA%20ADPCM%20documentation.txt
// Schnappy 2021
#include
#include
#include
#include
#include
// CD library
#include
// SPU library
#include
#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL
#define SCREENXRES 320 // Screen width
#define SCREENYRES 240 + (VMODE << 4) // Screen height : If VMODE is 0 = 240, if VMODE is 1 = 256
#define CENTERX SCREENXRES/2 // Center of screen on x
#define CENTERY SCREENYRES/2 // Center of screen on y
#define MARGINX 0 // margins for text display
#define MARGINY 32
#define FONTSIZE 8 * 16 // Text Field Height
DISPENV disp[2]; // Double buffered DISPENV and DRAWENV
DRAWENV draw[2];
short db = 0; // index of which buffer is used, values 0, 1
// SPU attributes
SpuCommonAttr spuSettings;
#define CD_SECTOR_SIZE 2048
// XA
// Sector offset for XA data 4: simple speed, 8: double speed
#define XA_CHANNELS 8
#define XA_CDSPEED XA_CHANNELS >> 2
// Number of XA samples ( != # of XA files )
#define XA_TRACKS 8
typedef struct XAsound {
u_int id;
u_int size;
// We can find size in sector : size / 2336, start value begins at 23, end value is at start + offset ( (size / 2336)-1 * #channels )
// subsequent start value have an 8 bytes offset (n-1.end + 8)
u_char file, channel;
u_int start, end;
int cursor;
} XAsound;
typedef struct XAbank {
u_int index;
int offset;
XAsound samples[];
} XAbank;
XAbank soundBank = {
8,
0,
{
// channel 5
// id size file channel start end cursor
{ 0, 18688, 0, 5, 0, 56, -1 },
{ 1, 44384, 0, 5 , 144, 288, -1 },
// channel 6
{ 2, 32704, 0, 6 , 0, 104, -1 },
{ 3, 56064, 0, 6 , 196, 380, -1 },
{ 4, 53728, 0, 6 , 468, 644, -1 },
// channel 7
{ 5, 84096, 0, 7 , 0, 260, -1 },
{ 6, 16352, 0, 7 , 368, 440, -1 },
// channel 8
{ 7, 114464, 0, 8 , 0, 384, -1 }
}
};
// XA file to load
static char * loadXA = "\\INTER8.XA;1";
// File informations : pos, size, name
CdlFILE XAPos = {0};
// CD filter
CdlFILTER filter;
// File position in m/s/f
CdlLOC loc;
void init(void)
{
ResetGraph(0); // Initialize drawing engine with a complete reset (0)
SetDefDispEnv(&disp[0], 0, 0 , SCREENXRES, SCREENYRES); // Set display area for both &disp[0] and &disp[1]
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES); // &disp[0] is on top of &disp[1]
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES); // Set draw for both &draw[0] and &draw[1]
SetDefDrawEnv(&draw[1], 0, 0 , SCREENXRES, SCREENYRES); // &draw[0] is below &draw[1]
// Set video mode
if (VMODE){ SetVideoMode(MODE_PAL);}
SetDispMask(1); // Display on screen
setRGB0(&draw[0], 50, 50, 50); // set color for first draw area
setRGB0(&draw[1], 50, 50, 50); // set color for second draw area
draw[0].isbg = 1; // set mask for draw areas. 1 means repainting the area with the RGB color each frame
draw[1].isbg = 1;
PutDispEnv(&disp[db]); // set the disp and draw environnments
PutDrawEnv(&draw[db]);
FntLoad(960, 0); // Load font to vram at 960,0(+128)
FntOpen(MARGINX, MARGINY, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 512 ); // FntOpen(x, y, width, height, black_bg, max. nbr. chars
}
void display(void)
{
DrawSync(0); // Wait for all drawing to terminate
VSync(0); // Wait for the next vertical blank
PutDispEnv(&disp[db]); // set alternate disp and draw environnments
PutDrawEnv(&draw[db]);
db = !db; // flip db value (0 or 1)
}
void spuSetup(SpuCommonAttr * spuSettings)
{
// Init Spu
SpuInit();
// Set master & CD volume to max
spuSettings->mask = (SPU_COMMON_MVOLL | SPU_COMMON_MVOLR | SPU_COMMON_CDVOLL | SPU_COMMON_CDVOLR | SPU_COMMON_CDMIX);
spuSettings->mvol.left = 0x6000;
spuSettings->mvol.right = 0x6000;
spuSettings->cd.volume.left = 0x6000;
spuSettings->cd.volume.right = 0x6000;
// Enable CD input ON
spuSettings->cd.mix = SPU_ON;
// Apply settings
SpuSetCommonAttr(spuSettings);
// Set transfer mode
SpuSetTransferMode(SPU_TRANSFER_BY_DMA);
}
void XAsetup()
{
u_char param[4];
// ORing the parameters we need to set ; drive speed, ADPCM play, Subheader filter, sector size
// If using CdlModeSpeed(Double speed), you need to load an XA file that has 8 channels.
// In single speed, a 4 channels XA is to be used.
param[0] = CdlModeSpeed|CdlModeRT|CdlModeSF|CdlModeSize1;
// Issue primitive command to CD-ROM system (Blocking-type)
// Set the parameters above
CdControlB(CdlSetmode, param, 0);
// Pause at current pos
CdControlF(CdlPause,0);
}
int main(void)
{
// Init display
init();
// Set SPU
spuSetup(&spuSettings);
// Init CD system
CdInit();
// Pad
PadInit(0);
// Load XA file from cd
// Find XA file pos
CdSearchFile( &XAPos, loadXA);
// Set cd head to start of file
soundBank.offset = CdPosToInt(&XAPos.pos);
// Set cd XA playback parameters and pause cd
XAsetup();
// Pad variables to avoid multifire
int pad, oldpad;
// Keep track of XA Sample currently playing
int sample = -1;
while (1) // infinite loop
{
// if sample is set
if (sample != -1 ){
// Begin XA file playback...
// if sample's cursor is 0
if (soundBank.samples[sample].cursor == 0){
// Convert sector number to CD position in min/second/frame and set CdlLOC accordingly.
CdIntToPos( soundBank.samples[sample].start + soundBank.offset , &loc);
// Send CDROM read command
CdControlF(CdlReadS, (u_char *)&loc);
// Set playing flag
}
// if sample's cursor is close to sample's end position, stop playback
if ((soundBank.samples[sample].cursor += XA_CDSPEED) >= soundBank.samples[sample].end - soundBank.samples[sample].start ){
CdControlF(CdlStop,0);
soundBank.samples[sample].cursor = -1;
sample = -1;
}
}
// Pad
// Use up, down, left, right, triangle, cross, circle, square to play various samples
// Read pad values
pad = PadRead(0);
// Left
if (pad & PADLleft && !(oldpad & PADLleft)){
// Set sample ID for playback
sample = 7;
// Change file/channel in the filter struct
filter.chan = soundBank.samples[sample].channel;
filter.file = soundBank.samples[sample].file;
// Set filter
CdControlF(CdlSetfilter, (u_char *)&filter);
// Reset sample's cursor
soundBank.samples[sample].cursor = 0;
// Store pad value for release
oldpad = pad;
}
if (pad & PADLright && !(oldpad & PADLright)){
sample = 6;
filter.chan = soundBank.samples[sample].channel;
filter.file = soundBank.samples[sample].file;
// Set filter
CdControlF(CdlSetfilter, (u_char *)&filter);
soundBank.samples[sample].cursor = 0;
oldpad = pad;
}
if (pad & PADLup && !(oldpad & PADLup)){
sample = 5;
filter.chan = soundBank.samples[sample].channel;
filter.file = soundBank.samples[sample].file;
// Set filter
CdControlF(CdlSetfilter, (u_char *)&filter);
soundBank.samples[sample].cursor = 0;
oldpad = pad;
}
if (pad & PADLdown && !(oldpad & PADLdown)){
sample = 4;
filter.chan = soundBank.samples[sample].channel;
filter.file = soundBank.samples[sample].file;
// Set filter
CdControlF(CdlSetfilter, (u_char *)&filter);
soundBank.samples[sample].cursor = 0;
oldpad = pad;
}
if (pad & PADRleft && !(oldpad & PADRleft)){
sample = 3;
filter.chan = soundBank.samples[sample].channel;
filter.file = soundBank.samples[sample].file;
// Set filter
CdControlF(CdlSetfilter, (u_char *)&filter);
soundBank.samples[sample].cursor = 0;
oldpad = pad;
}
if (pad & PADRright && !(oldpad & PADRright)){
sample = 2;
filter.chan = soundBank.samples[sample].channel;
filter.file = soundBank.samples[sample].file;
// Set filter
CdControlF(CdlSetfilter, (u_char *)&filter);
soundBank.samples[sample].cursor = 0;
oldpad = pad;
}
if (pad & PADRup && !(oldpad & PADRup)){
sample = 1;
filter.chan = soundBank.samples[sample].channel;
filter.file = soundBank.samples[sample].file;
// Set filter
CdControlF(CdlSetfilter, (u_char *)&filter);
soundBank.samples[sample].cursor = 0;
oldpad = pad;
}
if (pad & PADRdown && !(oldpad & PADRdown)){
sample = 0;
filter.chan = soundBank.samples[sample].channel;
filter.file = soundBank.samples[sample].file;
// Set filter
CdControlF(CdlSetfilter, (u_char *)&filter);
soundBank.samples[sample].cursor = 0;
oldpad = pad;
}
// Reset oldpad
if (!(pad & PADRdown) ||
!(pad & PADRup) ||
!(pad & PADRleft) ||
!(pad & PADRright) ||
!(pad & PADLdown) ||
!(pad & PADLup) ||
!(pad & PADLleft) ||
!(pad & PADLright)
){oldpad = pad;}
FntPrint("Hello multi XA ! %d\n", VSync(-1));
FntPrint("Use the pad to play various samples.\n");
for (int i=0;i");
}
FntPrint("%d: %d %d\n", i, soundBank.samples[i].start, soundBank.samples[i].end);
}
FntPrint("Cursor: %d\n", soundBank.samples[sample].cursor );
FntPrint("File offset: %d\n", soundBank.offset );
FntFlush(-1); // Draw print stream
display(); // Execute display()
}
return 0;
}
================================================
FILE: hello_multi_xa/isoconfig.xml
================================================
================================================
FILE: hello_multi_xa/system.cnf
================================================
BOOT=cdrom:\SCES_313.37;1
TCB=4
EVENT=10
STACK=801FFFF0
================================================
FILE: hello_multi_xa/xa/interleave8.txt
================================================
1 xa channel5.xa 0 5
1 xa channel6.xa 0 6
1 xa channel7.xa 0 7
1 xa channel8.xa 0 8
1 null
1 null
1 null
1 null
================================================
FILE: hello_ovl_exec/Makefile
================================================
.PHONY: all cleansub
all:
$(MAKE) -C hello_poly clean all
$(MAKE) -C hello_ovl_world clean all
$(MAKE) -C hello_tile clean all
mkpsxiso -y ./isoconfig.xml
cleansub:
$(MAKE) -C hello_poly clean
$(MAKE) -C hello_tile clean
$(MAKE) -C hello_ovl_world clean
$(MAKE) clean
rm -f hello_ovl_exec.cue hello_ovl_exec.bin
TARGET = hello_ovl_exec
OVERLAYSCRIPT ?= overlay.ld
OVERLAYSECTION ?= .ovly0 .ovly1 .ovly2
SRCS = hello_ovl_exec.c \
hello_ovl_world/hello_ovl_world.c \
hello_tile/hello_ovl_tile.c \
hello_poly/hello_ovl_poly.c \
include ../common.mk
================================================
FILE: hello_ovl_exec/README.md
================================================
This example shows how to load overlays from the CD and execute them.
Many thanks to @nicolasnobe for his help on getting this example working.
See the wiki for more details on overlays in the context of PSX development :
[https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/OVL](https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/OVL)
================================================
FILE: hello_ovl_exec/common.h
================================================
#pragma once
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "../thirdparty/nugget/common/syscalls/syscalls.h"
#define printf ramsyscall_printf
#define setRGB(p, r0, g0, b0) \
(p)->r = r0, (p)->g = g0, (p)->b = b0
#define VMODE 0
//~ #define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL
#define SCREENXRES 320 // Screen width
#define SCREENYRES (240 + (VMODE << 4)) // Screen height : If VMODE is 0 = 240, if VMODE is 1 = 256
#define CENTERX SCREENXRES/2 // Center of screen on x
#define CENTERY SCREENYRES/2 // Center of screen on y
#define MARGINX 0 // margins for text display
#define MARGINY 32
#define FONTSIZE 18 * 7 // Text Field Height
#define OTLEN 8 // Ordering Table Length
extern DRAWENV draw[2];
extern char primbuff[2][32768];
extern char *nextpri;
extern u_long ot[2][OTLEN];
extern uint8_t db;
extern uint8_t useOT;
extern CVECTOR BGcolor;
enum OverlayNumber {
MOTHERSHIP = -1,
OVERLAY_HELLO = 0,
OVERLAY_TILE = 1,
OVERLAY_POLY = 2,
};
extern enum OverlayNumber next_overlay;
extern void init(void);
extern void display(void);
================================================
FILE: hello_ovl_exec/hello_ovl_exec.c
================================================
// Load overlay files from CD and execute them
// Schnappy 10-2021
// With the help of Nicolas Noble and impiaa
// https://discord.com/channels/642647820683444236/663664210525290507/894624082367229952
#include "common.h"
DISPENV disp[2]; // Double buffered DISPENV and DRAWENV
DRAWENV draw[2];
char primbuff[2][32768]; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes
u_long ot[2][OTLEN];
char *nextpri = primbuff[0];
uint8_t db = 0;
CVECTOR BGcolor, MScolor = { 224, 108, 76 };
extern u_long load_all_overlays_here;
typedef struct Overlay {
char filename[0x7c];
int (*main)();
char commandline[0x180];
CVECTOR BGcolor;
} Overlay;
int ovl_main_hello();
int ovl_main_tile();
int ovl_main_poly();
Overlay g_chainload[] = {
{"\\HELLO.OVL;1", ovl_main_hello, "", { 225, 220, 40 }},
{"\\TILE.OVL;1", ovl_main_tile, "", { 150, 30 , 40 }},
{"\\POLY.OVL;1", ovl_main_poly, "", { 60 , 127, 0 }},
};
enum OverlayNumber next_overlay = MOTHERSHIP;
enum OverlayNumber prev_overlay = MOTHERSHIP;
int main(void);
int loadOverlayAndStart(Overlay * overlay);
void EmptyOTag(u_long ot[2][OTLEN]);
void EmptyPrimBuf(char primbuff[2][32768], char ** nextpri);
void preInitOvl(Overlay * overlay);
void postInitOvl(Overlay * overlay);
void clearVRAM(void);
void (*dispp) (void);
uint8_t useOT = 0;
int CDreadOK = 0;
void init(void)
{
ResetCallback();
ResetGraph(0); // Initialize drawing engine with a complete reset (0)
// Initialize and setup the GTE
InitGeom();
SetGeomOffset(CENTERX,CENTERY);
SetGeomScreen(CENTERX);
SetDefDispEnv(&disp[0], 0, 0 , SCREENXRES, SCREENYRES); // Set display area for both &disp[0] and &disp[1]
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES); // &disp[0] is on top of &disp[1]
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES); // Set draw for both &draw[0] and &draw[1]
SetDefDrawEnv(&draw[1], 0, 0 , SCREENXRES, SCREENYRES); // &draw[0] is below &draw[1]
// Set video mode
if (VMODE){ SetVideoMode(MODE_PAL);}
SetDispMask(1); // Display on screen
setRGB0(&draw[0], BGcolor.r, BGcolor.g, BGcolor.b); // set color for first draw area
setRGB0(&draw[1], BGcolor.r, BGcolor.g, BGcolor.b); // set color for second draw area
draw[0].isbg = 1; // set mask for draw areas. 1 means repainting the area with the RGB color each frame
draw[1].isbg = 1;
PutDispEnv(&disp[db]); // set the disp and draw environnments
PutDrawEnv(&draw[db]);
FntLoad(960, 0); // Load font to vram at 960,0(+128)
FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); // FntOpen(x, y, width, height, black_bg, max. nbr. chars
}
void display(void)
{
DrawSync(0); // Wait for all drawing to terminate
VSync(0); // Wait for the next vertical blank
PutDispEnv(&disp[db]); // set alternate disp and draw environnments
PutDrawEnv(&draw[db]);
if (useOT)
{
DrawOTag(&ot[db][OTLEN - 1]);
}
db = !db; // flip db value (0 or 1)
nextpri = primbuff[db];
}
void clearVRAM(void)
{
RECT vram = {0,0,1024,512};
ClearImage(&vram,0,0,0);
}
void preInitOvl(Overlay * overlay)
{
//~ clearVRAM();
ResetCallback();
ResetGraph(3);
EmptyPrimBuf(primbuff, &nextpri);
EmptyOTag(&ot[db]);
setRGB(&BGcolor, overlay->BGcolor.r, overlay->BGcolor.g, overlay->BGcolor.b );
}
void postInitOvl(Overlay * overlay)
{
//~ clearVRAM();
//~ ResetGraph(3);
EmptyPrimBuf(primbuff, &nextpri);
EmptyOTag(&ot[db]);
setRGB(&BGcolor, MScolor.r, MScolor.g, MScolor.b);
setRGB0(&draw[0], BGcolor.r, BGcolor.g, BGcolor.b); // set color for first draw area
setRGB0(&draw[1], BGcolor.r, BGcolor.g, BGcolor.b);
FntLoad(960, 0); // Load font to vram at 960,0(+128)
FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); // FntOpen(x, y, width, height, black_bg, max. nbr. chars
}
int loadOverlayAndStart(Overlay * overlay)
{
int CDreadResult = 0;
int next_ovl = -1;
// Load overlay file
CDreadResult = CdReadFile(overlay->filename, &load_all_overlays_here, 0);
CdReadSync(0, 0);
printf( "CD read: %d", CDreadResult);
// If file loaded sucessfully
if (CDreadResult)
{
// Pre OVL
preInitOvl(overlay);
// Exec OVL
next_ovl = overlay->main();
// Post OVL
postInitOvl(overlay);
}
return next_ovl;
}
void EmptyPrimBuf(char primbuff[2][32768], char ** nextpri)
{
for(uint16_t p; p < 32768; p++)
{
primbuff[0][p] = 0;
primbuff[1][p] = 0;
}
*nextpri = primbuff[0];
}
void EmptyOTag(u_long ot[2][OTLEN])
{
for (uint16_t p; p < OTLEN; p++)
{
ot[0][p] = 0;
ot[1][p] = 0;
}
}
int main(void)
{
int t = 0;
setRGB(&BGcolor, MScolor.r, MScolor.g, MScolor.b);
// init() display
init();
// Init CD system
CdInit();
prev_overlay = next_overlay;
while(1){
if (t == 100){
next_overlay = OVERLAY_HELLO;
}
if (next_overlay != -1 && next_overlay != prev_overlay){
prev_overlay = next_overlay;
next_overlay = loadOverlayAndStart(&g_chainload[next_overlay]);
t = 0;
}
t++;
FntPrint("Mothership: %d\nOvl: %d %d", t, prev_overlay, next_overlay);
FntFlush(-1);
display();
}
//
return 0;
}
================================================
FILE: hello_ovl_exec/hello_ovl_world/Makefile
================================================
TARGET = hello_world
SRCS = hello_world.c \
CPPFLAGS+=-DSTANDALONE
include ../../common.mk
================================================
FILE: hello_ovl_exec/hello_ovl_world/hello_ovl_world.c
================================================
#include "../common.h"
int ovl_main_hello(void)
{
init();
int i = 0;
while(1)
{
i++;
#ifndef STANDALONE
if (i==100){
next_overlay = OVERLAY_TILE;
break;
}
FntPrint("Hello world ! %d\n", i);
#else
FntPrint("Hello standalone ! %d\n", i);
#endif
FntFlush(-1);
display();
}
return next_overlay;
};
================================================
FILE: hello_ovl_exec/hello_ovl_world/hello_world.c
================================================
#include "../common.h"
DISPENV disp[2]; // Double buffered DISPENV and DRAWENV
DRAWENV draw[2];
u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes
char primbuff[2][32768];// double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes
char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0]
uint8_t db = 0; // index of which buffer is used, values 0, 1
CVECTOR BGcolor = { 0, 255, 100 };
//~ int next_overlay;
void init(void)
{
ResetGraph(0);
SetDefDispEnv(&disp[0], 0, 0, SCREENXRES, SCREENYRES);
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[1], 0, 0, SCREENXRES, SCREENYRES);
if (VMODE)
{
SetVideoMode(MODE_PAL);
disp[0].screen.y += 8;
disp[1].screen.y += 8;
}
SetDispMask(1); // Display on screen
setRGB0(&draw[0], BGcolor.r, BGcolor.g, BGcolor.b); // set color for first draw area
setRGB0(&draw[1], BGcolor.r, BGcolor.g, BGcolor.b); // set color for second draw area
draw[0].isbg = 1;
draw[1].isbg = 1;
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
FntLoad(960, 0);
FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 );
}
void display(void)
{
DrawSync(0);
VSync(0);
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
DrawOTag(&ot[db][OTLEN - 1]);
db = !db;
nextpri = primbuff[db];
}
#include "hello_ovl_world.c"
int main(void)
{
ovl_main_hello();
};
================================================
FILE: hello_ovl_exec/hello_poly/Makefile
================================================
TARGET = hello_poly
SRCS = hello_poly.c \
CPPFLAGS+=-DSTANDALONE
include ../../common.mk
================================================
FILE: hello_ovl_exec/hello_poly/hello_ovl_poly.c
================================================
#include "../common.h"
int ovl_main_poly(void)
{
#ifndef STANDALONE
useOT = 1;
#endif
init();
uint16_t timer = 0;
uint16_t timeout = 100;
MATRIX IDMATRIX = {0};
POLY_F4 *poly = {0};
SVECTOR RotVector = {0, 0, 0};
VECTOR MovVector = {0, 0, CENTERX, 0};
SVECTOR VertPos[4] = {
{-32, -32, 1 },
{-32, 32, 1 },
{ 32, -32, 1 },
{ 32, 32, 1 }
};
MATRIX PolyMatrix = IDMATRIX;
long polydepth;
long polyflag;
long OTz;
init();
while (1)
{
ClearOTagR(ot[db], OTLEN);
poly = (POLY_F4 *)nextpri; // Set poly to point to the address of the next primitiv in the buffer
// Set transform matrices for this polygon
RotMatrix(&RotVector, &PolyMatrix); // Apply rotation matrix
TransMatrix(&PolyMatrix, &MovVector);
SetRotMatrix(&PolyMatrix); // Set default rotation matrix
SetTransMatrix(&PolyMatrix); // Set default transformation matrix
setPolyF4(poly); // Initialize poly as a POLY_F4
setRGB0(poly, 255, 255, 0); // Set poly color
// RotTransPers
OTz = RotTransPers4(
&VertPos[0], &VertPos[1], &VertPos[2], &VertPos[3],
(long*)&poly->x0, (long*)&poly->x1, (long*)&poly->x2, (long*)&poly->x3,
&polydepth,
&polyflag
); // Perform coordinate and perspective transformation for 4 vertices
RotVector.vy += 4;
RotVector.vz += 4; // Apply rotation on Z-axis. On PSX, the Z-axis is pointing away from the screen.
//~ addPrim(ot[db], poly); // add poly to the Ordering table
addPrim(ot[db][OTLEN-1], poly); // add poly to the Ordering table
nextpri += sizeof(POLY_F4); // increment nextpri address with size of a POLY_F4 struct
timer++;
FntPrint("Hello Poly ! %d", timer);
FntFlush(-1);
#ifndef STANDALONE
if (timer == timeout){
useOT = 0;
next_overlay = MOTHERSHIP;
break;
}
#endif
display();
}
return next_overlay;
};
================================================
FILE: hello_ovl_exec/hello_poly/hello_poly.c
================================================
#include "../common.h"
DISPENV disp[2]; // Double buffered DISPENV and DRAWENV
DRAWENV draw[2];
u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes
char primbuff[2][32768]; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes
char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0]
uint8_t db = 0; // index of which buffer is used, values 0, 1
CVECTOR BGcolor = { 50, 255, 150 };
void init(void)
{
ResetCallback();
ResetGraph(3);
// Initialize and setup the GTE
InitGeom();
SetGeomOffset(CENTERX,CENTERY);
SetGeomScreen(CENTERX);
SetDefDispEnv(&disp[0], 0, 0, SCREENXRES, SCREENYRES);
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[1], 0, 0, SCREENXRES, SCREENYRES);
if (VMODE)
{
SetVideoMode(MODE_PAL);
disp[0].screen.y += 8;
disp[1].screen.y += 8;
}
setRGB0(&draw[0], BGcolor.r, BGcolor.g, BGcolor.b); // set color for first draw area
setRGB0(&draw[1], BGcolor.r, BGcolor.g, BGcolor.b); // set color for second draw area
SetDispMask(1);
draw[0].isbg = 1;
draw[1].isbg = 1;
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
FntLoad(960, 0);
FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 );
}
void display(void)
{
DrawSync(0);
VSync(0);
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
DrawOTag(&ot[db][OTLEN - 1]);
db = !db;
nextpri = primbuff[db];
}
#include "hello_ovl_poly.c"
int main(void)
{
ovl_main_poly();
}
================================================
FILE: hello_ovl_exec/hello_tile/Makefile
================================================
TARGET = hello_tile
SRCS = hello_tile.c \
CPPFLAGS+=-DSTANDALONE
include ../../common.mk
================================================
FILE: hello_ovl_exec/hello_tile/hello_ovl_tile.c
================================================
#include "../common.h"
int ovl_main_tile(void)
{
#ifndef STANDALONE
useOT = 1;
#endif
uint16_t timer = 0;
uint16_t timeout = 100;
TILE * blue_tile = 0;
TILE * pink_tile = 0;
// This one is added at a different OT index
TILE * yellow_tile = 0;
init();
while(1)
{
// Initialize the reversed ordering table. This means the elements at index OTLEN - 1 is drawn first.
ClearOTagR(ot[db], OTLEN);
// yellow_tile
yellow_tile = (TILE * ) nextpri; // yellow_tile is a pointer to primbuf content at adress nextpri, that's cast (type converted) to a TILE struc.
setTile(yellow_tile); // initialize the TILE structure ( fill the length and tag(?) value )
setXY0(yellow_tile, CENTERX - 32 , CENTERY - 48); // Set X,Y
setWH(yellow_tile, 128, 40); // Set Width, Height
setRGB0(yellow_tile, 255, 255, 0); // Set color
addPrim(ot[db][OTLEN - 1], yellow_tile); // Add primitive to ordering table
nextpri += sizeof(TILE);
// blue_tile
blue_tile = (TILE * ) nextpri; // blue_tile is a pointer to primbuf content at adress nextpri, that's cast (type converted) to a blue_tile struc.
setTile(blue_tile); // initialize the blue_tile structure ( fill the length and tag(?) value )
setXY0(blue_tile, CENTERX - 16, CENTERY - 32); // Set X,Y
setWH(blue_tile, 32, 64); // Set Width, Height
setRGB0(blue_tile, 60, 180, 255); // Set color
addPrim(ot[db][OTLEN - 2], blue_tile); // Add primitive to ordering table
nextpri += sizeof(TILE); // Increment the adress nextpri points to by the size of TILE struct
// pink_tile
pink_tile = (TILE * ) nextpri; // pink_tile is a pointer to primbuf content at adress nextpri, that's cast (type converted) to a TILE struc.
setTile(pink_tile); // initialize the TILE structure ( fill the length and tag(?) value )
setXY0(pink_tile, CENTERX, CENTERY - 64); // Set X,Y
setWH(pink_tile, 64, 64); // Set Width, Height
setRGB0(pink_tile, 255, 32, 255); // Set color
addPrim(ot[db][OTLEN - 2], pink_tile); // Add primitive to ordering table
nextpri += sizeof(TILE);
timer ++;
FntPrint("Hello tile ! %d\n", timer);
FntFlush(-1);
#ifndef STANDALONE
if (timer == timeout){
useOT = 0;
next_overlay = OVERLAY_POLY;
break;
}
#endif
display();
}
return next_overlay;
};
================================================
FILE: hello_ovl_exec/hello_tile/hello_tile.c
================================================
#include "../common.h"
DISPENV disp[2]; // Double buffered DISPENV and DRAWENV
DRAWENV draw[2];
u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes
char primbuff[2][32768]; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes
char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0]
uint8_t db = 0; // index of which buffer is used, values 0, 1
CVECTOR BGcolor = { 50, 255, 150 };
void init(void)
{
ResetGraph(0);
SetDefDispEnv(&disp[0], 0, 0, SCREENXRES, SCREENYRES);
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[1], 0, 0, SCREENXRES, SCREENYRES);
if (VMODE)
{
SetVideoMode(MODE_PAL);
disp[0].screen.y += 8;
disp[1].screen.y += 8;
}
SetDispMask(1); // Display on screen
setRGB0(&draw[0], BGcolor.r, BGcolor.g, BGcolor.b); // set color for first draw area
setRGB0(&draw[1], BGcolor.r, BGcolor.g, BGcolor.b); // set color for second draw area
draw[0].isbg = 1;
draw[1].isbg = 1;
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
FntLoad(960, 0);
FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 );
}
void display(void)
{
DrawSync(0);
VSync(0);
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
// We're using a reverse OT, so we want to display the last item first. See PsyQ's LibRef47.pdf, p.277
DrawOTag(&ot[db][OTLEN - 1]);
// Comment above line, and uncomment the following line to use a regular oredered OT. Comment l.71 and Uncomment l.73 accordingly
db = !db;
nextpri = primbuff[db];
}
#include "hello_ovl_tile.c"
int main(void)
{
ovl_main_tile();
};
================================================
FILE: hello_ovl_exec/isoconfig.xml
================================================
================================================
FILE: hello_ovl_exec/overlay.ld
================================================
__heap_base = MAX(__ovly0_end, __ovly2_end);
SECTIONS {
OVERLAY __bss_end : NOCROSSREFS SUBALIGN(4)
{
.ovlyload
{
load_all_overlays_here = .;
}
.ovly0
{
KEEP(hello_poly/*.o(.text))
__ovly0_ctor = .;
KEEP(hello_poly/*.o(.text.startup._GLOBAL__*))
KEEP(hello_poly/*.o(.text.*))
KEEP(hello_poly/*.o(.rodata*))
KEEP(hello_poly/*.o(.sdata*))
KEEP(hello_poly/*.o(.data*))
KEEP(hello_poly/*.o(.sbss*))
KEEP(hello_poly/*.o(.bss*))
KEEP(hello_poly/*.o(.ctors))
. = ALIGN(4);
__ovly0_end = .;
}
.ovly1
{
KEEP(hello_tile/*.o(.text))
__ovly1_ctor = .;
KEEP(hello_tile/*.o(.text.startup._GLOBAL__*))
KEEP(hello_tile/*.o(.text.*))
KEEP(hello_tile/*.o(.rodata*))
KEEP(hello_tile/*.o(.sdata*))
KEEP(hello_tile/*.o(.data*))
KEEP(hello_tile/*.o(.sbss*))
KEEP(hello_tile/*.o(.bss*))
KEEP(hello_tile/*.o(.ctors))
. = ALIGN(4);
__ovly1_end = .;
}
.ovly2
{
KEEP(hello_ovl_world/*.o(.text))
__ovly2_ctor = .;
KEEP(hello_ovl_world/*.o(.text.startup._GLOBAL__*))
KEEP(hello_ovl_world/*.o(.text.*))
KEEP(hello_ovl_world/*.o(.rodata*))
KEEP(hello_ovl_world/*.o(.sdata*))
KEEP(hello_ovl_world/*.o(.data*))
KEEP(hello_ovl_world/*.o(.sbss*))
KEEP(hello_ovl_world/*.o(.bss*))
KEEP(hello_ovl_world/*.o(.ctors))
. = ALIGN(4);
__ovly2_end = .;
}
}
}
================================================
FILE: hello_ovl_exec/system.cnf
================================================
BOOT=cdrom:\SCES_313.37;1
TCB=4
EVENT=10
STACK=801FFFF0
================================================
FILE: hello_pad/Makefile
================================================
TARGET = hello_pad
SRCS = hello_pad.c \
include ../common.mk
================================================
FILE: hello_pad/hello_pad.c
================================================
// hello_pad example
//
// We're using libetc PadInit() and PadRead() that only supports the 16 buttons pad
// but doesn't need the libpad lib. It's fine for prototyping and simple stuff.
// Schnappy 2021
#include
#include
#include
#include
#include
#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL
#define SCREENXRES 320
#define SCREENYRES 240
#define CENTERX SCREENXRES/2
#define CENTERY SCREENYRES/2
#define MARGINX 32 // margins for text display
#define MARGINY 32
#define FONTSIZE 8 * 2 // Text Field Height
#define OTLEN 8 // Ordering Table Length
DISPENV disp[2]; // Double buffered DISPENV and DRAWENV
DRAWENV draw[2];
u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes
char primbuff[2][32768]; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes
char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0]
short db = 0; // index of which buffer is used, values 0, 1
void init(void)
{
ResetGraph(0);
SetDefDispEnv(&disp[0], 0, 0, SCREENXRES, SCREENYRES);
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[1], 0, 0, SCREENXRES, SCREENYRES);
if (VMODE)
{
SetVideoMode(MODE_PAL);
disp[0].screen.y += 8;
disp[1].screen.y += 8;
}
SetDispMask(1); // Display on screen
setRGB0(&draw[0], 50, 50, 50);
setRGB0(&draw[1], 50, 50, 50);
draw[0].isbg = 1;
draw[1].isbg = 1;
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
FntLoad(960, 0);
FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 );
}
void display(void)
{
DrawSync(0);
VSync(0);
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
DrawOTag(&ot[db][OTLEN - 1]);
db = !db;
nextpri = primbuff[db];
}
int main(void)
{
TILE * PADL; // Tile primitives
TILE * TRIGGERL;
TILE * PADR;
TILE * TRIGGERR;
TILE * START, * SELECT;
int pad = 0;
init();
PadInit(0); // Initialize pad. Mode is always 0
while (1)
{
ClearOTagR(ot[db], OTLEN);
// D-cross
PADL = (TILE *)nextpri;
setTile(PADL);
setRGB0(PADL, 0, 0, 255);
setXY0(PADL, CENTERX - 80, CENTERY);
setWH(PADL, 24, 24);
addPrim(ot[db], PADL);
nextpri += sizeof(TILE);
// L1+L2
TRIGGERL = (TILE *)nextpri;
setTile(TRIGGERL);
setRGB0(TRIGGERL, 255, 0, 0);
setXY0(TRIGGERL, CENTERX - 80, CENTERY - 80);
setWH(TRIGGERL, 24, 24);
addPrim(ot[db], TRIGGERL);
nextpri += sizeof(TILE);
// /\, X, O, []
PADR = (TILE *)nextpri;
setTile(PADR);
setRGB0(PADR, 0, 255, 0);
setXY0(PADR, CENTERX + 50, CENTERY);
setWH(PADR, 24, 24);
addPrim(ot[db], PADR);
nextpri += sizeof(TILE);
// R1+R2
TRIGGERR = (TILE *)nextpri;
setTile(TRIGGERR);
setRGB0(TRIGGERR, 255, 0, 255);
setXY0(TRIGGERR, CENTERX + 50, CENTERY -80);
setWH(TRIGGERR, 24, 24);
addPrim(ot[db], TRIGGERR);
nextpri += sizeof(TILE);
// START + SELECT
START = (TILE *)nextpri;
setTile(START);
setRGB0(START, 240, 240, 240);
setXY0(START, CENTERX - 16, CENTERY - 36);
setWH(START, 24, 24);
addPrim(ot[db], START);
nextpri += sizeof(TILE);
// Pad stuff
pad = PadRead(0); // Read pads input. id is unused, always 0.
// PadRead() returns a 32 bit value, where input from pad 1 is stored in the low 2 bytes and input from pad 2 is stored in the high 2 bytes. (https://matiaslavik.wordpress.com/2015/02/13/diving-into-psx-development/)
// D-pad
if(pad & PADLup) {PADL->y0 = CENTERY - 16;} // 🡩 // To access pad 2, use ( pad >> 16 & PADLup)...
if(pad & PADLdown) {PADL->y0 = CENTERY + 16;} // 🡫
if(pad & PADLright){PADL->x0 = CENTERX - 64;} // 🡪
if(pad & PADLleft) {PADL->x0 = CENTERX - 96;} // 🡨
// Buttons
if(pad & PADRup) {PADR->y0 = CENTERY - 16;} // △
if(pad & PADRdown) {PADR->y0 = CENTERY + 16;} // ╳
if(pad & PADRright){PADR->x0 = CENTERX + 66;} // ⭘
if(pad & PADRleft) {PADR->x0 = CENTERX + 34;} // ⬜
// Shoulder buttons
if(pad & PADL1){TRIGGERL->y0 = CENTERY - 64;} // L1
if(pad & PADL2){TRIGGERL->y0 = CENTERY - 96;} // L2
if(pad & PADR1){TRIGGERR->y0 = CENTERY - 64;} // R1
if(pad & PADR2){TRIGGERR->y0 = CENTERY - 96;} // R2
// Start & Select
if(pad & PADstart){START->w = 32; START->h = 32;START->x0 -= 4;START->y0 -= 4;} // START
if(pad & PADselect){START->r0 = 0;} // SELECT
FntPrint("Hello Pad!");
FntFlush(-1);
display();
}
return 0;
}
================================================
FILE: hello_poly/Makefile
================================================
TARGET = hello_poly
SRCS = hello_poly.c \
include ../common.mk
================================================
FILE: hello_poly/hello_poly.c
================================================
// Draw a colored poly primitive
//
// With help from Nicolas Noble, Jaby smoll Seamonstah, Lameguy64
//
// From ../psyq/addons/graphics/MESH/RMESH/TUTO0.C :
// Schnappy 2021
#include
#include
#include
#include
#include
#include
#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL
#define SCREENXRES 320 // Screen width
#define SCREENYRES 240 // Screen height
#define CENTERX SCREENXRES/2 // Center of screen on x
#define CENTERY SCREENYRES/2 // Center of screen on y
#define MARGINX 0 // margins for text display
#define MARGINY 32
#define FONTSIZE 8 * 7 // Text Field Height
#define OTLEN 8 // Ordering Table Length
DISPENV disp[2]; // Double buffered DISPENV and DRAWENV
DRAWENV draw[2];
u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes
char primbuff[2][32768]; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes
char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0]
short db = 0; // index of which buffer is used, values 0, 1
void init(void)
{
ResetGraph(0);
// Initialize and setup the GTE
InitGeom();
SetGeomOffset(CENTERX,CENTERY);
SetGeomScreen(CENTERX);
SetDefDispEnv(&disp[0], 0, 0, SCREENXRES, SCREENYRES);
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[1], 0, 0, SCREENXRES, SCREENYRES);
if (VMODE) {
SetVideoMode(MODE_PAL);
disp[0].disp.y = disp[1].disp.y = 8;
}
SetDispMask(1);
setRGB0(&draw[0], 50, 50, 50);
setRGB0(&draw[1], 50, 50, 50);
draw[0].isbg = 1;
draw[1].isbg = 1;
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
FntLoad(960, 0);
FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 );
}
void display(void)
{
DrawSync(0);
VSync(0);
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
DrawOTag(&ot[db][OTLEN - 1]);
db = !db;
nextpri = primbuff[db];
}
int main(void)
{
POLY_F4 *poly = {0}; // pointer to a POLY_F4
SVECTOR RotVector = {0, 0, 0}; // Initialize rotation vector {x, y, z}
VECTOR MovVector = {0, 0, CENTERX, 0}; // Initialize translation vector {x, y, z}
VECTOR ScaleVector ={ONE, ONE, ONE}; // ONE is define as 4096 in libgte.h
SVECTOR VertPos[4] = { // Set initial vertices position relative to 0,0 - see here : https://psx.arthus.net/docs/poly_f4.jpg
{-32, -32, 1 }, // Vert 1
{-32, 32, 1 }, // Vert 2
{ 32, -32, 1 }, // Vert 3
{ 32, 32, 1 } // Vert 4
};
MATRIX PolyMatrix = {0};
long polydepth;
long polyflag;
long OTz;
init();
while (1)
{
ClearOTagR(ot[db], OTLEN);
poly = (POLY_F4 *)nextpri; // Set poly to point to the address of the next primitiv in the buffer
// Set transform matrices for this polygon
RotMatrix(&RotVector, &PolyMatrix); // Apply rotation matrix
TransMatrix(&PolyMatrix, &MovVector);
ScaleMatrix(&PolyMatrix, &ScaleVector); // Apply translation matrix
SetRotMatrix(&PolyMatrix); // Set default rotation matrix
SetTransMatrix(&PolyMatrix); // Set default transformation matrix
setPolyF4(poly); // Initialize poly as a POLY_F4
setRGB0(poly, 255, 0, 255); // Set poly color
// RotTransPers using 4 calls
//~ OTz = RotTransPers(&VertPos[0], (long*)&poly->x0, &polydepth, &polyflag);
//~ RotTransPers(&VertPos[1], (long*)&poly->x1, &polydepth, &polyflag);
//~ RotTransPers(&VertPos[2], (long*)&poly->x2, &polydepth, &polyflag);
//~ RotTransPers(&VertPos[3], (long*)&poly->x3, &polydepth, &polyflag);
// RotTransPers4 equivalent
OTz = RotTransPers4(
&VertPos[0], &VertPos[1], &VertPos[2], &VertPos[3],
(long*)&poly->x0, (long*)&poly->x1, (long*)&poly->x2, (long*)&poly->x3,
&polydepth,
&polyflag
); // Perform coordinate and perspective transformation for 4 vertices
RotVector.vy += 4;
RotVector.vz += 4; // Apply rotation on Z-axis. On PSX, the Z-axis is pointing away from the screen.
addPrim(ot[db], poly); // add poly to the Ordering table
nextpri += sizeof(POLY_F4); // increment nextpri address with size of a POLY_F4 struct
FntPrint("Hello Poly !");
FntFlush(-1);
display();
}
return 0;
}
================================================
FILE: hello_poly_ft/Makefile
================================================
TARGET = hello_poly_ft
SRCS = hello_poly_ft.c \
../TIM/bousai.tim \
include ../common.mk
================================================
FILE: hello_poly_ft/hello_poly_ft.c
================================================
// Draw a textured poly primitive
//
// With help from Nicolas Noble, Jaby smoll Seamonstah, Lameguy64
//
// From ../psyq/addons/graphics/MESH/RMESH/TUTO0.C :
// Schnappy 2021
#include
#include
#include
#include
#include
#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL
#define SCREENXRES 320 // Screen width
#define SCREENYRES 240 // Screen height
#define CENTERX SCREENXRES/2 // Center of screen on x
#define CENTERY SCREENYRES/2 // Center of screen on y
#define MARGINX 32 // margins for text display
#define MARGINY 32
#define FONTSIZE 8 * 5 // Text Field Height
#define OTLEN 8 // Ordering Table Length
DISPENV disp[2]; // Double buffered DISPENV and DRAWENV
DRAWENV draw[2];
u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes
char primbuff[2][32768]; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes
char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0]
short db = 0; // index of which buffer is used, values 0, 1
// 16bpp TIM
extern unsigned long _binary____TIM_bousai_tim_start[];
extern unsigned long _binary____TIM_bousai_tim_end[];
extern unsigned long _binary____TIM_bousai_tim_length;
TIM_IMAGE bousai;
void LoadTexture(u_long * tim, TIM_IMAGE * tparam){ // This part is from Lameguy64's tutorial series : lameguy64.net/svn/pstutorials/chapter1/3-textures.html login/pw: annoyingmous
OpenTIM(tim); // Open the tim binary data, feed it the address of the data in memory
ReadTIM(tparam); // This read the header of the TIM data and sets the corresponding members of the TIM_IMAGE structure
LoadImage(tparam->prect, tparam->paddr); // Transfer the data from memory to VRAM at position prect.x, prect.y
DrawSync(0); // Wait for the drawing to end
if (tparam->mode & 0x8){ // check 4th bit // If 4th bit == 1, TIM has a CLUT
LoadImage(tparam->crect, tparam->caddr); // Load it to VRAM at position crect.x, crect.y
DrawSync(0); // Wait for drawing to end
}
}
void init(void)
{
ResetGraph(0);
// Initialize and setup the GTE
InitGeom();
SetGeomOffset(CENTERX,CENTERY);
SetGeomScreen(CENTERX);
SetDefDispEnv(&disp[0], 0, 0, SCREENXRES, SCREENYRES);
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[1], 0, 0, SCREENXRES, SCREENYRES);
if (VMODE)
{
SetVideoMode(MODE_PAL);
disp[0].screen.y += 8;
disp[1].screen.y += 8;
}
SetDispMask(1); // Display on screen
setRGB0(&draw[0], 128, 128, 128);
setRGB0(&draw[1], 128, 128, 128);
draw[0].isbg = 1;
draw[1].isbg = 1;
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
FntLoad(960, 0);
FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 );
}
void display(void)
{
DrawSync(0);
VSync(0);
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
DrawOTag(&ot[db][OTLEN - 1]);
db = !db;
nextpri = primbuff[db];
}
int main(void)
{
POLY_FT4 *poly = {0}; // pointer to a POLY_G4
SVECTOR RotVector = {0, 0, 0}; // Initialize rotation vector {x, y, z}
VECTOR MovVector = {0, 0, CENTERX/2, 0}; // Initialize translation vector {x, y, z, pad}
VECTOR ScaleVector = {ONE, ONE, ONE}; // ONE is define as 4096 in libgte.h
SVECTOR VertPos[4] = { // Set initial vertices position relative to 0,0 - see here : https://psx.arthus.net/docs/poly_f4.jpg
{-32, -32, 1 }, // Vert 1
{-32, 32, 1 }, // Vert 2
{ 32, -32, 1 }, // Vert 3
{ 32, 32, 1 } // Vert 4
};
MATRIX PolyMatrix = {0};
long polydepth;
long polyflag;
int ping = 0;
init();
LoadTexture(_binary____TIM_bousai_tim_start, &bousai);
while (1)
{
ClearOTagR(ot[db], OTLEN);
poly = (POLY_FT4 *)nextpri; // Set poly to point to the address of the next primitiv in the buffer
// Set transform matrices for this polygon
RotMatrix(&RotVector, &PolyMatrix); // Apply rotation matrix
TransMatrix(&PolyMatrix, &MovVector); // Apply translation matrix
ScaleMatrix(&PolyMatrix, &ScaleVector); // Apply scale matrix
SetRotMatrix(&PolyMatrix); // Set default rotation matrix
SetTransMatrix(&PolyMatrix); // Set default transformation matrix
setPolyFT4(poly); // Initialize poly as a POLY_F4
poly->tpage = getTPage(bousai.mode&0x3, 0, bousai.prect->x, bousai.prect->y); // Get Tpage coordinates from the TIM_IMAGE mode and prect members.
setRGB0(poly, 128, 128, 128); // Set poly color (neutra here)
RotTransPers4(
&VertPos[0], &VertPos[1], &VertPos[2], &VertPos[3],
(long*)&poly->x0, (long*)&poly->x1, (long*)&poly->x2, (long*)&poly->x3,
&polydepth,
&polyflag
); // Perform coordinate and perspective transformation for 4 vertices
setUV4(poly, 0, 0, 0, 144, 144, 0, 144, 144); // Set UV coordinates in order Top Left, Bottom Left, Top Right, Bottom Right
// Let's have some fun on the Z axis
if(!ping){
if (MovVector.vz < CENTERX){ // While Poly position on Z axis is < 160, push it
MovVector.vz += 1; // Push on Z axis
} else {
ping = !ping; // Switch ping value
}
}
if(ping){
if (MovVector.vz > CENTERX/2){ // While Poly position on Z axis is > 80, pull it
MovVector.vz -= 1; // Pull on Z axis
} else {
ping = !ping; // Switch ping value
}
}
addPrim(ot[db], poly); // add poly to the Ordering table
nextpri += sizeof(POLY_FT4); // increment nextpri address with size of a POLY_F4 struct
FntPrint("Hello textured poly !");
FntFlush(-1);
display();
}
return 0;
}
================================================
FILE: hello_poly_fun/Makefile
================================================
TARGET = hello_poly_fun
SRCS = hello_poly_fun.c \
include ../common.mk
================================================
FILE: hello_poly_fun/hello_poly_fun.c
================================================
// Having fun with polygons, matrices and vectors
// Credits : Schnappy
//With great help from Jaby smoll Seamonstah, Nicolas Noble, NDR008, paul, sickle on https://discord.com/invite/Zd82yXvs
// 11/2020
#include
#include
#include
#include
#include
#include
#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL
#define SPIN 16 // Rotation speed increment
#define SCREENXRES 320
#define SCREENYRES 240
#define CENTERX SCREENXRES/2
#define CENTERY SCREENYRES/2
#define MARGINX 10 // margins for text display
#define MARGINY 4
#define FONTSIZE 8 * 7 // Text Field Height
#define OTLEN 16 // Ordering Table Length
DISPENV disp[2]; // Double buffered DISPENV and DRAWENV
DRAWENV draw[2];
u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes
char primbuff[2][32768]; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes
char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0]
short db = 0; // index of which buffer is used, values 0, 1
CVECTOR BgColor[3] = {20, 20, 20};
struct polygon
{
POLY_F4 * poly_f4;
CVECTOR color;
short width;
short height;
SVECTOR RotV_L;
VECTOR TransV_L;
VECTOR ScaleV_L;
SVECTOR PivotV_L;
SVECTOR Verts[4];
MATRIX Matrix;
long depth;
long flag;
short rotSpeed;
int otz;
};
void init(void)
{
ResetGraph(0);
// Initialize and setup the GTE : Not needed ?
InitGeom();
SetGeomOffset(CENTERX,CENTERY);
SetGeomScreen(CENTERX);
PadInit(0);
SetDefDispEnv(&disp[0], 0, 0, SCREENXRES, SCREENYRES);
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[1], 0, 0, SCREENXRES, SCREENYRES);
if (VMODE)
{
SetVideoMode(MODE_PAL);
disp[0].screen.y += 8;
disp[1].screen.y += 8;
}
SetDispMask(1); // Display on screen
setRGB0(&draw[0], BgColor->r, BgColor->g, BgColor->b);
setRGB0(&draw[1], BgColor->r, BgColor->g, BgColor->b);
draw[0].isbg = 1;
draw[1].isbg = 1;
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
FntLoad(960, 0);
FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 );
}
void display(void)
{
DrawSync(0);
VSync(0);
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
DrawOTag(&ot[db][OTLEN - 1]);
db = !db;
nextpri = primbuff[db];
}
void pivotPoint(SVECTOR VertPos[3],short width,short height, SVECTOR pivot){
// Not very efficient I think
VertPos[0].vx = -pivot.vx;
VertPos[0].vy = -pivot.vy;
VertPos[0].vz = 1;
VertPos[1].vx = width - pivot.vx;
VertPos[1].vy = -pivot.vy;
VertPos[1].vz = 1;
VertPos[2].vx = -pivot.vx;
VertPos[2].vy = height-pivot.vy;
VertPos[2].vz = 1;
VertPos[3].vx = width - pivot.vx;
VertPos[3].vy = height - pivot.vy;
VertPos[3].vz = 1;
}
int main(void)
{
u_short BtnTimer = 0; // Timer to limit pad input rate
u_short polyCount = 1; // current polygon index
int otz; // z-index
struct polygon *CurrentPoly; // points to the address of selected polygon
// White cursor : shows which polygon is selected
struct polygon cursorS = {
cursorS.poly_f4,
{255, 255, 255}, // color
30, 30, // width, height
{0,0,0}, // RotV_L
{0,0,0, 0}, // TransV_L
{4096,4096,4096}, // ScaleV_L
{1,1,1}, // PivotV
{ // Verts[4]
{-1, -1, 1},
{ 1, -1, 1},
{-1, 1, 1},
{ 1, 1, 1}
},
{0} // Matrix
};
//Red
struct polygon polyS = {
polyS.poly_f4,
{255, 0, 0}, // color
30, 30, // width, height
{0,0,0}, // RotV_L
{-48, -30, 0, 0}, // TransV_L
{4096,4096,4096}, // ScaleV_L
{15,15,1}, // PivotV
{ // Verts[4]
{0, 0, 0},
{0, 0, 0},
{0, 0, 0},
{0, 0, 0}
},
{0}, // Matrix
0,0, // depth, flag
8, // rotSpeed
1 // z-index
};
//Yellow
struct polygon poly1S = {
poly1S.poly_f4,
{255, 187, 0}, // color
28, 28, // width, height
{0,0,0}, // RotV_L
{-20, 10, 0, 0}, // TransV_L
{4096,4096,4096}, // ScaleV_L
{4,4,1}, // PivotV
{ // Verts[4]
{0, 0, 0},
{0, 0, 0},
{0, 0, 0},
{0, 0, 0}
},
{0}, // Matrix
0,0, // depth, flag
-12, // rotSpeed
2 // z-index
};
//Green
struct polygon poly2S = {
poly2S.poly_f4,
{0, 255, 153}, // color
24, 24, // width, height
{0,0,0}, // RotV_L
{36, -10, 0, 0}, // TransV_L
{4096,4096,4096}, // ScaleV_L
{12,12,1}, // PivotV
{ // Verts[4]
{0, 0, 0},
{0, 0, 0},
{0, 0, 0},
{0, 0, 0}
},
{0}, // Matrix
0,0, // depth, flag
-6, // rotSpeed
3 // z-index
};
//Blue
struct polygon poly3S = {
poly3S.poly_f4,
{112, 254, 254}, // color
26, 26, // width, height
{0,0,0}, // RotV_L
{20, 20, 0, 0}, // TransV_L
{4096,4096,4096}, // ScaleV_L
{13,13,1}, // PivotV
{ // Verts[4]
{0, 0, 0},
{0, 0, 0},
{0, 0, 0},
{0, 0, 0}
},
{0}, // Matrix
0,0, //depth, flag
256, //rotSpeed
4 // z-index
};
/////
CurrentPoly = &polyS;
pivotPoint(polyS.Verts, polyS.width, polyS.height, polyS.PivotV_L);
pivotPoint(poly1S.Verts, poly1S.width, poly1S.height, poly1S.PivotV_L);
pivotPoint(poly2S.Verts, poly2S.width, poly2S.height, poly2S.PivotV_L);
pivotPoint(poly3S.Verts, poly3S.width, poly3S.height, poly3S.PivotV_L);
init();
while (1)
{
ClearOTagR(ot[db], OTLEN);
cursorS.poly_f4 = (POLY_F4 *)nextpri;
RotMatrix(&cursorS.RotV_L , &cursorS.Matrix);
TransMatrix(&cursorS.Matrix, &CurrentPoly->TransV_L);
SetRotMatrix(&cursorS.Matrix);
SetTransMatrix(&cursorS.Matrix);
setPolyF4(cursorS.poly_f4);
setRGB0(cursorS.poly_f4,cursorS.color.r,cursorS.color.g,cursorS.color.b);
//~ setXY4(cursorS, MovVector.vx-1, MovVector.vy-1 ,MovVector.vx + 1, MovVector.vy -1,MovVector.vx-1, MovVector.vy+1,MovVector.vx+1, MovVector.vy+1);
RotTransPers4(
&cursorS.Verts[0], &cursorS.Verts[1], &cursorS.Verts[2], &cursorS.Verts[3],
(long*)&cursorS.poly_f4->x0, (long*)&cursorS.poly_f4->x1, (long*)&cursorS.poly_f4->x2, (long*)&cursorS.poly_f4->x3,
&cursorS.depth,
&cursorS.flag
);
addPrim(ot[db], cursorS.poly_f4);
nextpri += sizeof(POLY_F4);
///// Red
polyS.poly_f4 = (POLY_F4 *)nextpri;
polyS.RotV_L.vz += polyS.rotSpeed;
RotMatrix(&polyS.RotV_L, &polyS.Matrix);
TransMatrix(&polyS.Matrix, &polyS.TransV_L);
ScaleMatrix(&polyS.Matrix, &polyS.ScaleV_L);
SetRotMatrix(&polyS.Matrix);
SetTransMatrix(&polyS.Matrix);
setPolyF4(polyS.poly_f4);
setRGB0(polyS.poly_f4, polyS.color.r,polyS.color.g,polyS.color.b);
RotTransPers4(
&polyS.Verts[0], &polyS.Verts[1], &polyS.Verts[2], &polyS.Verts[3],
(long*)&polyS.poly_f4->x0, (long*)&polyS.poly_f4->x1, (long*)&polyS.poly_f4->x2, (long*)&polyS.poly_f4->x3,
&polyS.depth,
&polyS.flag
);
addPrim(ot[db][polyS.otz], polyS.poly_f4);
nextpri += sizeof(POLY_F4);
///// Yellow
poly1S.poly_f4 = (POLY_F4 *)nextpri;
poly1S.RotV_L.vz += poly1S.rotSpeed;
RotMatrix(&poly1S.RotV_L, &poly1S.Matrix);
TransMatrix(&poly1S.Matrix, &poly1S.TransV_L);
ScaleMatrix(&poly1S.Matrix, &poly1S.ScaleV_L);
SetRotMatrix(&poly1S.Matrix);
SetTransMatrix(&poly1S.Matrix);
setPolyF4(poly1S.poly_f4);
setRGB0(poly1S.poly_f4, poly1S.color.r,poly1S.color.g,poly1S.color.b);
RotTransPers4(
&poly1S.Verts[0], &poly1S.Verts[1], &poly1S.Verts[2], &poly1S.Verts[3],
(long*)&poly1S.poly_f4->x0, (long*)&poly1S.poly_f4->x1, (long*)&poly1S.poly_f4->x2, (long*)&poly1S.poly_f4->x3,
&poly1S.depth,
&poly1S.flag
);
addPrim(ot[db][poly1S.otz], poly1S.poly_f4);
nextpri += sizeof(POLY_F4);
///// Green
poly2S.poly_f4 = (POLY_F4 *)nextpri;
poly2S.RotV_L.vz += poly2S.rotSpeed;
RotMatrix(&poly2S.RotV_L, &poly2S.Matrix);
TransMatrix(&poly2S.Matrix, &poly2S.TransV_L);
ScaleMatrix(&poly2S.Matrix, &poly2S.ScaleV_L);
SetRotMatrix(&poly2S.Matrix);
SetTransMatrix(&poly2S.Matrix);
setPolyF4(poly2S.poly_f4);
setRGB0(poly2S.poly_f4, poly2S.color.r,poly2S.color.g,poly2S.color.b);
RotTransPers4(
&poly2S.Verts[0], &poly2S.Verts[1], &poly2S.Verts[2], &poly2S.Verts[3],
(long*)&poly2S.poly_f4->x0, (long*)&poly2S.poly_f4->x1, (long*)&poly2S.poly_f4->x2, (long*)&poly2S.poly_f4->x3,
&poly2S.depth,
&poly2S.flag
);
addPrim(ot[db][poly2S.otz], poly2S.poly_f4);
nextpri += sizeof(POLY_F4);
///// Blue
poly3S.poly_f4 = (POLY_F4 *)nextpri;
poly3S.RotV_L.vz += poly3S.rotSpeed;
RotMatrix(&poly3S.RotV_L, &poly3S.Matrix);
TransMatrix(&poly3S.Matrix, &poly3S.TransV_L);
ScaleMatrix(&poly3S.Matrix, &poly3S.ScaleV_L);
SetRotMatrix(&poly3S.Matrix);
SetTransMatrix(&poly3S.Matrix);
setPolyF4(poly3S.poly_f4);
setRGB0(poly3S.poly_f4, poly3S.color.r,poly3S.color.g,poly3S.color.b);
RotTransPers4(
&poly3S.Verts[0], &poly3S.Verts[1], &poly3S.Verts[2], &poly3S.Verts[3],
(long*)&poly3S.poly_f4->x0, (long*)&poly3S.poly_f4->x1, (long*)&poly3S.poly_f4->x2, (long*)&poly3S.poly_f4->x3,
&poly3S.depth,
&poly3S.flag
);
addPrim(ot[db][poly3S.otz], poly3S.poly_f4);
nextpri += sizeof(POLY_F4);
// Pad stuff
int pad = PadRead(0); // init pad
// Right D-pad
if(pad & PADRup){
if (CurrentPoly->PivotV_L.vy >= 0){
CurrentPoly->PivotV_L.vy -= 1;
pivotPoint(CurrentPoly->Verts, CurrentPoly->width, CurrentPoly->height, CurrentPoly->PivotV_L);
}
else {
CurrentPoly->PivotV_L.vy = CurrentPoly->PivotV_L.vy;
}
};
if(pad & PADRdown){
if (CurrentPoly->PivotV_L.vy <= CurrentPoly->height ){
CurrentPoly->PivotV_L.vy += 1;
pivotPoint(CurrentPoly->Verts, CurrentPoly->width, CurrentPoly->height, CurrentPoly->PivotV_L);
}
else {
CurrentPoly->PivotV_L.vy = CurrentPoly->PivotV_L.vy;
}
};
if(pad & PADRleft){
if (CurrentPoly->PivotV_L.vx >= 0){
CurrentPoly->PivotV_L.vx -= 1;
pivotPoint(CurrentPoly->Verts, CurrentPoly->width, CurrentPoly->height, CurrentPoly->PivotV_L);
}
else {
CurrentPoly->PivotV_L.vx = CurrentPoly->PivotV_L.vx;
}
};
if(pad & PADRright){
if (CurrentPoly->PivotV_L.vx <= CurrentPoly->width ){
CurrentPoly->PivotV_L.vx += 1;
pivotPoint(CurrentPoly->Verts, CurrentPoly->width, CurrentPoly->height, CurrentPoly->PivotV_L);
}
else {
CurrentPoly->PivotV_L.vx = CurrentPoly->PivotV_L.vx;
}
};
// R1, R2, L2, L2
if(pad & PADR1){
if(BtnTimer == 0){
if (polyCount < 4){
CurrentPoly -= 1;
BtnTimer = 10;
polyCount++;
}
else {
CurrentPoly = &polyS + 1;
polyCount = 0;
}
}
}
if(pad & PADR2){
if(BtnTimer == 0){
if(CurrentPoly->otz < 5 ){
CurrentPoly->otz += 1;
BtnTimer = 10;
} else {
CurrentPoly->otz = 1;
BtnTimer = 10;
}
}
}
if(pad & PADL1){
if(BtnTimer == 0){
if (CurrentPoly->rotSpeed <= 320){
CurrentPoly->rotSpeed += 8;
}
BtnTimer = 10;
}
}
if(pad & PADL2){
if(BtnTimer == 0){
if (CurrentPoly->rotSpeed >= -320){
CurrentPoly->rotSpeed -= 8;
}
BtnTimer = 10;
}
}
// Left D-Pad
if(pad & PADLup){
if(BtnTimer == 0){
CurrentPoly->TransV_L.vy -= 1;
//~ BtnTimer = 2;
}
}
if(pad & PADLdown){
if(BtnTimer == 0){
CurrentPoly->TransV_L.vy += 1;
//~ BtnTimer = 2;
}
}
if(pad & PADLleft){
if(BtnTimer == 0){
CurrentPoly->TransV_L.vx -= 1;
//~ BtnTimer = 2;
}
}
if(pad & PADLright){
if(BtnTimer == 0){
CurrentPoly->TransV_L.vx += 1;
//~ BtnTimer = 2;
}
}
if(pad & PADstart){
if(BtnTimer == 0){
CurrentPoly->ScaleV_L.vx += 100;
CurrentPoly->ScaleV_L.vy += 100;
//~ CurrentPoly->TransV_L.vz += 1;
}
}
if(pad & PADselect){
if(BtnTimer == 0){
CurrentPoly->ScaleV_L.vx -= 100;
CurrentPoly->ScaleV_L.vy -= 100;
//~ CurrentPoly->TransV_L.vz -= 1;
}
}
// Btn_timer decrement
if(BtnTimer > 0){
BtnTimer -= 1;
}
// On-screen instructions
FntPrint("\
D-Pad:move polygon.\n\
[],X,O,\/\\ : Move pivot point.\n\
L1,L2 : Rotations speed +/-\n\
R1 : select polygon\n\
R2 : change z-index\n\
Start,Select : Scale polygon +/-\
");
FntFlush(-1);
display();
}
return 0;
}
================================================
FILE: hello_poly_gt/Makefile
================================================
TARGET = hello_poly_gt
SRCS = hello_poly_gt.c \
../TIM/bousai.tim \
include ../common.mk
================================================
FILE: hello_poly_gt/hello_poly_gt.c
================================================
// Draw a textured poly primitive with gouraud shading
//
// With help from Nicolas Noble, Jaby smoll Seamonstah, Lameguy64
//
// From ../psyq/addons/graphics/MESH/RMESH/TUTO0.C :
// Schnappy 2021
#include
#include
#include
#include
#include
#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL
#define SCREENXRES 320 // Screen width
#define SCREENYRES 240 // Screen height
#define CENTERX SCREENXRES/2 // Center of screen on x
#define CENTERY SCREENYRES/2 // Center of screen on y
#define MARGINX 32 // margins for text display
#define MARGINY 32
#define FONTSIZE 8 * 5 // Text Field Height
#define OTLEN 8 // Ordering Table Length
DISPENV disp[2]; // Double buffered DISPENV and DRAWENV
DRAWENV draw[2];
u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes
char primbuff[2][32768]; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes
char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0]
short db = 0; // index of which buffer is used, values 0, 1
// 16bpp TIM
extern unsigned long _binary____TIM_bousai_tim_start[];
extern unsigned long _binary____TIM_bousai_tim_end[];
extern unsigned long _binary____TIM_bousai_tim_length;
TIM_IMAGE bousai;
void LoadTexture(u_long * tim, TIM_IMAGE * tparam){ // This part is from Lameguy64's tutorial series : lameguy64.net/svn/pstutorials/chapter1/3-textures.html login/pw: annoyingmous
OpenTIM(tim); // Open the tim binary data, feed it the address of the data in memory
ReadTIM(tparam); // This read the header of the TIM data and sets the corresponding members of the TIM_IMAGE structure
LoadImage(tparam->prect, tparam->paddr); // Transfer the data from memory to VRAM at position prect.x, prect.y
DrawSync(0); // Wait for the drawing to end
if (tparam->mode & 0x8){ // check 4th bit // If 4th bit == 1, TIM has a CLUT
LoadImage(tparam->crect, tparam->caddr); // Load it to VRAM at position crect.x, crect.y
DrawSync(0); // Wait for drawing to end
}
}
void init(void)
{
ResetGraph(0);
// Initialize and setup the GTE
InitGeom();
SetGeomOffset(CENTERX,CENTERY);
SetGeomScreen(CENTERX);
SetDefDispEnv(&disp[0], 0, 0, SCREENXRES, SCREENYRES);
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[1], 0, 0, SCREENXRES, SCREENYRES);
if (VMODE)
{
SetVideoMode(MODE_PAL);
disp[0].screen.y += 8;
disp[1].screen.y += 8;
}
SetDispMask(1); // Display on screen
setRGB0(&draw[0], 128, 128, 128);
setRGB0(&draw[1], 128, 128, 128);
draw[0].isbg = 1;
draw[1].isbg = 1;
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
FntLoad(960, 0);
FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 );
}
void display(void)
{
DrawSync(0);
VSync(0);
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
DrawOTag(&ot[db][OTLEN - 1]);
db = !db;
nextpri = primbuff[db];
}
int main(void)
{
int i;
POLY_GT4 *poly = {0}; // pointer to a POLY_G4
SVECTOR RotVector = {0, 0, 0}; // Initialize rotation vector {x, y, z}
VECTOR MovVector = {0, 0, 120, 0}; // Initialize translation vector {x, y, z}
SVECTOR VertPos[4] = { // Set initial vertices position relative to 0,0 - see here : https://psx.arthus.net/docs/poly_f4.jpg
{-32, -32, 0 }, // Vert 1
{-32, 32, 0 }, // Vert 2
{ 32, -32, 0 }, // Vert 3
{ 32, 32, 0 } // Vert 4
};
MATRIX PolyMatrix = {0};
DR_TPAGE * bousai_tpage;
long polydepth;
long polyflag;
init();
LoadTexture(_binary____TIM_bousai_tim_start, &bousai);
while (1)
{
ClearOTagR(ot[db], OTLEN);
poly = (POLY_GT4 *)nextpri; // Set poly to point to the address of the next primitiv in the buffer
// Set transform matrices for this polygon
RotMatrix(&RotVector, &PolyMatrix); // Apply rotation matrix
TransMatrix(&PolyMatrix, &MovVector); // Apply translation matrix
SetRotMatrix(&PolyMatrix); // Set default rotation matrix
SetTransMatrix(&PolyMatrix); // Set default transformation matrix
setPolyGT4(poly); // Initialize poly as a POLY_F4
poly->tpage = getTPage(bousai.mode&0x3, 0, bousai.prect->x, bousai.prect->y);
setRGB0(poly, 128, 128, 128); // Set vertice 1 color
setRGB1(poly, 255, 0, 0); // Set vertice 2 color
setRGB2(poly, 0, 255, 0); // Set vertice 3 color
setRGB3(poly, 0, 0, 255); // Set vertice 4 color
RotTransPers4(
&VertPos[0], &VertPos[1], &VertPos[2], &VertPos[3],
(long*)&poly->x0, (long*)&poly->x1, (long*)&poly->x2, (long*)&poly->x3,
&polydepth,
&polyflag
); // Perform coordinate and perspective transformation for 4 vertices
setUV4(poly, 0, 0, 0, 144, 144, 0, 144, 144); // Set UV coordinates in order Top Left, Bottom Left, Top Right, Bottom Right
RotVector.vx += 8; // Apply rotation on Z-axis. On PSX, the Z-axis is pointing away from the screen.
addPrim(ot[db], poly); // add poly to the Ordering table
nextpri += sizeof(POLY_GT4); // increment nextpri address with size of a POLY_F4 struct
FntPrint("Hello textured shaded !");
FntFlush(-1);
display();
}
return 0;
}
================================================
FILE: hello_poly_gt_tw/Makefile
================================================
TARGET = hello_poly_gt_tw
SRCS = hello_poly_gt_tw.c \
../TIM/bousai.tim \
include ../common.mk
================================================
FILE: hello_poly_gt_tw/hello_poly_gt_tw.c
================================================
// Draw a textured poly primitive with gouraud_shading, using a repeating texture pattern
//
// With help from Nicolas Noble, Jaby smoll Seamonstah, Lameguy64
//
// From ../psyq/addons/graphics/MESH/RMESH/TUTO0.C :
// Schnappy 2021
#include
#include
#include
#include
#include
#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL
#define SCREENXRES 320 // Screen width
#define SCREENYRES 240 // Screen height
#define CENTERX SCREENXRES/2 // Center of screen on x
#define CENTERY SCREENYRES/2 // Center of screen on y
#define MARGINX 32 // margins for text display
#define MARGINY 32
#define FONTSIZE 8 * 5 // Text Field Height
#define OTLEN 8 // Ordering Table Length
DISPENV disp[2]; // Double buffered DISPENV and DRAWENV
DRAWENV draw[2];
u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes
char primbuff[2][32768]; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes
char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0]
short db = 0; // index of which buffer is used, values 0, 1
// 16bpp TIM
extern unsigned long _binary____TIM_bousai_tim_start[];
extern unsigned long _binary____TIM_bousai_tim_end[];
extern unsigned long _binary____TIM_bousai_tim_length;
TIM_IMAGE bousai;
void LoadTexture(u_long * tim, TIM_IMAGE * tparam){ // This part is from Lameguy64's tutorial series : lameguy64.net/svn/pstutorials/chapter1/3-textures.html login/pw: annoyingmous
OpenTIM(tim); // Open the tim binary data, feed it the address of the data in memory
ReadTIM(tparam); // This read the header of the TIM data and sets the corresponding members of the TIM_IMAGE structure
LoadImage(tparam->prect, tparam->paddr); // Transfer the data from memory to VRAM at position prect.x, prect.y
DrawSync(0); // Wait for the drawing to end
if (tparam->mode & 0x8){ // check 4th bit // If 4th bit == 1, TIM has a CLUT
LoadImage(tparam->crect, tparam->caddr); // Load it to VRAM at position crect.x, crect.y
DrawSync(0); // Wait for drawing to end
}
}
void init(void)
{
ResetGraph(0);
// Initialize and setup the GTE
InitGeom();
SetGeomOffset(CENTERX,CENTERY);
SetGeomScreen(CENTERX);
SetDefDispEnv(&disp[0], 0, 0, SCREENXRES, SCREENYRES);
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[1], 0, 0, SCREENXRES, SCREENYRES);
if (VMODE)
{
SetVideoMode(MODE_PAL);
disp[0].screen.y += 8;
disp[1].screen.y += 8;
}
SetDispMask(1); // Display on screen
setRGB0(&draw[0], 128, 128, 128);
setRGB0(&draw[1], 128, 128, 128);
draw[0].isbg = 1;
draw[1].isbg = 1;
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
FntLoad(960, 0);
FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 );
}
void display(void)
{
DrawSync(0);
VSync(0);
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
DrawOTag(&ot[db][OTLEN - 1]);
db = !db;
nextpri = primbuff[db];
}
int main(void)
{
int i;
POLY_GT4 *poly = {0}; // pointer to a POLY_G4
SVECTOR RotVector = {0, 0, 0}; // Initialize rotation vector {x, y, z}
VECTOR MovVector = {0, 0, 120, 0}; // Initialize translation vector {x, y, z}
SVECTOR VertPos[4] = { // Set initial vertices position relative to 0,0 - see here : https://psx.arthus.net/docs/poly_f4.jpg
{-32, -32, 0 }, // Vert 1
{-32, 32, 0 }, // Vert 2
{ 32, -32, 0 }, // Vert 3
{ 32, 32, 0 } // Vert 4
};
MATRIX PolyMatrix = {0};
DR_TPAGE * bousai_tpage;
long polydepth;
long polyflag;
// Texture window
DR_MODE * dr_mode; // Pointer to dr_mode prim
RECT tws = {64, 32, 32, 32}; // Texture window coordinates : x, y, w, h
// See libref47.pdf, p.242, 7-6, table 7-2 for possible values
init();
LoadTexture(_binary____TIM_bousai_tim_start, &bousai);
while (1)
{
ClearOTagR(ot[db], OTLEN);
poly = (POLY_GT4 *)nextpri; // Set poly to point to the address of the next primitiv in the buffer
// Set transform matrices for this polygon
RotMatrix(&RotVector, &PolyMatrix); // Apply rotation matrix
TransMatrix(&PolyMatrix, &MovVector); // Apply translation matrix
SetRotMatrix(&PolyMatrix); // Set default rotation matrix
SetTransMatrix(&PolyMatrix); // Set default transformation matrix
setPolyGT4(poly); // Initialize poly as a POLY_F4
poly->tpage = getTPage(bousai.mode&0x3, 0, bousai.prect->x, bousai.prect->y);
setRGB0(poly, 128, 128, 128); // Set vertice 1 color
setRGB1(poly, 255, 0, 0); // Set vertice 2 color
setRGB2(poly, 0, 255, 0); // Set vertice 3 color
setRGB3(poly, 0, 0, 255); // Set vertice 4 color
RotTransPers4(
&VertPos[0], &VertPos[1], &VertPos[2], &VertPos[3],
(long*)&poly->x0, (long*)&poly->x1, (long*)&poly->x2, (long*)&poly->x3,
&polydepth,
&polyflag
); // Perform coordinate and perspective transformation for 4 vertices
setUV4(poly, 0, 0, 0, 144, 144, 0, 144, 144); // Set UV coordinates in order Top Left, Bottom Left, Top Right, Bottom Right
RotVector.vy += 14; // Apply rotation on Z-axis. On PSX, the Z-axis is pointing away from the screen.
addPrim(ot[db], poly); // add poly to the Ordering table
nextpri += sizeof(POLY_GT4); // increment nextpri address with size of a POLY_GT4 struct
// drawing mode primitive
dr_mode = (DR_MODE *)nextpri; // initialize drawing mode primitive
setDrawMode(dr_mode, 1, 0, getTPage(bousai.mode&0x3, 0, bousai.prect->x, bousai.prect->y), &tws); //set texture window
addPrim(ot[db], dr_mode);
nextpri += sizeof(DR_MODE); // increment nextpri address with size of a DR_MODE struct
FntPrint("Hello textured shaded !");
FntFlush(-1);
display();
}
return 0;
}
================================================
FILE: hello_poly_inline/Makefile
================================================
TARGET = hello_poly_inline
SRCS = hello_poly_inline.c \
include ../common.mk
================================================
FILE: hello_poly_inline/hello_poly_inline.c
================================================
// Hello poly ! Inline / DMPSX version
//
// Ref : /psyq/DOCS/Devrefs/Inlinref.pdf, p.18
// https://psx-spx.consoledev.net/geometrytransformationenginegte/
// PSX / Z+
// screen /
//coordinate +-----X+
//system / |
// eye | Y+
//
// Credits, thanks : Nicolas Noble, Sickle, Lameguy64 @ psxdev discord : https://discord.com/invite/N2mmwp
// https://discord.com/channels/642647820683444236/663664210525290507/834831466100949002
// Schnappy 07-2021
#include
#include
#include
#include
#include
// OldWorld PsyQ has a inline_c.h file for inline GTE functions. We have to use the one at https://github.com/grumpycoders/pcsx-redux/blob/07f9b02d1dbb68f57a9f5b9773041813c55a4913/src/mips/psyq/include/inline_n.h
// because the real GTE commands are needed in nugget : https://psx-spx.consoledev.net/geometrytransformationenginegte/#gte-coordinate-calculation-commands
#include
//~ #include // gtemac contains macro versions of the libgte functions, worth checking out to see the operations order.
#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL
#define SCREENXRES 320 // Screen width
#define SCREENYRES 240 + (VMODE << 4) // Screen height : If VMODE is 0 = 240, if VMODE is 1 = 256
#define CENTERX ( SCREENXRES >> 1 ) // Center of screen on x
#define CENTERY ( SCREENYRES >> 1 ) // Center of screen on y
#define MARGINX 0 // margins for text display
#define MARGINY 32
#define FONTSIZE 8 * 7 // Text Field Height
#define OTLEN 10 // Ordering Table Length
DISPENV disp[2]; // Double buffered DISPENV and DRAWENV
DRAWENV draw[2];
u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes
char primbuff[2][32768]; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes
char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0]
short db = 0; // index of which buffer is used, values 0, 1
void init(void)
{
ResetGraph(0);
// Initialize and setup the GTE
InitGeom();
//~ SetGeomOffset(CENTERX,CENTERY);
gte_SetGeomOffset(CENTERX,CENTERY);
gte_SetGeomScreen(CENTERX);
// Set display environment
SetDefDispEnv(&disp[0], 0, 0, SCREENXRES, SCREENYRES);
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES);
// Set draw environment
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[1], 0, 0, SCREENXRES, SCREENYRES);
// If PAL, use 320x256, hence 256 - 240 = 16 / 2 = 8 px vertical offset
if (VMODE) SetVideoMode(MODE_PAL);
SetDispMask(1);
// Set background color
setRGB0(&draw[0], 50, 50, 50);
setRGB0(&draw[1], 50, 50, 50);
draw[0].isbg = 1;
draw[1].isbg = 1;
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
FntLoad(960, 0);
FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 );
}
void display(void)
{
// Wait for drawing
DrawSync(0);
// Wait for vsync
VSync(0);
// Flip DISP and DRAW env
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
DrawOTag(&ot[db][OTLEN - 1]);
// Flip db index
db = !db;
// Get next primitive in buffer
nextpri = primbuff[db];
}
int main(void)
{
long p, flag, OTz;
SVECTOR rotVector, rotVector4 = {0}; // Initialize rotation vector {x, y, z}
VECTOR transVector = {0, 0, CENTERX, 0}; // Initialize translation vector {x, y, z}
SVECTOR vertPos[4] = {
{ 0, -32, 0, 0 }, // Vert 1
{ 32, 0, 0, 0 }, // Vert 2
{ -32, 0, 0, 0 },
{ 0, 32, 0, 0 }
}; // Vert 3
MATRIX workMatrix = {0};
POLY_F3 * poly = {0}; // pointer to a POLY_F4
POLY_F4 * poly4 = {0}; // pointer to a POLY_F4
init();
while (1)
{
// Set Ordering table
ClearOTagR(ot[db], OTLEN);
// Draw on the left part of the screen
transVector.vx = -CENTERX/2;
// Increment rotation angle on Y axis
rotVector.vy += 8;
rotVector.vx -= 4 ;
// Find rotation matrix from vector, store in
RotMatrix_gte(&rotVector, &workMatrix);
// Ditto for translation
TransMatrix(&workMatrix, &transVector);
// Set the matrices we just found
gte_SetRotMatrix(&workMatrix);
gte_SetTransMatrix(&workMatrix);
// Cast next primitive in buffer as a POLY_F4 (see display() )
poly = (POLY_F3 *)nextpri;
// Draw a Tri
// Initialize poly as a POLY_F3
setPolyF3(poly);
// Set poly color - Hot pink
setRGB0(poly, 255, 0, 255);
// Store vertex positions for current polygon in registers v0,v1,v2
// Can be replaced by one gte_ldv3 call :
// gte_ldv3(&vertPos[0], &vertPos[1], &vertPos[2]);
gte_ldv0(&vertPos[0]);
gte_ldv1(&vertPos[1]);
gte_ldv2(&vertPos[2]);
// RotTransPers3 : Perform coordinate and perspective transformation for three vertices.
// Use gte_rtps() for one vertex.
gte_rtpt();
// Get screen coordinates from cop2 registers XY0,XY1,XY2 and store them in primitive's x0, y0, x1, y1, x2, y2 members.
// Can be replace with one gte_stsxy3() call :
// gte_stsxy3(&poly->x0, &poly->x1, &poly->x2);
// Can also be replaced with a primitive type dependant version :
// gte_stsxy3_f3(poly);
gte_stsxy0(&poly->x0);
gte_stsxy1(&poly->x1);
gte_stsxy2(&poly->x2);
// Get depth interpolation coefficient p
gte_stdp(&p);
// Get the flag - see libover47.pdf, p.143 for details on ppossible values
gte_stflg(&flag);
// Get screen coordinate Z/4
gte_stszotz(&OTz);
// GTE macro version - needs 'gtemac.h' to be included - uncomment l.21
//~ gte_RotTransPers3( &VertPos[0], &VertPos[1], &VertPos[2],
//~ &poly->x0, &poly->x1, &poly->x2,
//~ &p, &flag, &OTz );
// add poly to the Ordering table
addPrim(ot[db], poly);
// increment nextpri address with size of a POLY_F3 struct
nextpri += sizeof(POLY_F3);
// Draw a Quad
//
// The GTE rtpt can only transform 3 vertices at a time, so we have to do all operations as 3 + 1.
// Move to right of screen
transVector.vx = CENTERX/2;
// Increment rot on X/Y axis
rotVector4.vy -= 8 ;
rotVector4.vx -= 4 ;
// Set matrices
RotMatrix_gte(&rotVector4, &workMatrix);
TransMatrix(&workMatrix, &transVector);
gte_SetRotMatrix(&workMatrix);
gte_SetTransMatrix(&workMatrix);
// Cast a POLY_F4 at the address we just incremented.
poly4 = (POLY_F4 *)nextpri;
// Initialize poly as a POLY_F4
setPolyF4(poly4);
// Set Poly color - Blue
setRGB0(poly4, 0, 255, 255);
// Transform 3 first vertices
gte_ldv3(&vertPos[0], &vertPos[1], &vertPos[2]);
gte_rtpt();
gte_stsxy3_f4(poly4);
// Transform remaining vertex
gte_ldv0(&vertPos[3]);
gte_rtps();
// SXY3 is set with gte_stsxy() or gte_stsxy2() ¯\_(ツ)_/¯
gte_stsxy(&poly4->x3);
// Get p, flag and OTz
gte_stdp(&p);
gte_stflg(&flag);
gte_stszotz(&OTz);
addPrim(ot[db], poly4); // add poly to the Ordering table
nextpri += sizeof(POLY_F4); // increment nextpri address with size of a POLY_F3 struct
// Display text
FntPrint("Hello Inline GTE !\n");
FntFlush(-1);
display();
}
return 0;
}
================================================
FILE: hello_poly_stp/Makefile
================================================
TARGET = hello_poly_stp
SRCS = hello_poly_stp.c \
TIM/stpOnAlpha.tim \
TIM/stpOnAlphaI.tim \
TIM/stpOnBlack.tim \
TIM/stpOnColIndex.tim \
TIM/stpOnNonBlack.tim \
include ../common.mk
================================================
FILE: hello_poly_stp/README.md
================================================

# STP : Semi-Transparency usage
This example shows the various way of converting an image with transparency to a TIM and use it in code.
It also shows the effect of activating Semi-Transparency on a primitive textured with those images.
Use the `SELECT` button to switch primitive semi-transparency on and off.
It also features a few C struct to facilitate access to the TIM file / pixel data.
See [https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/STP](https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/STP) for more information on how to convert your images to TIM while preserving transparency.
See [https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/TIM#transparency](https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/TIM#transparency) for mor details on transparency.
================================================
FILE: hello_poly_stp/hello_poly_stp.c
================================================
// Demo the different settings for pixel and primitive semi-transparency
//
// Schnappy 07-2021
#include
#include
#include
#include
#include
#include
#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL
#define SCREENXRES 320 // Screen width
#define SCREENYRES 240 // Screen height
#define CENTERX SCREENXRES/2 // Center of screen on x
#define CENTERY SCREENYRES/2 // Center of screen on y
#define MARGINX 16 // margins for text display
#define MARGINY 16
#define OTLEN 8 // Ordering Table Length
DISPENV disp[2]; // Double buffered DISPENV and DRAWENV
DRAWENV draw[2];
u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes
char primbuff[2][32768]; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes
char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0]
short db = 0; // index of which buffer is used, values 0, 1
// RGB pixels are 16bpp, 5b Red, 5b Green, 5b Blue, 1b STP (semi-transparency)
// See http://psx.arthus.net/sdk/Psy-Q/DOCS/FileFormat47.pdf, p.183
typedef struct RGB_PIX {
u_int R:5, G:5, B:5, STP:1;
} RGB_PIX;
// TIM's pixel data
// See http://psx.arthus.net/sdk/Psy-Q/DOCS/FileFormat47.pdf, p.182
typedef struct PIXEL {
u_long bnum;
u_short DX, DY;
u_short W, H;
RGB_PIX data[];
} PIXEL;
// TIM's CLUT section - exists only in 4/8bpp TIMs
// See See http://psx.arthus.net/sdk/Psy-Q/DOCS/FileFormat47.pdf, p.181
typedef struct CLUT {
u_long bnum;
u_short DX, DY;
u_short W, H;
u_short clut[];
} CLUT;
// 4/8bpp TIM files have CLUT
typedef struct TIM_FILE_CLUT{
u_long ID;
u_long flag;
u_long clut;
PIXEL pixel[];
} TIM_FILE_CLUT;
// 16/24bpp TIM files have not CLUT member
// See See http://psx.arthus.net/sdk/Psy-Q/DOCS/FileFormat47.pdf, p.179
typedef struct TIM_FILE{
u_long ID;
u_long flag;
PIXEL pixel[];
} TIM_FILE;
// If we were using C++, we could use templates
//~ struct EmbeddedClut { u_long clut; };
//~ struct NoEmbeddedClut { };
//~ template
//~ struct TIM_FILE {
//~ u_long ID;
//~ u_long flag;
//~ std::conditional clut;
//~ PIXEL pixel[];
//~ };
// 16bpp TIM
// STP set on black pixels ( STP, B, R, G == 1, 0, 0 ,0)
extern TIM_FILE _binary_TIM_stpOnBlack_tim_start;
// STP set on non black pixels ( STP, B, R, G == 1, !0, !0 ,!0)
extern TIM_FILE _binary_TIM_stpOnNonBlack_tim_start;
// STP set on image's alpha channnel ( STP, B, R, G == 1, a, a ,a)
extern TIM_FILE _binary_TIM_stpOnAlphaI_tim_start;
// STP set on 8bpp TIM's CLUT index 0 ( STP, B, R, G == 1, i, i, i)
extern TIM_FILE _binary_TIM_stpOnColIndex_tim_start;
// Store in an array so we can iterate over it
TIM_FILE * timFiles[4];
TIM_IMAGE timImages[4];
// Number of primitives to draw
#define NUM_PRIM 4
// Primitive stp flag : 0 == off, 1 == on
char stpFlag = 0;
void LoadTexture(TIM_FILE * tim, TIM_IMAGE * tparam){ // This part is from Lameguy64's tutorial series : lameguy64.net/svn/pstutorials/chapter1/3-textures.html login/pw: annoyingmous
OpenTIM( ( u_long * ) tim); // Open the tim binary data, feed it the address of the data in memory
ReadTIM(tparam); // This read the header of the TIM data and sets the corresponding members of the TIM_IMAGE structure
LoadImage(tparam->prect, tparam->paddr); // Transfer the data from memory to VRAM at position prect.x, prect.y
DrawSync(0); // Wait for the drawing to end
if (tparam->mode & 0x8){ // check 4th bit // If 4th bit == 1, TIM has a CLUT
LoadImage(tparam->crect, tparam->caddr); // Load it to VRAM at position crect.x, crect.y
DrawSync(0); // Wait for drawing to end
}
}
void init(void)
{
ResetGraph(0);
// Initialize and setup the GTE
InitGeom();
SetGeomOffset( 0 , 0 );
SetGeomScreen( CENTERX );
SetDefDispEnv(&disp[0], 0, 0, SCREENXRES, SCREENYRES);
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[1], 0, 0, SCREENXRES, SCREENYRES);
if (VMODE)
{
SetVideoMode(MODE_PAL);
disp[0].screen.y += 8;
disp[1].screen.y += 8;
}
SetDispMask(1); // Display on screen
setRGB0(&draw[0], 255, 0, 128);
setRGB0(&draw[1], 255, 0, 128);
draw[0].isbg = 1;
draw[1].isbg = 1;
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
FntLoad(960, 0);
FntOpen(MARGINX, MARGINY, SCREENXRES - MARGINX * 2, SCREENXRES - MARGINY * 2, 0, 512 );
}
void display(void)
{
DrawSync(0);
VSync(0);
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
DrawOTag(&ot[db][OTLEN - 1]);
db = !db;
nextpri = primbuff[db];
}
int main(void)
{
// Populate array with pointers to TIM data
timFiles[0] = &_binary_TIM_stpOnBlack_tim_start;
timFiles[1] = &_binary_TIM_stpOnNonBlack_tim_start;
timFiles[2] = &_binary_TIM_stpOnAlphaI_tim_start;
timFiles[3] = &_binary_TIM_stpOnColIndex_tim_start;
// Init Disp/Draw, double buffer, font
init();
// Init proto pad
PadInit(0);
int pad, oldPad;
// Pointer to a POLY_G4
POLY_FT4 * poly[4] = {0};
SVECTOR VertPos[4] = { // Set initial vertices position relative to 0,0 - see here : https://psx.arthus.net/docs/poly_f4.jpg
{-32, -32, 1 }, // Vert 1
{-32, 32, 1 }, // Vert 2
{ 32, -32, 1 }, // Vert 3
{ 32, 32, 1 } // Vert 4
};
VECTOR transVector = { SCREENXRES/3, SCREENYRES/4, 128, 0}; // Initialize translation vector {x, y, z, pad}
SVECTOR rotVector = {0}; // Initialize rotation vector {x, y, z}
// Load textures to VRAM
for (char tim = 0; tim < NUM_PRIM; tim++){
LoadTexture(timFiles[tim], &timImages[tim]);
}
while (1)
{
MATRIX Work;
// Clear OT
ClearOTagR(ot[db], OTLEN);
// Use a temporary work matrix
// Set Trans/Rot vectors to work matrix
RotMatrix(&rotVector, &Work); // Apply rotation matrix
TransMatrix(&Work, &transVector); // Apply translation matrix
SetRotMatrix(&Work); // Set default rotation matrix
SetTransMatrix(&Work); // Set default transformation matrix
// Draw NUM_PRIM primitives
for (int i = 0; i < NUM_PRIM; i++){
long p, flag;
// Draw prims with an offset base on iteration number
transVector.vx = SCREENXRES/NUM_PRIM + (i * (SCREENXRES/NUM_PRIM + 32) ) ;
transVector.vy = SCREENYRES/NUM_PRIM;
if ( i >= 2) {
transVector.vx = SCREENXRES/NUM_PRIM + ((i - 2) * (SCREENXRES/NUM_PRIM + 32) ) ;
transVector.vy = SCREENYRES/2 + 24;
}
TransMatrix(&Work, &transVector);
SetTransMatrix(&Work);
// Set poly
poly[i] = (POLY_FT4 *)nextpri; // Set poly to point to the address of the next primitiv in the buffer
setPolyFT4(poly[i]); // Initialize poly as a POLY_F4
// Get texture page
poly[i]->tpage = getTPage( timImages[i].mode & 0x3,
0,
// Get Tpage coordinates from the TIM_IMAGE mode and prect members.
timImages[i].prect->x,
timImages[i].prect->y);
// If 8/4bpp, get CLUT
if ( (timImages[i].mode & 0x3) < 2 ) {
setClut(poly[i],
timImages[i].crect->x,
timImages[i].crect->y
);
}
setRGB0(poly[i], 128, 128, 128); // Set poly color (neutra here)
SetSemiTrans(poly[i], stpFlag);
RotTransPers4(
&VertPos[0], &VertPos[1], &VertPos[2], &VertPos[3],
(long*)&poly[i]->x0, (long*)&poly[i]->x1, (long*)&poly[i]->x2, (long*)&poly[i]->x3,
&p,
&flag
); // Perform coordinate and perspective transformation for 4 vertices
setUV4(poly[i], 0, 0, 0, 144, 144, 0, 144, 144); // Set UV coordinates in order Top Left, Bottom Left, Top Right, Bottom Right
// Add poly to the Ordering table
addPrim(ot[db], poly[i]);
// Increment nextpri address with size of a POLY_F4 struct
nextpri += sizeof(POLY_FT4);
}
// Get pad input
pad = PadRead(0);
// If select button is used
if ( pad & PADselect && !( pad & oldPad ) ){
// Flip STP flag
stpFlag = !stpFlag;
// Set flag to avoir misfire
oldPad = pad;
}
// Reset flag when button released
if (!(pad & PADselect)) {
oldPad = 0;
}
FntPrint("Hello semi-transparency !\nPrim STP (push Select) : %d\n\n\n\n\n\n\n\n\n\n\n\n", stpFlag);
FntPrint(" stp on black stp on non-black\n\n\n\n\n\n\n\n\n\n\n\n");
FntPrint(" stp on non-black stp on col index");
FntFlush(-1);
display();
}
return 0;
}
================================================
FILE: hello_sio/Makefile
================================================
TARGET = hello_sio
SRCS = hello_sio.c \
include ../common.mk
================================================
FILE: hello_sio/hello_sio.c
================================================
// hello_sio example
// This example will display the RX data in a 64 char rolling buffer.
// Use minicom or any other serial comm program and a serial/USB cable.
//
// Relevant doc is libref47.pdf, l.1120-1127
// Schnappy - 04/2021
//
// Based on : ../psyq/psx/sample/serial/SIO
// sio echo back
// 1.00 Jan.28.1997 shino
#include
#include
#include
#include
#include
// Needed for SIO operations
#include
// Needed for manipulating strings
#include
// Display stuff (see hello_tile for the basics)
#define VMODE 0
#define SCREENXRES 320
#define SCREENYRES 240
#define CENTERX SCREENXRES/2
#define CENTERY SCREENYRES/2
#define OTLEN 2048 // Maximum number of OT entries
#define PRIMBUFFLEN 32768 // Maximum number of POLY_GT3 primitives
// Display and draw environments, double buffered
DISPENV disp[2];
DRAWENV draw[2];
u_long ot[2][OTLEN]; // Ordering table (contains addresses to primitives)
char primbuff[2][PRIMBUFFLEN]; // Primitive list // That's our prim buffer
char * nextpri = primbuff[0]; // Primitive counter
short db = 0; // Current buffer counter
// SIO
#define MAX_CHARS 64
u_char SIO = 1; // Is SIO enabled ?
u_char SIOinit = 0; // Is SIO initialized ?
// Prototypes
void init(void);
void display(void);
void init(){
// Reset the GPU before doing anything and the controller
ResetGraph(0);
// Set the display and draw environments
SetDefDispEnv(&disp[0], 0, 0 , SCREENXRES, SCREENYRES);
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[1], 0, 0, SCREENXRES, SCREENYRES);
// If in PAL mode, add vertical offset
if (VMODE)
{
SetVideoMode(MODE_PAL);
disp[0].screen.y += 8;
disp[1].screen.y += 8;
}
SetDispMask(1);
setRGB0(&draw[0], 0, 0, 255);
setRGB0(&draw[1], 0, 0, 255);
draw[0].isbg = 1;
draw[1].isbg = 1;
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
// Init font system
FntLoad(960, 0);
FntOpen(16, 16, 196, 64, 0, 256);
}
void display(void){
DrawSync(0);
VSync(0);
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
db = !db;
}
int main() {
init();
// Main loop
while (1) {
// Buffer for the RX data of size MAX_CHARS
static char buffer[ MAX_CHARS ] = {0};
// If SIO flag is set, initialize and get data
if( SIO ){
// Is SIO is not initialized, dot it
if( ! SIOinit ){
ResetCallback();
// Load SIO driver at 115200bps
AddSIO(115200);
ResetGraph(0);
// Use _sio_control to clear driver status error-related bits
// See psyq's libref47.pdf, p.1125 for the commands and status tables
_sio_control(2,1,0);
SIOinit = 1;
}
// Limit input buffer to MAX_CHARS chars, making it a rolling buffer
if( strlen(buffer) > MAX_CHARS ){
// If that limit is reached, remove first char in string
memmove(buffer, buffer + 1, strlen(buffer));
}
// Check if sio driver is able to write communications data
// If so, this means reading is not occuring
if( _sio_control(0,0,0) & SR_RXRDY ){ // SR_RXRDY == 0x2
// Read byte
char c = _sio_control(0,4,0);
// Add to buffer
strncat(buffer, &c, 1);
}
}
// END SIO FUN
FntPrint("Hello Serial!\n\n");
if( buffer ){
FntPrint("%s", buffer);
}
FntFlush(-1);
display();
}
return 0;
}
================================================
FILE: hello_sprt/Makefile
================================================
TARGET = hello_sprt
SRCS = hello_sprt.c \
../TIM/TIM16.tim \
../TIM/TIM8.tim \
../TIM/TIM4.tim \
include ../common.mk
================================================
FILE: hello_sprt/hello_sprt.c
================================================
// Draw a textured sprt primitive
//
// based on Lameguy64's tutorial : http://lameguy64.net/svn/pstutorials/chapter1/3-textures.html
// Schnappy 2020
#include
#include
#include
#include
#include
#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL
#define SCREENXRES 320
#define SCREENYRES 240
#define CENTERX SCREENXRES/2
#define CENTERY SCREENYRES/2
#define MARGINX 32 // margins for text display
#define MARGINY 44
#define FONTSIZE 8 * 3 // Text Field Height
#define OTLEN 8 // Ordering Table Length
DISPENV disp[2]; // Double buffered DISPENV and DRAWENV
DRAWENV draw[2];
u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes
char primbuff[2][32768]; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes
char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0]
short db = 0; // index of which buffer is used, values 0, 1
// Embed TIM files
// See https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/(Archived)--Embedding-binary-data-in-your-psx-executable
// 16bpp TIM
extern unsigned long _binary____TIM_TIM16_tim_start[];
extern unsigned long _binary____TIM_TIM16_tim_end[];
extern unsigned long _binary____TIM_TIM16_tim_length;
// 8bpp TIM
extern unsigned long _binary____TIM_TIM8_tim_start[];
extern unsigned long _binary____TIM_TIM8_tim_end[];
extern unsigned long _binary____TIM_TIM8_TIM_length;
// 4bpp TIM
extern unsigned long _binary____TIM_TIM4_tim_start[];
extern unsigned long _binary____TIM_TIM4_tim_end[];
extern unsigned long _binary____TIM_TIM4_tim_length;
TIM_IMAGE TIM_16;
TIM_IMAGE TIM_8;
TIM_IMAGE TIM_4;
void LoadTexture(u_long * tim, TIM_IMAGE * tparam){ // This part is from Lameguy64's tutorial series : lameguy64.net/svn/pstutorials/chapter1/3-textures.html login/pw: annoyingmous
OpenTIM(tim); // Open the tim binary data, feed it the address of the data in memory
ReadTIM(tparam); // This read the header of the TIM data and sets the corresponding members of the TIM_IMAGE structure
LoadImage(tparam->prect, tparam->paddr); // Transfer the data from memory to VRAM at position prect.x, prect.y
DrawSync(0); // Wait for the drawing to end
if (tparam->mode & 0x8){ // check 4th bit // If 4th bit == 1, TIM has a CLUT
LoadImage(tparam->crect, tparam->caddr); // Load it to VRAM at position crect.x, crect.y
DrawSync(0); // Wait for drawing to end
}
}
void init(void)
{
ResetGraph(0);
SetDefDispEnv(&disp[0], 0, 0, SCREENXRES, SCREENYRES);
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[1], 0, 0, SCREENXRES, SCREENYRES);
if (VMODE)
{
SetVideoMode(MODE_PAL);
disp[0].screen.y += 8;
disp[1].screen.y += 8;
}
SetDispMask(1); // Display on screen
setRGB0(&draw[0], 50, 50, 50);
setRGB0(&draw[1], 50, 50, 50);
draw[0].isbg = 1;
draw[1].isbg = 1;
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
FntLoad(960, 0);
FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 );
}
void display(void)
{
DrawSync(0);
VSync(0);
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
DrawOTag(&ot[db][OTLEN - 1]);
db = !db;
nextpri = primbuff[db];
}
int main(void)
{
SPRT * sprt_16b; // Define 3 pointers to SPRT struct
SPRT * sprt_8b;
SPRT * sprt_4b;
DR_TPAGE * tpage_16b; // Define 3 pointers to DR_TPAGE struct. We need three because our images are on three
DR_TPAGE * tpage_8b; // different texture pages.
DR_TPAGE * tpage_4b;
init();
LoadTexture(_binary____TIM_TIM16_tim_start, &TIM_16); // Load everything to vram
LoadTexture(_binary____TIM_TIM8_tim_start, &TIM_8);
LoadTexture(_binary____TIM_TIM4_tim_start, &TIM_4);
while (1)
{
ClearOTagR(ot[db], OTLEN);
// Loading a 16 bit TIM
sprt_16b = (SPRT *)nextpri; // Cast whats at nexpri as a SPRT named sprt_16b
setSprt(sprt_16b); // Initialize the SPRT struct
setRGB0(sprt_16b, 128, 128, 128); // Set RGB color. 128,128,128 is neutral. You can color the image by adjusting these values
setXY0(sprt_16b, 28, MARGINY); // Set sprite position
setWH(sprt_16b, 64, 128 ); // Set sprite width and height
addPrim(ot[db], sprt_16b); // add the sprite primitive to the ordering table
nextpri += sizeof(SPRT); // increment nextpri so that it points just after sprt_16b in the primitive buffer
// Set Texture page for the 16bit tim : 768, 0 - No CLUT
// Note : You need to use setDrawTPage each time you want to use a texture that's on a different texture page
tpage_16b = (DR_TPAGE*)nextpri;
setDrawTPage(tpage_16b, 0, 1, // Set the Texture Page the texture we want resides on.
getTPage(TIM_16.mode&0x3, 0, // Here we are using bitmasking to deduce the picture mode : &0x3
TIM_16.prect->x, TIM_16.prect->y)); // In binary, 3 is 11, so we only keep the first two bits
// Values can be 00 (0), 01 (1), 10(2), respectively, 4bpp, 8bpp, 15bpp, 24bpp. See Fileformat47.pdf, p.180
// Similarly, we could use bitmasking to deduce if there is a CLUT by bitmasking the 4th bit : if(TIM_IMAGE.mode & 0x8) LoadImage... :
addPrim(ot[db], tpage_16b); // add the sprite primitive to the ordering table
nextpri += sizeof(DR_TPAGE); // Advance next primitive address
// Loading a 8 bit TIM
sprt_8b = (SPRT *)nextpri;
setSprt(sprt_8b);
setRGB0(sprt_8b, 128, 128, 128);
setXY0(sprt_8b, sprt_16b->x0 + sprt_16b->w + 32, MARGINY);
setWH(sprt_8b, 64, 128 );
setClut(sprt_8b, TIM_8.crect->x, TIM_8.crect->y); // Only difference here is we set the CLUT to the position of the VRAM we loaded the palette earlier (see LoadTexture())
addPrim(ot[db], sprt_8b);
nextpri += sizeof(SPRT);
// Set Texture page for the 8bit tim : 512, 256 - CLUT is at 0, 480
tpage_8b = (DR_TPAGE*)nextpri;
setDrawTPage(tpage_8b, 0, 1,
getTPage(TIM_8.mode&0x3, 0,
TIM_8.prect->x, TIM_8.prect->y));
addPrim(ot[db], tpage_8b);
nextpri += sizeof(DR_TPAGE);
// Loading a 4 bit TIM
sprt_4b = (SPRT *)nextpri;
setSprt(sprt_4b);
setRGB0(sprt_4b, 128, 128, 128);
setXY0(sprt_4b, sprt_8b->x0 + sprt_8b->w + 32, MARGINY);
setWH(sprt_4b, 64, 128 );
setClut(sprt_4b, TIM_4.crect->x, TIM_4.crect->y);
addPrim(ot[db], sprt_4b);
nextpri += sizeof(SPRT);
// Set Texture page for the 4bit tim : 512, 256 - CLUT is at 0, 480
tpage_4b = (DR_TPAGE*)nextpri;
setDrawTPage(tpage_4b, 0, 1,
getTPage(TIM_4.mode&0x3, 0,
TIM_4.prect->x, TIM_4.prect->y));
addPrim(ot[db], tpage_4b);
nextpri += sizeof(DR_TPAGE);
FntPrint("16 Bit! ");
FntPrint("8 Bit! ");
FntPrint("4 Bit!\n\n");
FntPrint("Check VRAM in emu to see the dif");
FntFlush(-1);
display();
}
return 0;
}
================================================
FILE: hello_spu_readback/Makefile
================================================
.PHONY: all cleansub
all:
mkpsxiso -y ./isoconfig.xml
cleansub:
$(MAKE) clean
rm -f hello_spu_readback.cue hello_spu_readback.bin
TARGET = hello_spu_readback
SRCS = hello_spu_readback.c \
include ../common.mk
================================================
FILE: hello_spu_readback/README.md
================================================
This example is adapted from PsyQ's sample : `psyq/psx/sample/sound/CDVOL, main.c,v 1.14 1997/05/02 13:05:21 by ayako`.
It was edited to fix typos, have a hopefully better code organization with hopefully more usefull variable names.
What it does is demonstrate how to transfer data from the PSX 's SPU to main memory in order to analyze / process the audio signal and dostuff accordingly.
In this instance, it's used to determine the coordinates of a few primitives to display a [VU-meter](https://en.wikipedia.org/wiki/VU_meter).
This technique is known to be used in certain games for lipsynching or audio visualization ( Crash team racing, Hercules, Vib Ribbon ...).
## pcsx-redux : no animation
Pcsx-redux does not yet support these specific SPU buffers nor triggerring IRQ from them so as of 11-2021 this example doesn't work in this particular emulator.
If looking for an alternative, check [duckstation](https://www.duckstation.org/) out.
## PsyQ's SpuReadDecodedData() doc errata
The main function for transferring data from the SPU to the RAM is `SpuReadDecodedData()`, and is documented in **LibRef47.pdf, p1054**.
The table on this page (Table 15-2) contains erroneous data and should read :

The correct address ranges for the SPU buffer is :
| Map (bytes) | Data contents |
|-------------|---------------|
| 0x000 - 0x3ff | CD Left channel |
| 0x400 - 0x7ff | CD Right channel |
| 0x800 - 0xbff | Voice 1 |
| 0xC00 - 0xfff | Voice 3 |
## Compiling
You need [mkpsxiso](https://github.com/Lameguy64/mkpsxiso) in your $PATH to generate a PSX disk image.
Typing
```bash
make
```
in a terminal will compile and generate the bin/cue files.
Typing
```bash
make cleansub
```
will clean the current directory
## More on CDDA
See the [hello_cdda](https://github.com/ABelliqueux/nolibgs_hello_worlds/tree/main/hello_cdda) example in this repo.
## Docs and links
Original psyq example : `psyq/psx/sample/sound/CDVOL, main.c,v 1.14 1997/05/02 13:05:21 by ayako`
## Music credits
Track 1 :
Beach Party by Kevin MacLeod
Link: https://incompetech.filmmusic.io/song/3429-beach-party
License: https://filmmusic.io/standard-license
Track 2:
Funk Game Loop by Kevin MacLeod
Link: https://incompetech.filmmusic.io/song/3787-funk-game-loop
License: https://filmmusic.io/standard-license
================================================
FILE: hello_spu_readback/hello_spu_readback.c
================================================
// SPU readback example
// adapted from PsyQ's sample : psyq/psx/sample/sound/CDVOL, main.c,v 1.14 1997/05/02 13:05:21 by ayako
// Schnappy 11-2021
#include
#include
#include
#include
#include
#include
#include
// CD library
#include
// SPU library
#include
#include "../thirdparty/nugget/common/syscalls/syscalls.h"
#define printf ramsyscall_printf
#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL
#define SCREENXRES 320 // Screen width
#define SCREENYRES 240 + (VMODE << 4) // Screen height : If VMODE is 0 = 240, if VMODE is 1 = 256
#define CENTERX SCREENXRES/2 // Center of screen on x
#define CENTERY SCREENYRES/2 // Center of screen on y
#define MARGINX 0 // margins for text display
#define MARGINY 32
#define FONTSIZE 8 * 7 // Text Field Height
#define OTLEN 8 // Ordering Table Length
// Number of bars
#define BARNUM 2
// Peak cursor width
#define TSIZE 10
// Bar size / 2
#define BSIZE 128
// Top Y coordinate of Left volume bar
#define BARTOP 100
// Bottom Y coordinates of Left volume bar
#define BARBOTTOM ((BARTOP)+5)
// Vertical spacing
#define MARGIN 40
// Bars left coordinates
#define MINBAR CENTERX - BSIZE
// Bars right coordinates
#define MAXBAR ( CENTERX + BSIZE + TSIZE )
// Bars IDs
#define LEFTBAR 0
#define RIGHTBAR 1
// Return absolute value of a number
#define ABS(x) (((x)<0)?(-(x)):(x))
DISPENV disp[2]; // Double buffered DISPENV and DRAWENV
DRAWENV draw[2];
u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes
uint8_t primbuff[2][32768]; // double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes
uint8_t *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0]
uint8_t db = 0; // index of which buffer is used, values 0, 1
// SPU attributes
SpuCommonAttr spuSettings;
// SPU IRQ address
uint16_t SpuIrqAddr;
// SPU decoded data buffer
SpuDecodedData decodedData;
// CD volume: current sample's max values
ulong leftMax, rightMax;
// Last 2 seconds's peak volume values
ulong leftPeak, rightPeak;
// Primitives for drawing the VU-metre, double buffered
// Blue : background bar
POLY_F4 * bar[BARNUM];
// White : current value
POLY_F4 * current[BARNUM];
// Red : volume peak in the last 3 seconds
POLY_F4 * peak[BARNUM];
// Colors for the VU-metre
CVECTOR bg = {20, 10, 0};
CVECTOR fg = {10,200,20};
CVECTOR cursor = {200,40,10};
void init(void)
{
ResetGraph(0); // Initialize drawing engine with a complete reset (0)
InitGeom();
SetGeomOffset(CENTERX,CENTERY);
SetGeomScreen(CENTERX);
SetDefDispEnv(&disp[0], 0, 0 , SCREENXRES, SCREENYRES); // Set display area for both &disp[0] and &disp[1]
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES); // &disp[0] is on top of &disp[1]
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES); // Set draw for both &draw[0] and &draw[1]
SetDefDrawEnv(&draw[1], 0, 0 , SCREENXRES, SCREENYRES); // &draw[0] is below &draw[1]
// Set video mode
if (VMODE){ SetVideoMode(MODE_PAL);}
SetDispMask(1); // Display on screen
setRGB0(&draw[0], 50, 50, 50); // set color for first draw area
setRGB0(&draw[1], 50, 50, 50); // set color for second draw area
draw[0].isbg = 1; // set mask for draw areas. 1 means repainting the area with the RGB color each frame
draw[1].isbg = 1;
PutDispEnv(&disp[db]); // set the disp and draw environnments
PutDrawEnv(&draw[db]);
FntLoad(960, 0); // Load font to vram at 960,0(+128)
// Top bar
FntOpen (MINBAR, BARTOP - 10, 200, 150, 0, 64);
// Bottom bar
FntOpen (MINBAR, BARTOP - 10 + (MARGIN), 200, 150, 0, 64);
// Debug
FntOpen (32, SCREENYRES - 74, SCREENXRES - 64, 64, 0, 200);
}
void display(void)
{
DrawSync(0);
VSync(0);
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
DrawOTag(&ot[db][OTLEN - 1]);
db = !db;
nextpri = primbuff[db];
}
void initPrimitives(void)
{
// Set primitives from primbuff[]
bar[0] = (POLY_F4 *)nextpri;
bar[1] = (POLY_F4 *)nextpri + sizeof(POLY_F4);
current[0] = (POLY_F4 *)nextpri + (sizeof(POLY_F4) * 2);
current[1] = (POLY_F4 *)nextpri + (sizeof(POLY_F4) * 3);
peak[0] = (POLY_F4 *)nextpri + (sizeof(POLY_F4) * 4);
peak[1] = (POLY_F4 *)nextpri + (sizeof(POLY_F4) * 5);
// Set each primitive to their default settings
for (int i = 0; i < BARNUM; i++)
{
// Volume bar background is blue
SetPolyF4 ( bar[i] );
setRGB0 ( bar[i], bg.r,bg.g,bg.b );
setXY4 ( bar[i],
// Top-left
MINBAR, BARTOP + i * MARGIN,
// Top-right
MAXBAR, BARTOP + i * MARGIN,
// Bottom-left
MINBAR, BARBOTTOM + i * MARGIN,
// Bottom-right
MAXBAR, BARBOTTOM + i * MARGIN);
// Current volume is light purple-ish
SetPolyF4 (current[i]);
setRGB0 ( current[i], fg.r,fg.g,fg.b);
setXY4 ( current[i],
MINBAR, BARTOP + i * MARGIN,
MINBAR + TSIZE, BARTOP + i * MARGIN,
MINBAR, BARBOTTOM + i * MARGIN,
MINBAR + TSIZE, BARBOTTOM + i * MARGIN);
// Initialize peak cursor
SetPolyF4 ( peak[i] );
setRGB0 ( peak[i], cursor.r,cursor.g,cursor.b);
setXY4 ( peak[i],
MINBAR, BARTOP + i * MARGIN,
MINBAR + TSIZE, BARTOP + i * MARGIN,
MINBAR, BARBOTTOM + i * MARGIN,
MINBAR + TSIZE, BARBOTTOM + i * MARGIN);
}
}
// Unused - should be called whenever this madness needs to be ended
void terminate(void)
{
// Turn SPU irq off
SpuSetIRQ (SPU_OFF);
// Clear callback functions
SpuSetIRQCallback ((SpuIRQCallbackProc) NULL);
SpuSetTransferCallback ((SpuTransferCallbackProc) NULL);
// Reset SPU settings
spuSettings.mask = (SPU_COMMON_MVOLL |
SPU_COMMON_MVOLR |
SPU_COMMON_CDVOLL |
SPU_COMMON_CDVOLR |
SPU_COMMON_CDMIX
);
spuSettings.mvol.left = 0;
spuSettings.mvol.right = 0;
spuSettings.cd.volume.left = 0;
spuSettings.cd.volume.right = 0;
spuSettings.cd.mix = SPU_OFF;
SpuSetCommonAttr (&spuSettings);
// Stop CD
CdStop ();
// Stop SPU processing
SpuQuit ();
// Re-init display env
ResetGraph (3);
// stop callback processing
StopCallback ();
}
// Print corresponding data for each volume bar
void printDataInfo(void)
{
// We're using 2 streams
FntPrint (0, "L: %04x peak/%04x\n", leftMax, leftPeak);
FntPrint (1, "R: %04x peak/%04x\n", rightMax, rightPeak);
FntFlush (0);
FntFlush (1);
}
// SPU IRQ calback function
void eachIRQ (void)
{
SpuSetIRQ (SPU_OFF); /**/
SpuReadDecodeData (&decodedData, SPU_CDONLY); /**/
}
// DMA Transfer callback function
void eachDMA (void)
{
if (SpuIrqAddr == 0x0)
SpuIrqAddr = 0x200;
else
SpuIrqAddr = 0x0;
// Change IRQ address
SpuSetIRQAddr (SpuIrqAddr);
// Turn SPU IRQ requests on
SpuSetIRQ (SPU_ON);
}
void findSampleMaxVolume(void)
{
// Search maximum volume value of the SPU decoded data
// SPU buffer data range adresses
long dataLowerAdress, dataUpperAdress;
// Current sample's max and working value
short maxL = 0, tmpL;
short maxR = 0, tmpR;
// Timers for the Peak cursor, reset after 120 iterations.
static long timeCursorL = 0, timeCursorR = 0;
// Find SPU data range according to current half we're working on
if (SpuIrqAddr == 0x0) {
/* 1st part is available */
dataLowerAdress = 0x0;
dataUpperAdress = 0x1ff;
} else {
/* 2nd part is available */
dataLowerAdress = 0x200;
dataUpperAdress = 0x3ff;
}
// Examine and find max volume in the data range
for (long i = dataLowerAdress; i < dataUpperAdress; i ++) {
// Examine SPU decoded data
tmpL = ABS(decodedData.cd_left[i]);
tmpR = ABS(decodedData.cd_right[i]);
// Only keep maximum value for this sample
if (maxL < tmpL ) {
maxL = tmpL ;
}
if (maxR < tmpR ) {
maxR = tmpR;
}
}
leftMax = (long) maxL;
rightMax = (long) maxR;
// Peak level
if (leftPeak < leftMax) {
leftPeak = leftMax;
timeCursorL = 0;
}
if (rightPeak < rightMax) {
rightPeak = rightMax;
timeCursorR = 0;
}
// Peak cursors: hold 2s@60fps.
// Increment counters until 120 is reached, then set cursors position to current leftMax/rightMax values
if (timeCursorL < 120) {
timeCursorL ++;
} else {
timeCursorL = 0;
leftPeak = leftMax;
}
if (timeCursorR < 120) {
timeCursorR ++;
} else {
timeCursorR = 0;
rightPeak = rightMax;
}
}
int main(void)
{
// Values used to switch CD track
u_int counter = 0;
int8_t flip = 1;
// These will hold the normalised values of leftMax/rightMax, leftPeak/rightPeak
long lMax, rMax, lPeak, rPeak;
// Init display
init();
// Init CD system
CdInit ();
// Init Spu
SpuInit();
// Initialize SPU related variables
leftMax = rightMax = 0;
leftPeak = rightPeak = 0;
// Fill SPU data buffers with 0s
for (int i = 0; i < SPU_DECODEDDATA_SIZE; i ++) {
decodedData.cd_left[i] = 0;
decodedData.cd_right[i] = 0;
}
// SPU setup
// Set master & CD volume to max
spuSettings.mask = (SPU_COMMON_MVOLL |
SPU_COMMON_MVOLR |
SPU_COMMON_CDVOLL |
SPU_COMMON_CDVOLR |
SPU_COMMON_CDMIX);
// Master volume should be in range 0x0000 - 0x3fff
spuSettings.mvol.left = 0x3fff;
spuSettings.mvol.right = 0x3fff;
// Cd volume should be in range 0x0000 - 0x7fff
spuSettings.cd.volume.left = 0x7fff;
spuSettings.cd.volume.right = 0x7fff;
// Enable CD input ON
spuSettings.cd.mix = SPU_ON;
// Apply settings
SpuSetCommonAttr(&spuSettings);
// Set transfer mode
SpuSetTransferMode(SPU_TRANSFER_BY_DMA);
// Callbacks setup
// Set Transfer callback
(void) SpuSetTransferCallback ((SpuTransferCallbackProc) eachDMA);
// set IRQ callback
SpuSetIRQCallback ((SpuIRQCallbackProc) eachIRQ);
// Initialize SPU IRQ address
SpuIrqAddr = 0x200;
// Set IRQ address
SpuSetIRQAddr (SpuIrqAddr);
// Turn interrupt request ON
SpuSetIRQ(SPU_ON);
// CD Playback setup
// Play second audio track
// Get CD TOC
CdlLOC loc[100];
int ntoc;
while ((ntoc = CdGetToc(loc)) == 0) { /* Read TOC */
printf("No TOC found: please use CD-DA disc...\n");
FntPrint(2, "No TOC found: please use CD-DA disc...\n");
}
// Prevent out of bound pos
for (int i = 1; i < ntoc; i++) {
CdIntToPos(CdPosToInt(&loc[i]) - 74, &loc[i]);
}
// Those array will hold the return values of the CD commands
u_char param[4], result[8];
// Set CD parameters ; Report Mode ON, CD-DA ON. See LibeOver47.pdf, p.188
param[0] = CdlModeRept|CdlModeDA;
// Set CD mode
CdControlB (CdlSetmode, param, 0);
// Wait 3 vsync
VSync (3);
// Play second track in toc array
CdControlB (CdlPlay, (u_char *)&loc[3], 0);
// Graphics setup
initPrimitives();
while (1)
{
counter++;
ClearOTagR(ot[db], OTLEN);
// Normalize volume
lMax = (leftMax * 256) / 0x8000 + MINBAR;
rMax = (rightMax * 256) / 0x8000 + MINBAR;
lPeak = (leftPeak * 256) / 0x8000 + MINBAR;
rPeak = (rightPeak * 256) / 0x8000 + MINBAR;
// Update primitives XY coordinates
// Set coordinates for volume bar polygons
setXY4 ( current[LEFTBAR],
MINBAR, BARTOP,
lMax + TSIZE, BARTOP,
MINBAR, BARBOTTOM,
lMax + TSIZE, BARBOTTOM);
setXY4 (current[RIGHTBAR],
MINBAR, BARTOP + MARGIN,
rMax + TSIZE, BARTOP + MARGIN,
MINBAR, BARBOTTOM + MARGIN,
rMax + TSIZE, BARBOTTOM + MARGIN);
// Set coordinates for peak cursor polygons
setXY4 (peak[LEFTBAR],
lPeak, BARTOP,
lPeak + TSIZE, BARTOP,
lPeak, BARBOTTOM,
lPeak + TSIZE, BARBOTTOM);
setXY4 (peak[RIGHTBAR],
rPeak, BARTOP + MARGIN,
rPeak + TSIZE, BARTOP + MARGIN,
rPeak, BARBOTTOM + MARGIN,
rPeak + TSIZE, BARBOTTOM + MARGIN);
// Add prims to ordering table from bottom to top
for ( int i = 0; i < BARNUM; i++) {
addPrim(ot[db][OTLEN - 1], bar[i]);
addPrim(ot[db][OTLEN - 2], current[i]);
addPrim(ot[db][OTLEN - 3], peak[i]);
}
// Get current track number ~ every second
// See LibeOver47.pdf, p.188
if (counter%50 == 0){
CdReady(1, &result[0]);
// current track number can also be obtained with
// CdControlB (CdlGetlocP, 0, &result[0]);
}
// Switch track after ~ 20 seconds
if (counter%(50*20) == 0){
// Flip can have a value of 1 or -1
flip *= -1;
uint8_t nextTrackIndex = result[1] + flip;
// Send CD command to switch track
CdControlB (CdlPlay, (u_char *)&loc[ nextTrackIndex ], 0);
}
// Update current and peak values
findSampleMaxVolume();
// Print bar's infos
printDataInfo();
// Draw debug stream
FntPrint(2, "Hello SPU readback ! %d\n", counter);
FntPrint(2, "Current track: %d\n", result[1] );
FntPrint(2, "L: %08d, R: %08d\n", leftMax, rightMax);
FntPrint(2, "SPU Addr: 0x%03x ", SpuIrqAddr );
FntFlush(2);
// Update display
display();
}
return 0;
}
================================================
FILE: hello_spu_readback/isoconfig.xml
================================================
================================================
FILE: hello_spu_readback/system.cnf
================================================
BOOT=cdrom:\SCES_313.37;1
TCB=4
EVENT=10
STACK=801FFFF0
================================================
FILE: hello_str/Makefile
================================================
.PHONY: all cleansub
all:
mkpsxiso -y ./isoconfig.xml
cleansub:
$(MAKE) clean
rm -f hello_str.cue hello_str.bin
TARGET = hello_str
SRCS = hello_str.c \
include ../common.mk
================================================
FILE: hello_str/README.md
================================================
This example will play a fullscreen STR file and is as straightforward as possible. If you need more advanced control other the display size and position, see the [STR playback library](https://github.com/ABelliqueux/nolibgs_hello_worlds/tree/main/hello_str#str-playback-library) section.
## Compiling
You need [mkpsxiso](https://github.com/Lameguy64/mkpsxiso) in your $PATH to generate a PSX disk image.
Typing
```bash
make
```
in a terminal will compile and generate the bin/cue files.
Typing
```bash
make cleansub
```
will clean the current directory.
## STR playback library
@Lameguy64 has spent some time making a STR playback library that's easily included in a project :
> One thing that I find somewhat missing here is a decent piece of code for playing STR video files easily. So, what I did was take the old and messy PsyQ STR player example, clean it up entirely, and finally make it into a cute little c library for easy implementation.
Original post : http://www.psxdev.net/forum/viewtopic.php?t=507
Original download link : https://www.mediafire.com/download/s61u86sxd1djncy/strplay.7z
Mirror : http://psx.arthus.net/code/strplay.7z
## Converting to AVI
You need `AVI file 320x240, 15 fps, 24-bit color, Stereo 16-bit sound @ 44100 Hz`.
```
Stream #0:0: Video: rawvideo, bgr24, 320x240, 27763 kb/s, 15 fps, 15 tbr, 15 tbn, 15 tbc
Stream #0:1: Audio: pcm_u8 ([1][0][0][0] / 0x0001), 44100 Hz, 2 channels, u8, 705 kb/s
```
### Video to AVI
Use Virtualdub or ffmpeg :
```bash
ffmpeg -i $INPUT.MKV -vcodec rawvideo -pix_fmt bgr24 -vf scale=320:240,setsar=1:1 -acodec pcm_u8 -ar 44100 -r 15 $OUTPUT.avi
```
### AVI to STR
Use [`MC32.EXE`](http://psx.arthus.net/tools/pimp-psx.zip) to convert the AVI file to STR using these settings :
```
Format : Input : Avi (Uncompressed), Output : str (MDEC)
Sound: 37.8 KHz, Stereo;
Easy: Double Speed, 15 fps, 1ch, Leap Sector;
MDEC: version 2
```

**If `MC32.exe` crashes when hitting the 'Go' button, you have to open the ffmpeg AVI file in virtualdub, then save it again ; `File > Save as AVI...` or `F7` key, then retry.**
You should now have a STR file and a XA file that you have to interleave in `MC32`:
```
Format : Input : str (MDEC), Output: str (MDEC)
Sound: Input: XA , 37.8 KHz, Stereo;
Frame rate: 15 fps, # Channels : 1(150sectors/s), Leap Sector;
CD-ROM speed : Double Speed;
```

### Finding a video's frame count
With `ffmpeg` :
```bash
ffprobe -v error -select_streams v:0 -count_packets -show_entries stream=nb_read_packets -of csv=p=0 VIDEOFILE.AVI
```
Alternatively, open the STR file in `MC32.exe` and look at the bottom left of the window.
### Tools & Refs
MC32 : http://psx.arthus.net/tools/pimp-psx.zip
STR converter : http://psx.arthus.net/tools/str_converter.rar
Original PsyQ sample code :
```
/psyq/psx/sample/scee/CD/MOVIE2
/psyq/addons/cd/MOVIE (same as /psx/sample/cd/MOVIE )
/addons/sound/STREAM/TUTO2.C
```
Original post : http://www.psxdev.net/forum/viewtopic.php?t=507
Video to STR conversion tutorial : http://www.psxdev.net/forum/viewtopic.php?f=51&t=277
MDEC notes : http://psx.arthus.net/sdk/Psy-Q/DOCS/TECHNOTE/mdecnote.pdf
## Video credits
The video and song used in this example are by Nina Paley : https://archive.org/details/CopyingIsNotTheft-ScratchTrack1280X720Hdv
================================================
FILE: hello_str/hello_str.c
================================================
// Stream a STR file from CD, decompress and play it.
// Schnappy 07-2021
// based on Lameguy64 strplay library : http://www.psxdev.net/forum/viewtopic.php?t=507
// Original PsyQ sample code : /psyq/addons/cd/MOVIE
// Video to STR conversion : https://github.com/ABelliqueux/nolibgs_hello_worlds/tree/main/hello_str
#include
#include
#include
#include
#include
#include
// CD library
#include
// CODEC library
#include
#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL
#define TRUECOL 0 // 0 : 16bpp, 1: 24bpp
#define SCREENXRES 320 // Screen width
#define SCREENYRES 240 + (VMODE << 4) // Screen height : If VMODE is 0 = 240, if VMODE is 1 = 256
#define CENTERX SCREENXRES/2 // Center of screen on x
#define CENTERY SCREENYRES/2 // Center of screen on y
#define MARGINX 8 // margins for text display
#define MARGINY 16
#define FONTSIZE 8 * 7 // Text Field Height
DISPENV disp[2]; // Double buffered DISPENV and DRAWENV
DRAWENV draw[2];
short db = 0; // index of which buffer is used, values 0, 1
#define STR_POS_X 0
// If PAL mode, add 8 pixels offset on Y (256-240)/2
#define STR_POS_Y (VMODE << 4)/2
// Ring Buffer size (32 sectors seems good enough)
#define RING_SIZE 32
#if TRUECOL
// pixels per short word (16b/2B)
// 1px is 3B in 24bpp
// 1px is 2B(one word) in 16bpp
// therefore 2B will hold 3/2 pixels
#define PPW 3/2
// DCT mode - bit 0 : depth (0 = 16b, 1 = 24b), bit 1: in 16b mode, set STP(Semi-Transparency) bit 15.
// 24bpp = 01b => 1
#define DCT_MODE 1
#else
#define PPW 1
// 16bpp = 10b => 2
#define DCT_MODE 2
#endif
// Stop playback if set
static int endPlayback = 0;
// STR file infos : Filename on CD, Width, Height, Length in frames
static char * StrFileName = "\\COPY.STR;1";
static int StrFileX = 320;
static int StrFileY = 240;
static int StrFileLength = 893;
// When using RGB24, a special routine has to be setup as a callback function to avoid MDEC/CDROM conflict
// See http://psx.arthus.net/sdk/Psy-Q/DOCS/LibRef47.pdf , p.713
static void strCheckRGB24();
void init(void)
{
ResetCallback();
ResetGraph(0); // Initialize drawing engine with a complete reset (0)
SetDefDispEnv(&disp[0], 0, 0 , SCREENXRES, SCREENYRES); // Set display area for both &disp[0] and &disp[1]
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES); // &disp[0] is on top of &disp[1]
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES); // Set draw for both &draw[0] and &draw[1]
SetDefDrawEnv(&draw[1], 0, 0 , SCREENXRES, SCREENYRES); // &draw[0] is below &draw[1]
// Set video mode
#if VMODE
SetVideoMode(MODE_PAL);
disp[0].disp.y = 8;
disp[1].disp.y = 8;
#endif
SetDispMask(1); // Display on screen
setRGB0(&draw[0], 155, 0, 150); // set color for first draw area
setRGB0(&draw[1], 155, 0, 150); // set color for second draw area
draw[0].isbg = 0; // set mask for draw areas. 1 means repainting the area with the RGB color each frame
draw[1].isbg = 0;
#if TRUECOL
disp[0].isrgb24 = 1;
disp[1].isrgb24 = 1;
#endif
PutDispEnv(&disp[db]); // set the disp and draw environnments
PutDrawEnv(&draw[db]);
FntLoad(960, 0); // Load font to vram at 960,0(+128)
FntOpen(MARGINX, MARGINY, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); // FntOpen(x, y, width, height, black_bg, max. nbr. chars
}
void display(void)
{
DrawSync(0); // Wait for all drawing to terminate
VSync(0); // Wait for the next vertical blank
PutDispEnv(&disp[db]); // set alternate disp and draw environnments
PutDrawEnv(&draw[db]);
db = !db; // flip db value (0 or 1)
}
int main() {
// CD File descriptor
CdlFILE STRfile;
// Parameter we want to set the CDROM with
u_char param=CdlModeSpeed;
// Buffers total ~110KB in memory
// SECTOR_SIZE is defined in words : 512 * 4 == 2048 Bytes/sector
// Ring buffer : 32 * 2048 = 65536 Bytes
u_long RingBuff[ RING_SIZE * SECTOR_SIZE ];
// VLC buffers : display area in words (hence /2), 160*320 == 38400 Bytes
u_long VlcBuff[2][ SCREENXRES / 2 * StrFileY ];
// If using 16bpp, fetch 16xYres strips, if 24bpp, fetch 24xYres strips, 5120*PPW Bytes
u_short ImgBuff[2][ 16 * PPW * StrFileY];
u_long * curVLCptr = VlcBuff[db];
// Init Disp/Draw env, Font, etc.
init();
SpuInit();
// Init CDrom system
CdInit();
// Reset the MDEC
DecDCTReset(0);
// Set callback routine
DecDCToutCallback(strCheckRGB24);
// Set ring buffer
StSetRing(RingBuff, RING_SIZE);
// Set streaming parameters
StSetStream(TRUECOL, 1, StrFileLength, 0, 0);
// Get the CD location of the STR file to play
if ( CdSearchFile(&STRfile, StrFileName) == 0 ) {
FntPrint("File not found :%s\n", StrFileName);
}
// Set the seek target position
CdControl(CdlSetloc, (u_char *)&STRfile.pos, 0);
// Set CD mode to CdlModeSpeed
CdControl(CdlSetmode, ¶m, 0);
// Read from CD at position &STRfile.pos
// Enable streaming, double speed and ADPCM playback
CdRead2(CdlModeStream|CdlModeSpeed|CdlModeRT);
// Use a counter to avoid deadlocks
int wait = WAIT_TIME;
// Next Ring Buffer Frame address
u_long * nextFrame = 0;
// Ring buffer frame address
u_long * frameAddr = 0;
// Ring buffer frame header
StHEADER * sectorHeader;
// Main loop
while (1) {
// Set some pointers to the relevant buffer addresses
u_long * curVLCptr = &VlcBuff[!db][0];
u_short * curIMGptr = &ImgBuff[db][0];
// While end of str is not reached, play it
while (!endPlayback) {
// Use this area to draw the slices
RECT curSlice = { STR_POS_X,
(db * StrFileY) + STR_POS_Y,
// In 24bpp, use 24 pixels wide slices
16 * PPW ,
StrFileY};
int frameDone = 0;
// Reset counter
wait = WAIT_TIME;
// Dont try decoding if not data has been loaded from ring buffer
if ( frameAddr ){
// Begin decoding RLE-encoded MDEC image data
DecDCTin( curVLCptr , DCT_MODE);
// Prepare to receive the decoded image data from the MDEC
while (curSlice.x < STR_POS_X + StrFileX * PPW) {
// Receive decoded data : a 16*ppw*240 px slice in long word (4B), so / 2
DecDCTout( (u_long *) curIMGptr, curSlice.w * curSlice.h / 2);
// Wait for transfer end
DecDCToutSync(0);
// Transfer data from main memory to VRAM
LoadImage(&curSlice, (u_long *) curIMGptr );
// Wait for drawing termination
DrawSync(0);
// Increment drawArea's X with slice width (16 or 24 pix)
curSlice.x += 16 * PPW;
}
// Set frameDone flag to 1
frameDone = 1;
curSlice.x = STR_POS_X;
curSlice.y = (db * StrFileY) + STR_POS_Y;
}
// Get one frame of ring buffer data
// StGetNext is non-blocking, so we wait for it to return 0.
// StGetNext will lock the region at &frameAddr until StFreeRing() is called.
while ( StGetNext((u_long **)&frameAddr,(u_long **)§orHeader) ) {
wait--;
if (wait == 0)
break;
}
// If the current frame's number is bigger than the number of frames in STR,
// set the endPlayback flag.
if (sectorHeader->frameCount >= StrFileLength)
endPlayback = 1;
// Grab a frame from the stream
wait = WAIT_TIME;
while ((nextFrame = frameAddr) == 0) {
wait--;
if ( wait == 0 ){
break;
}
}
// Decode the Huffman/VLC compressed data
DecDCTvlc(nextFrame, &VlcBuff[!db][0]);
// Unlock area obtained by StGetNext()
StFreeRing(nextFrame);
// Reset counter
wait = WAIT_TIME;
// Wait until the whole frame is loaded to VRAM
while ( frameDone == 0 ) {
wait--;
if ( wait == 0 ) {
// If a timeout occurs, force switching buffers
frameDone = 1;
curSlice.x = STR_POS_X;
curSlice.y = (db * StrFileY) + STR_POS_Y;
}
}
FntFlush(-1);
display();
}
// Disable callback
DecDCToutCallback(0);
// Release two interrupt functions CdDataCallback() and CdReadyCallback() hooked by CDRead2()
StUnSetRing();
// Put CDROM on pause at current position
CdControlB(CdlPause, 0, 0);
}
return 0;
};
static void strCheckRGB24() {
/* From http://psx.arthus.net/sdk/Psy-Q/DOCS/, p.713
* When playing a movie in 24-bit mode, there is a potential hardware conflict between the CD subsystem
* and the MDEC image decompression system which can result in corrupted data. To avoid this,
* StCdInterrupt() may defer transferring a sector and instead set a flag variable called StCdInterFlag to
* indicate that a CD sector is ready to be transferred. Once the MDEC is finished transferring data, your
* application should check StCdIntrFlag and call StCdInterrupt() directly if it is set.
*/
#if TRUECOL
extern int StCdIntrFlag;
// If flag was set
if ( StCdIntrFlag ) {
// Trigger data transfer
StCdInterrupt();
// Reset flag
StCdIntrFlag = 0;
}
#endif
}
================================================
FILE: hello_str/isoconfig.xml
================================================
================================================
FILE: hello_str/str/copyings.str
================================================
[File too large to display: 19.9 MB]
================================================
FILE: hello_str/system.cnf
================================================
BOOT=cdrom:\SCES_313.37;1
TCB=4
EVENT=10
STACK=801FFFF0
================================================
FILE: hello_strplay/Makefile
================================================
.PHONY: all cleansub
all:
mkpsxiso -y ./isoconfig.xml
cleansub:
$(MAKE) clean
rm -f hello_strplay.cue hello_strplay.bin
TARGET = hello_strplay
SRCS = hello_strplay.c \
include ../common.mk
================================================
FILE: hello_strplay/README.md
================================================
This example show how to use Lameguy64's [STR playback library](https://github.com/ABelliqueux/nolibgs_hello_worlds/tree/main/hello_str#str-playback-library).
For a barebone example, see the [hello_str](https://github.com/ABelliqueux/nolibgs_hello_worlds/tree/main/hello_str) example.
## Compiling
You need [mkpsxiso](https://github.com/Lameguy64/mkpsxiso) in your $PATH to generate a PSX disk image.
Typing
```bash
make
```
in a terminal will compile and generate the bin/cue files.
Typing
```bash
make cleansub
```
will clean the current directory.
## STR playback library
@Lameguy64 has spent some time making a STR playback library that's easily included in a project :
> One thing that I find somewhat missing here is a decent piece of code for playing STR video files easily. So, what I did was take the old and messy PsyQ STR player example, clean it up entirely, and finally make it into a cute little c library for easy implementation.
Original post : http://www.psxdev.net/forum/viewtopic.php?t=507
Original download link : https://www.mediafire.com/download/s61u86sxd1djncy/strplay.7z
Mirror : http://psx.arthus.net/code/strplay.7z
## Video encoding and more informations
See the [wiki](https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/STR).
## Video credits
The video and song used in this example are by Nina Paley : https://archive.org/details/CopyingIsNotTheft-ScratchTrack1280X720Hdv
================================================
FILE: hello_strplay/hello_strplay.c
================================================
// Using the strplay library.
// Schnappy 07-2021
// based on Lameguy64 strplay library : http://www.psxdev.net/forum/viewtopic.php?t=507
// Original PsyQ sample code : /psyq/addons/cd/MOVIE
// Video to STR conversion : https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/STR
#include
#include
#include
#include
#include
// CD library
#include
// CODEC library
#include
// include Lameguy64's library
#include "strplay.c"
#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL
#define TRUECOL 1 // 0 : 16bpp, 1: 24bpp
#define SCREENXRES 320 // Screen width
#define SCREENYRES 240 + (VMODE << 4) // Screen height : If VMODE is 0 = 240, if VMODE is 1 = 256
#define CENTERX SCREENXRES/2 // Center of screen on x
#define CENTERY SCREENYRES/2 // Center of screen on y
#define MARGINX 8 // margins for text display
#define MARGINY 16
#define FONTSIZE 8 * 7 // Text Field Height
DISPENV disp[2]; // Double buffered DISPENV and DRAWENV
DRAWENV draw[2];
short db = 0; // index of which buffer is used, values 0, 1
STRFILE StrFile[] = {
// File name Resolution Frame count
"\\COPY.STR;1", 320, 240, 893
};
// When using RGB24, a special routine has to be setup as a callback function to avoid MDEC/CDROM conflict
// See http://psx.arthus.net/sdk/Psy-Q/DOCS/LibRef47.pdf , p.713
static void strCheckRGB24();
void init(void)
{
ResetCallback();
ResetGraph(0); // Initialize drawing engine with a complete reset (0)
SetDefDispEnv(&disp[0], 0, 0 , SCREENXRES, SCREENYRES); // Set display area for both &disp[0] and &disp[1]
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES); // &disp[0] is on top of &disp[1]
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES); // Set draw for both &draw[0] and &draw[1]
SetDefDrawEnv(&draw[1], 0, 0 , SCREENXRES, SCREENYRES); // &draw[0] is below &draw[1]
// Set video mode
#if VMODE
SetVideoMode(MODE_PAL);
disp[0].disp.y = 8;
disp[1].disp.y = 8;
#endif
SetDispMask(1); // Display on screen
setRGB0(&draw[0], 155, 0, 150); // set color for first draw area
setRGB0(&draw[1], 155, 0, 150); // set color for second draw area
draw[0].isbg = 0; // set mask for draw areas. 1 means repainting the area with the RGB color each frame
draw[1].isbg = 0;
#if TRUECOL
disp[0].isrgb24 = 1;
disp[1].isrgb24 = 1;
#endif
PutDispEnv(&disp[db]); // set the disp and draw environnments
PutDrawEnv(&draw[db]);
FntLoad(960, 0); // Load font to vram at 960,0(+128)
FntOpen(MARGINX, MARGINY, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); // FntOpen(x, y, width, height, black_bg, max. nbr. chars
}
void display(void)
{
DrawSync(0); // Wait for all drawing to terminate
VSync(0); // Wait for the next vertical blank
PutDispEnv(&disp[db]); // set alternate disp and draw environnments
PutDrawEnv(&draw[db]);
db = !db; // flip db value (0 or 1)
}
void main() {
// Reset and initialize stuff
init();
CdInit();
PadInit(0);
// Play the video in loop
while (1) {
if (PlayStr(320, 240, 0, 0, &StrFile[0]) == 0) // If player presses Start
break; // Exit the loop
}
}
static void strCheckRGB24() {
/* From http://psx.arthus.net/sdk/Psy-Q/DOCS/, p.713
* When playing a movie in 24-bit mode, there is a potential hardware conflict between the CD subsystem
* and the MDEC image decompression system which can result in corrupted data. To avoid this,
* StCdInterrupt() may defer transferring a sector and instead set a flag variable called StCdInterFlag to
* indicate that a CD sector is ready to be transferred. Once the MDEC is finished transferring data, your
* application should check StCdIntrFlag and call StCdInterrupt() directly if it is set.
*/
#if TRUECOL
extern u_long StCdIntrFlag;
// If flag was set
if ( StCdIntrFlag ) {
// Trigger data transfer
StCdInterrupt();
// Reset flag
StCdIntrFlag = 0;
}
#endif
}
================================================
FILE: hello_strplay/isoconfig.xml
================================================
================================================
FILE: hello_strplay/str/copyings.str
================================================
[File too large to display: 19.9 MB]
================================================
FILE: hello_strplay/strplay.c
================================================
/*
Simple STR Player Library by Lameguy64
(?) 2014 Meido-Tek Productions/Lame Studios
Original PsyQ sample programmed by:
Yutaka
Suzu
Masa
Ume
Code heavily refined by:
Lameguy64
What Lameguy did to the original code:
- Removed all of the icky yucky UTF-16 junk
- Fixed all crap-English comments
- Greatly improved code formatting
- Renamed variables with better names
- Buffer arrays are now initialized only when the playback routine is called
Libraries Required:
libetc
libgte
libgpu
libcd
Function list:
int PlayStr(int xres, int yres, int xpos, int ypos, STRFILE *str)
Parameters:
xres, yres - Video resolution.
xpos, ypos - Framebuffer offset on where to draw the video.
STRFILE *str - STRFILE entry to play.
Notes:
Just make sure that you have at least 192KB of free memory before calling
the PlayStr function otherwise, the console will crash. As for the video
resolution, it must be equal or less than 256 as the second buffer
is located directly below the first buffer.
Note:
If compiling the sample fails, open your psyq.ini file located in \psyq\bin and
append the following into the stdlib line:
libds.lib libpress.lib
*/
#define IS_RGB24 1 // 0:16-bit playback, 1:24-bit playback (recommended for quality)
#define RING_SIZE 32 // Ring Buffer size (32 sectors seems good enough)
#if IS_RGB24==1
#define PPW 3/2 // pixels per short word
#define DCT_MODE 3 // Decode mode for DecDCTin routine
#else
#define PPW 1
#define DCT_MODE 2
#endif
// A simple struct to make STR handling a bit easier
typedef struct {
char FileName[32];
int Xres;
int Yres;
int NumFrames;
} STRFILE;
// Decode environment
typedef struct {
u_long *VlcBuff_ptr[2]; // Pointers to the VLC buffers
u_short *ImgBuff_ptr[2]; // Pointers to the frame slice buffers
RECT rect[2]; // VRAM parameters on where to draw the frame data to
RECT slice; // Frame slice parameters for loading into VRAM
int VlcID; // Current VLC buffer ID
int ImgID; // Current slice buffer ID
int RectID; // Current video buffer ID
int FrameDone; // Frame decode completion flag
} STRENV;
// A bunch of internal variables
static STRENV strEnv;
static int strScreenWidth=0,strScreenHeight=0;
static int strFrameX=0,strFrameY=0;
static int strNumFrames=0;
static int strFrameWidth=0,strFrameHeight=0; // Frame size of STR file
static int strPlayDone=0; // Playback completion flag
// Main function prototypes
int PlayStr(int xres, int yres, int xpos, int ypos, STRFILE *str);
// Internal function prototypes
static void strDoPlayback(STRFILE *str);
static void strCallback();
static void strNextVlc(STRENV *strEnv);
static void strSync(STRENV *strEnv, int mode);
static u_long *strNext(STRENV *strEnv);
static void strKickCD(CdlLOC *loc);
int PlayStr(int xres, int yres, int xpos, int ypos, STRFILE *str) {
/*
Main STR playback routine.
Returns:
0 - Playback failed or was skipped.
1 - Playback was finished.
*/
strNumFrames=str->NumFrames;
strScreenWidth=xres;
strScreenHeight=yres;
strFrameX=xpos;
strFrameY=ypos;
strPlayDone=0;
strDoPlayback(str);
if (strPlayDone == 0)
return(0);
else
return(1);
}
static void strDoPlayback(STRFILE *str) {
/*
Does the actual STR playback.
*/
int id; // Display buffer ID
DISPENV disp; // Display environment
CdlFILE file; // File info of video file
// Buffers initialized here so we won't waste too much memory for playing FMVs
// (just make sure you have at least 192KB of free memory before calling this routine)
u_long RingBuff[RING_SIZE*SECTOR_SIZE]; // Ring buffer
u_long VlcBuff[2][str->Xres/2*str->Yres]; // VLC buffers
u_short ImgBuff[2][16*PPW*str->Yres]; // Frame 'slice' buffers
// Set display mask so we won't see garbage while the stream is being prepared
SetDispMask(0);
// Get the CD location of the STR file to play
if (CdSearchFile(&file, str->FileName) == 0) {
#ifdef DEBUG
printf("ERROR: I cannot find video file %s\n", str->FileName);
#endif
SetDispMask(1);
return;
}
// Setup the buffer pointers
strEnv.VlcBuff_ptr[0] = &VlcBuff[0][0];
strEnv.VlcBuff_ptr[1] = &VlcBuff[1][0];
strEnv.VlcID = 0;
strEnv.ImgBuff_ptr[0] = &ImgBuff[0][0];
strEnv.ImgBuff_ptr[1] = &ImgBuff[1][0];
strEnv.ImgID = 0;
// Setup the display buffers on VRAM
strEnv.rect[0].x = strFrameX; // First page
strEnv.rect[0].y = strFrameY;
strEnv.rect[1].x = strFrameX; // Second page
strEnv.rect[1].y = strFrameY+strScreenHeight;
strEnv.RectID = 0;
// Set the parameters for uploading frame slices
strEnv.slice.x = strFrameX;
strEnv.slice.y = strFrameY;
strEnv.slice.w = 16*PPW;
strEnv.FrameDone = 0;
// Reset the MDEC
DecDCTReset(0);
// Set callback routine
DecDCToutCallback(strCallback);
// Set ring buffer
StSetRing(RingBuff, RING_SIZE);
// Set streaming parameters
StSetStream(IS_RGB24, 1, 0xffffffff, 0, 0);
// Begin streaming!
strKickCD(&file.pos);
// Load the first frame of video before entering main loop
strNextVlc(&strEnv);
while (1) {
// Decode the compressed frame data
DecDCTin(strEnv.VlcBuff_ptr[strEnv.VlcID], DCT_MODE);
// Prepare to receive the decoded image data from the MDEC
DecDCTout((u_long*)strEnv.ImgBuff_ptr[strEnv.ImgID], strEnv.slice.w*strEnv.slice.h/2);
// Get the next frame
strNextVlc(&strEnv);
// Wait for the frame to finish decoding
strSync(&strEnv, 0);
// Switch between the display buffers per frame
id = strEnv.RectID? 0: 1;
SetDefDispEnv(&disp, 0, strScreenHeight*id, strScreenWidth*PPW, strScreenHeight);
// Set parameters for 24-bit color mode
#if IS_RGB24 == 1
disp.isrgb24 = IS_RGB24;
disp.disp.w = disp.disp.w*2/3;
#endif
VSync(0); // VSync to avoid screen tearing
PutDispEnv(&disp); // Apply the video parameters
SetDispMask(1); // Remove the display mask
if(strPlayDone == 1) {
break;
}
if(PadRead(1) & PADstart) { // stop button pressed exit animation routine
break;
}
}
// Shutdown streaming
DecDCToutCallback(0);
StUnSetRing();
CdControlB(CdlPause, 0, 0);
}
static void strCallback() {
/*
Callback routine which is called whenever a slice has finished decoding.
All it does is transfer the decoded slice into VRAM.
*/
RECT TransferRect;
int id;
// In 24-bit color, StCdInterrupt must be called in every callback
#if IS_RGB24==1
extern u_long StCdIntrFlag;
if (StCdIntrFlag) {
StCdInterrupt();
StCdIntrFlag = 0;
}
#endif
id = strEnv.ImgID;
TransferRect = strEnv.slice;
// Switch slice buffers
strEnv.ImgID = strEnv.ImgID? 0:1;
// Step to next slice
strEnv.slice.x += strEnv.slice.w;
// Frame not yet decoded completely?
if (strEnv.slice.x < strEnv.rect[strEnv.RectID].x + strEnv.rect[strEnv.RectID].w) {
// Prepare for next slice
DecDCTout((u_long*)strEnv.ImgBuff_ptr[strEnv.ImgID], strEnv.slice.w*strEnv.slice.h/2);
} else { // Frame has been decoded completely
// Set the FrameDone flag
strEnv.FrameDone = 1;
// Switch display buffers
strEnv.RectID = strEnv.RectID? 0: 1;
strEnv.slice.x = strEnv.rect[strEnv.RectID].x;
strEnv.slice.y = strEnv.rect[strEnv.RectID].y;
}
// Transfer the slice into VRAM
LoadImage(&TransferRect, (u_long *)strEnv.ImgBuff_ptr[id]);
}
static void strNextVlc(STRENV *strEnv) {
/*
Performs VLC decoding and grabs a frame from the stream.
*/
int cnt=WAIT_TIME;
u_long *next;
u_long *strNext();
// Grab a frame from the stream
while ((next = strNext(strEnv)) == 0) {
if (--cnt == 0) // Timeout handler
return;
}
// Switch VLC buffers
strEnv->VlcID = strEnv->VlcID? 0: 1;
// Decode the VLC
DecDCTvlc(next, strEnv->VlcBuff_ptr[strEnv->VlcID]);
// Free the ring buffer
StFreeRing(next);
}
static u_long *strNext(STRENV *strEnv) {
/*
Grabs a frame of video from the stream.
*/
u_long *addr;
StHEADER *sector;
int cnt = WAIT_TIME;
// Grab a frame
while (StGetNext((u_long **)&addr,(u_long **)§or)) {
if (--cnt == 0) // Timeout handler
return(0);
}
// If the frame's number has reached number of frames the video has,
// set the strPlayDone flag.
if (sector->frameCount >= strNumFrames)
strPlayDone = 1;
// if the resolution is differ to previous frame, clear frame buffer
if (strFrameWidth != sector->width || strFrameHeight != sector->height) {
RECT rect;
setRECT(&rect, 0, 0, strScreenWidth * PPW, strScreenHeight*2);
ClearImage(&rect, 0, 0, 0);
strFrameWidth = sector->width;
strFrameHeight = sector->height;
}
// set STRENV according to the data on the STR format
strEnv->rect[0].w = strEnv->rect[1].w = strFrameWidth*PPW;
strEnv->rect[0].h = strEnv->rect[1].h = strFrameHeight;
strEnv->slice.h = strFrameHeight;
return(addr);
}
static void strSync(STRENV *strEnv, int mode) {
/*
Waits for the frame to finish decoding.
*/
u_long cnt = WAIT_TIME;
// Wait for the frame to finish decoding
while (strEnv->FrameDone == 0) {
if (--cnt == 0) { // Timeout handler
// If a timeout occurs, force switching buffers
#ifdef DEBUG
printf("ERROR: A frame cannot be played!\n");
#endif
strEnv->FrameDone = 1;
strEnv->RectID = strEnv->RectID? 0: 1;
strEnv->slice.x = strEnv->rect[strEnv->RectID].x;
strEnv->slice.y = strEnv->rect[strEnv->RectID].y;
}
}
strEnv->FrameDone = 0;
}
static void strKickCD(CdlLOC *loc) {
/*
Begins CD streaming.
*/
u_char param=CdlModeSpeed;
loop:
// Seek to the STR file to play
while (CdControl(CdlSetloc, (u_char *)loc, 0) == 0);
while (CdControl(CdlSetmode, ¶m, 0) == 0);
VSync(3); // Wait for 3 screen cycles before changing drive speed
// Start streaming
if(CdRead2(CdlModeStream|CdlModeSpeed|CdlModeRT) == 0)
goto loop; // If it fails, try again
}
================================================
FILE: hello_strplay/system.cnf
================================================
BOOT=cdrom:\SCES_313.37;1
TCB=4
EVENT=10
STACK=801FFFF0
================================================
FILE: hello_tile/Makefile
================================================
TARGET = hello_tile
SRCS = hello_tile.c \
include ../common.mk
================================================
FILE: hello_tile/hello_tile.c
================================================
// Draw a tile primitive
// Schnappy 2021
// based on Lameguy64's tutorial : http://lameguy64.net/svn/pstutorials/chapter1/2-graphics.html
#include
#include
#include
#include
#include
#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL
#define SCREENXRES 320
#define SCREENYRES 240
#define CENTERX SCREENXRES/2
#define CENTERY SCREENYRES/2
#define MARGINX 0 // margins for text display
#define MARGINY 32
#define FONTSIZE 8 * 7 // Text Field Height
#define OTLEN 8 // Ordering Table Length
DISPENV disp[2]; // Double buffered DISPENV and DRAWENV
DRAWENV draw[2];
u_long ot[2][OTLEN]; // double ordering table of length 8 * 32 = 256 bits / 32 bytes
char primbuff[2][32768];// double primitive buffer of length 32768 * 8 = 262.144 bits / 32,768 Kbytes
char *nextpri = primbuff[0]; // pointer to the next primitive in primbuff. Initially, points to the first bit of primbuff[0]
short db = 0; // index of which buffer is used, values 0, 1
void init(void)
{
ResetGraph(0);
SetDefDispEnv(&disp[0], 0, 0, SCREENXRES, SCREENYRES);
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[1], 0, 0, SCREENXRES, SCREENYRES);
if (VMODE)
{
SetVideoMode(MODE_PAL);
disp[0].screen.y += 8;
disp[1].screen.y += 8;
}
SetDispMask(1); // Display on screen
setRGB0(&draw[0], 50, 50, 50);
setRGB0(&draw[1], 50, 50, 50);
draw[0].isbg = 1;
draw[1].isbg = 1;
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
FntLoad(960, 0);
FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 );
}
void display(void)
{
DrawSync(0);
VSync(0);
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
// We're using a reverse OT, so we want to display the last item first. See PsyQ's LibRef47.pdf, p.277
DrawOTag(&ot[db][OTLEN - 1]);
// Comment above line, and uncomment the following line to use a regular oredered OT. Comment l.71 and Uncomment l.73 accordingly
//~ DrawOTag(ot[db]);
db = !db;
nextpri = primbuff[db];
}
int main(void)
{
// These two tiles are added at the same OT index
TILE * blue_tile;
TILE * pink_tile;
// This one is added at a different OT index
TILE * yellow_tile;
init();
while (1)
{
// Initialize the reversed ordering table. This means the elements at index OTLEN - 1 is drawn first.
ClearOTagR(ot[db], OTLEN);
// Use regular order OT, uncomment l.53 accordingly
//~ ClearOTag(ot[db], OTLEN);
// yellow_tile is before pink and blue tile in the code,
// and it displays behind because it is added to a different ot index (od[db] + OTLEN - 1)
// Using a Regular or Reverse OT will have an effect on drawing order. (See lines 53 and 73)
yellow_tile = (TILE * ) nextpri; // yellow_tile is a pointer to primbuf content at adress nextpri, that's cast (type converted) to a TILE struc.
setTile(yellow_tile); // initialize the TILE structure ( fill the length and tag(?) value )
setXY0(yellow_tile, CENTERX - 32 , CENTERY - 48); // Set X,Y
setWH(yellow_tile, 128, 40); // Set Width, Height
setRGB0(yellow_tile, 255, 255, 0); // Set color
addPrim(ot[db][OTLEN - 1], yellow_tile); // Add primitive to ordering table
nextpri += sizeof(TILE);
// blue_tile added at od[db] + OTLEN - 2
blue_tile = (TILE * ) nextpri; // blue_tile is a pointer to primbuf content at adress nextpri, that's cast (type converted) to a blue_tile struc.
setTile(blue_tile); // initialize the blue_tile structure ( fill the length and tag(?) value )
setXY0(blue_tile, CENTERX - 16, CENTERY - 32); // Set X,Y
setWH(blue_tile, 32, 64); // Set Width, Height
setRGB0(blue_tile, 60, 180, 255); // Set color
addPrim(ot[db][OTLEN - 2], blue_tile); // Add primitive to ordering table
nextpri += sizeof(TILE); // Increment the adress nextpri points to by the size of TILE struct
// pink_tile is after blue_tile in the code,
// so it is drawn before, thus under blue_tile.
// However, it is added at the same ot index (od[db] + OTLEN - 2)
// so using a Regular or Reverse OT won't have an effect on drawing order.
pink_tile = (TILE * ) nextpri; // pink_tile is a pointer to primbuf content at adress nextpri, that's cast (type converted) to a TILE struc.
setTile(pink_tile); // initialize the TILE structure ( fill the length and tag(?) value )
setXY0(pink_tile, CENTERX, CENTERY - 64); // Set X,Y
setWH(pink_tile, 64, 64); // Set Width, Height
setRGB0(pink_tile, 255, 32, 255); // Set color
addPrim(ot[db][OTLEN - 2], pink_tile); // Add primitive to ordering table
nextpri += sizeof(TILE);
FntPrint("Hello tile !");
FntFlush(-1);
display();
}
return 0;
}
================================================
FILE: hello_vag/Makefile
================================================
TARGET = hello_vag
SRCS = hello_vag.c \
../VAG/hello_poly.vag
include ../common.mk
================================================
FILE: hello_vag/README.md
================================================
See here for more informations about the VAG fileformat and tools :
https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/VAG
================================================
FILE: hello_vag/hello_vag.c
================================================
// VAGDEMO2020 by Schnappy
// December 2020
// Based on VAGDEMO_FIXED by Yagotzirck
// Based on VAGDEMO by Shadow
// based on psyq/addons/sound/TUTO3.C
//
//
// Load a VAG file to SPU sound buffer and play it back.
//
// WAV creation: use ffmpeg to create a 16-bit ADPCM mono WAV file - change -ar to reduce filesize (and quality)
// $ ffmpeg -i input.mp3 -acodec pcm_s16le -ac 1 -ar 44100 output.wav
//
// WAV to VAG convertion using WAV2VAG : https://github.com/ColdSauce/psxsdk/blob/master/tools/wav2vag.c
// change -freq according to the -ar setting above
// $ wav2vag input.wav output.vag -sraw16 -freq=44100 (-L)
//
// Alternatively, you can use PsyQ VAGEDIT.EXE to change the sampling frequency of an existing VAG file.
//
// Docs : see libformat47.pdf p.209
// libover47.pdf, p.271
// libref47.pdf, p.980
// URLS : http://psx.arthus.net/code/VAG/
#include
#include
#include
#include
#include
// Sound system
#include
#include
#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL
#define SCREENXRES 320
#define SCREENYRES 240
#define CENTERX SCREENXRES/2
#define CENTERY SCREENYRES/2
#define MARGINX 0 // margins for text display
#define MARGINY 32
#define FONTSIZE 8 * 7 // Text Field Height
DISPENV disp[2]; // Double buffered DISPENV and DRAWENV
DRAWENV draw[2];
short db = 0; // index of which buffer is used, values 0, 1
// Sound stuff
#define MALLOC_MAX 3 // Max number of time we can call SpuMalloc
//~ // convert Little endian to Big endian
#define SWAP_ENDIAN32(x) (((x)>>24) | (((x)>>8) & 0xFF00) | (((x)<<8) & 0x00FF0000) | ((x)<<24))
typedef struct VAGheader{ // All the values in this header must be big endian
char id[4]; // VAGp 4 bytes -> 1 char * 4
unsigned int version; // 4 bytes
unsigned int reserved; // 4 bytes
unsigned int dataSize; // (in bytes) 4 bytes
unsigned int samplingFrequency;// 4 bytes
char reserved2[12]; // 12 bytes -> 1 char * 12
char name[16]; // 16 bytes -> 1 char * 16
// Waveform data after that
}VAGhdr;
SpuCommonAttr commonAttributes; // structure for changing common voice attributes
SpuVoiceAttr voiceAttributes ; // structure for changing individual voice attributes
u_long vag_spu_address; // address allocated in memory for first sound file
// DEBUG : these allow printing values for debugging
u_long spu_start_address;
u_long get_start_addr;
u_long transSize;
// Memory management table ; allow MALLOC_MAX calls to SpuMalloc() - libref47.pdf p.1044
char spu_malloc_rec[SPU_MALLOC_RECSIZ * (MALLOC_MAX+1)];
// VAG files
// We're using GrumpyCoder's Nugget wrapper to compile the code with a modern GCC : https://github.com/grumpycoders/pcsx-redux/tree/main/src/mips/psyq
// To include binary files in the exe, add your VAG files to the SRCS variable in Makefile
// and in common.mk, add this rule to include *.vag files :
//
//~ %.o: %.vag
//~ $(PREFIX)-objcopy -I binary --set-section-alignment .data=4 --rename-section .data=.rodata,alloc,load,readonly,data,contents -O elf32-tradlittlemips -B mips $< $@
// hello_poly.vag - 44100 Khz
extern unsigned char _binary____VAG_hello_poly_vag_start[]; // filename must begin with _binary_ followed by the full path, with . and / replaced, and then suffixed with _ and end with _start[]; or end[];
extern unsigned char _binary____VAG_hello_poly_vag_end[]; // Going up one directory level is 4 '_' : ____ as ./ is replaced by __
// https://discord.com/channels/642647820683444236/663664210525290507/780866265077383189
void initGraph(void)
{
ResetGraph(0);
SetDefDispEnv(&disp[0], 0, 0, SCREENXRES, SCREENYRES);
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES);
SetDefDrawEnv(&draw[1], 0, 0, SCREENXRES, SCREENYRES);
if (VMODE)
{
SetVideoMode(MODE_PAL);
disp[0].screen.y += 8;
disp[1].screen.y += 8;
}
SetDispMask(1); // Display on screen
setRGB0(&draw[0], 50, 50, 50);
setRGB0(&draw[1], 50, 50, 50);
draw[0].isbg = 1;
draw[1].isbg = 1;
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
FntLoad(960, 0);
FntOpen(8, 60, 304, 200, 0, 500 );
}
void display(void)
{
DrawSync(0);
VSync(0);
PutDispEnv(&disp[db]);
PutDrawEnv(&draw[db]);
db = !db;
}
// Audio initialisation & functions
void initSnd(void){
SpuInitMalloc(MALLOC_MAX, spu_malloc_rec); // Maximum number of blocks, mem. management table address.
commonAttributes.mask = (SPU_COMMON_MVOLL | SPU_COMMON_MVOLR); // Mask which attributes to set
commonAttributes.mvol.left = 0x3fff; // Master volume left
commonAttributes.mvol.right = 0x3fff; // see libref47.pdf, p.1058
SpuSetCommonAttr(&commonAttributes); // set attributes
SpuSetIRQ(SPU_OFF);
}
u_long sendVAGtoRAM(unsigned int VAG_data_size, unsigned char *VAG_data){
u_long size;
SpuSetTransferMode(SpuTransByDMA); // DMA transfer; can do other processing during transfer
size = SpuWrite (VAG_data + sizeof(VAGhdr), VAG_data_size); // transfer VAG_data_size bytes from VAG_data address to sound buffer
SpuIsTransferCompleted (SPU_TRANSFER_WAIT); // Checks whether transfer is completed and waits for completion
return size;
}
void setVoiceAttr(unsigned int pitch, long channel, unsigned long soundAddr ){
voiceAttributes.mask= //~ Attributes (bit string, 1 bit per attribute)
(
SPU_VOICE_VOLL |
SPU_VOICE_VOLR |
SPU_VOICE_PITCH |
SPU_VOICE_WDSA |
SPU_VOICE_ADSR_AMODE |
SPU_VOICE_ADSR_SMODE |
SPU_VOICE_ADSR_RMODE |
SPU_VOICE_ADSR_AR |
SPU_VOICE_ADSR_DR |
SPU_VOICE_ADSR_SR |
SPU_VOICE_ADSR_RR |
SPU_VOICE_ADSR_SL
);
voiceAttributes.voice = channel; //~ Voice (low 24 bits are a bit string, 1 bit per voice )
voiceAttributes.volume.left = 0x1000; //~ Volume
voiceAttributes.volume.right = 0x1000; //~ Volume
voiceAttributes.pitch = pitch; //~ Interval (set pitch)
voiceAttributes.addr = soundAddr; //~ Waveform data start address
voiceAttributes.a_mode = SPU_VOICE_LINEARIncN; //~ Attack rate mode = Linear Increase - see libref47.pdf p.1091
voiceAttributes.s_mode = SPU_VOICE_LINEARIncN; //~ Sustain rate mode = Linear Increase
voiceAttributes.r_mode = SPU_VOICE_LINEARDecN; //~ Release rate mode = Linear Decrease
voiceAttributes.ar = 0x0; //~ Attack rate
voiceAttributes.dr = 0x0; //~ Decay rate
voiceAttributes.rr = 0x0; //~ Release rate
voiceAttributes.sr = 0x0; //~ Sustain rate
voiceAttributes.sl = 0xf; //~ Sustain level
SpuSetVoiceAttr(&voiceAttributes); // set attributes
}
void playSFX(void){
SpuSetKey(SpuOn,SPU_0CH); // Set several channels by ORing each channel bit ; ex : SpuSetKey(SpuOn,SPU_0CH | SPU_3CH | SPU_8CH); channels 0, 3, 8 are on.
}
int main(void)
{
short counter = 0;
const VAGhdr * VAGfileHeader = (VAGhdr *) _binary____VAG_hello_poly_vag_start; // get header of VAG file
// From libover47.pdf :
// The sampling frequency of the original audio file can be used to determine the pitch
// at which to play the VAG. pitch = (sampling frequency << 12)/44100L
// Ex: 44.1kHz=0x1000 22.05kHz=0x800 etc
unsigned int pitch = (SWAP_ENDIAN32(VAGfileHeader->samplingFrequency) << 12) / 44100L;
SpuInit(); // Initialize SPU. Called only once.
initSnd();
//~ // First VAG
vag_spu_address = SpuMalloc(SWAP_ENDIAN32(VAGfileHeader->dataSize)); // Allocate an area of dataSize bytes in the sound buffer.
spu_start_address = SpuSetTransferStartAddr(vag_spu_address); // Sets a starting address in the sound buffer
get_start_addr = SpuGetTransferStartAddr(); // SpuGetTransferStartAddr() returns current sound buffer transfer start address.
transSize = sendVAGtoRAM(SWAP_ENDIAN32(VAGfileHeader->dataSize), _binary____VAG_hello_poly_vag_start);
// set VAG to channel
setVoiceAttr(pitch, SPU_0CH, vag_spu_address);
initGraph();
while (1)
{
if(!counter){
playSFX();
counter = 180;
}
FntPrint("\nPitch : %08x-%dKhz", pitch, (SWAP_ENDIAN32(VAGfileHeader->samplingFrequency)) );
FntPrint("\nSet Start addr : %08x", vag_spu_address);
FntPrint("\nReturn start addr : %08x", spu_start_address);
FntPrint("\nGet Start addr : %08x", get_start_addr);
FntPrint("\nSend size : %08x", SWAP_ENDIAN32(VAGfileHeader->dataSize));
FntPrint("\nReturn size : %08x\n", transSize);
FntPrint("\nCounter : %d\n", counter);
FntFlush(-1);
counter --;
display();
}
return 0;
}
================================================
FILE: hello_world/Makefile
================================================
TARGET = hello_world
SRCS = hello_world.c \
include ../common.mk
================================================
FILE: hello_world/hello_world.c
================================================
// Demonstrate DISP/DRAW env, font setup, and display a text.
// Schnappy 2020
// Based on Lameguy64 tutorial : http://lameguy64.net/svn/pstutorials/chapter1/1-display.html
#include
#include
#include
#include
#include
#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL
#define SCREENXRES 320 // Screen width
#define SCREENYRES 240 // Screen height
#define CENTERX SCREENXRES/2 // Center of screen on x
#define CENTERY SCREENYRES/2 // Center of screen on y
#define MARGINX 0 // margins for text display
#define MARGINY 32
#define FONTSIZE 8 * 7 // Text Field Height
DISPENV disp[2]; // Double buffered DISPENV and DRAWENV
DRAWENV draw[2];
short db = 0; // index of which buffer is used, values 0, 1
void init(void)
{
ResetGraph(0); // Initialize drawing engine with a complete reset (0)
SetDefDispEnv(&disp[0], 0, 0 , SCREENXRES, SCREENYRES); // Set display area for both &disp[0] and &disp[1]
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES); // &disp[0] is on top of &disp[1]
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES); // Set draw for both &draw[0] and &draw[1]
SetDefDrawEnv(&draw[1], 0, 0 , SCREENXRES, SCREENYRES); // &draw[0] is below &draw[1]
if (VMODE) // PAL
{
SetVideoMode(MODE_PAL);
disp[0].screen.y += 8; // add offset : 240 + 8 + 8 = 256
disp[1].screen.y += 8;
}
SetDispMask(1); // Display on screen
setRGB0(&draw[0], 50, 50, 50); // set color for first draw area
setRGB0(&draw[1], 50, 50, 50); // set color for second draw area
draw[0].isbg = 1; // set mask for draw areas. 1 means repainting the area with the RGB color each frame
draw[1].isbg = 1;
PutDispEnv(&disp[db]); // set the disp and draw environnments
PutDrawEnv(&draw[db]);
FntLoad(960, 0); // Load font to vram at 960,0(+128)
FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); // FntOpen(x, y, width, height, black_bg, max. nbr. chars
}
void display(void)
{
DrawSync(0); // Wait for all drawing to terminate
VSync(0); // Wait for the next vertical blank
PutDispEnv(&disp[db]); // set alternate disp and draw environnments
PutDrawEnv(&draw[db]);
db = !db; // flip db value (0 or 1)
}
int main(void)
{
init(); // execute init()
while (1) // infinite loop
{
FntPrint("Hello world !"); // Send string to print stream
FntFlush(-1); // Draw printe stream
display(); // Execute display()
}
return 0;
}
================================================
FILE: hello_xa/Makefile
================================================
.PHONY: all cleansub
all:
mkpsxiso -y ./isoconfig.xml
cleansub:
$(MAKE) clean
rm -f hello_xa.cue hello_xa.bin
TARGET = hello_xa
SRCS = hello_xa.c \
include ../common.mk
================================================
FILE: hello_xa/README.md
================================================
See here for more informations about the XA fileformat and tools :
https://github.com/ABelliqueux/nolibgs_hello_worlds/wiki/XA
================================================
FILE: hello_xa/hello_xa.c
================================================
// XA track playback example
// base on `psyq/addons/scee/CD/XAPLAYER`
// Refs : http://psx.arthus.net/code/XA/XATUT.pdf
// http://psx.arthus.net/code/XA/xatut.zip
// http://psx.arthus.net/code/XA/XA%20ADPCM%20documentation.txt
// Schnappy 2021
#include
#include
#include
#include
#include
// CD library
#include
// SPU library
#include
#define VMODE 0 // Video Mode : 0 : NTSC, 1: PAL
#define SCREENXRES 320 // Screen width
#define SCREENYRES 240 + (VMODE << 4) // Screen height : If VMODE is 0 = 240, if VMODE is 1 = 256
#define CENTERX SCREENXRES/2 // Center of screen on x
#define CENTERY SCREENYRES/2 // Center of screen on y
#define MARGINX 0 // margins for text display
#define MARGINY 32
#define FONTSIZE 8 * 7 // Text Field Height
DISPENV disp[2]; // Double buffered DISPENV and DRAWENV
DRAWENV draw[2];
short db = 0; // index of which buffer is used, values 0, 1
// SPU attributes
SpuCommonAttr spuSettings;
#define CD_SECTOR_SIZE 2048
// XA
// Sector offset for XA data 4: simple speed, 8: double speed
#define XA_SECTOR_OFFSET 4
// Number of XA files
#define XA_TRACKS 1
typedef struct {
int start;
int end;
} XA_TRACK;
// Declare an array of XA_TRACK
XA_TRACK XATrack[XA_TRACKS];
// Name of file to load
static char * loadXA = "\\INTER8.XA;1";
CdlFILE XAPos = {0};
// Start and end position of XA data, in sectors
static int StartPos, EndPos;
// Current pos in file
static int CurPos = -1;
// Playback status : 0 not playing, 1 playing
static int gPlaying = 0;
// Current XA channel
static char channel = 0;
void init(void)
{
ResetGraph(0); // Initialize drawing engine with a complete reset (0)
SetDefDispEnv(&disp[0], 0, 0 , SCREENXRES, SCREENYRES); // Set display area for both &disp[0] and &disp[1]
SetDefDispEnv(&disp[1], 0, SCREENYRES, SCREENXRES, SCREENYRES); // &disp[0] is on top of &disp[1]
SetDefDrawEnv(&draw[0], 0, SCREENYRES, SCREENXRES, SCREENYRES); // Set draw for both &draw[0] and &draw[1]
SetDefDrawEnv(&draw[1], 0, 0 , SCREENXRES, SCREENYRES); // &draw[0] is below &draw[1]
// Set video mode
if (VMODE){ SetVideoMode(MODE_PAL);}
SetDispMask(1); // Display on screen
setRGB0(&draw[0], 50, 50, 50); // set color for first draw area
setRGB0(&draw[1], 50, 50, 50); // set color for second draw area
draw[0].isbg = 1; // set mask for draw areas. 1 means repainting the area with the RGB color each frame
draw[1].isbg = 1;
PutDispEnv(&disp[db]); // set the disp and draw environnments
PutDrawEnv(&draw[db]);
FntLoad(960, 0); // Load font to vram at 960,0(+128)
FntOpen(MARGINX, SCREENYRES - MARGINY - FONTSIZE, SCREENXRES - MARGINX * 2, FONTSIZE, 0, 280 ); // FntOpen(x, y, width, height, black_bg, max. nbr. chars
}
void display(void)
{
DrawSync(0); // Wait for all drawing to terminate
VSync(0); // Wait for the next vertical blank
PutDispEnv(&disp[db]); // set alternate disp and draw environnments
PutDrawEnv(&draw[db]);
db = !db; // flip db value (0 or 1)
}
int main(void)
{
// Init display
init();
// SPU setup
// Init Spu
SpuInit();
// Set master & CD volume to max
spuSettings.mask = (SPU_COMMON_MVOLL | SPU_COMMON_MVOLR | SPU_COMMON_CDVOLL | SPU_COMMON_CDVOLR | SPU_COMMON_CDMIX);
spuSettings.mvol.left = 0x6000;
spuSettings.mvol.right = 0x6000;
spuSettings.cd.volume.left = 0x6000;
spuSettings.cd.volume.right = 0x6000;
// Enable CD input ON
spuSettings.cd.mix = SPU_ON;
// Apply settings
SpuSetCommonAttr(&spuSettings);
// Set transfer mode
SpuSetTransferMode(SPU_TRANSFER_BY_DMA);
// Init CD system
CdInit();
// Optional : Set CD Attenuation volume
//
// CdlATV cd_vol;
// cd_vol.val0 = cd_vol.val1 = cd_vol.val2 = cd_vol.val3 = 0x40;
// CdMix(&cd_vol);
//
// Load XA file from cd
// Find XA file pos
CdSearchFile( &XAPos, loadXA);
XATrack[0].start = CdPosToInt(&XAPos.pos);
XATrack[0].end = XATrack[0].start + (XAPos.size/CD_SECTOR_SIZE) - 1;
StartPos = XATrack[0].start;
EndPos = XATrack[0].end;
// XA setup
u_char param[4];
// ORing the parameters we need to set ; drive speed, ADPCM play, Subheader filter, sector size
// If using CdlModeSpeed(Double speed), you need to load an XA file that has 8 channels.
// In single speed, a 4 channels XA is to be used.
param[0] = CdlModeSpeed|CdlModeRT|CdlModeSF|CdlModeSize1;
// Issue primitive command to CD-ROM system (Blocking-type)
// Set the parameters above
CdControlB(CdlSetmode, param, 0);
// Pause at current pos
CdControlF(CdlPause,0);
// Set filter
// This specifies the file and channel number to actually read data from.
CdlFILTER filter;
// Use file 1, channel 0
filter.file = 1;
filter.chan = channel;
// Set filter
CdControlF(CdlSetfilter, (u_char *)&filter);
// Position of file on CD
CdlLOC loc;
// Set CurPos to StartPos
CurPos = StartPos;
while (1) // infinite loop
{
// Begin XA file playback
if (gPlaying == 0 && CurPos == StartPos){
// Convert sector number to CD position in min/second/frame and set CdlLOC accordingly.
CdIntToPos(StartPos, &loc);
// Send CDROM read command
CdControlF(CdlReadS, (u_char *)&loc);
// Set playing flag
gPlaying = 1;
}
// When endPos is reached, set playing flag to 0
if ((CurPos += XA_SECTOR_OFFSET) >= EndPos){
gPlaying = 0;
}
// If XA file end is reached, stop playback
if ( gPlaying == 0 && CurPos >= EndPos ){
// Stop XA playback
// Stop CD playback
CdControlF(CdlStop,0);
// Optional
// Reset parameters
// param[0] = CdlModeSpeed;
// Set CD mode
// CdControlB(CdlSetmode, param, 0);
// Switch to next channel and start play back
channel = !channel;
filter.chan = channel;
// Set filter
CdControlF(CdlSetfilter, (u_char *)&filter);
CurPos = StartPos;
}
FntPrint("Hello XA ! %d\n", VSync(-1));
FntPrint("Start, End Pos: %d %d\n", StartPos, EndPos);
FntPrint("Current Pos: %d\n", CurPos );
FntPrint("Playback status: %d\n", gPlaying );
FntFlush(-1); // Draw printe stream
display(); // Execute display()
}
return 0;
}
================================================
FILE: hello_xa/isoconfig.xml
================================================
================================================
FILE: hello_xa/system.cnf
================================================
BOOT=cdrom:\SCES_313.37;1
TCB=4
EVENT=10
STACK=801FFFF0
================================================
FILE: hello_xa/xa/interleave4.txt
================================================
1 xa xa/funk.xa 1 0
1 xa xa/beach.xa 1 1
1 null
1 null
================================================
FILE: hello_xa/xa/interleave8.txt
================================================
1 xa xa/funk.xa 1 0
1 xa xa/beach.xa 1 1
1 null
1 null
1 null
1 null
1 null
1 null
================================================
FILE: includes/CPUMAC.H
================================================
/*
** cpumac.h
mike acton
*/
// cpu_ldr(cpu register,data pointer)
// copy 32bit data from dp to r
#define cpu_ldr(r,dp)\
asm(\
"lw %0, 0(%1);"\
: "=r" (r)\
: "r" (dp)\
)
// cpu_gted0(cpu register)
// copy 32bit data from r to gte register 0
#define cpu_gted0(r)\
asm(\
"mtc2 %0, $0;"\
:\
: "r" (r)\
)
// cpu_gted1(cpu register)
// copy 32bit data from r to gte register 1
#define cpu_gted1(r)\
asm(\
"mtc2 %0, $1;"\
:\
: "r" (r)\
)
// cpu_gted2(cpu register)
// copy 32bit data from r to gte register 2
#define cpu_gted2(r)\
asm(\
"mtc2 %0, $2;"\
:\
: "r" (r)\
)
// cpu_gted3(cpu register)
// copy 32bit data from r to gte register 3
#define cpu_gted3(r)\
asm(\
"mtc2 %0, $3;"\
:\
: "r" (r)\
)
// cpu_gted4(cpu register)
// copy 32bit data from r to gte register 4
#define cpu_gted4(r)\
asm(\
"mtc2 %0, $4;"\
:\
: "r" (r)\
)
// cpu_gted5(cpu register)
// copy 32bit data from r to gte register 5
#define cpu_gted5(r)\
asm(\
"mtc2 %0, $5;"\
:\
: "r" (r)\
)