Wolfenstein / DOOM for Oric : has this been done?

Want to talks about games you like, would like to see developed on the Oric, it's here.
User avatar
jbperin
Flight Lieutenant
Posts: 480
Joined: Wed Nov 06, 2019 11:00 am
Location: Valence, France

Re: Wolfenstein / DOOM for Oric : has this been done?

Post by jbperin »

Dbug wrote: Thu Oct 28, 2021 7:10 pm - Complete source code should be out, and possible to compile out of the box without any special setup
- Need some instrumentation code (at the very least min/max/average frame time)
Code source of Castoric is totally open to readings, comments and/or push request:
https://github.com/oric-software/castoric
My current works on the use of castoric to create a FPS are all published in a public repository :
https://github.com/jbperin/castoric/tree/moveSprites

Means of profiling already exist and I used them at some moments:
https://osdk.org/index.php?page=articles&ref=ART11

All the rest is willing and motivation.
Dbug wrote: Thu Oct 28, 2021 7:10 pm My question is: Why is your repository private?
I can understand if you want to only give write access to some people, but there's no reason not to have everybody look at the code: More eyes, more chances people have ideas.
The only repository that is private is the one I just created to host development of a clone of wolfenstein.
I did that because this repo hosts art files (textures, music) which belong to a company rather than individuals I can easily get agreement from.
I don't know what's the position of the company about me diffusing some content they own.
People that I invite to contribute to this project gives what they accept to give .. under eyes of all other contributors.
User avatar
Dbug
Site Admin
Posts: 4444
Joined: Fri Jan 06, 2006 10:00 pm
Location: Oslo, Norway
Contact:

Re: Wolfenstein / DOOM for Oric : has this been done?

Post by Dbug »

Based on the fact they released the source code publicly (https://github.com/id-Software/wolf3d) it's probably safe to make derivative work, as long as:
- it is non commercial
- it is acknowledge that the original work is still copyrighted by ID

The complete licence file is in the repository:

Permitted Uses.
For educational purposes only, you, the end-user, may use portions of the Source Code, such as particular routines, to develop your own software, but may not duplicate the Source Code, except as noted in paragraph 4. The limited right referenced in the preceding sentence is hereinafter referred to as "Educational Use." By so exercising the Educational Use right you shall not obtain any ownership, copyright, proprietary or other interest in or to the Source Code, or any portion of the Source Code. You may dispose of your own software in your sole discretion. With the exception of the Educational Use right, you may not otherwise use the Software, or an portion of the Software, which includes the Source Code, for commercial gain.

Prohibited Uses:
Under no circumstances shall you, the end-user, be permitted, allowed or authorized to commercially exploit the Software. Neither you nor anyone at your direction shall do any of the following acts with regard to the Software, or any portion thereof:
- Rent;
- Sell;
- Lease;
- Offer on a pay-per-play basis;
- Distribute for money or any other consideration;
or In any other manner and through any medium whatsoever commercially exploit or use for any commercial purpose.
User avatar
jbperin
Flight Lieutenant
Posts: 480
Joined: Wed Nov 06, 2019 11:00 am
Location: Valence, France

Re: Wolfenstein / DOOM for Oric : has this been done?

Post by jbperin »

OK .. I trust in you ..

I think you understand well these license things and as we don't plan to make money with this clone, we can safely have some art data that belongs to this company.

The repository is now public:
https://github.com/jbperin/wolfric

The link above is the main branch that will only receive released version (this is why it is empty).

Developments are done in branch develop before being merged to branch main :
https://github.com/jbperin/wolfric/tree/develop

Anyone wanting to contribute can create his own branch from this develop branch
User avatar
Dbug
Site Admin
Posts: 4444
Joined: Fri Jan 06, 2006 10:00 pm
Location: Oslo, Norway
Contact:

Re: Wolfenstein / DOOM for Oric : has this been done?

Post by Dbug »

jbperin wrote: Fri Oct 29, 2021 1:38 pm OK .. I trust in you ..

I think you understand well these license things and as we don't plan to make money with this clone, we can safely have some art data that belongs to this company.

The repository is now public:
https://github.com/jbperin/wolfric
The idea was to link with the appropriate copyright information and statement about what the project is (non commercial fan conversion/port) before making it public :)
User avatar
ibisum
Wing Commander
Posts: 1645
Joined: Fri Apr 03, 2009 8:56 am
Location: Vienna, Austria
Contact:

Re: Wolfenstein / DOOM for Oric : has this been done?

Post by ibisum »

Awesome! Thanks for making the repo open, some fun code to read!
User avatar
jbperin
Flight Lieutenant
Posts: 480
Joined: Wed Nov 06, 2019 11:00 am
Location: Valence, France

Re: Wolfenstein / DOOM for Oric : has this been done?

Post by jbperin »

Dbug wrote: Fri Oct 29, 2021 3:41 pm The idea was to link with the appropriate copyright information and statement about what the project is (non commercial fan conversion/port) before making it public :)
OK .. you're officially appointed chief lawyer in charge of all the bothering legal things that noone understand nothing about.

The thing I'd like to point out and make clear is that I got nothing from :
https://github.com/id-Software/wolf3d

I wrote all the code of castoric without having a single look at what id software had done.
Even the art thing , I got them from sites I refer to in this page:
https://github.com/jbperin/wolfric/blob ... /README.md

So.. my questions to the specialist you are in the game industry are :
- Can we legally have fun redoing a king of wolfenstein for free on Oric ?
- Are we sure castoric (and other works of contributors) are not going to belong to id-software just because we use them in a clone of wolfenstein?
- What kind of text shoud we had to be clean regarding the legal stuffs ?
User avatar
Chema
Game master
Posts: 3014
Joined: Tue Jan 17, 2006 10:55 am
Location: Gijón, SPAIN
Contact:

Re: Wolfenstein / DOOM for Oric : has this been done?

Post by Chema »

In case it could serve as inspiration;

User avatar
jbperin
Flight Lieutenant
Posts: 480
Joined: Wed Nov 06, 2019 11:00 am
Location: Valence, France

Re: Wolfenstein / DOOM for Oric : has this been done?

Post by jbperin »

Chema wrote: Tue Nov 02, 2021 12:02 am In case it could serve as inspiration;
Waou !! this game looks great. Thank you for the link.

I find they really did a good work with this ray casting engine. They reach 25-30 fps !! amazing !!

It makes me think that Oric would also deserve to have such a game in its asset. Because we have the same CPU as they have on 8 bits atari (except that their clock is 1.77Mhz instead of 1Mhz on Oric).

Unfortunately, I don't think castoric can reach a similar frame rate.
And as you already mentioned, Castoric is more Dungeon Crawler oriented.
But no one prevents us from getting as close as possible.

Anyway thank you for making me discover this marvel.

On Apple II they have "Lawless Legends"
On Atari XL they have "Final Assault"
Does anyone now what they have on C64 ?
And on other 8 bits machine ?
User avatar
Dbug
Site Admin
Posts: 4444
Joined: Fri Jan 06, 2006 10:00 pm
Location: Oslo, Norway
Contact:

Re: Wolfenstein / DOOM for Oric : has this been done?

Post by Dbug »

So, I took the latest version of Castoric from github and looked at the source code and I still think that the easiest way to boost the framerate would be to change the way the rasterization is done:
  • Drop DDA and use FixedPoint iterations (no divs required, just precompute the values for the possible sizes of segments on screen in some array
  • The array could even contain the start position in the texture and the maximum number of iterations, so takes care of very close walls that are taller than the screen view
  • Using the single column array I was talking about earlier, you can ditch the mul120 array
The trade-of is that when there's nothing on screen (or very small walls far away), the framerate is going to be slower, but when there's a lot of things on screen, the framerate should be humongously faster, and the code much easier to read, because let's face it, DDA is super complicated while fixed point is trivial.

For the ones who don't know fixed point, the idea is to use 16 bit adds to simulate decimal values, using 8 bits for the integer part and 8 bits for the decimal part.

It's what I use in my rotozooms, and the code look like that:

Code: Select all

	ldx	#37		; 37 columns
loop_draw_x
	clc
	lda	_U            ; U
	adc _IU             ; +U increment
	sta _U               ; Store decimal part
	lda _U1             ; Same with integer part
	adc _IU1   
	sta _U1             ; Which we use as the actual coordinate
	sta _adr_read_pixel+1

	clc
	lda	_V           ; Same for V
	adc _IV            ; with (U,V) the coordinates in the texture
	sta _V
	lda _V1
	adc _IV1
	sta _V1
	and #63
	clc
	adc #>(_Picture)
	sta _adr_read_pixel+2

_adr_read_pixel
	lda	$1234

screen_adr
	sta	$bb80,x
	dex
	bne	loop_draw_x
And that's it, that's the entire code of the rotozoom for a single line on the screen, and conceptually, mapping a segment of wall texture is identical to what I have here.
User avatar
jbperin
Flight Lieutenant
Posts: 480
Joined: Wed Nov 06, 2019 11:00 am
Location: Valence, France

Re: Wolfenstein / DOOM for Oric : has this been done?

Post by jbperin »

Not sure to understand well because it seems to me that it is already done the way you're suggesting (except the fixed 8.8 that was not required because all steps are precomputed in 8 bits).
I think you had already suggested me to used precomputed dda steps rather than computing them live.

Can you look at followings pointer to check if it matches with your recommendations ?
Dbug wrote: Sat Nov 06, 2021 1:38 pm
  • Drop DDA and use FixedPoint iterations (no divs required, just precompute the values for the possible sizes of segments on screen in some array
The DDA is only used for very close walls that are taller than the screen view. In this case, the texture is oversampled.
In all other conditions, the DDA is replaced by a sequential access of precomputed consecutive steps in texture.
Dbug wrote: Sat Nov 06, 2021 1:38 pm
  • The array could even contain the start position in the texture and the maximum number of iterations, so takes care of very close walls that are taller than the screen view
All run-through steps in texture are precomputed in this array.
and pointers to the area corresponding to the size of segments are stored in this table
The relevant pointer is selected in the PREPARE_UNROLL_SAMPLE which is called before unrolling a column.
Dbug wrote: Sat Nov 06, 2021 1:38 pm
  • Using the single column array I was talking about earlier, you can ditch the mul120 array
Are you talking about this access to multi120 ? It is only used to compute the start position in screen memory to unroll the texture for a given column and a given length of segment. It is used out of loop.
User avatar
Dbug
Site Admin
Posts: 4444
Joined: Fri Jan 06, 2006 10:00 pm
Location: Oslo, Norway
Contact:

Re: Wolfenstein / DOOM for Oric : has this been done?

Post by Dbug »

Ok, so what I suggest is that you add comments on the various part of the code to indicate what they do and in which context they are used because right now it's pretty much impossible to follow what the code does since it's just macros calling macros and there are no comments other than the equivalent C code.

Regarding
The DDA is only used for very close walls that are taller than the screen view. In this case, the texture is oversampled.
Well, as it happens that's the case where the rendering is the slowest, so that would at least help on this specific case.
The relevant pointer is selected in the PREPARE_UNROLL_SAMPLE which is called before unrolling a column.
Ok, the fact PREPARE_UNROLL_SAMPLE calls DDA_STEP kind of made me believe it used DDA, if it does not, then maybe rename the macro, or alias it.

If you want people being able to help, the code has to be written so other people can understand it.

For example, PREPARE_UNROLL_SAMPLE does use "_ddaNbStep", which does not help.

Ok, so let's dig in the code (the unroll part, and tell me if I got it all right), I'm going to try to understand how all the macro stuff work together:

The core of the unroll is that simple snippet that uses three macros, with the UNROLL_SAMPLE taking two other macros that gets expanded in "prim_frst and prim_scnd):

Code: Select all

goUnroll    
   PREPARE_UNROLL_SAMPLE
   DDA_STEP
   UNROLL_SAMPLE(COLOR_LEFT_TEXEL_FIRST,COLOR_LEFT_TEXEL_SECOND)
PREPARE_UNROLL_SAMPLE and DDA_STEP are relatively simple and short:

Code: Select all

#define PREPARE_UNROLL_SAMPLE 
	lda _ddaNbStep: sta _ddaNbIter 
	tay  
	lda _adrTabIdxRd_low,Y 
	sta _ptrOffsetIndex 
	lda _adrTabIdxRd_high,Y 
	sta _ptrOffsetIndex+1 
	lda #0 : sta _nxtOffsetIndex 
So ok, the "_ddaNbStep" and "_ddaNbIter" are used to access the _adrTabIdxRd_low/high to patch the _ptrOffsetIndex, which itself is read from DDA_STEP using the _nxtOffsetIndex.

Code: Select all

#define DDA_STEP  
	ldy _nxtOffsetIndex
	lda (_ptrOffsetIndex), Y
	sta _ddaCurrentValue
	iny
	sty _nxtOffsetIndex
Before proceeding to UNROLL_SAMPLE, let's check the two macros COLOR_LEFT_TEXEL_FIRST and COLOR_LEFT_TEXEL_SECOND

Code: Select all

; x contains _renCurrentColor
#define COLOR_LEFT_TEXEL_FIRST 
    lda _tabLeftRed,x
    ldy #0
    sta (_theAdr),y
    lda _tabLeftGreen,x
    ldy #40
    sta (_theAdr),y
    lda _tabLeftBlue,x
    ldy #80
    sta (_theAdr),y
    bcc skip: inc _theAdr+1: skip
So what that does, is that X contains the texture pixel color, and using the three tables the texel is converted into values that can be displayed over three scanlines to generate a RGB triplet.

(There's an optimisation possibility there, you don't actually need the bcc skip/inc _theAdr+1 in the first macro.)

Code: Select all

; x contains _renCurrentColor
#define COLOR_LEFT_TEXEL_SECOND 
    lda _tabLeftRed,x
    ldy #120
    sta (_theAdr),y
    lda _tabLeftGreen,x
    ldy #160
    sta (_theAdr),y
    lda _tabLeftBlue,x
    ldy #200
    sta (_theAdr),y
    clc     
    lda _theAdr
    adc #240
    sta _theAdr
    bcc skip: inc _theAdr+1: skip
That one does the same thing for a second texel, the reason of having two dedicated code is that each scanline is 40 bytes long, 40*3=120, 120*2=240, so you can treat two pixels using the same Y indexing offset, after that you ned to change the pointer address.

Using my suggestion of the intermediate column buffer, you can just remove all the ldy #immediate as well as the indirect addressing, so instead of:

Code: Select all

    ldy #120
    sta (_theAdr),y
you just get

Code: Select all

    sta _ColumnBuffer,y
    iny
and you don't need a special case to treat them by blocks of two pixels, just iterate over the entire texture naturally, makes the code much easier.

Which brings us to the last big part, UNROLL_SAMPLE.
That one is massive, and get even larger with the expansion of the two COLOR_LEFT_TEXEL macros, so I'm splitting it in two parts.

Code: Select all

#define UNROLL_SAMPLE(prim_frst,prim_scnd) 
	lda #TEXTURE_SIZE: sta _ddaCurrentError
loop_000 
	lda _idxScreenLine 
	cmp #VIEWPORT_START_LINE 
	bpl end_loop_000
	DDA_STEP
	dec _ddaNbIter
	inc _idxScreenLine
	jmp loop_000 
end_loop_000
The very first line is a remnant of your old code and seem useless, so you can remove the lda #TEXTURE_SIZE: sta _ddaCurrentError.

Then I'm wondering why you do need to iterate at all: Can't you just precompute in a table all the possible parameters you need? With the resolution we have (which technically since we use three scanlines per pixels is no more than 200/3=66 pixels tall), that's not many possible combinations at all.

Also, since this loop contains DDA_STEP, it also means that for each iteration loop you recompute the _ddaCurrentValue... but then again I removed the DDA_STEP and it did not seem to change anything at all... maybe because DDA_STEP is also called in the main loop in the rest of the code after?

Code: Select all

    ldy _idxScreenLine:lda _multi120_low,Y:clc:adc _baseAdr:sta _theAdr
    lda _multi120_high,Y:adc _baseAdr+1:sta _theAdr+1 
loop_001 
	DDA_STEP
	ldy _ddaCurrentValue : lda (_ptrReadTexture),Y 
	tax
	prim_frst
	inc _idxScreenLine
	dec _ddaNbIter
	beq endloop_001 
	DDA_STEP
	ldy _ddaCurrentValue : lda (_ptrReadTexture),Y 
	tax
	prim_scnd
	inc _idxScreenLine
	lda _idxScreenLine 
	cmp #VIEWPORT_HEIGHT + VIEWPORT_START_LINE 
	bcs endloop_001 
	dec _ddaNbIter
	beq endloop_001 
	jmp loop_001 
endloop_001
So before the main loop we start by a computation of the base address using the multi120 table, the only two places where it's used have the exact same code, so a possible optimisation would be to have the table be a table of offsets instead of multiplication by 120, so you could skip the addition, transforming this:

Code: Select all

    ldy _idxScreenLine
    lda _multi120_low,Y:clc:adc _baseAdr:sta _theAdr
    lda _multi120_high,Y:adc _baseAdr+1:sta _theAdr+1 
into this:

Code: Select all

    ldy _idxScreenLine
    lda _multi120_low,Y:sta _theAdr
    lda _multi120_high,Y:sta _theAdr+1 
That's not a huge optimisation, but that can be helpful since now the carry is not modified, and technically the lda/sta could become ldx/stx which gives you some flexibility to use the table access in other locations.

When the code is expended, what follows transforms this:

Code: Select all

	DDA_STEP
	ldy _ddaCurrentValue : lda (_ptrReadTexture),Y 
	tax
into this:

Code: Select all

	ldy _nxtOffsetIndex
	lda (_ptrOffsetIndex), Y
	sta _ddaCurrentValue
	iny
	sty _nxtOffsetIndex
	ldy _ddaCurrentValue : lda (_ptrReadTexture),Y 
	tax
Because you are using many macros, it's hard to see the possible optimisations, but basically the _ddaCurrentValue and _nxtOffsetIndex variables are probably not necessary, you could just do that instead:

Code: Select all

ldy _nxtOffsetIndex
lda (_ptrOffsetIndex), Y
tay
lda (_ptrReadTexture),Y
inc _nxtOffsetIndex
tax
Not a huge optimisation, but that's 22 cycles instead of 24.
Further optimisations would probably require changing some table structures to get them page aligned.

The cool thing about this type of thing, is that it makes the code just short enough that you can avoid having to use the reverse branch + jmp idiom, so you can replace at the end this:

Code: Select all

	beq endloop_001 
	jmp loop_001

by this:

Code: Select all

	bne loop_001 
Not much, but that's done for every two pixels, so quite often in the end.

The final optimization I could see (without changing the code structure) would be to not have to test both the _idxScreenLine and _ddaNbIter to know if you are done, the number of iteration is known in advance, and one check should be enough, so you can just remove these sections of code:

Code: Select all

    inc _idxScreenLine
and

Code: Select all

    inc _idxScreenLine
    lda _idxScreenLine 
    cmp #VIEWPORT_HEIGHT + VIEWPORT_START_LINE 
    bcs endloop_001 
As far as I can see, the code still runs correctly.
User avatar
jbperin
Flight Lieutenant
Posts: 480
Joined: Wed Nov 06, 2019 11:00 am
Location: Valence, France

Re: Wolfenstein / DOOM for Oric : has this been done?

Post by jbperin »

Thank you very much for this in-depth reading.

Each time you peer-review my code, I'm impressed by the relevancy and the accuracy of your remarks.

It's a very valuable reading that you do.
Not only because it will help to improve the performance but also because it requires high skills to catch how the code is structured and how it works.
So there are not so many people able to provide this level of reading and understanding of the code.

As you could see, the code is somewhat obfuscated by the intense use of macros that makes it difficult to follow.
I used macros because I wanted to avoid jsr/rts while keeping a structured code and avoiding code duplication.
So I decided to let the preprocessor do the inlining.

I'm not going to take into account these remarks right now because I currently want to keep focus on my writing of the sprite routine.
But be sure that I will pay close attention to all the optim you've just pointed out.

At first sight, I can say that I'm surprised that it is possible to get rid of DDA_STEP.

I can also say that it breaks my heart each time I used the reverse branch + jmp idiom.
I know it's a costy approach and I only accept it because it's a safe approach. Not knowing if a page will be crossed or not (dependig on where the code is going to located) it's the only way to make sure it will systematically work.

Thanks again for this peer review.
I measure the effort it takes to dive into the design as you've just done.
I'll keep you posted of next steps.
User avatar
Dbug
Site Admin
Posts: 4444
Joined: Fri Jan 06, 2006 10:00 pm
Location: Oslo, Norway
Contact:

Re: Wolfenstein / DOOM for Oric : has this been done?

Post by Dbug »

jbperin wrote: Mon Nov 08, 2021 1:16 pm I can also say that it breaks my heart each time I used the reverse branch + jmp idiom.
I know it's a costy approach and I only accept it because it's a safe approach. Not knowing if a page will be crossed or not (dependig on where the code is going to located) it's the only way to make sure it will systematically work.
The only impact of the page crossing is to pay one more cycle or not, that's all.

The inverted branch + jmp is only there to help with the code is too large to do a +/-127 relative jump, and is always slower that just done the normal relative code if you can afford it.

There's nothing wrong with using macros, I use that as well, but ultimately it's still expanded as part of the same "function" using the same registers and variables, you need to keep in mind the big picture on how it all fits together.

Regarding the obfuscation, I was not able to work on your original code, what I did is:
- Remove every single gcc reference
- Remove every single C version of functions
- remove every single #ifdef/#ifndef
At this point only was I able to see which code was used and which was not (like your unrolled code to clear the back buffer, it's linked but it's not used).

And then to figure out the draw wall, I had to extract each of the macros, put them in my text editor, strip ou all the :\ because as I told you about the VR source code, I can't read horizontal aligned assembler, I just can't.
User avatar
Chema
Game master
Posts: 3014
Joined: Tue Jan 17, 2006 10:55 am
Location: Gijón, SPAIN
Contact:

Re: Wolfenstein / DOOM for Oric : has this been done?

Post by Chema »

jbperin wrote: Mon Nov 08, 2021 1:16 pm
Each time you peer-review my code, I'm impressed by the relevancy and the accuracy of your remarks.
Been there, experienced that.
Dbug rocks!
User avatar
jbperin
Flight Lieutenant
Posts: 480
Joined: Wed Nov 06, 2019 11:00 am
Location: Valence, France

Re: Wolfenstein / DOOM for Oric : has this been done?

Post by jbperin »

WhichGameIsIt.gif
WhichGameIsIt.gif (373.34 KiB) Viewed 3006 times
Post Reply